지난 시간에는 Netty의 공식문서에 있는 특징에 대해 알아보고 NIO 에 대한 예시 코드를 작성해보았다.
그리고 Architecture 초입부를 읽었는데... 그 전에!
이번 시간에는 자바공화국 한국에서 가장 많이 사용하는 Spring framework 와 Netty Framework을 간단하게 비교해보고
Netty 공식문서에서 제공하고 있는 예시 코드를 탐방해볼 것이다.
개인적으로 특정 기술에 대해 잘 모르겟으면, 필자 키메라는 비교군을 찾는 것이 좋다고 생각한다.
기존의 Spring은 뭐가 부족했길레 Netty 가 필요했던 것이고 두 개의 성격을 비교해보고자 한다.
물론 이전의 글 중 하나인 [Netty] Netty가 뭐에요? - 2탄 : Netty가 필요하기 까지 를 보면 짐작할 수 있겠지만,
이에 대한 근거를 좀 더 확인해보고 가려고 한다.

1. Spring MVC
왜 뜬금없이 Spring MVC에 대해 다시 보냐고?
우리는 2탄 글에서 Servlet에 대해서 알아보았다. (이전글 - [Netty] Netty가 뭐에요? - 2탄 : Netty가 필요하기 까지 )
Spring 에도 MVC 패턴에서 확인할 수 있는 Servlet 구현체가 있는데, Dispatcher Servlet이다.
사실 Spring MVC에 대한 소개를 공식문서에서 할 때 Servlet 기반으로 만들어졌다는 글이 똭! 그대로 적혀있다.
Spring 공식 문서에 따르면, Spring Web MVC는 Servlet API 위에 구축된 원조 웹 프레임워크로,
Spring Framework 초창기부터 포함되어 왔다.
공식 명칭은 "Spring Web MVC"지만, 일반적으로는 "Spring MVC"로 더 잘 알려져 있다.
출처 : [Spring Web MVC Documentation](https://docs.spring.io/spring-framework/reference/web/webmvc.html)
Servlet이 뭐였는지 지난 글을 다시 보자면
서블릿은 요청-응답 프로그래밍 모델(request-response programming model) 을 통해 접근되는 애플리케이션을 호스팅하는 서버의 기능을 확장하기 위해 사용되는 Java 프로그래밍 언어 클래스이다.
그리고 Serlvet는 그냥 규격이고 직접 구현은 안되었다는 단점이 존재했다.
이를 Spring 에서는 Spring MVC, 혹은 Spring Web MVC로 하나의 프레임워크로 제공하는 것이다.
작동 순서를 정리하면 다음과 같다.
Spring MVC 전체 동작 순서
0. HTTP 요청 수신
- 클라이언트의 요청이 DispatcherServlet으로 전달
1. 핸들러 조회
- HandlerMapping을 통해 요청 URL에 매핑된 핸들러(컨트롤러)를 조회
2. 핸들러 어댑터 조회
- 조회된 핸들러를 실행할 수 있는 HandlerAdapter를 조회
3. 핸들러 어댑터 실행
- HandlerAdapter를 통해 실제 핸들러 호출
4. 핸들러 실행
- HandlerAdapter가 실제 핸들러(컨트롤러)를 실행
- 비즈니스 로직 처리 및 결과 반환
5. ModelAndView 반환
- HandlerAdapter가 핸들러의 반환값을 ModelAndView로 변환하여 DispatcherServlet에 반환
6. ViewResolver 호출
- 뷰 이름을 기반으로 ViewResolver를 호출
7. View 반환
- ViewResolver가 논리 이름을 물리 이름(실제 JSP 경로 등)으로 변환
- 렌더링을 담당하는 View 객체 반환
8. 뷰 렌더링
- View 객체를 통해 Model 데이터를 사용하여 최종 HTML 생성
9. HTTP 응답
- 렌더링된 결과를 클라이언트에게 응답
뭐.. 그렇단다.
2. Spring Web Flux
Spring Framework 5.0(2017년)에서 reactive-stack 웹 프레임워크인 Spring WebFlux가 추가되었다.
주요 특징
- 완전한 Non-blocking 지원
- Reactive Streams 백프레셔 지원
- Netty, Undertow, Servlet 컨테이너 등 다양한 서버에서 실행 가능 기존
Spring MVC가 Servlet API에 특화되어 있었다면, WebFlux는 비동기 Non-blocking 처리에 최적화된 프레임워크다.
출처: [Spring WebFlux Documentation](https://docs.spring.io/spring-framework/reference/web/webflux.html)
이전에는 비동기 프로그래밍은 직접 해야 하거나 Netty를 사용해야 한 것 같다.
여기서 reactive-stack, reactive streams, back pressure 그리고 Undertow 가 뭐고, Netty도 지원한다는게 무슨 말인지
또한 Servlet container같은 서버에서도 작동한다는게 무슨 말인지 이해가 안됐다.
Reactive streams?
Reactive Streams는 백프레셔를 지원하는 비동기 컴포넌트 간의 상호작용을 정의하는 스펙이다(Java 9에서도 채택됨).
동작 방식
- Publisher(데이터 생성자): 예를 들어 데이터 저장소
- Subscriber(데이터 소비자): 예를 들어 HTTP 서버
- Subscriber가 Publisher의 데이터 생성 속도를 제어할 수 있음
이를 통해 데이터 생산자와 소비자 간의 속도 차이로 인한 문제를 해결한다.
Reactive Streams에 대한 자세한 내용은 별도로 찾아보는 것을 추천한다.
Back Pressure
Back Pressure는 유체역학(Fluid Dynamics)에서는 유동유체의 흐름이 방해를 받아 유체의 흐름방향과 반대방향으로 유체에 가해지는 압력으로,
기본적으로 일정한 유량이 흐르지만 파이프의 크기가 작아지면 속도가 빨라진다. (베르누이 방정식)
에너지 보존 법칙에 따르면 속도는 빨라지지만 압력은 낮아질 것이다. 유량의 에너지는 마찰력이 0이라는 가정 하게 동일하게 유지되기 때문이다. (갑분 유체역학 시간...)
그런데 여기서 파이프가 막혀버린 것이다. 그러면 압력이 증가한다. 뒤로 나오는 역압력(back pressure)이 증가한다.
이를 IT분야에서 차용한다면 백프레셔는 이미 받은 요청 내에서 데이터 흐름이 막혔을 때 발생한다.
데이터 흐름을 눌러서 천천히 들어오게 하는 것이다.
즉, Spring Web Flux 에서 말하는 백프레셔 지원은 우리가 서버 → 데이터 소스 간의 흐름 제어를 할 수 있도록 도와준다는 말이다.
Spring Web Flux - Server 섹션
공식문서에서 해당 섹션의 글을 잘 보면 Netty를 기본값으로 사용한다고 한다.
그러니까 netty를 기본적으로 application 서버로 사용한다는 말이다.
결국 Spring Web Flux는 Reactive Streams를 기존에 잘 사용하도록 도와주는 framework로 따로 기존에 있던 서버를 호환해서 사용할 수 있도록 지원해주는 비동기 프레임워크이다.
기본적으로 Netty서버를 기반으로 하기에 Netty를 잘 이해하면 결국 추후에 Spring Web Flux같은 프레임워크도 이해하는게 어렵지 않을 거라는 판단이다.
그런데 이해가 안되는 부분이 있는데,
Netty는 공식문서에서 framework라고 소개했는데, Spring Web Flux에서는 서버로 사용한다니 이게 무슨 말인가?
Netty는 빠른 유지가능한 고성능 프로토콜 서버 클라이언트 개발을 위한 비동기 이벤트기반 네트워크 어플리케이션 프레임워크다.
아니 도대체 어디있어? 했지만... 나는 굉장히 많이 봐왔었다. 공식 문서에 맨 처음 상단에 노출되어 있다.
다만 이걸 내가 부족해서 이해를 못했을 뿐 ㅠㅠ...
서버 클라이언트 개발을 위한 비동기 이벤트기반 네트워크 어플리케이션 프레임워크
라고 대놓고 공식문서 맨 앞에 대문짝만하게 명시가 되어 있다.
와하... 이걸 놓쳐서 spring webflux 에서 서버로 사용한다고 했을 때 이해를 못했구나~
이전에 NIO 예시 코드를 물어봐서 작성을 해봤을 때 NIO에서 client 역할도 하고, server역할도 하긴 했다.
아하! 그래서 이전에 NIO 예시 코드로 그래서 파일이 여러개였구나...
client과 server역할을 하는 코드를 따로 분리해서 client에서 server에 호출하는 구조였다.
이에 대해서 이해를 하니 gpt코드가 더 잘 이해되는거 같기도 하고... 아님 말고! 데헷 ㅎ
(이전 코드가 여기 클릭 - [Netty] Netty가 뭐에요? - 3탄 : Netty의 특징과 구조 및 NIO 테스트)
결론적으로 Netty가 서버 클라이언트 개발을 위한 비동기 이벤트기반 네트워크 어플리케이션 프레임워크라면
Spring WebFlux는 Reactive Streams 기반으로 웹 애플리케이션 개발을 편하게 해주는 비동기 웹 프레임워크로 다양한 서버를 지원한다.
Spring Webflux위에 포함된 것이 아닌 지원해주는 것으로 Netty를 실행할 수 있는 구조이다.
휴... 이해하기 참 힘드네 정말 ㅋㅋ
Spring Webflux 에 대한 공식문서를 보면 더 많은 내용이 있지만, 이번에는 이정도로만 보겠다.
3. Netty 예시 찾아보기 - DiscardServer와 내부 class 파악
그려면 드디어... 실제 예시 코드를 찾아 보려고 한다.
user guide를 공식 사이트에서 예시들을 다양하게 제공하고 있다.
필자는 4.x 버전의 가이드를 보려고 한다. (회사에서 그걸 봄)
첫 번째 예시로 writing a Discard Server를 소개한다.
DiscardServerHandler.java
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
* Handles a server-side channel.
*/
public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1)
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
// Discard the received data silently.
((ByteBuf) msg).release(); // (3)
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}
여기서 알아볼것들은 다음과 같다.
- Channel
- ChannelHandler
- ChannelHandlerContext
- ChannelHandlerAdapter
- ChannelInounbHandler
- ChannelInounbHandlerAdapter
ChannelHandler 즉, 채널 관리자가 많이 나오는데, Channel도 실제로 까봐야 겠더라 싶었다.
Channel
Channel의 다양한 상태
기존 Java NIO의 Channel은 isOpen()과 close() 정도만 제공했지만,
Netty의 Channel은 훨씬 풍부한 상태 관리 기능을 제공한다:
- `isOpen()`: 채널이 열려있는가?
- `isRegistered()`: EventLoop에 등록되었는가?
- `isActive()`: 채널이 활성 상태이고 연결되어 있는가?
- `isWritable()`: 쓰기가 가능한가?
- 그 외 다양한 설정 및 제어 메서드들
Channel 인터페이스의 전체 코드는
[Netty API 문서](https://netty.io/4.0/api/io/netty/channel/Channel.html)에서 확인할 수 있다.
뭐...? 넥서스...?

Channel은 단순한 채널, 즉 연결점이 아니라 내부적으로 여러 일을 해주는것 같다.
Channel Handler
Handles or intercepts a ChannelInboundInvoker or ChannelOutboundInvoker operation, and forwards it to the next handler in a ChannelPipeline.
ChannelInboundInvoker와 ChannelOutboundInvoker 작업을 다루거나 intercept하고 ChannelPipeline에서 다음 handler로 넘긴다.
인바운드와 아웃바운드는 트래픽에 네트워크 간에 이동하는 방향을 말하는데,
인바운드라고 하면 네트워크 안으로 들어오는 것이고, 아웃바운드는 네트워크에서 나가는 것이다.
ChannelHandler는 ChannelHandlerContext 객체에서 제공받는다.
ChannelHandler는 컨텍스트 객체를 통해 자신이 속한 ChannelPipeline과 상호작용하도록 되어 있다.
컨텍스트 객체를 사용하여, ChannelHandler는 이벤트를 상위(upstream) 또는 하위(downstream)로 전달하거나, 파이프라인을 동적으로 수정하거나, 핸들러에 특화된 정보를 저장(AttributeKey 사용)할 수 있다.
ChannelHandlerContext
Enables a ChannelHandler to interact with its ChannelPipeline and other handlers. A handler can notify the next ChannelHandler in the ChannelPipeline, modify the ChannelPipeline it belongs to dynamically.
ChannelHandler가 ChannelPipeline과 다른 handler들과 상호작용 할 수 있게 한다.
Handler는 다음 ChannelHandler와 ChannelPipeline으로 알리거나 동적으로 속하는 ChannelPipeline을 수정할 수 있다.
ChannelHandlerAdapter
Skeleton implementation of a ChannelHandler.
ChannelHandler를 위한 스켈레톤 구현체.
해골 구현체...? 뒤에 다시 알아보자.
ChannelInboundHandler
ChannelHandler which adds callbacks for state changes. This allows the user to hook in to state changes easily.
ChannelHandler는 상태 변경에 대한 콜백을 추가하는 핸들러입니다.
이를 통해 사용자는 상태 변경에 쉽게 hook을 걸 수 있습니다.
Hook? 이거는 왜 여기서 나오는 것인가... Hook이라는게 Java에도 있던건지 나는 잘 몰랐다.
react 에 대해서 공부를 좀 했었는데, 이에 대해서 좀 알아봐야겠다.
ChannelInboundHandlerAdapter
Abstract base class for ChannelInboundHandler implementations which provide implementations of all of their methods.
This implementation just forward the operation to the next ChannelHandler in the ChannelPipeline.
Sub-classes may override a method implementation to change this.
ChannelInboundHandler 구현체들을 위한 추상 베이스 클래스로, 모든 메서드의 구현을 제공합니다.
이 구현은 단순히 작업을 ChannelPipeline의 다음 ChannelHandler로 전달합니다.
서브클래스는 이를 변경하기 위해 메서드 구현을 오버라이드할 수 있습니다.
여기서 쭉 나온 의문점을 정리해보면
ChannelPipeline, Hook, 해골 구현체? 그리고 구조에 대한 이해가 잘 되지 않았다.
위 4가지에 대해서 좀 알아보도록 하겠다.
4. ChannelPipeline and structure, Hook, Skeleton implementation
필자가 ChannelPipeline에 대한 글을 찾던 중, 아주 잘 정리한 Medium의 글을 알게 되었다. (여기 클릭)
사실 공식 문서에서는 설명을 하고 예시를 주고 땡(?)이다.
나름 잘 정리되어 있기는 하지만, 이를 더 잘 정리한 고수님들의 글을 참고하면 좋을 듯하다.
Netty의 데이터 모델 과 Channel 그리고 ChannelPipeline
Data model
Netty’s data model is fairly straightforward: the core type is a Channel, which has its own ChannelPipeline and is associated with a single EventLoop from an EventLoopGroup.
데이터 모델
netty의 데이터모델은 꽤 직설적이다. 핵심 타입은 Channel인데, ChannelPipeline을 가지고 있고 EventLoopGroup에서 하나의 EventLoop와 연관되어 있다.
각 채널들은 자신만의 채널 파이프라인을 가지고 있고, 이는 Handler들을 넘기는 구조이다.

인터페이스 ChannelPipelines
한 채널의 inbound 이벤트들과 outbound 작업들을 다루거나 가로채는(intercept) ChannelHandler의 리스트.
ChannelPipeline은 Intercepting Filter 패턴의 고급 형태를 구현하여, 사용자에게 이벤트가 어떻게 처리되는지
그리고 파이프라인 내의 ChannelHandler들이 서로 어떻게 상호작용하는지에 대한 완전한 제어권을 제공한다;
채널 안에 채널 파이프라인이 있고, 이것을 EventLoopGroup 내부의 EventLoop가 바라보는 형식이다.
여기서 ChannelPipeline은 Intercepting Filter 패턴의 고급 형태를 구현한다는데,
단순한 filter에서 뭔가 ... 기능이 더 추가된걸로 이해된다.

Hook
상태변경 시점에 낚아채서 별도의 작업을 할 수 있도록 하는 기능이다.
Handler이것들이 Hook, 즉 무언가 상태가 변경되는 시점인가보다.
그러면 뭐의 상태가 변할 때 Hook이 걸린다는건지... 하고 찾아보니 Channel이 그렇다.
Channel의 주요 메서드
Netty의 Channel 인터페이스는 다양한 상태 관리 메서드를 제공한다:
상태 조회 메서드
- isOpen(): 채널이 열려 있는가?
- isRegistered(): EventLoop에 등록되었는가?
- isActive(): 활성 상태이고 연결되어 있는가?
- isWritable(): 쓰기가 가능한가?
설정 및 정보
- config(): 채널 설정 반환
- pipeline(): ChannelPipeline 반환
- eventLoop(): 연결된 EventLoop 반환
I/O 작업
- read(), write(), flush()
- connect(), bind(), close()
기존 Java NIO의 Channel은 isOpen()과 close() 정도만 제공했지만,
Netty는 훨씬 풍부한 상태 관리와 제어 기능을 제공한다.
전체 API 문서: [Netty Channel Interface](https://netty.io/4.0/api/io/netty/channel/Channel.html)
하여간 다양한 상태가 있는데, 기존의 nio 에서 지원하는 channel의 상태는 isOpen, close 밖에 없지만 netty에서의 channel은 훨씬 많은 상태 제어를 제공한다.
Skeleton Implementation

찾아보니 Effective Java에서 소개된 패턴이라고한다.
[이펙티브 자바] 아이템 20 - 추상 클래스보다는 인터페이스를 우선하라 인데...
인터페이스와 추상클래스를 결합한 패턴이며, 인터페이스만 사용시 중복 코드가 계속 생기지만, 이러한 중복 코드를 추상클래스로 뺀 것이다.
즉, 미리 추상클래스에서 선언해놓고, 실제로 상속받아서 구현하는 것의 경우에는 원하는 것만 override하면 된다.
예시
// 1. 인터페이스 (규약)
public interface MyInterface {
void method1();
void method2();
void method3();
void method4();
}
// 2. 추상 골격 클래스 (공통 구현)
public abstract class AbstractMyInterface implements MyInterface {
// 공통 구현 제공
@Override
public void method1() {
// 기본 구현
}
@Override
public void method2() {
// 기본 구현
}
// method3, method4는 추상 메서드로 남겨둠
// (구현 클래스가 반드시 구현해야 함)
}
// 3. 구체 클래스 (필요한 것만 구현)
public class ImplA extends AbstractMyInterface {
@Override
public void method3() {
// A만의 구현
}
@Override
public void method4() {
// A만의 구현
}
// method1, method2는 상속받아서 사용!
}
5. 다시 예시 확인하기 - DiscardServer
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
* Handles a server-side channel.
*/
public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1)
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
// Discard the received data silently.
((ByteBuf) msg).release(); // (3)
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}
코드를 읽어보면, Channel 내부의 ChannelPipeline에 서 관리하는 DiscardServerHandler를 구현한 것이다.
release()하는 코드는 channel로 넘어오는 msg를 그대로 버린다.
exceptionCaught 는 I/O 작업시 혹은 이벤트 처리시에 에러가 날 시 에러를 처리한다.
주석에 적힌대로 에러가 발생하면 연결을 끊는다.
일반적으로 데이터를 처리하려면 연결을 열고, 처리하고 닫는게 순리니까...
ByteBuf는 기존에 Java 에서 제공하는 ByteBuffer을 좀 더 쉽게 사용할 수 있도록 Netty에서 제공하는 class이다.
우리는 Handler, 즉 channl내로 들어오는 응답을 처리하는 handler를 만든 것이다.
그러면 이것을 실제로 쓸 수 있도록 해야 하지 않나?
server를 만들어서 Handler를 배정해야한다.
DiscardServer.java
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* Discards any incoming data.
*/
public class DiscardServer {
private int port;
public DiscardServer(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(new ChannelInitializer<SocketChannel>() { // (4)
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new DiscardServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128) // (5)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
// Bind and start to accept incoming connections.
ChannelFuture f = b.bind(port).sync(); // (7)
// Wait until the server socket is closed.
// In this example, this does not happen, but you can do that to gracefully
// shut down your server.
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
}
new DiscardServer(port).run();
}
}
- NioEventLoopGroup: I/O 작업을 처리하는 멀티스레드 이벤트 루프로, boss(연결 수락)와 worker(트래픽 처리) 두 개를 사용함
- ServerBootstrap: 서버 설정을 쉽게 해주는 헬퍼 클래스
- NioServerSocketChannel: 들어오는 연결을 수락하기 위한 서버 채널 클래스
- ChannelInitializer: 새로 수락된 Channel의 파이프라인에 핸들러를 추가하여 구성하는 특수 핸들러
- Channel 옵션: TCP 소켓 옵션(tcpNoDelay, keepAlive 등)을 설정할 수 있음
- option() vs childOption(): option()은 ServerSocketChannel용, childOption()은 클라이언트 SocketChannel용 설정
- 서버 시작: bind() 메서드로 포트에 바인딩하여 서버를 시작하며, 여러 번 호출해서 여러 포트에 바인딩 가능
이제 그럼 테스트를 해봐야지?

