서버 근본 : Web Server와 WAS - Before You Study Spring (1)

강지혁·2022년 8월 16일
1

Before You Study Spring

목록 보기
2/3

오늘날의 웹 프레임워크들은 사용하기 너무 편해서, 기저에서 무슨 일이 일어나는 지 알고자 하지 않으면 사실상 모르고도 개발이 가능하다.

그러나 처음부터 그 흐름을 잘 이해하고 공부하는 편이 훨씬 낫다고 생각한다.
필자는 일단 돌아가는 앱을 만들면 만족하는 식으로 공부를 시작했었는데, 돌이켜보면 프레임워크의 기능을 익힐 때 붕 뜨는 느낌이 든 적이 많았다. 큰 그림에 대해 이해하고 있지 않으니, 반쪽짜리 시야와 사고에서 출발하게 되는 것이다.

세상에 마법은 없다는 것을 알고, 서버는 무엇을 하는 것인지, 서버를 작동시키기 위해 무엇이 필요한 지 제대로 이해해두어야 한다. 그렇게 하면 웹 프레임워크들이 (대체로 닮아있는) 제각각의 방식으로 어떻게 애플리케이션 서버 구축을 돕는지 이해가 훨씬 빠를거고, 개발자로서 성장하는 속도도 분명 더 빠를거다.

말하자면 근본 같은 것..
웹 애플리케이션 서버 개발의 근본을 한번 정리해보았다.

Web Server

html 기반의 정적 콘텐츠만 서빙하던 시절이 있었다는 것은 아주 유명하다.
정적 컨텐츠 서빙을 수행할 수 있으면 웹 서버다.

(웹 서버 = 정적 컨텐츠 서빙만을 수행하는 서버 ❌ 가 아니고, 초기 웹 서버들이 정적 컨텐츠 서빙만을 수행할 수 있는 Static Web Server들이었다고 표현하는 것이 적절한 것 같다. 아래 mozilla 사진 참고)

대표적인 웹 서버로는 Apache, NginX 등이 있다.

모두가 알고 있듯이, 이후 동적으로 파일을 서빙할 필요가 생겼고, 이를 수행하기 위해 정적 웹 서버들에 웹 애플리케이션 서버 (WAS)라는 것이 등장하게 된다.

Web Application Server

웹 애플리케이션 서버는 앞서 언급한 대로 동적 파일 서빙이 가능한 서버다.
WAS는 독립적으로도 운용 가능하고, 웹 서버와 붙어서, 웹 서버가 처리 못하는 동적인 요청을 다루어주는 방식으로도 운용 가능하다. (이러한 역할 분담에 대한 설명은 뒤에서 더 자세히 다루겠다.)

어떻게 동적으로 파일을 전달할까?

어떻게 전달하겠어.. 프로그램으로 전달하지. 당연한 이야기지만,
php든, 파이썬이든, 자바든,, 뭐든 써서 데이터를 만들어내면 된다.

CGI (Common Gateway Interface)

그런데 말처럼 호락호락하지만은 않았던 것 같다.
기존의 정적인 웹 서버 체계는 리눅스와 C언어를 기반으로 구축되어있었는데, 동적 파일 서빙을 위한 애플리케이션 프로그램은 개발자들마다 만들기 나름이었을 테다.

따라서 웹 서버 <=> 애플리케이션 프로그램 간의 표준 인터페이스가 필요했고, 가장 먼저 CGI가 탄생했다. 웹 서버가 필요한 프로그램을 호출하는 원시적인 인터페이스다.

CGI를 통해 웹 서버는 스크립트 파일 (.py, .php)이나 기계어로 컴파일된 프로그램을 실행하고, 실행 결과를 받아오는 표준 방식을 가질 수 있게 되었다.

그런데 이렇게 외부 프로세스를 하나 띄워서 실행 결과를 받아오는 방식에는 한계가 아주 많다.

  1. 요청 하나 당 프로세스가 하나 생긴다 => 오버헤드가 장난 아닐 수 밖에 없다.
  2. 데이터 캐싱이 불가능하다.
  3. Java에서는 사용이 거의 불가능하다.
    • 자바의 .class 파일은 바이트코드로 컴파일되고, JVM이 기계어로 번역한다.
    • 그런데 JVM은 OS에 묶여있기도 하고, 아주아주 무겁다. (매번 JVM 재부팅...?!)
    • (성능 문제 말고도 JVM으로 인한 다양한 장애물들이 있는데, 자세한 내용은 링크를 참고하면 될 것 같다.)

웹 컨테이너, WSGI의 등장

앞서 설명한 것보다 훨씬 많은 이유로, CGI는 개선될 필요가 있었다.
어떻게 개선할 수 있을지 3초 정도 고민해보자..

아래와 같은 답을 생각해볼 수 있을 것이다.

프로세스를 매번 띄우는 대신, 미리 프로세스를 하나 띄워두면 어떨까?

그리고 실제로 cgi는 이렇게 개선되었다.
이 때 미리 떠있는 프로세스 == 우리가 구동하고 싶은 애플리케이션과 쉽게 호환이 되는 서버 라고 생각하면 될 것 같다.

wsgi

파이썬 진영에서는 wsgi라는 인터페이스가 등장하고 그 구현체로 uwsgi, gunicorn 등이 등장하면서 훨씬 효율적인 동적 서버 서빙이 가능해졌다. (장고와 같은 웹 애플리케이션을 만들면, uwsgi랑 붙여서 WAS를 운용하게 됨)

Servlet & Web Container

