오늘의 주제는 Exception이다. 최근 공통 프로젝트에서 무지성 200 OK를 반환하는 바람에, 이제 슬슬 예외 처리에 대한 필요성을 느끼고 있다. 더 깔끔한 코드를 작성하기 위해서 이펙티브 자바를 읽으며 공부해 본 내용을 공유하겠다.
표준 예외를 사용하자
표준 예외는 이미 많은 개발자들에게 익숙한 예외들이다. 그렇기 때문에 다른 개발자들이 읽기 쉽고 사용하기 쉬워진다. 또한 예외 클래스 수가 적어질수록 메모리 사용량과 클래스 적재 시간이 줄어든다. Java 라이브러리에서 대부분의 API를 커버할 수 있는 예외를 제공하기 때문에 표준 예외로도 충분하다.
가장 자주 재사용하는 Exception 몇 가지를 소개해보겠다.
IllegalArgumentException | 허용하지 않는 값이 parameter로 건네졌을 때 (null은 관례상 NullPointerException으로 처리한다.) |
IllegalStateException | 객체가 메서드를 수행하기에 적절하지 않은 상태일 때 |
NullPointerException | null을 허용하지 않는 메서드에 null을 건넸을 때 |
IndexOutOfBoundsException | 인덱스가 범위를 넘어섰을 때 |
ConcurrentModificationException | 허용하지 않는 동시 수정이 발견됐을 때 |
UnsupportedOperationException | 호출한 메서드를 지원하지 않을 때 |
ArithmeticException, NumberFormatException | 산수나 수 변환에서 에러가 발생했을 때 |
상황에 부합한다면 표준 예외를 사용하는 것이 좋다. 다만, API 문서를 읽고 Exception이 던져지는 상황에 대한 이해는 필수적이다.
예외 번역(exception translation) 하자
호출한 메서드에서 발생한 Exception을 무턱대고 전파했던 기억이 있다. 그렇다면 그 예외는 결국 메서드가 하는 일과 전혀 관련없는 예외처럼 보여서 정확히 에러의 원인을 알기 힘들다. 이 문제를 해결하기 위해서 예외 번역을 사용하자.
예외 번역은 상위 계층에서 저수준의 예외를 잡아서 자신의 추상화 수준에 맞는 예외로 바꿔던지는 것이다.
try {
// 저수준의 추상화 이용
...
} catch (LowerLevelException e) {
// 예외 번역
throw new HigherLevelException();
}
넓은 범위의 Exception을 좀 더 구체화해서 던지면 좋다는 의미이다. 예외를 계속해서 넘기는 것보다 의미를 구체화해서 다시 던져주는 게 더 좋지만, 아래 계층에 parameter 값을 전달하기 전에 검증해서 예외가 발생하지 않도록 하거나 현재 계층에서 Exception을 처리하고 logging 하는 것도 좋은 방법이다.
예외 메세지에 발생 원인 정보를 적자
예외를 잡지 못해서 프로그램이 실패했을 때 자바 시스템에서는 stack trace 정보를 자동으로 출력하지만 경우에 따라서 Exception의 message만 볼 수 있을 때가 있다. 만약 예외 상황을 재현하지 못할 때는 얻을 수 있는 정보가 해당 message가 전부일 때가 있다.
message에 예외가 터진 상황에 대한 정보를 담으려면 예외에 관여된 모든 매개변수와 필드의 값을 Exception message에 담아야 한다. 만약 특정 변수에 null이 담겨서 NullPointerException이 터졌다면 해당 변수명과 null이라는 정보를 담아주면 된다.
다만, Exception message와 최종 사용자에게 보여줄 오류 메시지는 별개이다. 최종 사용자에게는 가독성 좋은 메세지를 보여줘야 하고 Exception message는 Exception이 터지는 상황에 대한 정보를 담는 것이 가장 중요하다.
예외를 무시하지 말자
메서드 선언에 Exception이 있다는 것은 해당 Exception에 대한 처리를 요구하는 것이다. 예외는 try-catch문으로 잡아버리면 그만이지만 catch문을 비워서는 안 된다.
가끔 Exception을 무시해야할 때도 있는데 그럴 때는 주석을 남기고 Exception 변수명을 ignored로 바꿔두자.
try {
url = URLDecoder.decode(url, "UTF-8");
} catch (UnsupportedEncodingException ignored) {
// UTF-8 사용
}
글을 마치며
지금까지 Exception을 깔끔하게 사용하기 위한 내용들을 알아봤다. 굉장히 기본적인 내용들이지만 개발하다 보면 귀찮아서 자연스럽게 코드를 이상하게 적는다. 🥲
try {
} catch (Exception e) {
e.printStackTrace();
}
사소한 곳에서 오는 비용이 꽤 크다. 다른 팀원들과 협업하며 개발하면서 사소한 걸 놓치면 의사소통이 잘 되지 않는다는 걸 느끼기도 했고, 결국 코드가 더러워져서 리팩토링에도 큰 시간을 할애해야 한다. 기본적인 내용들을 지키며 개발하도록 노력해야겠다.
'Java' 카테고리의 다른 글
븟츠의 clone 재정의는 항상 조심하자! (0) | 2024.08.25 |
---|---|
자바 코딩 컨벤션 정리 (0) | 2024.08.18 |
븟츠의 JWT 소개 및 정리 (0) | 2024.08.03 |
븟츠의 try-finally 보다는 try-with-resources를 사용하자! (0) | 2024.07.28 |
만두의 불변객체(Immutable Object)와 record (0) | 2024.07.27 |