상세 컨텐츠

본문 제목

[스프링2] 10장. 스프링 MVC 프레임워크 동작 방식

22-23/22-23 Spring 2

by YUZ 유즈 2022. 12. 1. 10:00

본문

728x90

 

 

10장의 키워드

#DispatcherServlet

#HandlerMapping

#HandlerAdapter

#ViewResolver


1. 스프링 MVC 핵심 구성 요소

 

  • <<spring bean>> : 스프링 빈으로 등록
  • <<spring bean>> 컨트롤러, JSP : 개발자가 직접 구현
  • DispatcherServlet은 모든 연결을 담당

 

<순서>

  1. 웹 브라우저로부터 요청이 들어오면 DispatcherServlet이 전달 받는다. (일종의 창구 역할)
  2. 요청을 처리하기 위한 컨트롤러 객체 HandlerMapping에게 검색해달라 요청한다.
    2-1. HandlerMapping이 이를 처리할 컨트롤러 빈 객체를 DispatcherServlet에게 리턴한다. (클라이언트 요청 경로 이용)
  3. DispathcerServlet은 컨트롤러 객체를 처리할 수 있는 HandlerAdapter 빈에게 요청 처리를 위임한다.
  4. HandlerAdapter는 컨트롤러의 알맞은 메서드를 호출한다.
  5. 컨트롤러는 요청을 처리하고 그 결과를 리턴하여 HandlerAdapter가 받는다. 
  6. HandlerAdapter는 컨트롤러의 처리 결과를 ModelAndView라는 객체로 변환해서 DispatcherServlet에 리턴한다.
  7. DispatcherServlet은 ViewResolver에게 결과를 보여줄 뷰를 요청한다.
    7-1. ViewResolver는 ModelAndView는 컨트롤러가 리턴한 뷰 이름을 이용해 이 뷰 이름에 해당하는 View 객체를 찾거나 생성해서 DispatcherServlet에게 리턴한다.
  8. DispatcherServlet은 ViewResolver가 리턴한 View 객체에게 응답 결과 생성을 요청한다.
  9. JSP를 사용하는 경우, View 객체는 JSP를 실행함으로써 웹 브라우저에 전송할 응답 결과를 생성한다.

 

 

 

 

1.1 컨트롤러와 핸들러

 

Controller를 찾아주는 객체는 왜 HandlerMapping일까?

 

클라이언트 요청 처리 방법이 @Controller 애노테이션을 붙인 클래스를 이용하는 것과, 자신이 직접 만든 클래스를 이용하는 것 두 가지가 있기 때문이다.

즉, DispatcherServlet은 클라이언트 요청을 처리하는 객체의 타입이 반드시 @Controller를 적용할 클래스일 필요가 없기 때문에 MVC는 웹 요청을 실제로 처리하는 객체를 핸들러(Handler)라고 표현한다.

@Controller 적용 객체나 Controller 인터페이스를 구현한 객체는 모두 스프링 MVC 입장에서 핸들러가 된다.

 

핸들러 객체의 실제 타입마다 그에 알맞은 HandlerMappging과 HandlerAdapter가 존재하기 때문에, 사용할 핸들러의 종류에 따라 해당 HandlerMapping과 HandlerAdapter를 스프링 빈으로 등록해야 한다.

 

 

 

 

2. DispatcherServlet과 스프링 컨테이너

9장의 web.xml 파일을 보면 DispatcherServlet의 contextConfiguration 초기화 파라미터를 이용해 스프링 설정 클래스 목록을 전달했다.

<servlet>
    		<!--DispatcherServlet을 dispatcher라는 이름으로 등록-->
		<servlet-name>dispatcher</servlet-name> 
		<servlet-class>
			org.springframework.web.servlet.DispatcherServlet
		</servlet-class>
        
        	<!--contextClass 초기화 파라미터 설정.-->
		<init-param>
			<param-name>contextClass</param-name>
			<param-value>
				org.springframework.web.context.support.AnnotationConfigWebApplicationContext
			</param-value>
		</init-param>
        
        	<!--contextConfiguration 초기화 파라미터 값 지정. 파라미터로는 스프링 설정 클래스 목록을 지정-->
		<init-param> 
			<param-name>contextConfigLocation</param-name>
			<param-value>
				config.MvcConfig
				config.ControllerConfig
			</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

DispatcherSevlet은 전달받은 설정 파일을 이용해서 스프링 컨테이너를 생성한다.

HandlerMapping, HandlerAdapter, 컨트롤러, ViewResovler 등의 빈은 이 스프링 컨테이너에서 구하기 때문에,

