지금까지 여러 용법을 학습하고 그에 따른 실습 문제까지 작성해왔습니다. 실습 문제의 목표는 각 챕터에서 배운 용법을 직접 활용해보는 것이었기 때문에 예외 처리하는 부분에서 허점이 많이 있었습니다. 예를 들어, 사용자로부터 시험 과목 수를 입력받을 때 음수를 입력하면 안되지만 따로 예외 처리를 하지 않았기 때문에 음수를 입력해도 에러가 발생하지 않습니다. 그래서 이번 강의에서 에러 처리에 대해 학습하고, 프로그램을 만들면서 함수의 반환값으로 결괏값와 함께 에러 값을 반환해 로직상의 예외를 처리할 수 있도록 합니다.
에러 처리를 하는 이유는 컴파일러가 알아차리지 못하는 프로그램상의 오류를 예방하기 위해서입니다. 따라서 반환값이 있는 함수는 에러 처리(논리상 예외가 있을만한 부분을 에러 처리)를 통해 결괏값과 에러 값을 함께 반환해야합니다. 우리가 함수를 직접 만들고 에러 처리를 직접 해보기 전에 Go언어 패키지에서 제공하는 표준 함수는 어떻게 에러 처리를 하는지 알아보겠습니다. Go언어 실습시 가장 기본 틀로 제공되는 코드를 살펴보겠습니다. 우리가 항상 무슨 의미인지 모르고 지나갔던 코드가 에러 처리가 되어있는 코드입니다.
아래 지금까지 많이 봤지만 무슨 의미인지 몰랐던 예시를 한 번 실행해보세요.
아무것도 입력하지 않으면 예외 처리를 통해 에러(panic)를 발생시킵니다.
에러 처리에 대해 알기 전에 위 예시에서 의아한 부분이 있을 것입니다. 바로 fmt.Scanln(&input)
을 변수에 초기화한 것입니다. 우리는 지금까지 fmt.Scanln()
함수를 사용자로부터 값을 입력받는 용도로만 사용해왔습니다. 그래서 이 함수가 반환값이 없는 것으로 알고 있었습니다. 하지만 Scanln() 함수를 포함한 "fmt" 패키지의 표준 입/출력 함수는 아래와 같이 모두 반환 값을 가집니다. 그리고 에러 처리를 위한 에러 값도 반환합니다.
위 예시 코드에서 쓰인 Scanln() 함수는 두 개의 반환값을 가지는데 첫 번째 입력 개수, 두 번째 반환값은 에러 값입니다. Scanln() 함수는 입력한 변수만큼 입력하지 않았을 때 에러 값을 반환합니다. 따라서 _, err := fmt.Scanln(&input);
부분에서 입력 개수는 생략하고, err
변수에 에러 값을 반환 받습니다. 만약에 사용자가 개수만큼 입력하면 에러 값을 반환하지 않아 err
는 nil
이 됩니다. 따라서 다음에 에러 검사를 하는 부분인 if err != nil {
을 지나갑니다. 하지만 입력 개수만큼 입력하지 않아 에러 값을 반환 받는다면 if문에 panic(err)
구문을 실행해 에러 값을 출력하고 panic이 발생합니다.
아래 예시는 다양한 표준 함수들이 반환 값이 존재한다는 것정도로 확인하기 바랍니다.
"fmt" 패키지에서 제공하는 함수입니다.(전부는 아님)
- func Scan(a ...interface{}) (n int, err error)
- func Scanln(a ...interface{}) (n int, err error)
- func Scanf(format string, a ...interface{}) (n int, err error)
- func Print(a ...interface{}) (n int, err error)
- func Println(a ...interface{}) (n int, err error)
- func Printf(format string, a ...interface{}) (n int, err error)
- func Fprint(w io.Writer, a ...interface{}) (n int, err error)
- func Fprintln(w io.Writer, a ...interface{}) (n int, err error)
- func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)
- func Fscan(r io.Reader, a ...interface{}) (n int, err error)
- func Fscanln(r io.Reader, a ...interface{}) (n int, err error)
아래는 "os" 패키지에서 제공하는 파일 처리 함수입니다.(전부는 아님)
- func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error)
- func Create(name string) (file *File, err error)
- func Open(name string) (file *File, err error)
모든 함수의 상세 정보를 알고싶다면 Go언어 공식 사이트에서 패키지를 확인하세요.
- func (f *File) Close() error
지금까지 Go언어 자체에서 제공하는 표준 패키지의 함수들로 에러 처리를 어떤 방식으로 하는지 알아보았습니다. 사실 이러한 표준 함수들은 에러 상황과 값을 이미 구현했기 때문에 상황에 따른 에러 값을 반환 받고 if err != nil {
과 같은 조건문으로 에러 값을 출력하고 에러 처리를 하면 됩니다. 따라서 개발자가 만든 함수에서 에러 처리를 할 때는 에러 상황을 직접 정의하고 에러 값도 직접 지정해야합니다.
사용자에게 두 실수를 입력받고 나눗셈을 하는 프로그램을 만든다고 생각해봅시다. 이때, 나누는 수를 0으로 하면 무한대가 되기 때문에 에러 처리를 하도록 하고싶습니다. 그럼 두 가지를 설정해야합니다.
- 어떻게 에러 값을 설정할 것인가?
- 어떻게 에러 상황을 출력하고 처리할 것인가?
이것들을 설정하기 위해 우선 "어떻게 에러 값을 설정할 것인가?"에 대한 답을 알아보겠습니다.
에러 값 설정
에러 값을 설정하기에 앞서 func Scanln(a ...interface{}) (n int, err error)
에서 볼 수 있듯이, error
타입이라는 것이 무엇인지 알아야합니다. error
는 우리가 일반적으로 생각하는 반환형인 int
, float32
, string
과 같은 타입은 아닙니다. error
형은 '인터페이스' 챕터에서 배운 인터페이스형입니다. error
인터페이스는 Error()
라는 string형을 반환값으로 갖는 메소드를 한개만 가지고 있습니다.
Error()
메소드의 원형이 있습니다. 이 메소드의 receiver 부분을 보면 구조체 errorString의 주소에 직접 접근해서 필드값 s를 반환합니다.