파이썬 try-except 예외 처리 완벽 가이드: 사용자 정의 예외와 자원 안전 해제
파이썬 try-except의 실행 흐름과 구체적인 예외 처리, else·finally 구분, 사용자 정의 예외, 예외 연결 및 파일·연결 자원을 안전하게 해제하는 방법을 예제로 설명합니다.

파이썬의 try-except는 오류를 숨기는 문법이 아니라, 예상 가능한 실패만 구분해 처리하고 예상하지 못한 오류는 다시 드러내는 장치입니다. 실무에서는 구체적인 예외부터 잡고, 정상 흐름은 else에 두며, 파일·연결 같은 자원은 with 또는 finally로 반드시 정리하는 방식이 안전합니다.
핵심 요약
except:또는 무조건적인except Exception보다 처리할 수 있는 예외 형식을 구체적으로 지정합니다.else에는 예외가 없을 때만 실행할 코드를,finally에는 성공·실패와 관계없이 실행할 정리 코드를 둡니다.- 업무 규칙 위반은
Exception을 상속한 사용자 정의 예외로 표현하면 호출자가 원인을 정확히 구분할 수 있습니다. - 파일은 가능한 한
with문으로 열어 예외가 발생해도 자동으로 닫히게 합니다.
try-except 실행 흐름부터 이해하세요
try에서 예외가 발생하면 남은 문장은 건너뛰고, 형식이 일치하는 첫 번째 except로 이동합니다. 처리기가 없으면 예외는 바깥 호출자에게 전달됩니다.
def parse_age(value):
try:
age = int(value)
except (TypeError, ValueError) as exc:
print(f"나이 변환 실패: {exc}")
return None
else:
return age
print(parse_age("52"))
위 코드는 정수 변환 과정에서 예상되는 TypeError와 ValueError만 처리합니다. SyntaxError는 먼저 코드를 수정해야 합니다.
구체적인 예외부터 잡아야 하는 이유
모든 오류를 한꺼번에 잡으면 개발자가 즉시 알아야 할 문제까지 정상 처리된 것처럼 보일 수 있습니다. 복구 방법이 같은 예외만 튜플로 묶으세요.
| 예외 | 주요 상황 | 권장 대응 |
|---|---|---|
ValueError |
형식은 맞지만 값 변환 불가 | 입력값 안내 후 재입력 |
TypeError |
지원하지 않는 자료형 | 호출부와 타입 검증 |
FileNotFoundError |
대상 파일 없음 | 경로 확인 또는 기본 파일 생성 |
PermissionError |
읽기·쓰기 권한 없음 | 권한과 저장 위치 안내 |
KeyError |
딕셔너리 키 없음 | get() 또는 스키마 검증 |
상위 클래스인 Exception을 마지막에 잡아 기록해야 한다면, 로그를 남긴 뒤 raise로 다시 올리는 방법을 고려하세요. SystemExit와 KeyboardInterrupt까지 포함하는 BaseException은 일반 업무 코드에서 잡지 않는 것이 원칙입니다.
else와 finally는 역할이 다릅니다
else는 예외가 없을 때만 실행됩니다. 후속 코드를 이곳으로 빼면 너무 넓은 범위의 예외를 잡는 일을 줄일 수 있습니다. finally는 성공·실패와 관계없이 실행됩니다.
connection = None
try:
connection = connect_database()
except ConnectionError as exc:
print(f"연결 실패: {exc}")
else:
save_report(connection)
finally:
if connection is not None:
connection.close()
finally 안의 return은 원래 반환값이나 예외를 가릴 수 있으므로 피하세요.
사용자 정의 예외로 업무 규칙을 표현하세요
잔액 부족이나 허용되지 않은 상태 변경 같은 업무 규칙 위반은 전용 예외 클래스로 표현할 수 있습니다. 이름은 Error로 끝내고 Exception을 상속합니다.
class InsufficientBalanceError(Exception):
"""출금액이 사용 가능한 잔액보다 클 때 발생"""
def withdraw(balance, amount):
if amount <= 0:
raise ValueError("출금액은 0보다 커야 합니다")
if amount > balance:
raise InsufficientBalanceError(
f"잔액 {balance:,}원, 요청 {amount:,}원"
)
return balance - amount
하위 예외를 의미 있는 오류로 바꿀 때는 raise 새예외(...) from exc를 사용하세요. 원래 원인이 traceback에 연결됩니다.
파일과 연결은 with 문으로 안전하게 해제하세요
파일을 직접 열면 예외 발생 시 close()까지 도달하지 못할 수 있습니다. 컨텍스트 관리자를 지원한다면 with로 자동 정리하세요.
from pathlib import Path
path = Path("settings.txt")
try:
with path.open("r", encoding="utf-8") as file:
settings = file.read()
except FileNotFoundError:
settings = ""
except PermissionError as exc:
raise RuntimeError("설정 파일을 읽을 권한이 없습니다") from exc
네트워크 요청에서는 타임아웃, 연결 실패, HTTP 상태 오류를 구분하고 반복 실패를 로그로 남기세요.
예외 처리 실수 방지 체크리스트
- 복구할 수 있는 예상 예외만 잡았는지 확인합니다.
- 하위 예외 클래스를 상위 클래스보다 먼저 배치합니다.
- 빈
except와 이유 없는pass를 사용하지 않습니다. - 오류 메시지에 비밀번호, 토큰, 개인정보를 넣지 않습니다.
- 정상 후속 처리는
else, 필수 정리는finally또는with에 둡니다. - 기록 후 상위 호출자가 판단해야 하는 오류는 다시
raise합니다. - 테스트에서 정상·예상 실패·예상 밖 실패 경로를 각각 확인합니다.
자주 묻는 질문
1. except Exception은 절대 쓰면 안 되나요?
최상위 경계에서 오류를 기록하고 다시 발생시키는 용도처럼 목적이 분명하면 쓸 수 있습니다. 다만 조용히 무시하는 방식은 피해야 합니다.
2. except:와 except Exception의 차이는 무엇인가요?
빈 except는 종료 신호 성격의 예외까지 잡을 수 있습니다. 일반 오류를 대상으로 할 때는 Exception이 더 안전합니다.
3. 여러 예외를 한 번에 처리할 수 있나요?
except (ValueError, TypeError) as exc:처럼 튜플로 묶을 수 있습니다. 대응 방법이 같을 때만 묶으세요.
4. raise만 단독으로 쓰면 어떻게 되나요?
현재 처리 중인 예외를 traceback과 함께 다시 발생시킵니다. except 블록 밖에서 쓰면 오류가 납니다.
5. finally는 return 뒤에도 실행되나요?
네. 함수가 반환되기 직전에 실행됩니다. 다만 finally 안의 return은 기존 결과와 예외를 가릴 수 있어 권장되지 않습니다.
6. 파일 닫기는 finally와 with 중 무엇이 좋나요?
컨텍스트 관리자를 지원하는 파일에는 with가 더 간결하고 안전합니다. 여러 자원을 수동 관리해야 할 때 finally가 필요할 수 있습니다.
7. 사용자 정의 예외에 데이터를 넣어도 되나요?
가능합니다. 생성자에서 오류 코드나 입력값을 속성으로 저장하면 호출자가 구조적으로 처리할 수 있습니다.
8. 예외 메시지는 사용자에게 그대로 보여줘도 되나요?
내부 경로와 시스템 정보가 포함될 수 있으므로 외부 사용자에게는 정제된 안내를 보여주고 상세 내용은 안전한 로그에 남기세요.
9. 예외 처리와 입력 검증은 같은가요?
다릅니다. 예측 가능한 잘못된 입력은 먼저 검증하고, 실행 중 발생할 수밖에 없는 실패는 예외로 처리하는 것이 좋습니다.
10. 예외를 성능 때문에 피해야 하나요?
정상 제어 흐름을 반복적으로 예외에 의존하는 것은 피해야 하지만, 실제 실패 상황을 명확히 처리하는 용도로는 올바르게 사용해야 합니다.
공식 출처
공식 문서 확인일: 2026년 8월 30일