티스토리 뷰
CustomException
백엔드 시스템을 개발하다 보면 정상적인 흐름에서 벗어난 다양한 예외 상황을 만나게 됩니다. 이러한 예외를 어떻게 처리하느냐에 따라 코드의 가독성, 유지보수성, 그리고 일관성이 크게 달라집니다.
이 글에서는 Custom Exception을 사용했을 때의 이점과, Spring에서 이를 어떻게 효율적으로 처리할 수 있는지를 다뤄보겠습니다.
1. CustomException 없이 예외 처리할 때
조건 분기 방식
가장 많이 접하는 형태입니다. 분기를 사용하여 예외처리를 한 코드이다. 해당 코드가 나쁜 코드라고 단언할 수 없으나 예외 상황에 대해 의미 전달력이 떨어지고 개발자마다 예외 메시지 등 일관적인 처리가 달라질 가능성이 충분하다. 그리고 예외 상황마다 반복적으로 나타나는 코드가 견디기 힘들다.
public Map<String, Object> getUser(String userId) {
Map<String, Obejct> result = new HashMap<>();
User user = userRepository.findById(userId);
if (user == null) {
result.put("code", 404);
result.put("message", "사용자 없음");
return result;
}
if (!user.isActive()) {
result.put("code", 404);
result.put("message", "비활성 사용자");
return result;
}
result.put("code", 200);
result.put("message", "success");
result.put("data", user);
return result;
}
RuntimeException 직접 사용
이 방법은 코드가 간결해지지만, 예외가 어떤 종류인지 분류하기 어렵고, 전역적으로 구분도 불가능해집니다.
if (user == null) {
throw new RuntimeException("사용자 없음");
}
2. CustomException을 사용한 예외
예외 클래스 정의
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(Long id) {
super("사용자 ID " + id + "를 찾을 수 없습니다.");
}
}
public class InactiveUserException extends RuntimeException {
public InactiveUserException(Long id) {
super("사용자 ID " + id + "는 비활성 상태입니다.");
}
}
사용 예시
public User getUser(Long userId) {
User user = userRepository.findById(userId);
if (user == null) {
throw new UserNotFoundException(userId);
}
if (!user.isActive()) {
throw new InactiveUserException(userId);
}
return user;
}
CustomException을 사용하면 예외 발생 시 일관적인 메시지 반환이 가능하고 코드의 흐름상 의미 전달도 명확하게 된다.
3. Spring을 활용한 예외 처리 전략
Spring에서는 @ControllerAdvice와 @ExceptionHandler를 통해 예외 상황을 전역에서 일관되게 처리할 수 있습니다.
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse("USER_NOT_FOUND", e.getMessage()));
}
@ExceptionHandler(InactiveUserException.class)
public ResponseEntity<ErrorResponse> handleInactiveUser(InactiveUserException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse("INACTIVE_USER", e.getMessage()));
}
}
전역 예외처리는 기본적으로 모든 Controller에 적용되지만 패키지나, 개별 컨트롤러를 지정하여 처리 범위를 제한할 수 있다.
@ControllerAdvice(basePackages = "com.example.controllers")
@ControllerAdvice(assignableTypes = {UserController.class, ProductController.class})
예외 처리시 클라이언트에게 전달하는 응답값 ResponseEntity는 아래와 같은 구조를 이루고 있다.
new ResponseEntity<>(body, status);
new ResponseEntity<>(body, headers, status);
new ResponseEntity<>(status);
결론
CustomException은 예외 상황을 의미 있게 설명 할 수 있는 수단이며, Spring의 예외 처리 기능과 결합하면 일관되고 의미 전달렸이 높은 API를 설계할 수 있습니다. 실무에서 예외 처리가 반복되거나 불명확하다면, 이 구조를 도입해보는 걸 추천합니다.
'라이브러리&프레임워크 > Spring' 카테고리의 다른 글
| 동시성 문제와 해결 전략 (비관적 락 vs 낙관적 락) (0) | 2025.05.10 |
|---|---|
| AOP란? (0) | 2025.05.02 |
| XML 설정 시 반드시 알아야 할 XSD와 namespace 개념 정리 (0) | 2025.04.24 |
| Spring 데이터 영속성 예제 (0) | 2025.04.07 |
| MVC 구조 웹 애플리케이션 개발 (0) | 2025.03.23 |
