
예전에 spring관련 글을 적은걸 한번 읽어봤는데..
지금읽어보니 그때당시에 잘못이해한게 좀 많아보였다.

일단 찾은건 이것인데
WAS와 Servlet Container의 차이도 모르고
Tomcat이 Dispatcher Servlet을 등록하고,
특정 요청이 들어왔을때 등록한 Dispatcher Servlet으로 전달된다.
라고 적어야 원래 맞는 것이다.
Tomcat은 단순히 외부에서 들어오는 요청을 Servlet에 전달하는것이고
이 Tomcat/Spring Container를 합쳐서 Spring Server라고 이해해야 한다.
하지만 내가 적었던 글은
Tomcat에 Dispatcher Servlet이 포함되었다고 잘못 이해한 것이 적혀있는 것이다. 즉 Web Application Server와 '서버'라는 개념이 잘 잡혀있지 않아서 그랬던것 같다.
따라서, 서버라는것이 무엇이고, 어떤 기능을 하며
Web과 WAS의 차이에 대해 좀더 깊히 알아볼 생각이다.
가장 기초적인 부분부터 파고들어가보자.
서버는 무엇일까?
서버(영어: server, 문화어: 봉사기/奉仕器)는 클라이언트에게 네트워크를 통해 정보나 서비스를 제공하는 컴퓨터 시스템으로 컴퓨터 프로그램(server program) 또는 장치(device)를 의미한다. 특히, 서버에서 동작하는 소프트웨어를 서버 소프트웨어(server software)라 한다. 주로 리눅스 등의 운영 체제를 설치한 대형 컴퓨터를 쓰지만, 그렇지 않은 경우도 있다. - wiki
위 설명에서 알수있는것은
1. 서버는 장치/프로그램으로 나뉨
2. 서버는 클라이언트에게 정보나 서비스를 제공하는것
인 것을 알수있다.
물리적 서버

영화 '마션'을 보면, 프로그래머가 덜덜 떨면서
서버에 계산을 직접 입력하는것을 볼 수 있다.
이때, 배경에 보이는 저 거대한 컴퓨터 장치들은 보통 우리가 생각하는 '서버'일 것이다.
서버실의 기온이 낮은것은 서버 컴퓨터의 방대한 열을 식히기 위해서 그런것인데,
방열양이 엄청나다는 것은 서버 컴퓨터의 연산량이 엄청나다는 것이고
영화 속 서버실의 성능이 뛰어나다는것을 유추할 수 있다.
갑자기 왜 이런소리를하냐면
우리가 알고있는 '서버'의 성능은 물리적 서버와 관련이 매우 깊기 때문이다.

위 스팩은 내가 현재 AWS에서 사용하는 EC2 인스턴스의 스팩이다.
AWS 프리티어 상품이고, 메모리며 vCPU며 성능이 매우 낮다.

그리고 이 인스턴스를 사용해서 서버 부하 테스트를 돌렸을때,
CPU의 사용률이 100프로를 가볍게 찍어버렸고, 성능은 매우 낮았다.
어찌보면 정말 당연한 것이지만, 서버의 리소스를 제공해주는건 물리적 서버이다.
이전 최적화 글에서도 적었는데, 이러한 물리적 서버가 주는 리소스 양은 한정적이라
서버에 들어오는 요청의 양, 밀도 등을 적절하게 생각해서
분산시스템을 구성하던, 로그를 외부 HDD에 따로 저장하는 등의 태크닉이 필요하다.
서버 소프트웨어
영문 위키에서 말하는 엄밀한(Strictly) 의미의 서버이다.
Strictly speaking, the term server refers to a computer program or process (running program). - wiki
서버는 단적으로 말하면 무언가를 제공해주는 것 이다.
service + ~er로 이루어져 있으며,
무언가를 제공해 주는 사람/것이라는 뜻이다.
서버의 종류는 엄청나게 많고, 세부화 되었다.
레퍼런스 : https://en.wikipedia.org/wiki/Server_(computing)
예를 들면
등등의 서버가 있으며, 각 서버는 자신의 기능에 맞는 역할을 수행한다.
우리는 이중 Spring에서 사용하는 서버에 대해 알아볼 것이다.

