예외의 상세 메시지에 실패 관련 정보를 담으라 - Effective Java[75]

2 minute read

🔗 예외의 toString 메서드에 실패 원인에 관한 정보를 가능한 한 많이 담아 반환하는 일은 아주 중요하다.
  • 예외를 잡지 못해 프로그램이 실패하면 자바 시스템은 그 예외의 스택 추적(stack trace) 정보를 자동으로 출력한다.

  • 스택 추적은 예외 객체의 toString 메서드를 호출해 얻는 문자열로, 보통은 예외의 클래스 이름 뒤에 상세 메시지가 붙는 형태다.

    • 이 정보가 실패 원인을 분석해야하는 프로그래머 혹은 사이트 신뢰성 엔지니어가 얻을 수 있는 유일한 정보인 경우가 많다.

    • 더구나 그 실패를 재현하기 어렵다면 더 자세한 정보를 얻기가 어렵거나 불가능하다.

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


💎 실패 순간을 포착하려면 발생한 예외에 관여된 모든 매개변수와 필드의 값을 실패 메시지에 담아야 한다.
  • ex) IndexOutOfBoundsException의 상세 메시지는 범위의 최솟값과 최댓값, 그리고 그 범위를 벗어났다는 인덱스의 값을 담아야 한다.

    • 이 정보는 실패에 관한 많은 것을 알려준다.

    • 셋 중 한두 개 혹은 셋 모두가 잘못됐을 수 있다.

      • ex) 인덱스가 최솟값보다 1만큼 작거나 최댓값과 같을 수도 있다(인덱스는 0부터 시작하므로 최댓값과 같으면 안 된다.)
    • 혹은 범위를 아주 크게 벗어났을수도, 심지어 최솟값이 최댓값보다 클 수도 있다(내부의 불변식이 심각히 깨진 경우다.)

  • 이상의 현상들은 모두 원인이 다르므로, 현상을 보면 무엇을 고쳐야 할지를 분석하는 데 큰 도움이 된다.

    • 보안과 관련한 정보는 주의해서 다뤄야 한다.
      • 문제를 진단하고 해결하는 과정에서 스택 추적 정보를 많은 사람이 볼 수 있으므로 상세 메시지에 비밀번호나 암호 키 같은 정보까지 담아서는 안 된다.

💎 관련 데이터를 모두 담아야 하지만 장황할 필요는 없다.
  • 문제를 분석하는 사람은 스택 추적뿐 아니라 관련 문서와 (필요하다면) 소스코드를 함께 살펴본다.

  • 스택 추적에는 예외가 발생한 파일 이름과 줄번호는 물론 스택에서 호출한 다른 메서드들의 파일 이름과 줄번호까지 정확히 기록되어 있는 게 보통이다.

    • 그러니 문서와 소스코드에서 얻을 수 있는 정보는 길게 늘어놔봐야 군더더기가 될 뿐이다.

💎 예외의 상세 메시지와 최종 사용자에게 보여줄 오류 메시지를 혼동해서는 안 된다.
  • 최종 사용자에게는 친절한 안내 메시지를 보여줘야 하는 반면, 예외 메시지는 가독성보다는 담긴 내용이 훨씬 중요하다

    • 예외 메시지의 주 소비층은 문제를 분석해야 할 프로그래머와 SRE 엔지니어이기 때문이다.

    • 또한 최종 사용자용 메시지는 현지어로 번역해주기도 하지만, 예외 메시지는 그런일이 거의 없다.


💎 실패를 적절히 포착하려면 필요한 정보를 예외 생성자에서 모두 받아서 상세 메시지까지 미리 생성해놓는 방법도 괜찮다.
  • 예를 들어 현재의 IndexOutOfBoundsException 생성자는 String을 받지만, 다음과 같이 구현했어도 좋았을 것이다.
/**
* 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.upperBound = upperBound;
    this.index = index;
}
  • 자바 9에서는 IndexOutOfBoundsException에 드디어 정수 인덱스를 받는 생성자가 추가 되었다.

    • 하지만 아쉽게도 최솟값과 최댓값까지 받지는 않는다.
  • 위 코드처럼 해두면 프로그래머가 던지는 예외는 자연스럽게 실패를 더 잘 포착한다.

    • 실패를 포착하지 못하게 만드는 게 오히려 더 어려울 것이다.

    • 또한 고품질의 상세 메시지를 만들어내는 코드를 예외 클래스 안으로 모아주는 효과도 있어, 클래스 사용자가 메시지를 만드는 작업을 중복하지 않아도 된다.


💎 예외는 실패와 관련한 정보를 얻을 수 있는 접근자 메서드를 적절히 제공하는 것이 좋다.
  • 포착한 실패 정보는 예외 상황을 복구하는 데 유용할 수 있으므로 접근자 메서드는 비검사 예외보다는 검사 예외에서 더 빛을 발한다.

  • (전혀 없지는 않겠지만) 비검사 예외의 상세 정보에 프로그램적으로 접근하길 원하는 프로그래머는 드물 것이다.

    • 하지만 ‘toString이 반환한 값에 포함된 정보를 얻어올 수 있는 API를 제공하자’하는 일반 원칙을 따른다른 관점에서, 비검사 예외라도 상세 정보를 알려주는 접근자 메서드를 제공하자.
참조 - 이펙티브 자바 3/E - 조슈아 블로크때

Categories:

Updated:

Comments