부자 되기 위한 블로그, 머니킹

Description

요즘 스프링 프로젝트를 개발하면서 기본적인 부분에 대해서 부족함을 많이 느꼈다. 그래서 다시 스프링 기본 이론부터 동작 방식 등을 공부하고 있는데 정말 방대하게 모르는 지식들이 나왔다. 이번 기회에 제대로 정리하고자 하는 마음으로 공부한 내용을 정리하고 포스팅하려고 한다.

 

WAS와 Web Server

무엇이 다를까?

 

이 둘은 비슷하면서도 다른 면모를 보이고 있다. Web Server은 주된 기능으로 Http 프로토콜을 기반으로 정적인 컨텐츠를 제공하는 기능을 하고 있다. 이에 반해 Was는 애초에 목적인 동적 컨텐츠를 제공 하기 위해 만들어졌는데 Web Application Server라고도 불리며 Web Server은 정적인 컨텐츠를 제공할때는 Was를 거치지 않지만 동적 컨텐츠가 필요한 경우에는 해당 요청을 Was로 전달하는 것이다.

 

Was는 결국 Web Server와 비슷하게 요청을 받으면 응답을 한다는 기본 목적에 충실한다. 다만 Web Server의 기능에서 Web Conatiner의 개념이 추가된 형태이다. apache의 경우가 주로 정적인 컨텐츠에 대해서만 정해진 Http 규격에 따라 요청을 보내게 되고 요청 데이터를 단순 매칭하고 응답하여 Static Web Server라고 부른다.



Web Container (Servlet Container)

 

web container는 많은 기능들을 제공하는데 먼저 container 안에는 Servlet이 있는데 이 Servlet은 쉽게 말해 클라이언트와 서버의 양방향 통신을 지원하여 동적 페이지 구축에 필요한 웹 어플리케이션 컴포넌트라고 보면 된다. (WAS 별 다양한 Container가 존재하며 Servlet 기능과 관련있는 Container를 Servlet Container라고 한다)

 

즉 우리가 브라우저에서 URL을 통해 요청을 보내면 해당 요청에 적합한 Servlet 로직이 실행되어 응답을 보내 주는 구조이다. Web Container는 이러한 Servlet의 생명 주기를 관리한다. Servlet을 로드/초기화 하고 클라이언트 요청에 맞는 서블릿 메소드를 호출하고 Web Container가 종료시 서블릿을 종료 시켜 메모리를 정리하는 역할을 한다. (가비지 컬렉션)

 

아까 Web Server가 동적인 컨텐츠를 제공해야 할 때 WAS로 요청을 전달한다고 했는데 WAS 구성 요소인 Web Container가 이 요청을 받아 분석하여 서블릿을 실행시키고 통신을 지원한다. 우리가 흔히 들어봤던 Socket 기술을 사용하여 Servlet Socket을 만들고 특정 포트 리스닝 후 연결 요청 들어오면 스트림을 생성하여 요청/응답 처리가 이루어지는 것이다.

 

이러한 특성을 가진 것이 tomcat인데 정적 + 동적 컨텐 페이지 구현이 가능한 apache와 tomcat을 조합하여 사용하기 때문에 apache tomcat이라고 부르는 것이다. 이 때 apache는 Web Server이고 tomcat은 ServletContext이다.



WAS 요청 처리 과정

Tomcat Request 처리

대표적인 WAS는 Tomcat이 있다. Tomcat은 Java로 작성되어 JVM 위에서 동작하게 되는데 하나의 Tomcat Instance당 하나의 Process로 동작하며 Tomcat은 TCP를 지원하는 Coyote(Http Component)과, Java Servlet을 호스팅하는 Catalina, JSP 페이지 요청을 처리하는 Jasper 기능을 가지고 있다.

 

tomcat은 단일 프로세스로 동작하여 요청 처리를 스레드로 할당한다. 이 때 일정한 Thread 개수를 정해 Thread Pool을 만드는데 HttpRqeust 요청이 들어올 때 Thread Pool에 있는 Thread를 배정 및 재사용을 진행하다. 기본적인 Thread Pool의 Thread 개수는 200개이지만 이는 설정을 통해 max 값을 지정할 수 있다.

 

Spring Bean 로직 처리

 

그렇다면 우리는 Tomcat이 요청이 들어올때마다 스레드에 해당 작업을 할당하는 것까지는 알았다. 그렇다면 해당 요청에 대한 실제 처리 로직인 Spring Bean(Controller)는 어떻게 작동하는가? 한개의 요청을 볼떄는 쉽지만 같은 요청이 발생했을때 Spring Bean은 요청과 같은 수의 instance가 생겨 처리를 할까?

 

