[Spring] 스프링 MVC 프레임워크 동작 구조
2023. 11. 17.
반응형

 
이 포스팅에서는 스프링 MVC의 동작 구조에 대해서 다룹니다.
 
https://coding-log.tistory.com/286

[Spring] 스프링 MVC 시작하기 (with IntelliJ)

이 포스팅에서는 IDE에서 스프링 MVC 프로젝트를 만드는 법을 다룹니다. 웹 어플리케이션을 개발을 위한 프로젝트는 웹을 위한 디렉토리 구조가 추가됩니다. 그동안 다뤘던 비즈니스 로직(Model, Co

coding-log.tistory.com

 
앞의 포스팅에서 우리는 스프링 MVC 시작을 위한 기본적은 코드들을 작성해 보았습니다.
특히 MvcConfig 파일을 통한 설정으로 우리는 스프링 MVC가 어떻게 컨트롤러를 실행하고 뷰를 찾는지 몰라도 구현을 할 수 있었습니다.
 
그래서 이번 포스팅에서는 스프링 MVC를 구성하는 요소와 그것들의 동작 방식에 대해서 살펴보겠습니다.
일단 동작 순서에 대해서 간결하게 알아보겠습니다.
 

  1. 웹 브라우저에서 요청 전송
  2. DispatcherServlet이 요청을 받아서 HandlerMapping이라는 빈 객체에 요청 URL과 매칭되는 컨트롤러 검색
  3. HandlerMapping이 요청 경로를 이용해 처리할 컨트롤러 빈 객체를 DispatcherServlet에 전달 후 DispatcherServlet이 HandlerAdapter 빈에게 처리 요청
  4. HandlerAdapter가 컨트롤러의 알맞은 메서드를 호출해서 요청을 실행 후 결과를 리턴 받음
  5. HandlerAdapter에서 컨트롤러의 실행 결과를 ModelAndView로 변환해서 DispatcherServlet에 리턴
  6. DispatcherServlet가 컨트롤러의 실행 결과를 보여줄 View를 검색하기 위해 ViewResolver의 빈 객체 사용
  7. DispatcherServlet이 ViewResolver가 리턴한 뷰 객체에게 응답 생성 요청
  8. 뷰 객체가 JSP를 실행해 웹 브라우저에 응답

 
동작 순서를 보면 클라이언트의 요청을 실제로 처리하는 것은 컨트롤러고 DispatcherServlet은 클라이언트의 요청을 전달받는 창구 역할을 한다는 것을 알 수 있습니다. DispatcherServlet는 그런 컨트롤러를 찾기 위해 HandlerMapping 객체를 사용합니다. 
 
 
 

반응형

 
 
 
스프링 MVC에서는 웹 요청을 실제로 처리하는 객체핸들러 객체라고 합니다. 따라서 HandlerMapping이라는 이름이 붙었다고 할 수 있습니다. DispatcherServlet은 핸들러 객체의 실제 타입에 상관없이 실행 결과를 ModelAndView 타입으로 받습니다. 이를 변환해 주는 객체가 HandlerAdapter인 것입니다. 핸들러 객체의 실제 타입마다 그에 알맞은 HandlerMapping과 HandlerAdapter가 존재하기 때문에 종류에 따라 스프링 빈으로 등록해야 합니다. (스프링이 제공하는 설정 기능을 사용하면 직접 하지 않아도 됨)
 
우리는 web.xml을 통해서 DispatcherServlet의 contextConfiguration 초기화 파라미터를 이용해 스프링 설정 클래스 목록을 전달했습니다.
 

  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
      config.MvcConfig
      config.ControllerConfig
    </param-value>
  </init-param>

 
 
config 패키지의 MvcConfig, ControllerConfig 파일이 바로 그것입니다.
DispatcherServlet은 이 파일들을 이용해 스프링 컨테이너를 생성합니다.
스프링 컨테이너에는 다음과 같은 구성 요소가 있습니다.
 

  • HandlerMapping
  • HandlerAdapter
  • 컨트롤러 빈
  • ViewResolver

이 구성 요소들의 빈은 DispatcherServlet이 컨테이너의 빈에서 구합니다.
따라서 DispatcherServlet이 사용하는 설정 파일에 이들 빈에 대한 정의가 포함되어 있어야 합니다.
 
 
 
 

반응형

 
 
 
@Controller 적용 객체는 DispatcherServlet의 입장에서 보면 한 종류의 핸들러 객체입니다. 웹 요청을 실제로 처리하는 객체이기 때문입니다. DispatcherServlet은 핸들러 객체를 찾기 위해 HandlerMapping을 사용하고 핸들러를 실행하기 위해 HandlerAdapter를 사용합니다. 이들을 사용하기 위해서는 빈이 스프링 설정에 등록되어 있어야 합니다. 우리는 이를 위해 @EnableWebMvc 어노테이션을 코드에 추가했습니다.
 

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
	...
}

 
 
