본문 바로가기

백엔드 개발/SpringMVC

DispatcherServlet Source 까보기 (어떻게 돌아가는지)

1. DispatcherServlet이란 뭐였나? (복습)

본래 하나의 서블릿에 입력 처리 출력 과정이 다 담겨 있었다. 여기서 모든 서블릿에 공통적이고 돌아가는 원리의 차이가 없는 입력 부분을 앞으로 빼서, 하나의 서블릿으로 만들었다. (처리나 출력은 서블릿마다 기능이 다를 수 있지만, 사용자의 요청을 받는 입력부분은 거의 모든 서블릿이 공통적으로 같다.) 이것이 디스패쳐 서블릿이다. 스프링은 서블릿을 전처리 전담하는 DispatcherServlet과 처리담당 Controller, 출력담당 View로 나누어서 관심사를 분리했다.

(관심사란 사용자가 관심을 가지고 해결해야할 작업을 말한다. MVC 패턴은 해결해야할 분야를 각각 DisPatcherServlet, Controller, view로 나누어서 관심사의 분리를 실현했다.) 

 

MVC의 작동과정을 단순하게 보면 

(1) DispatcerServlet이 요청을 받는다. 

(2) 그리고 요청을 해결할 수 있는 매소드가 무엇인지 찾고, 그 매소드를 가지고 있는 컨트롤러에게 요청을 전달한다.

(3) 컨트롤러는 요청을 처리하고, 클라이언트 화면에 보여야할 view가 무엇인지 반환한다. 

(4) DispatcherServlet은 컨트롤러의 처리 결과(모델에 담겨있는 것)를 해당 View에게 전달한다. 

(5) View는 모델에 담겨있는 처리 결과를 활용해 요청에 대한 응답을 만들어 브라우저에게 보낸다. 

2. MVC 작동 과정 (100% 자세히)

위의 과정에서는 많은 분야가 생략되어 있다. 이제 MVC 원리가 대략적으로 익숙해졌으니 생략했던 절차들도 되짚어보자.

(1) 요청을 받으면 DispatcerServlet은 HandlerMapping에게 해당 URL과 매핑된 매소드가 무엇인지 물어본다. 

(2) HandlerMapping은 URL (key) , Mapping된 매소드(Value)로 하는 Map을 가지고 있다.

     HandlerMapping은 해당 표를 보고 요청 받은 URL이 어떤 매소드와 매핑되어 있는지 찾고, 매핑된 매소드의 정보를 

     다시 서블릿에게 반환한다.

(3) DispatcherServlet은 해당 매소드를 가진 HandlerAdapter가 무엇인지 알아낸다. 

     (그림과 다르게 HandlerAdapter는 여러 개 있다. 그래서 DS가 전부한테 니가 가지고 있냐? 라고 물어본다.)

(4) 해당 매소드를 가진 HandlerAdapter를 찾으면 그 녀석한테 요청을 전달하고, HandlerAdapter는 그 요청을 다시 컨트롤러한테 전달한다. 

(5) 다시 HandlerAdapter가 컨트롤러의 처리결과(모델에 담아서)와 응답에 쓸 view의 이름을 받아와 DS에게 전달한다.

(6) DS는 단지 view의 이름만 보고 해당 view가 어디에 있고 무엇인지 알 수 가 없다. 왜냐하면 view의 이름에는 경로주소와 확장자가 쓰여있지 않기 때문이다. 그래서 DS는 다시 ViewResolver한테 해당 이름을 가진 view의 경로주소와 확장자가 무엇인지 알려달라고 한다.

(7) ViewResolver는 경로주소와 확장자까지 쓰인 view의 완전한 이름을 다시 DS에게 반환한다.

(8) DS는 이걸 JstlView라는 인터페이스에게 전달한다. JstlView는 일종의 탐색기 같은 녀석으로 해당 view를 찾아준다.

(9) view가 모델에 담긴 처리결과를 활용해 응답을 만들어내서 브라우저에 반환한다. 

 

**굳이 Controller와 Dispatcher 사이에 HandlerAdaptor를 쓰는 이유가 무엇인가? 

핸들러 어댑터를  사용하면 Dispatcher와 Controller 사이에 느슨한 연결이 가능해진다. 느슨한 연결이란 변경이 유리한 설계의 한 종류이다. 만약에 DS가 컨트롤러를 직접 호출하는 경우라면, 만약 호출해야할 컨트롤러 자체가 바뀌었을 때, DS를 수정 해야한다. 

하지만 핸들러 어댑터를 중간에 쓰면 교체가 쉬워진다. 여러 종류의 핸들러 어댑터 중 우리가 교체하고픈 컨트롤러를 가진 녀석으로 갈아끼우기만 하면 되기 때문이다. DS 내부를 뜯어 고칠 필요가 없다. 

또한 한 가지 장점이 더 있다면 HandlerAdapter는 컨트롤러 이외의 다른 객체도 필요하다면 호출할 수 있게 되있다. 그 점에서도 컨트롤러 직접 호출보다 유연성이 높다.

 

** 우리는 viewResolver 중 InternalResourceViewResolver라는 녀석을 쓴다.

3. 코드 까보기 

위에서 설명한 내용들이 실제로 어떻게 구현되고 있는지 미약하게 남아 알아보겠다. 

설명할 내용이 어렵지만, 전체 과정은 MVC 돌아가는 원리와 일치한다. 

Debug로 보면 실제 요청들이 어떤 순서로 컨트롤러로 넘어오는지 볼 수 있다. 

먼저 Method.invoke는 요청이 들어온 상황이다. 여기서 HandlerMethod란 HandlerMapping을 이용하는 것을 말한다. 

HandlerMapping으로 요청이 온 URL을 넣어서 그와 매핑된 매소드 정보를 반환 받는다. 

그 후에는 HandlerAdapter가 가동 중임을 알 수 있다. 

doPatch는 해당 매소드를 호출하는 것이다. 

/login/login 요청을 예로 들어서 실행했는데, 이건 Get 요청이라 doGet이 호출되었음을 알 수 있다. 

더 자세히는 아직 잘 모르겠어서 다음 복습 때 마저 하겠다.