프로그래밍백엔드 개발자

Go에서 오류 처리는 어떻게 구현되었으며, Go가 자신의 오류 처리 모델을 선택한 이유와 일상 프로그래밍에서의 오류 사용에 대한 모범 사례는 무엇인가요?

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

답변.

문제의 역사:

Go는 오류가 예외가 아닌 값으로 처리되는 점에서 많은 언어와 다릅니다. 이러한 디자인은 투명성과 단순성을 위해 선택되었습니다. 무언가 잘못될 수 있는 경우 함수는 오류를 명시적으로 두 번째 반환 값으로 반환합니다.

문제:

많은 초보자들이 Go에 익숙한 try-catch 패턴을 적용하려 해 혼란스러워 하거나, 많은 오류 확인에 지치거나, 오류를 감싸서 컨텍스트를 전달하지 않곤 합니다. 이는 정보 손실과 디버깅의 어려움으로 이어집니다.

해결책:

Go에서는 오류를 반환할 수 있는 함수가 다음과 같이 선언됩니다:

func doSomething() (ResultType, error) { // ... if somethingWrong { return nil, errors.New("뭔가 잘못되었습니다") } return result, nil }

오류 검사 — 호출하는 측의 의무:

res, err := doSomething() if err != nil { log.Fatalf("프로세스 실패: %w", err) }

Go 1.13 이상에서는 오류를 fmt.Errorf("%w", err)로 "감싸서" 오류 체인을 구축할 수 있습니다. 이는 진단을 향상시키고 오류의 컨텍스트 설명에 대한 모범 사례를 촉진합니다.

주요 특징:

  • 오류는 함수가 반환하는 값입니다 (try/catch 없음)
  • 사용자 정의 오류 유형을 생성할 수 있습니다 (error 인터페이스 사용)
  • 오류 감싸기는 디버깅 및 로깅을 훨씬 투명하게 만듭니다.

함정 질문.

왜 오류를 그냥 errors.New("...")로 반환하는 대신 사용자 정의 오류 유형을 만들까요?

정확한 대답: 사용자 정의 오류 유형은 오류 메시지를 전달할 뿐만 아니라 향후 처리에 유용한 추가 정보를 저장할 수 있게 하며(예: 상태 코드, 컨텍스트), type assertion을 통해 더 세밀한 처리를 구현할 수 있습니다.

예시:

type NotFoundError struct { Resource string } func (e NotFoundError) Error() string { return fmt.Sprintf("%s를 찾을 수 없습니다", e.Resource) } // 체크 if _, ok := err.(NotFoundError); ok { // 검색 오류 처리 }

panic이 치명적인 오류에 대한 좋은 대체가 될까요?

아니요, Go에서 panic은 실제로 막다른 상황에서만 사용되며 — 프로그램 자체의 오류(프로그래밍 불변성)와 같은 — 일반적인 실패(예: 파일을 열 수 없음)에 대해서는 사용되지 않아야 합니다. 루틴 오류의 신호로 panic을 사용하면 읽기 어려운, 비관리 코드로 이어집니다.

내부 함수에서 오류 처리 (err)를 return하지 않으면 어떻게 되나요?

오류가 "사라지게" 되고, 코드는 계속 실행되어 잘못된 상태가 퍼질 수 있습니다. 모든 오류 반환을 올바르게 처리하는 것이 항상 중요합니다.

일반적인 오류 및 안티 패턴

  • _ = ...를 사용하여 반환된 오류 무시
  • 오류 대신 panic/recover를 사용하여 흐름 제어
  • 컨텍스트 메시지 없이 전송되지 않은 오류를 리다이렉션

일상적인 예

부정적 사례

각 함수는 단순히 errors.New(...)를 반환하고, 오류가 감싸이지 않으며, 오류 유형이 다르지만 처리는 항상 동일합니다 — 로깅되고 중단됩니다. 결과적으로 로그 파일은 비정보적인 메시지로 가득 차고, 원인 추적이 불가능합니다.

장점:

  • 코드 작성이 빠름
  • 처리 코드가 적음

단점:

  • 잘못된 추적
  • 유형별로 오류를 필터링하거나 구분할 수 없음

긍정적 사례

fmt.Errorf("%w", err)를 통한 오류 감싸기, 유용한 필드를 가진 사용자 정의 오류 사용, errors.Is()/errors.As()를 통한 검사를 사용합니다. 이를 통해 디테일한 로깅이 가능하고, 비즈니스 오류와 환경 실패를 분리하며 신뢰할 수 있는 재시도 로직을 작성할 수 있습니다.

장점:

  • 편리한 디버깅
  • 더 깔끔한 코드
  • 유연한 오류 처리

단점:

  • 더 많은 코드와 템플릿 로직
  • 오류 유형을 관리해야 함