설모의 기록
[우아한테크캠프] 12일차 본문
HandlerMethodArgumentResolver 인터페이스 적용
Spring 프레임워크를 이용해 API 를 관리할 때 컨트롤러에 들어오는 파라미터를 공통적으로 추가해야 하는 경우가 있습니다. 예를 들어, 로그인한 유저만 할 수 있는 기능이 많다고 생각해보자. 그러면 보통은 매번 HttpSession 에서 유저를 꺼내 체크해주는 로직이 반복될 것입니다.
이럴 때 사용하는 것이 HandlerMethodArgumentResolver 인터페이스입니다. HandlerMethodArgumentResolver 는 컨트롤러에 사용자의 요청이 도달하기 전에 요청에 대한 파라미터를 수정해 넘길 수 있습니다. 아래의 예제로 살펴보겠습니다.
우선 @LoginUser 라는 인터페이스를 구현하겠습니다.
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 LoginUser {
boolean required() default true;
}
이 후, HandlerMethodArgumentResolver 를 구현한 LoginUserHandlerMethodArgumentResolver 클래스를 구현하겠습니다.
먼저 HandlerMethodArgumentResolver 인터페이스에 있는 두 개의 메소드를 오버라이드합니다.
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;
public class LoginUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
}
먼저 supportsParameter() 는 Resolver 를 적용 가능한 지를 체크합니다.
두번째로 resolveArgument() 는 인자로 파라미터와 3개의 인자를 받아들여 실제 객체를 반환합니다. 그럼 세션에 유저가 있는지를 확인하는 예제로 위의 클래스를 구현해보겠습니다.
import codesquad.UnAuthorizedException;
import codesquad.domain.User;
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;
public class LoginUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(LoginUser.class);
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
User user = HttpSessionUtils.getUserFromSession(webRequest);
if (!user.isGuestUser()) {
return user;
}
LoginUser loginUser = parameter.getParameterAnnotation(LoginUser.class);
if (loginUser.required()) {
throw new UnAuthorizedException("You're required Login!");
}
return user;
}
}
위의 코드에서 supportsParameter() 에서는 파라미터가 LoginUser 타입으로 정의되어 있는지를 검사해 반환하도록 구현했습니다.
resolveArgument() 는 세션에서 유저를 꺼내 user 변수에 저장합니다. 만약 user 가 Guest (로그인하지 않은 유저) 가 아니면 user 를 반환해 실제 컨트롤러에서는 user 를 사용할 수 있도록 합니다.
이렇게 HandlerMethodArgumentResolver 를 구현한 클래스는 WebMvcConfigurer 인터페이스를 구현한 클래스에서 등록해줘야 합니다. 그 예는 아래와 같습니다.
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Bean
public LoginUserHandlerMethodArgumentResolver loginUserArgumentResolver() {
return new LoginUserHandlerMethodArgumentResolver();
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(loginUserArgumentResolver());
} ...
}
위와같이 LoginUserHandlerMethodArgumentResolver 를 Bean 으로 등록한 후 List<HandlerMethodArgumentResolver> 타입의 argumentResolvers 에 추가(add) 해줍니다. 그러면 LoginUserHandlerMethodArgumentResolver 가 Bean 으로 등록되어 있으므로, 호출될 때 BeanFactory 에 해당 Bean 이 있는지를 검사합니다. 있으면 Bean 객체를 꺼내오고, 없으면 생성해 리스트에 넣어주는 방식입니다.
사용예제는 아래와 같습니다.
@RestController
@RequestMapping("/api/questions")
public class ApiQuestionController {
@PostMapping("")
public ResponseEntity<Void> create(@LoginUser User loginUser, @RequestBody Question question) {
...
}
}
따라서 세션에 로그인 된 유저가 있지 않으면 컨트롤러 내부의 코드는 실행되지 않고 UnAuthorizedException 예외가 발생합니다.
'일상 > 우아한테크캠프' 카테고리의 다른 글
[우아한테크캠프] 14일차 (0) | 2018.07.19 |
---|---|
[우아한테크캠프] 13일차 (1) | 2018.07.18 |
[우아한테크캠프] 10일차 (0) | 2018.07.14 |
[우아한테크캠프] 9일차 (0) | 2018.07.12 |
[우아한테크캠프] 8일차 (0) | 2018.07.12 |