Skip to content

Reactive Programming ‐ Reactive System & Reactive Programming

woojin.jang edited this page Apr 13, 2026 · 4 revisions

Reactive Programming - Reactive System & Reactive Programming

MVC 기반 아키텍처 흐름과 리액티브 프로그래밍 탄생 배경

스크린샷 2026-04-13 11 51 57

1. Blocking I/O와 스레드 풀 고갈

  • 200개의 스레드가 모두 DB 조회(200ms)나 외부 API 호출(500ms) 결과를 기다리며 대기(Block) 상태에 빠지게 된다.
  • 리소스 낭비 : 이 대기 시간 동안 스레드들은 CPU 연산을 전혀 수행하지 않으면서도, 단지 응답이 오기를 기다린다는 이유만으로 시스템 리소스(스레드)를 점유하고 있다.
  • 스레드 풀 고갈(Thread Pool Exhaustion) : 기본 Tomcat 설정인 200개의 스레드가 모두 이런 I/O 대기 상태에 묶여버리면, 이후에 들어오는 201번째 요청은 처리할 여유 스레드가 없어 큐에서 대기하게 되고, 결국 타임아웃이나 Connection Refused 에러와 함께 요청이 유실된다.

2. 스레드를 늘리는 것의 물리적 한계

  • 쓰레드라는 자원은 공짜가 아니라는 것을 명심해야 한다.
  • 메모리 오버헤드 : 스레드는 생성될 때마다 독립적인 스택 메모리(일반적으로 1MB)를 할당받는다. 스레드가 기하급수적으로 늘어나면 애플리케이션의 메모리 점유율도 감당할 수 없이 커지게 된다.
  • 컨텍스트 스위칭(Context Switching) 비용 : CPU 코어 수는 제한적인데 동작해야 할 스레드만 많아지면, CPU가 여러 스레드를 번갈아 실행하기 위해 현재 상태를 저장하고 다른 상태를 복원하는 작업에 과도한 연산 능력을 낭비하게 되어 오히려 전체 처리량(Throughput)이 저하된다.

3. 패러다임의 전환: 리액티브 프로그래밍과 Non-Blocking I/O

  • Event Loop 기반 비동기 처리 : 요청이 들어오면 대규모 스레드 풀 대신, 소수의 스레드(보통 CPU 코어 수와 동일하거나 2배)로 동작하는 이벤트 루프가 이를 수신한다.
  • 작업 위임과 즉시 반환 : 스레드가 DB나 외부 API에 데이터를 요청한 뒤, 결과를 그 자리에서 기다리지 않고 즉시 스레드 풀로 돌아가 대기 중인 다른 클라이언트의 요청을 처리한다.
  • 이벤트 콜백 : DB나 API로부터 응답 데이터가 준비되어 도착하면 시스템에 이벤트를 발생시키고, 여유가 있는 스레드가 해당 결과값을 받아 나머지 비즈니스 로직을 마저 처리한 뒤 클라이언트에게 응답한다.

4. 진정한 리액티브 아키텍처를 위한 조건

  • 리액티브 스택의 높은 동시성 처리 이점을 극대화하려면 웹 계층만 비동기로 동작해서는 안 되며, 데이터베이스 접근 계층까지 모두 Non-Blocking으로 구성해야한다.
  • 웹 계층을 WebFlux로 구현했더라도 DB 연결에 기존의 동기식 JDBC를 사용한다면 스레드는 결국 DB 응답을 기다리며 블로킹된다.
  • 따라서 다수의 외부 API를 호출하여 시세 데이터를 병합하는 크롤러나, 높은 트래픽의 금융 데이터를 다루는 아키텍처에서는 기존 패러다임을 깨고 R2DBC와 같은 비동기 논블로킹 DB 드라이버를 결합해야만 진정한 리액티브 시스템의 성능을 끌어낼 수 있다.

데이터 스트림 구조에서 OOM 방지를 위한 BackPressure 효과

