Spring/Spring Framework

[Netty] Netty가 뭐에요? - 2탄 : Netty가 필요하기 까지

공대키메라 2025. 9. 24. 23:53

이전 글에서는 Netty의 소개를 읽고 관련 있는 기본 지식들에 대해 알아보았다. (여기 클릭!)

 

이번 시간에는 어떻게 해서 Netty가 출현하게 되었는지를 정리하려고 한다.

 

설명이 좀 부족할 수 있지만... 액기스와 중요한 부분만 캐치하고 흐름대로 정리하려고 한다.

 

물론 다 옛날에 분명 배웠고 기억도 가물가물하다. 하지만 다시 복기하는 차원에서...


1. 초기 웹 : Static Web Server

초기에는 정적 웹 서버만 제공을 했다.

 

정적 웹 서버란, 말 그대로 정적 파일만 제공하는 서버이다.

 

HTML, CSS, Javascript 같은 단순한 파일을 그대로 전달해주고, 서버 쪽에서 별도 동적 연산을 하지 않는다.

 

 

우선 웹페이지를 가져오려면 우리 브라우저에 웹 서버에 파일을 요청한다.

 

그러면 server가 그것을 찾아서 읽고 browser에 전달한다. 

 

 

이 과정에서 필요한 정적 파일을 제공하는 서버인 것이다.

 

 

그러면 이를 통해서 생각해 볼 수 있는 단점은? 복잡한 동적 처리가 불가능하다는 것이다. 

 

서버와 DB연동이 필요한 복잡한 업무는 처리가 안됐다.

 

그래서 이를 처리하기 위해서 CGI가 등장했다.

 

2. CGI와 Servlet의  등장

CGI의 등장

 

CGI란 Common Gateway Interface의 약자로 웹 서버가 HTTP 혹은 HTTPS 유저 요청을 처리하기 위해 외부의 프로그램을 실행하도록 하기 위한 인터페이서 명세다. 

 

CGI의 목적에 대해서 위키피디아에 상세히 나와있는데, 

 

웹 서버(Web Server)가 클라이언트(브라우저)의 요청을 외부 프로그램(스크립트 혹은 실행 파일)에 넘기고, 그 프로그램이 처리한 뒤 결과를 서버가 클라이언트에 돌려주는 일련의 규칙(specification)이다.

 

사실 그렇게 흥미가 많지는 않아서... 필자는 그러려니 하고 넘어갔다. 

 

뭐 당연... 이게 좋았으면 사실 이것만 썻겟지? 

 

상태유지 관리가 어렵고, 보안 문제 등의 이슈로 대안이 많이 제시되었는데, 

 

우리가 익히 아는 Servlet이 이 이후에 등장하게 된다.

 

Servlet의 등장

What Is a Servlet?

A servlet is a Java programming language class used to extend the capabilities of servers that host applications accessed by means of a request-response programming model.

Although servlets can respond to any type of request, they are commonly used to extend the applications hosted by web servers.

For such applications, Java Servlet technology defines HTTP-specific servlet classes.

The javax.servlet and javax.servlet.http packages provide interfaces and classes for writing servlets.

All servlets must implement the Servlet interface, which defines lifecycle methods. When implementing a generic service, you can use or extend the GenericServlet class provided with the Java Servlet API. The HttpServlet class provides methods, such as doGet and doPost, for handling HTTP-specific services.

 

서블릿은 요청-응답 프로그래밍 모델(request-response programming model) 을 통해 접근되는 애플리케이션을 호스팅하는 서버의 기능을 확장하기 위해 사용되는 Java 프로그래밍 언어 클래스이다.

 

서블릿은 어떤 종류의 요청에도 응답할 수 있지만, 일반적으로 웹 서버가 호스팅하는 애플리케이션을 확장하는 데 사용된다.

 

이러한 애플리케이션을 위해 Java Servlet 기술은 HTTP에 특화된 서블릿 클래스를 정의한다.

 

 

모든 서블릿은 Serlvet 인터페이스를 구현해야 하며 이 인터페이스에는 init(), service(), destroy()같은 생명주기 메서드가 있다.

 

 

이 Servlet을 사용하면서 요청을 스레드 단위로 처리하고,

 

자바 코드에서 직접 HTTP 요청과 응답을 다룰 수 있게 되면서 기존의 CGI보다는 편리해졌다.

 

 

하지만 위의 Servlet에 대한 정의와 설명에 대해서 잘 읽어보면 이런 부분이 있다.

 

All servlets must implement the Servlet interface, which defines lifecycle methods

 

Servlet는 즉 그냥 규격이고 직접 구현은 안되었다는 단점이 있다.

 

아니... 그러면 우리가 여태 쓰는 톰캣은 도대체 뭐야?

 

하는데... 이 Servlet의 규약을 기반으로 구현한 Tomcat을 우리는 Servlet Container 라고 한다

 

Servlet Container? Servlet의 명세를 구현한 실행 환경이라는 말이다.

 

