클라이언트와 통신하다보면 전처리가 필요할 때가 있다.
간단한 예로 session에서 userId를 가져와야 한다고 가정해보겠다.
- HttpSession에서 UserID를 받아온다.
- UserID를 UserAuth로 변환해서 서비스 레이어로 넘긴다.
@PostMapping("/api/sign-up")
public ResponseEntity<Void> signUp(@RequestBody SignUpReq signUpReq, HttpSession session) {
Long userId = (Long) session.getAttributre("userId");
loginService.signUp(signUpReq, AuthUser.of(userId));
return ResponseEntity.ok().build();
}
Controller에서 HttpSession을 직접 받는 것도 조금 찝찝할 수 있고, 세션 값을 받아와서 변환해야 하는 번거로움이 있다. 그런 것도 그런 거지만 내 생각에 가장 큰 문제는 User 정보 같은 건 자주 필요하기 때문에 이걸 매번 해줘야 할 수도 있다.
애초에 userID를 UserAuth로 변환한 것을 받으면 좋지 않을까?
@PostMapping("/api/sign-up")
public ResponseEntity<Void> signUp(@RequestBody SignUpReq signUpReq,
**AuthUser auth**) {
Long userId = (Long) session.getAttributre("userId");
loginService.signUp(signUpReq, **auth**);
return ResponseEntity.ok().build();
}
이렇게 여러 곳에서 똑같이 전처리를 해야 하는 과정을 자동화할 수 있는데 이게 Argument Resolver의 역할이다.
Spring Argument Resolver는 HandlerMethodArugmentResolver라는 인터페이스를 상속하여 애플리케이션에 맞는 새로운 리졸버를 만들고, 실행 시 리졸버를 리스트에 추가함으로써 적용할 수 있다. 이 리졸버를 추가하는 것은 직접 등록을 해줘야 한다.
MVC 흐름
- 클라이언트의 요청을 DispatcherServlet이 받음
- DispaterServlet은 HandlerMapping에서 URL을 검색
- RequestMappingHandlerAdapter가 모든 핸들러 리스트를 가지고 있음.
- 원하는 URL을 찾은 경우 첫 번째로 인터셉터를 처리한다.
- Argument Resolver 처리
- Message Converter 처리
- Controller Method invoke
구현
public class AuthUserResolver implements HandlerMethodArgumentResolver {
/**
* 파라미터가 AuthUser.class라면 resolveArgument 실행한다.
* @param parameter
* @return
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return AuthUser.class.isAssignableFrom(parameter.getParameterType());
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
final Long userId = (Long) webRequest.getAttribute(LOGIN_SESSION_KEY, WebRequest.SCOPE_SESSION);
if (userId == null)
throw new RuntimeException("bad request. no session.");
return AuthUser.of(userId);
}
HandlerMethodArgumentResolver를 구현해서 아규먼트리졸버를 만들 수 있다.
suppoertsParameter()에서 파라미터를 검증한다. 우리가 지정한 클래스라면 resolverArgument()를 실행한다.
resolverArgument에서 우리가 원하는 전처리를 진행하면 된다.
여기서 끝이 아니고 직접 ArgumentResolver를 Spring에 등록해줘야 한다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new AuthUserResolver());
}
}
WebMvcConfigurer를 상속받아서 addArgumentResolvers()를 오버라이딩 한다.
기존 리졸버들에 우리가 커스텀한 리졸버를 추가해주면 끝.
참고로 예제는 클래스지만 특정 어노테이션이 달린 파라미터를 전처리하는 것도 가능하다.
'Spring' 카테고리의 다른 글
Springboot 통합 테스트로 불안한 리팩토링에서 벗어나기 (2) | 2022.08.24 |
---|---|
ConstraintValidator 그리고 유효성 검사에 대한 고민.. (0) | 2022.05.16 |
@ControllerAdivce를 사용한 예외 처리 로직 분리 (0) | 2021.05.15 |
초간단 Spring Scheduler 적용 (0) | 2021.03.02 |
Springboot + JPA + Querydsl로 좋아요 기능 만들기 1 - 등록 (10) | 2021.02.07 |