golang은 예외처리방법이 조금은 특이합니다.
제가 접한 언어들은 모두 try catch(c#, java, javascript)나 try except(python) 같은 형태로 예외를 잡았지만,
golang은 try catch가 없으며, error라는 인터페이스를 이용하여 예외처리를 하는게 컨벤션입니다.
이번게시글에서 정리해보겠습니다.
예외처리란
프로세스는 예외가 발생하면 콜스택에 예외가 쌓이게 되고 결국 프로세스가 뻗어 되어버립니다.
예외처리를 왜하는지 모른다면 아래게시글을 참고해주세요
c#으로 되어있지만 try catch의 사용이유를 직접 확인할수 있는 게시글입니다.
https://frozenpond.tistory.com/21
사용자가 회원가입을 요청했는데 널포인트 익셉션이 발생했다고 서버가 죽어버리면 안되겠죠..(웹서버로 예를 들었지만 웹서버의 웬만한 예외처리는 웹 프레임워크가 다 해주며, 익셉션처리가 안되있으면 컴파일이 안되게 막기도 합니다.)
예외만들기
package main
import "fmt"
func main() {
result := divider(1, 0)
fmt.Println("결과는:", result)
fmt.Println("프로세스 끝") // 이 문자열은 꼭 출력해야합니다.
}
func divider(a int, b int) int {
response := a / b
return response
}
0으로 나눠서 예외를 발생시킵니다.
$ go run main.go
panic: runtime error: integer divide by zero
goroutine 1 [running]: main.divider(...)
C:/Users/user/develop/go/main.go:12
main.main()
C:/Users/user/develop/go/main.go:6 +0x12
exit status 2
처리하지 않은 예외가 발생하여 panic을 뱉어버리며 프로그램이 죽어버립니다.
프로세스 끝 이라는 문자열또한 출력되지 않았습니다.
예외처리하기
package main
import (
"errors"
"fmt"
)
func main() {
result, err := divider(1, 0)
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println("결과는:", result)
}
fmt.Println("프로세스 끝")
}
func divider(a int, b int) (int, error) {
if b == 0 {
return -1, errors.New("0으로 나눌수 없습니다.")
}
response := a / b
return response, nil
}
$ go run main.go
0으로 나눌수 없습니다.
프로세스 끝
divider의 리턴값에 error인터페이스를 추가합니다.
에러가 발생하면 erros.New로 에러를 생성, 포매팅해주고
해당 메서드를 불러온 스코프에서 err.Error()로 접근합니다.
내가알던 예외처리는 이게아닌데... 일일이 잡아야한다고?
컨벤션은 error인터페이스로 에러를 모두 잡고, 모듈을 가져다 쓸때도 리턴값으로 err를 리턴받도록 약속하였습니다.
대표적인 모듈인 fmt.println() 을 확인해보겠습니다.
// Println formats using the default formats for its operands and writes to standard output.
// Spaces are always added between operands and a newline is appended.
// It returns the number of bytes written and any write error encountered.
func Println(a ...interface{}) (n int, err error) {
return Fprintln(os.Stdout, a...)
}
이렇게 생겼습니다.
주석의 마지막줄에 number of bytes를 리턴해주고, error를 리턴해준다고 되어있습니다.
매개변수로는 a ...interface{} 가 들어가고 n과 error를 리턴해줍니다.
(a ...interface{} 는 타입상관없는 데이터를 갯수 상관없이 받겠다는 의미입니다.)
package main
import (
"fmt"
)
func main() {
result, err := fmt.Println("test")
if err != nil {
fmt.Println(err.Error())
}
fmt.Println(result)
}
$ go run main.go
test
5
예외가 발생할 수 있는 모듈은 두번째 리턴값으로 error인터페이스를 리턴해주는게 약속입니다.
(5byte는 4개의 아스키byte에(test) 줄바꿈아스키가 추가된 값 입니다.)
모든 error를 잡아 err 인터페이스를 채워 두번째 인자로 내려줘야합니다.
하지만 사람이 만들다보니 panic(exception)을 피할수는 없습니다.
goalng은 panic(exception)이 발생하는경우 복구하는 방법을 제공합니다.(defer와 recover)
https://frozenpond.tistory.com/147
타모듈을 사용하는경우 외부 스코프에서 예외처리하는 방법
package main
import (
"fmt"
)
func main() {
result, err := myPrinter("test")
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(result, "바이트를 출력했습니다.")
}
func myPrinter(text string) (int, error) {
result, err := fmt.Println(text)
if err != nil {
return -1, err
}
return result, nil
}
$ go run main.go
test
5 바이트를 출력했습니다.
어거지로 만든감이 없지않습니다만.. myPrinter는 외부모듈을 사용하는 메서드라는것만 생각하시면 됩니다.
golang의 모듈들은 모두 error라는 인터페이스를 리턴하도록 되어있습니다.
myPrinter라는 메서드는 외부모듈의 사용 결과와 예외를 리턴해주는 메서드입니다.
myPrinter를 불러올때는 이런식으로 예외를 처리하고 상위 스코프에서
결과와 예외를 처리해주면 깔끔합니다.
'golang' 카테고리의 다른 글
[golang] 고루틴(go routine)이란 (0) | 2021.11.15 |
---|---|
[golang] golang defer 사용법 및 예제(golang의 catch는 defer안에서) (0) | 2021.11.15 |
[golang] go언어를 객체지향언어처럼 사용하는방법 (0) | 2021.08.16 |
[golang] go언어의 의존성 주입(di)과 제어의 역전(ioc) (0) | 2021.08.15 |
[golang] golang echo framework와 layered architecture를 활용한 백엔드 api 서버 구축하기 (0) | 2021.07.14 |
댓글