1. Argument Resolver 소개
- Spring MVC의 Argument Resolver는 컨트롤러 메서드의 파라미터를 어떻게 처리하고 바인딩할지 결정하는 핵심 메커니즘입니다.
- 정식 이름은
HandlerMethodArgumentResolver
로, 컨트롤러 메서드(핸들러)가 호출될 때 메서드의 파라미터 값을 결정하는 역할을 담당합니다. - 이 인터페이스는 HTTP 요청에서 데이터를 추출하여 컨트롤러 메서드의 파라미터로 변환하는 과정을 처리합니다.
1.1 주요 역할
- HTTP 요청의 여러 부분(경로 변수, 쿼리 파라미터, 헤더, 쿠키, 세션, 요청 본문 등)에서 데이터를 추출합니다.
- 추출한 데이터를 컨트롤러 메서드 파라미터의 타입에 맞게 변환합니다.
- 어노테이션을 기반으로 특정 파라미터에 값을 주입하는 방법을 결정합니다.
2. HandlerMethodArgumentResolver 인터페이스
HandlerMethodArgumentResolver
인터페이스는 두 개의 핵심 메서드로 구성됩니다.
2.1 인터페이스 구조
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception;
}
supportsParameter
: 이 리졸버가 주어진 파라미터를 지원하는지 확인합니다.resolveArgument
: 지원되는 파라미터에 대해 실제 값을 결정합니다.- 참고
2.2 동작 원리
- Spring MVC는 컨트롤러 메서드가 호출될 때 모든 등록된
HandlerMethodArgumentResolver
에 대해supportsParameter
메서드를 호출하여 각 파라미터를 처리할 수 있는 리졸버를 찾습니다. - 적합한 리졸버를 찾으면
resolveArgument
메서드를 호출하여 실제 파라미터 값을 결정합니다. - 이 값은 컨트롤러 메서드를 호출할 때 해당 파라미터로 전달됩니다.
3. Spring MVC의 기본 Argument Resolver
- Spring MVC는 다양한 상황에 맞게 여러 Argument Resolver를 기본으로 제공합니다.
3.1 주요 내장 Argument Resolver
RequestParamMethodArgumentResolver
:@RequestParam
어노테이션이 붙은 파라미터 처리PathVariableMethodArgumentResolver
:@PathVariable
어노테이션이 붙은 파라미터 처리RequestHeaderMethodArgumentResolver
:@RequestHeader
어노테이션이 붙은 파라미터 처리CookieValueMethodArgumentResolver
:@CookieValue
어노테이션이 붙은 파라미터 처리ModelMethodProcessor
:Model
타입의 파라미터 처리RequestResponseBodyMethodProcessor
:@RequestBody
및@ResponseBody
어노테이션이 붙은 파라미터 처리SessionAttributeMethodArgumentResolver
:@SessionAttribute
어노테이션이 붙은 파라미터 처리ServletRequestMethodArgumentResolver
:HttpServletRequest
및 관련 타입의 파라미터 처리ServletResponseMethodArgumentResolver
:HttpServletResponse
및 관련 타입의 파라미터 처리- 참고
3.2 동작 예시
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public User getUser(@PathVariable Long id,
@RequestParam(required = false) String name,
@RequestHeader("User-Agent") String userAgent,
HttpServletRequest request) {
// ...
}
@PostMapping
public User createUser(@RequestBody User user) {
// ...
}
}
이 예시에서:
@PathVariable Long id
:PathVariableMethodArgumentResolver
가 처리@RequestParam String name
:RequestParamMethodArgumentResolver
가 처리@RequestHeader String userAgent
:RequestHeaderMethodArgumentResolver
가 처리HttpServletRequest request
:ServletRequestMethodArgumentResolver
가 처리@RequestBody User user
:RequestResponseBodyMethodProcessor
가 처리
4. 커스텀 Argument Resolver 구현
- 기본 제공되는 Argument Resolver만으로는 특정 요구사항을 충족하기 어려울 때 커스텀 리졸버를 구현할 수 있습니다.
4.1 구현 단계
- 커스텀 어노테이션 정의 (필요한 경우)
HandlerMethodArgumentResolver
인터페이스 구현- 구현한 리졸버를 Spring MVC 설정에 등록
4.2 커스텀 어노테이션 예시
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentUser {
}
4.3 커스텀 리졸버 구현 예시
public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(User.class) &&
parameter.hasParameterAnnotation(CurrentUser.class);
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
String token = extractTokenFromRequest(request);
// 토큰에서 사용자 정보 추출 로직
User currentUser = getUserFromToken(token);
return currentUser;
}
private String extractTokenFromRequest(HttpServletRequest request) {
// 요청에서 토큰 추출하는 로직
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
return authHeader.substring(7);
}
return null;
}
private User getUserFromToken(String token) {
// 토큰에서 사용자 정보를 가져오는 로직
// 실제 구현은 JWT 파싱 등의 작업이 포함될 수 있음
// ...
return new User();
}
}
4.4 리졸버 등록
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new CurrentUserArgumentResolver());
}
}