Java/Spring

HandlerMethodArgumentResolver 란?

Z_Z 2026. 1. 7. 16:44
728x90
반응형

HandlerMethodArgumentResolver 란?

컨트롤러 메서드의 파라미터를 직접 해석해서 객체로 만들어주는 확장 포인트 인터페이스다

쉽게 말해, Spring MVC 에서 컨트롤러 메서드의 매개변수를 HTTP 요청에서 실제 인자 값으로

변환하는 인터페이스이다.

 

DispatcherServlet 이 HandlerMapping 에서 적절한 Mapping URL 을 찾아내고 HanlderAdapter 를 통해

적절한 컨트롤러 메서드를 호출한다.

여기서 컨트롤러 메서드를 호출하기 전 @RequestParam, @PathVariable, @RequestBody 등의 데이터들을

가공하거나 커스터마이징할 때 사용한다.

 

1️⃣ 왜 필요한가? (사용 이유)

@GetMapping("/users")
public String users(
    @RequestParam String id,
    @RequestHeader("Authorization") String token,
    HttpServletRequest request
)

 

❌ 문제점

  • 컨트롤러마다 같은 파라미터 파싱 코드 반복
  • 인증 정보, 사용자 정보, 헤더 값 처리 로직이 컨트롤러에 섞임
  • 테스트 어려움 / 관심사 분리 안됨

👉 그래서

컨트롤러 파라미터 하나로 의미 있는 객체를 자동 주입하고 싶을 때 HandlerMethodArgumentResolver를 사용한다.

 

2️⃣ HandlerMethodArgumentResolver 인터페이스

public interface HandlerMethodArgumentResolver {

    boolean supportsParameter(MethodParameter parameter);

    Object resolveArgument(
        MethodParameter parameter,
        ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest,
        WebDataBinderFactory binderFactory
    ) throws Exception;
}

 

핵심 메서드 2개

메서드 역할
supportsParameter() 이 파라미터를 내가 처리할 수 있는지 판단
resolveArgument() 실제로 파라미터 객체를 만들어 반환

 

3️⃣ 동작 흐름 (중요 ⭐)

HTTP 요청
 ↓
DispatcherServlet
 ↓
HandlerMapping
 ↓
HandlerAdapter
 ↓
컨트롤러 메서드 호출 전
 ↓
HandlerMethodArgumentResolver 목록 순회
   └ supportsParameter() == true ?
        └ resolveArgument() 실행
 ↓
컨트롤러 메서드 실행

 

👉 컨트롤러는 파라미터 생성 과정을 전혀 모른다.

 

4️⃣ 실제 예제 (가장 이해 잘 되는 예)

🎯 목표

@GetMapping("/me")
public String me(@LoginUser User user) {
    return user.getName();
}

 

4-1️⃣ 커스텀 어노테이션

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginUser {
}

 

4-2️⃣ Resolver 구현

@Component
public class CustomResolver implements HandlerMethodArgumentResolver {

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		System.out.println("parameter === "+parameter.getParameterName());
		System.out.println("parameter === "+parameter.getParameterType());
		System.out.println("parameter === "+parameter.getParameterType().equals(int.class));
		return parameter.hasMethodAnnotation(LoginUser.class);
	}

	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
		HttpServletRequest request =
            (HttpServletRequest) webRequest.getNativeRequest();

        // 예: 세션에서 사용자 조회
        HttpSession session = request.getSession(false);
        return session != null ? session.getAttribute("LOGIN_USER") : null;
	}
}

 

4-3️⃣ Spring MVC에 등록

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
	
	@Autowired
	CustomResolver customResolver;
	
	@Override
	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
		resolvers.add(customResolver);
	}
}

 

4-4️⃣ 컨트롤러 사용

@GetMapping("/me")
public String me(@LoginUser User user) {
    return user.getName();
}

 

컨트롤러 메서드에 오기전에 파라미터에 대한 공통 작업들을 처리해놓으면 깔끔해진다.

 

5️⃣ 언제 쓰면 좋은가?

✅ 쓰면 좋은 경우

  • 로그인 사용자 주입(@LoginUser)
  • JWT → User 객체 변환
  • 공통 헤더 파싱( @ClientIp, @ApiVersion )
  • Request → 도메인 객체 변환 로직이 반복될 때

 

❌ 굳이 안 써도 되는 경우

  • 단순 @RequestParam, @PathVariable
  • 컨트롤러마다 다른 파싱 로직
  • 일회성 처리

 

6️⃣ Spring Security 와의 관계 (중요)

public String test(@AuthenticationPrincipal UserDetails user)

 

➡ 내부적으로 HandlerMethodArgumentResolver 구현체가 있음

 

 

 

 

728x90
반응형