Search

Item 73. 추상화 수준에 맞는 예외를 던져라

생성일
2023/08/15 05:13
챕터
10장 - 예외

요약

저수준 예외를 잘 처리하고, 고수준에서 조용히 처리하고 로깅을 남겨두자.
그렇지 않다면 예외 번역과 연쇄 예외를 잘 활용하자.

예외 번역

메서드가 저수준의 예외를 처리하지 않고 바깥으로 전파하는 경우, 종종 수행하려는 일과 관련없어 보이는 예외가 튀어나오게 된다.
이는 내부 구현 방식을 드러내어 상위 레벨의 API를 오염시키기 때문에 문제가 된다. 다음 릴리스에서 구현 방식을 바꾸면 다른 예외가 튀어나와 기존 클라이언트 프로그램을 깨지게 할 수도 있다.
이러한 문제를 피하려면 상위 계층에서는 저수준 예외를 잡아 자신의 추상화 수준에 맞는 예외로 바꾸어 던져야한다. 이를 예외 번역(exception translation)이라 한다.
try { ... // 저수준 추상화를 이용한다. } catch (LowerLevelException e) { // 추상화 수준에 맞게 번역한다. throw new HigherLevelException(...); }
Java
복사
아래는 List 인터페이스의 골격 구현 클래스인 AbstractSequentialList의 get 메서드이다.
/** * 이 리스트 안의 지정한 위치의 원소를 반환한다. * @throws IndexOutOfBoundsException index가 범위 밖이라면, * 즉 ({@code index < 0 || index >= size()})이면 발생한다. */ public E get(int index) { ListIterator<E> i = listIterator(index); try { return i.next(); } catch (NoSuchElementException e) { throw new IndexOutOfBoundsException("인덱스: " + index); } }
Java
복사
이 예외번역은 List<E> 인터페이스의 get 메서드 명세에 명시된 필수사항이다.

예외 연쇄

예외 번역을 사용할 때, 저수준 예외가 디버깅에 도움이 된다면 예외 연쇄(exception chaining)를 사용하는 것이 좋다. 예외 연쇄란 근본 원인인 저수준 예외를 고수준 예외에 담아서 보내어, 필요하면 언제든 저수준 예외를 꺼내볼 수 있는 방식이다.
try { ... // 저수준 추상화를 이용한다. } catch (LowerLevelException cause) { throw new HigherLevelException(cause); } // 예외 연쇄용 생성자 class HigherLevelException extends Exception { HigherLevelException(Throwalbe cause) { super(cause); } }
Java
복사
대부분의 표준 예외는 예외 연쇄용 생성자를 갖추고 있다. 그렇지 않더라도 Throwalbe의 initCause 메서드를 사용해 예외의 원인을 직접 설정할 수 있다.

결론

예외를 무턱대고 전파하는 것보다는 예외 번역을 사용하는 것이 더 좋지만, 예외 번역을 남용해서는 안된다.
가능하다면 저수준 메서드가 반드시 성공하도록하여, 아래 계층에서는 예외가 발생하지 않도록 하는 것이 최선이다.
아래 계층에서의 예외를 피할 수 없다면, 상위 계층에서 해당 예외를 조용히 처리하여 문제를 API 호출자까지 전파하지 않는 방법을 차선책으로 하자. 이런 경우에는 로그를 분석해 추가 조치를 취할 수 있게, 적절한 로깅 기능을 활용해 기록해두는 것이 좋다.