대답은 아니다이다. 기본적인 Spring Bean 구조는 Singleton 방식으로 구현된다. 즉 하나의 Bean 객체만 생성된 뒤에 이를 공유하는 방식이다. Spring Bean에서 가장 중요한 개념은 state를 가지는냐일 것이다. 객체는 state 상태에 따라 stateless와 stateful 로 나뉘어진다.

 

stateless는 쉽게 말해 공유 filed가 없는 객체이다. 즉 이전의 시점과 상호작용할 필요 없이 격리된 상태를 의미하며 이에 따라 과거의 상태를 저장할 필요가 없는 객체를 말한다. Spring Bean을 설계할 떄 왠만하면 stateless한 상태로 설계하는 것이 좋으며 stateless 상태일때는 객체를 Singleton으로 설정하는 것이 표준이다.

 

stateful 상태는 이전의 시점과 상호작용하는 객체이며 보통 공유 field를 가져 과거와 현재의 state를 공유하는 객체이다. 왠만하면 Spring Bean을 stateful 상태로 놓는 것은 좋지 않으며 꼭 필요한 경우에는 해당 객체를 Prototype로 설정한다.

 

그렇다면 Singleton의 경우 statelesas 상태를 만들어야 하는 이유는 무엇일까? 또한 이런 Singleton의 Spring Bean 객체들은 실제 요청 처리에서 어떻게 동작할까?



Singleton Spring Bean이 Thread마다 실행될 수 있는 원리

그렇다면 대다수의 Spring Bean이 SingleTon으로 되어 있는데 실제 요청에 대한 로직을 담고 있기 때문에 요청마다 발생하는 쓰레드에 Spring Bean 로직이 실행되야한다. 그렇다면 Singleton임에도 불구하고 쓰레드마다 할당되기 위해 여러개의 Spring Bean이 생성되는가?

 

대답은 아니다이다. 이를 알려면 JVM의 메모리 부분에 대해서 알아야 한다.Spring Bean(Controller)가 생성되면 객체, instatnce 자체는 Heap에 생성되지만 해당 Class 정보는 Method Area에 저장된다. 이는 JVM 역할과 관련있는데 프로그램 실행 중 클래스가 생성되면 해당 인스턴스 변수, 메소드 등을 Method Area에 저장한다.

 

Heap에 저장되는 것은 인스턴스가 저장되는 것이 아닌 포인터가 저장되어 참조 역할을 힌다. 참고로 Heap 영역은 Garbage Collection의 대상이 되는 영역이다. Heap에서는 참조되지 않은 인스턴스 정보를 얻기 때문에 이를 대상으로 GC가 일어나는 것이다. GC와, JVM의 stack과 heap에 대해서는 나중에 포스팅으로 다루겠다.

 

말이 길어졌지만 Heap이든 Method Area든 이러한 객체에 관련된 정보들은 모든 Thread에 Binary Code 정보를 고유할 수 있다. Singleton Bean 객체가 요청마다 생성되는 Thread에 로직을 실행하는 방법도 이와 관련이 있다. 객체를 생성해 로직을 실행하는 것이 아닌 공유된 객체 정보를 사용하여 쓰레드마다 해당 로직을 실행하는 것이다.

 

TMI이지만 Singleton Spring Bean 객체를 statless로 만들어야 하는 이유는 만약 stateful로 만든다면 스레드마다 해당 state에 대해서 동기화를 해줘야 하는데 이는 Singleton의 효율성을 저하시키는 행동으로서 이런 로직을 짰다면 굳이 Singleton을 사용할 필요가 없다는 것이다.



JVM , Was 그리고 Servlet Container

JVM의 Servlet

Tomcat은 java로 작성된 파일이라고 했다. Tomcat은 Servlet Container가 구성요소로 있으며 이 안에 Servlet을 가지고 있고 Servlet Container는 Servlet을 실행시킨다. 이 말은 JVM에서 Servlet을 실행한다는 이야기이다.

 

그렇다면 웹서버 동작시 일반 클래스와 Servlet 클래스의 실행 차이는 무엇이 있을까? Servlet으로 등록된 객체는 web.xml에 정보가 등록되어 있으며 Container가 web.xml을 읽고 해당 정보를 바탕으로 Servlet 클래스를 Class loader로 등록한다.



WAS 요청과 응답

