웹 애플리케이션 톺아보기
백엔드 개발은 오랜 시간을 거쳐 발전해왔다. 개발하면서 반복되는 일은 자동화하고 유지보수에 용이하도록 추상화해오면서 발전된 지금의 기술은 개발자의 많은 고민을 덜어준다.
그렇기에 내가 쓰고 있는 기능들이 어떤 방식으로 편리함을 제공하는지 몰라도 개발을 할 수 있다.
하지만 그런 이해의 부재는 좋은 도구를 100% 활용하지 못하게 만든다.
토비의 스프링 3.1 VOL. 1 43쪽 발췌문
스프링의 가치를 제대로 누리며 사용하려면 스프링을 제대로 공부해야 한다. 여타 프레임워크도 그렇겠지만 스프링은 특히 프레임워크가 지향하는 가치와 프로그래밍 모델을 이해하지 못하고는 제대로 활용할 수 없다. 반면에 한번 스프링의 원리와 개발 사상을 확실히 이해하고 나면 그 이후에 새로운 기능이 아물 많이 추가되더라도 빠르고 쉽게 학습하고 사용할 수 있다.
어떤 언어를 사용하건 어떤 프레임워크를 선택하건 백엔드는 결국 클라이언트의 요청에 응답하는 서버를 개발한다. 그렇기 때문에 백엔드 개발을 어떻게 할 수 있는지 이해하려면 웹 애플리케이션을 먼저 이해해야 한다.
이번 글에서는 과거부터 오랫동안 웹 개발에 쓰이며 발전해온 스프링 MVC Framework의 기반을 곁들여 웹 애플리케이션을 이해해보도록 하자.
INDEX
- 웹 서버, 웹 애플리케이션
- 서블릿
- 동시 요청 - 멀티 쓰레드
1. 웹 서버, 웹 애플리케이션
1-1. Web server
웹 서버는 클라이언트가 브라우저 주소창에 URL을 입력해 어떤 웹 페이지를 요청하면 HTTP 요청을 해석해 HTML 문서와 같은 정적인 컨텐츠를 클라이언트에게 전달하는 역할을 한다.
웹 서버의 대표적인 임무는 2가지로
- 단순히 저장된 웹 리소스들을 클라이언트에게 전달하거나, 클라이언트로부터 콘텐츠를 전달받아 저장한다.
- 클라이언로부터 동적인 요청을 받게되면, 해당 요청을 WAS에게 전달한다.
1-2. WAS(Web Application Server)
WAS도 웹 서버와 동일하게 HTTP 기반으로 동작한다. 웹 서버가 할 수 있는 대부분의 기능을 WAS도 갖고 있으며, 비즈니스 로직(서버사이드 코드) 처리를 맡고 있어 클라이언트에게 동적인 컨텐츠를 전달하는 역할을 갖는다. 주로 데이터베이스 서버와 함께 수행된다.
1-3. Web server와 WAS의 차이점
웹 서버는 정적인 데이터를 처리한다. 이미지나 단순 HTML 같은 정적인 리소스들을 전달하며, WAS만을 이용할 때보다 빠르고 안정적으로 기능을 수행한다.
반면에 WAS는 동적인 데이터를 위주로 처리한다. DB와 연결되어 필요한 데이터를 전달하거나, 애플리케이션 로직을 수행한다.
1-4. 구분 짓는 이유
웹 서버와 WAS에 대해 알고나니, 웹 서버의 기능 대부분을 WAS도 갖고 있으니, WAS만으로도 충분할 것 같다는 생각이 든다.
하지만 WAS는 DB 조회를 포함한 다양한 비즈니스 로직을 수행하는데에 집중되어야 한다. 단순한 정적 컨텐츠 처리는 웹 서버에게 맡겨서, 역할을 분리해야한다. 둘로 나누어 역할을 분리시키지 않으면, 서버 과부하에 취약해진다.
그리고 만약 WAS가 정적 콘텐츠 요청까지 처리하게 된다면, 부하가 커지고 동적 컨텐츠 처리가 지연되면서 수행 속도가 느려지게 된다.
그만큼 WAS에는 장애가 발생하기 쉬운데, 정적 리소스만 제공하는 웹 서버를 함께 운영하면 WAS 측에 장애가 생겼을 때 오류 화면을 안정적으로 제공할 수 있다.
게다가 웹 서버와 WAS로 서버를 구분 지으면 효율적인 리소스 관리가 가능해진다.
정적인 리소스가 많이 사용되면 Web 서버만 증설하면 되고,
애플리케이션 리소스가 많이 사용되면 WAS만 증설하면 된다.
2. 서블릿
WAS는 HTTP 요청 메세지를 해석해서 업무를 처리하고 응답 메세지를 보내준다. POST 메세지가 왔다고 하면 아래의 처리과정을 수행할 수 있어야 한다.
- 서버 TCP/IP 연결 대기, 소켓 연결
- HTTP 요청 메세지를 파싱해서 읽기
- POST 방식, /save URL 인지
- Content-Type 확인
- HTTP 메세지 바디 내용 파싱
- 저장 프로세스 실행
- 비즈니스 로직 실행
- 데이터베이스 저장 요청
- HTTP 응답 메세지 생성 시작
- HTTP 시작 라인 생성
- Header 생성
- 메세지 바디에 HTML 생성
- TCP/IP에 응답 전달, 소켓 종료
백엔드 개발자가 만들어야 할 처리 프로세스가 한 두개가 아닌데, 매번 이 모든 걸 만드는 것은 에너지 낭비다. 핵심 비즈니스 로직만 빼내면 반복 작업을 해야하기 때문이다.
지금 들은 예시에선 POST 메세지를 받고 있는데, 비즈니스 로직을 실행하는 부분만 빼면 POST 메세지를 받았을 때 서버가 처리할 프로세스는 다 똑같다.
서블릿은 이 똑같은 프로세스를 대신해주는 도구다.
2-1. 서블릿을 사용하기
WAS는 HTTP 요청을 받으면 Request와 Response 객체를 새로 만들어서 서블릿 객체를 호출한다.
개발자는 Request 객체에서 HTTP 요청 정보를 편리하게 꺼내서 비즈니스 로직에 사용한 후에 응답 정보를 Response 객체에 편리하게 입력해서 반환하면 WAS가 HTTP 응답 메세지를 만들어서 웹 브라우저에 전달해준다.
2-2. 서블릿 컨테이너
이런 기특한 서블릿의 생성, 초기화, 호출, 생명주기 관리까지 개발자가 매번 작업할 필요없게 도맡아주는 서블릿 컨테이너라는 게 있다.
톰캣처럼 서블릿을 지원하는 WAS가 서블릿 컨테이너이다. 서블릿 컨테이너는 아래와 같은 특징이 있다.
- 매번 동일한 서블릿을 생성하지 않고 싱글톤으로 관리해준다. 그렇기 때문에 공유 변수를 사용할 땐 주의해야 한다.
- 멀티 쓰레드 처리가 지원되어 동시 요청도 처리할 수 있다.
JSP도 서블릿으로 변환되어 사용된다.
3. 쓰레드
WAS에서 HTTP 요청을 받게되면 서블릿 객체를 호출한다는 걸 알아봤다. 그 서블릿 객체를 가져오는걸 쓰레드가 한다.
백엔드 개발자는 많은 트래픽을 처리할 수 있도록 설계하기 위해 어떻게 쓰레드 전략을 세울 수 있을까?
두 가지 방법이 있다.
3-1. 요청마다 쓰레드 생성
장점
동시 요청을 처리할 수 있다.
리소스(CPU, 메모리)가 허용할 때까지 처리 가능
하나의 쓰레드가 지연되어도, 나머지 쓰레드는 정상 동작한다.
단점
- 쓰레드의 생성 비용은 매우 비싸다.
- 그러므로 고객의 요청이 올 때마다 쓰레드를 생성하면, 응답 속도가 늦어진다.
- 쓰레드는 컨텍스트 스위칭 비용이 발생한다.
- 쓰레드 생성에 제한이 없어서 고객 요청이 너무 많이오면, CPU나 메모리 임계점을 넘어 서버가 죽을 수 있다.
- 쓰레드의 생성 비용은 매우 비싸다.
이런 치명적인 단점이 있어 WAS는 보통 Thread Pool을 사용한다.
3-2. 쓰레드 풀
특징
필요한 쓰레드를 쓰레드 풀에 보관하고 관리한다.
쓰레드 풀에 생성 가능한 쓰레드의 최대치를 관리한다. 톰캣은 최대 200개 기본 설정(변경 가능).
사용
쓰레드가 필요하면, 이미 생성되어 있는 쓰레드를 쓰레드 풀에서 꺼내 사용한다.
사용을 종료하면 쓰레드 풀에 해당 쓰레드를 반납한다.
최대 갯수의 쓰레드가 모두 사용 중이라서 쓰레드 풀에 쓰레드가 없으면?
- 기다리는 요청은 거절하거나 특정 숫자만큼만 대기하도록 설정할 수 있다.
장점
쓰레드가 미리 생성되어 있으므로, 쓰레드를 생성하고 종료하는 비용(CPU)이 절약되고, 응답 시간이 빠르다.
생성 가능한 쓰레드의 최대치가 있으므로 너무 많은 요청이 들어와도 기존 요청은 안전하게 처리할 수 있다.
(1) Tip
WAS의 주요 튜닝 포인트는 최대 쓰레드(max thread) 수 이다.
이 값을 너무 낮게 설정하면 동시 요청이 많을 때 서버 리소스는 여유롭지만 클라이언트는 쉽게 응답 지연을 겪게 된다.
반대로 이 값을 너무 높게 설정하면 동시 요청이 많을 때 CPU나 메모리 리소스 임계점 초과로 서버가 다운된다.
장애 발생시에 클라우드로 서버를 운영하고 있다면 일단 서버부터 늘린 후, 튜닝하고 클라우드가 아니면 열심히 튜닝하자.
쓰레드 풀의 적정 갯수는,
애플리케이션 로직의 복잡도, 서버의 CPU, 메모리, IO 리소스 등 여러 상황을 고려해야 한다. 그렇기 때문에 성능 테스트를 통해 정하는 걸 권장한다.
테스트는 최대한 실제 서비스와 비슷한 환경에서 시도하자. 테스트 툴로는 아파치 ab, 제이미터, nGrinder가 있다.
3-3. WAS의 멀티 쓰레드 지원
개발자는 멀티 쓰레드 처리에 대한 많은 고민을 내려놓아도 괜찮다.
왜냐하면 WAS가 멀티 쓰레드에 대한 부분을 처리할 수 있게 개발되어 있기 때문이다. 하지만 지원되는 기능이 모든 환경을 다 커버할 수는 없기 때문에 개발자 본인이 필요에 따라 쓰레드 처리를 할 수 있어야 겠다.