도당탕탕

Item71 : 필요 없는 검사 예외 사용은 피하라 본문

JAVA

Item71 : 필요 없는 검사 예외 사용은 피하라

backlo 2023. 1. 30. 15:10

검사 예외를 싫어하는 자바프로그래머가 많지만 제대로 활용하면 API와 프로그램의 질을 높일 수 있다. 검사 예외를 사용하면 문제를 프로그래머가 처리하여 안정성을 높이게끔 해주지만, 과하게 사용하면 오히려 쓰기 불편한 API가 된다.

어떤 메서드에서 검사 예외를 과하게 사용하면 이를 호출하는 코드에서 catch 블록을 두어 그 예외를 붙잡아 처리하거나, 더 바깥으로 던져 문제를 전파해야만 한다. 더욱이 자바 8에 stream에서는 직접 사용할 수 없기 때문에 부담이 더욱 커졌다.

검사 예외의 문제

API를 제대로 사용해도 발생할 수 있는 예외이거나, 프로그래머가 의미 있는 조치를 취할 수 있는 정도라면 이 정도의 부담쯤은 받아들일 수 있을 것이다. 하지만 그렇지 않더라면 비검사 예외를 사용하는 게 좋다. 다음 예를 보자.

  • 검사 예외 코드
// throw 하는 방법
} catch (TheCheckedException e) {
  throw new AssertionError();
}

// 로직으로 처리하는 방법
} catch (TheCheckedException e) {
  e.printStackTrace();
  System.exit(1);
}

위 예는 검사 예외라 catch 문으로 처리하는데 throw나 시스템 종료라는 로직이 비검사 예외와 차이가 없어 보인다. 따라서 별 다른 방법이 없으면 비검사 예외를 선택해야 한다.

또한 검사 예외는 하나의 예외라도 try-catch문 블록을 써야 하기 때문에 불편하다는 단점이 있다.

검사 예외 회피 방법

검사 예외를 회피하는 가장 쉬운 방법은 적절한 결과 타입을 담은 옵셔널을 반환하는 것이다. 검사 예외를 던지는 대신 단순히 빈 옵셔널을 반환하면 된다.

이 방식의 단점이라면 예외가 발생한 이유를 알려주는 부가 정보를 담을 수 없다는 것이다. 반면 예외를 사용하면 구체적인 예외 타입과 그 타입이 제공하는 메소드들을 활용해 부가 정보를 제공할 수 있다.

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

  • 검사 예외를 던지는 메소드 - 리팩토링 전
try {
    obj.action(args);
} catch (TheCheckedException e) {
    ... // 예외 상황에 대처한다.
}
  • 상태 검사 메소드와 비검사 예외를 던지는 메서드 - 리팩토링 후
if (obj.actionPermitted(args)) { // 예외가 던져질지 여부를 boolean 값으로 표현 - 상태 검사 메소드
    obj.action(args);
} else {
    ... // 예외 상황에 대처한다.
}

이 리팩터링을 모든 상황에서 적용할 수는 없다. 그래도 적용할 수만 있다면 더 쓰기 편한 API를 제공할 수 있다. 리팩터링 후의 API가 딱히 더 아름답지 않지만, 더 유연한 건 사실이다. 만약 프로그래머가 이 메서드가 성공하리라는 걸 안다거나, 실패 시 스레드를 중단하기 원한다면 obj.action(args); 그자체를 사용해 한 줄로 작성해도 무방하다.

상태 검사 메서드는 외부 동기화 없이 여러 스레드가 동시에 접근할 수 있거나 외부 요인에 의해 상태가 변할 수 있기 때문에 만약 그러한 상황이라면 위와 같은 리팩토링은 적절하지 않다. 또한 actionPermitted가 action메서드의 작업 일부를 중복 수행한다면 성능에서 손해이니, 이 역시 적절한 리팩토링이 아니라고 할 수 있다.

정리

  • 따라서 정리하자면 꼭 필요한 곳에만 검사 예외를 사용한다면 검사 예외는 프로그램의 안정성을 높여주지만, 남용하면 쓰기 고통스러운 API를 낳는다.
  • API 호출자가 예외 상황에서 복구할 방법이 없다면 비검사 예외를 던지자.
  • 복구가 가능하고 호출자가 그 처리를 해주길 바란다면, 우선 옵셔널을 반환해도 될지 고민하자.
  • 옵셔널만으로는 상황을 처리하기에 충분한 정보를 제공할 수 없을 때만 검사 예외를 던지자.
Comments