Custom Exception
커스텀 예외란 자바의 기본 예외(RuntimeException, IllegalArgumentException 등)를 상속받아 비즈니스 로직에 맞는 의미 있는 예외 클래스를 직접 정의하는 것을 말한다.
자바에서 제공하는 기본 예외를 이용하여 try-catch문을 통해 예외 처리를 해줘도 오류 상황을 면할수는 있지만, 어떤 비즈니스 로직에서 어떤 원인으로 예외가 발생했는지 명확하게 표현하기 힘들다.
기본 예외를 서비스 계층에서 던지게 구현을 했다면 컨트롤러에서 try-catch문을 통해 모두 예외 상황에 맞는 HTTP 상태코드를 지정해줘야 한다.
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
@GetMapping("/{id}")
public ResponseEntity<?> getUser(@PathVariable Long id) {
try {
User user = userService.getUser(id);
return ResponseEntity.ok(user);
} catch (IllegalArgumentException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(Map.of("message", e.getMessage()));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Map.of("message", "서버 오류가 발생했습니다."));
}
}
}
이러한 과정 때문에 생기는 코드 중복 문제를 해결하고, 일관된 상태 메시지를 제공하기 위해 Custom Exception과 ExecptionHandler를 사용할 수 있다.
구현 과정
BaseException
@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BaseException extends RuntimeException {
HttpStatus statusCode;
String responseMessage;
public BaseException(HttpStatus statusCode) {
super();
this.statusCode = statusCode;
}
public BaseException(HttpStatus statusCode, String responseMessage) {
super(responseMessage);
this.statusCode = statusCode;
this.responseMessage = responseMessage;
}
public BaseException(ErrorStatus errorStatus) {
super(errorStatus.getMessage());
this.statusCode = errorStatus.getHttpStatus();
this.responseMessage = errorStatus.getMessage();
}
public int getStatusCode() {
return this.statusCode.value();
}
}
- 커스텀 예외 클래스들의 부모 클래스 역할을 수행
- RuntimeException을 상속
- HttpStatus, 에러 메시지를 받는 생성자
ErrorStatus
예외 상황에 맞는 상태코드와 메시지를 한곳에서 관리하는 enum이다.
@Getter
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
public enum ErrorStatus {
private final HttpStatus httpStatus;
private final String message;
NOT_FOUND_USER(HttpStatus.NOT_FOUND,"사용자를 찾을 수 없습니다."),
NOT_FOUND_SAVED_JOB(HttpStatus.NOT_FOUND, "저장 내역이 없습니다."),
//...
public int getStatusCode() {
return this.httpStatus.value();
}
}
Custom Exception
BaseException을 상속받은 구체적인 커스텀 예외 클래스이다.
각 상황에 맞는 ErrorStatus(상태코드, 에러 메시지)를 이용해 BaseExeption의 생성자를 호출해줌으로써 예외가 발생했을 때 HTTP 상태코드와 에러 메시지를 자동으로 세팅할 수 있다.
public class NotFoundException extends BaseException {
public NotFoundException() {
super(HttpStatus.NOT_FOUND);
}
public NotFoundException(String message) {
super(HttpStatus.NOT_FOUND, message);
}
public NotFoundException(ErrorStatus errorStatus) {
super(errorStatus);
}
}
GlobalExceptionHandler
GlobalExceptionHandler 클래스는 스프링 애플리케이션 전역에서 발생하는 모든 예외를 한 곳에서 처리하는 핵심 클래스이다.
@RestControllerAdvice와 @ExceptionHandler로 모든 컨트롤러에서 발생하는 예외를 감지하고 상황에 맞게 처리할 수 있도록 해준다.
@RestControllerAdvice
- 모든 @RestController에서 발생한 예외를 감지해 공통적으로 처리
@ExceptionHandler
- 특정 예외 타입이 발생했을 때 실행할 처리 로직을 지정
@RestControllerAdvice
public class GlobalExceptionHandler {
/** 커스텀 예외(BaseException) */
@ExceptionHandler(BaseException.class)
public ResponseEntity<ApiResponse<Void>> handleBase(BaseException ex) {
final int code = ex.getStatusCode();
final String msg = ex.getResponseMessage() != null
? ex.getResponseMessage()
: HttpStatus.valueOf(code).getReasonPhrase();
return ResponseEntity.status(code).body(ApiResponse.fail(code, msg));
}
// ...
}
의도적으로 커스텀 예외인 NotFoundException을 발생시켜보면 응답결과가 다음과 같이 상태코드, 메시지와 함께 출력되는 것을 확인할 수 있다.

'BackEnd > Spring Boot' 카테고리의 다른 글
| Spring Data JPA Projection (0) | 2025.10.19 |
|---|---|
| Spring Boot + Prometheus + Grafana 모니터링 시스템 구축 (0) | 2025.09.24 |
| Jsoup을 활용한 웹 크롤링 (0) | 2025.09.19 |
| 대용량 데이터 수집을 병렬 처리로 최적화하기 (0) | 2025.09.15 |
| CORS란? Spring Boot에서 CORS 정책 설정 (2) | 2025.08.22 |