본문 바로가기

Programming/Spring 2

Spring MVC 프레임워크 동작 방식

참고도서

초보 웹 개발자를 위한 스프링4 프로그래밍 입문
국내도서
저자 : 최범균
출판 : 가메출판사 2015.03.02
상세보기



1. Spring MVC 핵심 구성 요소


① DispatcherSevlet 은 모든 연결을 담당하며, 웹 브라우저에서 요청이 들어오면 

② 그 요청을 처리하기 위해 HandlerMapping 객체에게 컨트롤러 검색을 요청한다.

    HandlerMapping은 클라이언트의 요청 경로를 이용해서 이를 처리할 컨트롤러 객체를 찾아서 DispatcherServlet 에 리턴한다.  

③  DispatcherServlet 은 @Controller 어노테이션을 이용해서 구현한 컨트롤러, 스프링 2.5까지 사용됐던 Controller 인터페이스를 구현한 

    컨트롤러, 특수 목적으로 사용되는 HttpRequestHandler 인터페이스를 구현한 클래스를 동일한 방식으로 실행하고 처리하기 위해

    HandlerAdapter 객체에게 요청 처리를 위임한다.

④ HandlerAdapter 객체는 컨트롤러의 알맞은 메소드를 호출해서 요청을 처리하고,

⑤ 반환 받은 결과를 

⑥ ModelAndView 객체에 담아서 DispatcherServlet 에 리턴한다.

⑦ ModelAndView 객체를 반환 받은 DispatcherServlet 은 ViewResolver 객체를 이용해서 결과를 보여줄 뷰를 검색한다.

    ViewResolver 객체는 ModelAndView 객체에 담긴 뷰 이름을 이용해서 View 객체를 찾거나 생성해서 리턴한다.

    ViewResolver 는 매번 새로운 View 객체를 생성해서 DispatcherServlet에 리턴한다.

⑧ DispatcherServlet 은 ViewResolver 가 리턴한 View 객체에게 응답 결과 생성을 요청한다.

⑨ JSP를 사용하는 경우, View 객체는 JSP를 실행함으로서 브라우저에게 전송할 응답 결과를 생성한다.

    ModelAndView 의 Model 객체에 담겨 있는 데이터가 응답 결과에 필요하면 Model 에서 데이터를 꺼내 JSP 에서 사용할 수 있다.



클라이언트의 요청을 실제로 처리하는 것은 컨트롤러이고, 

DispatcherServlet 은 클라이언트의 요청을 전달 받는 창구 역할을 한다.

DispatcherServlet 에게 어떤 컨트롤러가 요청을 처리하는가는 중요하지 않으며, 

처리 결과를 ModelAndView 타입의 객체로 전달 받을 수 있기만 하면 된다.

이 때, 사용할 컨트롤러를 찾고, 처리 결과를 ModelAndView 객체로 변환해 주는 객체가 HandlerAdapter 이다.


핸들러 객체의 실제 타입마다 그에 알맞은 HandlerMapping 과 HandlerAdapter 가 존재하기 때문에,

사용할 핸들러의 종류에 따라 해당 HandlerMapping 과 HandlerAdapter 를 스프링 빈으로 등록해 주어야 하는데,

스프링 설정 기능(<mvc:annotaion-driven>)을 사용하면 직접 등록하지 않아도 스프링이 알아서 처리해 준다.


요청을 처리할 컨트롤러를 찾기 때문에 ControllerMapping 이라는 이름이 어울리는데,

스프링 MVC 는 웹 요청을 처리할 수 있는 범용적인 프레임워크를 제공하고 있기 때문에,

클라이언트의 요청을 처리하는 객체가 컨트롤러가 아닐 수도 있다(HttpRequestHandler 등).

그래서 스프링 MVC 는 웹 요청을 실제로 처리하는 객체를 Handler 라고 표현하고 있다.

따라서 컨트롤러는 DispatcherServlet 입장에서 보면 한 종류의 핸들러 객체이다.


2. DispatcherServlet 과 Spring Container

스프링의 DispatcherServlet 은 설정 파일을 이용해서 스프링 컨테이너를 생성하는데,
기본적으로 WEB-INF 폴더 안의 WEB-INF/[서블릿 이름]-servlet.xml 파일을 설정 파일로 이용한다.

1
2
3
4
5
6
7
8
<servlet>
    <!-- WEB-INF/fornt-servlet.xml 을 설정 파일로 이용해서 스프링 컨테이너를 생성 -->
    <servlet-name>front</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
cs



