Spring/Spring boot

[Spring Boot] 예외 처리

챛채 2025. 2. 21. 10:15

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()) 바로 사용 가능