우리가 보통 Spring MVC 모델 서버를 생각하면
위와 같은 아키텍쳐가 떠오를 것이다.
하지만 잘 생각해보면, 클라이언트에서 오는 요청이 Dispatcher Servlet에 직접적으로 전달될까?
단순히 Spring MVC만을 보는게 아닌, 서버의 전체 동작은 어떻게 진행될까?
Web? WAS?
Spring 서버를 이해하려면 Web서버와 Web Application 서버의 차이를 이해하는것이 정말 중요하다.
Web과 WAS는 무슨 차이가 있을까?
웹 브라우저와 같은 클라이언트로부터 HTTP 요청을 받아들이고, HTML 문서와 같은 웹 페이지를 반환하는 컴퓨터 프로그램 - 위키
웹서버는 단순히 요청을 받아 반환하는 일을 할 뿐이다.
Web Server의 예로는
등이 있다.
웹 서버는 정적 데이터 제공 기능을 수행하며,
CPU 연산이 많이 필요한 복잡한 로직을 처리하지 않는다.
하지만 프록시(Proxy), 로드 밸런싱(Load Balancing), 보안(Security) 등의 기능도 수행할 수 있다.

(자바 진영의 was. Web서버와 servlet container가 같이 있음)
웹 애플리케이션 서버(Web Application Server, 약자 WAS)는 웹 애플리케이션과 서버 환경을 만들어 동작시키는 기능을 제공하는 소프트웨어 프레임워크이다. - 위키
WAS는 3가지의 기본 기능을 가진다.
가만히 생각해보면 우리가 코딩하는 Spring MVC 코드들은
단순히 Controller를 통해 응답을 해주는 코드를 적는것이 아닌가?
사실 우리의 코드들은 서버의 응답방식을 지정해주는 것이다.
그런데 데이터가 Clinet에서 코드까지 어떻게 들어오는지에 관심이 생기지 않는가?
우리의 서버 IP까지 도달한 요청이 바로 코드로 들어가진 않을테니 말이다.
바로 이런 요청 호스팅을 하는 기능을 WAS에서 한다는 것이다.
이 글에서는 Spring/Node.JS 2가지를 중점으로 다룰것인데
Spring에서는 Tomcat(jetty도 많이씀)이,
Node.JS에서는 Node.JS 서버 자체가 WAS의 기능을 가진다.
위에서 던졌던 질문 중 하나인
클라이언트에서 오는 요청이 Dispatcher Servlet에 직접적으로 전달될까?
의 대답은 WAS에서 Dispatcher Servlet에게 요청을 전달하게 된다.
가 정답이 될 것이다.
그리고 각 WAS를 알아보다 보면 왜 서버마다 성능의 차이가 발생하게 되는지, 어떤 장점과 단점이 발생하는지에 대해서도 알 수 있을 것이다.

(이후 설명은 Tomcat 기반으로 함.)
스프링 서버의 WAS 부분을 정말 잘 나타낸 그림이다.
우선 JAVA 진영의 WAS를 이해하려면 Servlet이라는 개념에 대해 알아야 한다.
자바 서블릿(Java Servlet)은 자바를 사용하여 웹페이지를 동적으로 생성하는 서버측 프로그램 혹은 그 사양 - 위키
먼저 서블릿을 사용하게된 이유에 대해서 알아보자.
과거 웹 서버는 정적인 HTML만 제공할 수 있었고, 동적인 처리를 위해 CGI(Common Gateway Interface) 방식이 사용되었다. 하지만 CGI는 요청당 새로운 프로세스를 생성해야 하는 비효율적인 구조를 가지고 있어 성능이 저하되었다.
Servlet은 이러한 문제를 해결하기 위해 등장했으며, 요청을 멀티스레드 기반으로 처리하여 성능을 개선하였다.
서블릿은 하나의 객체를 유지하면서 여러 요청을 처리할 수 있어 메모리 효율성이 뛰어나고, 현재 Spring MVC 같은 웹 프레임워크의 기반 기술이 되었다.
또 하나 중요한 개념은 WAS와 WebContainer의 차이점을 알아야 한다.
즉, WAS가 Web Container를 포함하는 개념인 것이다.
보통 WAS에 Tomcat을 언급하고는 하는데,
엄밀히 말하면 Tomcat은 Servlet Container(=Web Container)이라는 것이다.
이 부분을 이해못하고 글을 적으니까 용어도 틀리고 인터넷 글을 봐도 이해도 못하고 아주 난리가 났던 것이다.
그리고 Tomcat의 구조를 다시 살펴보자.

위에서 내가 잘못썼던 Tomcat에 대한 설명인데
지금 읽어보니 참 머리가 아프다.
이때까지 적었던 글들을 정리해 보자.
1. 서블릿(Servlet)은 과거 CGI의 단점을 해결하기 위해 등장했으며, 멀티스레드 기반으로 요청을 처리하여 성능을 개선했다.
2. WAS는 Web Container를 포함하는 개념이며, Java EE의 전체 기능을 지원한다.
3. Web Container는 Servlet, JSP, JSTL 관련 기능만 제공하며, 대표적으로 Tomcat이 있다.
4. Tomcat은 WAS가 아니라 Web Container(Servlet Container)이다.

