
Go 언어를 사용하면서 가장 먼저 낯설게 느껴지는 부분 중 하나는 에러 처리 방식입니다.
특히 Python이나 JavaScript에 익숙한 개발자라면 Go의 에러 처리가 다소 생소하게 느껴질 수 있습니다.
하지만 Go의 철학을 이해하고 나면 이 방식이 얼마나 명확하고 예측 가능하게 설계되었는지를 알 수 있습니다.
예외를 사용하지 않고, 모든 것을 값으로 처리하는 방식은 코드의 안정성과 가독성을 높이는 데 많은 도움이 됩니다.
go error는 값
Go 언어에서 에러는 단순히 오류가 발생했음을 나타내는 객체가 아니라, 값입니다.
go error
타입은 특별히 정의된 내장 인터페이스로, 에러를 처리하는 방식에서 중요한 역할을 합니다.
이 인터페이스는 다음과 같이 정의되어 있습니다.
type error interface {
Error() string
}
즉, 에러는 단순히 Error()
메서드를 가진 어떤 값일 뿐, 에러를 처리할 때 반드시 이 메서드를 통해 오류 메시지를 확인할 수 있습니다.
중요한 점은, Go에서는 이 error
인터페이스를 만족하는 타입이라면 어떤 타입이든지 에러로 처리할 수 있다는 점입니다.
이로 인해 Go에서는 타입이 매우 유연하게 에러를 표현할 수 있습니다.
함수의 반환값으로 에러 처리
Go에서는 예외(exception)를 사용하지 않습니다.
대신 에러 처리는 명시적인 값 반환 방식으로 이뤄집니다.
이는 코드의 흐름을 보다 직관적으로 만들며, 런타임 시 발생할 수 있는 예외 상황을 함수 수준에서 직접 다루도록 유도합니다.
이러한 방식은 특히 대규모 시스템에서 예외로 인한 흐름 왜곡을 방지하고, 예측 가능한 구조를 유지하는 데 큰 도움이 됩니다.
대신 대부분의 함수는 결과와 함께 에러를 반환합니다.
예를 들어 파일을 여는 함수는 다음과 같은 형태로 작성됩니다.
file, err := os.Open("myfile.txt")
if err != nil {
log.Fatal(err)
}
여기서 os.Open()
은 두 개의 값을 반환합니다.
첫 번째는 열려진 파일 객체이고, 두 번째는 에러 객체입니다.
에러가 발생하지 않았다면 err
는 nil
이 됩니다.
이 방식은 에러를 명시적으로 처리하게 만들며, 코드의 흐름을 예측하기 쉽게 해줍니다.
커스텀 에러 생성
Go는 표준 패키지에서 제공하는 errors.New
나 fmt.Errorf
를 사용하여 새로운 에러를 생성할 수 있습니다.fmt.Errorf
는 특히 Go 1.13에서 도입된 %w
구문을 통해 에러를 wrapping 할 수 있도록 해줍니다.
이는 에러 체인(error chain)을 따라가며 원인 에러를 추적할 수 있게 해줍니다.
import (
"errors"
"fmt"
)
var ErrSomething = errors.New("무언가 잘못되었습니다")
func doSomething() error {
return fmt.Errorf("작업 실패: %w", ErrSomething)
}
이렇게 생성된 에러는 fmt.Errorf
를 통해 더 많은 정보를 포함하게 되며,
후속 작업에서 에러의 원인을 추적할 때 유용하게 사용됩니다.
에러 비교와 추출
Go 1.13 이후로는 errors.Is()
와 errors.As()
함수가 도입되어 에러를 보다 쉽게 처리하고 분석할 수 있게 되었습니다.
이 함수들은 복잡한 에러 처리에서 중요한 역할을 합니다.errors.Is()
는 에러가 특정 값인지 확인하고, errors.As()
는 에러가 특정 타입으로 변환될 수 있는지를 확인합니다.
예를 들어, 다음과 같이 errors.Is()
와 errors.As()
를 활용할 수 있습니다.
if errors.Is(err, ErrSomething) {
fmt.Println("특정 에러 발생")
}
var pathErr *os.PathError
if errors.As(err, &pathErr) {
fmt.Println("경로 관련 에러:", pathErr.Path)
}
에러 처리의 철학
Go의 창시자인 Rob Pike는 “에러는 다루어져야 합니다(errors are values to be handled)”라고 말한 바 있습니다.
이는 ‘Errors are values’에서 강조된 내용으로, Go 언어의 설계 철학을 잘 보여주는 예시입니다.
이처럼 에러를 값으로 다룬다는 접근 방식은 예외를 통해 비정상적인 흐름 전환을 하는 것보다 더 명시적이고 예측 가능한 코드 흐름을 유지하는데 도움이 됩니다.
예외를 통한 비정상적인 흐름 전환보다는, 각 단계에서 예상 가능한 방식으로 에러를 확인하고 처리하는 것이 Go의 철학입니다.
필자는 이 방식이 단순함 속에서 오는 견고함을 제공한다고 생각한다.
코드가 더 명시적이고, 예측 가능한 흐름을 유지할 수 있다.
협업이나 디버깅 시에 오히려 더 편리하다고 생각한다.
물론 코드 라인이 늘어나기는 하지만, 그만큼 프로그램의 안정성과 가독성이 향상된다고 생각한다.
Go 언어에서의 에러 처리 방식은 단순하지만 강력합니다.
예외를 사용하지 않고 명시적으로 에러를 처리함으로써, 코드의 흐름을 더 예측 가능하고 안정적으로 유지할 수 있습니다.
Go의 에러 처리 방식은 다른 언어에서의 예외 처리 방식과 비교했을 때 더욱 직관적이고 명확하며, 프로그램의 안정성을 보장하는 데 중요한 역할을 합니다.
프로그램을 작성할 때, 에러는 피할 수 없는 부분입니다.
따라서 Go의 에러 처리 방식은 그 자체로 중요한 학습 요소이며, 이를 잘 활용하면 더 견고한 코드를 작성할 수 있을 것입니다.