어제 오늘 내일

[Spring WebFlux] "MVC랑 뭐가 달라요?" 블로킹(Blocking) vs 논블로킹(Non-Blocking) 완벽 이해 본문

IT/SpringBoot

[Spring WebFlux] "MVC랑 뭐가 달라요?" 블로킹(Blocking) vs 논블로킹(Non-Blocking) 완벽 이해

hi.anna 2026. 3. 30. 08:59

 

우리가 지금까지 써왔던 Spring MVC는 정말 훌륭한 프레임워크입니다. 안정적이고, 코짜기도 쉽고, 디버깅도 편하죠.
하지만 서비스가 커지고 동시 접속자가 수만 명을 넘어가면 문제가 생기기 시작합니다. 서버를 아무리 늘려도(Scale-out) 응답 속도가 느려지고, 메모리가 부족해집니다.

이유는 단 하나, MVC가 "기다림의 미학(Blocking)"을 추구하기 때문입니다.
반면 WebFlux는 "기다리지 않는 효율성(Non-Blocking)"을 추구합니다. 이 둘의 차이를 완벽하게 파헤쳐 보겠습니다.


1. Spring MVC: "손님 1명당 직원 1명" (Thread per Request)

MVC는 전통적인 서블릿(Servlet) 모델을 따릅니다.
요청이 들어올 때마다 쓰레드(Thread)라는 일꾼을 하나씩 배정합니다.

  1. 요청 도착: 쓰레드 풀에서 놀고 있는 쓰레드 A를 깨웁니다.
  2. 작업 수행: 쓰레드 A가 DB에 쿼리를 날립니다.
  3. 대기 (Blocking): DB가 응답을 줄 때까지 쓰레드 A는 아무것도 안 하고 멍하니 기다립니다. (이게 문제입니다!)
  4. 응답: DB 응답이 오면 쓰레드 A가 결과를 가지고 사용자에게 반환합니다.

문제점: 직원(Thread)이 부족해요!

만약 DB 처리가 3초 걸린다면? 쓰레드 A는 3초 동안 마비됩니다.
동시 접속자가 폭주해서 쓰레드 풀(기본 200개)이 꽉 차면? 201번째 사용자는 대기열(Queue)에서 하염없이 기다려야 합니다. 이를 쓰레드 지옥(Thread Hell)이라고 부릅니다.


2. Spring WebFlux: "직원 1명이 모든 주문을 처리" (Event Loop)

WebFlux는 이벤트 루프(Event Loop) 방식을 사용합니다. Node.js와 비슷한 구조죠.
적은 수의 쓰레드(보통 CPU 코어 수만큼)만으로 엄청난 트래픽을 처리합니다.

  1. 요청 도착: 요청을 받는 직원(Event Loop)이 주문만 받고 바로 주방(DB)에 던집니다.
  2. 작업 수행: "DB야, 데이터 찾으면 나한테 알려줘(Callback)!"라고 말하고, 직원은 기다리지 않고 바로 다음 손님의 주문을 받으러 갑니다. (Non-Blocking)
  3. 이벤트 발생: DB가 일을 다 하면 "다 찾았어요!"라고 이벤트를 보냅니다.
  4. 응답: 직원이 그 이벤트를 받아서 사용자에게 결과를 전달합니다.

장점: 직원이 놀지 않아요!

쓰레드가 멍하니 기다리는 시간이 없습니다. 끊임없이 다음 요청을 처리하죠. 그래서 적은 리소스로도 동시성(Concurrency)이 엄청나게 높아집니다.


3. 쉬운 비유: 동네 분식집 vs 스타벅스

이 차이를 가장 쉽게 이해하는 비유가 있습니다.

  • Spring MVC (동네 분식집):
  • 아주머니(Thread)가 주문을 받습니다.
  • 라면을 끓입니다. (3분 소요)
  • 라면을 손님에게 주고 나서야, 다음 손님의 주문을 받습니다.
  • 손님이 많아지면? 아주머니를 계속 고용해야 합니다. (메모리 부족)
  • Spring WebFlux (스타벅스):
  • 직원(Event Loop)이 주문을 받고 진동벨을 줍니다. (0.1초 소요)
  • "라떼 나왔습니다~" 할 때까지 직원은 다른 손님 100명의 주문을 더 받습니다.
  • 커피 머신(DB)이 커피를 다 내리면, 직원이 진동벨을 울립니다.
  • 직원 1명으로도 수백 명을 커버할 수 있습니다.

4. 성능 비교: 언제 빨라지나요?

"그럼 무조건 WebFlux가 빠른가요?"
아니요, 그렇지 않습니다.

  • 요청량이 적을 때: MVC가 더 빠를 수 있습니다. (WebFlux는 구조가 복잡해서 기본 오버헤드가 있음)
  • 요청량이 폭주할 때: MVC는 쓰레드가 고갈되어 성능이 수직 하락하지만, WebFlux는 일정한 응답 속도를 유지하며 버팁니다.

즉, WebFlux는 "개별 요청 속도"를 빠르게 하는 게 아니라, "동시 처리량(Throughput)"을 늘리는 기술입니다.


5. 언제 WebFlux를 써야 하나요?

무작정 도입하면 개발 생산성만 떨어집니다. 다음 상황에서만 고려하세요.

  1. 고성능/고효율이 필요할 때: 적은 서버 자원으로 많은 트래픽을 감당해야 할 때.
  2. 느린 외부 API 연동이 많을 때: 마이크로서비스(MSA) 환경에서 다른 서버의 응답을 기다리는 시간이 길 때.
  3. 스트리밍 서비스: 넷플릭스처럼 데이터를 지속적으로 흘려보내야 할 때.
  4. 실시간 통신: 웹소켓이나 채팅 서버.

반대로, 전통적인 CRUD 게시판이나 복잡한 비즈니스 로직이 많은 쇼핑몰(결제 등)은 여전히 Spring MVC가 더 좋은 선택일 수 있습니다.


마치며

오늘의 결론입니다.

  1. MVC (Blocking)는 요청당 쓰레드를 하나씩 써서, I/O 작업 시 멍하니 기다리는 시간이 많다.
  2. WebFlux (Non-Blocking)는 이벤트 루프를 써서, 기다리지 않고 딴 일을 하다가 완료되면 돌아온다.
  3. WebFlux는 적은 자원으로 최대의 동시성을 뽑아내는 기술이다.

개념은 잡혔지만, 막상 코드를 짜려면 막막하실 겁니다. List<String> 대신 Flux<String>이라는 이상한 녀석이 나오거든요.

다음 포스팅에서는 "데이터가 물처럼 흐른다?" 리액티브 프로그래밍의 핵심 개념인 리액티브 스트림즈(Reactive Streams)와 Backpressure에 대해 알아보겠습니다.

도움이 되셨다면 좋아요와 댓글 부탁드립니다! 😊

반응형
Comments