그럼 이제 Tomcat에서 Servlet을 어떻게 관리하는지 알아보자.
먼저, Tomcat은 Spring의 Dispatcher Servlet을 등록한다.
이후, Dispatcher Servlet에 전달해야 하는 요청이 오면, 그 요청을 Dispatcher Servlet에 전달한다.

이 요청은 Servlet에 의해 같은 Web Server에 존재하는 Dispatcher Servlet에 연결된 Spring Container에 전달되며,

Spring MVC 핸들링에 의해 처리가 된다.
Spring에 전달된 요청은 전부 이렇게 처리되나?
라고 궁금해할 수 있는데,

(Spring Doc의 목차)
Spring은 Servlet Stack, Reactive Stack으로 아키텍쳐가 나뉘며
따라서 우리가 평소 사용하는 MVC로 요청이 들어온다면
위와 같은 Tomcat-SpringContainer 방식을 통해 요청이 처리가 된다.
위의 글을 다시한번 정리하게 되면
로 정리할 수 있겠다.
그럼 왜 Java-Spring 서버의 복잡한 로직 처리 성능이 좋은지도 알아봐야 하지 않겠는가?
그 이유를 정리해 보면
정도가 되겠다.
JVM,GC,JPA/Hibernate 등의 내용은 이후 차차 다룰 예정이다.
단점이라고 한다면 역시나 JVM 사용으로 인한 무거운 프레임워크가 가장 클 것이다.
부팅 속도도 느리고, 메모리는 엄청 잡아먹고, GC또한 튜닝이 필요하며 학습하기 매우 어려우며 배포도 어렵다(Docker를 활용해 그나마 편했지 정신나가는줄알았다).
느리고, 유지비용이 많이들어가지만 강력한 '장갑차' 같은 프레임워크라고 생각한다.
Node.JS architecture : https://medium.com/@ibrahimlanre1890/node-js-architecture-understanding-node-js-architecture-5fb32879b994
이제 자바스크립트 진영으로 넘어가 보자.

위 그림은 Node.JS 서버가 어떻게 동작하는지 보여주는 그림이다.
Node.JS의 특징은 '싱글스레드 기반'이라는 것에 있다.
아니 위에 워커스레드라고 뻔히적혀있는데 무슨소리냐? 라고 할 수 있지만
여기서 말하는 싱글스레드라는 것은 Event Loop의 스레드가 1개라는 것이다.
그럼 Node.JS의 작동방식에 대해 알아보자.

Spring 서버와는 비교도 안될정도로 간결하게 처리가 끝난다.
왜냐? EventLoop가 요청을 보고 I/O, 연산을 판단한 후 다른 스레드에게 시키면 되기 때문이다.
때문에 Node.JS의 장점과 단점은 명확하다.
장점
단점
대표적인 장/단점은 위와 같다.
즉, 작은 서버는 Node.JS를, 큰 서버는 Spring을 사용해라는 이유가 바로 나와버린다.
다른건 다 그렇다고 쳐도 CPU 사용이 많아졌을때 EventLoop의 스레드가 Block이 걸리는게 너무 뼈아프다..
++ 실제로 Blocking이 되지는 않지만, CPU 사용량이 높아져 다른 이벤트를 처리하지 못하는것을 보고 Blocking되었다고 표현함.
레퍼런스 : https://nodejs.org/uk/learn/asynchronous-work/dont-block-the-event-loop#dont-block-the-event-loop-or-the-worker-pool

Node.JS와 Spring이 어떤 장단점이 있는지도 알았고
적합하다는 것을 알게 되었다.
하지만 여기서 한번더 걸고 넘어지면
우리는 어떻게 백엔드 서버를 구성해야 하는가?
라고 생각할 수 있다.
위는 HelloWorld 성능비교를 한 것이다.
어느 프레임워크가 더 뛰어나다고 말하고 싶은것이 아닌,
내가 말하고자 하는 부분은 이것이다.