DispatcherServlet이 사용하는 설정 파일에 빈에 대한 정의가 포함되어야한다.

 

 

 

  

3. @Controller를 위한 HandlerMapping과 HandlerAdapter

DispatcherServlet은 웹 브라우저의 요청을 처리할 핸들러 객체를 찾기 위해 HandlerMapping을 사용하고,

찾은 핸들러를 실행하기 위해 HandlerAdapter를 사용하므로 두 빈이 스프링 설정에 등록되어 있어야 한다.

그러나 9장에서 작성한 예제(스프링 설정 파일)에서는 @EnableWebMvc 애노테이션만 추가했다.

 

 

@EnableWebMvc의 역할?

 

@EnableWebMvc는 매우 다양한 스프링 빈 설정을 추가해준다.

이 태그가 빈으로 추가해주는 클래스 중에는 @Controller 타입의 핸들러 객체를 처리하기 위한 다음의 두 클래스가 포함되어 있다.

  • org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
  • org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

 

RequestMappingHandlerMapping은 @Controller가 적용된 객체의 요청 매핑 애노테이션(@GetMapping) 값을 이용해서 웹 브라우저의 요청을 처리할 컨트롤러 빈을 찾는다.

 

RequestMappingHandlerAdpater 컨트롤러의 메서드를 알맞게 실행하고

그 결과를 ModelAndView 객체로 변환해서 DispatcherServlet에 리턴한다.

 

// 9장 HelloController 클래스
@Controller // 스프링 MVC에서 컨트롤러로 사용
public class HelloController {

	@RequestMapping("/hello") // "/hello" 요청 경로에 대해 hello() 메서드를 호출
    	// Model 파라미터는 컨트롤러의 처리 결과를 뷰에 전달할 때 사용
	public String hello(Model model,   
            @RequestParam(value = "name", required = false) String name) {  // HTTP 요청 파라미터의 값을 메서드의 두번째 파라미터로 전달
		model.addAttribute("greeting", "안녕하세요, " + name); 
		return "hello"; // 컨트롤러의 처리 결과를 보여줄 뷰 이름으로 "hello" 사용
	}
}

 

 

컨트롤러 메서드 결과 값이 String이면 해당 값을 뷰 이름으로 갖는 ModelAndView 객체를 생성해서 DispatcherServlet에 리턴하며 Model 객체에 보관된 값도 ModelAndView에 함께 전달한다.

 

 

 

 

4. WebMvcConfigurer 인터페이스와 설정

@EnableWebMvc을 사용하면 WebMvcConfigurer 타입의 빈을 이용해 MVC 설정을 추가로 생성한다.

// 스프링 설정 파일
@Configuration // 컨테이너에 빈으로 등록된다.
@EnableWebMvc // 스프링 MVC 설정을 활성화하는 애노테이션. 스프링 MVC를 사용하는데 필요한 다양한 설정을 생성
public class MvcConfig implements WebMvcConfigurer {  // MvcConfig 클래스는 WebMvcConfigurer 타입의 빈이 된다.

	// DispatcherServlet의 매핑 경로를 '/'로 주었을때, JSP/HTML/CSS 등을 올바르게 처리하기 위한 설정 추가
	@Override
	public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
		configurer.enable();
	}
	
    	// JSP를 이용해 컨트롤러의 실행 결과를 보여주기 위한 설정 추가
	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		registry.jsp("/WEB-INF/view/", ".jsp");
	}

}

@EnableWebMvc 애노테이션을 사용하면 WebMvcConfigurer 타입인 빈 객체의 메서드를 호출해서 MVC 설정을 추가한다.

ex. configureViewResolvers() 메서드 : ViewResolver설정을 추가한다.

즉, 각각의 메서드가 MVC 설정을 추가한다!!!

 

 

 

 

 

5. JSP를 위한 ViewResolver

컨트롤러 처리 결과를 JSP를 이용해서 생성하기 위해 다음 설정을 사용한다.

// 스프링 설정 파일
@Configuration // 컨테이너에 빈으로 등록된다.
@EnableWebMvc // 스프링 MVC 설정을 활성화하는 애노테이션. 스프링 MVC를 사용하는데 필요한 다양한 설정을 생성
public class MvcConfig implements WebMvcConfigurer {  // MvcConfig 클래스는 WebMvcConfigurer 타입의 빈이 된다.

    	// JSP를 이용해 컨트롤러의 실행 결과를 보여주기 위한 설정 추가
	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		registry.jsp("/WEB-INF/view/", ".jsp");
	}

}

ViewResolverRegistry의 jsp()메서드를 사용하면 JSP를 위한 ViewResolver를 설정할 수 있다.

 

