Post

21. 에러 핸들링

21. 에러 핸들링

✅ 1. 에러 반환

  • 에러를 처리하는 가장 기본 방식은 에러를 반환하고 알맞게 처리하는 방식이다
    1
    2
    3
    4
    
    if err != nil {
      fmt.Println("파일 생성에 실패했습니다.", err)
      return
    }
    

1.1 사용자 에러 반환

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
	"fmt"
	"math"
)

func Sqrt(f float64) (float64, error) {
	if f < 0 {
		return 0, fmt.Errorf("제곱근은 양수여야 합니다. f:%g", f)
	}
	return math.Sqrt(f), nil
}
  • fmt 패키지의 Errorf() 함수를 이용하면 원하는 에러 메시지를 만들 수 있다
  • errors 패키지의 New() 함수를 이용해서 error를 생성할 수도 있다
    1
    2
    3
    
      import "errors"
    	
      errors.New("에러 메시지")
    

✅ 2. 에러 타입

  • 사실 error는 인터페이스로, 문자열을 반환하는 Error() 메서드로 구성되어 있다.
    1
    2
    3
    
      type error interface {
          Error() string
      }
    
  • 즉, 어떤 타입이든 Error() string 메서드를 포함한다면 에러로 사용할 수 있다.

    2.1 에러 랩핑

  • fmt.Errorf() 함수와 %w 서식으로 에러를 감쌀 수 있다
  • errors.As() 함수를 사용하면 에러 체인을 따라서 변환 가능한 에러를 찾으며, 변환 가능한 에러가 있다면 값을 넣고 true를 반환한다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
	"errors"
	"fmt"
	"strconv"
)

func Example() error {
	val := "test"

	if _, err := strconv.Atoi(val); err != nil {
		return fmt.Errorf("parse error=%q: %w", val, err)
	}
	return nil
}

func main() {
	err := Example()
	if err != nil {

		fmt.Printf("%T\n", err) // *fmt.wrapError

		var numsErr *strconv.NumError
		if errors.As(err, &numsErr) {
			fmt.Println("Func:", numsErr.Func, "Num:", numsErr.Num, "Cause:", numsErr.Err) // Func: Atoi Num: test Cause: invalid syntax
			return
		}
	}
}

✅ 3. 패닉

  • 패닉(panic)은 프로그램을 정상 진행시키기 어려운 경우, 프로그램 흐름을 중지시키는 기능이다
  • Go 언어에서는 내장 함수 panic()으로 패닉 기능을 제공한다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func main() {
	divide(9, 0) // panic: b cannot be zero
}

func divide(a, b int) {
	if b == 0 {
		panic("b cannot be zero")
	}

	fmt.Printf("%v / %v = %v", a, b, a/b)
}
  • panic()을 호출하고 인수로 에러 메시지를 입력하면 프로그램을 즉시 종료하고 에러 메시지를 출력하고 콜 스택을 표시한다

3.1 패닉 생성

1
func panic(interface{})
  • 어떤 타입이든 인자로 가능하나, 일반적으로는 string 타입 메시지나 fmt.Errorf()를 통해 만든 에러 타입을 주로 인수로 넣어준다

3.2 패닉 전파 및 복구

  • panic은 호출 순서를 거슬러 올라가며 전파되며, main() 함수에서까지 복구되지 않으면 프로그램이 강제종료된다
  • recover() 함수를 호출해 패닉 복구를 할 수 있다
  • recover() 함수가 호출되는 시점에 패닉이 전파 중이면 panic 객체를 반환하고 아니면 nil을 반환한다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import "fmt"

func main() {
	f()
	fmt.Println("프로그램 계쏙 실행")
}

func f() {
	fmt.Println("f() 실행")

	defer func() {
		if r := recover(); r != nil {
			fmt.Println("패닉 복구 - ", r)
		}
	}()

	g()
}

func g() {
	panic("패닉 테스트")
}

// f() 실행
// 패닉 복구 -  패닉 테스트
// 프로그램 계쏙 실행

3.3 recover() 결과

1
func recover() interface{}
  • recover()로 반환한 타입을 실제로 사용하려면 Type assertion으로 검사해야한다
1
2
3
if r, ok := recover().(net.Error); ok {
	fmt.Println("r is net Error Type")
}
This post is licensed under CC BY 4.0 by the author.