스크린샷 2026-04-13 21 17 53
  • 트래픽이 폭주하더라도 시스템의 어느 한쪽이 OOM 등으로 무너지는 것을 막고 자신의 처리 능력(Capacity) 안에서 가장 안전하고 효율적으로 동작하도록 보장하는 방어 메커니즘이다.
  • Consumer-Driven : 데이터를 만들어내는 쪽(Publisher)이 일방적으로 데이터를 밀어 넣는(Push) 것이 아니라, 데이터를 소비하는 쪽(Subscriber)이 철저하게 트래픽의 주도권을 쥐게 된다.
  • Dynamic Adjustment : 실시간으로 데이터 수신량을 조절한다.
  • Graceful Completion : 모든 데이터를 동적으로 보내고 지체 없이 onComplete()를 호출하여 자원 낭비 없이 안전하게 통신을 종료한다.

Spring MVC와 Spring WebFlux의 차이점과 주의사항 정리

스크린샷 2026-04-13 21 28 19

1. 이벤트 루프

  • 이벤트 루프는 무한히 회전하며 큐(Queue)에 들어오는 이벤트(클라이언트 요청, DB 응답 등)를 확인하고 처리하는 싱글 쓰레드이다.

2. 요청 1 유입 및 작업 위임

  • 요청 1이 들어오는데 만약 이 요청이 CPU만 쓰는 단순 계산이라면 이벤트 루프가 바로 처리해서 응답한다.
  • 하지만 DB 조회, 외부 API 호출, 파일 읽기 등 오래 걸리는 I/O 작업이라면 이벤트 루프는 자기가 직접 기다리지 않고 운영체제나 백그라운드 쓰레드 풀에 해당 작업을 처리해달라고 요청을 등록한다.

3. 막힘없는 동시 처리

  • 요청 1의 I/O를 던져버린 이벤트 루프는 블로킹되지 않는다.
  • 즉시 다음으로 들어온 요청 2, 요청 3을 받아서 똑같이 처리하거나 위임한다. 하나의 쓰레드로 수천, 수만 개의 동시 커넥션을 맺을 수 있는 이유가 바로 Non-Blocking 기반이기 때문이다.

4. 완료 이벤트

  • 백그라운드에서 요청 1의 I/O 작업이 끝나고 이벤트 루프 측에 작업이 다 되었다는 완료 이벤트를 보낸다.

5. 응답 처리

  • 계속 들고 있던 이벤트 루프가 이 완료 이벤트를 발견하고 비로소 조회된 데이터를 가공해 요청 1을 보냈던 클라이언트에게 최종 응답을 내려준다.

Reactive
Spring WebFlux
우아한 기술 블로그 (배민)
OKKY 실제 테스트 결과
과연 매번 WebFlux는 옳을까?? reddit의 토론글

📖 Java

📖 Kotlin

📖 Coroutine

📖 Spring

📖 Spring Security

📖 Spring Batch

📖 Reactive Programming

📖 Database

📖 MySQL

📖 Redis

📖 JPA

📖 QueryDsl

📖 MSA

📖 Kafka

📖 Apache Flink

  • [Apache Flink - Apache Flink Architecture]
  • [Apache Flink - Stream Processing]
  • [Apache Flink - Data Stream API & Window]
  • [Apache Flink - State Management]

📖 HTTP

📖 AWS

📖 Docker

📖 Kubernetes

📖 CI/CD

📖 Nginx

📖 Monitoring🥈

  • [Monitoring - Log Concept]
  • [Monitoring - Log Level & Filter]
  • [Monitoring - Logback]
  • [Monitoring - Log Collection with ELK Stack]
  • [Monitoring - Log Monitoring with Kibana]
  • [Monitoring - Building a Monitoring System with Spring Boot Actuator]
  • [Monitoring - Server Monitoring with Prometheus and Grafana with Discord Alerts]

📖 Test

📖 Effective Java 3/E

📖 Kotlin Academy - Effective Kotlin

📖 Kotlin Academy - 핵심편

📖 스프링으로 시작하는 리액티브 프로그래밍

📖 가상 면접 사례로 배우는 대규모 시스템 설계 기초 1

📖 가상 면접 사례로 배우는 대규모 시스템 설계 기초 2

📖 Clean Code

📖 리팩토링 2판

📖 주니어 백엔드 개발자가 반드시 알아야 할 실무 지식

📖 GraphQL

Clone this wiki locally