일반적인 I/O 성능은 Node.JS가 훨씬 뛰어나다.
그리고 우리가 생각할 것은 기본적인 깡통이 리소스를 얼마나 잡아먹느냐 이다.
기본적으로 Java는 좀 비싸다.
비싸다는게 가격적인 부분이 아니라 리소스를 많이 먹는다는 뜻이다.
위는 Hello World 기준으로 300~400MB정도 나왔지만
클래스와 static 변수 등이 많아지면 메모리는 지속적으로 증가한다.
그리고 Java는 JVM이라는 가상머신 위에서 작동한다.
레퍼런스 : https://medium.com/technology-hits/minimizing-object-creation-for-high-performance-java-applications-553f9c9278c1
jvm의 객체 생성 비용이 크다는것은 유명하고
기본적으로 node.js보다 많은 스레드를 사용하며
jvm은 많은 기능을 포함하고 있기 때문에 메모리 사용량이 크다.
기능의 예시로는
등이 있다.
이러한 기능들은 장기적으로 보았을때
메모리의 사용을 효율적으로 할 수 있게 도와주고
최적화를 통해 서버의 기능을 안정화시켜주지만
작은 서버를 사용할때, 적고 간단한 요청을 사용할때는
굳이 JAVA를 사용할 필요가 없다는 결론이 나온다.
그런데 여기서 내가 진짜 말하고 싶은 문제가 나온다.
그러한 오버해드는 감당할수 있는 수준인데, 그럼 JAVA가 무조건 정답이 아닌가?
나는 아니다 라고 말하고 싶다.
일단 개발 공부를 하면서 머리박으면서 여러번 깨져본 결과
서버와 개발속도/유지보수는 빼놓을수 없는 관계를 가지고 있다.
다른 언어로 쉽게 구현가능한것을 굳이 힘들게 돌아갈 필요는 없다.
또한 돈이 걸려있는 상황이라면 쉽고 빠르게 개발하는 Node.JS가 있는데
굳이 비싼돈 줘가면서 Spring을 쓸 필요가 없다는 것이다.
비싼돈이면 얼마나 하겠나 싶지만
Spring기본 메모리 + 런타임 동적 메모리 사용량을 고려한다면
메모리가 작은 서버임에도 불구하고 꽤 유의미하게 들어간다는 것을 예측할 수 있다.
그리고 애초에 I/O만을 고려했을때 Node.JS가 빠른데 굳이 느린 Java를 쓸 이유가 없다.
나중에 서버가 커지면 힘들겠지만
마이그레이션 하는것은 갈려나가는 개발자들의 몫이니..
이런것들을 고민하다보니
프레임워크와 언어는 도구일 뿐이고 CS/기본기가 중요하다
라는 유명한 말을 더욱 절실하게 느낄 수 있었다.
서버는 단순하게 말하면 Client의 요청에 알맞는 응답을 제공해주는 자판기이고
자세하게 말하면 그 응답을 안정적이고 빠르게 제공하기 위해
메모리관리/런타임 관리/외부 플랫폼(DB, I/O등등..)과의 연결
과 같은 여러가지 기능을 효율적으로 관리해야 한다.
그리고 물리적 서버를 언급한 이유는
서버의 성능은 물리적 서버에 종속되어 있으며(램 부족하면 spring은 실행도못함)
이 물리적 서버의 성능이 어느 정도의 서버 소프트웨어를 감당할 수 있는지
점검해 보는것도 중요할 것이라 생각하기 때문이다.
마이그레이션 하는것이 감당이 안될 정도가 아니라면
너무 먼 미래를 생각하는 것이 아닌
지금 효율적으로 사용할 수 있는 프레임워크를 사용하는것이 좋아보인다.
물론 나는 Spring을 공부하기 위해 이 프로젝트를 진행하는것임으로
갑자기 Node.JS나 Nest.JS로 마이그레이션을 하진 않을 것이다.
지금까지 공부했던것은 서버 외적인 부분에 대해서 공부해 봤다.
자동배포라던가, 도커라던가 등등.
여기까지 하면서 느꼈던 것은
튼튼한 서버 라는 말의 범위가 정말 넓다는 것이다.
하드웨어 부분에서의 서버라고 하면
전기 잘 공급해주고 좋은 부품을 사용하는것이고
소프트웨어 관리에서의 서버라고 하면
유연하고 쉽게 유지보수가 가능한 것이고
그리고 우리가 보통 말하는 서버라고 하면
프로그래밍 언어와 프레임워크를 사용해서
돌아가는 '실질적인 연산부분'을 말하는 것이다.
따라서 지금까지는 서버 외적인 부분에 대해서 알아봤으니
이제 진짜 서버에 대해서 공부해볼 차례라고 생각했다.
그래서 이제 서버를 직접 코딩하고 만들어보면서
경험을 쌓으려고 한다.
이 길이 맞는건지는 나도 잘 모르겠지만
나 자신을 믿고 꾸준히 노력하자.