백엔드 면접 용어 사전
Spring·JPAbackpressure · 역압

백프레셔

소비자가 감당 가능한 만큼만 요청해 생산자 속도를 거꾸로 제어하는 흐름 제어. 리액티브 스트림의 핵심이자 가상 스레드가 대체 못 하는 지점.

소비자가 "지금 N개만 더 보내라"고 생산자에게 되받아 요구하는 흐름 제어 방식. 압력이 데이터가 흐르는 방향의 반대로 걸린다고 해서 back(뒤로) + pressure(압력)다.

왜 필요한가 — 버퍼는 무한하지 않다

생산자: 초당 10,000건 생산  (예: 카프카 토픽·DB 커서·센서 스트림)
소비자: 초당 1,000건 처리   (예: 외부 API 호출이 끼어 있음)

차이 9,000건/초가 버퍼에 쌓인다
  ↓ 10초 후 90,000건
  ↓ 100초 후 900,000건
  ↓ 힙 부족 → OutOfMemoryError

메모리 관점에서 보면 이건 큐에 든 객체들이 GC 대상이 되지 못하고 계속 참조되는 상황이다. 생산 속도가 소비 속도보다 조금이라도 빠르면 시간 문제일 뿐 반드시 터진다.

리액티브 스트림의 해법

리액티브 스트림 규격에서 소비자는 request(n)으로 받을 수 있는 개수를 명시한다. 생산자는 그 수를 넘겨 보내면 안 된다.

소비자 → request(10)  → 생산자
소비자 ← onNext ×10   ← 생산자   (딱 10개만)
소비자 → request(10)  → 생산자   (소화한 만큼 다시 요청)

이게 "밀어내기(push)"와 "당겨오기(pull)"의 중간이라 push-pull 하이브리드라고 부른다.

생산자가 못 따를 때 — 전략을 고른다

외부 이벤트처럼 속도를 못 늦추는 생산자도 있다. 그럴 땐 넘치는 것을 어떻게 할지 정한다.

전략동작쓰는 곳
onBackpressureBuffer버퍼에 쌓는다(한계·오버플로 동작 지정 가능)잠깐의 스파이크 흡수
onBackpressureDrop넘치면 버린다유실돼도 되는 로그·메트릭
onBackpressureLatest가장 최신 것만 남긴다시세·센서값처럼 최신값만 의미 있는 경우

왜 가상 스레드가 이걸 대체 못 하나

가상 스레드는 동시성의 비용을 없앴을 뿐이다. "스레드 10만 개를 띄워도 싸다"는 것과 "생산자에게 천천히 보내라고 요구한다"는 완전히 다른 문제다. 대용량 스트림 파이프라인에서 리액티브가 여전히 답인 이유가 이것이다.

면접 포인트

"WebFlux를 언제 쓰나"에 "백프레셔가 실제로 필요할 때" 라고 답하고, 위의 OOM 시나리오를 곁들이면 개념을 실제로 이해했다는 신호가 된다.

전략을 코드로

Flux.from(sensorStream)
    .onBackpressureLatest()            // 넘치면 최신 값만 유지
    .publishOn(Schedulers.boundedElastic(), 32)   // 프리페치 32
    .subscribe(this::handle);
// 소비자가 직접 요청량을 제어하는 가장 낮은 층
subscription.request(10);      // "10개만 더 보내라"
// ... 10개 소화 후
subscription.request(10);

연산자들은 결국 이 request(n) 위에 얹힌 편의 문법이다.

어디까지가 백프레셔의 몫인가

  • 생산자가 속도를 늦출 수 있다 — → 진짜 백프레셔 (request(n) 이 위로 전달됨)
  • 생산자가 속도를 못 늦춘다 — → 전략 선택 (buffer / drop / latest)
    • 예: 외부 이벤트·센서·시세 피드 "무엇을 버릴 것인가"의 문제로 바뀐다

두 번째 경우에는 백프레셔가 유실을 막아 주지 않는다. 어디서 어떻게 버릴지를 명시적으로 고르게 해 줄 뿐이다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 모던 자바·스프링 — 가상 스레드·JEP 491·리액티브·jOOQ·Boot 4