복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라

@023· February 27, 2025 · 4 min read

자바는 문제 상황을 알리는 타입(throwable)으로 검사 예외, 런타임 예외, 에러 이렇게 세 가지를 제공하고 있다. 이 글에서는 이러한 검사 예외, 런타임 예외, 에러가 무엇이고 언제 사용해야 하는지 알아본다.

검사 예외 (Checked Exception)

호출하는 쪽에서 복구하리라 여겨지는 상황이라면 검사 예외를 사용해야 한다. 검사 예외는 예외를 던지면 호출자가 그 예외를 catch로 잡아 처리하거나 더 바깥으로 전파하도록 강제하게 된다. 이러한 검사 예외는 메서드를 호출하는 쪽에서 회복하리라 여겨지는 상황에서 사용해야 하는데 달리 말하면, API 설계자는 API 사용자에게 검사 예외를 던져주어 그 상황에서 회복해내라고 요구하는 것이다.

비검사 예외 (Unchecked Exception)

비검사 예외는 RuntimeException를 상속 받은 클래스로서, 런타임 시 프로그래밍 오류를 나타낼 때 사용한다. 이런 throwable을 잡지 않은 스레드는 적절한 오류 메시지를 내뱉으며 중단된다. 런타임 예외의 대부분은 전제조건을 만족하지 못했을 때 발생한다. 전제조건 위배란 단순히 클라이언트가 해당 API의 명세에 기록된 제약을 지키지 못했다는 뜻이다. 그 예로 배열 인덱스를 벗어나는 경우가 이에 해당한다. 배열의 인덱스는 0에서 배열 크기 -1 사이여야 한다. ArrayIndexOutOfBoundsException이 발생했다는 건 이 전제 조건이 지켜지지 않았다는 뜻이다.

에러 (Error)

에러는 Error를 상속 받은 클래스로 보통 JVM이 자원부족, 불변식 깨짐 등 더 이상 수행을 계속할 수 없는 상황을 나타낼 때 사용한다. 여기서 주의할 점은 Error 클래스를 상속해 하위 클래스를 만드는 일은 자제하지 말아야 한다. 다시 말해 우리가 구현하는 비검사 throwable은 모두 RuntimeException의 하위 클래스여야 한다는 뜻이다.. Error는 상속하지 말아야 할 뿐 아니라, throw 문으로 직접 던지는 일도 없어야 한다.(단, AssertionError는 예외이다.)

검사 예외와 비검사 예외

그럼 언제 검사 예외를 사용해야 하고, 언제 비검사 예외를 사용해야 할까? 해당 고민은 API 설계자의 판단에 달려있는데 복구 가능하다고 생각하면 검사 예외를, 불가능하다면 비검사 예외를 사용해야 한다. 만일 확신하기 어렵다면 비검사 예외를 사용하는 것이 좋다.(아이템 71)

정리

복구할 수 있는 상황이면 검사 예외를, 프로그래밍 오류라면 비검사 예외를 던지자. 확실하지 않다면 비검사 예외를 던지자. 검사 예외도 아니고 런타임 예외도 아닌 throwable은 정의하지도 말자. 검사 예외라면 복구에 필요한 정보를 알려주는 메서드도 제공하자.

@023
focus and hustle