[Spring] Tomcat + Spring 구성요소

김지환·2023년 2월 12일
0

TL:DR;

Spring의 servlet에 대해 찾아보다보니 Tomcat과 Spring에 대한 관계를 이해할 필요가 있다고 생각해 Tomcat과 Spring에 대한 정리를 하고 WAS 에서의 웹통신 흐름이 어떻게 이루어지는 지에 대해서 정리해보고자 한다.

Tomcat 이란?

Tomcat 은 WAS ( Web Application Server ) 이다.

WAS란 Web Server 가 request에 대해서 정적인 응답만을 준다면 여기에 동적인 처리까지 가능하게 만든 것이다.

SpringBoot 를 사용하게 되면 자체적으로 내장된 Tomcat을 실행하게 되고 spring + tomcat 으로 웹 에플리케이션이 동작된다.

Tomcat에서 자체적으로 웹 서버의 기능을 수행해줄 수도 있지만 servlet container, spring container를 실행하며 웹 서버 역할까지 한다면 부담이 될 수 있기 때문에 따로 Appache HTTP server 혹은 Nginx와 같은 웹서버를 추가로 설치하여서 WAS를 구성할 수 있다.

Spring의 웹 아키텍처

1. Tomcat (Servlet Container)

톰캣은 WAS로써 미들웨어역할을 하지만 아파치의 일부분 기능을 서비스(httpd(웹서비스 데몬) native 모듈 포함)하고있어 Web Server역할도 수행할 수 있다.

톰캣의 메인 기능으로 서블릿 컨테이너역할인데 이는 서블릿의 라이플 사이클을 관리하며 DispatcherServlet도 해당 컨테이너에서 수행된다.
또한, 응답을 위한 소켓을 만드는 역할과 요청마다 쓰레드를 생성해 요청을 처리하기 위한 스레드풀을 관리한다.

해당 Tomcat도 결국 자바프로그램이기 때문에 별도의 JVM이 동작한다.

1. Servlet

요청에 대한 웹페이지나 결과값을 동적으로 생성 해주기 위한 역할을 수행하는 자바 프로그램이다.

SpringMVC에서는 DispatcherServlet이라는 FrontController 역할을 수행하는 Servlet이 존재하여 요청을 다른 컨트롤러에게 위임하는 방식으로 처리

service()를 실행하고 해당 메서드에서 dispatch.doService() -> doDispatch()를 실행한다. (doDispatch는 HandlerMapping에서 핸들러를 가져오는 역할)

2. Filter

서블릿 필터는 DispatcherServlet 이전에 실행이 되는데 필터가 동작하도록 지정된 자원의 앞단에서 요청내용을 변경하거나, 여러가지 체크를 수행할 수 있다.

또한 자원의 처리가 끝난 후 응답내용에 대해서도 변경하는 처리를 할 수가 있다.

보통 web.xml에 등록하고, 일반적으로 인코딩 변환 처리, XSS방어 등의 요청에 대한 처리로 사용된다.

필터의 실행 메서드
ㆍinit() - 필터 인스턴스 초기화
ㆍdoFilter() - 전/후 처리
ㆍdestroy() - 필터 인스턴스 종료

3. Delegating Filter Proxy

Servlet Container의 Filter는 ~~Servlet 스펙이기 때문에 스프링에서 정의된 빈을 주입받을 수 없다. ~~(Springboot에서는 웹서버(Tomcat)을 직접관리하기 때문에 Filter또한 빈관리가 가능)때문에 DelegatingFilterProxy 를 통해 서블릿 컨테이너에서 필터로서 요청을 취득하고, 스프링 컨테이너에 존재하는 특정 빈 FilterChainProxy 을 찾아서 요청을 위임하게 된다.

아래는 DelegatingFilterProxy class 내부 doFilter 메서드의 모습


	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		// Lazily initialize the delegate if necessary.
		Filter delegateToUse = this.delegate;
		if (delegateToUse == null) {
			synchronized (this.delegateMonitor) {
				delegateToUse = this.delegate;
				if (delegateToUse == null) {
					WebApplicationContext wac = findWebApplicationContext();
					if (wac == null) {
						throw new IllegalStateException("No WebApplicationContext found: " +
								"no ContextLoaderListener or DispatcherServlet registered?");
					}
					delegateToUse = initDelegate(wac);
				}
				this.delegate = delegateToUse;
			}
		}

		// Let the delegate perform the actual doFilter operation.
		invokeDelegate(delegateToUse, request, response, filterChain);
	}

