[Spring Boot] 예외 처리
Sring Boot 예외 처리
1. Spring Boot에서 예외 처리가 중요한 이유
- 애플리케이션에서 에러가 발생했으 경우 이를 잘 관리해서 사용자에게 친절한 응답을 제공하기 위함
- 전역에서 처리를 하면 코드 중복을 줄이고 유지보수성이 향상됨
- 흐름 : 사용자 요청 -> Controller -> Service -> Repository -> 예외 발생 -> GlobalExceptionHandler -> 클라이언트에게 JSON 응답 반환
[ErrorCode]
- 각종 예외 코드를 Enum으로 관리
@Getter
@RequiredArgsConstructor
public enum ErrorCode {
// 404: 리소스를 찾을 수 없음
USER_NOT_FOUND(HttpStatus.NOT_FOUND, "유저가 존재하지 않습니다."),
// 400: 잘못된 요청
INVALID_REQUEST(HttpStatus.BAD_REQUEST, "잘못된 요청입니다.");
private final HttpStatus status;
private final String message;
}
[CustomException]
- 사용자 정의 예외로 비즈니스 로직에서 throw new CustomException(ErrorCode.XXX); 로 예외를 발생시킬 수 있도록 정의
-RuntimeException을 상속 받아서 Unchecked Exception으로 처리
-ErrorCode를 전달 받아서 해당 예외의 message를 반환
@Getter
public class CustomException extends RuntimeException {
private final ErrorCode errorCode;
public CustomException(ErrorCode errorCode) {
super(errorCode.getMessage()); // 부모 클래스(RuntimeException)에 메시지 전달
this.errorCode = errorCode;
}
}
[ErrorResponse]
- 예외 응답 DTO로 예외 발생 시 클라이언트에게 반환할 JSON 응답 형식
- status : HTTP 상태코드(404, 400등)
- message : 에러메세지("유저가 존재하지 않습니다.")
- of() : 메서드 활용하여 쉽게 ErrorResponse 객체 생성 가능하게 함
@Getter
@RequiredArgsConstructor
public class ErrorResponse {
private final int status;
private final String message;
public static ErrorResponse of(ErrorCode errorCode) {
return new ErrorResponse(errorCode.getStatus().value(), errorCode.getMessage());
}
}
{
"status": 404,
"message": "유저가 존재하지 않습니다."
}
[GlobalExceptionHandler]
- 전역 예외 핸들러로 예외가 발생하면 이를 한 곳에서 처리하여 클라이언트에게 일관된 응답을 반환
- @ResControllerAdvice : 모든 컨트롤러에서 발생하는 예외를 처리
- @ExceptionHandler(CustomException.Class) : CustomException 예외가 발생하면서 메서드 실행
2. HttpStatus.NOT_FOUND vs NOT_FOUND.value() 차이
코드 | 설명 |
HttpStatus.NOT_FOUND | HttpStatus Enum 자체를 의미(객체) |
NOT_FOUND.value() | HttpStatus.NOT_FOUND의 숫자 값을 반환(404) |
3. 왜 HttpStatus.NOT_FOUND와 NOT_FOUND.value()를 다르게 사용했을까?
- HttpStatus.NOT_FOUND를 사용하는 경우
public enum ErrorCode {
USER_NOT_FOUND(HttpStatus.NOT_FOUND, "유저가 존재하지 않습니다.");
private final HttpStatus status;
private final String message;
ErrorCode(HttpStatus status, String message) {
this.status = status;
this.message = message;
}
}
ErrorCode error = ErrorCode.USER_NOT_FOUND;
System.out.println(error.getStatus()); // NOT_FOUND (Enum)
System.out.println(error.getStatus().value()); // 404
System.out.println(error.getStatus().getReasonPhrase()); // "Not Found"
- 이렇게 하면 HttpStatus 타입 그대로 저장 가능
- 장점: status.getReasonPhrase() 같은 추가 정보 ("Not Found")를 활용할 수 있음.
- 단점: int 값이 필요할 때마다 status.value()를 호출해야 함.
- NOT_FOUND.value()를 사용하는 경우
public enum ErrorCode {
USER_NOT_FOUND(NOT_FOUND.value(), "유저가 존재하지 않습니다.");
private final int status;
private final String message;
ErrorCode(int status, String message) {
this.status = status;
this.message = message;
}
}
- 이렇게 하면 int 타입으로 저장 가능
- 장점: HttpStatus 없이도 숫자 값(404)으로 바로 사용할 수 있음.
- 단점: getReasonPhrase() 같은 추가 정보를 활용할 수 없음.
- 결론
사용해야 할 경우 | HttpStatus.NOT_FOUND (Enum) | NOT_FOUND.value() (int) |
추가 정보 필요 (getReasonPhrase() 등) | 가능 | 불가능 |
상태 코드를 바로 숫자로 사용 | 불편 (매번 .value() 호출) | 편리 |
일관된 HTTP 상태 코드 관리 (HttpStatus) | 가능 | 불가능 |
JSON 응답에서 직접 status 값 반환 | 변환 필요 (status.value()) | 바로 사용 가능 |