목차
● 1. 웹 서버 & 웹 애플리케이션 서버
● 2. 스프링부트 환경 내장 서버
● 1. 웹 서버란?
1. 웹 서버 & 웹 애플리케이션 서버
웹 서버란?
1. HTTP 프로토콜을 기반으로 클라이언트의 요청을 받아주고 정적인 리소스를 제공해주는 역할을 한다.
2. 정확히는, 정적 리소스를 WAS에서 받아서 해석해서 클라이언트에게 전달해주는 역할을 한다.
웹 애플리케이션 서버란?
1. 웹 서버가 존재해야지만 존재할 수 있는 서버이며, 동적인 컨텐츠를 제공해주기 위해 존재하는 서버이다.
2. 동적인 컨텐츠로는 DB조회나 다양한 로직 처리 및 계산 등 처리를 해서 웹 서버로 전달을 해주는 역할을 한다.
2. 스프링부트 환경 내장 서버
스프링부트는 웹 서버 & 웹 애플리케이션 서버를 합친 서버를 내장 서버인 "아파치 톰캣"을 가지고 있다.
아파치 제단에서 만든 아파치 웹서버의 기술과 톰캣이라는 웹 애플리케이션 서버의 기술을 통합해서 만들어진 서버이다.
implementation 'org.springframework.boot:spring-boot-starter-web'
스프링부트 환경에서 기본적으로 위의 코드와 같이 web 의존성을 추가하게되면 자동적으로 아파치 톰캣의 라이브러리를 받아올 수 있다. 즉, 아파치 톰캣 내장서버는 자바코드인 것이다.
Tomcat의 구성 요소
1. ServletContainer
Tomcat의 핵심 구성 요소 중 하나로, 서블릿을 로드하고 실행하며, 서블릿 생명 주기를 관리합니다.
이는 HTTP 요청을 서블릿으로 라우팅하고, 서블릿의 응답을 클라이언트에게 반환합니다.
2. Catalina ( ServletContainer 구현체 => Coyote를 포함하고있는 핵심 구현체 )
Tomcat의 ServletContainer 구현체로, 실제로 서블릿 API를 구현하고 요청을 처리하는 역할을 해주며 Coyote를 포함하고있다. Catalina는 서블릿 생명 주기를 관리하고 서블릿에 대한 요청을 처리하는 하나의 모듈이다.
( 구체적으로는 서블릿의 초기화, 호출, 소멸 등을 관리하게 된다. )
2-1. Thread Pool 관리
Thread Pool의 관리를 하고 각각의 요청을 처리할 스레드를 할당을 해준다.
2-2. HTTP 요청 파싱
세부적인 역할로는 HTTP 요청에 대한 요청 메시지를 파싱하여 요청 라인, 헤더, 본문 등의 정보를 추출한 뒤 요청 객체인 Request객체와 Response객체를 생성하여 요청에 대한 정보를 담게 된다. 이때 들어가는 데이터로는 HTTP 메서드의 정보, 요청 URL, 헤더 정보, 본문 데이터 등의 정보가 저장이 된다.
또한, 응답에 대해서도 요청을 처리하고 생성된 HTTP 응답 객체인 Response객체를 받아 응답 객체의 정보를 바탕으로 HTTP 응답 메시지를 생성한 뒤 클라이언트에게 응답 메시지를 전송하는 방식이다.
그 외에도 Servlet 매핑, HTTP 응답의 구성을 수행해준다.
3. Coyote ( ServletContainer 구현체 )
Coyote는 네트워크 수준에서의 처리를 담당하며, 서블릿 컨테이너에게 들어오는 요청을 중계하여 처리를 한다.
쉽게 말해, HTTP 프로토콜을 처리하는 커넥터로, 클라이언트로부터의 요청을 받아들이고, 응답을 보내는 역할을 한다.
4. Jasper ( ServletContainer 구현체 )
JSP 파일을 처리하는 컴포넌트로, JSP를 서블릿으로 변환하고 실행합니다.
5. Thread Pool
Tomcat은 내부적으로 쓰레드 풀을 사용하여 요청 처리를 효율화합니다. 각 HTTP 요청은 쓰레드 풀에서 쓰레드를 할당받아 처리되며, 이를 통해 자원 관리와 성능 최적화를 도모합니다.
* 해당 쓰레드 풀을 관리하는건 Catalina이다.
서블릿 프로젝트 동작 방식
1. 서블릿 프로젝트 서버 구동 시점
아파치 톰캣의 서버를 이용하여 서버를 구동 시키게 되면 ServletContainer가 뜨게 되고 해당 서블릿 컨테이너 내부에는
기존의 설정 파일로 정의를 해둔 Servlet이나 애너테이션으로 정의해둔 Servlet이 존재할 경우 해당 Servlet을 init() 메서드를 통해 ServletContainer에 싱글톤 인스턴스로 보관을 하게된다.
이 부분에서는 xml방식과 애너테이션의 방식의 차이가 존재한다.
* xml방식
서버의 구동 시점에 xml 설정 파일의 정보를 읽어 해당 api 엔드포인트와 Servlet의 클래스명을 기재함으로써 서버가 뜰 때
바로 ServletContainer에 해당 Servlet을 싱글톤 인스턴스로 보관하게 된다.
* 애너테이션 방식
서버의 구동이 끝나고 요청이 들어와 해당 Servlet에 엔드포인트를 애너테이션 메타 데이터를 읽어 찾게 된다.
찾게되면 해당 애너테이션은 그때 해당 서블릿을 init()메서드를 통해 ServletContainer에 싱글톤 인스턴스로 만들어져
보관이 되게 된다. ( 즉, 런타임 시점에 요청에대해 동적으로 만들어져서 Container에 등록이 되는 것이다. )
이 처럼 기존 서블릿 프로젝트 방식은 api에 대한 요청을 각각의 서블릿이 처리를 해주어 Servlet의 코드에 대한 중복이 많이 발생하였다. ( 해당 문제점을 보완하여 만들어진 것이 SpringWebMVC이다. )
2. 서블릿 프로젝트 요청 시점
요청이 들어오면 아파치 톰캣은 클라이언트로부터의 요청을 받아 ServletContainer의 내부의 Thread Pool에서 요청을 처리해 줄 Thread 1개를 할당을 받아 요청에 알맞는 Servlet(Container 싱글톤 인스턴스)을 찾아 request와 response 데이터를 넣어 요청을 처리하게 된다.
스프링 프로젝트 동작 방식 ( Spring Web MVC )
스프링은 DispatcherServlet을 통해 위의 서블릿 프로젝트의 과정을 보완하고있다.
기존 서블릿 방식의 동작 방식과 비슷하게 아파치 톰캣 서버가 뜰 때 해당 DispatcherServlet을 ServletContainer에
init() 메서드를 통해 싱글톤 인스턴스로 보관을 하게 된다.
요청이 들어올 때 해당 요청을 단순하게 DispatcherServlet을 통해 api 엔드포인트에 맞는 Controller를 찾아 매핑을 하게 된다. Controller를 찾아 요청을 처리하고 응답 데이터를 만들어 반환하는 방식이다.
기존 서블릿 프로젝트 방식과 다르게 하나의 서블릿을 통해 api 요청을 처리하고 있는 방식이다.
1. 스프링부트 서버가 켜질 때 시점
스프링부트를 처음 실행하게 되면 내부서버의 동작으로 ServletContainer가 뜨게 되고 ServletContainer 구현체인 Coyote 커넥터를 이용해 네트워크 소켓을 열어 클라이언트로부터의 HTTP 요청을 대기하게 된다.
// 그 후 ServletContainer는 내부적으로 DispatcherServlet 객체를 싱글톤으로 하나를 생성해서 가지고 있게 된다.
( SpringContainer가 Bean을 등록하여 싱글톤으로 하나만 가지고 있다는 것과 두개 다 Container라는 점을 볼 때 해당 프로젝트에서 쓰이는 Container라는건 객체를 한개만 가지는 싱글톤 컨테이너라고 볼 수 있다. )
2. 스프링부트 서버에 요청이 들어올 때
1. 웹으로부터 요청이 들어오게 되면 스프링부트 내장 서버인 아파치톰캣이 요청을 수신하게 된다.
2. 내부에 가지고있는 Thread Pool에서 요청을 처리할 수 있는 사용 가능한 스레드를 할당을 받아 요청을 처리하게 됨
3. 넘어온 HTTP 요청 메시지를 ServletContainer가 파싱하여 HTTP 메서드, URL, 헤더, 바디 등의 정보를 추출하여 HttpServletRequest와 HttpServletResponse객체에 담게 된다.
4. 이후 service() 메서드에 request와 response를 넣어 호출을 하게되면 DispatcherServlet이 호출이 된다.
5. DispacherServlet은 받아온 request와 response에 대해 요청을 처리 후 ServletContainer에 역순으로 보내주게 된다.
6. 응답은 다시 ServletContainer가 받아 HTTP 메시지로 만들어 응답을 다시 보내주게 된다.
* 애너테이션 방식
위의 서블릿의 애너테이션 방식과 동일하게 Spring은 애너테이션 방식을 이용하여 DispatcherServlet을 요청 시점에 생성을 해서 싱글톤으로 가지게된다. ( 서버 구동 시점에 만들어주는 것이 아니다!! )
위의 요청과정에서 4번의 첫 요청이라면 init() 메서드를 호출하여 DispatcherServlet을 싱글톤 객체로 보관을
스프링부트의 서블릿은 애너테이션 방식을 사용한다.
여기서 4번의 과정에서 서버가 뜬 후 처음 요청이 오는 것이라면 애너테이션방식을 이용하여 서블릿을 요청시점에 DispathcerServlet을 싱글톤 인스턴스로 등록을 하게 된다.
( 여기서 쓰이는 애너테이션은 런타임시점에 동적으로 객체를 만들어주는 역할을 하는 것이다. )
정리
아파치 톰캣 내부에는 Thread Pool이 존재하며 Thread Pool에 미리 정의해둔 Thread 갯수만큼 가지고 있는다. 요청이 하나 들어오게 되면 Thread Pool에서 Thread하나를 Pool에서 가지고와 요청 처리를 시작하게 된다. 그 후, 요청에 대한 HTTP 메시지를 ServletContainer가 파싱을 하여 HttpServletRequest, HttpServletResponse 객체를 생성하여 파싱 된
요청에대한 정보를 담고 DispatcherServlet객체가 컨테이너 내부에 생성이 되어있다면 service() 메서드에 요청과 응답 정보를 넣어 DispatcherServlet에 전달을 해주고, 만약 생성이 안되어있다면 생성을 하여 싱글톤 인스턴스로 만들어 요청을 처리하게 된다.
# 오늘의 회고
톰캣과 서블릿을 깊게 들어가서 공부를 하게 되었고 많은 것을 이해할 수 있었다.
특히 웹 서버와 웹 애플리케이션 서버의 개념을 명확하게 이해할 수 있었다.
스프링의 서블릿과 레거시한 서블릿 프로젝트를 비교하며 두개의 차이도 알 수 있었다.
XML 방식과 애너테이션의 방식 두 가지의 설정에 대한 정보를 알 수 있었다.