본문 바로가기
스프링/기본지식

서블릿 컨테이너와 스프링 컨테이너의 완벽한 이해

by 순원이 2024. 12. 13.

1. 웹 애플리케이션의 기본 구조 이해하기


1.1 Web Server와 WAS의 구분


image

Web Server

  • Apache, Nginx와 같은 서버
  • HTML, CSS, JavaScript 등 정적 파일 제공
  • 간단한 요청-응답 처리
  • 빠른 처리 속도가 장점

WAS

  • Web Server + web Container
  • Tomcat, JBoss와 같은 서버
  • 비즈니스 로직 처리
  • 데이터베이스 연동
  • 동적 컨텐츠 제공

1.2 Web Server와 Web Container가 같이 있을 경우


image

  • WAS는 웹 서버의 기능을 포함하기 때문에 웹 서버의 기능을 제공할 수 있다.
  • WAS와 DB만으로도 시스템 구성이 가능하지만, WAS가 너무 많은 기능을 담당해 서버 과부하의 우려가 생긴다.
  • 정적 컨텐츠의 요청까지 WAS가 처리한다면 정적 데이터 처리로 인해 서버에 부하가 커지게 되고, 동적 컨턴츠의 처리가 지연됨에 따라 서비스의 속도가 느려질 수 있다.
  • WAS 장애시 오류화면 노출이 불가능해진다.

1.3 Web Server와 Web Container와 분리하는 이유


  • 기능을 분리하여 서버 부하 방지
    • WAS가 웹 서버의 기능을 포함해 모든 역할을 혼자 수행할 수 있지만 그만큼 WAS가 감당해야하는 작업이 많아지게 된다. 그렇기 때문에 각 서버가 감당하는 작업을 줄여 보다 효율적인 사용이 가능하도록 하기 위해 웹 서버와 WAS를 분리한다.
  • 물리적으로 분리하여 보안 강화
    • WAS에는 웹 어플리케이션이 올라가있기 때문에 외부와 직접 연결되어 있다면 중요한 설정이나 파일들이 외부로 노출될 위험이 있다. 웹 서버를 WAS 앞에 위치시켜 리소스를 보다 안전하게 보호할 수 있다.
  • 웹 서버에 여러대의 WAS를 연결 할 수 있다.
    • Load Balancing(서버나 컴퓨터에 가해지는 부하를 분산해주는 기술)을 위해서 Web Server를 사용한다.
    • fail over(작동 중지된 WAS 대신 다른 WAS를 사용해 장애 극복)에 유용
    • fail back(작동 중지된 WAS를 재작동 시킴)에 유용
    • 특히 대용량 웹 어플리케이션의 경우(여러개의 서버 사용) Web Server와 WAS를 분리하여 무중단 운영을 위한 장애 극복에 쉽게 대응할 수 있다.
      • 예를 들어, 앞 단의 Web Server에서 오류 발생시 WAS를 이용하지 못하도록 한 후 WAS를 재시작함으로써 사용자는 오류를 느끼지 못하고 이용할 수 있다.
  • 여러 웹 어플리케이션 서비스 가능
    • 예를 들어, 하나의 서버에서 PHP Application과 Java Application을 하나의 웹 서버에 연결해 함께 사용할 수 있다.

1.4 Web Service Architecture


image

웹 시스템은 다양한 구조로 설계가 가능하다.

  • Client - Web Server - DB
  • Clinet - WAS - DB
  • Client - Web Server - WAS - DB

이중에서 Client - Web Server - WAS - DB의 웹 시스템 구성이 가장 효율적으로 리소스를 관리할 수 있는 구성이다.

Web Server를 WAS앞에 두고 정적 콘텐츠를 Web Server가 처리하고 동적 콘텐츠는 WAS에게 요청을 위임함으로써 WAS는 중요한 애플리케이션 로직 처리를 전담할 수 있게 된다.

1.5 전통적인 Client - Web Server - WAS - DB 웹 시스템 구조의 요청 처리 순서


  1. Web Server는 클라이언트로부터 HTTP 요청을 받는다.
  2. Web Server는 클라이언트의 요청을 WAS에 보낸다.
  3. WAS는 관련된 Servlet을 메모리에 올린다.
  4. WAS는 web.xml을 참조하여 해당 Servlet에 대한 Thread를 생성한다. (Thread Pool 이용)
  5. HttpServletRequestHttpServletResponse 객체를 생성하여 Servlet에 전달한다.
    • Thread는 Servlet의 service()를 호출한다.
    • service()는 요청에 맞게 doGet() 또는 doPost() 메서드를 호출한다.
  6. doGet(), doPost() 메서드는 인자에 맞게 생성된 적절한 동적 페이지를 Response 객체에 담아서 WAS에 전달한다.
  7. WAS는 Response 객체를 HttpResponse 형태로 바꾸어 Web Server에 전달한다.
  8. 생성된 Thread를 종료하고, HttpServletRequestHttpServletResponse객체를 제거한다.

