5주차 위클리페이퍼 주제이다.
- 웹 서버(Web Server)와 WAS(Web Application Server)의 차이를 설명하고, Spring Boot의 내장 톰캣이 이 둘 중 어디에 해당하는지 설명해주세요.
- Spring Boot에서 사용되는 다양한 Bean 등록 방법들에 대해 설명하고, 각각의 장단점을 비교하세요.
현재 포스팅은 첫 번째 주제에 해당한다.
웹 서버란 HTTP 프로토콜을 통해 인터넷을 사용하는 사용자에게 요청을 받아 응답(웹 서비스)을 제공하기 위한 서버이다. 우리가 인터넷을 사용하면 가장 먼저 하는 일이 무엇인가? 바로 주소를 입력하는 것이다. 바로가기 아이콘을 눌러서든 직접 URL을 입력하든 주소창에 우리가 이용할 웹사이트의 주소를 입력한다.
예를 들어 네이버를 생각해보자. www.naver.com 또는 m.naver.com 이라는 주소를 입력하면 우리는 네이버의 홈페이지를 볼 수 있다. 이 과정을 살펴보면 주소창에 위의 주소를 입력하는 것이 네이버 회사에 웹 서비스 제공을 요청한 것이다.이 요청을 받은 네이버는 웹 서버를 통해서 HTML, CSS, 이미지 등으로 이루어진 홈화면을 우리의 브라우저에 응답(전달) 해 주는 것이다.
이를 좀 더 이해하기 쉽게 식당에 비유해보겠다.
먼저 식당에 손님이 들어와서 메뉴판을 본다. 메뉴판에 있는 메뉴는 주소창에 입력할 수 있는 URL이라고 볼 수 있다.
손님이 메뉴판에 있는 치즈버거를 보고 "저기요, 치즈버거 하나 주세요"라고 주문을 하면 이 행위가 주소창에 URL을 입력해 HTTP 요청을 한 것이다. 그러면 주문을 받은 점원이 주방에 전달하고, 주방은 만들어진 치즈버거를 우리에게 갖다주는데 점원이 웹 서버고, 치즈버거가 바로 요청한 웹페이지이다.
그런데 여기서 만약에 치즈버거가 편의점 햄버거라고 가정해보자. 우리는 버거에 토핑을 추가하거나 뺼 수 없다. 그래서 패티가 2개 있는 햄버거가 먹고싶으면 아예 다른 햄버거를 사야한다. 만약 맥도날드였다면? 우리는 그냥 키오스크에서 주문할 때 패티 추가 또는 토마토 제외 버튼을 누르면 동일한 버거에서 내용물을 바꿔 끼울 수 있는 것이다.
이 예시에서 이미 만들어진 편의점 버거가 정적 콘텐츠이고, 맥도날드 버거가 동적 콘텐츠이다.
웹사이트의 일부가 수정될 때(가령, 마이페이지에서 사용자의 닉네임만 다르게 표시해야될 때) 정적인 웹 페이지로 관리해야한다면 모든 사용자의 개수만큼 정적 콘텐츠를 가지고 있어야한다. 이때, 만약 위에서 언급한 맥도날드 버거처럼 내용물을 갈아끼울 수 있다면? 하나의 마이페이지에서 닉네임만 갈아끼우면 되는 것이다. 이것이 바로 동적 컨텐츠의 필요성이다. 이러한 동적 컨텐츠를 사용할 수 있는 것이 바로 WAS(Web Application Server)이다.
이제 WS와 WAS를 어느정도 알았으니 각 특징을 한눈에 비교해보자
Web Server (WS)
- 핵심 역할: 정적(Static) 콘텐츠 처리 (HTML, CSS, 이미지 등)
- 특징: 비즈니스 로직 수행 없이 파일 시스템에 있는 파일을 그대로 클라이언트에게 전달합니다.
- 대표 주자: Nginx, Apache HTTP Server
- 키워드: Event-Driven, Zero Copy
WAS (Web Application Server)
- 핵심 역할: 동적(Dynamic) 콘텐츠 처리 (DB 조회, 비즈니스 로직 수행)
- 특징: 사용자 요청에 따라 데이터를 가공하고, 코드를 실행하여 결과를 만들어냅니다.
- 대표 주자: Apache Tomcat, Jetty
- 키워드: Servlet Container, Thread Pool
자 이제 Spring Boot의 내장 톰캣이 이중에서 어디에 해당하는지 알아보자.
사실 톰캣이 서블릿 컨테이너라는 것을 알면 바로 정답을 알 수 있다. 서블릿 컨테이너는 자바에서 서블릿을 이용하여 동적 웹 페이지를 생성할 때 사용하는 프로그램이기 때문이다. 즉 Spring Boot의 톰캣은 자바의 서블릿 컴포넌트를 사용해서 동적 콘텐츠를 처리할 수 있는 서블릿 컨테이너이며 일종의 WAS인 것이다.
그런데 여기서 한가지 재밌는 사실이 있다. 톰캣은 정적인 컨텐츠를 서빙할 수 있는 DefaultServlet이 있고 동적인 컨텐츠를 서빙할 수 있는 DispatcherServlet이 있다. 그렇다면 WAS 단독으로 정적 컨텐츠, 동적 컨텐츠 모두 처리할 수 있는데 실제로는 WAS 앞에 WS를 사용한다. 그 이유가 뭘까?
전통적은 서블릿 기반(Spring MVC 등)의 블로킹 I/O 모델에서는 WAS는 어떠한 요청이든 요청 1개에 하나의 스레드를 배정한다. 그래서 정적 컨텐츠든 동적 컨텐츠이든 요청이 들어오면 무조건 스레드 하나를 점유하게 된다. 여기서 문제가 발생한다. 만약 용량이 큰 파일을 다운로드 받거나, 네트워크가 느린 사용자가 다운로드를 하면 그 사용자의 다운로드가 전부 끝날때까지 그 스레드는 아무런 연산을 하지 않은 채, 연결을 유지하며 Blocking 상태로 대기하게 된다.
결국 이러한 상황이 반복되어 Thread Pool의 모든 스레드가 대기 상태에 빠지게 되면 정작 비즈니스 로직이나 DB조회 등의 요청을 WAS가 응답할 수 없게 된다. 이것이 스레드 기아이며 이 상태에서는 요청이 큐에서 대기하거나, 최대치에 도달하면 요청이 거부될 수 있다. 반면 Nginx같은 WS는 Event-driven 방식을 사용하기 때문에 적은 수의 worker process가 event loop 기반으로 다수의 연결을 비동기로 유지할 수 있으며 정적 파일을 전송할 때 Zero Copy기술로 데이터 복사에 필요한 CPU 사용과 컨텍스트 스위칭을 최소화 한채 파일을 전송할 수 있다.
이것을 식당에 비유하면 다음과 같다.
WAS의 스레드는 요리사이다. 음식을 만드느라(비즈니스 로직 처리) 굉장히 바쁘다. 이때 만약 서빙하는 직원이 없다면(Nginx와 같은 WS) 주문을 받으러 가는 것도 요리사이고 서빙을 해야 하는 것도 요리사이다. 이런 상황에서 말을 굉장히 느리게 주문을 하는 사람이 있고, 또는 주문 자체가 굉장히 많다면? 아니면 서빙을 하기에 굉장히 무거워서 천천히 옮겨야 하는 음식이 있다면? 요리사는 주문과 서빙을 하느라 정작 요리를 못하게 될 것이다. 그래서 서빙하는 직원(WS)가 필요한 것이다.