Menu
Coddy logo textTech

errors.Is()와 errors.As()

Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 107개 중 56번째.

에러가 래핑되면, err == ErrNotFound와 같은 단순한 비교 확인은 더 이상 작동하지 않습니다. errors 패키지는 래핑된 에러 체인을 조사하기 위한 두 가지 함수인 errors.Is()errors.As()를 제공합니다.

errors.Is()는 에러 체인 내의 어떤 에러가 특정 센티넬 에러(sentinel error)와 일치하는지 확인합니다:

import "errors"

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

func FindUser(id int) error {
    return fmt.Errorf("database lookup failed: %w", ErrNotFound)
}

err := FindUser(42)
if errors.Is(err, ErrNotFound) {
    fmt.Println("User doesn't exist")
}

반환된 에러가 래핑되어 있더라도, errors.Is()는 체인을 언래핑하여 내부에서 ErrNotFound를 찾습니다.

errors.As()는 에러 체인 내의 에러가 특정 타입과 일치하는지 확인하고 이를 추출합니다:

type ValidationError struct {
    Field string
}

func (e *ValidationError) Error() string {
    return "validation failed: " + e.Field
}

err := fmt.Errorf("request failed: %w", &ValidationError{Field: "email"})

var ve *ValidationError
if errors.As(err, &ve) {
    fmt.Println("Invalid field:", ve.Field)
}

센티넬 에러를 확인할 때는 errors.Is()를 사용하고, 사용자 정의 에러 타입의 필드에 접근해야 할 때는 errors.As()를 사용하세요. 두 함수 모두 전체 에러 체인을 순회하므로, 래핑된 에러를 올바르게 처리하는 데 필수적입니다.

challenge icon

챌린지

쉬움

errors.Is()errors.As()를 사용하여 래핑된 에러 체인을 올바르게 검사하는 방법을 보여주는 파일 처리 시스템을 구축해 보겠습니다. 여러 레이어를 거치면서 래핑되는 에러를 생성한 다음, 이러한 함수들을 사용하여 에러를 식별하고 정보를 추출해 볼 것입니다.

코드는 두 개의 파일로 구성됩니다:

  • errors.go: 파일 시스템을 위한 에러 인프라를 정의합니다.

    센티널 에러(sentinel error)를 생성하세요:

    • 메시지가 "permission denied"ErrPermissionDenied

    사용자 정의 에러 타입을 생성하세요:

    • Filename (string)과 Operation (string) 필드를 가진 FileError 구조체
    • 이 구조체의 Error() 메서드는 [Operation] failed on file: [Filename] 형식을 반환해야 합니다.

    파일 작업을 시뮬레이션하고 에러를 래핑하는 두 개의 함수를 구현하세요:

    • ReadFile(filename string) error - 만약 filename이 "secret.txt"라면, ErrPermissionDenied를 반환합니다. 만약 filename이 "missing.txt"라면, Operation은 "read"이고 해당 Filename을 가진 *FileError를 반환합니다. 그 외의 경우에는 nil을 반환합니다.
    • ProcessFile(filename string) error - ReadFile을 호출합니다. 에러가 반환되면, fmt.Errorf를 사용하여 "processing failed: %w" 형식으로 래핑하여 반환합니다. 에러가 없으면 nil을 반환합니다.
  • main.go: 입력에서 파일 이름을 읽고 ProcessFile을 호출합니다. errors.Is()errors.As()를 사용하여 래핑된 에러 체인을 검사하고, 내부에서 발견된 내용에 따라 적절한 메시지를 출력합니다.

다음과 같은 입력이 제공됩니다:

  • Line 1: 처리할 파일 이름

에러 체인을 검사하여 결과를 처리하세요:

  • 만약 errors.Is()가 체인에서 ErrPermissionDenied를 찾으면: 전체 에러 메시지를 출력하고, 다음 줄에 Access denied - check file permissions를 출력합니다.
  • 만약 errors.As()가 체인에서 *FileError를 찾으면: 전체 에러 메시지를 출력하고, 다음 줄에 File issue: [Filename] during [Operation]을 출력합니다.
  • 에러가 발생하지 않으면: File '[filename]' processed successfully를 출력합니다.

예를 들어, secret.txt가 주어지면 출력은 다음과 같아야 합니다:

processing failed: permission denied
Access denied - check file permissions

그리고 missing.txt가 주어지면 출력은 다음과 같아야 합니다:

processing failed: read failed on file: missing.txt
File issue: missing.txt during read

그리고 data.txt가 주어지면 출력은 다음과 같아야 합니다:

File 'data.txt' processed successfully

ProcessFile에 의해 에러가 래핑되었음에도 불구하고, errors.Is()로 원래의 센티널 에러를 감지하고 errors.As()로 사용자 정의 에러 타입을 추출할 수 있음에 주목하세요. 이 함수들은 당신이 찾는 것을 발견할 때까지 전체 에러 체인을 순회합니다.

치트 시트

에러가 래핑(wrapped)된 경우, errors 패키지의 errors.Is()errors.As()를 사용하여 에러 체인을 검사하세요.

errors.Is()는 에러 체인에 특정 센티넬 에러(sentinel error)와 일치하는 에러가 있는지 확인합니다:

import "errors"

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

err := fmt.Errorf("database lookup failed: %w", ErrNotFound)
if errors.Is(err, ErrNotFound) {
    // 에러 체인에서 ErrNotFound를 찾았습니다.
}

errors.As()는 에러 체인에 특정 타입과 일치하는 에러가 있는지 확인하고 이를 추출합니다:

type ValidationError struct {
    Field string
}

func (e *ValidationError) Error() string {
    return "validation failed: " + e.Field
}

err := fmt.Errorf("request failed: %w", &ValidationError{Field: "email"})

var ve *ValidationError
if errors.As(err, &ve) {
    // ve는 이제 에러 체인에 포함된 ValidationError를 가집니다.
    fmt.Println(ve.Field)
}

센티넬 에러에는 errors.Is()를 사용하고, 커스텀 에러 타입의 필드에 접근해야 할 때는 errors.As()를 사용하세요. 두 함수 모두 전체 에러 체인을 순회합니다.

직접 해보기

package main

import (
	"errors"
	"fmt"
)

func main() {
	// 입력에서 파일 이름을 읽습니다
	var filename string
	fmt.Scanln(&filename)

	// 파일 이름으로 ProcessFile을 호출합니다
	err := ProcessFile(filename)

	// TODO: 에러 체인을 검사하여 결과를 처리합니다
	// 
	// errors.Is()를 사용하여 ErrPermissionDenied가 체인에 있는지 확인합니다:
	// - 발견된 경우: 전체 에러를 출력한 다음, "Access denied - check file permissions"를 출력합니다
	//
	// errors.As()를 사용하여 *FileError가 체인에 있는지 확인합니다:
	// - 발견된 경우: 전체 에러를 출력한 다음, "File issue: [Filename] during [Operation]"을 출력합니다
	//
	// 에러가 없는 경우: "File '[filename]' processed successfully"를 출력합니다

	_ = errors.Is // 힌트: errors.Is()를 사용하세요
	_ = errors.As // 힌트: errors.As()를 사용하세요
	_ = err
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