2. Servlet Container


2.1 Servlet


서블릿(Servlet)은 클라이언트의 요청을 처리하고, 그 결과를 다시 클라이언트에게 전송합니다. Servlet을 통해 웹 페이지를 동적으로 생성하여 클라이언트에게 반환해 줄 수 있습니다.

image

Servlet 인스턴스는 싱글톤으로 생성되며, Thread-Safe 하지 않기 때문에 무상태 혹은 읽기 전용 상태, 동기화 처리된 구조로 설계되어야 합니다. 이러한 특성을 가지는 Servlet 을 Servlet Container 가 멀티 쓰레딩 기능을 통해 관리합니다

2.2 전통적인 Servlet Container가 하는 일


서버에 만들어진 서블릿이 스스로 작동하는 것이 아니라, 서블릿을 관리 해주는 것이 필요한데, 이러한 역할을 하는 것이 바로 서블릿 컨테이너 입니다.

즉, 서블릿요구사항 명세서라고 표현한다면, 서블릿 컨테이너는 그 명세서를 보고 개발하는 개발자입니다.

  • 웹 서버와 통신 지원이를 Servlet Container 가 알아서 해주기 때문에 개발자는 통신 기능에 대해서 신경 쓰지 않고
  • 클라이언트의 요청에 대한 비즈니스 로직에만 집중할 수 있습니다
  • Servlet ContainerServletWeb Server 가 통신할 수 있도록 해줍니다.
  • Servlet 생명주기 관리요청이 들어오면 관련된 Servlet 을 찾고 없다면 초기화합니다.Servlet 의 역할이 끝난후 GC 를 진행하여 관련된 인스턴스를 소멸시킵니다.
  • 또한 Servlet 의 service() 메소드를 호출해서 실제 기능을 하도록 합니다.
  • Servlet 의 생성과 호출 소멸을 관리합니다.
  • 멀티 쓰레딩 관리Servlet Container 는 요청이 들어올 때마다 새로운 자바 쓰레드를 생성하여 멀티 쓰레딩 방식으로 처리합니다.
  • 여기서 쓰레드는 무한적으로 생성할 수 있는것은 아닙니다.Servlet Container 내부에는 쓰레드 풀을 가지고 있으며 기본 값으로 200개의 쓰레드(톰캣 기준)가 존재합니다.요청이 들어오면 쓰레드 풀에서 쓰레드를 하나 가져옵니다 ❗️(컨트롤러는 Thread-Safe 함)
  • 앞에서 언급했던 것처럼 Servlet Container 가 관리하는 Servlet 은 싱글톤 패턴으로 관리됩니다. 그렇다면 동시에 같은 Servlet 로 여러 요청이 들어올 때 어떻게 처리할 수 있을까요?
  • JSP 지원JSP(Java Server Page) 를 Servlet 으로 변환시켜 처리합니다.

2.3 전통적인 Servlet 생명주기 관리


image

  • 클라이언트 요청이 들어오면 Servlet ContainerServlet 이 힙 영역에 있는지 확인합니다.만약 존재하지 않는다면 init() 메소드를 호출하여 적재합니다.
  • 클라이언트 요청에 따라서 service() 메소드를 통해 요청에 대한 응답이 생성됩니다.
  • Servlet ContainerServlet 종료 요청을 하면 destory() 메소드가 호출됩니다.종료시 처리해야 하는 작업은 destory() 메소드를 오버라이딩하여 구현하면 됩니다. 종료된 Servlet 인스턴스는 CG 에 의해 메모리에서 제거됩니다.

image

서블릿 컨테이너는 서블릿의 생명주기를 다음과 같이 관리합니다:

  1. 초기화 단계 (init)
    • 서블릿 인스턴스 생성
    • 필요한 리소스 할당
    • 한 번만 실행
  2. 서비스 단계 (service)
    • 클라이언트 요청 처리
    • doGet(), doPost() 등 메서드 호출
    • 여러 번 실행 가능
  3. 소멸 단계 (destroy)
    • 리소스 해제
    • 서블릿 종료
    • 한 번만 실행

2.5 Spring의 Servlet Container


image

스프링 MVC와 DispatcherServlet

