본문 바로가기
Spring Framework

[MVC Servlet의 모든 것] 자바 EE, Servlet, ServletContext 등 개념

by Jordy-torvalds 2020. 3. 11.
반응형

Java EE

Java EE는 자바를 이용한 서버측 개발을 위한 플랫폼으로, Java EE 스펙에 따라 제품 구현한 것을 WAS 라고 함.

Java Servlet

자바 서블릿(Java Servlet)은 자바를 사용하여 웹페이지를 동적으로 생성하는 서버측 프로그램 혹은 그 사양을 말하며, 흔히 "서블릿"이라 불린다. 자바 서블릿은 웹 서버의 성능을 향상하기 위해 사용되는 자바 클래스의 일종이다. 자바 EE 사양의 일부분이다.

생명주기

  • 서블릿 컨테이너가 서블릿 인스턴스의 init() 메소드를 호출하여 초기화 한다.
    • 최초 요청을 받았을 때 한번 초기화 하고 나면 그 다음 요청부터는 이 과정을 생략한다.(싱글톤 패턴 사용)
  • 서블릿이 초기화 된 다음부터 클라이언트의 요청을 처리할 수 있다. 각 요청은 별도의 쓰레드로 처리하고 이때 서블릿 인스턴스의 service() 메소드를 호출한다.
    • 이 안에서 HTTP 요청을 받고 클라이언트로 보낼 HTTP 응답을 만든다.
    • service()는 보통 HTTP Method에 따라 doGet(), doPost() 등으로 처리를 위임한다.
    • 따라서 보통 doGet() 또는 doPost()를 구현한다.
  • 서블릿 컨테이너 판단에 따라 해당 서블릿을 메모리에서 내려야 할 시점에 destroy()를 호출한다.

Servlet Container

  • 서블릿 컨테이너는 개발자가 웹서버와 통신하기 위하여 소켓을 생성하고, 특정 포트에 리스닝하고, 스트림을 생성하는 등의 복잡한 일들을 할 필요가 없게 해준다.
  • 컨테이너는 Servlet의 생성부터 소멸까지의 일련의 과정(Life Cycle)을 관리한다. 서블릿 컨테이너는 요청이 들어올 때마다 새로운 자바 스레드를 만든다.
  • 우리가 알고 있는 대표적인 Servlet Container가 Tomcat 이다. 톰캣같은 was가 java파일을 컴파일해서 Class로 만들고 메모리에 올려 Servlet객체를 만든다.
  • Client - Servlet Container간 동작 과정

.

  1. 사용자가 URL을 클릭하면 HTTP Request를 Servlet Container에 보낸다.
  2. Servlet Container는 HttpServletRequest, HttpServletResponse 두 객체를 생성한다.
  3. 사용자가 요청한 URL을 분석하여 어느 서블릿에 대한 요청인지 찾는다.
  4. 컨테이너는 서블릿 service() 메소드를 호출하며, POST, GET여부에 따라 doGet() 또는 doPost()가 호출된다.
  5. doGet() or doPost() 메소드는 동적인 페이지를 생성한 후 HttpServletResponse객체에 응답을 보낸다.
  6. 응답이 완료되면 HttpServletRequest, HttpServletResponse 두 객체를 소멸시킨다.

ServletContext

  • 톰캣이 실행되면서 생성됩니다.
  • 서블릿 컨텍스트(ServletContext)란 하나의 서블릿이 서블릿 컨테이너와 통신하기 위해서 사용되어지는 메서드들을 가지고 있는 클래스가 바로 ServletContext다.
  • 하나의 web application 내에 하나의 컨텍스트가 존재합니다. web application내에 있는 모든 서블릿들을 관리하며 정보공유할 수 있게 도와 주는 역할을 담당하는 놈이 바로 ServletContext다.
  • 쉽게 말하면 웹 애플리케이션의 등록 정보라고 볼 수 있다.
  • 필터와 리스너 또한 등록하여 통신 간에 활용할 수 있다.
    • 리스너는 서블릿 리스너, 세션 리스너 등 EventListener 구현체는 뭐든지 등록할 수 있다.
    • 필터는 characterEncoding 등 Filter 구현체는 뭐든지 등록할 수 있다.

