DEV ℧ Developer Diary

[EffectiveJava] item71 - 필요 없는 검사 예외 사용은 피하라

검사 예외는 제대로 활용하면 API와 프로그램의 질을 높일 수 있다. 결과를 코드로 반환하거나 비검사 예외를 던지는 것과 달리, 검사 예외는 발생한 문제를 프로그래머가 처리하여 안전성을 높이게끔 해준다.

사용자에게는 불편할 수 있다. 어떤 메서드가 검사 예외를 던질 수 있다고 선언됐다면, 이를 호출하는 코드에서는 catch 블록을 두어 그 예외를 붙잡아 처리하거나 더 바깥으로 던져 문제를 전파해야 한다.
어느 쪽이든 API 사용자에게 부담을 줄 수 있다. 자바 8 부터는 검사 예외를 던지는 메서드는 스트림 안에서 직접 사용할 수도 없어 부담이 커졌다.

검사 예외(Checked Exception) vs 비검사 예외(Unchecked Exception)

만약 API를 제대로 사용해도 발생할 수 있는 예외이거나, 프로그래머가 의미 있는 조치를 취할 수 있는 경우라면 이러한 부담은 받아들일 수 있을 것이다.
그러나 둘 중 어디에도 해당하지 않는다면 비검사 예외를 사용하는 게 좋다.

다음의 예시를 보자.

검사 예외

} catch (TheCheckedException e) {
    throw new AssertionError(); /** 일어날 수 없다! */
}
} catch (ThrCheckedException e) {
    e.printStackTrace(); /** 우리가 졌다. */
    System.exit(1);
}

둘의 코드 둘다 좋은 방식은 아니다.

더 나은 방법이 없다면 비검사 예외를 선택해야 한다.

검사 예외의 단점

검사 예외가 프로그래머에게 지우는 부담은 메서드가 단 하나의 검사 예외만 던질 때가 특히 크다.

이미 다른 검사 예외도 던지는 상황에서 또 다른 검사 예외를 추가하는 경우라면 catch 문 하나 추가하는 선에서 끝이다.

하지만 검사 예외가 단 하나 뿐이라면 오직 그 예외 하나 때문에 API 사용자는 try 블록을 추가해야 하고 스트림에서는 사용하지 못하게 된다.

검사 예외 회픠

위와 같은 상황이라면 검사 예외를 안던지는 방법이 없는지 고민해볼 가치가 있다.

빈 옵셔널

검사 예외를 회피하는 가장 쉬운 방법은 적절한 결과 타입을 담은 옵셔널을 반환하는 것이다.

검사 예외를 던지는 대신 단순히 빈 옵셔널을 반환하면 된다. 이 방식의 단점이라면 예외가 발생한 이유를 알려주는 부가정보를 담을 수가 없다.
반면, 예외를 사용하면 구체적인 예외 타입과 그 타입이 제공하는 메서드를 활용해 부가정보를 제공할 수 있다.

비검사 예외로 변환

또 다른 방법으로, 검사 예외를 던지는 메서드를 2개로 쪼개 비검사 예외로 바꿀 수 있다. 다음의 방법을 보자.

검사 예외를 던지는 메서드 - 리팩터링 전

try {
    obj.action(args);
} catch (TheCheckedException e) {
    ... // 예외 상황에 대처한다.
}

상태 검사 메서드와 비검사 예외를 던지는 메서드 - 리팩터링 후

if (obj.actionPermitted(args)) {
    obj.action(args);
} else {
    ... // 예외 상황에 대처한다.
}

이 리팩터링을 모든 상황에 적용할 수는 없지만 적용할 수만 있다면 더 쓰기 편한 API를 제공할 수 있다.

실패 시 스레드를 중단하길 원한다면 obj.action(args); 한 줄로만 작성해도 무방하다.

만약 이 한 줄짜리 호출 방식이 주로 쓰일 거로 판단되면 리팩터링 하는 편이 바람직 하다. 한편 리팩터링 후의 actionPermitted는 상태 검사 메서드에 해당하므로 Item69에서 말한 단점도 그대로 적용이 된다.

외부 동기화 없이 여러 스레드가 동시에 접근할 수 있거나 외부요인에 의해 상태가 변할 수 있다면 아래와 같은 이유로 이 리팩터링은 적절하지않다.

  1. actionPermittedaction 호출 사이에 객체의 상태가 변할 수 있다.
  2. actionPermittedaction 메서드의 작업 일부를 중복 수행한다면 성능에서 손해이다.