목차
1. 컨텍스트 스위칭
2. 동기/비동기 처리
3. Blocking I/O
4. Non-Blocking I/O
5. 동기/비동기와 Blocking/Non-Blocking의 차이
6. Spring MVC, Spring WebFlux 비교
7. 왜 Spring WebFlux를 사용하는가?
스프링 MVC와 스프링 WebFlux를 이해하기 위해 알아야 할 CS지식을 먼저 다루고, 스프링 MVC와 스프링 WebFlux에서 어떤 차이가 있는지, WebFlux의 장단점, 어떠한 경우에 쓰는 것이 적절한지를 다루어보겠다.
컨텍스트 스위칭은 CPU가 하나의 프로세스에서 다른 프로세스로 실행을 전환하는 과정을 의미한다. 마치 한 사람이 여러 개의 일을 처리하기 위해 하던 일을 멈추고 다른 일을 전환하는 것과 같다. 운영체제는 CPU의 유휴시간을 효율적으로 활용하고 여러 프로세스가 동시에 실행되는 것처럼 보이게 하기 위해 이 작업을 수행한다.
이 과정은 현재 실행 중이던 프로세스의 상태(레지스터 값, 프로그램 카운터 등)를 PCB(Process Control Block)라는 메모리 공간에 저장하고, 다음에 실행할 프로세스의 상태를 불러와 CPU에 로드하는 방식으로 이루어진다. 이렇게 한 프로세스에서 다른 프로세스로 전환되는 동안 CPU는 실제적인 작업은 하지 않고 상태를 저장하고 복원하는 데 자원을 사용하게 되는데, 이를 컨텍스트 스위칭 오버헤드라고 부른다. I/O 대기나 타임 퀀텀만료 등 다양한 이유로 컨텍스트 스위칭이 빈번하게 일어나면, 이러한 오버헤드가 누적되어 전체적인 시스템 성능이 저하될 수 있다.
동기 처리는 요청이 완료될 때까지 다음 작업을 시작하지 않고 기다리는 방식이다.
비동기 처리는 작업이 완료될 때까지 기다리지 않고 다음 작업을 바로 시작하는 방법이다.
동기/비동기 처리 방식은 I/O 작업(예: 데이터베이스 조회, 외부 API 호출)을 어떻게 다루는지에 따라 Blocking I/O와 Non-Blocking I/O로 나뉜다.
Blocking I/O는 스레드가 외부 I/O 작업의 완료를 기다리느라 멈춰서는(block) 방식이다. 한 스레드가 디스크나 네트워크에 데이터를 요청하면, 운영체제는 이 스레드를 '대기' 상태로 바꾸고, CPU는 이 스레드가 데이터가 도착할 때까지 아무 일도 하지 않고 놀게 된다. 이러한 CPU의 유휴상태를 막기 위해, 운영체제는 현재 멈춰선 스레드의 상태를 저장하고 다른 실행 가능한 스레드로 컨텍스트 스위칭을 수행한다. 그러나 이는 CPU에게는 추가적인 작업이 되며, I/O 요청이 많아질수록 컨텍스트 스위칭 횟수가 기하급수적으로 늘어나 전체적인 성능을 저하시키는 오버헤드가 발생한다.
반면, Non-Blocking I/O는 스레드가 I/O 작업의 완료를 기다리지 않고 즉시 다음 작업을 진행하도록 설계되었다. 한 스레드가 디스크나 네트워크에 데이터를 요청하면, 운영체제는 I/O 작업을 백그라운드에서 처리하게 하고 스레드에게 '지금은 데이터가 준비되지 않았으니 나중에 다시 확인하라'는 신호를 즉시 보낸다. 따라서 스레드는 멈춰서지 않고 바로 다음 작업을 처리한다. I/O 작업이 완료되면, 운영체제는 준비 완료 상태를 알리는 이벤트를 발생시키고, CPU는 소수의 스레드로 실행되는 이벤트 루프를 통해 이 이벤트를 감지한다. 그리고 나서 해당 데이터가 필요한 콜백 함수를 실행한다. 이 방식은 I/O 작업 대기 동안 스레드가 멈추지 않아도 되므로 컨텍스트 스위칭이 거의 발생하지 않는다. 결과적으로 CPU를 지속적으로 효율적으로 사용하게 되어 동시에 많은 요청을 처리하는데 유리하다.
동기/비동기는 작업의 조정방식을 나타내는 개념이고, Blocking/Non-Blocking은 I/O 작업의 특성을 나타내는 개념이다.
스프링 MVC | 스프링 웹플럭스 | |
---|---|---|
처리 모델 | 동기, Blocking I/O | 비동기, Non-Blocking I/O |
동시성 모델 | 요청당 스레드 할당 | 소수의 스레드로 여러 요청 처리(이벤트 루프) |
적용 기술 | Servlet API, Tomcat, Jetty | Reactive Streams API(Reactor), Netty |
코드 스타일 | 명령형 프로그래밍 | 함수형, 반응형 프로그래밍 |
자원 활용 | I/O 대기시 스레드 낭비 | I/O 대기시 스레드 재사용 |
컨텍스트 스위칭 | I/O 대기시 빈번하게 발생 | 최소화 |
적합한 환경 | I/O 작업이 적거나 CPU 중심적, 개발 편의성이 중요한 경우 | I/O 작업이 많고 동시성 높은 환경 (마이크로서비스, 스트리밍) |
Spring WebFlux의 가장 큰 특징은 Non-Blocking I/O 모델을 기반으로 한다는 점이다. 전통적인 스프링 MVC가 요청 하나당 하나의 스레드를 할당하고 I/O 작업이 완료될 때까지 해당 스레드를 멈춰세우는 반면, WebFlux는 I/O 작업이 진행되는 동안 스레드를 멈추지 않고 다른 요청을 계속 처리하기 때문이다.
이러한 방식은 적은 수의 스레드로도 수많은 요청을 효율적으로 처리할 수 있게 해준다. 느린 외부 서비스 호출(데이터베이스 조회, 마이크로서비스 통신, 외부 API 호출 등)이 많은 환경에서, Blocking I/O 모델은 스레드 자원고갈을 초래하지만 Non-Blocking I/O는 이러한 문제 없이 높은 성능을 유지하기 때문이다.
스프링 MVC의 스레드당 요청 모델에서는 I/O 작업 대기시 컨텍스트 스위칭이 자주 발생한다. 반면, WebFlux는 이벤트 루프 모델을 통해 컨텍스트 스위칭 오버헤드를 최소화한다. 소수의 스레드가 I/O 작업의 완료를 기다리지 않고 다른 요청을 처리하다가, 작업이 완료되었다는 이벤트가 발생하면 콜백 함수를 실행한다. 이를 통해 CPU를 지속적으로 유효하게 사용하여 시스템의 전반적인 성능을 향상시킨다.