ServletContext를 얻는 방법

  • ServletContext는 ServletConfig의 getServletContext() 사용하여 얻는다.
  • Servlet(서블릿)은 HttpServlet을 상속한다. 그리고 HttpServlet은 ServletConfig를 구현하고 있기 때문에 getServletContext() 메서드를 바로 이용할 수 있다.
  • Servlet extends (HttpServlet implements ServletConfig)
  • ServletContext sc = getServletContext(); //서블릿 내에서 사용가능

Servlet Listener

웹 애플리케이션에서 발생하는 주요 이벤트를 감지하고 각 이벤트에 특별한 작업이 필요한 경우에 사용할 수 있다.

  • 서블릿 컨텍스트 수준의 이벤트
    • 컨텍스트 라이프사이클 이벤트
    • 컨텍스트 애트리뷰트 변경 이벤트
  • 세션 수준의 이벤트
    • 세션 라이프사이클 이벤트
    • 세션 애트리뷰트 변경 이벤트

ContextLoaderListener

  • ServletListener의 구현체
  • WebApplicationContext를 만들어 준다.
  • WebApplicationContext를 서블릿 컨텍스트 라이프사이클에 따라 등록하고 소멸시켜준다.
  • 서블릿에서 IoC 컨테이너를 ServletContext를 통해 꺼내 사용할 수 있다.

 

public class ServletWebServerApplicationContext extends GenericWebApplicationContext
		implements ConfigurableWebServerApplicationContext {
    
    ...
    /*
    
    - Spring Boot는 아래 메소드가 실행되면서 서블렛컨텍스트에 웹 어플리케이션 컨텍스트를 등록합니다.
    - 주요 attribute 초기화 뿐만 아니라, 빈 등록도 이뤄집니다.
    - RootWebApplicationContext에 대한 설정을 합니다.
    - ServletWebApplicationContext에 대한 처리는 최초 요청이 들어와서 DispatcherServlet이 
      초기화가 이뤄지면 그때 합니다.
      
    */
    
    private void selfInitialize(ServletContext servletContext) throws ServletException {
		prepareWebApplicationContext(servletContext);
		registerApplicationScope(servletContext);
		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
			beans.onStartup(servletContext);
		}
	}
    
    ...
    
}

 

Servlet Filter

  • 들어온 요청을 서블릿으로 보내고, 또 서블릿이 작성한 응답을 클라이언트로 보내기 전에 특별한 처리가 필요한 경우에 사용할 수 있다.
  • 체인 형태의 구조

.