즉, Servlet을 직접 실행할 수 있는 그릇 역할을 하는 것을 Servlet Container라고 한다.

 

 

Tomcat 은 웹 서버의 기능에 Servlet을 구현하여 동적웹 기능을 추가했고,

 

이것을 우리는 WAS(Web Application Server)라고 한다.

 

 

내용이 너무 많아서 이걸 다 일일히 분석할 수는 없었지만, GPT의 도움을 받아 핵심 특징 구절을들 뽑아달라 했더니

Tomcat의 버전에 따라서 공식 문서를 볼 수 있었다.

 

핵심적으로 알 수 있은 부분은 물론... 다들 익히 알겠지만 Thread 하나당 요청이 들어오고 Blocking I/O를 기본으로 동작한다.

 

Tomcat에서 Connector라는 부분을 보면 다음과 같은 글이 있다.

 

Each incoming request requires a thread for the duration of that request.

If more simultaneous requests are received than can be handled by the currently available request processing threads, additional threads will be created up to the configured maximum (the value of the maxThreads attribute).

If still more simultaneous requests are received, they are stacked up inside the server socket created by the 
Connector, up to the configured maximum (the value of the acceptCount attribute).

Any further simultaneous requests will receive "connection refused" errors, until resources are available to process them.

 

 

각각의 요청은 그 요청의 지속동안 하나의 쓰레드를 필요로한다.

 

사용가능한 요청 처리 쓰레드보다 더 많은 요청이 동시 요청이 들어오면 추가적인 쓰레드가 최대치까지 생성된다. 

 

만약 더 많은 요청이 들어오면 최대 설정치까지 Connector에 의해 생성된 서버 소켓 안에 쌓일 것이다. 

 

더더욱 많은 동시 요청은 자원 처리가 가능할 때 까지 연결 거절 에러를 받는다.

 

 

다른 글을 보면 Tomcat 의 Advanced I/O 기능에 대해 설명한다.

 

NIO기능도 제공을 하는거 같다.

 

CommetEvent, CometFilter, Comet timeouts를 포함하는  Commet support가 있고 Asynchronous하게 파일을 제공하는 방법도 있다.

 

Comet applications are event driven applications that hold HTTP connections open waiting for events.

 

Comet 애플리케이션은 HTTP 연결을 열린 상태로 유지하면서 이벤트를 기다리는 이벤트 기반 애플리케이션이다.

 

잠깐만... 뭔가 이상하다. 연결을 열린 상태로 유지해? 뭔가 어디서 본거같지 않은가?

 

이전에 폴링이라는 단어를 들어봤을 것이다. 

 

 

그렇다.  tomcat은 실제로 http를 기준으로 사용해서 연결을 끊지 않고 실시간 비동기 처리를 가능하게 만들었던 것이다.

 

즉, 유사 비동기를 구현해서 제공하던 것이다.

 

 

애초에 Tomcat 은 목적 자체가 비동기 처리와는 알맞지 않다.

 

The Apache Tomcat® software is an open source implementation of the Jakarta Servlet, Jakarta Pages, Jakarta Expression Language, Jakarta WebSocket, Jakarta Annotations and Jakarta Authentication specifications. These specifications are part of the Jakarta EE platform.

 

Tomcat의 본질은 Servlet, JSP, EL, WebSocket 스펙을 구현하는 서버라는 것이다.

 

All servlet containers must support HTTP as a protocol for requests and responses, but additional request/response-based protocols such as HTTPS (HTTP over SSL) may be supported. 

 

자카르타 서블릿의 글을 보면 모든 서블릿은 반드시 요청과 응답 프로토콜로 HTTP 를 지원해야한다. 하지만 추가적으로 요청/응답 기반의 프토로콜인  HTTPS 도 지원될수도 있다고 한다.

 

애초에 비동기를 기반으로 만든 것이 아닌 HTTP 를 통한 동기 통신에 기반을 두고 있는 것이다.

 

이쯤되면 톰캣의 단점이자 Blocking I/O 의 한계를 정리해보자.

 

  • 요청 1개당 스레드 1개 필요
  • DB 조회, 외부 API 호출 등 대기 시간 동안 스레드가 묶임
  • 접속자가 많아지면 스레드 수 폭증 → CPU·메모리 한계 발생
  • 스레드 풀(Thread Pool)로 최적화했지만 근본적 한계 존재
  • 따라서 대규모 동시 접속 서비스(채팅, 스트리밍, 게임) 에서는 부적합

3. Non-blocking I/O (Java NIO)의 등장

Java 1.4에 NIO 기능이 추가되었다.

 

NIO는 New I/O로 그대로 읽으면 I/O 신기능이다.

 

공식문서를 보는거보다는, 어느 사이트에서 Java NIO 에 대한 튜토리얼을 만들어놨는데

 

그것이 다음이다.


Java NIO: Non-blocking IO

