[Spring] ArgumentResolver

ArgumentResolver

Spring Framework에서 컨트롤러(Controller)의 메서드로 들어오는 파라미터를 유연하게 바인딩(가공/주입)해주는 역할

ArgumentResolver 없는 코드

매번 세션을 확인하고 캐스팅하는 코드가 반복(지저분함)

@GetMapping("/my-info")
public ResponseEntity<UserInfo> getMyInfo(HttpServletRequest request) {
    // 1. 세션 가져오기
    HttpSession session = request.getSession();

    // 2. 세션에서 유저 정보 꺼내기 (반복되는 코드)
    User user = (User) session.getAttribute("LOGIN_USER");

    // 3. 비즈니스 로직
    return ResponseEntity.ok(user.getInfo());
}

ArgumentResolver 적용 예시

반복되는 작업을 없애기 위해 @LoginUser라는 어노테이션만 붙이면 자동으로 유저 객체를 주입받도록 만들어 보자.

Step 1: 커스텀 어노테이션 생성

@Target(ElementType.PARAMETER) // 파라미터에만 붙임
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginUser {
}

Step 2: HandlerMethodArgumentResolver 구현

@Component
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {

    // 1. 이 Resolver가 동작할 조건 (파라미터 검사)
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        // 파라미터에 @LoginUser 어노테이션이 붙어있고, 타입이 User 클래스인 경우만 동작
        boolean isLoginUserAnnotation = parameter.getParameterAnnotation(LoginUser.class) != null;
        boolean isUserClass = User.class.equals(parameter.getParameterType());
        
        return isLoginUserAnnotation && isUserClass;
    }

    // 2. 파라미터에 들어갈 값을 실제로 생성/가공하는 로직
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        
        // 세션에서 유저 정보 가져오기
        HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
        HttpSession session = request.getSession(false);
        
        if (session == null) {
            return null;
        }
        
        return session.getAttribute("LOGIN_USER"); // 이 리턴값이 컨트롤러 파라미터로 들어감
    }
}

Step 3: WebMvcConfigurer에 등록

@Configuration
public class WebConfig implements WebMvcConfigurer {

    private final LoginUserArgumentResolver loginUserArgumentResolver;

    // 생성자 주입 생략

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(loginUserArgumentResolver);
    }
}

ArgumentResolver 적용 후 코드

@GetMapping("/my-info")
public ResponseEntity<UserInfo> getMyInfo(@LoginUser User user) {
    // 세션에서 꺼내는 과정이 사라짐! 바로 user 객체 사용 가능
    return ResponseEntity.ok(user.getInfo());
}

© 2023 Lee. All rights reserved.