위 설정은 org.springframework.web.servlet.view.InternalResourceViewResolver 클래스를 이용해서 다음 설정과 같은 빈을 등록한다.

@Bean
public ViewResolver viewResolver() {
	InternalResourceViewResover vr = new InternalResourceViewResolver();
    vr.setPrefix("/WEB-INF/view/");
    vr.setSuffix(".jsp");
    return vr;
}

컨트롤러의 실행 결과(by HandlerAdapter)를 받은 DispatcherServlet은 ViewResolver에게 뷰 이름에 해당하는 View 객체를 요청한다.

이때  InterenalResourceViewResolver가 "prefix+뷰이름+suffix"에 해당하는 경로를 뷰 코드로 사용하는 InternalResourceView 타입의 View 객체 리턴한다.

ex) 뷰 이름 : "hello",   경로 : "/WEB-INF/view/hello.jsp",   
리턴 :  "/WEB-INF/view/hello.jsp"를 뷰 코드로 사용하는 InternalResourceView 객체

 

 

 

DispatcherServlet은 컨트롤러의 실행 결과를 ModelAndView 형태로 전달받는다. 이때 Model에 담긴 값도 같이 전달되는데, View 객체에 Map 형식으로 전달된다.

	@RequestMapping("/hello") // "/hello" 요청 경로에 대해 hello() 메서드를 호출
    	// Model 파라미터는 컨트롤러의 처리 결과를 뷰에 전달할 때 사용
	public String hello(Model model,   
            @RequestParam(value = "name", required = false) String name) {  // HTTP 요청 파라미터의 값을 메서드의 두번째 파라미터로 전달
		model.addAttribute("greeting", "안녕하세요, " + name);  // Model에 속성을 전달 	
}

이 경우 DispatcherServlet은 View 객체에 응답 요청할 때 greeting 키를 갖는 Map 객체를 View 객체에 전달한다.

View 객체는 전달받은 Map 객체에 담긴 값을 이용해서 알맞은 응답 결과를 출력한다.

 

컨트롤러에서 지정한 Model 속성은 request 객체 속성으로 JSP에 전달되기 때문에 JSP는 모델에 지정한 속성 이름을 사용해서 값을 사용할 수 있게 된다.  ex) {greeting}

 

 

 

 

 

 

6. 디폴트 핸들러와 HandlerMapping의 우선순위

<!--모든 요청을 DispatcherServlet이 처리하도록 서블릿 매핑 설정-->
<servlet-mapping> 
	<servlet-name>dispatcher</servlet-name>
	<url-pattern>/</url-pattern>
</servlet-mapping>

9장의 web.xml 설정을 보면 DispatcherServlet에 대한 매핑 경로를 다음과 같이 '/'로 주었다. (url-pattern 태그)

이 경우 .jsp로 끝나는 요청을 제외한 모든 요청을 DispatcherServlet이 처리하게 된다. (ex./index.html, /css/bootstrap.css)

 

그런데 @EnableWebMvc 애노테이션이 등록하는 HandlerMapping은 @Controller 애노테이션을 적용한 빈 객체가 처리할 수 있는 요청 경로만 대응할 수 있다.  ☞ 다른 경로를 처리할 수 있는 컨트롤러 객체를 찾을 수 없다.

 

 

따라서 WebMvcConfigurer의 configureDefaultServletHandling()메서드를 사용한다.

// 스프링 설정 파일
@Configuration // 컨테이너에 빈으로 등록된다.
@EnableWebMvc // 스프링 MVC 설정을 활성화하는 애노테이션. 스프링 MVC를 사용하는데 필요한 다양한 설정을 생성
public class MvcConfig implements WebMvcConfigurer {  // MvcConfig 클래스는 WebMvcConfigurer 타입의 빈이 된다.

	// DispatcherServlet의 매핑 경로를 '/'로 주었을때, JSP/HTML/CSS 등을 올바르게 처리하기 위한 설정 추가
	@Override
	public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
		configurer.enable();
	}
}

DefaultServletHandlerConfigurer의 enable()메서드는 다음의 두 빈 객체를 추가한다.

  • DefaultServletHttpRequestHandler
  • SimpleUrlHandlerMapping

DefaultServletHttpRequestHandler는 클라이언트의 모든 요청을 WAS(웹 어플리케이션 서버)가 제공하는 디폴트 서블릿에 전달하여 처리하도록 한다. 

 

** WAS 참고

https://codechasseur.tistory.com/25

 

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

 

 

적용 우선 순위 :

@EnableWebMvc가 등록하는 RequestMappingHandlerMapping > DefaultServletHandlerConfigurer의 enable() 메서드가 등록하는 SimpleUrlHandlerMapping

 