이후의 과정은 많은 이들이 아는 기본적인 요청과 응답 처리 과정이다. Http 요청이 들어오면 Servlet Container는 HttpServletRequest, HttpServletResponse 두개의 객체를 생성하고 들어오는 요청의 Method 방식, Get, Post에 따라 doGet(),doPost()를 실행하여 동적 페이지를 생성하고 HttpServletResponse 객체를 통해 응답을 보낸다.

 

이후에는 Http 요청이 올때마다 기존 Servlet Instance를 이용한다. 하나의 Servlet Instance는 여러개의 Http 요청을 동시처리하는 것이다. Servlet Conatinaer가 사용되지 않는 Servlet을 제거할 때는 destroy 함수를 호출하면 JVM GC에서는 Servlet Instance를 해지할 수 있게 표시해두고 이 후 표시된 것을 해지한다.

 

Spring Container

spring의 container와 was container은 비슷한 기능을 한다. 다만 그 대상이 Servlet이냐 Spring Bean이냐의 차이이다. 처음 Spring을 공부하여 Container 용어를 배우고 이후 Was 에서 Container 용어를 들었을 때 두개가 같은 것이라고 착각하곤 했다.

 

Spring Container의 기능은 대표적은 Spring Bean의 생명 주기를 관리한다. 해당 기능을 위해 제어의 역전, 즉 IOC를 이용하는데 객체의 호출을 자발적으로 결정하는 것이 아닌 외부에서 결정되는 것이다.

 

그렇다면 개발자는 등록된 Spring Bean을 관리할 수 없을까? 아니다. Spring Container에는 또한 BeanFactory가 있고 ApplicationContext는 이를 상속하는데 두 컨테이너를 이용하여 주입된 Spring Bean들을 제어할 수 있다.

 

BeanFactory는 스프링 IOC 컨테이너 최상위 있는 인터페이스고 ApplicationContext는 이를 상속받으면서 실제 개발하는데 필요한 여러가지 기능을 덧붙인 확장판이라고 할 수 있다. 리소스 로딩, 다국어 지원, 이벤트, 등 여러가지 기능을 가지고 있기 때문에 우리가 실제로 사용하는 것은 ApplicatonContext일 것이다.

 

Web Application 실행시 WAS와 Spring Container

  1. web application이 실행되면 WAS에 의해 web.xml이 로딩되면서 등록 정보를 읽어 들인다.
  2. web.xml 등록 정보를 바탕으로 ContextLoaderListener가 Java Class로 생성되고 root-content.xml이나 ApplicatonContext.xml에 따라 ApplicationContext를 생성한다.
  3. Spring Bean이 등록되고 공유되는 ApplicationContext는 Servlet Container에 속한 Servlet이다.
  4. ApplicationContext.xml에 설정값에 따라 Spring Container가 구동되면서 Bean에 등록된 로직과 개발자가 작성한 여러 객체들이 생성된다.
  5. Spring의 Dispatcher Servlet이 생성되는데 이 객체 또한 Servlet Container의 Servlet이다. 모든 Client 요청이 Dispatcher Front Controller 패턴 특성을 가지고 있어 처음 요청이 들어오면 들어가는 Servlet Container에서 Dispatcher Servlet을 찾을 수 있다.
  6. Spring은 모든 요청이 들어와 Servlet Container로 들어오면 그 다음으로 Dispatcher Servlet으로 요청을 넘기라는 설정을 등록시켰다.
  7. Dispatcher Servlet은 요청이 들어오면 이에 적합한 Controller(Spring Bean)을 찾아 해당 요청을 넘긴다.
  8. 이 때 Hanlder Mapping, Adapter, View Resolver 등 Spring MVC 가 실행되어 우리가 개발하고 있는 형태로 개발만 하면 웹 사이트가 나오는 것이다.

즉 요약해보면 우리가 등록하는 Spring Bean은 실제 WAS Servlet Container에 등록된 Servlet은 아니라는 것이다. Servlet으로 등로고딘 DispatcherServlet을 통해서 WAS를 이용해 HTTP 양방향 통신이 가능한셈이다. Spring Bean은 Servlet이기 보다는 Spring Container에 올려서 마치 서블릿처럼 사용 가능한 것이다.

( 이 부분은 확실치 않음, 조금 공부하고 수정하겠음)



Bean 의 생성 과정

ComponentScan → Bean initialize → getBean 순서로 Bean이 생성된다.



결론

결국 WAS의 핵심적인 부분은 Servlet인데 Servlet은 tomcat이 관리하는 WebServlet과 Spring Bean이 있다. Spring을 사용하는 것은 Spring을 이용해 Servlet을 다루겠다는 것이다.

아직도 헷갈리는 부분이 많다. 좀 더 공부를 해야겠다..