0Pricing
Go Academy · 강의

오류 래핑과 언래핑

%w, errors.Is 및 errors.As 사용

오류 래핑과 언래핑은(는) CoddyKit의 무료 Go Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Go Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Go Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

오류 래핑이란 무엇인가요

오류 래핑은 새 오류 안에 원래 오류를 포함하는 방식입니다. 원래 오류를 프로그램에서 검사할 수 있도록 보존하면서 컨텍스트를 추가합니다. Go 1.13에서 정식으로 도입되었습니다:

package main
import ("fmt"; "errors")

var ErrDB = errors.New("database unreachable")

func queryUsers() error {
    return fmt.Errorf("queryUsers: %w", ErrDB)
}

func loadProfile(id int) error {
    return fmt.Errorf("loadProfile(%d): %w", id, queryUsers())
}

func main() {
    err := loadProfile(42)
    fmt.Println(err) // loadProfile(42): queryUsers: database unreachable
}

%w 동사

오류를 래핑하려면 fmt.Errorf에서 %v가 아닌 %w를 사용하세요. 래핑된 오류에는 errors.Unwrap으로 접근할 수 있습니다:

package main
import ("fmt"; "errors")

func main() {
    inner := errors.New("inner error")
    outer := fmt.Errorf("outer: %w", inner)

    fmt.Println(outer)              // outer: inner error
    fmt.Println(errors.Unwrap(outer)) // inner error
    fmt.Println(errors.Is(outer, inner)) // true
}

errors.Unwrap

errors.Unwrap은 체인의 다음 오류(래핑된 오류)를 반환하며, 다음 오류가 없으면 nil을 반환합니다:

package main
import ("fmt"; "errors")

type Layer struct{ msg string; err error }
func (l *Layer) Error() string { return l.msg }
func (l *Layer) Unwrap() error { return l.err }

func main() {
    root := errors.New("root")
    mid  := &Layer{"mid", root}
    top  := &Layer{"top", mid}

    fmt.Println(errors.Unwrap(top))  // mid
    fmt.Println(errors.Unwrap(mid))  // root
    fmt.Println(errors.Unwrap(root)) // <nil>
}

errors.Is — 체인 탐색

errors.Is(err, target)는 일치하는 오류를 찾거나 nil에 도달할 때까지 오류 체인을 재귀적으로 언래핑합니다:

package main
import ("fmt"; "errors")

var ErrNotFound = errors.New("not found")

func findItem(id int) error {
    return fmt.Errorf("findItem(%d): %w",
        id, fmt.Errorf("repo: %w", ErrNotFound))
}

func main() {
    err := findItem(7)
    fmt.Println(errors.Is(err, ErrNotFound)) // true — deep in chain
    fmt.Println(err)                         // full message chain
}

errors.As — 타입을 위한 체인 탐색

errors.As(err, &target)는 target의 타입에 할당할 수 있는 오류를 찾으며 체인을 탐색합니다:

package main
import ("fmt"; "errors")

type StatusError struct{ Code int }
func (e *StatusError) Error() string { return fmt.Sprintf("status %d", e.Code) }

func doRequest() error {
    return fmt.Errorf("request: %w", &StatusError{503})
}

func main() {
    err := doRequest()
    var se *StatusError
    if errors.As(err, &se) {
        fmt.Println("status code:", se.Code) // 503
    }
}

체인을 위한 사용자 지정 Unwrap

사용자 지정 오류 타입에 Unwrap() error를 구현하여 errors.Is와 errors.As를 지원하세요:

package main
import ("fmt"; "errors")

type AppError struct {
    Op  string
    Err error
}
func (e *AppError) Error() string  { return fmt.Sprintf("%s: %v", e.Op, e.Err) }
func (e *AppError) Unwrap() error  { return e.Err }

var ErrAuth = errors.New("unauthorized")

func main() {
    err := &AppError{"login", ErrAuth}
    fmt.Println(errors.Is(err, ErrAuth)) // true
}

fmt.Errorf에서의 %v와 %w 비교

%v와 %w의 차이는 다음과 같습니다:

package main
import ("fmt"; "errors")

func main() {
    inner := errors.New("inner")

    withV := fmt.Errorf("outer: %v", inner) // string only, no wrap
    withW := fmt.Errorf("outer: %w", inner) // wraps inner

    fmt.Println(errors.Is(withV, inner)) // false — not wrapped
    fmt.Println(errors.Is(withW, inner)) // true  — wrapped
}

실전 래핑: HTTP 핸들러

일관된 오류 래핑은 운영 시스템에서 디버깅을 더 쉽게 만듭니다:

package main
import "fmt"

func dbQuery(sql string) error {
    return fmt.Errorf("db.Query(%q): connection refused", sql)
}

func getUserByID(id int) (string, error) {
    if err := dbQuery("SELECT * FROM users WHERE id=?"); err != nil {
        return "", fmt.Errorf("getUserByID(%d): %w", id, err)
    }
    return "Alice", nil
}

func handler() error {
    _, err := getUserByID(1)
    if err != nil {
        return fmt.Errorf("handler: %w", err)
    }
    return nil
}

func main() { fmt.Println(handler()) }

errors.Join — 여러 오류 래핑(Go 1.20 이상)

errors.Join은 여러 오류를 래핑하는 오류를 만듭니다. errors.Is는 모든 오류를 확인합니다:

package main
import ("fmt"; "errors")

var ErrA = errors.New("err A")
var ErrB = errors.New("err B")

func main() {
    joined := errors.Join(ErrA, ErrB)
    fmt.Println(joined)
    fmt.Println(errors.Is(joined, ErrA)) // true
    fmt.Println(errors.Is(joined, ErrB)) // true
}

언래핑 모범 사례

오류 래핑 지침:

  • 프로그램에서 검사할 수 있도록 오류를 보존하려면 %w로 래핑하세요
  • 컨텍스트를 위해 작업 이름을 추가하세요: fmt.Errorf("funcName: %w", err)
  • 호출자가 errors.Is를 사용하지 않는다면 센티널 오류를 래핑하지 마세요
  • 모든 수준에서 같은 컨텍스트를 이중으로 래핑하지 마세요
  • 체인에 참여하도록 사용자 지정 타입에 Unwrap()을 구현하세요

빠른 확인

fmt.Errorf에서 %v와 %w를 사용하는 것의 차이는 무엇인가요?

복습: 오류 래핑과 언래핑

요약:

  • fmt.Errorf("ctx: %w", err)는 컨텍스트와 함께 오류를 래핑합니다
  • errors.Unwrap은 체인의 다음 오류를 가져옵니다
  • errors.Is는 동일한 오류를 찾기 위해 체인을 탐색합니다
  • errors.As는 일치하는 타입을 찾기 위해 체인을 탐색합니다
  • 사용자 지정 타입은 Unwrap() error를 구현합니다
  • errors.Join은 여러 오류를 래핑합니다(Go 1.20 이상)

자주 묻는 질문

“오류 래핑과 언래핑” 강의는 무료인가요?

네 — “오류 래핑과 언래핑” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Go Academy 강의 전체를 잠금 해제할 수 있습니다. Go Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“오류 래핑과 언래핑”에서 뭘 배우나요?

%w, errors.Is 및 errors.As 사용 브라우저에서 직접 실행하는 실습 코드로 Go Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Go Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Go Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“오류 래핑과 언래핑” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Go Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Go Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. error 인터페이스
  2. 사용자 지정 오류 만들기
  3. 오류 래핑과 언래핑
  4. panic, recover 및 defer
← Go Academy(으)로 돌아가기