개발을 하다보면 서비스 로직에서 많은 예외 처리를 해야할 때도 있고, 커스텀 예외를 사용하여서 분류별로 계층구조를 활용해 처리하는 등의 전략으로 예외를 책임분리시켜 개발하고 할 때도 있습니다.
하지만 커스텀 예외를 할 때 진짜 예외 상황을 구분할 줄 알고, 어느 순간에는 커스텀 예외를 하지말아야 하는지를 알게 되면 개발 환경에 있어서 복잡성이 눈에 띄게 낮게 될 것입니다. 우선 오류와 예외의 구분과 자주 쓰이는 예외를 통해 커스텀 예외 처리 방법을 접해서 설명을 해보겠습니다.
오류와 예외
오류와 예외는 발생 원인과 처리 방법에서 큰 차이가 있습니다. 오류는 외부 요인에 의해 발생하는 문제로, 예를 들어 OutOfMemoryError, StackOverflowError와 같은 것이 있습니다.
이는 개발자가 사전에 예측하기 어렵고, 설령 예측하더라도 수정이 불가능한 경우가 많습니다. 이러한 오류는 시스템의 심각한 문제를 나타내며, 예를 들어 사용자가 프로그램을 정상적으로 사용할 수 없게 되는 상황을 초래할 수 있습니다.
반면, 예외는 주로 개발자의 로직 실수나 잘못된 입력값 처리, 누락된 검증 등으로 인해 발생합니다. 예외는 비교적 덜 심각하며, 사전에 대비하고 적절히 처리할 수 있습니다. 예를 들어, 입력값 검증을 통해 발생 가능성을 줄이거나 try-catch 문을 사용해 처리할 수 있으며 개발자의 역량에 따라 생산성이 좌우 되는 부분입니다.
또한 커스텀 예외도 중요하지만 예외 발생 시 로그를 확인하는 것도 중요합니다. 예를 들어 다음 코드처럼 예외 메시지와 발생 위치를 확인할 수 있습니다
try {
// 코드 실행
} catch (Exception e) {
e.printStackTrace(); // 로그로 예외 메시지와 발생 위치 확인
}
커스텀 예외 생성 방법과 전략
개발자는 필요에 따라 커스텀 예외를 정의하기도 하며 커스텀 예외를 정의하려면 다음과 같은 방법을 사용합니다:
public class CustomException extends Exception {
public CustomException(String message) {
super(message);
}
}
커스텀 예외는 RunTime 혹은 Exception 둘 중 하나를 상속받아 사용하게 되는데요 이 두개는 체크 or 언체크로 구분짓게 됩니다. 다들 알다싶이 RunTimeException은 언체크, Exception은 체크 예외입니다.
체크 예외는 컴파일 단계에서 예외 처리를 강제하기에 예외를 놓칠 수 없게 만들어 개발자의 실수를 방지할 순 있지만 그만큼 예외 핸들링 코드가 많아지고 의존성이 높아지게 될 수 있습니다.
언체크드 예외는 실행 시점에서 예외를 처리하며 유연하게 처리할 수 있지만 예외를 놓칠 가능성이 있습니다.
추가로 커스텀 예외는 끝에 Exception을 붙이는 것이 관례이며, 커스텀 예외의 제목만으로 개발자가 눈에 띄게 예외를 구분할 수 있기에 좋게되지만 너무 남발하게 되면 오히려 복잡성이 증가하게 되고 또한 개발자의 실수로 인해 이름이 정상적이지 못한 경우 오히려 생산성이 약화가 될 수 있으니 커스텀 예외를 해야 할 때를 잘 구분해야 합니다.
커스텀 예외 전략 1: 표준 예외 클래스를 사용하라
표준 예외 클래스를 적극 활용하는 것도 중요한 전략입니다. 즉 주의해야 할 건 아래와 같이 대중적인 예외 상황임에도 불구하고 커스텀 예외를 나타내어서 클래스 이름으로 에러를 좀 더 명확히 나타내고 싶은 욕심에 오히려 클래스를 늘리게 되어 복잡성이 증가하게 될 수 있습니다. 예를 들면 이러한 것들이 있습니다.
이렇게 대중적으로 유명한 예외이고 개발하는 입장에서도 무슨 예외를 처리하는지 쉽게 보이는 상황에서 커스텀 예외를 무리하게 추가하게 되면, 꼬리에 꼬리를 물듯이 예외 클래스가 자칫하면 너무 많아질 수 있습니다.

| IOException | 입출력 작업 중 발생 |
| FileNotFoundException | 존재하지 않는 파일에 접근할 때 발생 |
| IllegalArgumentException | 잘못된 값이 전달된 경우 |
| NullPointerException | null 값에 접근하려고 할 때 발생 |
| IndexOutOfBoundsException | 인덱스 범위 초과할 때 발생 |
커스텀 예외 전략 2: 진짜 예외와 가짜 예외를 구분할 줄 알아야 한다
사실 커스텀 전략의 핵심은 이것이 예외를 처리해야 하는 상황이냐 아니냐를 구분할 줄 알아야 된다는 것입니다. 즉 가짜 예외는 조건문으로 충분히 처리할 수 있는 상황을 의미하며 예외를 방지하고, 확실한 로직으로 풀어나갈 수 있음에도 무리하게 예외를 잡지 않느지부터 확인을 해야 예외 발생 건 수 자체를 낮추게 할 수 있습니다.
다음은 잘못된 예외 처리와 개선된 코드의 예입니다:
잘못된 예외 처리:
try {
j1.rideSubway();
j1.buyBritto();
j1.success();
} catch (ClosedFoodStore e) {
j1.hungry(); // 브리또를 먹고 싶은데 예외 발생
} finally {
j1.arriveHome();
}
개선된 코드
if (!store.isClosed()) {
j1.buyBritto();
j1.success();
} else {
j1.arriveHome();
}
조건문으로 처리 가능한 상황에서는 예외 처리를 남용하지 않고, 진짜 예외 상황에만 예외 처리를 사용하는 것이 좋으며 표준 예외를 활용하고, 필요한 경우에만 커스텀 예외를 추가하여 코드의 가독성과 유지보수성을 높이는 것이 바람직합니다.
커스텀 예외는 상황에 따라 매우 유용한 도구가 될 수 있지만, 그 사용에는 신중함이 필요합니다. 표준 예외를 우선적으로 활용하고, 필요할 때에만 커스텀 예외를 도입하여 코드의 복잡성을 최소화하세요. 또한, 조건문으로 처리할 수 있는 상황에서는 예외를 사용하지 않는 것이 좋습니다. 이러한 전략을 통해 개발 환경의 복잡성을 줄이고 유지보수성을 높일 수 있습니다.
'Java' 카테고리의 다른 글
| [동시성 문제] - HashMap / ConcurrentHashMap (0) | 2024.08.25 |
|---|---|
| Java (람다와 함수 인터페이스) (1) | 2024.01.07 |
| Java (익명 클래스) (1) | 2024.01.06 |
| Java (컬렉션 프레임워크) (0) | 2024.01.06 |
| Java (제네릭스) (1) | 2024.01.04 |