Java NIO enables you to do non-blocking IO. For instance, a thread can ask a channel to read data into a buffer. While the channel reads data into the buffer, the thread can do something else. Once data is read into the buffer, the thread can then continue processing it. The same is true for writing data to channels.

Java NIO: Channels and Buffers

In the standard IO API you work with byte streams and character streams. In NIO you work with channels and buffers. Data is always read from a channel into a buffer, or written from a buffer to a channel.

Java NIO: Selectors

Java NIO contains the concept of "selectors". A selector is an object that can monitor multiple channels for events (like: connection opened, data arrived etc.). Thus, a single thread can monitor multiple channels for data.


정리하면, Non blocking을 지원하고, channel과 buffer를 통해서 NIO가 작동하며, Selector라는 것이 이벤트에 대한 여러 채널을 감시하기에 하나의 스레드가 여러 채널을 감시할 수 있다. (Event Looping 개념이 여기 나와있네!)

 

channel은 I/O 작업을 수행할 수 있는 엔티티를 위한 연결을 담당한다.

 

buffer는 특정 원시 타입의 데이터를 위한 컨테이너, NIO에서 데이터를 담고 읽고 쓰는 컨테이너로 용량이 있고 한계가 있으며 위치

도 있다. 스레드 안전하지 않으니 동기화가 필요하다.

 

어휴 벌써부터 NIO 쓰기 피곤하네.... 공부할게 너무 많다.

 

아 이거 누가 쉽게 안해주나... 좀 편리하게 쓰려면 프레임워크를 쓰면 좋지 아니한가?

 

4. 혼돈의 시대(?), Netty의 탄생

직접 쓰기 어려운 NIO를 쉽게 쓸 수 있는 프레임워크, Netty는 공식문서에서 다음과 같이 설명하고 있다.

 

Netty is an NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It greatly simplifies and streamlines network programming such as TCP and UDP socket server.


Netty는 NIO 클라이언트 서버 프레임워크인데, 프로토콜 서버와 클라이언트같은 네트워크 어플리케이션의 개발을 빠르고 쉽게 한다. TCP 나 UDP 소켓 서버 같은 네트워크 프로그래밍을 매우 간편화 혹은 간소화한다.

 

이 Netty 시리즈 글을 정리할 때 맨 처음에 해당 글에 대해서 설명을 했다.

 

이 뒤에 사실 글이 더 있다.

 

'Quick and easy' doesn't mean that a resulting application will suffer from a maintainability or a performance issue.

Netty has been designed carefully with the experiences earned from the implementation of a lot of protocols such as FTP, SMTP, HTTP, and various binary and text-based legacy protocols.

As a result, Netty has succeeded to find a way to achieve ease of development, performance, stability, and flexibility without a compromise.


빠르고 쉽게는 어플리케이션이 유지성과 성능 이슈로 고통받는것을 말하는게 아니다.

Netty는 FTP, SMTP, HTTP 그리고 다양한 binary, text기반 레거시 프로토콜 같은 많은 프로코콜의 구현으로부터 얻은 경험으로 조심스럽게 디자인 되었다.

그 결과 Netty는 타협없이 개발의 쉬움, 성능, 안정성 그리고 유연성을 성취할 수 있는 하나의 방법을 찾는데 성공했다.
(얏호!)

 

그래. 결국은 이러한 큰 흐름에서 필요성을 채우고자 Netty가 생겨났고,

 

Netty는 NIO의 사용을 쉽게 도와준다고 한다.

 

5. 결론

Web Server → CGI → Servlet → Servlet Container → Blocking I/O 한계 → Non-blocking I/O → Netty

  • 범용 HTTP 서버는 일반 웹 요청에는 충분하지만,
  • 실시간 서비스(채팅, 게임), 대규모 트래픽, 특수 목적 최적화에는 한계 존재

 

이번 시간에는 쭈욱... 글에 대해 찾아서 정리를 해봤다.

 

사실 내가 한건 별거 없다. 여러 정보가 있는데 GPT에 물어봐서 공식 문서에 대해 물어보고 읽고 간결하게 정리했다.

 

시간이 난다면 이 글들을 읽어보는게 좋겟다. 

 

다만 흐름을 알고자 필자는 엄~청 축약을 했으니 오해말길!

 

다음에는 Netty의 구조와 사용법에 대해 알아보려고 한다.

 

 

참고:

https://developer.mozilla.org/en-US/docs/Learn_web_development/Howto/Web_mechanics/What_is_a_web_server

https://en.wikipedia.org/wiki/Common_Gateway_Interface

https://docs.oracle.com/javaee/6/tutorial/doc/bnafe.html

https://tomcat.apache.org/tomcat-7.0-doc/realm-howto.html

https://tomcat.apache.org/tomcat-6.0-doc/aio.html

https://jakarta.ee/specifications/servlet/6.0/jakarta-servlet-spec-6.0.html

https://jenkov.com/tutorials/java-nio/index.html