ArgumentResolver(HandelrMethodArgumentResolver)란 무엇일까?
스프링을 사용하면 애노테이션 기반의 컨트롤러는 매우 다양한 파라미터를 사용할 수 있는 걸 볼 수 있다.
HttpServletRequest, Model, @RequestParam, @ModelAttribute은 물론 @RequestBody, HttpEntity 같은 HTTP 메시지를 처리하는 부분까지 매우 큰 유연함을 보여준다.
어떻게 이렇게 파라미터를 유연하게 처리할 수 있는 것일까?
그 이유가 바로 오늘 알아보고자 하는 ArgumentResolver 덕분이다.
정확히는 HandlerMethodArgumentResolver인데 줄여서 ArgumentResolver라고 부른다.
애노테이션 기반 컨트롤러 처리하는 RequestMappingHandlerAdapter는 ArgumentResolver를 호출해서 컨트롤러(핸들러)가 필요로 하는 다양한 파라미터의 값(객체)를 생성한다.
그리고 이렇게 파라미터의 값 모두 준비되면 컨트롤러 호출하면서 값을 넘겨준다.
HandelrMethodArgumentResolver 인터페이스 코드를 살펴보고 동작방식이 어떻게 흘러가는지 살펴보자
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
@Nullable
Object resolveArgument(MethodParameter parameter,
@Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
@Nullable WebDataBinderFactory binderFactory) throws Exception;
}
ArgumentResolver의 supportsParameter()를 호출해서 해당 파라미터를 지원하는지 체크하고,
지원하면 resolveArgument()를 호출해서 실제 객체를 생성한다.
그리고 이렇게 생성된 객체가 컨트롤러 호출시 넘어가는 것이다.
ArgumentResolver 활용하기
직접 이 인터페이스를 확장해서 원하는 ArgumentResolver를 만들 수도 있다.
로그인 회원을 ArgumentResolver를 활용해서 조금 더 편리하게 찾아보는 예제를 다뤄보자
Controller
Controller 코드에 보면 Member 클래스 타입 앞에 @Login 애노테이션을 붙였다.
이 애노테이션을 직접 만들 것이다.
직접 만든 ArgumentResolver가 동작해서 자동으로 세션에 있는 로그인 회원을 찾아주고, 세션이 없으면 null을 반환하도록 할 것이다.
@Slf4j
@Controller
@RequiredArgsConstructor
public class HomeController {
private final MemberRepository memberRepository;
@GetMapping("/")
public String homeLoginArgumentResolver(@Login Member loginMember, Model model) {
if (loginMember == null) {
return "home";
}
model.addAttribute("member", loginMember);
return "loginHome";
}
}
@Login 애노테이션 생성
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 Login {
}
LoginMemberArgumentResolver 생성
HandlerMethodArgumentResolver 인터페이스를 구현하는 LoginMemberArgumentResolve이다.
- supportsParameter() : 직접 구현한 애노테이션(@Login)이 있으면서 Member 타입이면 해당 ArgumentResolver가 사용된다.
- resolveArgument() : 컨트롤러 호출 직전에 호출 되어서 필요한 파라미터 정보를 생성해준다.
- 여기서는 세션에 있는 로그인 회원 정보인 member 객체를 찾아서 반환해주고 이후 스프링MVC는 컨트롤러의 메서드를 호출하면서 여기에서 반환된 member 객체를 파라미터에 전달해준다.
/**
* @Login 애노테이션을 사용해도 ArgumentResolver로 인식하지 않고, ModelAndAttribute로 인식하기 때문에 아래와 같이 설정을 해줘야함
*/
@Slf4j
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
log.info("supportsParameter 실행");
// 컨트롤러를 호출하기 전에 해당 애노테이션이 붙어있는지 확인
boolean hasLoginAnnotation = parameter.hasParameterAnnotation(Login.class);
// 컨트롤러를 호출할 때 넘어오는 파라미터의 타입이 Member인지 확인
boolean hasMemberType = Member.class.isAssignableFrom(parameter.getParameterType());
return hasLoginAnnotation && hasMemberType;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
log.info("resolveArgument 실행");
// HttpServletRequest 가져오기
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
HttpSession session = request.getSession(false);
if (session == null) return null;
return session.getAttribute(SessionConst.LOGIN_MEMBER);
}
}
WebMvcConfigurer에 설정 추가
WebConfig 클래스에 WebMvcConfigurer 인테페이스를 구현하여 앞에서 직접 만든 LoginMemberArgumentResolver를 등록해준다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new LoginMemberArgumentResolver());
}
}
'TIL > Spring' 카테고리의 다른 글
[Spring] 서블릿 필터와 스프링 인터셉터(Servlet Filter, Spring Interceptor) (0) | 2025.03.28 |
---|---|
[Spring] Session으로 로그인 기능 구현하기(HttpSession, TrackingModes, Session Timeout) (0) | 2025.03.27 |
[Spring] Spring - Bean Validation (0) | 2025.03.20 |
[Spring] Spring Validation-BindingResult(with.thymeleaf) (0) | 2025.03.16 |
[Spring] 빈 스코프 란? (0) | 2025.03.08 |