~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. BaseException 은 SystemExit, 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: ...
가상 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.