728x90

@RequestParam을 처리해주는 아규먼트 리졸버 = RequestParamMethodArgumentResolver

RequestParamMethodArgumentResolver는 AbstractNamedValueMethodArgumentResolver를 상속한 콘크리트 클래스고, resolveArgument를 오버라이딩 하지 않았기 때문에 AbstractNamedValueMethodArgumentResolver.resolveArgument가 호출된다.

흐름도

스크린샷 2021-11-25 오후 9 14 30

일단 흐름도는 이러하다.

어제 살펴봤던 내용은 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());
    }

}

일단 돌리게 되었을 때 DispatcherServletRequestMappingHandlerAdapter를 호출하게 된다.

여기서 요청 객체를 처리하는줄 알았었는데

ArgumentResolver들 중에 해당 객체를 처리할 수 있는 Resolver 클래스를 찾는다.

스크린샷 2021-11-25 오후 9 25 03

RequestMappingHandlerAdapter의 일부 메소드인데 이 클래스가

부름을 받으면 바로 Resolver들을 추가해주는데

여기서 유심히 봐야하는 부분이 디버그로 파란줄 쳐진 부분과

//catch-all아래 2번째줄 이 두줄을 유심히 봐야하는데

여기서 내가 느낀것은 useDefaultResolution이 옵션이라고 생각하고 넘어갔다.

스크린샷 2021-11-25 오후 9 28 58

HandlerMethodArgumentResolverComposite

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

스크린샷 2021-11-25 오후 9 41 16

그것은 이 HandlerMethodArgumentResolverComposite에서 for문으로 찾아주게 되어있다 ❗️❗️❗️❗️

RequestParamMethodArgumentResolver

여기서 체크하는 로직이 위 이미지 RequestParamMethodArgumentResolver 이다.

보면 파라미터 어노테이션을 갖고 있는지 여부, 또는 @RequestPart를 갖고있는지 등등

분기로 판단해서 객체를 처리하려고 한다.

여기서 아까 보고 넘어갔다했던 useDefaultResolution 애가 분기문에 this.useDefaultResolution이 보이는데

이게 true라면 현재 가지고있는 매개변수의 타입대로 따라가서 값을 매핑시켜준다.

스크린샷 2021-11-25 오후 9 35 30

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을 붙여주는것이 좋다.

정리

테코톡에서 디버그를 본 후에 깊게 한번 들어와서 공부를 해보려고 어제부터 탐색을 했다.

깊게 이렇게 들어와서 하나씩 보는것이 소스분석에도 도움이 되고 더 나아가서는

회사코드를 인계받을 때에도 이렇게 분석을 하면 핵심 로직을 빠르게 파악할 수 있을 것 같다.

그러면서 동시에 이렇게 정리까지 하니 머릿속에 많이 남아서 지식으로 가져가는 것도 좋은것 같다.

공부를 이렇게 했어야 됐는데 너무 늦은건가 싶기도 하지만 꾸준한게 답인것 같다. 🔥🔥🔥

728x90

'디버깅' 카테고리의 다른 글

AWS SNS 토큰 에러  (0) 2022.08.10
YAML 파일을 읽어보자  (0) 2022.08.09
AbstractMessageConverter  (0) 2022.08.09
RequestMapping 동작  (0) 2022.08.09

+ Recent posts