<DispatcherServlet에 요청을 처리하는 방법>

  1. RequestMappingHandlerMapping으로 요청을 처리할 핸들러를 검색한다.
    1-1. 존재하면 해당 컨트롤러를 이용해서 요청을 처리한다.
  2. 존재하지 않으면 SimpleUrlHandlerMapping으로 요청을 처리할 핸들러를 검색한다.
    2-1. 모든 경로("/**")에 대해 DefaultServletHttpRequestHandler를 리턴한다.
    2-2. DispatcherServlet은 DefaultServletHttpRequestHandler에 처리를 요청한다.
    2-3. DefaultServletHttpRequestHandler은 디폴트 서블릿에 처리를 위임한다.

 

DefaultServletHandlerConfigurer의 enable()메서드 외에 몇몇 설정도 SimpleUrlHandlerMapping을 등록하는데 enable()이 등록하는 SimpleUrlHandlerMapping의 우선순위가 가장 낮다.

따라서 DefaultServletHandlerConfigurer의 enable()을 설정하면 별도 설정이 없는 모든 요청 경로를 디폴트 서블릿이 처리하게 된다.

 

 

 


문제 1.

다음은 스프링 MVC 프레임워크의 동작 방식이다. 빈칸을 순서대로 채우시오.

답: 1. DispatcherServlet, 2. HandlerMapping, 3. HandlerAdapter, 4. 컨트롤러, 5.ViewResolver, 6. View, 7. JSP

 

문제 2. 

HandlerAdapter는 컨트롤러의 처리 결과를 (ModelAndView)라는 객체로 변환해서 DispatcherServlet에 리턴한다.

 

문제 3.

DispatcherSevlet은 전달받은 설정 파일을 이용해서 (스프링 컨테이너)를 생성한다.

HandlerMapping, HandlerAdapter, 컨트롤러, ViewResovler 등의 빈은 이 (스프링 컨테이너)에서 구하기 때문에,

DispatcherServlet이 사용하는 설정 파일에 (빈에 대한 정의)가 포함되어야한다.

 

문제 4.

(@EnableWebMvc)는 매우 다양한 스프링 빈 설정을 추가해준다.

이 태그가 빈으로 추가해주는 클래스 중에는 @Controller 타입의 핸들러 객체를 처리하기 위한 다음의 두 클래스가 포함되어 있다.

  • org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
  • org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

문제 5.

컨트롤러의 실행 결과를 받은 DispatcherServlet은 ViewResolver에게 뷰 이름에 해당하는 View 객체를 요청하는데,

이때  InterenalResourceViewResolver가 ("prefix+뷰이름+suffix")에 해당하는 경로를 뷰 코드로 사용하는 (InternalResourceView) 타입의 View 객체 리턴한다.

 

문제 6.

매핑 경로가 '/'인 경우, 웹 브라우저의 요청이 들어왔을 때 DispatcherServlet은 먼저 SimpleUrlHandlerMapping을 사용해서 요청을 처리할 핸들러를 검색한다. (O/X)

답: X

 

 

 

코드 문제

문제 1. 

@EnableWebMvc을 사용하면 ★타입의 빈을 이용해 MVC 설정을 추가로 생성한다.

// 스프링 설정 파일
@Configuration // 컨테이너에 빈으로 등록된다.
@EnableWebMvc // 스프링 MVC 설정을 활성화하는 애노테이션. 스프링 MVC를 사용하는데 필요한 다양한 설정을 생성
public class MvcConfig implements ★ {  // MvcConfig 클래스는 ★ 타입의 빈이 된다.

	// DispatcherServlet의 매핑 경로를 '/'로 주었을때, JSP/HTML/CSS 등을 올바르게 처리하기 위한 설정 추가
	@Override
	public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
		configurer.enable();
	}
	
    	// JSP를 이용해 컨트롤러의 실행 결과를 보여주기 위한 설정 추가
	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		registry.jsp("/WEB-INF/view/", ".jsp");
	}

}

★에 들어갈 것은?

답 : WebMvcConfigurer

 

 

 

문제 2.

ViewResolverRegistry의 jsp()메서드를 사용하면 JSP를 위한 ViewResolver를 설정할 수 있다.

위 설정은 org.springframework.web.servlet.view.♥ 클래스를 이용해서 다음 설정과 같은 빈을 등록한다.

@Bean
public ViewResolver viewResolver() {
	♥ vr = new ♥();
    vr.setPrefix("/WEB-INF/view/");
    vr.setSuffix(".jsp");
    return vr;
}

♥에 들어갈 것은?

답 : InternalResourceViewResover

728x90

관련글 더보기