포스트

프로그램의 안전망: 예외 처리(Exception Handling)의 모든 것

프로그램의 안전망: 예외 처리(Exception Handling)의 모든 것

“한 줄 요약: 좋은 프로그램은 문제가 없는 프로그램이 아니라 문제가 생겼을 때 무너지지 않는 프로그램이다.”

1. 왜 예외 처리가 필요한가?

개발자는 항상 ‘정상적인 상황’을 가정하고 코드를 짜지만 현실은 다릅니다. 사용자는 숫자가 필요한 곳에 문자를 입력하고("12a") 네트워크는 갑자기 끊기며 읽어야 할 파일은 사라지기도 합니다.

  • 핵심 문제: 예외 발생 자체가 문제가 아니라 발생했을 때 어떻게 행동할지 준비하지 않은 것이 진짜 문제입니다.

2. 에러(Error) vs 예외(Exception)

자바에서 발생하는 모든 ‘문제’가 다 같은 것은 아닙니다.

구분의미예시대응 방식
Error시스템 수준의 심각한 문제OutOfMemoryError, StackOverflowError애플리케이션 코드에서 복구 불가능
Exception코드로 예측하고 대응 가능한 문제IOException, NullPointerExceptiontry-catch로 처리하거나 전파(throws)

개발자인 우리가 ‘Exception’에 집중해야 하는 이유

위 표에서 알 수 있듯 Error는 하드웨어나 JVM 자체의 문제이므로 개발자가 코드로 수습할 수 있는 영역이 아닙니다(예: 메모리 부족). 하지만 Exception은 다릅니다.

  • 예측 가능한 사고: 네트워크 끊김이나 잘못된 사용자 입력은 ‘일어날 수도 있는 일’입니다.
  • 복구의 기회: 예외 처리를 잘 해두면 프로그램이 완전히 죽지 않고 사용자에게 “잠시 후 다시 시도해 주세요”라는 안내를 보내며 유연하게 대처가 가능합니다.
  • 개발자의 책임: 시스템(Error)을 탓할 순 없지만 발생 가능한 예외(Exception)를 방치하여 프로그램이 뻗는 것은 명백한 설계의 부재입니다.

결론: 개발자의 실력은 “코드를 얼마나 잘 짜느냐” 뿐만 아니라 “예외 상황에서 프로그램이 얼마나 유연하게 대처하느냐” 에서 드러납니다. 우리가 예외 처리를 공부하고 구현해야 하는 이유가 바로 여기에 있습니다.


3. 예외의 계층 구조 (Inheritance)

자바의 예외는 객체지향 원칙에 따라 상속 구조를 가집니다. 이 구조를 이해해야 효율적인 처리가 가능합니다.

상위 타입 catch의 특성

  • 예외도 클래스이므로 다형성이 적용됩니다.
  • catch (Exception e)는 모든 하위 예외를 한꺼번에 잡을 수 있습니다.
  • 주의: 너무 넓게 잡으면 정확히 무엇이 문제인지 파악하기 어려워지므로 구체적인 예외부터 잡고 마지막에 넓은 예외를 배치해야 합니다.
1
2
3
4
5
6
7
try {
    // 로직 실행
} catch (NumberFormatException e) {
    // 1순위: 가장 구체적인 예외 처리
} catch (Exception e) {
    // 2순위: 그 외 예상치 못한 모든 예외 처리
}

4. 예외 처리의 기본 구조 (Try-Catch-Finally)

프로그램 실행 중 발생할 수 있는 예기치 못한 상황에 대비하기 위한 가장 기본적인 장치입니다.

  • try: 감시 구역. 예외가 발생할 가능성이 있는 코드를 둡니다.
  • catch: 대응 구역. 발생한 예외를 처리하고 수습합니다.
  • finally: 정리 구역. 성공/실패 여부와 상관없이 무조건 실행됩니다. (파일 닫기, DB 연결 해제 등 자원 정리에 필수)

5. 던지고 알리기: throw vs throws

이름은 비슷하지만 역할은 완전히 다릅니다.

  • throw (행동): 예외를 실제로 발생시키는 코드입니다. “여기 문제가 있어!”라고 수동으로 폭탄을 터뜨리는 것과 같습니다.
  • throws (선언): 메서드 시그니처에 붙어 “이 메서드는 이런 예외가 발생할 수 있으니 주의해!“라고 호출자에게 경고하는 안내문입니다.

핵심 요약: throw는 직접 문제를 알리는 책임이고 throws는 해결을 호출자에게 넘기는 책임 전가입니다.


6. Checked vs Unchecked Exception

자바 예외 처리의 가장 큰 갈림길이며 설계 시 반드시 고려해야 할 부분입니다.

구분확인 시점컴파일러 강제성주요 예시
Checked컴파일 타임강제함 (try-catch 필수)IOException, SQLException
Unchecked런타임강제하지 않음NullPointerException, NumberFormatException
  • Checked: “외부 환경(파일, DB)은 언제든 실패할 수 있으니 대비책을 반드시 세워라“는 컴파일러의 간섭입니다.
  • Unchecked: “이건 프로그래머가 코드를 더 잘 짰으면 안 일어날 일이니 무조건 catch하기보다 코드를 고쳐라”는 의미에 가깝습니다.

7. 피해야 할 안티 패턴 (Bad Habits)

실무에서 유지보수성을 떨어뜨리는 나쁜 습관들입니다.

  • catch 블록 비워두기: 오류를 숨기는 행위입니다. 원인 추적이 불가능해져 장애 대응을 어렵게 만듭니다.
  • 무조건 catch (Exception e): 어떤 예외인지 구분하지 않으면 상황에 맞는 적절한 대응이 불가능합니다.
  • 흐름 제어용 예외 사용: 예외는 말 그대로 ‘예외적인 상황’에만 써야 합니다. 일반적인 조건문(if)으로 해결 가능하다면 예외를 던지지 마세요.

8. 오늘의 회고: 설계로서의 예외 처리

오늘 정리를 통해 예외 처리가 단순히 에러 메시지를 띄우는 것이 아니라 ‘프로그램의 연속성을 설계하는 과정’ 임을 깨달았습니다.

  • 반성: 그동안 코드가 ‘돌아가게’ 만드는 데만 집중해서, 예외가 터졌을 때 시스템이 어떻게 대처할지에 대한 고민이 부족했습니다.
  • 훈련 계획:
    1. 모든 메서드에서 발생 가능한 예외를 Javadoc으로 명시하기.
    2. 무분별한 try-catch보다는 호출자가 더 나은 결정을 내릴 수 있도록 throws를 적절히 활용하기.
    3. finally 혹은 try-with-resources를 사용해 자원 누수를 철저히 방지하기.
  • 다짐: 단순히 에러가 안 나는 코드가 아니라 장애 상황에서도 신뢰할 수 있는 ‘제품’ 을 만드는 개발자가 되겠습니다.

References

  • Oracle Java Documentation: Lesson: Exceptions
  • Joshua Bloch: Effective Java (3rd Edition) - Chapter 10. Exceptions (Item 69-77)
  • Robert C. Martin: Clean Code - Chapter 7. Error Handling
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.