WebApplicationContext
Servlet이란?
자바 VM에게 main() 메소드를 가진 클래스를 시작시켜 달라고 요청할 수 있는 독립 자바 프로그램과는 다르게, 웹 환경에서는 자바 VM에게 main() 메소드를 호출할 방법이 없다. 또한 여러 명의 사용자가 동시에 웹 애플리케이션을 이용한다.
그래서 웹 환경에서는 main() 메소드 대신 서블릿 컨테이너가 HTTP 요청을 받아서, 해당 요청에 매핑되어 있는 서블릿을 실행해주는 방식으로 동작한다. main() 메소드 역할을 하는 서블릿을 만들어두고, 미리 애플리케이션 컨텍스트를 생성해 둔 다음, 요청이 서블릿으로 들어올 때마다 getBean()으로 필요한 빈을 가져와서 정해진 메소드를 실행하는 방식으로 동작한다.
WebApplicationContext의 구조
스프링은 서블릿 웹 애플리케이션의 컨텍스트를 두 가지로 분리해놓았다.
- 루트 애플리케이션 컨텍스트
- 웹 기술에서 완전히 독립적인 비즈니스 서비스 계층과 데이터 액세스 계층
- 서블릿 애플리케이션 컨텍스트
- 스프링 웹 기술을 기반으로 동작하는 웹 관련 빈
이렇게 두 가지로 분리한 이유는 스프링 웹 서블릿 컨텍스트를 다른 기술로 대체할 수 있도록 하기 위해서이다.
스프링 MVC와 DispatcherServlet 전략
스프링의 철학
스프링은 유연성과 확장성에 중점을 두고 어떤 종류의 시스템 개발이나 환경에도 잘 들어맞도록 재구성할 수 있는 범용적 프레임워크로, 이런 프레임워크는 각 계층과 기술 사이의 독립성을 중요하게 생각한다.
- 각 계층을 독립적으로 개발하고 테스트할 수 있으며, 한 계층의 기술이나 구현은 다른 계층의 코드에 영향을 주지 않은 채로 자유롭게 변경하거나 교체할 수 있다.
- 같은 계층에서도 환경에 종속적인 로우 레벨의 기술과 상위 레벨의 애플리케이션 코드는 서비스 추상화와 같은 방식으로 최대한 느슨하게 연결해서 의존성이 발생하지 않도록 만든다.
이런 원리는 스프링의 웹 기술에도 동일하게 적용된다.
스프링은 특정 기술이나 방식에 매이지 않으면서 웹 프레젠테이션 계층의 각종 기술을 조합, 확장해서 사용할 수 있는 매우 유연한 웹 프레젠테이션 계층의 각종 기술을 조합, 확장해서 사용할 수 있는 매우 유연한 웹 애플리케이션 개발의 기본 틀을 제공해준다.
스프링 웹 기술의 핵심이자 기반이 되는 것은 DispatcherServlet이다. 이 서블릿은 스프링의 웹 기술을 구성하는 다양한 전략을 DI로 구성해서 확장하도록 만들어진 스프링 서블릿/MVC의 엔진과 같은 역할을 한다.
DispatcherSerlvet과 MVC 아키텍처
스프링의 웹 기술은 MVC 아키텍처를 근간으로 한다.
MVC는 프레진테이션 계층의 구성요소를 정보를 담은 모델 (M), 화면 출력 로직을 담은 뷰(V), 그리고 제어 로직을 담은 컨트롤러(C)로 분리하고 이 세 가지 요소가 서로 협력해서 하나의 웹 요청을 처리하고 응답을 만들어내는 구조이다.
프론트 컨트롤러 패턴
MVC 아키텍처는 보통 프론트 컨트롤러 패턴과 함께 사용된다. 스프링 MVC 역시 프론트 컨트롤러 아키텍처로 구성되어있다
프론트 컨트롤러 패턴은 중앙집중형 컨트롤러를 프레젠테이션 계층의 제일 앞에 둬서 서버로 들어오는 모든 요청을 먼저 받아서 처리하게 만든다.
프론트 컨트롤러는 클라이언트가 보낸 요청을 받아서 공통적인 작업을 먼저 수행한 후에 적절한 세부 컨트롤러로 작업을 위임해주고, 클라이언트에게 보낼 뷰를 선택해서 최종 결과를 생성하는 등의 작업을 수행한다. (예외가 발생했을 때 이를 일관된 방식으로 처리하는 것도 프론트 컨트롤러의 역할이다.)
요약
- Spring MVC는 대표 서블릿인 DispatcherServlet에서 모든 요청을 받아서 공통의 작업을 처리한 후에, 적절한 핸들러(컨트롤러)에게 작업을 위임하는 "프론트 컨트롤러 패턴"로 구성되어 있다.
처리 과정
1. DispatcherServlet의 HTTP 요청 접수
- 자바 서버의 서블릿 컨테이너는 HTTP 프로토콜을 통해 들어오는 요청이 스프링의 DispatcherServlet에 할당된 것이라면 HTTP 요청정보를 DispatcherServlet에 전달해준다.
- DispatcherServlet은 모든 요청에 대해 공통적으로 진행해야 하는 전처리 작업이 등록된 것이 있다면 이를 먼저 수행한다. (공통적으로 이용 가능한 보안이나 파라미터 조작, 한글 디코딩 같은 작업)
2. DispatcherServlet에서 컨트롤러로 HTTP 요청 위임
- DispatcherServlet은 URL이나 파라미터 정보, HTTP 명령 등을 참고해서 어떤 컨트롤러에게 작업을 위임할지 결정한다. 사용자 요청을 기준으로 어떤 핸들러에게 작업을 위임할지를 결정해주는 것을 핸들러 매핑 전략이라고 한다.
- 어떤 컨트롤러(핸들러)가 요청을 처리하게 할지를 결정했다면, 다음은 해당 컨트롤러 오브젝트의 메소드를 호출해서 실제로 웹 요청을 처리하는 작업을 위임하는데, DispatcherServlet이 매핑으로 찾은 컨트롤러를 가져와 실행하려면 컨트롤러 메소드를 어떻게 호출할지를 알고 있어야 한다. 이때 DispatcherServlet은 어떤 종류의 오브젝트라도 컨트롤러로 사용할 수 있다.
- 여기서는 전형적인 오브젝트 어댑터 패턴을 사용해서, 특정 컨트롤러를 호출해야 할 때는 해당 컨트롤러 타입을 지원하는 어댑터를 중간에 껴서 호출한다. 그러면 DispatcherServlet은 항상 일정한 방식으로 컨트롤러를 호출하고 결과를 받을 수 있다.
- DispatcherServlet이 핸들러 어댑터에 웹 요청을 전달할 때는 모든 웹 요청 정보가 담긴 HttpServetRequest 타입의 오브젝트를 전달해준다. 이를 어댑터가 적절히 변환해서 컨트롤러의 메소드가 받을 수 있는 파라미터로 변환해서 전달해주는 것이다.
3. 컨트롤러의 모델 생성과 정보 등록
- 컨트롤러의 작업은 먼저 사용자의 요청을 해석하고, 그에 따라 비즈니스 로직을 수행하도록 서비스 계층 오브젝트로 작업을 위임하는 것, 그리고 결과를 받아서 모델을 생성하고, 어떤 뷰를 사용할지 결정하는 역할을 수행한다.
4. 컨트롤러의 결과 리턴: 모델과 뷰
- 모델과 뷰 정보를 DispatcherServlet에게 넘겨준다.
5. DispatcherServlet의 뷰 호출과 모델 참조
- DispatcherServlet이 컨트롤러부터 모델과 뷰를 받은 후에, 뷰 오브젝트에 모델을 전달해주고 클라이언트에게 돌려줄 최종 결과물을 생성 요청 후 결과물을 HttpServletResponse 오브젝트에 담는다.
6. HTTP 응답 돌려주기
- DispatcherSerlvet은 등록된 후처리가 있으면 후속 작업을 진행한 뒤에 뷰가 만들어준 HttpServletResponse에 담긴 최종 결과를 서블릿 컨테이너에게 돌려준다.
- 서블릿 컨테이너는 HttpServletResponse에 담긴 정보를 HTTP 응답으로 만들어 사용자의 브라우저나 클라이언트에게 전송하고 작업을 종료한다.
DispatcherServlet DI 가능한 전략
HandlerMapping
- URL과 요청 정보를 기준으로 어떤 핸들러 (컨트롤러)를 사용할 것인지를 결정하는 로직을 담당한다.
- BeanNameUrlHandlerMapping, DefaultAnnotationHandlerMapping 두 가지가 디폴트로 설정되어 있음.
HandlerAdapter
- 핸들러 매핑으로 선택한 컨트롤러를 DispatcherServlet이 호출할 때 사용하는 어댑터.
- 컨트롤러의 타입에는 제한이 없으며, 컨트롤러 호출 방법은 타입에 따라 다르기 때문에 컨트롤러를 결정했다고 해서 호출 방법을 DispatcherServlet은 알 수가 없다. 그래서 컨트롤러 타입을 지원하는 HandlerAdapter가 필요하다.
- HttpRequestHandlerAdapter, SimpleControllerHandlerAdapter, AnnotationMethodHandlerAdatper 세 가지가 디폴트 전략으로 설정되어 있다.
HandlerExceptionResolver
예외가 발생했을 때 이를 처리하는 로직을 담당.
- 예외가 발생했을 때 예외의 종류에 따라 에러 페이지를 표시하거나 관리자에게 통보해주는 등의 작업은 개발 컨트롤러가 아니라 프론트 컨트롤러인 DispatcherServlet을 통해 처리되어야 한다.
- DispatcherServlet은 등록된 HandlerExceptionResolver 중에 발생한 예외에 적합한 것을 찾아서 예외처리를 위임한다.
- AnnotationMethodHandlerExceptionResolver, ResponseStatusExceptionResolver, DefaultHandlerExceptionResolver, ExceptionHandlerExceptionResolver 가 디폴트 전략으로 등록되어 있다.
ViewResolver
- 컨트롤러가 리턴한 뷰 이름을 참고해서 적절한 뷰 오브젝트를 찾아주는 로직을 가진 전략 오브젝트.
- InternalResourceViewResolver가 디폴트로 등록되어 있어, JSP나 서블릿 같이 RequestDispatcher에 의해 포워딩될 수 있는 리소스를 뷰로 사용하게 해 준다.
LocaleResolver
- 지역정보를 결정해주는 전략.
- AcceptHeaderLocaleResolver가 디폴트로 등록되어 있는데, HTTP 헤더의 정보를 보고 지역 정보를 설정해준다.
기타
그 외에도 ThemeResolver, MultipartResolver, FlashMapManager 등이 존재한다.
추가 사항
- 이러한 전략들은 DispatcherServlet의 동작 방식을 확장할 수 있도록 만들어진 확장 포인트이다.
- DispatcherServlet은 WebApplicationContext의 각 전략들을 확인하고, 별도로 설정이 없을 경우 DispatcherServlet.properties라는 파일로부터 디폴트 값을 가져와서 초기화한다.
- DispatcherServlet은 서블릿 컨테이너가 생성하고 관리하는 오브젝트이지, 스프링의 컨텍스트에서 관리하는 빈 오브젝트가 아니다. 따라서 DispatcherServlet에 직접 DI 설정을 해줄 수 있는 방법은 없다.
- 대신 DispatcherServlet은 내부에 서블릿 웹 애플리케이션 컨텍스트를 갖고 있고, 내부 컨텍스트로부터 개발자가 추가하거나 설정을 수정한 전략이 담긴 빈 오브젝트를 가져와서 디폴트 전략을 대신하여 사용한다.
참고
- 토비의 스프링 3.1 Vol.2 스프링의 기술과 선택
- Spring Docs https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#spring-web
'Application > Spring Framework' 카테고리의 다른 글
스프링 트랜잭션, Spring AOP (0) | 2021.08.29 |
---|---|
로컬에서 임베디드 S3 사용하기 (4) | 2021.07.27 |
스프링 인터셉터와 어노테이션으로 인증 및 권한 관리하기 (0) | 2021.03.08 |
[Spring] IoC 컨테이너와 DI와 빈의 스코프 (0) | 2021.02.16 |
[스프링 인 액션 정리] 11-12 리액티브 API, 데이터 퍼시스턴스 (0) | 2021.01.12 |