June
13th,
2021
현재 상황
/*
* 아이 내역 수정
*/
@PutMapping("/{kidId}")
public void updateKid(@PathVariable int kidId, @Valid @RequestBody Kid kid,
HttpSession httpSession) {
int parentId = getSessionUserId(httpSession);
kidService.updateKid(kid, parentId);
}
중간중간 수정된 부분도 있긴 하지만, 컨트롤러 단에서 항상 httpSession의 userID attribute가져오는 작업을 진행하고 있다. 중복적인 코드를 작성해야 하는 불필요함 때문에, 해당 부분을 개성해보기로 하였다.
HandlerMethodArgumentResolver
- 이 인터페이스는 컨트롤러 메소드에서 특정 조건에 맞는 파라미터가 있을 때, 원하는 값을 바인딩해주는 인터페이스이다.
- 스프링에서는 Controller에서 @RequestBody를 통해 body 값을 받아올 때와, @PathVariable을 통해 path parameter를 받아올 때 HandlerMethodArgumentResolver를 사용하여 값을 받아온다.
구현 방법
1. 어노테이션 작성
우선 어노테이션 기반으로 세션 객체를 바인딩 하기 위해, 세션유저 어노테이션을 만든다.
package com.flab.kidsafer.annotation;
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 {
}
- @Target(ElementType.PARAMETER)
- 이 어노테이션이 사용될 수 있는 위치를 말한다.
- 컨트롤러의 파라미터단에 쓰이기 때문에, PARAMETER로 지정해주었다. 즉, 메소드의 파라미터에만 사용 가능하다는 뜻이다.
- @Retention(RetentionPolicy.RUNTIME)
- 어노테이션의 유지 정책을 말한다.
- RUNTIME은 바이트 코드 파일까지 어노테이션 정보를 유지할 수 있기 때문에, 런타임시 리플렉션을 이용해 해당 어노테이션의 정보를 가져올 수 있다.
2. 세션 객체 작성
package com.flab.kidsafer.domain;
import java.io.Serializable;
public class SessionUser implements Serializable {
private int id;
private String email;
private String type;
private String status;
public SessionUser(User user) {
this.id = user.getUserId();
this.email = user.getEmail();
this.type = user.getType();
this.status = user.getStatus();
}
}
- 세션에 저장하기 위해 serializable을 구현하도록 처리한다.
3. HandlerMethodArgumentResolver 상속
HandlerMethodArgumentResolver을 상속하기 위해서는 아래 두가지 메소드를 구현해야 한다.
boolean supportsParameter(MethodParameter parameter);
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
- supportsParameter : 파라미터가 Resolver에 의해 수행될 수 있는 타입인지를 판단한다. true시에는 resolveArgument()를 실행한다.
- 우선 메소드의 파라미터들이 methodParameter에 매핑된다. 그 때 annotation으로 @LoginUser을 사용하였으면 true를 반환한다.
- @LoginUser 어노테이션이 붙어있는 메소드 파라미터가 SessionUser타입이면 true를 반환한다.
- resolveArgument : 실제로 파라미터와 바인딩할 객체를 리턴한다. NativeWebRequest를 통해 클라이언트 요청이 담긴 파라미터를 컨트롤러보다 먼저 받아 작업을 수행할 수 있다.
package com.flab.kidsafer.config;
import com.flab.kidsafer.annotation.LoginUser;
import com.flab.kidsafer.domain.SessionUser;
import javax.servlet.http.HttpSession;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
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;
@Component
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
private final HttpSession httpSession;
public LoginUserArgumentResolver(HttpSession httpSession) {
this.httpSession = httpSession;
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
boolean isLoginUserAnnotation = parameter.getParameterAnnotation(LoginUser.class) != null;
boolean isUserClass = SessionUser.class.equals(parameter.getParameterType());
return isLoginUserAnnotation && isUserClass;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
return httpSession.getAttribute("user");
}
}
4. Resolver 등록
구현한 HandlerMethodArgumentResolver를 스프링에 등록하는 작업이 필요하다.
package com.flab.kidsafer.config;
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final LoginUserArgumentResolver loginUserArgumentResolver;
public WebConfig(LoginUserArgumentResolver loginUserArgumentResolver) {
this.loginUserArgumentResolver = loginUserArgumentResolver;
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolver) {
argumentResolver.add(loginUserArgumentResolver);
}
}
- Spring Boot에서 WebMvcConfigurer를 구현하는 방법을 통해서 추가로 스프링에 대한 환경설정 작업을 진행할 수 있다.