스프링부트 에서 netty에 대한 설명과 사용방법을 정리

스프링부트에서 Netty는 주로 WebFlux의 기본 서버로 쓰이며, 논블로킹 I/O와 이벤트 기반 처리에 강한 네트워크 프레임워크입니다. 일반적인 Spring MVC는 서블릿 컨테이너(Tomcat 등)를 쓰는 반면, Spring Boot WebFlux는 Netty 같은 런타임을 기본으로 선택할 수 있습니다.

Netty란

Netty는 자바로 만든 고성능 네트워크 애플리케이션 프레임워크로, TCP/UDP/HTTP/WebSocket 같은 다양한 프로토콜을 다루기 좋게 추상화해 둔 라이브러리입니다.
핵심은 논블로킹이벤트 루프 기반채널 파이프라인 구조입니다. 연결을 받는 스레드와 실제 I/O를 처리하는 스레드를 분리해, 많은 연결을 효율적으로 처리하는 데 유리합니다.

스프링부트에서 쓰는 경우

스프링부트에서 Netty를 쓰는 가장 흔한 경우는 WebFlux 기반의 리액티브 웹 애플리케이션입니다. 이때 기본 내장 서버로 Netty가 선택될 수 있습니다.
또 다른 경우는 HTTP 서버가 아니라, TCP 소켓 서버나 커스텀 프로토콜 서버를 따로 만들 때입니다. 이 경우에는 Spring 애플리케이션 안에서 Netty 서버를 별도로 띄우는 방식으로 많이 구성합니다.

장점과 단점

장점은 비동기 논블로킹 처리로 높은 동시성에 강하고, 프로토콜 제어가 세밀하며, WebSocket이나 커스텀 TCP 프로토콜 구현이 편하다는 점입니다.
단점은 Spring MVC/Tomcat 방식보다 구조가 낯설고, 블로킹 코드와 섞으면 Netty의 장점이 크게 줄어든다는 점입니다. 따라서 DB 호출이나 외부 API 호출도 리액티브 방식으로 맞추는 편이 좋습니다.

스프링부트에서 사용법

가장 간단한 방법은 WebFlux 의존성을 넣는 것입니다. 그러면 기본적으로 Netty 기반으로 동작하는 구성이 가능합니다.

Gradle 예시

text
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webflux'
}

Maven 예시

xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

이렇게 하면 spring-boot-starter-web 대신 WebFlux를 사용하게 되고, 리액티브 웹 서버로 Netty를 활용하는 구성이 됩니다.

기본 구조

Netty 애플리케이션은 보통 아래 흐름으로 구성합니다.

  1. Bootstrap 또는 ServerBootstrap으로 서버 설정을 만든다.

  2. EventLoopGroup을 생성해 accept와 I/O 처리를 분리한다.

  3. ChannelInitializer에서 pipeline에 handler를 연결한다.

  4. handler에서 inbound/outbound 이벤트를 처리한다.

간단한 TCP 서버 예시

java
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();

try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new SimpleChannelInboundHandler<ByteBuf>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {
ctx.writeAndFlush(msg.retain());
}
});
}
});

ChannelFuture f = b.bind(8080).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}

이 예시는 클라이언트가 보낸 데이터를 그대로 돌려주는 에코 서버 형태입니다. Netty의 기본 구조를 이해하기에 좋은 출발점입니다.

실무에서 주의할 점

Netty에서는 이벤트 루프 스레드에서 오래 걸리는 작업을 하면 안 됩니다. 블로킹 DB 작업이나 무거운 계산을 직접 넣으면 전체 처리량이 급격히 떨어질 수 있습니다.
Spring WebFlux와 같이 쓸 때도, 리액티브 체인을 깨는 블로킹 호출은 피하는 게 좋습니다. 필요하면 별도 스레드풀이나 리액티브 드라이버를 사용해야 합니다.

언제 선택하면 좋은가

다음 상황이면 Netty가 잘 맞습니다.

  • 동시 접속이 많고 논블로킹이 중요한 서비스.

  • WebSocket, TCP 소켓, 커스텀 프로토콜 같은 네트워크 제어가 필요한 서비스.

  • Spring WebFlux 기반으로 리액티브 스택을 끝까지 가져가고 싶은 경우.

반대로 일반 CRUD 웹앱이라면 Tomcat 기반 Spring MVC가 더 단순하고 유지보수가 쉬운 경우가 많습니다. Netty는 강력하지만, 필요한 이유가 분명할 때 쓰는 편이 좋습니다.

스프링부트 에서 netty에 대한 설명과 사용방법을 정리

 Spring Boot에서 Netty를 “웹 서버로 쓸 때”와 “TCP 소켓 서버로 직접 쓸 때”를 명확히 구분해서 볼 수 있습니다. WebFlux를 쓰면 Spring Boot가 기본적으로 Reactor Netty를 서버로 잡을 수 있고, 별도 TCP 서버는 Netty ServerBootstrap으로 직접 띄우는 방식이 일반적입니다.

전체 구조

Spring Boot + Netty 프로젝트는 보통 webflux 기반 HTTP 서버와 netty 기반 TCP 서버를 분리해서 생각하면 설계가 쉬워집니다. HTTP API는 Spring WebFlux, 커스텀 프로토콜이나 소켓 통신은 순수 Netty로 나누는 구성이 많습니다.
실무에서는 한 프로젝트 안에 두 서버를 함께 넣기도 하지만, 운영과 배포를 단순하게 하려면 모듈을 분리하는 편이 좋습니다.

