본문 바로가기
Language/Java

[JAVA] Checked Exception과 Unchecked Exception의 전략적 선택 기준

by Papa Martino V 2026. 1. 26.
728x90
Checked Exception과 Unchecked Exception
Checked Exception과 Unchecked Exception

 

자바 프로그래밍에서 예외 처리(Exception Handling)는 단순히 에러를 막는 수단이 아닙니다. 이는 시스템의 복구 가능성(Recoverability)코드의 가독성, 그리고 API의 설계 철학을 결정짓는 핵심적인 요소입니다. 많은 개발자들이 Checked ExceptionUnchecked Exception의 기술적 차이는 알고 있지만, "언제 무엇을 사용해야 하는가?"라는 설계적 고민 앞에서는 흔들리곤 합니다. 본 포스팅에서는 실무적인 관점에서 두 예외의 본질을 파헤치고, 최신 자바 트렌드에 맞는 전략적 선택 기준을 제시합니다.


1. 예외의 계층 구조와 본질적 차이

자바의 모든 예외는 java.lang.Throwable 클래스를 상속받습니다. 여기서 핵심은 RuntimeException의 상속 여부입니다.

  • Checked Exception: RuntimeException을 상속받지 않은 Exception의 하위 클래스들입니다. 컴파일 시점에 체크되며, 반드시 try-catch로 처리하거나 throws로 명시해야 합니다.
  • Unchecked Exception: RuntimeException을 상속받은 예외들입니다. 실행 시점(Runtime)에 발생하며, 명시적인 처리를 강제하지 않습니다.

2. 전략적 선택을 위한 비교 분석

두 예외의 특성을 명확히 비교하여 설계 시 참고할 수 있는 기준을 마련했습니다.

처리 강제성컴파일러가 강제함 (Mandatory)개발자의 선택 (Optional)
주된 발생 원인외부 환경(파일 I/O, 네트워크, DB)프로그래밍 오류 (Logic Error, Null 참조)
복구 가능성높음 (사용자가 조치 후 재시도 가능)낮음 (코드 수정이 필요한 경우가 많음)
트랜잭션 처리기본적으로 Rollback 하지 않음기본적으로 Rollback 진행

3. 실무에서의 선택 기준 (Decision Rule)

Rule 1: 호출자가 예외 상황을 복구할 수 있는가?

만약 API를 사용하는 쪽에서 예외가 발생했을 때, 대안책을 강구하거나 다시 시도하여 정상 흐름으로 돌릴 수 있다면 Checked Exception을 사용하십시오. 이는 호출자에게 "이 상황은 발생할 수 있으니 반드시 대비하라"는 강력한 메시지를 전달합니다.

Rule 2: 호출자가 할 수 있는 일이 없는가?

대부분의 RuntimeException은 프로그래머의 실수(Precondition 위반)에서 비롯됩니다. 예를 들어 ArrayIndexOutOfBoundsException이 발생했을 때 호출자가 catch해서 할 수 있는 일은 거의 없습니다. 이럴 때는 프로그램을 종료시키거나 전역적인 에러 핸들러에서 처리하도록 Unchecked Exception을 던지는 것이 가독성 면에서 훨씬 유리합니다.

Rule 3: 가급적 Unchecked를 지향하라 (Modern Java Trend)

최근 스프링 프레임워크를 포함한 현대적인 자바 라이브러리들은 Checked Exception보다는 Unchecked Exception을 선호합니다. 너무 많은 throws 선언은 코드의 의존성을 높이고 가독성을 해치기 때문입니다. 복구 가능한 상황이라도 Runtime 예외로 래핑(Wrapping)하여 던지는 방식이 널리 쓰입니다.


4. Sample Example: 외부 API 호출 시나리오

다음은 사용자의 포인트 결제 시스템에서 발생할 수 있는 예외 처리 예시입니다.


// 1. 복구 가능한 상황: Checked Exception 정의
class InsufficientBalanceException extends Exception {
    public InsufficientBalanceException(String message) {
        super(message);
    }
}

// 2. 프로그래밍 오류 상황: Unchecked Exception 정의
class InvalidPaymentRequestException extends RuntimeException {
    public InvalidPaymentRequestException(String message) {
        super(message);
    }
}

public class PaymentService {
    public void processPayment(Long userId, long amount) throws InsufficientBalanceException {
        if (amount <= 0) {
            // 결제 금액이 0보다 작으면 개발자의 실수이므로 Runtime 예외
            throw new InvalidPaymentRequestException("결제 금액은 0보다 커야 합니다.");
        }
        
        if (!hasEnoughBalance(userId, amount)) {
            // 잔액 부족은 사용자가 충전 후 재시도 가능하므로 Checked 예외
            throw new InsufficientBalanceException("잔액이 부족합니다.");
        }
        
        // 결제 로직 수행...
    }
}

5. 결론: 책임의 전가와 수용

예외 설계의 핵심은 "책임"입니다. Checked Exception은 해당 문제를 해결할 책임을 호출자에게 넘기는 행위이며, Unchecked Exception은 시스템이 감당하지 못하는 상황임을 인정하고 상위 핸들러로 제어권을 넘기는 행위입니다. 무분별한 throws Exception 남발을 지양하고, 비즈니스 로직의 성격에 따라 명확한 기준을 가지고 예외를 정의하시기 바랍니다.

 

참조 및 출처

  • Oracle Java Documentation: Exceptions (https://docs.oracle.com/javase/tutorial/essential/exceptions/)
  • Effective Java 3rd Edition (Joshua Bloch 저) - Item 70, 71
  • Clean Code (Robert C. Martin 저) - Chapter 7: Error Handling
728x90