예외는 정상적인 결과를 만들 수 없음을 호출자에게 전달한다. 실패 지점에서 던지고, 복구할 수 있는 경계에서 잡는다.

try:
    depth = int(raw)
except ValueError as exc:
    raise ValueError("깊이는 정수여야 합니다") from exc
else:
    run(depth)
finally:
    close_log()

`else`는 예외가 없을 때만, `finally`는 성공과 실패 모두에서 실행된다. try 범위를 작게 하면 어느 문장이 실패했는지 명확하다.

`raise ... from exc`는 원래 예외를 원인으로 연결한다. 외부 라이브러리의 세부 예외를 도메인 예외로 바꿀 때 디버깅 정보를 보존한다. `raise`만 쓰면 현재 예외와 traceback을 그대로 다시 던진다.

자원 해제는 finally를 직접 쓰기보다 다음 편의 context manager로 표현하는 편이 안전하다.

Python 10. 예외를 처리하는 방법의 핵심 흐름도
그림. 예상한 예외만 좁게 잡고 자원 정리는 항상 실행한다.