Skip to content

Latest commit

 

History

History
260 lines (176 loc) · 6.8 KB

에러_반환.md

File metadata and controls

260 lines (176 loc) · 6.8 KB

Tucker의 Go 언어 프로그래밍 - 요약


#12 에러 반환



Error Return

Go 언어에서 에러를 처리하는 기본적인 방식은 함수가 에러를 반환하고, 호출자가 그 에러를 적절히 처리하는 것이다.

런타임 에러가 발생했을 때 프로그램이 강제로 종료되는 것보다는 적절한 에러 메시지를 출력하는 것이 사용자 경험을 향상시킨다.

Go 언어에서는 이러한 패턴을 장려하며, 함수는 보통 결과값과 함께 에러도 반환한다.



Custom Error Return

  • fmt 패키지의 Errof() 함수를 이용해서 원하는 에러 메시지를 만들 수 있다.

  • errors 패키지의 New() 함수를 이용해서 error를 생성할 수 있다.

package main

import (
	"errors"
	"fmt"
)

func main() {
	// 에러 발생 시나리오 1: 파일을 찾을 수 없는 경우
	err1 := fileNotFoundError("myFile.txt")
	if err1 != nil {
		fmt.Println("에러 발생:", err1)
	}

	// 에러 발생 시나리오 2: 유효하지 않은 사용자 입력
	err2 := invalidInputError(42)
	if err2 != nil {
		fmt.Println("에러 발생:", err2)
	}
}

// fmt.Errorf()를 사용하여 포맷된 에러 메시지를 생성.
func fileNotFoundError(fileName string) error {
	return fmt.Errorf("파일을 찾을 수 없습니다: %s", fileName)
}

// errors.New()를 사용하여 간단한 에러 메시지를 생성.
func invalidInputError(input int) error {
	return errors.New(fmt.Sprintf("유효하지 않은 입력: %d", input))
}



Error Type

error는 인터페이스로 문자열을 반환하는 Error() 메서드로 구성되어 있다.

type error interface{
    Error() string
}

어떤 타입이든 문자열을 반환하는 Error() 메서드를 포함하고 있다면 에러로 사용할 수 있다.

이를 이용하면 에러에 더 많은 정보를 포함시킬 수 있다.

package main

import "fmt"

type PasswordError struct {
    Len        int
    RequireLen int
}

func (err PasswordError) Error() string {
    return "암호 길이가 짧습니다."
}

func RegisterAccount(name, password string) error {
    if len(password) < 8 {
        return PasswordError{len(password), 8}
    }
    return nil
}

func main() {
    err := RegisterAccount("myID", "myPw")
    if err != nil {
        if errInfo, ok := err.(PasswordError); ok {
            fmt.Printf("%v Len: %d RequireLen: %d\n", errInfo, errInfo.Len, errInfo.RequireLen)
        }
    } else {
        fmt.Println("회원 가입 됐습니다.")
    }
}

✅ 보충 설명

위 코드에서 사용된 ok는 Go 언어의 타입 단언(type assertion)을 사용할 때 얻을 수 있는 불리언 값이다.

타입 단언은 특정 인터페이스가 특정 타입을 가지고 있는지를 검사하고, 그 타입으로 변환된 값을 얻기 위해 사용된다.


ok는 불리언(boolean) 값이다.

  • 만약 err이 PasswordError 타입이면, ok는 true가 되고, errInfo는 err의 PasswordError 타입으로 변환된 값을 가진다.

  • 만약 err이 PasswordError 타입이 아니라면, ok는 false가 되고, errInfo는 PasswordError 타입의 제로 값 (이 경우는 PasswordError{Len: 0, RequireLen: 0})이 된다.



Error Wrapping

Go 언어에서는 에러 랩핑(wrapping)을 통해 하위 에러에 대한 추가 컨텍스트를 제공하거나, 에러를 한 단계 더 추상화하여 상위 레벨의 에러를 생성할 수 있다.

%w 형식 지정자를 사용하여 fmt.Errorf를 통해 에러를 랩핑하고, errors.As 함수를 사용하여 랩핑된 에러를 확인할 수 있다.


1. %w를 사용한 에러 랩핑

2. errors.As 함수를 사용한 랩핑된 에러 확인

package main

import (
	"errors"
	"fmt"
)

// 사용자 정의 에러 타입.
type CustomError struct {
	msg string
}

func (e *CustomError) Error() string {
	return e.msg
}

func main() {
	// 하위 레벨에서 에러 생성
	err1 := &CustomError{"원본 에러"}

	// 에러 랩핑
	err2 := fmt.Errorf("랩핑된 에러: %w", err1)

	// 랩핑된 에러 출력
	fmt.Println(err2)

	// 랩핑된 에러에서 원본 에러 추출
	var errUnwrapped *CustomError
	if errors.As(err2, &errUnwrapped) {
		fmt.Println("추출된 원본 에러:", errUnwrapped)
	}
}



Panic

Go 언어에서 panic 함수는 프로그램이 회복할 수 없는 상태에 직면했을 때 사용된다.

panic을 호출하면 즉시 현재 함수의 실행을 멈추고, 콜 스택을 거슬러 올라가면서 각 함수에 대한 defer 스테이트먼트를 실행한다.

이러한 과정이 루트까지 계속되면, 프로그램은 에러 메시지와 함께 종료된다.


panic은 주로 예상치 못한 에러가 발생했을 때, 또는 정상적인 에러 처리로는 회복할 수 없는 상황에서 사용된다.

panic은 어떤 타입의 인자도 받을 수 있지만, 주로 문자열 또는 에러 타입을 전달하는 것이 일반적이다.



Panic 예제

panic(42)
panic("unreachable")
panic(fmt.Errorf("This is error num:%d , num)



Panic Propagation and Recovery

Go 언어에서 panic은 현재 함수의 실행을 즉시 중단하고, 함수의 호출 스택을 거슬러 올라가며 각 함수의 defer 구문을 실행한다.

이때 recover 함수를 사용하면 panic으로 인해 중단된 함수 호출 스택에서 프로그램의 제어를 회복할 수 있다.


recoverpanic에 의해 전달된 객체를 반환하며 이 객체는 interface{} 타입이므로,

실제 사용하기 위해서는 타입 단언(type assertion)이나 타입 스위치(type switch)를 사용하여 적절한 타입으로 변환해야 한다.


아래 예제 코드는 panicrecover를 사용하여 예외 상황을 처리하고, 발생한 패닉의 타입에 따라 다른 처리를 하는 방법을 보여준다.

package main

import (
	"fmt"
	"net"
	"os"
)

func main() {
	fmt.Println("Calling a function...")
	callFunction()
	fmt.Println("Returned normally from the function.")
}

func callFunction() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Recovered in callFunction:", r)
			// 타입 검사를 통해 특정 타입의 패닉에 대한 처리를 수행
			if _, ok := r.(net.Error); ok {
				fmt.Println("Recovered a net.Error in callFunction")
			} else if msg, ok := r.(string); ok {
				fmt.Println("Recovered a string error:", msg)
			} else {
				fmt.Println("Recovered unknown error")
			}
		}
	}()
}

✅ 보충 설명

defer func() { ... }()

이 부분은 익명 함수를 정의하고, 바로 실행한다.

defer 키워드는 이 함수가 callFunction 함수의 나머지 부분이 실행된 후, callFunction 함수가 리턴하기 직전에 실행되도록 한다.