파이썬 예외 처리 흔한 함정 피하기: 올바른 예외 처리법
많은 사람이 예외를 잘못 처리하는 거 봅니다. 혹시 당신도 해당될 수 있습니다. 다음 상황 익숙합니까?
코드 작성 중인데, 사용하는 라이브러리가 예외 발생시킬 수 있음. 정확히 어떤 예외인지 기억 안 남. 이 지점에서 소위 '잡아먹기(catch-all)' 블록 써서 재밌는 부분으로 넘어가기 유혹적.
목차
최악의 방법
할 수 있는 최악은 뭐든 잡는 try-except 블록 만드는 것. 잡아먹기라 함은 이런 거:
try:
...
except:
pass
이런 잡아먹기 블록 나쁜 이유:
- 다른 어떤 예외 발생할지 모름(나중에 더 설명).
pass로 에러 숨김, 로깅 안 함.
게다가 빈 except는 KeyboardInterrupt(Ctrl+C), SystemExit, 심지어 NameError까지 잡음! 그래서 다음 코드 깔끔하게 멈출 수 없음:
from time import sleep
while True:
try:
print("Try and stop me")
sleep(1)
except:
print("Don't.. stop.. me now!")
직접 해보세요. 터미널 창 닫거나 파이썬 프로세스 죽여야 이 프로그램 멈춥니다.
모든 예외 잡는 약간 나은 방법
반면 except Exception 쓰면, 여전히 너무 많은 예외 잡는 빠르고 지저분한 방법이지만, 적어도 실행 중인 프로세스 제대로 멈출 수 있음:
from time import sleep
while True:
try:
print("Try and stop me")
sleep(1)
except Exception:
print("Ok I'll stop!")
Exception 잡으면 SystemExit, KeyboardInterrupt 등 그런 예외 안 잡힘. 왜냐고요?
모든 예외는 BaseException 클래스 상속. 공식 문서: "try 문에서 특정 클래스 언급한 except 절, 그 클래스 파생 예외 클래스도 처리." 빈 except는 except BaseException과 동일, 그래서 모든 가능한 예외 잡음.
반면 Exception 클래스 정의: "모든 내장 비시스템 종료 예외 이 클래스 파생. 사용자 정의 예외도 이 클래스 파생해야 함."
더 나빠지는 경우
다음 예시에서 os 라이브러리로 현재 작업 디렉토리 가져오려 함. 하지만 내 뚱뚱한 손가락이 오타 냄:
import os
try:
working_dir = os.getcdw()
print(working_dir)
except:
print('error')
os.getcdw는 os 모듈에 없는 함수라 NameError 발생. 실패 대신 except 절이 에러 잡고 'error' 출력, 프로그램 우리 뻔뻔한 오타에도 계속 진행. 안타깝게 이거 Exception 잡는 걸로도 해결 안 됨!
우리가 1단계서 배운 작은 트릭이 우리 모든 문제 해결책 아님. 그럼 뭐를 해야 할까?
다룰 수 있는 것만 잡기
예외 대해 자주 듣는 말: 다룰 수 있는 것만 잡으라. 많은 개발자 예외 직접 다루려 유혹받지만, 종종 예외를 실제로 다룰 수 있는 프로그램 부분으로 전파시키는 게 더 나음.
예: 파일 열고 로드하는 텍스트 에디터 부분, OpenFile 클래스라 하자. 사용자 없는 파일 열 요청 시, 에러 직접 처리하거나 전파 가능.
이 경우 호출자로 전파하는 게 나음. OpenFile은 이 예외가 호출자에게 얼마나 나쁜지 모름. 호출자가 여러 방식으로 상황 처리 가능:
- 그 이름으로 새 파일 만들어 계속 진행
- 아마 호출자는 파일 반드시 있어야 함, 에러 다이얼로그로 사용자에게 파일 없음 알림
어느 쪽이든 FileNotFoundError 경우 뭐 할지는 OpenFile 클래스 몫 아님.
그럼 예외 항상 전파해야 하나? 아님. OpenFile 클래스서 다룰 수 있는 예외 예: TimeoutError. 몇 번 재시도 해 호출자 귀찮게 안 할 수 있음. 이건 OpenFile이 다룰 수 있는 예외니 잡고 재시도 괜찮음.
결론
어떤 상황에서도 다룰 수 있는 것보다 더 많은 예외 잡지 말아야 함. 담요 같은 except 블록은 버그와 예측 불가능 코드 레시피. 다른 말로: 다룰 수 있는 것만 잡으라.
코드 '다룰 수 있는 것만 잡으라' 마인드로 쓰면 잡아먹기 블록은 모든 규칙 위반. 제발 그만하세요. 연습으로 기존 코드 다시 보며 이 새 지식으로 개선 가능한지 확인해보세요!