권장 패키지 예시

text
src/main/java/com.example.app
├── AppApplication.java
├── config
│ ├── WebFluxConfig.java
│ └── NettyServerConfig.java
├── web
│ └── ApiController.java
├── tcp
│ ├── TcpServer.java
│ ├── TcpChannelInitializer.java
│ ├── handler
│ │ └── EchoHandler.java
│ └── codec
│ └── MessageCodec.java
└── service
└── BusinessService.java

이 구조는 WebFlux 설정, Netty 서버 설정, 비즈니스 로직을 분리해서 테스트와 유지보수를 쉽게 하려는 목적에 맞습니다.

WebFlux 설정

Spring Boot에서 WebFlux를 쓰면 Reactor Netty가 기본 서버로 자동 구성될 수 있습니다. 즉, spring-boot-starter-webflux를 넣는 것만으로도 Netty 기반 HTTP 서버 구성이 가능합니다.
추가로 서버 포트를 바꾸거나 Netty 전용 옵션을 주고 싶으면 application.yml에서 조정하는 방식이 가장 단순합니다.

build.gradle

text
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webflux'
}

application.yml

text
server:
port: 8080

간단한 WebFlux 컨트롤러

java
@RestController
@RequestMapping("/api")
public class ApiController {

@GetMapping("/hello")
public Mono<String> hello() {
return Mono.just("hello netty");
}
}

WebFlux에서는 반환형이 Mono나 Flux가 되는 흐름이 자연스럽고, 블로킹 코드를 최소화하는 것이 중요합니다.

Netty 서버 직접 설정

TCP 소켓 서버처럼 HTTP가 아닌 통신을 하려면 Netty를 직접 설정합니다. 이때 핵심은 EventLoopGroupServerBootstrapChannelInitializer, pipeline handler입니다.
bossGroup은 연결 수락, workerGroup은 실제 I/O 처리를 담당하는 형태로 나누는 구성이 일반적입니다.

의존성

text
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'io.netty:netty-all'
}

Netty 서버 코드

java
@Component
public class TcpServer implements CommandLineRunner {

@Override
public void run(String... args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();

try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new TcpChannelInitializer());

ChannelFuture future = bootstrap.bind(9090).sync();
future.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}

이 방식은 Spring Boot의 생명주기 안에서 Netty 서버를 함께 시작하는 패턴입니다.

채널 초기화

ChannelInitializer에서 codec과 handler를 pipeline에 붙입니다. TCP는 메시지 경계가 없어서, 문자 단위로 받을지, 길이 프레임 기반으로 받을지 먼저 정해야 합니다.
예제는 가장 단순한 에코 서버 형태로, 들어온 문자열을 그대로 응답하는 구조입니다.

ChannelInitializer

java
public class TcpChannelInitializer extends ChannelInitializer<SocketChannel> {

@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline()
.addLast(new LineBasedFrameDecoder(1024))
.addLast(new StringDecoder())
.addLast(new StringEncoder())
.addLast(new EchoHandler());
}
}

Handler

java
public class EchoHandler extends SimpleChannelInboundHandler<String> {

@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
ctx.writeAndFlush("echo: " + msg + "\n");
}
}

이 예시는 줄바꿈 기준으로 프레임을 자르고 문자열로 처리하는 방식이라 테스트하기 쉽습니다.

서버 분리 기준

HTTP API만 필요하면 WebFlux 하나로 충분하고, Netty를 직접 쓸 이유가 크지 않을 수 있습니다. 반대로 장비 연동, 게임 서버, 사내 프로토콜, 실시간 메시징처럼 TCP 제어가 중요하면 직접 Netty를 쓰는 편이 적합합니다.
실무에서는 “WebFlux + Netty HTTP”와 “Netty TCP 서버”를 한 서비스에 섞기보다 역할별로 분리하는 경우가 많습니다. 구조가 단순해지고 장애 원인도 추적하기 쉬워집니다.

주의할 점

Netty 이벤트 루프 안에서는 블로킹 작업을 피해야 합니다. DB 조회나 외부 API 호출이 오래 걸리면 전체 처리 성능에 영향을 줄 수 있습니다.
또한 TCP는 패킷 경계가 없기 때문에, StringDecoder만 넣는다고 안전하게 한 번에 한 메시지가 들어온다고 가정하면 안 됩니다. 길이 기반 프레이밍이나 구분자 기반 프레이밍을 반드시 고려해야 합니다.

추천 시작점

가장 쉬운 시작은 WebFlux로 HTTP 서버를 먼저 만들고, TCP가 필요할 때만 별도 Netty 서버를 추가하는 방식입니다. 그러면 Spring Boot의 장점과 Netty의 장점을 동시에 가져갈 수 있습니다.
현재 필요한 예제가 있으면 다음 중 하나로 바로 확장할 수 있습니다:

  • 길이 프레임 기반 TCP 서버.

  • JSON 프로토콜 TCP 서버.

  • Spring WebFlux와 Netty TCP 서버를 함께 띄우는 멀티 모듈 구조.