본문 바로가기
IT/JAVA&SpringBoot

[Java 21] 서버 성능을 10배 높이는 비밀: 블로킹 vs 논블로킹, 그리고 가상 스레드의 혁명

by twofootdog 2025. 12. 15.
반응형

백엔드 개발을 하다 보면 "트래픽이 몰리면 왜 서버가 느려질까?", "AWS 비용을 줄이면서 동시 접속자를 늘릴 수는 없을까?"라는 고민에 빠지게 됩니다. 서버의 성능을 결정짓는 가장 핵심적인 요소는 바로 '데이터 입출력(I/O)을 어떻게 처리하느냐'에 달려 있습니다. 

 

오늘은 자바 애플리케이션의 심장부라 할 수 있는 I/O 모델에 대한 설명과, 최근 Java 21 업데이트로 인해 완전히 뒤바뀐 새로운 트렌드까지 아주 쉽고 친절하게 정리해 드리겠습니다.

 


1. 전통적인 방식: "한 놈만 팬다" 블로킹 I/O (Thread-per-Request)

가장 먼저 우리가 오랫동안 사용해왔던 RestTemplate이나 OpenFeign, 그리고 최신의 RestClient가 기본적으로 사용하는 방식인 블로킹 I/O에 대해 알아보겠습니다.

이 모델은 아주 직관적입니다. 손님(요청)이 오면 점원(스레드) 한 명이 끝까지 책임지는 방식입니다.

  • 동작 원리: 클라이언트가 요청을 보내면, 스레드는 응답이 올 때까지 'BLOCKED' 상태가 됩니다. 즉, 아무것도 안 하고 멍하니 기다리는 것이죠.
  • 문제점: 여기서 치명적인 문제가 발생합니다. 바로 '비용'입니다.

운영체제(OS) 입장에서 스레드는 생성하는 데 비용이 많이 들고, 메모리(스택)도 꽤 많이 차지하는 비싼 자원입니다. 만약 동시 요청이 1,000개가 들어오면 스레드도 1,000개를 만들어야 합니다. 이때 CPU는 이 많은 스레드를 관리하느라(컨텍스트 스위칭) 정작 중요한 일은 못 하고 체력을 낭비하게 됩니다. 

게다가 외부 API가 느려져서 응답이 늦어지면 어떻게 될까요? 우리 서버의 스레드 풀이 대기 상태로 꽉 차버려서, 새로운 요청을 아예 받을 수 없는 '스레드 풀 고갈(Thread Pool Hell)' 상태가 됩니다. 이는 결국 서버 전체가 멈추는 연쇄 장애로 이어지기 십상입니다. 

 

 


2. 리액티브 혁명: "쉬지 않고 일한다" 논블로킹 I/O (Event Loop)

블로킹 방식의 비효율성을 해결하기 위해 등장한 것이 바로 WebClient가 채택한 논블로킹 I/O입니다. Netty 같은 비동기 라이브러리를 기반으로 하며, 완전히 다른 방식으로 동작합니다.

  • 동작 원리 (이벤트 루프): 마치 스타벅스 진동벨 시스템과 비슷합니다. 점원(스레드)은 주문만 받고 바로 다음 손님을 받습니다. 커피가 완성되면 진동벨(이벤트)이 울리고 그때 처리합니다.
  • 효율성: 아주 적은 수의 스레드(보통 CPU 코어 수의 2배)만으로도 수만 개의 요청을 처리할 수 있습니다. 스레드가 노는 시간(Wait)이 없기 때문이죠.

이 방식은 대기 시간이 긴 I/O 작업이 많은 게이트웨이나 스트리밍 서비스에서 엄청난 효율을 보여줍니다. 하지만 치명적인 단점이 하나 있습니다. 바로 코드를 짜기가 너무 어렵다는 것입니다. 흐름이 복잡하고 디버깅이 힘들어서, 성능은 좋지만 개발자들의 머리를 아프게 만들었습니다. 

 

 


3. 게임 체인저의 등장: 가상 스레드 (Virtual Threads)

"블로킹 방식처럼 코드는 쉽게 짜고 싶은데, 성능은 논블로킹처럼 낼 수 없을까?"

이 꿈같은 이야기를 현실로 만들어준 것이 바로 Java 21의 가상 스레드(Project Loom)입니다. 이것은 기존의 판도를 완전히 뒤집어 놓았습니다.

  • 가상 스레드란?: 기존의 OS 스레드와 1:1로 매핑되는 무거운 스레드가 아닙니다. JVM 내부에서 수백만 개를 가볍게 생성할 수 있는 경량 스레드입니다.
  • 어떻게 동작하나요?: RestClient 같은 동기 코드를 실행하다가 I/O 대기(블로킹)가 발생하면, JVM이 아주 똑똑하게 해당 가상 스레드를 잠시 내려놓습니다(Unmount). 그리고 실제 일하는 스레드(Carrier Thread)는 즉시 다른 작업을 처리하러 갑니다.

즉, 개발자는 머리 아픈 리액티브 코드를 짤 필요 없이 기존처럼 편하게 코드를 짜면(동기식), JVM이 알아서 논블로킹처럼 효율적으로 처리(비동기 효율)해주는 것입니다.

이것이 바로 Spring Boot 3.4 시대에 복잡한 WebClient 대신, 다시 RestClient가 주목받는 이유입니다. 단순함과 성능, 두 마리 토끼를 모두 잡았기 때문입니다. 

 

 


📝 요약

오늘 내용을 한 줄로 요약하자면 다음과 같습니다.

  1. 블로킹 I/O: 쉽지만 스레드 낭비가 심하고 확장에 한계가 있다. (RestTemplate)
  2. 논블로킹 I/O: 성능은 최고지만 코드가 어렵고 러닝 커브가 높다. (WebClient)
  3. 가상 스레드: 쉬운 코드로 최고의 성능을 낸다. (RestClient + Java 21)

여러분이 만약 Java 21 이상을 사용하는 환경에서 새로운 프로젝트를 시작한다면, 망설임 없이 가상 스레드와 RestClient 조합을 선택하시길 추천해 드립니다. 그것이 바로 유지보수하기 쉬우면서도, 억 단위 트래픽을 견디는 고성능 서버를 만드는 지름길입니다.

 


참조 링크

  1. [InfoQ] Java 21의 가상 스레드: 고성능 동시성의 새로운 표준 : 바로 가기
  2. [Spring Blog] 가상 스레드 수용하기 (Embracing Virtual Threads) : 바로 가기
  3. [Naver D2] 자바의 미래, Virtual Thread 파헤치기 : 바로 가기
  4. OpenJDK Project Loom 공식 페이지 : 바로가기
  5. Spring Boot 3.2 릴리즈 노트 : 바로가기
  6. Tecoble - Blocking vs Non-Blocking, Sync vs Async : 바로가기
  7. Oracle Java Documentation - Virtual Threads : 바로가기
  8. Netflix Tech Blog - Virtual Threads for High Throughput : 바로가기
  9. [Baeldung] Java Virtual Threads vs Reactive Programming : 바로 가기
반응형

댓글