본문 바로가기
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 같은 내장 예외가 실패의 큰 종류만 말한다면 도메인 예외는 “잔액 부족”, “인증 실패”처럼 처리 방법이 다른 상황을 구분해. 일반 프로그램 예외는 BaseException이 아니라 Exception을 상속해.

메시지보다 구조화된 정보를 남겨

계정, 요청값, 남은 값 같은 속성을 넣으면 호출자가 문자열을 해석하지 않고 복구할 수 있어. 여러 오류가 같은 방식으로 처리된다면 타입을 지나치게 쪼개지 말고 공통 기반과 속성을 써.

원인을 지우지 말고 번역해

raise NewError(...) from original은 낮은 층의 원인을 __cause__에 보존하면서 높은 층의 의미로 바꿔. 두 traceback이 함께 남아 진단할 수 있어.

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

PaymentError 아래 InvalidCard, InsufficientFunds, RateLimitExceeded를 만들고 각각 카드 끝 네 자리, 계정·요청·가용액, 재시도 초를 속성으로 담아. charge가 입력에 따라 예외를 내게 하고 공통·구체 처리와 raise from을 모두 보여줘.

Progress

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

댓글 0

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

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