WebApplicationContext 에서 등록된 bean을 찾아서 요청을 위임하게 된다.

4. DispatcherServlet

클래스 내부에 핸들러, 어댑터, 리졸버 등을 가지고 있어 요청에대한 응답이 가능하다.
이들을 인터페이스로 가지고 있으며 생성되는 시점에 ApplicationContext에서 빈들을 주입받아 생성된다. Spring이 생성하는 Servlet이다.

  • HandlerMapping :
    요청을 분석하여 맵핑된 Controller확인 만약 Controller를 실행하기 전 Interceptor가 있다면 해당 정보도 같이 전달해줘서 선, 후 실행할 수 있도록함.

  • HandlerAdapter :
    Controller 에게 요청을 보내는 역할

  • HandlerInterceptor:
    필터는 스프링 컨텍스트 외부에 존재하여 스프링과 무관한 자원에 대해 동작한다. 하지만 인터셉터는 스프링의 DistpatcherServlet이 컨트롤러를 호출하기 전, 후로 끼어들기 때문에 스프링 컨텍스트(Context, 영역) 내부에서 Controller(Handler)에 관한 요청과 응답에 대해 처리한다. 스프링의 모든 빈 객체에 접근할 수 있다.
    인터셉터는 여러 개를 사용할 수 있고 로그인 체크, 권한체크, 프로그램 실행시간 계산작업 로그확인 등의 업무처리할 수 있다.

    Interceptor 실행 메서드
    preHandler() - 컨트롤러 메서드가 실행되기 전
    postHanler() - 컨트롤러 메서드 실행직 후 view페이지 렌더링 되기 전
    afterCompletion() - view페이지가 렌더링 되고 난 후

  • ViewResolver :
    Controller 에서 view를 return시 해당 view를 찾아 리턴해주는 역할

4. Spring Container

Spring Container 는 Servlet WebApplicationContext와 Root WebApplicationContext가 존재한다.

1. Servlet WebApplicationContext

  • HandlerMapping : 요청을 분석하여 맵핑된 Controller확인 만약 Controller를 실행하기 전 Interceptor가 있다면 해당 정보도 같이 전달해줘서 선, 후 실행할 수 있도록함.
  • HandlerAdapter : Controller 에게 요청을 보내는 역할
  • ViewResolver : Controller 에서 view를 return시 해당 view를 찾아 리턴해주는 역할

2. Root WebApplicationContext

Service, datasource,repositories 들을 포함하고 있는 Context이고 Servlet WebApplicationContext는 RootWebApplicationContext를 상속받아 구현된 Context로 주로 Controller, Intercepter, ViewResolver, HandlerMapping 등 과 같은 빈들이 존재한다.

Servlet을 구현할때 위처럼 상속관계로 나누어 Servlet WebApplicationContext를 구현한 이유는 서블릿 컨테이너 내부에 여러개의 서블릿이 올 수도 있는데 Service, datasource들은 공통적으로 사용될 수 있기 때문이다.

Container가 Bean 생성 시, Service-Locator 패턴으로 의존성을 주입하며 생성한다.

서블릿의 생명주기를 관리한게 서블릿 컨테이너였다면 스프링 컨테이너는 빈의 라이플 사이클을 관리하며 IOC/DI를 제공해주는 역할을 수행한다.

Dispatcher Servlet 좀 더 자세히 보기

서버 시작과 요청까지의 단계

  1. Web server init

  2. Root WebApplicationContext 로딩

  3. Web server start

  4. Client가 Web server로 Request

  5. Web Server가 Servlet Container로 전달

  6. Servlet 스레드 생성

  7. 서블릿이 생성안되어있다면 DispatcherServlet init

  8. 생성된 스레드에서 DisapatcherServlet의 service() 메서드 호출

  9. HandlerMapping을 통해 컨트롤러 조회

  10. HandlerAdapter를 통해 컨트롤러에게 전달

  11. Contrller -> Service-> 동작 후 응답

Reference

https://www.waytoeasylearn.com/learn/spring-web-mvc-components/
https://velog.io/@yaho1024/spring-security-delegatingFilterProxy
https://logical-code.tistory.com/194

profile
Developer

0개의 댓글