먼저 이 글은 Spring Web MVC 에 해당되며, Spring WebFlux 는 다른 글을 참고하시길 바랍니다.
HTTP Request 를 처리하는 과정 - DispatcherServlet 원리
Spring Web MVC 에서 HTTP Request 를 처리하는 핵심 클래스가 DispatcherServlet 이란 클래스입니다. DispatcherServlet 가 무엇인지 알아야 Spring Web MVC 서버가 어떻게 동작하는지를 알 수 있습니다. 클래스 이름 중 Servlet 단어가 클래스가 어떤 역할을 하는지 설명하는 핵심적인 단어입니다. 왜냐하면 Spring Web MVC 는 Servlet API (javax.servlet) 를 기반으로 만들어진 프레임워크이기 때문입니다.
Servlet
일반적으로 Servlet 이라 하면 네트워크 요청을 처리하기 위한 클래스를 의미합니다. 따라서 클래스에 Servlet 이라는 이름이 붙어 있으면, 네트워크 요청을 처리하기 위한 클래스 라고 할 수 있습니다. 요즘 웹 서버가 대부분이라, Servlet 이라 하면 HTTP 요청을 처리하는 클래스로 생각하셔도 무방합니다. Servlet 은 Servlet Container 에 의해 관리되며, Servlet Container 로는 Tomcat 이 대표적입니다.
DispatcherServlet
나머지 단어인 Dispatcher 는 단어 뜻 그대로 분류하여 전달한다는 의미입니다. 즉 DispatcherServlet 은 네트워크 요청을 받아 분배하는 역할을 하는 클래스입니다. Front Controller 디자인 패턴에 따라 Spring Web MVC 에서 DispatcherServlet 는 서버로 오는 모든 네트워크 요청을 받아서 분류하는 역할을 맡고 있습니다. DispatcherServlet 클래스가 네트워크 요청을 어떻게 처리할지는 당연히 개발자가 지정해줍니다.
HandlerMapping 원리
HandlerMapping은 일반적으로 HandlerMapping 을 상속하는 RequestMappingHandlerMapping 을 통해 설정합니다. 개발자가 아래와 같이 Controller 클래스 위에 @RequestMapping 을 표시하면 Spring 이@RequestMapping 을 확인하고 RequestMappingHandlerMapping 을 만들어 DispatcherServlet 에 등록합니다.
@RestController
@RequestMapping("/persons")
class PersonController {
@GetMapping("/{id}")
public Person getPerson(@PathVariable Long id) {
// ...
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public void add(@RequestBody Person person) {
// ...
}
}
Controller 등록
@Controller
class MyController {
}
일반적으로 @Controller 를 표시하면 알아서 등록되지만 아래와 같이 등록할 수도 있습니다.
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
}
}
<mvc:view-controller path="/" view-name="home"/>
ViewResolver 역할
Spring Web MVC 에 여러 ViewResolver 가 기본으로 등록되어 있습니다. 특히 UrlBasedViewResolver가 적용되어 있어, Controller 메서드에서 반환할 때 redirect: 또는 forward: 를 포함할 수 있습니다. 그리고 Header, Cookie, Session, Locale 정보에 따라 알맞은 View 를 찾는 기능도 포함되어 있기 때문에 Client 상태에 따라 같으 정보로 다른 View 를 반환할 수도 있습니다.
새로운 ViewResolver 는 아래와 같이 등록하면 됩니다.
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
// ViewResolver 등록
}
}
<mvc:view-resolvers>
<!-- ViewResolver 등록 -->
</mvc:view-resolvers>
DispatcherServlet 를 자세하게 설정하는 방법
DispatcherServlet 생성에 직접적으로 개입하는 방법은 아래 2 가지 방법이 있습니다. 하지만 기본적으로 적용되는 설정만 사용해도 충분하므로 아래 방법을 굳이 사용할 필요는 없습니다.
첫번째 방법은 WebApplicationInitializer 를 구현하는 방법입니다.
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) {
// WebApplicationContext 생성하고 설정
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(AppConfig.class);
// DispatcherServlet 를 생성하고 등록
DispatcherServlet servlet = new DispatcherServlet(context);
ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/app/*");
}
}
두번째 방법은 web.xml 을 활용하는 방법입니다.
<web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
DispatcherServlet 구성에 개입하는 특별한 Bean 목록
DispatcherServlet 설정에 영향을 미치는 특별한 Bean 도 있습니다. 아래 클래스를 사용하게 되면 DispatcherServlet 가 네트워크 요청을 처리하는 과정에 변화를 줄 수 있습니다. 자세한 사항은 공식 문서를 참고해주세요.
'Develop > Backend 가이드' 카테고리의 다른 글
[Spring] RDB 에서 계층적인 데이터 구조 관리 전략 - Nested set (0) | 2020.09.28 |
---|---|
[Spring] RDB 에서 계층적인 데이터 구조 관리 전략 - Path enumeration (0) | 2020.09.28 |
[Spring] RDB 에서 계층적인 데이터 구조 관리 전략 - Closure table (0) | 2020.09.28 |
[Spring] RDB 에서 계층적인 데이터 구조 관리 전략 - Adjacency list (0) | 2020.09.28 |
[Spring] JPA Mapping - @ManyToMany (0) | 2020.05.23 |
꾸준히 노력하는 개발자 "김예건" 입니다.