DispatcherServlet

  • 서블릿 애플리케이션에 스프링 연동하기
    • 서블릿에서 스프링이 제공하는 IoC 컨테이너 활용하는 방법
    • 스프링이 제공하는 서블릿 구현체 DispatcherServlet 사용하기
  • DispatcherServlet
    • 스프링 MVC의 핵심.
    • Front Controller 역할을 한다.
    • DispatcherServlet 초기화
      • 기본적인 프로퍼티 기반의 설정을 스프링 부트 앱 초기화 때 하고, 핸들러 빈 등록 등의 작업은 최초 호출을 받은 이후에 이뤄진다.
      • 기본적으로 서블렛의 경우 싱글톤 패턴이므로, 최초 초기화를 한 이후에는 초기화를 하지 않는데 같은 맥락을 따르고 있음을 알 수 있다.
      • // log
        2021-07-29 11:18:29.148  INFO 12380 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
        2021-07-29 11:18:29.149  INFO 12380 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
        
        // DispatcherServlet 초기화 코드 일부
        
        class DispatcherServlet extends FrameworkServlet {
        
        	...
            
        	@Override
        	protected void onRefresh(ApplicationContext context) {
        		initStrategies(context);
        	}
        
        	/**
        	 * 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);
        	}
            
        }
    • 다음의 특별한 타입의 빈들을 찾거나, 기본 전략에 해당하는 빈들을 등록한다.
      • HandlerMapping: 핸들러를 찾아주는 인터페이스
      • HandlerAdapter: 핸들러를 실행하는 인터페이스
      • HandlerExceptionResolver
      • ViewResolver
      • ...

DispatcherServlet 동작 순서

  1. 요청을 분석한다. (로케일, 테마, 멀티파트 등)
  2. (핸들러 맵핑에게 위임하여) 요청을 처리할 핸들러를 찾는다.
  3. (등록되어 있는 핸들러 어댑터 중에) 해당 핸들러를 실행할 수 있는 “핸들러 어댑터”를 찾는다.
  4. 찾아낸 “핸들러 어댑터”를 사용해서 핸들러의 응답을 처리한다.
  5. 핸들러의 리턴값을 보고 어떻게 처리할지 판단한다.
    뷰 이름에 해당하는 뷰를 찾아서 모델 데이터를 랜더링한다.
    @ResponseEntity가 있다면 Converter를 사용해서 응답 본문을 만든다.
  1. (부가적으로) 예외가 발생했다면, 예외 처리 핸들러에 요청 처리를 위임한다.
  2. 최종적으로 응답을 보낸다.
  3. .

뷰 이름에 해당하는 뷰를 찾아서 모델 데이터를 랜더링한다.
@ResponseEntity가 있다면 Converter를 사용해서 응답 본문을 만든다.

Front Controller

  • 중복 제어를 회피하고, 중앙 집중식 액세스 지점을 원하는 경우에 적합한 Java EE 디자인 패턴
  • URL Pattern이 다른 Front Controller를 생성할 경우 분기하는 것 또한 가능 하다.

DispatcherServlet의 구성요소

재정리

DispatcherServlet 초기화

  1. 특정 타입에 해당하는 빈을 찾는다.
  2. 없으면 기본 전략을 사용한다. (DispatcherServlet.properties)

스프링 부트 사용하지 않는 스프링 MVC

  • 서블릿 컨네이너(ex, 톰캣)에 등록한 웹 애플리케이션(WAR)에 DispatcherServlet을 등록한다.
    • web.xml 혹은 WebApplicationInitializer에 서블릿 등록
    • 또는 WebApplicationInitializer에 자바 코드로 서블릿 등록 (스프링 3.1+, 서블릿 3.0+)
  • 세부 구성 요소는 빈 설정하기 나름.

스프링 부트를 사용하는 스프링 MVC

  • 자바 애플리케이션에 내장 톰캣을 만들고 그 안에 DispatcherServlet을 등록한다.
    • 스프링 부트 자동 설정이 자동으로 해줌.
  • 스프링 부트의 주관에 따라 여러 인터페이스 구현체를 빈으로 등록한다.

Spring MVC 설정

@EnableWebMvc

애노테이션 기반 스프링 MVC를 사용할 때 편리한 웹 MVC 기본 설정

WebMvcConfigurer Interface

@EnableWebMvc가 제공하는 빈을 커스터마이징할 수 있는 기능을 제공하는 인터페이스.

스프링 부트의 스프링 MVC 자동 설정 주의사항

  • @EnableWebMvc를 사용시 스프링부트 자동 설정 사용 불가
    • WebMvcConfigurer를 구현한 @Configuration Bean에서 @EnableWebMvc 자동 설정을 커스터마이즈 할 수 있음.
  • @EnableWebMvc를 미사용시 스프링부트 자동 설정 사용 가능
    • WebMvcConfigurer를 구현한 @Configuration Bean에서 스프링부트 자동 설정을 커스터마이즈 할 수 있음.
반응형