C.W.K.
Stream
Lesson 03 of 06 · published

커스텀 예외 클래스 — 자체 에러 타입 디자인

~18 min · custom-exception, exception-class, raise

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

왜 커스텀 예외가 중요

빌트인 예외는 일반 목적 — ValueError, TypeError, RuntimeError. 문제 카테고리 알려주지만 코드가 겪은 특정 실패는 X. 커스텀 예외 — InsufficientFunds, RateLimitExceeded, InvalidUserToken — 의도 전달 + 호출자가 자기 신경쓰는 케이스만 처리.

맞는 베이스에서 상속

항상 Exception 상속, BaseException X. BaseExceptionSystemExit, KeyboardInterrupt 같이 일반 핸들러가 안 잡아야 할 거의 root. 커스텀 예외는 Exception 아래 일반 "복구 가능한 에러" 트리에.

컨텍스트 추가 — 예외에 속성

가장 단순한 커스텀 예외 — class MyError(Exception): pass. 한 단계 위 — 관련 데이터 캡쳐하는 커스텀 __init__InsufficientFunds(account_id, requested, available). 이제 예외 핸들러가 e.account_id 검사 + 지능적 응답 가능.

예외 계층 — 관련 에러 많을 때

라이브러리에 관련 에러 패밀리 있으면 작은 계층 구축. class ApiError(Exception): pass root, 그 다음 RateLimitExceeded(ApiError), AuthenticationFailed(ApiError). 호출자가 "이 라이브러리에서 뭐든" 엔 ApiError, 세밀한 처리엔 특정 서브클래스.

raise from — 원인 체이닝

한 예외가 다른 거 부를 때 — 저레벨 FileNotFoundError 잡고 고레벨 ConfigMissing raise 하고 싶으면 — raise ConfigMissing(...) from original_exc. Python 이 둘 다 출력, 체인 보여. 새 예외의 __cause__ 속성이 원본 가리킴.

Code

최소 커스텀 예외·python
class InsufficientFunds(Exception):
    pass

def withdraw(balance, amount):
    if amount > balance:
        raise InsufficientFunds(f"{amount} 요청, 잔액 {balance}")
    return balance - amount

try:
    withdraw(100, 200)
except InsufficientFunds as e:
    print("잡음:", e)
구조화된 속성 가진 커스텀 예외·python
class InsufficientFunds(Exception):
    def __init__(self, account_id, requested, available):
        super().__init__(
            f"계좌 {account_id}: {requested} 요청, {available} 가능"
        )
        self.account_id = account_id
        self.requested = requested
        self.available = available

try:
    raise InsufficientFunds("acc-42", 200, 100)
except InsufficientFunds as e:
    print("메시지:", e)
    print("부족분:", e.requested - e.available)
    # 핸들러가 구조화된 필드 사용
    if e.account_id.startswith("acc-"):
        print("일반 계좌")
예외 계층 — 세밀한 잡기·python
class ApiError(Exception):
    """모든 API 클라이언트 에러의 베이스."""

class RateLimitExceeded(ApiError):
    pass

class AuthenticationFailed(ApiError):
    pass

class ServerError(ApiError):
    pass

def call_api(token):
    if not token:
        raise AuthenticationFailed("토큰 없음")
    if token == "limited":
        raise RateLimitExceeded("느려져")
    raise ServerError("500 internal")

# 호출자가 넓게 잡기
try:
    call_api(None)
except ApiError as e:
    print("API 문제:", type(e).__name__, e)

# 또는 좁게
try:
    call_api("limited")
except RateLimitExceeded:
    print("backing off")
except ApiError as e:
    print("다른 api 에러:", e)
raise from — 원인 체이닝·python
class ConfigError(Exception):
    pass

def load_config(path):
    try:
        with open(path) as f:
            return f.read()
    except FileNotFoundError as e:
        raise ConfigError(f"{path} 에 config 없음") from e

try:
    load_config("/nonexistent.yaml")
except ConfigError as e:
    print("잡음:", e)
    print("원인:", e.__cause__)

# `raise X from Y` 면 traceback 출력:
#   FileNotFoundError: ...
#   The above exception was the direct cause of the following exception:
#   ConfigError: ...

External links

Exercise

가상 payment 모듈의 예외 계층 구축. Root — PaymentError. 서브클래스 — InvalidCard, InsufficientFunds, RateLimitExceeded. 각각 의미있는 속성 (예 — InvalidCard 가 카드 last4, InsufficientFunds 가 account_id / requested / available, RateLimitExceeded 가 retry_after_seconds). 단순 입력 기반으로 그 중 하나 raise 하는 함수 charge(card, amount). 호출 코드에서 넓은 PaymentError + 특정 서브클래스 둘 다 잡기 + raise from 한 번 사용 보여.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.