스프링을 사용하다 보면 API에 인자값들을 받는 모습을 쉽게 볼 수 있습니다.
예를 들어 @RequestBody를 통해 특정 객체를 받는 모습도 볼 수 있습니다.
또는 @PathValuable String memberId와 같이 경로의 값을 받는 모습도 볼 수 있습니다.
그런데 이런 과정이 Spring에서 어떻게 일어나고 있을까요?
이렇게 Argument에 대해 처리하는 것을 바로 ArgumentResolver라고 합니다.
오늘은 바로 이 ArgumentResolver에 대해서 알아보도록 하겠습니다.
목차
01. HandlerMethodArgumentResolver이란?
01-1. 적용 과정 예시
02. 적용 과정 정리
03. 실제 적용 예시 (Paging 처리)
※ 참고 (기본 세팅 되어 있는 ArgumentResolver들)
01. HandlerMethodArgumentResolver이란?
정의 : 컨트롤러의 Argument에 대해 자동으로 값을 처리해주는 인터페이스를 말합니다.
쉽게 말해 Arugment에 대해 커스텀한 값으로 변환하는 개념입니다.
01-1. 적용 과정 예시
1) 아래의 경우 일반적으로 스프링은 User객체를 처리할 수 없습니다.
그래서 @LoiginUser가 붙은 User파라미터에 대해 입력받은 값들로 유저 객체를 만들어서 반환을 해줘야합니다!
@GetMapping("/me")
public ResponseEntity<User> getCurrentUser(@LoginUser User user) {
return ResponseEntity.ok(user);
}
2) @LoginUser가 붙은 User객체를 처리하는 HandlerMethodArgumentResolver를 만들어야 합니다.
※ HandlerMethodArgumentResolver 인터페이스를 상속받으면 2가지 함수를 만들어야합니다.
- (1) supportsParameter 메소드 : 입력받은 parameter값이 해당 ArgumentResolver가 처리할 수 있는 Type인지 확인.
- (2) resolveArgument 메소드 : 입력받은 parameter값을 커스텀한 값으로 변환하는 로직이 들어갑니다.
현재 예시에 따르면 세션에 들어가있는 사용자 객체를 꺼내서 반환하도록 만들었습니다
그러면 결국 API의 Argument에 들어가는 User값에 세션에서 꺼낸 사용자 객체가 들어가게 되는 것입니다.
@Component
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
// @LoginUser 붙어 있고, 타입이 User인 경우
return parameter.hasParameterAnnotation(LoginUser.class)
&& parameter.getParameterType().equals(User.class);
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
// 세션 또는 SecurityContextHolder 등에서 사용자 객체 꺼내기
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
HttpSession session = request.getSession(false);
if (session != null) {
return session.getAttribute("loginUser"); // 세션에 저장된 사용자 정보
}
return null;
}
}
3) 마지막으로 만든 ArgumentResolver를 WebConfig파일에 등록만 하면 됩니다.
아래와 같이 등록을 하면 등록한 Argument가 파라미터로 들어왔을 경우 자동으로 Resolver가 적용됩니다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final LoginUserArgumentResolver loginUserArgumentResolver;
public WebConfig(LoginUserArgumentResolver loginUserArgumentResolver) {
this.loginUserArgumentResolver = loginUserArgumentResolver;
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(loginUserArgumentResolver);
}
}
02. 적용 과정 정리

1. Client로 Request가 오면 DispatcherSerlver에서 받습니다.
2. 받은 요청에 대해 처리할 수 있는 컨트롤러를 HadnlerMapping이 찾습니다.
3. HandlerMapping이 찾은 컨트롤러에 대해 HandlerAdapter가 실행을 해줍니다.
4. HanlderAdapter가 실행을 해주는 시점에서 Argument가 있을 시에 ArgumentResolver를 찾아서 인자값을 처리해줍니다.
5. 처리된 Argument를 컨트롤러가 받아서 로직을 수행해주고 결과를 반환합니다.
6. 반환된 결과값에 대해 @Controller라면 ViewRevoler가, @RestController라면 Converter 등을 거쳐 응답을 합니다.
ArgumentResolver가 무엇인지 알았고, 어떻게 사용하는지, 어떻게 적용되는지를 이해했습니다.
이번에는 실제 예시로 적용시켜서 ArgumentResolver를 활용해보겠습니다
예시는 서비스에서 주로 사용되는 Paging처리입니다.
03. 실제 적용 예시 (Paging 처리)
1. 특정 요청에 대한 Paging처리를 위해서는 해당 정보 값을 요청받도록 해야합니다.
그래서 저희는 http://localhost:8080/members?page=0&size=10와 같은 요청에 대한 처리를 해보겠습니다.
※ API내부에서는 Parameter값으로 Pageable 객체로 받도록 만들겠습니다.
@GetMapping("/members")
public List<Member> getMembers(Pageable pageable){
System.out.println(pageable.getNumberOfPages());
return Arrays.asList(new Member("신건영", 20, ClassType.A));
}
2. 만약 HandlerAdater가 실행하는 컨트롤러의 API의 Paramter에 Pageable 객체가 들어있다면
Request에서 page값과 size값을 추출하여 Pageable 객체로 만들어서 반환해주는 Resolver를 만들면 됩니다.
public class PageResolver implements HandlerMethodArgumentResolver {
private Pageable pageable;
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().isAssignableFrom(Pageable.class);
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) webRequest.getNativeRequest();
int page = Integer.parseInt(httpServletRequest.getParameter("page"));
int size = Integer.parseInt(httpServletRequest.getParameter("size"));
return PageRequest.of(page,size);
}
}
위와 같이 적용하면 Paging에 대한 파라미터 문자열 값을 Pageable 객체로 받을 수 있게됩니다.
사실 현재 스프링에서 사용하는 객체들이나 경로의 데이터값, 파라미터 값 등 모두 ArgumentResolver가 적용되어 있습니다. 이렇게 미리 선언되어 있는 Resolver들 덕분에 편하게 객체를 받고, 데이터를 인자로 받을 수 있는 것입니다.
※ 참고 (기본 세팅 되어 있는 ArgumentResolver들)
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);
// Annotation-based argument resolution
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());
// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
if (KotlinDetector.isKotlinPresent()) {
resolvers.add(new ContinuationHandlerMethodArgumentResolver());
}
// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
resolvers.add(new PrincipalMethodArgumentResolver());
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
지금까지 ArgumentResolver에 대해서 알아보았습니다.
스프링에서 인자값들을 자동으로 만들어 해준다고만 넘어갔었는데
이렇게 자동으로 변환되면 이유를 직접 확인해보니 스프링의 동작에 대한 더 높은 이해도가 생긴 것 같습니다.
지금까지 읽어주셔서 감사합니다.
'Spring' 카테고리의 다른 글
| [ Spring ] HttpMessageConverter 파헤치기 - 개발 지식 (0) | 2025.04.18 |
|---|---|
| [HTTP API 설계] Spring HTTP API란? - 개발 지식 (0) | 2025.04.14 |
| [IOC] 제어 역전의 모든 것 - Spring 지식 (0) | 2025.04.02 |
| [Spring Boot] 스프링 부트란? - 개발 지식 (2) | 2025.03.31 |
| [Spring] 시작, Spring이란? - 개발 지식 (1) | 2024.10.02 |