영권's

ServletContext, ApplicationContext, WebApplicationContext 본문

자바/Spring

ServletContext, ApplicationContext, WebApplicationContext

ykkkk 2021. 11. 8. 18:09

제가 개인적으로 공부하면서 헷갈리거나 잘 모르는 내용을 정리한 것입니다.

ServletContext

서블릿 컨텍스트(ServletContext)란 하나의 서블릿이 서블릿 컨테이너와 통신하기 위해서 사용되어지는 메서드들을 가지고 있는 클래스가 바로 ServletContext다.

 

여러 서블릿에서 공유할 수 있는 정보를 담는 객체이다.

(공식문서 : https://docs.jboss.org/jbossas/javadoc/7.1.2.Final/javax/servlet/ServletContext.html?is-external=true)

출처 : https://nesoy.github.io/articles/2019-02/Servlet

 

이미 서블릿과 서블릿 컨테이너를 아시는 분들이라면 이해를 잘 하시겠지만 저는 잘 이해가 안됐습니다. 그래서 한번 찾아봤습니다.

 

서블릿은 예전에는 정적 페이지로만 웹 서버에서 응답이 가능했습니다.

 

그렇게 되면 사용자의 상태라던가 게시글 추가 등의 시간의 흐름에 따라서 동적으로 변경되는 상태를 표현하는 것이 불가능 했는데요. 이런 한계점을 극복하고 해결하기 위해서 웹 서버에 프로그램을 붙여서 동적인 페이지를 생성할 수 있게 되었는데 그 중에서 자바에서 동적인 웹페이지를 개발하기 위한 기술 중 하나가 서블릿입니다.

그래서 요약을 하면 서블릿은 웹 서버로부터 요청을 받아서 처리하고 서버에 다시 응답을 하는 하나의 자바 프로그램이라고 할 수 있습니다.

출처 :  https://gmlwjd9405.github.io/2018/10/27/webserver-vs-was.html

 

그리고 서블릿 컨테이너는 말 그대로 서블릿들을 보관하는 그릇(컨테이너)라고 생각하면 되는데요.

서버에 만들어진 서블릿이 스스로 작동하는 것이 아니라, 서블릿을 관리 해주는 것이 필요한데, 이러한 역할을 하는 것이 바로 서블릿 컨테이너이고 대표적으로 오픈소스인 톰캣이 있습니다.

서블릿 컨테이너의 주 역할은

1. 웹서버와의 통신 지원

  • 서블릿 컨테이너는 서블릿과 웹서버가 손쉽게 통신할 수 있게 해주어, 소켓을 만들고 listen, accept 등을 API로 제공하여 복잡한 과정을 생략할 수 있게 해준다.

2. 서블릿 생명주기(Life Cycle) 관리

  • 서블릿 컨테이너는 서블릿의 탄생과 죽음을 관리한다.
    • 서블릿 클래스를 로딩하여 인스턴스화
    • 초기화 메소드를 호출
    • 요청이 들어오면 적절한 서블릿 메소드를 호출합니다.
    • 서블릿 소멸 시 Garbage Collection(가비지 컬렉션)을 진행

3. 멀티쓰레드 지원 및 관리

  • 서블릿 컨테이너는 요청이 올 때 마다 새로운 자바 쓰레드를 하나 생성
  • HTTP 서비스 메소드를 실행하고 나면, 쓰레드는 자동으로 소멸
  • 원래는 쓰레드를 관리해야 하지만 서버가 다중 쓰레드를 생성 및 운영해주니 쓰레드의 안정성에 대해서 걱정하지 않아도 된다.

4. 선언적인 보안 관리

  • 서블릿 컨테이너를 사용하면 개발자는 보안에 관련된 내용을 서블릿 또는 자바 클래스에 구현해 놓지 않아도 됩니다.
  • 일반적으로 보안관리는 XML 배포 서술자에 다가 기록하므로, 보안에 대해 수정할 일이 생겨도 자바 소스 코드를수정하여 다시 컴파일 하지 않아도 보안관리가 가능합니다.

사용자의 요청이 일어났을 때 하나의 라이프 사이클을 보면

  1. 사용자가 서블릿에 대한 링크를 클릭한다.(HTTP 요청)
  2. 컨테이너는 들어온 요청에 대해서 HttpServletRequest와 HttpServletResponse를 생성
  3. 사용자가 보낸 URL를 분석해서 어떤 서블릿에 대한 요청인지 알아내고 스레드에 서블릿을 할당해서 실행하고 Request와 Response를 인자로 넘깁니다.
  4. 컨테이너는 서블릿 Service()를 호출하고 Http메서드에 따라 doGet(), doPost()등을 호출
  5. 그러면 서블릿은 요청 처리에 대한 응답을 생성해서 Response객체와 같은 곳에 실어서 응답을 보냅니다.
  6. 응답이 이뤄지면 모든 작업이 끝난거고 컨테이너는 사용했던 request, response 객체를 소멸시키거나 쓰레드에 할당했던 서블릿을 해제하는 등의 일을 합니다.

이런 과정에서 예를 들어, 서블릿 컨테이너에서 요청을 처리 할 서블릿을 찾아서 요청을 보내거나 파일의 MIME TYPE을 가져오거나 할 때 서블릿 컨텍스트의 메서드를 사용할 수 있습니다.

하나의 web application 내에 하나의 컨텍스트가 존재합니다. web application내에 있는 모든 서블릿들을 관리하며 정보 공유할 수 있게 도와 주는 역할을 담당하는 것이 바로 ServletContext다.

출처 :  https://stackoverflow.com/questions/4223564/servletconfig-vs-servletcontext

 

Application Context(Spring)

Application Context을 알아보겠습니다.

스프링에서는 오브젝트의 생성과 관계 설정, 사용, 제거 등의 작업을 애플리케이션에서 개발자가 코드를 직접 구현하는 대신 독립된 컨테이너가 담당하는데 이를 컨테이너가 제어권을 가지고 있다고 해서 IoC라고 부릅니다.

또한, 스프링에서는 IoC를 담당하는 컨테이너를 빈 팩토리 또는 애플리케이션 컨텍스트라고 부릅니다.

오브젝트의 생성과 오브젝트 사이의 관계를 설정하는 DI관점에서 볼 때는 IoC 컨테이너를 빈 팩토리라고도 하는데 사실 스프링 컨테이너는 DI 작업보다 더 많은 일을 하고 이에 해당하는 필요한 여러가지 컨테이너 기능을 추가한 것을 애플리케이션 컨텍스트라고 부릅니다.

실제 ApplicationContext는 인터페이스이고, 실제 스프링 컨테이너는 이 ApplicationContext를 구현한 구현체를 말하게 됩니다.

예를 들면 annotation기반 AnnotationConfigApplicationContext 가 있고 그 외에도 xml기반, Groovy기반이 있습니다.

그래서 애플리케이션 컨텍스트가 스프링 애플리케이션을 만들기 위해 어떤 일을 하는지를 보면

POJO 클래스

POJO 클래스라는 것은 객체지향적인 원리에 충실하면서, 환경과 기술에 종속되지 않고 필요에 따라 재활용 될 수 있는 방식으로 설계된 오브젝트를 말한다.

설정 메타정보

POJO 클래스들 중에 애플리케이션에서 사용할 것을 선정하고 이를 IoC 컨테이너가 제어할 수 있도록 적절한 메타정보를 만들어 제공하는 작업이다.

IoC의 컨테이너의 가장 기초적인 역할은 오브젝트를 생성하고 관리하는 것이고 IoC 컨테이너가 관리하는 객체를 빈이라하고 설정 메타정보는 이 빈을 어떻게 만들고 어떻게 동작할 것인가에 대한 정보이다.

스프링의 설정 메타정보는 BeanDefinition 인터페이스로 표현되는 순수한 추상 정보고 애플리케이션 컨텍스트는 이 BeanDefinition으로 만들어진 메타정보를 담은 오브젝트를 사용해서 IoC와 DI작업을 수행한다고 합니다.

그래서 xml, 애너테이션, 자바코드, 프로퍼티 파일이든 상관없이 BeanDefinition으로 정의되는 스프링의 설정 메타정보의 내용을 표현한 것이면 무엇이든 사용 할 수 있는것 입니다.

여러 설정 파일들을 BeanDefinition으로 읽어주는게 BeanDefinitionReader라는게 있고 이거를 구현해서 BeanDefinition 타입으로 정의할 수만 있으면 어떤 설정 메타정보는 어떤 형식으로든 작성할 수 있다.

스프링 IoC 컨테이너는 각 빈에대한 정보를 담은 설정 메타정보를 읽어들인 뒤에, 이를 참고해서 빈 오브젝트를 생성하고 프로퍼티나 생성자를 통해 의존 오브젝트를 주입해주는 DI작업을 수행합니다.

이 작업을 통해 만들어지고, DI로 연결되는 오브젝트들이 모여서 하나의 애플리케이션을 구성하고 동작하게 되는데 이것이 IoC 컨테이너(Application Context)의 역할이 됩니다.

출처 : 토비의 스프링

결국 스프링 애플리케이션이란 POJO클래스와 설정 메타정보를 이용해서 IoC 컨테이너(ApplicationContext)가 만들어주는 오브젝트의 조합이라고 합니다.


더보기

BeanDefinition에서 정의되는 메타정보는

- 빈 아이디, 이름, 별칭 : 빈 오브젝트를 구분할 수 있는 식별자

- 클래스 or 클래스 이름 : 빈으로 만들 POJO 클래스 또는 서비스 클래스 정보

- 스코프

- 프로퍼티 값 또는 참조

- 생성자 파라미터 값 또는 참조

- 지연 로딩 여부

 

- 메타정보 리소스 : xml, 애너테이션, properties 등등

 

- 메타정보 리더 : BeanDefinitionReader라는 인터페이스

 

- 설정 메타정보: BeanDefinition라는 인터페이스

 

- POJO 클래스 : 특정 환경이나 규약에 종속되지 않는 재사용이 가능한 자바 오브젝트


WebApplicationContext(Spring)

WebApplicationContext는 그림과 같이 ApplicationContext를 확장한 인터페이스입니다.

이 WebApplicationContext를 구현한 구현체들이 스프링 애플리케이션에서 가장 많이 사용됩니다.

구현체는 Xml 설정파일을 사용하는 XmlWebApplicationContext, 애너테이션 기반의 설정 리소스를 사용하는 AnnotationConfigWebApplicationContext등이 있습니다.

 

WebApplicationContext의 사용법을 이해하려면 스프링의 IoC 컨테이너를 적용했을 때 애플리케이션을 기동시키는 방법에 대해서 알아야 합니다.

아까 말씀드렸다시피 빈설정 메타정보를 이용해 빈 오브젝트를 만들고 DI작업을 수행하지만 그것만으로는 애플리케이션이 동작하지는 않습니다.

마치 자바 애플리케이션의 main() 메서드처럼 어디선가 특정 빈 오브젝트의 메서드를 호출함으로써 애플리케이션이 동작이 되는데요.

보통 이런 기동시키는 역할을 맡은 빈을 사용하려면 IoC 컨테이너에서 요청해서 빈 오브젝트를 가져와야되고 만약 직접 IoC 컨테이너를 셋업했다면 다음과 같은 코드가 등장한다

적어도 한 번은 IoC 컨테이너에게 요청해서 빈 객체를 가져오고 이 때 getBean()을 사용해서 ApplicationContext안에 있는 객체를 가져온다.

그 이후로는 다시 빈으로 가져오지 않아도 되는데 빈 객체끼리 DI로 서로 연결되어 있으므로 의존관계를 타고 필요한 객체가 호출되면서 애플리케이션이 동작이 된다.

hello.print()를 호출했으면 더 이상 컨테이너의 도움을 받지 않고도 컨테이너 안에 구성된 빈 객체에 의해 애플리케이션이 동작합니다.

IoC 컨테이너의 역할은 이렇게 초기에 빈 객체를 생성하고 DI한 후 최초로 애플리케이션을 기동할 빈을 제공해주는 것 까지이다.

그런데 웹 애플리케이션은 사실 동작하는 방식이 다르다. 독립형 자바 프로그램은 자바Vm에게 main() 메서드를 가진 클래스를 시작 시켜달라고 요청할 수 있지만 웹에서는 main() 메서드를 호출할 방법이 없습니다.

게다가 사용자도 여러명이 될 수 있고 동시에 웹 애플리케이션을 사용하는데 그래서 웹 환경에서는 main() 메서드 대신 서블릿 컨테이너가 브라우저로부터 오는 HTTP 요청을 받아서 해당 요청에 매핑되어 있는 서블릿을 실행시켜주는 방식으로 동작한다. 서블릿이 일종의 main()의 역할을 하는셈이다.

그러면 웹 애플리케이션에서 스프링 애플리케이션을 기동시키는 방법은 무엇일까?

일단 main()역할을 하는 서블릿을 만들어두고, 미리 애플리케이션 컨텍스트를 생성해둔 다음, 요청이 서블릿으로 들어올 때마다 getBean()으로 필요한 빈을 가져와서 정해진 메서드를 실행시키면 된다.

웹 환경에서 스프링 애플리케이션이 기동하는 방식(출처 : 토비의 스프링 3.1)

main() 메서드안에서 애플리케이션 컨텍스트를 만들고 설정 메타정보를 읽어들여서 초기화하고 POJO 빈을 요청하고 메서드를 실행하는 것과 다를게 없다.

단지 main()메서드나 테스트 메서드에서 했던 작업을 웹 애플리케이션과 그에 소속된 서블릿이 대신 해줄 뿐입니다.

그리고 아까 말씀드렸다시피 서블릿 컨테이너는 사용자의 요청을 받아서 서블릿을 동작시켜주는 일을 맡습니다.

서블릿은 웹 애플리케이션이 시작될 때 미리 만들어둔 웹 애플리케이션 컨텍스트에게 빈 객체로 구성된 애플리케이션의 기동 역할을 해줄 빈을 요청해서 받아두고 미리 지정된 메서드를 호출함으로써 스프링 컨테이너가 DI방식으로 구성해둔 애플리케이션의 기능이 시작되는 것이다.

그런데 사실 저희는 이런 설정들을 한 적이 없는데 잘 동작하는것은 웹 환경에서 애플리케이션 컨텍스트를 생성하고 설정 메타정보로 초기화해주고, 클라이언트로부터 들어오는 요청마다 적절한 빈을 찾아서 이를 실행해주는 기능을 가진 DispathcerServlet이라는 이름의 서블릿을 스프링에서 제공하고 이거를 저희가 사용하기 때문입니다.

또 저희는 DispatcherServlet을 등록한적이 없는데 어떻게 되는건가? 라고 할수 있는데 과거에는 web.xml 파일에 등록했었고 springboot에서는 SpringAutoConfiguration에 의해서 spring.factories안에 있는 DispathcerServletAutoConfiguration이 명시 되어 있는것을 볼 수 있고 이 객체를 통해 DispatcherServlet을 등록하게 된다.

 


참고

 

도서

토비의 스프링 3.1 – 이일민

 

영상

박재성 - Servlet ContainerServlet의 관계(Youtube)

 

문서

https://docs.oracle.com/javaee/7/api/javax/servlet/ServletContext.html

https://docs.oracle.com/javaee/7/api/javax/servlet/Servlet.html

 

블로그

https://gmlwjd9405.github.io/2018/10/28/servlet.html

https://jordy-torvalds.tistory.com/14

https://velog.io/@han_been

https://stackoverflow.com/questions/4223564/servletconfig-vs-servletcontext

 

'자바 > Spring' 카테고리의 다른 글

IoC, DI, Application Context  (0) 2021.09.15
Spring initializr  (0) 2021.08.17
Comments