티스토리 뷰

개발 환경
Spring Boot 3.2.4
JAVA 17
MacOS
Postman v10.22
해당 블로그 코드들은 GitHub에 남겨져 있습니다.
ArgumentResolver를 사용하는 이유
컨트롤러에서 매개변수로 사용되는 인자에 대해 공통적으로 처리해야할 로직등이 있을 경우, 중복 코드를 줄이고 공통 기능으로 추출하여 사용하려고 쓴다.
그래서 컨트롤러(핸들러)가 필요로 하는 다양한 파라미터의 값(객체)을 생성할 수 있다.
즉, 커스텀해서 내가 원하는 파라미터(객체)로 변경해서 컨트롤러에서 편하게 사용할 수 있다는 것이다.
Tip) Resolver에서 처리할 수 있는 파라미터 목록들
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는 언제 작동할까?
- Client의 Request 요청
- Dispatcher Servlet 에서 요청 처리
- Client의 Request 에 대한 Handler Mapping
- a) RequestMappingHandlerAdapter(핸들러 어댑터) 가 수행된다.
- b) Interceptor(인터셉터) 가 수행 된다.
- c) ArgumentResolver가 처리된다.
- d) 필요에 따라서 Converter 가 수행 된다.
- 컨트롤러 (핸들러) 실행
동작 방식
스프링에 존재하는 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로 이루어진 컨트롤러에서 어떤 사람은 조회되고 어떤 사람은 조회 안되는 로직을 짤 수 있게 되지 않을까? 라고 만들게 되었습니다.

감사합니다.
'백엔드 > 🌸Spring' 카테고리의 다른 글
[Spring] Spring Boot3에 Swagger 적용하기 (6) | 2024.10.17 |
---|---|
[Spring] ThreadLocal : 동시성을 해결하는 물품 보관소 (0) | 2024.05.10 |
[Spring] 첨부파일과 여러 데이터 전송하기 (0) | 2024.04.10 |
[Spring] Validation : 데이터 유효성 검사의 핵심 요소 (0) | 2024.03.27 |
[Spring] 빈이 중복 조회가 된다면? : @Autowired, @Qualifier, @Primary 의존성 주입의 다양한 접근 방법 (0) | 2023.12.19 |
- Total
- Today
- Yesterday
- git
- 템플릿
- java
- 코딩테스트
- 계단 오르기
- 개발
- 디자인패턴
- 실시간 채팅
- AJAX
- 네트워크
- 개발환경
- Mac
- 깃허브 액션
- 오라클
- DBeaver
- 개발블로그
- Cors
- 데이터 베이스
- spring
- aws
- Spring Security
- 프론트
- 개발자
- JavaScript
- Fetch
- Front
- 자바스크립트
- 비동기
- 프로세스
- jvm
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |