프로그래밍백엔드 개발자

Go에서 error wrapping 패키지를 통해 오류 처리 작업이 어떻게 구성되어 있는지 설명해 주세요: 오류 wrapping이란 무엇이며, 이것이 중요한 이유와 오류 중첩을 올바르게 구현하는 방법은 무엇인가요?

Hintsage AI 어시스턴트로 면접 통과

답변.

질문 배경:

Go 1.13 이전에는 오류가 단순한 인터페이스였습니다. 오류에 대한 추가적인 컨텍스트를 제공하기 위해 종종 사용자 정의 유형을 만들었지만, modern Java 또는 C#에서와 같은 구조화된 중첩 메커니즘은 존재하지 않았습니다. Go 1.13에서는 fmt.Errorf 함수를 통한 오류 wrapping의 표준 방법과 근본 원인을 식별하는 방법(errors.Is/errors.As)이 도입되었습니다.

문제:

오류가 스택의 여러 계층에서 발생할 수 있는 복잡한 애플리케이션에서는 오류를 코드 깊숙한 곳에서 반환할 때 컨텍스트를 잃지 않는 것이 중요합니다. 그렇지 않으면 디버깅이 어려워지고 체인 내에서 실패가 발생한 위치를 이해할 수 없게 됩니다.

해결 방법:

Go는 원인을 포함하는 새로운 오류 객체에 오류를 감싸는 것을 제안합니다. 이를 위해 fmt.Errorf%w를 사용하고, 심층적 원인을 확인하기 위해 errors.Is 또는 errors.Aserrors 패키지에서 사용합니다.

코드 예제:

import ( "errors" "fmt" ) var ErrNotFound = errors.New("not found") func getData() error { return fmt.Errorf("service database: %w", ErrNotFound) } func main() { err := getData() if errors.Is(err, ErrNotFound) { fmt.Println("원인 발견: 찾을 수 없음") } else { fmt.Println("다른 오류:", err) } }

주요 특징:

  • fmt.Errorf를 통해 오류를 중첩할 때 %w 사용.
  • errors.Iserrors.As를 통한 중첩 오류 원인 식별.
  • 중첩 및 unwrapping 시 사용자 정의 오류 유형과의 호환성.

트릭 질문.

fmt.Errorf를 사용한 오류 wrapping에 사용해야 하는 포맷팅 연산자는 무엇인가요?

정확히 %w를 사용해야 하며, %v를 사용하면 안 됩니다 — %w만이 unwrapping 지원을 제공합니다.

코드 예제:

fmt.Errorf("오류: %w", err)

fmt.Errorf 없이 수동으로 오류 체인을 만들고 여전히 errors.Is를 통해 식별할 수 있나요?

아니요, Unwrap 인터페이스를 구현해야 합니다. 그렇지 않으면 표준 함수들이 체인을 펼칠 수 없습니다.

인터페이스 코드 예제:

type wrappedError struct { msg string err error } func (w wrappedError) Error() string { return w.msg + ": " + w.err.Error() } func (w wrappedError) Unwrap() error { return w.err }

중첩된 오류가 nil이면 wrapping 후 오류가 nil인가요?

아니요, 중첩 오류가 nil인 경우, 적절히 사용하면 wrapping된 오류도 nil입니다. 하지만 오류 필드가 nil인 래퍼 구조체를 수동으로 만들면 nil이 아니게 됩니다. 이는 확인할 때 혼란을 자주 초래합니다.

일반적인 오류 및 안티 패턴

  • %w가 아니라 %v 사용 — 체인 분석의 가능성을 잃게 됩니다.
  • 사용자 정의 오류 구조체에 대해 Unwrap()을 구현하지 않음.
  • errors.Is/As를 사용하지 않고 오류를 직접 비교함.
  • 원인 없는 새로운 오류 생성 (컨텍스트 상실).

실생활 예제

부정적 사례

애플리케이션에서 각 레벨마다 텍스트로 새로운 오류를 단순히 반환했습니다:

return errors.New("데이터베이스에 쓰기 오류")

장점:

  • 간단하고 빠릅니다.

단점:

  • 사실상 오류가 심층에서 오는 "not found"임을 구별할 수 없으며, 상위 비즈니스 논리를 깨뜨리게 됩니다.
  • 스택 및 오류 출처에 대한 정보 손실.

긍정적 사례

%w로 오류를 wrapping하고 errors.Is를 통해 분석한 경우:

if errors.Is(err, ErrNotFound) { return fmt.Errorf("서비스 레벨의 오류: %w", err) }

장점:

  • 어느 수준에서든 원인을 정확하게 식별합니다.
  • 디버깅이 용이하며 항상 원래의 컨텍스트가 보입니다.

단점:

  • wrapping의 작동 원리에 대한 이해와 올바른 오류 작성을 요구합니다.