설정 파일을 직접 지정하고 싶으면 contextConfiguration 초기화 파라미터를 설정하면 된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<servlet>    
    <servlet-name>front</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
 
    <!-- contextConfiguration 초기화 설정 -->
    <init-param>
        <!-- 파라미터 이름으로 contextConfigLocation 사용-->
        <param-name>contextConfigLocation</param-name>
        <!-- 파라미터 값으로 사용할 설정파일을 지정 -->
        <param-value>
            classpath:spring-mvc.xml
            classpath:spring-controller.xml
    <load-on-startup>1</load-on-startup>
</servlet>
cs



HandlerMapping, HandlerAdapter, 컨트롤러, ViewResolver 등의 객체는 

DisptacherServlet 이 생성하는 스프링 컨테이너로 부터 구하기 때문에,

DistacherServlet 이 사용하는 설정 파일에 이들 객체를 빈 으로 설정해야 한다.


DispatcherServlet => 설정 파일 로딩 => 스프링 컨테이너 생성 

=> 스프링 컨테이너 안에 빈으로 설정된 HandlerMapping, HandlerAdapter, 컨트롤러, ViewResolver 객체 이용

그리고 이렇게 생성되는 스프링 컨테이너는 WebApplicationContext 컨테이너이다.


3. @Controller 를 위한 HandlerMapping 과 HandlerAdapter

DispatcherServlet 은 웹 브라우저의 요청을 처리할 핸들러 객체를 찾기 위해 HandlerMapping 을 사용하고,
핸들러를 실행하기 위해 HandlerAdapter 를 사용한다.
그렇기 때문에 스프링 컨테이너 설정 파일에는 두 객체를 빈으로 등록해야 한다.
하지만 스프링 설정 기능인 <mvc:annotaion-driven> 을 사용하면 스프링이 두 객체를 포함하여 
스프링 MVC 에서 사용할 여러 가지 클래스를 빈으로 등록해 준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping {
}

@Controller 타입의 핸들러 객체 처리를 위한 HandlerMapping 클래스인 
RequestMappingHandlerMapping 클래스는
@Controller 어노테이션이 적용된 객체의 @RequestMapping 값을 이용해서
웹 브라우저의 요청을 처리할 컨트롤러 빈을 찾는다

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware,    InitializingBean {
}

RequestMappingHandlerAdapter 클래스는 웹 브라우저로부터 요청된 경로와
@RequestMapping 에 설정된 경로가 일치하는 메소드를 호출한다.
이 때 Model 객체를 생성해서 메소드의 첫 번째 파라미터로 전달한다.
메소드의 리턴 값이 String 타입인 경우, 해당 값을 뷰 이름으로 갖는
ModelAndView 객체를 생성해서 Model 에 담긴 데이터와 View 정보를
DispatcherServlet 에 리턴한다.
*/
 

// RequestMappingHandlerMapping 가 @Controller 가 붙은 객체의
// @RequestMapping 값을 이용해서 웹 브라우저의 요청을 처리할 컨트롤러 빈을 찾아낸다
@Controller
public class HelloController {
 