아 이러면... 넘어오는지 아닌지 확인해야하니 코드를 잠시 변경해보겠다.
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
* Handles a server-side channel.
*/
public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1)
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
// Discard the received data silently.
ByteBuf msg1 = (ByteBuf) msg;
String string = msg1.toString();
System.out.println("string = " + string);
msg1.release(); // (3)
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}
그리고 요청을 보내면 다음과 같이 나올것이다.

이렇게 여러개 알음알음 찾아보니... 결국 사용시에는 Channel 내부에 Handler를 잘 사용할 줄 알면 될 것 같다는 느낌도 들엇다.
그리고 처음에 코드를 보니 아 뭐야 예제 다 있네 껌이네! 하고 찾아보니 글이 엄청 길어졌다.
이렇게 생각하면 어떨까, 저렇게 생각하면 어떨까 하는 생각이 들어서 여러개 찾아보고 알아보다보니 나름 시간이 오래 걸렷다.
사실 불과 1년 전만해도 아무 강의나 막 보고 그랫었는데,
이러한 이해를 바탕으로 뭔가 하지 않으면 정말 금방 까먹는다는것을 깨닫고 (몇년이 걸린거냐 에휴 바보 ㅠㅠ)
현재 이렇게 찾다보니 정말 많은 것을 배울 수 있었다.
앞으로도 공식문서를 보고, AI와 함께 찾아보면서 학습하려고 한다.
또한, 공부를 진행하면서 부족한 부분이나 그러한 부분은 여태 좀 부족할 수 있으나 생각이 나면 다시 덫붙여서 사용하려고 한다.
아, 그리고 Effective Java도 2년전에 보려다가 에잇 하고 왜 필요한가 하고 치웠는데... 다시 이 책을 꺼낼때가 된 것 같다.
볼 당시에는 아... 이게 뭔 말인가 정말 필요한건가 했는데, 이 책에서 각 챕터가 주는 의미를 잘 이해하지 못했었다.
역시 고기도 먹어본 놈이 먹는다고 좀 뭘 알아야 아는것 같다.
다음 글에서는 더 많은 예시와 NioEventLoopGroup, ServerBootstrap, ChannelInitializer 에 대해 알아볼 예정이다.
물론! 이전에 Netty의 Architecture에 대해 탐방도 할 예정이다.
참고
https://docs.spring.io/spring-framework/reference/web/webflux.html
https://docs.spring.io/spring-framework/reference/web/webflux/new-framework.html
https://netty.io/wiki/user-guide-for-4.x.html
https://netty.io/4.0/api/io/netty/channel/ChannelHandler.html
https://medium.com/@akhaku/netty-data-model-threading-and-gotchas-cab820e4815a
'Spring > Spring Framework' 카테고리의 다른 글
| [Netty] Netty가 뭐에요? - 3탄 : Netty의 특징과 구조 및 NIO 테스트 (2) | 2025.09.28 |
|---|---|
| [Netty] Netty가 뭐에요? - 2탄 : Netty가 필요하기 까지 (2) | 2025.09.24 |
| [Netty] Netty가 뭐에요? - 1탄 : Netty 란? - 용어 정리 (3) | 2025.09.18 |