스프링 MVC의 DispatcherServlet은 일반적인 서블릿과는 다음과 같은 차이가 있습니다:

  1. DispatcherServlet은 싱글톤으로 유지:
    • 스프링 컨텍스트가 초기화될 때, DispatcherServlet도 함께 생성되고 애플리케이션이 종료될 때까지 싱글톤으로 유지됩니다.
    • 요청이 끝났다고 해서 DispatcherServlet이 소멸되거나 GC의 대상이 되지 않습니다. 계속 메모리에 남아 있으며, 모든 HTTP 요청을 처리하는 중심 역할을 수행합니다.
  2. 서블릿 생명주기와 스프링 관리:
    • Servlet의 생성, 초기화, 요청 처리, 소멸 등은 서블릿 컨테이너가 관리하지만, DispatcherServlet 내부에서 비즈니스 로직이나 컨트롤러, 서비스 등의 라이프사이클은 스프링 컨테이너(Application Context)가 관리합니다.
    • 서블릿 객체 자체의 생성과 소멸은 서블릿 컨테이너의 책임이지만, 그 내부 동작은 스프링이 주도합니다.
  • 일반 서블릿: 요청이 들어올 때마다 서블릿이 동적으로 생성되고, 요청이 끝나면 소멸되거나 GC 대상이 됩니다.
  • DispatcherServlet: 한 번 생성된 후 애플리케이션 종료 시까지 싱글톤으로 유지되며, 메모리에 상주하면서 모든 요청을 처리합니다.

3. Spring Container


위 Servlet Container는 서블릿들을 초기화할 때,
Spring Container는 각각의 서블릿이 제대로 동작하기 위해 필요한 의존성 주입(DI)을 제공한다.
두 컨테이너는 이렇게 밀접한 관계에 있기 때문에 Spring Container에 짚고 넘어갈 필요가 있다.

Spring Container의 역할은 빈(Bean) 객체들을 생성하고,
빈 객체들 간의 의존성을 관리하며 빈 객체들을 필요한 곳에서 주입(Injection)해주는 등의 역할을 맡는다.

빈 객체를 생성하고 생명주기를 관리할 때 스프링 컨테이너가 직접 컨트롤 하기 때문에
Inversion of Control (IoC) 방식이라고 불리며
객체 생성, 관리, 의존성 관련 코드들이 아닌, 서비스에 대한 코드에만 집중할 수 있는 장점이 있다.

때문에 Spring Container는 IoC Container라고도 불린다.

스프링 부트 어플리케이션을 실행하면, SpringApplication.run(....) 메서드가 알아서 어플리케이션 컨텍스트를 생성하고 초기화한다. 이 과정에서 어노테이션 기반의 설정 정보가 사용되므로 내부적으로는AnnotationConfigApplicationContext와 같은 클래스가 사용되지만, 개발자가 직접 이 클래스를 사용하는 것은 아니다. (즉, 스프링부트가 알아서 이 클래스를 사용해 빈을 등록하고 초기화하고 삭제한다.)

3.1 Spring Container의 종류


BeanFactory, ApplicationContext 크게 2가지로 나눌수 있다.

BeanFactory

BeanFactory는 빈을 등록하고 생성하고 조회하고 돌려주는 등 빈을 관리하는 역할을 한다.

getBean() 메소드를 통해 Bean을 인스턴스화 할 수 있으며 그 외에 부가적인 빈을 관리하는 기능을 담당한다.

즉 스프링컨테이너의 가장 기본적이며 핵심적인 기능을 담고있다.

getBean()이 호출되면 BeanFactory는 의존성 주입을 통해 빈을 인스턴스화하고, 빈의 특성을 설정하기 시작하며 여기서 빈의 라이프사이클이 시작된다.

ApplicationContext

스프링이 제공하는 각종 부가 서비스를 추가로 제공하며 BeanFactory를 상속한 인터페이스이다. 이 인터페이스를 구현하는 여러 컨테이너가 있으며

BeanFactory와 다른 점은 BeanFactory는 getBean() 메소드가 호출된 시점에서 지연로딩(LAZY)을 시작하여 생성해주지만 ApplicationContext 초기화 시점에 모든 빈을 미리 로드한 후(EAGER) 필요할때 빈을 지연 없이 얻을 수 있다.

ApplicationContext을 구현하는 컨테이너는 크게

FileSystemXmlApplicationContext, AnnotationConfigApplicationContext 또는

ClassPathXmlApplicationContext 등이 있다.

3.2 ApplicationContext의 구현체 3가지


ClassPathXmlApplicationContext 와 FileSystemXmlApplicationContext

xml 파일로 빈객체들의 생성과 의존관계를 정의하면 이 xml 파일을 이용하여 빈 객체를 생성하고 관리한다. 스프링 프레임워크 초기부터 사용된 가장 간단한 형태의 스프링 컨테이너라고 할 수 있다.

