spring - Exception 처리 (feat_RestControllerAdvice)

요한·2025년 7월 12일
0

사이드 프로젝트에서 예외처리를 위해 조금 더 잘 알기 위해 @RestControllerAdvice를 다시한번 정리 할려고한다.
개발할때마다 초기 설정하는건 금방 까먹는다..

@RestControllerAdvice

1. @RestControllerAdvice란

사진에서 보았듯이 @ControllerAdvice + @ResponseBody랑 합쳐져 있는걸 볼 수 있다.

😳 @Target, @Retention 이게 뭐지 ..

아 @Target은 어노테이션이 어디에 사용할수있는지 Type이니까 클래스 enum에 사용할 수 있거구나 !
@Retenion 은 Annotation 이 실제로 적용되고 유지되는 범위를 의미한다. 그러면 실행 중일때 적용되는구나!

2. 왜 쓸까 ? 🤔🤔

제가 생각에는 ! 코드 유지보수 시점에서 오류 터지는 부분과 데이터 및 결과가 정상 적으로 불러 올경우를 분리해서 코드가 더러워지는 걸 방지 한다고 생각합니다. 이러면 단일 책임에 원칙도 지켜지고 반복적인 작업 try,catch 코드 최소화 할 수 있다고 생각한다.

에러 종류

https://www.youtube.com/shorts/sC-mB31n05A

저는 이 영상 웃기게 봤어요 ㅎㅎ
요약하면

100번대 : 계속 진행해!

200번대 : 성공 했어!

300번대 : 요청은 수신했지만, 클라이언트가 이동하거나 추가 작업을 해야 완료!

400번대 : 클라이언트 너가 잘못했어!

500번대 : 서버가 잘못했어!

4. 사용 방법

ApiResponse.class

@NoArgsConstructor
@AllArgsConstructor
public class ApiResponse<T> {
    private int status; // 상태 코드
    private String message;
    private T data;

    public static <T> ApiResponse<T> success(T data) {
        return new ApiResponse<>(200,"success",data);
    }
    public static <T> ApiResponse<T> error(int status, String message) {
        return new ApiResponse<>(status,message,null);
    }
}

우리가 공통적인 데이터를 하나의 묶음으로 만들고 success와 error 이렇게 분리하면 error 즉 Excetpion 터졌을때 success 성공했을때 분리해서 관리 할 수 있다 .

ExceptionController.class

@RestControllerAdvice
public class ExceptionController {
    //일반적인 예외 처리
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ApiResponse<Object>> serverError(Exception e){
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(ApiResponse.error(500, e.getMessage()));
    }
	// url 요청 requestParam이 빠졋을 경우
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ResponseEntity<ApiResponse<Object>> handleMissingParam(MissingServletRequestParameterException e) {
        return ResponseEntity.badRequest()
                .body(ApiResponse.error(400,"요청 메시지가 빠졌습니다"));
    }
}    

맞는 상태 메세지, 메시지 등으로 처를 할 수 있다.

테스트

@GetMapping("/MissingServletRequestParameterException")
    public ResponseEntity<ApiResponse<Object>> illegalArgumentException(@RequestParam Integer count) {
        return new ResponseEntity<>(HttpStatus.OK);
    }


이러면 그전에 try-catch 로 있었던 부분 줄일 수 있다.

5. 이러면 커스텀도 할 수 있을까 ?🤔

가능하다! 이제 서버가 실행 중일 때 에러를 잡고 내가 에러 종류를 다 알 수 도 없고 에러도 커스텀해서 전송 할 수 있다.

ExceptionEnum.enum

@Getter
public enum ExceptionEnum {
    ERROR_ENCODE(5001,"암호화가 에러났습니다"),
    ERROR_DECODE(5002,"복화중에 에러가 났습니다"),
    ERROR_LENGTH(5003,"너무 길어요");

    private final int status;
    private final String message;

    ExceptionEnum(int status, String message) {
        this.status = status;
        this.message = message;
    }
}

enum을 사용해서 우리가 정의할 수 있다. enum을 대표적인 사용방법은
(월,화,수,목,금,토,일) 과 이제 (봄,여름,가을,겨울) 이미 정의해 놓은 것을 말한다. 우리도 에러를 정의해보자!

CustomExcption.class


public class CustomException extends RuntimeException {
    private final ExceptionEnum exceptionEnum;

    public CustomException(ExceptionEnum exceptionEnum) {
        super(exceptionEnum.getMessage());
        this.exceptionEnum = exceptionEnum;

    }
    public int getStatus(){
        return exceptionEnum.getStatus();
    }
    public String getMessage(){
        return exceptionEnum.getMessage();
    }
}

@RestControllerAdvice 부분에 추가

//커스터 Exception 암호화 부분이나 다른 에러 처리 못할때 정확한 내용을 정리하기위해
    @ExceptionHandler(CustomException.class)
    public ResponseEntity<ApiResponse<Object>> customException(CustomException e){
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(ApiResponse.error(e.getStatus(), e.getMessage()));
    } 

테스트

오류가 잘 나오는 것을 알 수 있습니다.

security에서도 @RestControllerAdvice를 작성했는데 초반 설정이 더 필요해서 다음 글에는 추가 적으로 작성하겠습니다.
긴 글 읽어주셔서 감사합니다. :)

profile
코드 깍는 개발자 kangyohan.dev.0421@gmail.com

0개의 댓글