이처럼 어노테이션을 통해 스프링에 빈 설정이 추가되었습니다.
 
RequestMappingHandlerMapping, RequestMappingHandlerAdapter는 @Controller 타입의 핸들러 객체를 처리하기 위한 클래스입니다.
RequestMappingHandlerMapping@Controller에 적용된 객체의 요청 맵핑 어노테이션(@GetMapping) 값을 통해 요청을 처리할 컨트롤러 빈을 찾습니다.
RequestMappingHandlerAdapter컨트롤러 메소드를 알맞게 실행하고 그 결과를 ModelAndView 객체로 변환해서 DispatcherServlet에 리턴합니다.
 
HelloController.java
 

@Controller
public class HelloController {

	@RequestMapping("/hello")
	public String hello(Model model,
			@RequestParam(value = "name", required = false) String name) {
		model.addAttribute("greeting", "안녕하세요, " + name);
		return "hello";
	}
}

 
 
RequestMappingHandlerAdapter 클래스는 "/hello" 요청 경로에 따라 hello() 메소드를 호출하고 컨트롤러 메소드 결과 값을 뷰 이름으로 갖는 ModelAndView 객체를 생성해서 DispatcherServlet에 리턴합니다.
이 경우 DispatcherServlet은 View 객체에 응답 생성을 요청할 때 greeting 키를 갖는 Map 객체를 View 객체에 전달합니다.
View 객체는 전달받은 Map 객체에 담긴 값을 request.setAttribute()를 이용해 request의 속성에 저장하고 해당 경로의 JSP를 실행합니다.
즉, 컨트롤러에서 지정한 Model 속성은 request 객체 속성으로 JSP에 전달되기 때문에 JSP는 모델에 지정한 속성 이름을 사용해서 값을 사용할 수 있게 됩니다.
 

인사말:${greeting}

 
 
 
 
 

반응형

 
 
 
 
 
MvcConfig.java
 

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {

  @Override
  public void configureViewResolvers(ViewResolverRegistry registry) {
    registry.jsp("/WEB-INF/view/", ".jsp");
  }

}

 
이 설정 클래스는 WebMvcConfigurer를 인터페이스를 구현하여 MVC 설정을 추가했습니다.
@Configuration을 붙인 클래스 또한 컨테이너에 빈으로 등록되므로 MvcConfig 클래스는 WebMvcConfigurer타입의 빈이 됩니다.
 
또한, ViewResolver 설정을 추가하기 위해 configureViewResolvers 메소드를 재정의한 것입니다.
이는 컨트롤러 처리 결과를 JSP로 이용해서 생성하기 위한 설정입니다.
ViewResolverRegistry 타입의 registry 파라미터를 가지게 되는데 이를 이용해 prefix와 suffix를 붙일 수 있습니다.
예를 들어 view의 주소가 원래 "http://localhost:8080/WEB-INF/view/hello.jsp"라면 위의 설정을 통해 "http://localhost:8080/hello"라고 사용할 수 있게 되는 것입니다.
 
 
 
 

