@ControllerAdvice에 대하여

이건희·2024년 12월 6일

이전 글에서 ControllerAdvice와 RequestBodyAdvice를 사용해 복호화를 진행했었다. 이전 글에서 ControllerAdvice까지 작성하기엔 너무 길어질 것 같아 따로 글을 작성한다.

@ControllerAdvice란?

@ControllerAdvice는 짧게 말해서 Spring이 제공하는 AOP의 기능 중에 하나이며, 전역에 있는 컨트롤러에 공통적으로 사용되는 것이 있을때 사용한다.

대부분 찾아보면 에러 처리를 위한 @ExceptionHandler를 위해 사용한다. 이외에도 어떻게 사용되는지 알아보자.


@ModelAttribute

@ModelAttribute는 컨트롤러 메서드의 모든 요청 처리 전에 공통 데이터를 모델에 추가하거나, 요청 파라미터를 특정 객체로 매핑할 때 사용된다.

역할

  • 공통 데이터 추가

    • 모든 Controller에서 사용될 데이터를 Model 객체에 추가.
  • 객체 초기화

    • 요청 파라미터를 특정 객체로 변환하고 초기화.
  • 작동 위치

    • Controller 메서드가 실행되기 전에 실행된다.

여러 컨트롤러에서 공통적으로 사용하는 데이터(예: 사용자 정보)를 자동으로 모델에 추가할 수 있다.

한번 예제를 보면 쉬울 것 같다.

@ControllerAdvice
public class GlobalModelAttribute {

    @ModelAttribute
    public void addCommonAttributes(Model model) {
        model.addAttribute("appName", "MyAPP");
    }
}

위에 예제는 공통적으로 사용되는 데이터 "appName" : "MyAPP"을 추가하여 모든 Controller에서 사용 가능하게 한다.


@ExceptionHandler

Controller 메서드 실행중 또는 ControllerAdvice에서 특정 예외를 처리하기 위해 사용된다.

아마 대부분 ControllerAdvice를 위의 방식으로 사용하고 있을 것이다. 나도 역시 그렇다.

아래는 현재 프로젝트에 적용된 @ExceptionHandler이다

@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * @Valid를 이용한 유효성 검증 커스텀
     * - key: errorMessage, value: DTO에서 지정한 메세지
     * - 클라이언트에게 에러 응답
     * @param ex
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        for (FieldError error : ex.getBindingResult().getFieldErrors()) {
            errors.put("errorMessage", error.getDefaultMessage());
        }
        return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
    }
}

나는 @Valid에서 유효성 검증 오류가 발생했을때 사용자게에 보낼 응답을 커스텀하기 위해서 사용하였다.


@InitBinder

요청 데이터를 바인딩하거나 특정 형식 데이터를 변환할때 사용한다.

  • 데이터 바인딩 전처리

    • 컨트롤러 메서드의 매개변수에 데이터를 바인딩하기 전에 이를 수정하거나 초기화한다.
  • 커스텀 에디터

    • 특정 데이터 형식을 변환하기 위한 커스텀 로직 추가.

    • 예를 들어, 날짜 형식이 문자열로 오면 이를 LocalDate 형식으로 변환하는 작업 등을 할 수 있다

  • 작동 위치

    • 컨트롤러 메서드 매개변수에 데이터를 바인딩하기 전에 실행된다.

아래는 문자열의 앞뒤 공백을 제거해주는 @InitBinder이다.

@ControllerAdvice
public class GlobalInitBinder {

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(String.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) {
                setValue(text.trim()); // 문자열 앞뒤 공백 제거
            }
        });
    }
}

RequestBodyAdvice & ResponseBodyAdvice

이전 글에서 설명했던 RequestBodyAdvice와 ResponseBodyAdvice이다.

위 두 인터페이스는 요청 본문과 응답 본문 데이터를 가로채거나 변환하기 위해 사용한다.
인터페이스이기 때문에 메서드 4개를 구현해야한다.

우선 RequestBodyAdvice부터 설명하자면,

  • 역할

    • 요청 본문을 읽기 전후에 데이터를 가공하거나 검증.
    • 나의 경우에는 요청 데이터를 암호화/복호화하거나 공통 로직 추가.
  • 작동 위치

    • 요청 데이터가 @RequestBody로 바인딩되기 전에 실행된다.

ResponseBodyAdvice는 RequestBodyAdvice와 거의 같지만 HTTP 변환 전(Controller 리턴 시)에 응답을 처리한다.

  • 역할

    • 응답 데이터를 공통 포맷으로 변환 (예: JSON 래핑).

    • 특정 응답에 메타데이터 추가.

  • 작동 위치

    • 컨트롤러 메서드가 응답 데이터를 반환한 후, HTTP 응답으로 변환되기 전에 실행된다.

구현해야 하는 메서드들은 이전 글에 작성했으니 생략하도록 하겠다.

정리

이들을 정리해보자면 아래와 같다.

기능역할실행 시점
@ModelAttribute공통 데이터 추가, 요청 데이터 초기화컨트롤러 메서드 실행 전
@ExceptionHandler컨트롤러에서 발생한 예외를 처리예외 발생 시
@InitBinder요청 데이터를 바인딩하거나 변환요청 데이터 바인딩 전
RequestBodyAdvice요청 본문 데이터 전처리요청 데이터 변환 전/후
ResponseBodyAdvice응답 본문 데이터 후처리응답 데이터를 변환하기 전
profile
백엔드 개발자가 되겠어요

0개의 댓글