어떠한 요청이 들어왔을 때, 요청 메시지에 있는 데이터를 활용하여 컨트롤러에서 사용할 자바 객체를 생성하도록 도와주는 객체이다. Spring 에서 기본적으로 제공하는 @RequestBody@RequestParam 과 유사한 동작을 수행한다.

ArgumentResolver 의 활용

예를 들어, 사용자 인증 정보를 토큰으로 관리한다고 가정하자. @RequestBody 같은 어노테이션은 요청 메시지 body 에 있는 정보를 자바 객체로 반환한다. 하지만 토큰을 사용한다면 토큰에 있는 정보를 추출 하고 객체를 생성해야 한다. 이미 제공하고 있는 어노테이션으로 활용할 수 없는 경우 커스텀으로 사용할 수 있다.

다음 예제는 우아한테크코스 장바구니 미션 중 받아온 토큰 정보를 활용하여 자바 객체로 만드는 로직이다.

1. ArgumentResolver 생성

src/main/java/woowacourse/auth/ui/AuthenticationPrincipalArgumentResolver.java

package woowacourse.auth.ui;

import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import woowacourse.auth.support.AuthenticationPrincipal;
import woowacourse.auth.application.AuthService;
import woowacourse.auth.support.AuthorizationExtractor;
import woowacourse.shoppingcart.application.dto.request.CustomerIdentificationRequest;

import javax.servlet.http.HttpServletRequest;

public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {
    private final AuthService authService;

    public AuthenticationPrincipalArgumentResolver(AuthService authService) {
        this.authService = authService;
    }

		// 원하는 어노테이션이 붙어있는지 확인한다.
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(AuthenticationPrincipal.class);
    }

		// 적절한 어노테이션이 붙어있는 경우 원하는 객체로 바인딩한다.
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
	      // 요청 헤더에 담긴 토큰을 추출해와 자바 객체로 생성하는 과정
				HttpServletRequest httpServletRequest = (HttpServletRequest) webRequest.getNativeRequest();
        
				String token = AuthorizationExtractor.extract(httpServletRequest);
        String payload = authService.getPayload(token);
				
        return new CustomerIdentificationRequest(payload);
    }
}

ArgumentResolver 를 생성하기 위해서는 HandlerMethodArgumentResolver 를 구현받아야 한다. HandelrMethodArgumentResolver 는 공식문서에서 다음과 같이 정의되어 있다.

Strategy interface for resolving method parameters into argument values in the context of a given.

2. 커스텀 어노테이션 생성

커스텀 어노테이션은 다음과 같이 생성하였다.

src/main/java/woowacourse/auth/support/AuthenticationPrincipal.java

package woowacourse.auth.support;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthenticationPrincipal {
}

3. ArgumentResolver 설정

src/main/java/woowacourse/auth/config/AuthenticationPrincipalConfig.java

package woowacourse.auth.config;

import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import woowacourse.auth.ui.AuthenticationPrincipalArgumentResolver;
import woowacourse.auth.application.AuthService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import woowacourse.auth.ui.LoginInterceptor;

import java.util.List;

@Configuration
public class AuthenticationPrincipalConfig implements WebMvcConfigurer {
    private final AuthService authService;

    public AuthenticationPrincipalConfig(AuthService authService) {
        this.authService = authService;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor(authService)).addPathPatterns("/auth/**");
    }

		// ArgumentResolver 설정
    @Override
    public void addArgumentResolvers(List argumentResolvers) {
        argumentResolvers.add(createAuthenticationPrincipalArgumentResolver());
    }

    @Bean
    public AuthenticationPrincipalArgumentResolver createAuthenticationPrincipalArgumentResolver() {
        return new AuthenticationPrincipalArgumentResolver(authService);
    }
}

https://tecoble.techcourse.co.kr/post/2021-05-24-spring-interceptor/