설모의 기록

[우아한테크캠프] 12일차 본문

일상/우아한테크캠프

[우아한테크캠프] 12일차

HA_Kwon 2018. 7. 18. 00:10


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
Comments