티스토리 뷰

Spring 로고

 

 

 

개발 환경

 

Spring Boot 3.2.4
JAVA 17
MacOS
Postman v10.22

 

해당 블로그 코드들은 GitHub에 남겨져 있습니다.

 

 

 

 

 

ArgumentResolver를 사용하는 이유

 

컨트롤러에서 매개변수로 사용되는 인자에 대해 공통적으로 처리해야할 로직등이 있을 경우, 중복 코드를 줄이고 공통 기능으로 추출하여 사용하려고 쓴다.

그래서 컨트롤러(핸들러)가 필요로 하는 다양한 파라미터의 값(객체)을 생성할 수 있다.
즉, 커스텀해서 내가 원하는 파라미터(객체)로 변경해서 컨트롤러에서 편하게 사용할 수 있다는 것이다.

 

 

Tip) Resolver에서 처리할 수 있는 파라미터 목록들

https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/arguments.html

 

Method Arguments :: Spring Framework

JDK 8’s java.util.Optional is supported as a method argument in combination with annotations that have a required attribute (for example, @RequestParam, @RequestHeader, and others) and is equivalent to required=false.

docs.spring.io

 

 

 

 

 

 

ArgumentResolver는 언제 작동할까?

 

  1. Client의  Request  요청

  2.  Dispatcher Servlet  에서 요청 처리

  3. Client의  Request 에 대한  Handler Mapping 
    • a)  RequestMappingHandlerAdapter(핸들러 어댑터)  가 수행된다.
    • b)  Interceptor(인터셉터) 가 수행 된다.
    • c) ArgumentResolver가 처리된다.
    • d) 필요에 따라서  Converter 가 수행 된다.

  4. 컨트롤러 (핸들러) 실행

 

 

 

 

 

동작 방식

 

스프링에 존재하는 Interface인 ArgumentResolver 코드를 보자.

// ArgumentResolver의 코드
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()  를 호출해서 실제 객체를 생성한다. 그리고 생성된 객체가 컨트롤러 호출 시 넘어가는 것이다.

 

우리는 이것을 확장하여 구현해볼라고 한다.

 

 

 

 

 

 

 

구현 예시

 

우리는 클라이언트가 @Pathvariable을 포함한 컨트롤러 매핑된 핸들러를 요청하면 

해당 클라이언트의 IP, 어떤 URI로 호출 했는지, 호출한 Controller에 매핑 패턴은 무엇인지, 어떠한 HTTP 메서드를 호출했는지

정보를 객체로 만들어 컨트롤러 단에서 바로 사용할 수 있게 만들었다.

 

즉, 클라이언트 요청 → 클라이언트 정보 생성 → 컨트롤러에서 사용 이렇게 활용 예시를 들라고 한다.

 

 

실제에서는 이렇게 잘 사용은 안하고 Intercepor에서 처리하거나 할텐데, 지금은 배우는 단계이고

Spring MVC를 이런식으로 사용되고 흐름이 이렇구나가 목적입니다.

 

 

 

 

 

코드

 

해당 블로그 코드들은 GItHub 에 남겨져 있습니다.

 

ClientRequest 객체 생성

@Setter
@Getter
@ToString
public class ClientRequest {

    private String url;
    private Object params;
    private String method;
    private String ip;
}

 

 

Annotation 생성

  • @Target(ElmentType.PARAMETER) : 파라미터에만 사용
  • @Retention(RetentionPolicy.RUNTIME) : 리플렉션 등을 활용할 수 있도록 런타임까지 애노테이션 정보가 남아있음
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CliectInfo {}

 

 

ClientInfoArgumentResolver (  =  HandlerMethodArgumentResolver 구현)

코드가 길어서 숨기게 되었습니다.

더보기
@Slf4j
public class ClientInfoArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        log.info("supportsParameter 실행!");

        // 파라미터에 어노테이션이 붙어 있는지
        boolean hasClientInfoAnnotation = parameter.hasParameterAnnotation(CliectInfo.class);

        // ClientRequest 객체에 붙어 있는지
        boolean hasClientType = ClientRequest.class.isAssignableFrom(parameter.getParameterType());

        return hasClientInfoAnnotation && hasClientType;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

        ClientRequest clientRequest = new ClientRequest();
        HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
        clientRequest.setIp(request.getRemoteAddr());
        clientRequest.setUrl(request.getRequestURI());
        clientRequest.setMethod(request.getMethod());
        clientRequest.setParams(request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE));

        // ClientRequest(url=/spring-mvc/coasis, params=/spring-mvc/{name}, method=GET, ip=0:0:0:0:0:0:0:1)
        return clientRequest;
    }
}

 

 

WebConfig에 등록

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addArgumentResolvers (List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new ClientInfoArgumentResolver());
    }
}

 

 

Controller에서 사용

@Slf4j
@Controller
@RequestMapping("/spring-mvc")
public class Contoller {

    // 페이지 이동
    @GetMapping("/{name}")
    public String detail(@CliectInfo ClientRequest clientRequest, @PathVariable String name, Model model){

        // clientRequest 객체 가지고 로직 수행

        model.addAttribute("name", name);
        return "detail";
    }

    // Api 일 경우
    @GetMapping("/api/{name}")
    @ResponseBody
    public String apiResponse(@CliectInfo ClientRequest clientRequest, @PathVariable String name){

        // clientRequest 객체 가지고 로직 수행

        return name;
    }
}

 

 

 

 

 

실행 결과

 

실행 결과

 

ArgumentResolver 를 실행한 후에 컨트롤러 실행하는 것을 볼 수 있다.

이제 이 정보를 가지고 PathVariable로 이루어진 컨트롤러에서 어떤 사람은 조회되고 어떤 사람은 조회 안되는 로직을 짤 수 있게 되지 않을까? 라고 만들게 되었습니다.

 

 

 

감사합니다.

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함