본문 바로가기

언어별 예외 처리의 철학 Python의 EAFP, Java의 Checked Exception을 비교하며 배우는 안정성 설계

storybust 님의 블로그 2025. 11. 22.

개발을 하다 보면 “이건 예외로 처리해야 할까, 아니면 조건문으로 막아야 할까?”라는 고민을 자주 하게 됩니다. 흥미로운 건, 이 질문에 대한 답이 언어마다 다르다는 점입니다. Python은 “일단 시도하고, 실패하면 처리하라(EAFP: Easier to Ask Forgiveness than Permission)”는 철학을 따릅니다. 반면 Java는 “실패할 가능성을 미리 선언하고 대비하라”는 Checked Exception 중심의 접근을 취하죠. 저는 두 언어를 모두 사용하면서, 예외 처리 방식이 단순한 문법 차이가 아니라 언어의 사고방식과 안정성 설계 철학을 반영한다는 걸 깨달았습니다. 이 글에서는 Python과 Java의 예외 처리 철학을 비교하며, 실무에서 어떤 균형점을 잡아야 하는지 이야기해보려 합니다.

 

 

Python: 실패를 두려워하지 않는 유연함

Python의 예외 처리 철학은 “일단 시도하고, 실패하면 잡아라”입니다. 즉, 코드가 실패할 가능성을 미리 검사하기보다, 실제로 실패했을 때 예외를 처리하는 쪽을 선호합니다. 예를 들어 파일을 열 때 os.path.exists()로 미리 확인하기보다, 그냥 open()을 시도하고 FileNotFoundError를 잡는 게 더 파이썬다운 방식이죠. 이 접근은 코드가 간결하고 읽기 쉽습니다. 저도 초기에 Python을 배울 때 “이렇게 단순해도 되나?” 싶었지만, 실제로는 예외가 발생했을 때의 흐름이 훨씬 명확했습니다. 다만 단점도 있습니다. 예외를 남발하면 디버깅이 어려워지고, 예상치 못한 예외가 상위 로직까지 전파될 수 있습니다. 그래서 Python에서는 명확한 예외 계층 설계 try-except 범위 최소화가 중요합니다. 실패를 허용하되, 그 실패의 책임을 명확히 하는 것이죠.

 

 

Java: 예측 가능한 안정성을 위한 선언적 예외

Java는 Python과 정반대의 철학을 가집니다. Checked Exception은 “이 메서드가 실패할 수 있다”는 사실을 컴파일 타임에 강제합니다. 즉, 개발자가 예외를 무시할 수 없게 만드는 구조죠. 저는 처음 Java를 배울 때 IOException이나 SQLException을 매번 처리해야 하는 게 번거롭게 느껴졌습니다. 하지만 대규모 시스템을 다루다 보니, 이 강제성이 오히려 안정성의 기반이 된다는 걸 깨달았습니다. 예외를 명시적으로 선언하면, 호출하는 쪽에서도 그 리스크를 인지하고 대비할 수 있습니다. 다만 Java의 Checked Exception은 남용되면 코드가 장황해지고, 예외 전파가 복잡해질 수 있습니다. 그래서 최근에는 RuntimeException을 활용해 필요한 곳에만 Checked Exception을 적용하는 절제된 설계가 선호됩니다.

 

 

두 철학의 충돌과 조화

Python의 EAFP는 빠른 프로토타이핑과 실험적인 개발에 적합합니다. 반면 Java의 Checked Exception은 대규모, 장기 운영 시스템에서 안정성을 보장하는 데 강점을 가집니다. 저는 실제 프로젝트에서 두 철학을 혼합해 사용합니다. 예를 들어, Python 백엔드에서는 외부 API 호출 시 예외를 포괄적으로 잡고 로깅한 뒤, 핵심 로직에서는 최소한의 예외만 처리합니다. 반면 Java 서비스에서는 데이터베이스나 파일 I/O처럼 실패 가능성이 높은 부분에 Checked Exception을 명시해, 호출 계층마다 책임을 분리합니다. 결국 중요한 건 언어의 문법이 아니라, 예외를 통해 시스템의 신뢰도를 어떻게 설계하느냐입니다.

 

 

예외 처리 설계의 실전 원칙

  • 1. 실패를 예상하되, 과도하게 방어하지 말라: 모든 상황을 try-catch로 감싸면 오히려 오류를 숨깁니다. 실패의 가능성을 인지하되, 필요한 곳에만 처리하세요.
  • 2. 예외는 제어 흐름이 아니다: 예외는 비정상 상황을 위한 도구입니다. 정상 로직을 예외로 처리하면 코드의 의도가 흐려집니다.
  • 3. 예외 계층을 설계하라: Python에서는 커스텀 예외 클래스를, Java에서는 의미 있는 예외 타입을 정의해 예외의 맥락을 명확히 하세요.
  • 4. 로깅과 복구를 분리하라: 예외를 잡았을 때, 단순히 로그만 남길지 복구 로직을 수행할지 명확히 구분해야 합니다.
  • 5. 언어의 철학을 존중하라: Python에서는 유연함을, Java에서는 명시성을 살리는 방향으로 예외를 설계하세요.

 

예외는 시스템의 신뢰를 설계하는 언어다

결국 예외 처리는 단순히 오류를 잡는 기술이 아니라, 시스템의 신뢰를 설계하는 언어입니다. Python은 “실패를 두려워하지 말라”고 말하고, Java는 “실패를 예측하라”고 말합니다. 두 철학은 상반되어 보이지만, 실제로는 같은 목표를 향합니다. 바로 예측 가능한 안정성이죠. 저는 이제 예외를 볼 때마다 “이건 단순한 에러가 아니라, 시스템이 나에게 보내는 신호”라고 생각합니다. 그 신호를 잘 해석하고, 언어의 철학에 맞게 대응하는 것이 진짜 안정성 설계의 시작입니다.

댓글