Posts [이펙티브자바] 아이템75-예외의 상세 메시지에 실패 관련 정보를 담으라
Post
Cancel

[이펙티브자바] 아이템75-예외의 상세 메시지에 실패 관련 정보를 담으라

사후 분석을 위해 실패 순간의 상황을 정확히 포착해 예외의 상세 메시지에 담아야 한다.

  • 예외를 잡지 못해 프로그램이 실패하면 자바 시스템은 그 예외의 stack trace 를 자동으로 출력한다.
  • stack tract 는 예외 객체의 toString 메서드를 호출해 얻는 문자열로 보통은 예외 클래스 이름 뒤에 상세 메시지가 붙는 형태다.
  • 이 정보가 실패 원인을 분석해야 하는 개발자들에겐 유일한 정보일 경우가 많기 때문에 예외의 메시지를 상세하게 담는게 중요하다.

실패 순간을 포착하려면 발생한 예외의 관여된 모든 매개변수와 필드의 값을 실패 메시지에 담야아 한다.

  • 예컨대 IndexOutOfBoundsException 의 상세 메시지는 범위 최솟값, 최대값, 그리고 그범위를 벗어났다는 인덱스의 값을 담아야 한다.
    • 이 정보는 명확하게 어떤 것이 잘못되었는지 실패에 관한 많은 것을 알 수 있게 해준다.
  • 인덱스의 범위가 벗어나는데는 다양한 이유가 존재할 수 있다.
    • 인덱스가 최솟값보다 1만큼 작거나 최대값과 같을 경우(인덱스는 0부터 시작하므로 최댓값과 같으면 안된다.)
    • 범위를 아주 크게 벗어난 경우
    • 최솟값이 최대값보다 클 경우(내부의 불변식이 심각히 깨진 경우)
  • 이상의 현상들은 모두 원인이 다르므로, 현상을 자세히 남기면 남길수록 무엇을 개선해야 할지 개발자들에게 큰 도움이 될 것이다.

Note: 문제를 진단하고 해결하는 과정에서 stack trace 를 많은 사람이 볼 수 있으므로 상세 메시지에 비밀번호나 암호 키 같은 정보까지 담아선 안된다.

예외 분석에 필요한 데이터(정보)만 담아라.

  • 관련 데이터를 모두 담아야 하지만 너무 장황할 필욘 없다.
  • 문제를 분석하는 사람은 스택 추적뿐 아니라 관련 문서와 (필요하다면) 소스코드를 함꼐 살펴볼 것인데 어차피 어떤 클래스의 몇번째 라인에서 문제가 생겼는지 아주 상세하게 나올 것이기 때문이다.

예외의 상세 메시지와 최종 사용자에게 보여줄 오류 메시지를 혼동해선 안된다.

  • 최종 사용자에겐 친절한 안내 메시지를 보여줘야 하는 반면, 예외 메시지는 가독성보단 담긴 내용이 훨씬 중요하다.
    • 최종 사용자용 메시지는 주 소비층이 문제를 분석해야 하는 개발자일 것이기 때문이다.
  • 최종 사용자용 메시지는 현지어로 번역해주기도 하지만, 예외 메시지는 그런 일이 거의 없다.

실패를 정확하게 포착하려면 필요한 정보를 예외 생성자에서 모두 받아서 상세 메시지까지 미리 생성해놓는 방법도 괜찮다.

  • 예를 들어 현재의 IndexOutOfBoundsException 생성자는 String을 받지만, 다음과 같이 구현했어도 좋았을 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
 * IndexOutOfBoundsException을 생성한다.
 *
 * @param lowerBound 인덱스의 최솟값
 * @param upperBound 인덱스의 최댓값 + 1
 * @param index 인덱스의 실젯값
 */
public IndexOutOfBoundsException(int lowerBound, int upperBound, int index) {
   // 실패를 포착하는 상세 메시지를 생성한다.
   super(String.format("최솟값: %d, 최댓값: %d, 인덱스: %d", lowerBound, upperBound, index));
   
   // 프로그램에서 이용할 수 있도록 실패 정보를 저장해둔다.
   this.lowerBound = lowerBound;
   this.upperBoudn = upperBound;
   this.index = index;
}
  • 자바9에선 IndexOutOfBoundsException 에 드디어 정수 인덱스 값을 받는 생성자가 추가되었다. 하지만 아쉽게도 최솟값과 최댓값까지 받지는 않는다.
  • 그리고 또한 고품질의 상세 메시지를 만들어내는 코드를 예외 클래스 안으로 모아주는 효과도 있어, 클래스 사용자가 메시지를 만드는 작업을 중복하지 않아도 된다.

Note: JPA 사용시 findById로 조회 후 없을 경우 예외를 발생시킨다 했을때 EntityNotFoundException 이란 예외 클래스를 만들어 해당 예외 클래스 내부에서 필요한 필드를 모두 생성자로 받아서 상세 메시지를 미리 생성해놓을 수 있을 것이다.

아이템70에서 제안하였듯, 예외는 실패와 관련한 정보를 얻을 수 있는 접근자 메서드를 적절히 제공하는 것이 좋다

  • 앞의 예에서라면 lowerBound, upperBoudn, index 정도가 적당할 것이다.
  • 포착한 실패 정보는 예외 상황을 복구하는데 유용할 수 있으므로, 접근자 메서드는 비검사 예외보단 검사 예외에서 더 빛을 발한다.
  • (전혀 없지는 않겠지만) 비검사 예외의 상세 정보에 프로그램적으로 접근하길 원하는 프로그래머는 드물 것이다.
    • 하지만 toString 이 반환한 값에 포함된 정보를 얻어올 수 있는 API를 제공하자’ 하는 일반 원칙(75쪽)을 따른다는 관점에서, 비검사 예외라도 상세 정보를 알려주는 접근자 메서드를 제공하라고 권한다.
This post is licensed under CC BY 4.0 by the author.

[이펙티브자바] 아이템73-추상화 수준에 맞는 예외를 던지라

[Database] charset과 collation