예외의 핵심은 "에러를 없애는 것"이 아니라 이 실패를 여기서 복구할지, 위로 넘길지, 정리만 하고 끝낼지를 정하는 데 있습니다.
try, except, else, finally, raise, raise ... from e를 각각 무슨 질문에 쓰는지 나눠 읽는 편이 중요합니다.
핵심 정리
try:
value = int(text)
except ValueError:
value = 0
else:
print("parse success")
finally:
print("done")문법
기본형은 try, except, else, finally 흐름을 함께 보면 됩니다.
try:
result = work()
except ValueError:
result = default
else:
log_success(result)
finally:
cleanup()예외 처리
복구할 수 있으면 try/except
예외는 현재 코드가 정상 경로에서 해결하지 못한 실패를 밖으로 올립니다.
그런데 지금 이 자리에서 기본값 대체나 재시도 같은 복구가 가능하다면 except에서 처리할 수 있습니다.
try:
port = int(text)
except ValueError:
port = 8000핵심은 "잡는다"가 아니라 "정말 여기서 복구할 수 있나"입니다.
예상한 예외만 좁게 잡는다
except Exception:처럼 넓게 잡으면 실제 버그도 함께 삼켜 버릴 수 있습니다.
가능하면 예상한 예외 타입만 잡는 편이 원인 추적에 훨씬 유리합니다.
try:
config = json.loads(raw)
except json.JSONDecodeError:
config = {}즉 실패를 처리하려면 먼저 "무슨 실패를 기대하는가"가 선명해야 합니다.
분기와 정리
else와 finally는 역할이 다르다
else: 예외가 없을 때만 실행finally: 성공/실패와 관계없이 항상 실행
file = open("data.txt", "r", encoding="utf-8")
try:
data = file.read()
except OSError:
data = ""
finally:
file.close()이 구분이 보이면 예외 흐름을 훨씬 읽기 쉬워집니다.
예외 변환
원인은 살리고 예외만 바꾸려면 raise ... from e
하위 계층 예외를 그대로 바깥으로 노출하고 싶지 않지만, 원인은 남기고 싶을 때 raise NewError(...) from e를 씁니다.
def fetch_user(user_id: int):
try:
return db.query(user_id)
except DatabaseError as e:
raise ValueError(f"invalid user: {user_id}") from e이 패턴은 계층 경계를 정리하면서도 traceback에서 원인을 잃지 않게 해 줍니다.
자원 정리는 가능하면 with, 예외 분류는 커스텀 예외로
파일 닫기 같은 자원 정리는 다음 카드의 with가 더 나은 경우가 많습니다.
반대로 도메인 실패를 분류해야 하면 Exception 상속 커스텀 예외를 두는 편이 except를 좁게 쓰기 좋습니다.
class ConfigError(Exception):
pass
def load_timeout(text: str) -> int:
try:
return int(text)
except ValueError as e:
raise ConfigError("TIMEOUT must be an integer") from eValueError를 그대로 밖에 노출하지 않고, 설정 계층에서는 ConfigError로 다시 감싸면 호출부에서 실패 의미를 더 직접적으로 읽을 수 있습니다.
주의할 점
너무 넓은 예외를 잡으면 버그가 숨어 버립니다. 디버깅이 어려워지고 실패 원인도 흐려집니다.
# ❌ 모든 예외를 삼켜 버림
try:
result = do_work()
except Exception:
result = None
# ❌ 실패 원인을 숨긴 채 그냥 지나감
try:
save_config(raw)
except ValueError:
pass
# ✅ 예상한 실패만 처리
try:
result = int(text)
except ValueError:
result = 0
# ✅ 처리하지 않을 예외는 다시 올려 보냄
try:
save_config(raw)
except ValueError as e:
raise ConfigError("invalid config") from e참고 링크
2 sources