자바 진영에서도 문제를 비슷하게 해결한다.
애초에 cgi로 자바 프로그램을 돌릴 필요 없이, HTTP request를 그대로 자바 프로세스 안에서 다 처리하는 것이다.

자바에서는 Servlet & Web Container를 통해 이러한 목표를 달성했다.

서블릿은 HTTP request & response를 Java Class로 표현한 하나의 객체다.
각 요청에 대한 처리는 곧 하나의 서블릿에 대한 처리로 치환된다.

GCI vs Servlet 글을 보면, CGI 방식과의 비교를 통해Servlet을 통한 자바 진영의 HTTP 요청 대응을 잘 이해할 수 있다.
주요한 차이점은 아래와 같이 정리할 수 있을 것 같다.

요청을 처리하는 단위HTTP Request생명주기
Servlet스레드알아듣고 처리 가능destroy()를 통해 명시적으로 제거
CGI프로세스읽을 줄 모름요청을 처리하는 즉시 소멸

Servlet은 누가 처리하나요 - Servlet Container (Web Container)

서블릿은 POJO다. 따라서 외부의 요청을 누군가 듣고, 대응되는 Servlet 클래스를 만들어서, 필요한 처리를 따로 취해주어야 한다. (Servlet은 능동적으로 무언가를 하는 친구가 아니다.) Oracle에서 제공하는 서블릿의 처리 명세만이 존재할 뿐이다.

해당 액션은 서블릿 컨테이너들에 의해 수행된다.
서블릿 컨테이너는 JVM 위에서 동작하며, 서블릿을 바탕으로 요청을 처리하기 위한 다양한 클래스들이 미리 로드되어있는 프로세스다.

아주 대표적인 서블릿 컨테이너로 Apache Tomcat이 존재한다.
여기서 간단한 서블릿을 톰캣에 로드해서 WAS를 구동하는 예제를 해볼 수 있다.

우리가 사용하는 스프링 MVC 아키텍처 역시 서블릿 컨테이너 위에서 동작한다.
DispatcherServlet이 톰캣의 Servlet으로 등록되고, 우리가 작성한 로직에 의해 요청이 처리된다.

(서블릿과 스프링의 DispatcherServlet에 대한 추가 설명은 별도의 포스팅으로 분리하는게 나을 것 같다.)


여기까지 이해하면, 웹 서버가 뭔지, WAS는 뭔지, Django & Spring 생태계에서는 어떤 방식으로 이들을 처리하는지 이해할 수 있다.

아래 이미지가 아주 적절한 것 같아서 긁어와봤다.

** Tomcat은 wsgi와 다르게, 웹 서버가 안에 내장되어있다. 따라서 사실 굳이 앞에 웹 서버를 둘 필요가 없다. 다만 무중단 배포나, 리버스 프록시의 다양한 활용을 위한 use-case가 존재하긴 하는 것 같다.


그러면 왜 WAS 앞에 웹서버를 두나요?

동적 파일 서빙이 가능하면, 정적 파일 서빙도 당연히 될 것이다.
그렇기 때문에, 사실 wsgi 서버나 servlet container만 돌려도 요청을 처리하는 '서버'를 구동하는 데에는 문제가 없다.
그런데 왜 우리는 여전히 NginX, Apache httpd 같은 웹 서버를 같이 돌릴까?

  1. 동적 파일 서빙과 정적 파일 서빙 분리
    • 인터넷 돌아다니면 가끔 보이는데,
    • 애초에 wsgi는 말이 안되고, 톰캣은 5.5부터 웹 서버가 내장되어있어서 따로 분리해서 구성하지 않아도 비슷한 성능을 보인다.
  2. 로드밸런싱
    • NginX의 리버스 프록시를 잘 써서, 로드 밸런싱을 한다거나 하는 효과를 기대할 수 있다.
  3. SSL, 무중단 배포 등의 부가 기능 사용
    • NginX를 통해 쉽게 무중단 배포나, SSL 연동이 가능하다.
    • 다만 ssl의 경우, 톰캣은 내부적으로 처리 가능하고, gunicorn도 어느 순간부터 지원이 되는 듯 하다. (사실 애초에 ssl은 LB나 API gateway에서 붙이겠지만..)

정리 (세줄요약)

  1. 웹 서버는 정적 파일 서빙에서 출발했고, 이후 다양한 프레임워크와 함께 동적 파일 서빙이 가능한 WAS의 형태로 진화했다.
  2. 동적 파일 서빙은 cgi라는 초기 형태에서, 각 언어 진영에 맞는 방식으로 진화했다.
  3. 파이썬 진영은 wsgi 기반의 WAS를, 자바 진영은 Servlet 기반의 WAS를 운용할 수 있다.

짧은 식견일 수 있지만,,
이제 웹 서버랑 WAS를 굳이 분리할 필요 없는 것 같다.
특히 REST API에서는 정적 파일을 서빙할 일이 많지도 않고,
심지어 톰캣은 웹 서버이기도 해버리니까.. 구분이 더더욱 모호하다.

그럼에도 불구하고 웹 서버를 같이 쓰는 케이스를 정리해두었지만, 이러한 것들(로드밸런싱이나 무중단배포)도 클라우드 환경이 발전하면서 다른 좋은 방법들이 많이 생겼다고 생각한다.


글 쓰다보니 Netty는 서블릿이랑 아예 상관 없는 것인지 궁금해졌다.
Netty 공부해야 하는데..


References

https://scshim.tistory.com/28
https://stg0123.github.io/study/41/
https://stackoverflow.com/questions/7213541/what-is-java-servlet

0개의 댓글