반응형

 
 
 
 
 
다음은 디폴트 핸들러와 HandlerMapping의 우선순위에 대해서 살펴보겠습니다.
web.xml 파일에서 DispatcherServlet의 매핑 경로를 "/"로 설정했었습니다.
 

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

 
 
매핑 경로가 "/"인 경우 .jsp로 끝나는 요청을 제외한 모든 요청을 DispatcherServlet이 처리합니다. 즉 /index.html이나 /css/bootstrap.css와 같이 확장자가 .jsp가 아닌 모든 요청을 DispatcherServlet이 처리하게 됩니다.
 
그런데 @EnableWebMvc가 등록하는 HandlerMapping은 @Controller가 적용한 빈 객체가 처리할 수 있는 요청 경로만 대응할 수 있습니다.
즉, 그 컨트롤러에서 @GetMapping("/hello")만 설정한다면 "/hello"이외의 다른 경로를 처리할 수 없게 됩니다. 그리고 404 error를 내게 됩니다.
 
이를 처리하기 위해 WebMvcConfigurer의 configureDefaultServletHandling 메소드를 사용했습니다.
 

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {

  @Override
  public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
  }
  
}

 
 
이 메소드는 DefaultServletHandlerConfigurer와 SimpleUrlHandlerMapping의 빈 객체를 추가합니다.
 
DefaultServletHandlerConfigurer는 클라이언트의 모든 요청을 WAS(웹 어플리케이션 서버, tomcat, 웹로직 등)가 제공하는 디폴트 서블릿에 전달합니다.
 
 
 
 

반응형

 
 
 
 
@EnableWebMvc의 RequestMappingHandlerMapping의 적용 우선순위가 DefaultServletHandlerConfigurer의 SimpleUrlHandlerMapping 보다 우선순위가 높기 때문에 웹 브라우저의 요청이 들어오면 DispatcherServlet이 다음과 같은 방식으로 요청을 처리합니다.
 
1. RequestMappingHandlerMapping을 사용해 요청을 처리할 핸들러를 검색
-> 없으면 해당 컨트롤러를 이용해 요청 처리
 
2. 없으면 SimpleUrlHandlerMapping을 사용해 요청을 처리할 핸들러 검색
->  DefaultServletHandlerConfigurer#enable() 메소드가 등록한 SimpleUrlHandlerMapping을 이용해 모든 경로 ("/**")에 대해 DefaultServletHttpRequestHandler를 리턴
-> DispatcherServlet은 DefaultServletHttpRequestHandler에 처리를 요청
-> DefaultServletHttpRequestHandler는 디폴트 서블릿에 처리를 위임
 
예를들어 DefaultServletHandlerConfigurer에 "/index.html"에 대한 처리를 요청이 들어오면 1번으로 찾지 못하기 때문에 2번 과정을 통해 디폴트 서블릿이 "/index.html" 요청을 처리하게 됩니다.
 
 
+스프링 MVC 구성요소 요약
 
DispatcherServlet: 웹 브라우저의 요청을 받기 위한 창구, 다른 구성 요소들을 이용해 요청 흐름 제어
HandlerMapping: 클라이언트의 요청을 처리할 핸들러 객체를 찾아줌.
핸들러 객체: 클라이언트 요청을 실제로 처리한 후 뷰 정보와 모델을 설정 (ex. HelloController)
HandlerAdapter: DispatcherServlet과 핸들러 객체 사이의 변환을 알맞게 처리해 줌
ViewResolver: 요청 처리 결과를 생성할 View를 찾아줌
View: 최종적으로 클라이언트에 응답을 생성해서 전달



 출처: 초보 웹 개발자를 위한 스프링 5 프로그래밍 입문




 
 
 

반응형
myoskin