에러 래핑 (fmt.Errorf)
Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 107개 중 54번째.
에러가 애플리케이션의 여러 계층을 거칠 때, 에러가 어디에서 발생했는지 아는 것이 매우 중요해집니다. %w 동사를 사용하는 Go의 fmt.Errorf 함수를 사용하면 원본 에러를 보존하면서 컨텍스트를 추가하여 에러를 래핑(wrap)할 수 있습니다.
에러 래핑(Error wrapping)은 에러 체인을 생성합니다. 각 계층은 에러가 발생했을 때 무엇을 하려고 했는지에 대한 정보를 추가할 수 있습니다:
func ReadConfig(filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("reading config file: %w", err)
}
// 데이터를 처리합니다...
return nil
}%w 서식 지정자는 특별합니다. 원본 에러를 새로운 에러 내부에 감싸기(wrap) 때문입니다. 이는 에러를 단순히 문자열로 변환하여 원본 에러의 정체성을 잃게 만드는 %v와는 다릅니다.
func LoadSettings() error {
err := ReadConfig("settings.json")
if err != nil {
return fmt.Errorf("loading settings: %w", err)
}
return nil
}LoadSettings가 실패하면 에러 메시지는 전체 체인을 보여줍니다: "loading settings: reading config file: open settings.json: no such file or directory". 각 레이어는 컨텍스트를 추가하여 디버깅을 훨씬 쉽게 만듭니다.
래핑된 에러는 여전히 그 안에 원본 에러를 포함하고 있습니다. 다음 레슨에서는 errors.Is()와 errors.As()를 사용하여 이러한 에러를 언래핑하고 내부 내용을 확인하는 방법을 배우게 됩니다.
챌린지
쉬움여러 계층에서 에러 래핑(error wrapping)을 시연하는 주문 처리 시스템을 구축해 보겠습니다. 각 계층이 에러에 컨텍스트를 추가하는 함수 체인을 만들어, 문제가 발생한 위치를 정확히 추적하기 쉽게 만들 것입니다.
코드는 두 개의 파일로 구성됩니다:
orders.go: 에러가 위로 전파될 때 에러를 래핑하는 여러 계층을 가진 주문 처리 로직을 작성합니다.처리 체인을 형성하는 세 가지 함수를 구현하세요:
ValidateItem(itemID string) error- itemID가"INVALID"인 경우"item not found"라는 메시지와 함께 에러를 반환하고, 그렇지 않으면nil을 반환합니다.ProcessOrder(orderID, itemID string) error-ValidateItem을 호출합니다. 에러가 반환되면"processing order [orderID]: %w"컨텍스트로 래핑합니다. 그렇지 않으면nil을 반환합니다.SubmitOrder(customerName, orderID, itemID string) error-ProcessOrder를 호출합니다. 에러가 반환되면"submitting order for [customerName]: %w"컨텍스트로 래핑합니다. 그렇지 않으면nil을 반환합니다.
각 계층은
fmt.Errorf와%w동사를 사용하여 하위 계층의 에러를 래핑하고 컨텍스트 체인을 구축해야 합니다.main.go: 입력에서 주문 세부 정보를 읽고,SubmitOrder를 호출한 후 결과를 표시합니다. 에러가 발생하면 전체 에러 체인을 출력합니다. 성공하면 확인 메시지를 출력합니다.
다음 입력이 제공됩니다:
- Line 1: 고객 이름 (Customer name)
- Line 2: 주문 ID (Order ID)
- Line 3: 아이템 ID (Item ID)
결과를 출력하세요:
- 에러가 발생한 경우:
Error: [full error chain] - 성공한 경우:
Order [orderID] submitted successfully for [customerName]
예를 들어, Alice, ORD-123, INVALID가 주어지면 출력은 다음과 같아야 합니다:
Error: submitting order for Alice: processing order ORD-123: item not found에러 메시지가 전체 체인을 어떻게 보여주는지 확인하세요. 최상위 제출 시도부터 주문 처리 과정을 거쳐 실제 유효성 검사 실패까지 문제를 추적할 수 있습니다. 각 계층은 %w를 사용하여 자체 컨텍스트를 추가했습니다.
그리고 Bob, ORD-456, ITEM-001이 주어지면 출력은 다음과 같아야 합니다:
Order ORD-456 submitted successfully for Bob치트 시트
fmt.Errorf와 %w 동사를 사용하여 원래의 에러를 보존하면서 에러를 감싸십시오(wrap):
func ReadConfig(filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("reading config file: %w", err)
}
return nil
}%w 동사는 원래의 에러를 새로운 에러 안에 감쌉니다. 에러를 문자열로만 변환하여 원래 에러의 정체성을 잃게 만드는 %v와는 다릅니다.
에러 래핑(Error wrapping)은 각 레이어가 컨텍스트를 추가하는 체인을 생성합니다:
func LoadSettings() error {
err := ReadConfig("settings.json")
if err != nil {
return fmt.Errorf("loading settings: %w", err)
}
return nil
}결과 에러 메시지는 전체 체인을 보여줍니다: "loading settings: reading config file: open settings.json: no such file or directory"
직접 해보기
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// 고객 이름 읽기
scanner.Scan()
customerName := scanner.Text()
// 주문 ID 읽기
scanner.Scan()
orderID := scanner.Text()
// 품목 ID 읽기
scanner.Scan()
itemID := scanner.Text()
// TODO: 입력된 값으로 SubmitOrder 호출
// TODO: 오류가 발생하면 다음을 출력: Error: [전체 오류 체인]
// TODO: 성공하면 다음을 출력: Order [orderID] submitted successfully for [customerName]
_ = customerName
_ = orderID
_ = itemID
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
8에러 처리와 OOP
error 인터페이스사용자 정의 에러 타입에러 래핑 (fmt.Errorf)센티넬 에러errors.Is()와 errors.As()Panic, Defer, Recover요약 - 파일 파서