@RequestParam을 처리해주는 아규먼트 리졸버 = RequestParamMethodArgumentResolver
RequestParamMethodArgumentResolver는 AbstractNamedValueMethodArgumentResolver를 상속한 콘크리트 클래스고, resolveArgument를 오버라이딩 하지 않았기 때문에 AbstractNamedValueMethodArgumentResolver.resolveArgument가 호출된다.
흐름도

일단 흐름도는 이러하다.
어제 살펴봤던 내용은 DispatcherServlet 이전의 처리내용이었다.
스프링 MVC에 대해서 교환대라고 할 수 있는 DispatcherServlet 이 클래스가
HandlerAdapter 등.. 조건에 부합하는 객체들을 찾아서 전달을 다 해준다.
@RequestParam 이 있을 때는 당연히 매핑이 되어서 가져올것은 알았지만
없을 때 생략해도 가져오는 이것은 동작이 어떻게 되는지 궁금했다.
코드부터 보자.
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(String id, String name) {
return id + name;
}
@GetMapping("/hello2")
public String hello2(@RequestParam String id, String name) {
return id + name;
}
}
이러한 두개의 예시 hello, hello2를 만들었고 테스트 코드로 디버깅하는게 더 좋을것 같아서
테스트 코드로 디버깅을 진행했다.
@WebMvcTest(HelloController.class)
@AutoConfigureMockMvc
class HelloControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void hello() throws Exception {
mockMvc.perform(get("/hello?id=lsj&name=홍길동"))
.andDo(print())
.andExpect(status().isOk());
}
}
일단 돌리게 되었을 때 DispatcherServlet이 RequestMappingHandlerAdapter를 호출하게 된다.
여기서 요청 객체를 처리하는줄 알았었는데
ArgumentResolver들 중에 해당 객체를 처리할 수 있는 Resolver 클래스를 찾는다.

RequestMappingHandlerAdapter의 일부 메소드인데 이 클래스가
부름을 받으면 바로 Resolver들을 추가해주는데
여기서 유심히 봐야하는 부분이 디버그로 파란줄 쳐진 부분과
//catch-all아래 2번째줄 이 두줄을 유심히 봐야하는데
여기서 내가 느낀것은 useDefaultResolution이 옵션이라고 생각하고 넘어갔다.

HandlerMethodArgumentResolverComposite
다음은 저 어댑터들을 가지고서 넘어온 파라미터를 처리해줄 수 있는 Resolver들을 찾는데

그것은 이 HandlerMethodArgumentResolverComposite에서 for문으로 찾아주게 되어있다 ❗️❗️❗️❗️
RequestParamMethodArgumentResolver
여기서 체크하는 로직이 위 이미지 RequestParamMethodArgumentResolver 이다.
보면 파라미터 어노테이션을 갖고 있는지 여부, 또는 @RequestPart를 갖고있는지 등등
분기로 판단해서 객체를 처리하려고 한다.
여기서 아까 보고 넘어갔다했던 useDefaultResolution 애가 분기문에 this.useDefaultResolution이 보이는데
이게 true라면 현재 가지고있는 매개변수의 타입대로 따라가서 값을 매핑시켜준다.

0번째 인자는 false, 25번째 인자는 true를 갖고있다.
정리를 해보자면
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
상태값이 false이면 @RequestParam이 있는 경우 매개변수 생성
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
상태값이 true이면 @RequestParam이 없는 경우 매개변수 생성을 하는 것
그래서 둘다 있으나 없으나 생성을 해준다.
근데 이제는 성능은 어떤게 좋냐고 묻는다면 명시적으로 @RequestParam을 붙인 객체는 0번째에서 바로
찾아서 매핑이 될것이다.
반대로 명시적으로 붙이지 않았다면?
매번 25번째에 있는 Resolver를 통해서 매핑시키게 될 것이다. 이게 단순 한개라면 모르겠지만,
여러 사용자 + 여러 스레드 + 파라미터의 갯수 세개의 조건이 셋중에 하나 또는 전부가 많아진다면
성능은 안좋아질게 훤히 보인다.
그래서 안붙여도 되지만 성능을 최적화 하려면 명시적으로 @RequestParam을 붙여주는것이 좋다.
정리
테코톡에서 디버그를 본 후에 깊게 한번 들어와서 공부를 해보려고 어제부터 탐색을 했다.
깊게 이렇게 들어와서 하나씩 보는것이 소스분석에도 도움이 되고 더 나아가서는
회사코드를 인계받을 때에도 이렇게 분석을 하면 핵심 로직을 빠르게 파악할 수 있을 것 같다.
그러면서 동시에 이렇게 정리까지 하니 머릿속에 많이 남아서 지식으로 가져가는 것도 좋은것 같다.
공부를 이렇게 했어야 됐는데 너무 늦은건가 싶기도 하지만 꾸준한게 답인것 같다. 🔥🔥🔥
'디버깅' 카테고리의 다른 글
| AWS SNS 토큰 에러 (0) | 2022.08.10 |
|---|---|
| YAML 파일을 읽어보자 (1) | 2022.08.09 |
| AbstractMessageConverter (0) | 2022.08.09 |
| RequestMapping 동작 (1) | 2022.08.09 |