본 문서는 2021년 12월 23일 에 작성되었다.
원제목 | 예외는 진짜 예외 상황에만 사용하라
예외는 오직 예외 상황에서만 써야한다.
절대로 일상적인 제어 흐름용으로 쓰여선 안된다.
그렇다면 어떠한 에러 상황에 대한 대처는 어떻게 해야 하는가?
다음 세 가지 방법이 존재한다.
원제목 | 복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라
자바는 문제상황을 알리는 타입인 throwable 으로 다음을 지원한다.
다음을 결정짓는 상황은 다음과 같다.
또한 검사 예외라면 복구에 필요한 정보를 제공하는 메서드도 제공하자
원제목 | 필요 없는 검사 예외 사용은 피하라
사실 개발자 커뮤니티에서도 try-catch 에 대해서 부정적인 의견을 가지고 계신 분들이 종종 있었다. 선생님들의 의견은 주로 try-catch 를 무지성으로 사용하면 무책임한 개발자 라고 생각하셨다.
기본적으로 에러를 던지고 받는 것은 그렇게 좋은 방법이 아니다.
그 이유는 에러를 던지는 메서드는 스트림에서 사용할 수 없기 때문이다.
물론,
어쩔 수 없는 상황 혹은 특정한 상황에서는 좋은 선택이 될 수 있다.
그 중 하나는,
이미 에러가 발생하고 있는 곳에서 검사 에러/비검사 에러를 한 번 더 던지는 것은 어떤가에 대한 생각이다.
} catch (TheCheckedException e) {
e.printStackTrace(); // 이런, 우리가 졌다!
System.exit(1);
}
} catch (TheCheckedException e) {
throw new AssertionError(); // 내가 졌다!
}
} catch (TheCheckedException e) {
return Optional<T>; // 빈 옵셔널 반환
}
원제목 | 표준 예외를 사용하라
예외 | 주요 쓰임 |
---|---|
IllegalArgumentException | 허용하지 않는 값이 인수로 건네졌을 때 null 은 따로 NullPointerException 으로 처리 |
IllegalStateException | 객체가 메서드를 수행하기에 적절하지 않은 상태일 때 |
NullPointerException | null 을 허용하지 않는 메서드에 null 을 건넸을 때 |
IndexOutOfBoundsException | 인덱스가 범위를 넘어섰을 때 |
ConcurrentModificationException | 허용하지 않는 동시수정이 발견됐을 때 |
UnsupportedOperationException | 호출한 메서드를 지원하지 않을 때 |
ArithmeticException | 복소수 |
NumberForamtException | 유리수 |
원제목 | 추상화 수준에 맞는 예외를 던져라
예외 번역이란 상위 계층에서 저수준 예외를 잡아 자신의 추상화 수준에 맞는 에러로 던지는 것을 의미한다.
가능한 한 저수준 에러를 저수준에서 해결하되,
그렇지 못한 경우에는 예외 번역 을 활용하여 정볼르 취합하여
API 호출자에게 전해주거나 logging 같은 라이브러리를 사용하여 기록하는 것이다.
원제목 | 메서드가 던지는 모든 예외를 문서화해라
메서드가 던질 수 있는 예외를 각각 @throws 태그로 문서화하되,
비검사 예외는 메서든 선언의 throws 목록에 넣자.
한 클래스의 많은 메서드가 같은 이유로 예외를 던진다면
그 예외를 클래스의 설명에 추가하는 방법도 있다. (NullPointerException)
원제목 | 예외의 상세 메세지에 실패 관련 정보를 담으라
예외의 상세 메세지와 최종 사용자에게 보여줄 오류 메세지를 혼동하지 말자.
예외의 상세 메세지는 개발자 혹은 DevOps 라인에서 확인할 정보이다.
스텍 추적에는 예외가 발생한 파일 이름과 줄 번호는 물론 스텍에서 호출한 다른 메서드들의 파일 이름과 줄번호까지 정확히 기록되어 있는게 보통이다.
원제목 | 가능한 실패 원자적으로 만들라
일반화해서 이야기하면,
호출된 메서드가 실패하더라도 해당 객체는 메서드 호출전 상태를 유지해야 한다.
이를 실패 원자적(Failure atomic) 이라고 한다.
원제목 | 예외를 무시하지 말라
예외를 무시한다라는 것은 try-catch 에서 catch 의 필드 { } 안을 비워두는 것을 의미한다.
try{
// 에외를 발생시킬 수 있는 부분
}catch(SomeException e) {
// 비워두지 말자
}
만약 예외를 무시할 이유가 있다면 다음과 같이 처리하자
try{
// 예외를 발생시킬 수 있는 부분
} catch (SomeException ingnored) {
// 왜 예외를 무시하기로 하였는지
}