Table of Contents
예외 처리(Exception handling), 단순한 코드 방어기술이 아닙니다.
지금 이 순간에도 수많은 Python 코드가 사용자 요청에 따라 실행되고 있지만, 모든 상황이 정상적일 수는 없습니다.
Python 예외 처리는 예상하지 못한 상황에서도 프로그램이 안정적으로 작동하게 만드는 핵심 기술입니다.
예외 처리(Exception handling), 왜 중요한가?
처음 프로그래밍을 배울 때 대부분 assert
같은 강제 종료 방식으로 오류를 처리하는 법을 접하게 됩니다. 예를 들어, 디바이스 드라이버 개발에서는 한 줄의 코드 실수로 커널 패닉이 발생할 수 있기 때문에 assert
로 중단시키는 것이 오히려 안전한 방법이 될 수 있습니다.
하지만 웹 애플리케이션, 데이터 자동화 도구, API 서비스 같은 사용자 지향 시스템에서는 프로그램이 예외 상황에서도 계속 동작해야 합니다. 이때 필요한 것이 바로 try-except
(link)구문을 이용한 예외 처리입니다.
기본 문법: try-except 구조 이해하기
try:
# 예외가 발생할 수 있는 코드
run_task()
except FileNotFoundError as e:
print(f"파일 오류: {e}")
else:
print("예외 없이 성공적으로 실행됨")
finally:
print("무조건 실행되는 블록 (리소스 정리 등)")
- try: 실행하고 싶은 주요 로직
- except: 예외가 발생했을 때 실행할 코드
- else: 예외 없이 성공했을 때 실행 (선택)
- finally: 예외와 상관없이 항상 실행 (선택)
Python 예외 처리(Exception handling)의 이점
장점
- 유연성: 다양한 에러 상황에 맞춰 적절한 대처가 가능
- 안정성: 프로그램 전체가 멈추는 것을 방지
- 가독성: 어떤 예외가 어디서 발생했는지 쉽게 파악 가능
- 유지보수성: 문제 추적과 로깅이 쉬워 디버깅 효율 향상
단점
- 과도한 사용: 모든 코드를 감싸면 오히려 디버깅이 어려움
- 성능 저하: 약간의 오버헤드 발생 (성능 민감한 코드에선 주의)
- 원인 은폐: 구체적 예외를 명시하지 않으면 진짜 원인을 놓칠 수 있음
실제 사례: 예외 처리가 없어서 생긴 문제
한 번은 크롤링 프로그램을 만들던 중 try: ... except:
블록이 너무 광범위하게 사용돼 문제가 발생했는데도 로그도 남지 않고, 사용자에게 아무런 알림도 없이 조용히 실패하는 현상이 있었습니다. 결국 수집된 데이터가 하나도 없었고, 해당 문제를 찾는 데 며칠이 소요됐습니다.
이후부터는 예외 범위를 좁히고, 구체적인 예외만 처리하며, logging을 반드시 사용하도록 원칙을 세웠습니다.
Python 예외 계층 구조 이해하기
파이썬의 예외 클래스는 계층적으로 구성돼 있어, 어떤 예외를 상속하는지에 따라 처리 방식도 달라질 수 있습니다.
BaseException
├── SystemExit
├── KeyboardInterrupt
└── Exception
├── ArithmeticError → ZeroDivisionError, OverflowError 등
├── AttributeError
├── LookupError → IndexError, KeyError
├── FileNotFoundError
├── ValueError
└── RuntimeError
실무에서는 Exception
이나 BaseException
처럼 상위 클래스를 무조건 사용하는 것이 아니라, 해당 상황에 맞는 구체적 예외 클래스를 선택하는 것이 중요합니다.
2025년 기준 예외 처리 Best Practices
1. except:
대신 구체적인 예외를 명시하자
# ❌ 안 좋은 예
try:
run()
except:
pass # 어떤 에러인지 알 수 없음
# ✅ 좋은 예
try:
run()
except ValueError as e:
logging.error(f"입력값 오류 발생: {e}")
2. logging
을 사용해 디버깅에 강한 코드를 만들자
import logging
logging.basicConfig(level=logging.ERROR)
try:
with open("data.txt") as f:
content = f.read()
except FileNotFoundError as e:
logging.error("파일을 찾을 수 없습니다: %s", e)
3. finally
는 반드시 리소스 해제용으로 활용하자
파일, DB, 네트워크 등은 예외가 발생하더라도 반드시 닫아야 합니다.
try:
conn = open_db()
query_db(conn)
finally:
conn.close()
4. 사용자 메시지와 개발자 로그는 분리하자
- 사용자에게는 단순한 메시지: “문제가 발생했습니다. 관리자에게 문의하세요.”
- 개발자 로그에는 상세한 traceback 기록
5. 예외는 가능한 한 작은 범위에서 처리하자
전체 함수를 감싸는 것보다, 실제로 예외가 날 수 있는 코드에만 적용하는 것이 디버깅에도 효과적입니다.
보너스: 사용자 정의 예외 사용하기
때때로 기본 예외만으로는 부족할 때가 있습니다. 이럴 땐 사용자 정의 예외 클래스를 만들어 사용하면 좋습니다.
class InvalidAgeError(Exception):
pass
def check_age(age):
if age < 0:
raise InvalidAgeError("나이는 음수가 될 수 없습니다.")
결론: 예외 처리는 선택이 아닌 생존 전략
현대 소프트웨어는 대부분 실시간, 다중 사용자, 자동화된 서비스입니다. 이런 시스템에서는 사소한 예외 하나로 전체 흐름이 중단될 수 있습니다.
Python Exception handling는 단순한 오류 방어가 아니라, 서비스 품질과 사용자 경험을 지키는 방패입니다.
- 무조건 감싸는 것이 아닌, 의미 있는 처리
- 로그를 남기고, 투명하게 원인을 파악
- 사용자와 개발자 각각에게 적절한 정보를 제공
이 원칙만 지켜도 여러분의 코드는 훨씬 더 견고해질 것입니다.