어떠한 요청이 들어왔을 때, 요청 메시지에 있는 데이터를 활용하여 컨트롤러에서 사용할 자바 객체를 생성하도록 도와주는 객체이다. Spring 에서 기본적으로 제공하는 @RequestBody
나 @RequestParam
과 유사한 동작을 수행한다.
예를 들어, 사용자 인증 정보를 토큰으로 관리한다고 가정하자. @RequestBody
같은 어노테이션은 요청 메시지 body 에 있는 정보를 자바 객체로 반환한다. 하지만 토큰을 사용한다면 토큰에 있는 정보를 추출 하고 객체를 생성해야 한다. 이미 제공하고 있는 어노테이션으로 활용할 수 없는 경우 커스텀으로 사용할 수 있다.
다음 예제는 우아한테크코스 장바구니 미션 중 받아온 토큰 정보를 활용하여 자바 객체로 만드는 로직이다.
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.
supportsParameter
: 요청받은 메서드의 인자에 원하는 어노테이션이 붙어있는지 확인하고 원하는 어노테이션을 포함하고 있으면 true를 반환한다.resolveArgument
: supportsParameter
에서 true 를 받은 경우, 즉, 특정 어노테이션이 붙어있는 어느 메서드가 있는 경우 parameter가 원하는 형태로 정보를 바인딩하여 반환하는 메서드이다.커스텀 어노테이션은 다음과 같이 생성하였다.
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 {
}
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/