    // 웹 브라우저로부터의 요청이 /hello 이면 
    //RequestMappingHandlerAdapter 가 hello() 메소드를 호출, 첫 번째 파라미터로 Model 객체 전달
    @RequestMapping("/hello")
    public String hello(Model model, @RequestParam(value="name", required=falseString name){
        // Model 객체에 greeting 이라는 속성 이름으로 속성 값 "안녕하세요, "+name 을 담는다  
        model.addAttribute("greeting""안녕하세요, "+name);
        // View 정보를 View 객체에 담고, 
        // Model 에 담긴 데이터와 View 정보를 ModelAndView 객체에 담아서  
        return "hello";
    }
}
cs


 

4. JSP 를 위한 ViewResolver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<mvc:view-resolver>
    <mvc:jsp prefix="/WEB-INF/view/" />
</mvc:view-resolver>
 
<!-- 
<mvc:jsp> 태그는 
public class InternalResourceViewResolver extends UrlBasedViewResolver {}
클래스를 빈 으로 등록한다
 
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/view/" />
    <property name="suffix" value=".jsp" />
</bean>
 
<mvc:resolver> 태그는 스프링 4.1 부터 지원하기 때문에, 하위 버전을 사용하는 경우는
직접 InternalResolverViewResolver 클래스를 빈 등록 해야 한다.
-->
 
 
<!--
컨트롤러의 실행 결과를 받은 DispatcherServlet 은 ViewResolver 에게
뷰 이름에 해당하는 View 객체를 요청한다.
이 때 InternalResourceViewResolver 빈 객체에 의해 
prefix + 뷰 이름 + suffix 에 해당하는 경로를 뷰 코드로 사용하는
InternalResourceView 타입의 View 객체를 리턴한다.
 
DispatcherServlet 이 InternalResourceView 객체에 응답 생성을 요청하면,
InternalResourceView 객체는 경로에 지정한 JSP 코드를 실행해서 응답 결과를 생성한다.
 
DispatcherServlet 이 View 객체에 응답을 요청할 때, 
Model 에 담긴 값을 View 객체에 Map 형식으로 전달한다.
 
model.addAttribute("greeting""안녕하세요, "+name); 의 경우
키 => greeting  값 => "안녕하세요, "+name
 
View 객체는 전달 받은 Map 객체에 담긴 값을 이용해서  알맞은 응답 결과를 출력하도록 구현한다.
InternalResourceView 의 경우, Map 객체에 담겨 있는 키 값을 
request.setAttribute() 를 이용해서 request 의 속성에 저장한다.
그리고 해당 경로의 JSP 를 실행시킨다.
 
Model 속성이 request 객체의 속성을 통해서 전달되기 때문에
JSP 에서는 ${greeting} 을 이용해서 모델에 담긴 값을 사용할 수 있다.
-->
cs


5. Default Handler 와  HandlerMapping 의 우선 순위

1
2
3
4
5
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <!-- 서블릿 매핑 경로가 '/' -->
    <url-pattern>/<url-pattern>
</servlet-mapping>
cs


서블릿 매핑 경로가 '/' 인 경우 .jsp 로 끝나는 요청을 제외한 모든 요청을 DispatcherServlet 이 처리한다.

즉, /index.html 이나 /css/bootstrap.css 와 같은 요청을 DispatcherServlet 이 처리하는 것이다.


그런데, <mvc:annotaiton-driven> 태그를 통해서 등록되는 HandlerMapping은 @Controller 어노테이션이 적용된

빈 객체가 처리할 수 있는 요청 경로만 대응할 수 있다.


@RequestMapping("/hello") => /hello 경로만 처리할 수 있음


따라서, /index.html 이나 /css/bootstrap.css 같은 요청을 처리할 수 있는 컨트롤러 객체를 찾지 못해

DispatcherServlet 은 404 응답을 전송한다.

이런 요청을 처리하기 위해서는 <mvc:default-servlet-handler /> 를 등록하면 된다.


1
2
3
4
5
6
7
8
<mvc:annotation-driven />
 
<mvc:default-servlet-handler />
<!-- 
<mvc:default-servlet-handler /> 가 생성하는 빈 객체
DefaultServletHttpRequestHandler
SimpleUrlHandlerMapping
-->
cs


DefaultServletHttpRequestHandler 는 클라이언트의 모든 요청을 WAS 가 제공하는 기본 서블릿에 전달한다.

/index.html 대한 처리를 DefaultServletHttpRequestHandler 에 요청하면,

이 요청을 다시 기본 서블릿에 전달해서 처리하게 한다.

그리고 SimpleUrlHandlerMapping 을 이용해서 모든 경로("/**")를 DefaultServletHttpRequestHandler 를 이용해서 처리하게 설정한다.


<mvc:annotation-driven> 태그가 등록하는 RequestMappingHandlerMapping 의 적용 우선 순위가

<mvc:default-servlet-handler> 태그가 등록하는 SimpleUrlHandlerMapping 의 우선 순위보다 높기 때문에,

웹 브라우저의 요청이 들어오면 DispatcherServlet 은


① RequestMappingHandlerMapping 을 사용해서 요청을 처리할 핸들러를 검색한다.

a. 존재하면 해당 컨트롤러를 이용해서 요청을 처리한다.

② 존재하지 않으면 SimpleUrlHandlerMapping 을 사용해서 요청을 처리할 핸들러를 검색한다.

a. <mvc:default-servlet-handler> 가 등록한 SimpleUrlHandlerMapping 은 모든 경로("/**")에 대해 

   DefaultServletHttpRequestHandler를 리턴한다.

b. DispatcherServlet 은 DefaultServletHttpRequestHandler 에 처리를 요청한다.

c. DefaultServletHttpRequestHandler 는 디폴트 서블릿에 처리를 위임한다.


의 방식으로 요청을 처리한다.


그리고, <mvc:default-servlet-handler> 태그가 등록하는 SimpleUrlHandlerMapping 의 우선 순위가 가장 낮기 때문에,

별도로 설정하지 않은 모든 요청 경로를 디폴트 서블릿이 처리하게 된다.

'Programming > Spring 2' 카테고리의 다른 글

Spring MVC (커맨드 객체, 모델)  (0) 2017.08.25
Spring MVC (요청 매핑, 리다이렉트)  (0) 2017.08.24
Spring MVC 프레임워크 동작 방식  (0) 2017.08.23
Spring MVC 기본 설정  (0) 2017.08.23
Spring JdbcTemplate Transaction  (0) 2017.08.22
Spring JdbcTemplate Method  (0) 2017.08.22