두 컨테이너의 가장 큰 차이점은 xml 파일의 경로이여

ClassPathXmlApplicationContext는 리소스 폴더(src/main/resources/...)에서,

FileSystemXmlApplicationContext는 보통 프로젝트 외부 경로의 xml 파일을 사용할 때 사용된다.

xml 파일 기반으로 빈객체들을 관리하기 때문에 다음과 같은 단점이 있다.

  • xml 파일의 내용이 바뀌면 다시 빌드/배포해야하는 번거로움이 있다.
  • 규모가 커진다면 xml 파일 내용이 길어지고 가독성이 떨어진다.
  • 빈객체들을 런타임때 메모리에 로드되므로 (LAZY로딩) 자원소모가 큰 경우 적절하지 못하다.
  • 동적으로 빈객체들의 구성이 변경되어야한다면 사용할 수 없다.

이와 같은 단점을 가지고 있지만 비교적 쉽게 사용할 수 있기 때문에 간단한 작은 규모의 테스트 프로젝트 등에서 종종 사용된다.

AnnotationConfigApplicationContext

가장 자주 사용되는 스프링 컨테이너이며 xml 파일을 사용하는 대신 자바 어노테이션으로 구성된 설정 클래스를 사용한다.

이 설정 클래스를 생성하고, 이를 바탕으로 스프링 빈을 생성한다.

@Component, @Service, @Repository, @Controller 등으로 자동으로 빈객체를 구성할 수 있고

@Autowired, @Qualifier, @Value 등의 어노테이션을 사용하여 빈객체 간의 의존성을 관리할 수 있다.

자바 코드로 작성되기 때문에 컴파일 단계에서 에러를 검출할 수 있다.

3.3 WebApplicationContext


Spring Web 관련 디펜던시를 추가하면 일반적으로 WebApplicationContext가 사용됩니다.

WebApplicationContext는 스프링 웹 애플리케이션에서 사용하는 ApplicationContext의 특별한 구현체입니다. 이 컨텍스트는 스프링 MVC 애플리케이션에서 사용되며, 웹 관련 빈을 관리합니다. WebApplicationContextApplicationContext를 상속하고 있으며, 웹 애플리케이션의 설정과 관련된 여러 기능을 제공합니다.

  • DispatcherServlet과 통합: 스프링 웹 애플리케이션에서는 DispatcherServletWebApplicationContext를 사용하여 요청을 처리합니다. DispatcherServlet은 이 컨텍스트를 통해 웹 애플리케이션의 컨트롤러, 서비스, 리포지토리 등을 관리하고 요청을 처리하는 로직을 수행합니다.
  • 루트 컨텍스트 초기화: ContextLoaderListenerWebApplicationContext를 초기화하고 관리합니다. 이 컨텍스트는 애플리케이션의 비즈니스 로직과 관련된 빈을 관리하며, 웹 애플리케이션에서 필요한 공통된 빈들을 제공합니다. 예를 들어, 데이터베이스 연결을 위한 서비스나 리포지토리 빈이 포함됩니다.

3.4 ApplicationContext와 WebApplicationContext 관계


imageimage

그림에서 보여지는 것처럼 WebApplicationContextApplicationContext는 선택해서 사용하는 것이 아니라 보통 함께 사용됩니다. 두 가지 컨텍스트는 다르게 동작하지만 서로 연관되어 있습니다.

WebApplicationContext는 웹 애플리케이션에서 필요한 DispatcherServlet과 관련된 빈들을 관리합니다. 이 컨텍스트는 주로 웹 관련 컴포넌트인 컨트롤러, 뷰 리졸버, 핸들러 매핑 등을 포함합니다. 웹 애플리케이션에서 DispatcherServlet이 요청을 처리할 때 WebApplicationContext가 사용됩니다.

반면에 ApplicationContext는 스프링 애플리케이션의 전반적인 설정과 빈들을 관리하는 상위 컨텍스트입니다. WebApplicationContext는 이 ApplicationContext의 하위 컨텍스트로서, 보다 넓은 범위에서의 설정을 관리합니다. ApplicationContext는 일반적으로 서비스, 데이터베이스 연결 등의 비즈니스 로직과 관련된 빈들을 관리합니다.

참고


https://sigridjin.medium.com/servletcontainer와-springcontainer는-무엇이-다른가-626d27a80fe5

'스프링 > 기본지식' 카테고리의 다른 글

@Transactional 어노테이션에 대한 작동방식  (0) 2024.12.16
OSIV  (0) 2024.12.10