I. Servelet 소개
1. Servlet 이란?
- 자바 엔터프라이즈 에디션에서 웹 애플리케이션 개발을 위한 스펙과 API를 제공하는데, 그 중 가장 중요한 것이 HttpServlet이다.
- 요청마다 프로세스를 생성하는 것이 아니라 한 프로세스 내에 자원을 공유하는 쓰레드로 요청을 처리함.
> 이식성이 좋다.
2. Servlet 엔진 또는 서블릿 컨테이너(톰캣, 제티 등)
- 세션관리
- 네트워크 서비스
- MIME 기반 메세지 인코딩, 디코등
- 서블릿 생명주기 관리
3. Servlet의 생명주기
- init: 서블릿 컨테이너가 서블릿 인스턴스를 초기화
- service: 서블릿 초기화 이후에는 모든 요청이 쓰레드 단위로 처리되고, 인스턴스의 service()메소드가 호출된다.
:이 안에서 HTTP 요청을 받고 클라이언트로 보낼 HTTP 응답을 만든다.
: service()는 보통 HTTP Method에 따라 doGet(), doPost() 등으로 처리를 위임한다.
: 따라서 보통 doGet()이나 doPost()를 구현한다.
- destroy : 서블릿 컨테이너 판단에 따라 서블릿을 메모리에서 내려야 할 때 destroy를 호출한다.
4. Servlet 리스너와 필터
- 리스너 : 웹 애플리케이션에서 발생하는 이벤트 리스너
* 이벤트 종류
- 서블릿 컨텍스트 수준의 이벤트
- 컨텍스트 라이프사이클 이벤트
- 컨텍스트 애트리뷰트 변경 이벤트
- 세션 수준의 이벤트
- 세션 라이프사이클 이벤트
- 세션 애트리뷰트 변경 이벤트
- 필터 : 서블릿 컨테이너와 클라이언트간의 요청-응답 사이에 처리 로직을 넣을 수 있는 기능. 체인 형태의 구조이다.
5. DispatcherServlet
- 서블릿 애플리케이션에 스프링 연동하는 방법은 두가지가 있는데,
- 서블릿에서 스프링이 제공하는 IoC 컨테이너 활용하는 방법
- 스프링이 제공하는 서블릿 구현체 DispatcherServlet 사용하는 방법이 있다.
DispatcherServlet은 FrontController의 역할을 한다.
Dispatcher servlet의 동작원리를 코드를 통해 알아보자.
컨트롤러에서 리턴값을 줄 때 뷰 모델을 리턴해줄 수 있고, 화면에 렌더링할 데이터를 리턴해 줄 수 도 있다.
// HelloController.class
@Controller
public class HelloController {
@Autowired
HelloService helloService;
@GetMapping("/hello") // #1. 화면에 뿌려줄 데이터를 return
@ResponseBody // #1.1 이런 경우 responseBody 애노테이션을 붙여주어야 한다.
public String hello(){
return "Hello, " + helloService.getName();
}
@GetMapping("/sample") // #2. 뷰 모델을 return
public String sample(){
return "/WEB-INF/sample.jsp"; // #2.1 String만 return을 해줬는데 뷰의 이름으로 인식을 한다.
}
}
// HelloService.class
@Service
@Getter
@Setter
public class HelloService {
private String name;
}
#2.1 내용을 다시 보면 Controller에서 @ResponseBody 애노테이션 없이 return 값을 String만 줬을 때 스프링에서는 이를 뷰의 이름으로 인식한다는 것을 알 수 있다.
package com.going.spring;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@org.springframework.stereotype.Controller("/simple")
public class SimpleController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response){
return new ModelAndView("/WEB-INF/simple.jsp")
}
}
혹은 이런식으로 ModelAndView를 직접 지정해 줄 수도 있다.
다시한번 DispatcherServelet의 동작 순서를 확인해보자면
- 요청을 분석한다(로케일, 테마, 멀티파트 등)
- (핸들러 맵핑에서 위임하여) 요청할 핸들러를 찾는다.
- (등록되어 있는 핸들러 어댑터 중에) 해당 핸들러를 실행할 수 있는 "핸들러 어댑터"를 찾는다.
- 찾아낸 "핸들러 어댑터"를 사용해서 핸들러의 응답을 처리한다.
- 핸들러의 리턴값을 보고 어떻게 처리할지 판단한다.
1. 뷰 이름에 해당하는 뷰를 찾아서 모델 데이터를 렌더링한다.
2.@ResponseBody가 있다면 Converter를 사용해서 응답 본문을 만들고, 최종적으로 응답을 보낸다.
DispatcherServlet의 동작원리를 조금 더 자세하게 알아보기 위해 아래의 경로에 있는 DispatcherServlet.java의 소스를 열어보자.
package org.springframework.web.servlet;
DispatcherServlet이 호출되면 가장먼저 실행되는 초기화 전략함수가 아래의 순서에 따라 호출되고,
각각의 함수는 각각의 전략에 따라 서브클래스에서 override될 수 있다.
/**
* Initialize the strategy objects that this servlet uses.
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
그 중에서도 initViewResolvers전략을 살펴보면 아래와 같다. 먼저 ApplicationContext에서 ViewResolver로 등록된 빈들을 가져오고, 등록된 빈이 없으면 기본 전략을 사용한다.
/**
* Initialize the ViewResolvers used by this class.
* <p>If no ViewResolver beans are defined in the BeanFactory for this
* namespace, we default to InternalResourceViewResolver.
*/
private void initViewResolvers(ApplicationContext context) {
this.viewResolvers = null;
if (this.detectAllViewResolvers) {
// Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
Map<String, ViewResolver> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.viewResolvers = new ArrayList<>(matchingBeans.values());
// We keep ViewResolvers in sorted order.
AnnotationAwareOrderComparator.sort(this.viewResolvers);
}
}
else {
try {
ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
this.viewResolvers = Collections.singletonList(vr);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default ViewResolver later.
}
}
// Ensure we have at least one ViewResolver, by registering
// a default ViewResolver if no other resolvers are found.
if (this.viewResolvers == null) {
this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No ViewResolvers declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
아래와 같이 Subclass에서 Override 하여 사용도 가능하다.
@Configuration
@ComponentScan
public class WebConfig {
@Bean
public ViewResolver viewResolver(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
그 외 다른 전략들도 DispatcherServlet.properties의 소스를 통해 살펴보자.
1. MultipartResolver
- 파일 업로드 요청 처리에 필요한 인터페이스
- MultipartResolver 빈이 구현이 되어있어야 사용이 가능하다.
- HttpServletRequest를 MultipartHttpServletRequest로 변환해주어 요청이 담고 있는 File을 꺼낼 수 있는 API 제공
2. LocaleResolver
- 클라이언트의 위치(Locale) 정보를 파악하는 인터페이스
- 기본 전략은 요청의 accept-language를 보고 판단.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
3. ThemeResolver
- 애플리케이션에 설정된 테마를 파악하고 변경할 수 있는 인터페이스
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
4. HandlerMapping
- 요청을 처리할 핸들러를 찾는 인터페이스
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
- 메소드 자체를 핸들러로 설정할 때는 애노테이션 기반(RequestMappingHandlerMapping)으로 / 클래스 자체를 핸들러로 설정할 때는 (BeanNameUrlHandlerMapping)을 기반으로 사용한다.
5. HandlerAdaptor
- HandlerMapping이 찾아낸 핸들러를 처리하는 인터페이스
- 스프링 MVC 확장력의 핵심
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
- @Controller 애노테이션 아래 구현된 함수라고 보면 된다. 사용자가 언제든지 Handler의 로직을 구현할 수 있기 때문에 확장력의 핵심이 된다.
6. HandlerExceptionHandler
- 요청 처리 중에 발생한 에러 처리하는 인터페이스
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
7. RequestToViewNameTranslator
- 핸들러에서 뷰 이름을 명시적으로 리턴하지 않은 경우, 요청을 기반으로 뷰 이름을 판단하는 인터페이스
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
8.ViewResolver
- 뷰 이름(String)에 해당하는 뷰를 찾아내는 인터페이스
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
9. FlashMapManager
- FlashMap 인스턴스를 가져오고 저장하는 인터페이스
- FlashMap은 주로 리다이렉션을 사용할 때 요청 매개변수를 사용하지 않고 데이터를 전달하고 정리할 때 사용한다.
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
'Study > Spring' 카테고리의 다른 글
[스프링 웹 MVC 활용] 01. HTTP Request 맵핑하기 (0) | 2022.03.01 |
---|---|
[스프링 웹 MVC 설정] 01. @EnableWebMvc와 WebMvcConfigurer (0) | 2022.03.01 |
스프링 핵심기술 06. AOP (0) | 2022.02.13 |
스프링 핵심기술 05. 추상화 (0) | 2022.02.13 |
스프링 핵심기술 04. ApplicationContext가 상속받는 인터페이스들 (0) | 2022.02.06 |