본문 바로가기

토비의 스프링

[토비의 스프링 2권] DispatcherServlet과 MVC 아키텍처

 

1) MVC 아키텍처와 프론트 컨트롤러 패턴

 

- 스프링의 웹 기술은 MVC 아키텍처를 근간으로 하고 있다.

  MVC는 프레젠테이션 계층의 구성욧소를 정보로 담은 모델(M),

  화면 출력 로직을 담은 뷰(V), 그리고 제어 로직을 담은 컨트롤러(C)로 분리하고,

  이 세 가지 요소가 서로 협력해서 하나의 웹 요청을 처리하고 응답을 만들어내는 구조다. 

 

- MVC 아키텍처는 보통 프론트 컨트롤러(front controller) 패턴과 함께 사용된다.

  프론트 컨트롤러 패턴은 중앙집중형 컨트롤러를 

  프레젠테이션 계층의 제일 앞에 둬서 서버로 들어오는 모든 요청을 먼저 받아서

  처리하게 만든다.

 

- 프론트 컨트롤러는 클라이언트가 보낸 요청을 받아서 

  공통적인 작업을 먼저 수행한 후에 적절한 세부 컨트롤러로 작업을 위임해주고,

  클라이언트에게 보낼 뷰를 선택해서 최종 결과를 생성하는 등의 

  작업을 수행한다. 

 

- 예외가 발생했을 때, 이를 일관된 방식으로 처리하는 것도 

  프론트 컨트롤러의 역할이다.

  프론트 컨트롤러는 컨트롤러와 뷰, 그리고 그 사이에서 주고 받는 모델,

  세 가지를 이용해서 작업을 수행하는 게 일반적이다. 

 

 
2) DispatcherServlet

스프링이 제공하는 스프링 서블릿/MVC의 핵심은 DispatcherServlet이라는 

  프론트 컨트롤러다.

  이 DispatcherServlet은 MVC 아키텍처로 구성된 프레젠테이션 계층을 만들 수 있도록

  설계되어 있다. 

 

- 다음은 서버가 브라우저나 여타 HTTP 클라이언트로부터

  HTTP 요청을 받기 시작해서 다시 HTTP로 결과를 응답해주기까지의 

  과정을 살펴보자. 


(1) DispatcherServlet의 HTTP 요청 접수

- 자바 서버의 서블릿 컨테이너는 HTTP 프로토콜을 통해 들어오는 요청이

  스프링의 DispatcherServlet에 할당된 것이라면

  HTTP 요청 정보를 DispatcherServlet에 전달해준다. 

 

- DisptacherServlet은 모든 요청에 대해 공통적으로 진행해야 하는 전처리 작업

  등록된 것이 있다면 이를 먼저 수행한다.

  공통적으로 이용 가능한 보안이나 파라미터 조작, 한글 디코딩과 같은 작업이 적용된다.

 

(2) DispatcherServlet에서 컨트롤러로 HTTP 요청 위임

- DispatcherServlet은 URL이나 파라미터 정보, HTTP 명령 등을 참고로 해서

  어떤 컨트롤러에게 작업을 위임할지 결정한다.

  컨트롤러를 선정하는 것은 DispatcherServlet의 핸들러 매핑 전략을 이용한다. 

 

- 스프링에서는 컨트롤러를 핸들러라고도 부른다.

  웹의 요청아 다루는(handle) 오브젝트라는 의미다.

  사용자 요청을 기준으로 어떤 핸들러에게 작업을 위임할지를 결정해주는 것을

  핸들러 매핑 전략이라고 한다. 

 

- 이를 전략이라고 부르는 이유는 DI의 가장 대표적인 용도라고 할 수 있는

  전략 패턴이 적용되어 있기 때문이다.

  DispatcherServlet의 핸들러 매핑 전략은 DispatcherServlet의 수정 없이도

  DI를 통해 얼마든지 확장 가능하다. 

  어떤 URL이 들어오면 어떤 컨트롤러 오브젝트가 이를 처리하게 할지를 매핑해주는

  전략을 만들어서 DI로 제공해주기만 하면 된다. 

 

- 엄밀히 말하면 DispatcherServlet은 그 자체로 스프링 컨텍스트에 등록된 빈이 아니므로

  DI가 일어나는 것은 아니다.

  하지만, 마치 DI가 적용되는 것처럼 서블릿 애플리케이션 컨텍스트의 빈을 가져와 사용한다. 

  이 때, 특정 인터페이스를 구현한 빈을 자동으로 찾아오는 자동와이어링 기법을 이용한다. 

 

- 어떤 컨트롤러/핸들러가 요청을 처리하게 할지를 결정했다면,

  다음은 해당 컨트롤러 오브젝트의 메소드를 호출해서

  실제로 웹 요청을 처리하는 작업을 위임할 차례다. 

 

- 그런데 DispatcherServlet이 매핑으로 찾은 컨트롤러를 가져와 실행하려면

  컨트롤러 메소드를 어떻게 호출할지를 알고 있어야 한다. 

  그렇다면 DispatcherServlet에서 호출 가능한 컨트롤러는 

  특정 인터페이스를 구현해야 한다는 식의 규약을 따라서 작성해야 할까? 

 

- 그건 아니다. DispatcherServlet이 요청을 위임하는 대상인 컨트롤러에는

  아무런 제약이나 선결 조건이 없다는 뜻이다.

  DispatcherServlet은 어떤 종류의 오브젝트라도 컨트롤러로 사용할 수 있다.

  이것이 DispatcherServlet이 갖는 무한한 확장성의 비결이다.

  DispatcherServlet에는 어떤 컨트롤러라도 사용 가능하다. 

 

- 하지만 자바의 오브젝트 사이에 무엇인가 요청이 전달되려면 메소드가 호출돼야 하고,

  그러려면 DispatcherServlet이 컨트롤러 오브젝트의 메소드를 호출할 수 있는 방법이 있어야 하지 않는가?

  어떻게 제각각 다른 메소드와 포맷을 가진 오브젝트를 컨트롤러로 만들어놓고

  DispatcherServlet이 이를 알아서 호출하게 만들 수 있을까? 

 

- 해결책은 어댑터를 이용하는 것이다.

  전형적인 오브젝트 어댑터 패턴을 사용해서, 특정 컨트롤러를 호출해야 할 때는

  해당 컨트롤러 타입을 지원하는 어댑터를 중간에 껴서 호출하는 것이다.

  그러면 DispatcherServlet은 항상 일정한 방식으로 컨트롤러를 호출하고 결과를 받을 수 있다.