Push-based vs. Pull-based Reactivity: Fine-Grained Systems 뒤의 두 가지 구동 모델

발행: (2025년 12월 15일 오전 09:40 GMT+9)
9 min read
원문: Dev.to

Source: Dev.to

푸시 기반 vs 풀 기반 반응성: 세밀한 시스템을 이끄는 두 모델

요약

반응성의 핵심 아이디어에 대한 이전 글을 바탕으로, 이번 파트에서는 푸시‑기반과 풀‑기반 반응성 모델의 차이를 명확히 합니다.

핵심 아이디어

세밀한 반응성에서는:

  • 푸시‑기반 시스템은 값이 변할 때 즉시 계산을 수행합니다.
  • 풀‑기반 시스템은 누군가 값을 읽을 때까지 계산을 미룹니다.

실제 예시

푸시‑기반

푸드 코트에서 음식을 주문한다고 생각해 보세요.

  • 쓰기(주문): 주문을 합니다.
  • 푸시(완료 알림): 음식이 준비되면 버저가 진동하거나 불이 켜집니다 — 업데이트가 바로 여러분에게 푸시됩니다.
  • 효과(픽업): 카운터로 가서 음식을 가져갑니다.

반응성 해석: 소스가 변하면, 의존 노드가 즉시 재계산되고 바로 알림이 전달됩니다.

풀‑기반

버블티를 주문한다고 가정해 보세요.

  • 쓰기(주문): 음료를 주문합니다.
  • 마크(상태만 업데이트): 음료가 준비되면 가게는 화면에 번호만 표시합니다 — 직접 알림을 주지는 않습니다.
  • 읽기 → 계산(필요할 때만): 화면을 확인하는 순간, “아, 준비됐네, 가서 가져가야겠다”는 읽기 연산이 트리거됩니다.
  • 효과(픽업): 카운터로 가서 음료를 가져갑니다.

반응성 해석: 쓰기는 노드를 더럽게(Dirty) 표시만 할 뿐이며, 실제 계산은 누군가 값을 읽을 때 나중에 이루어집니다.

형식적 정의

모델동작 초점단순화된 흐름
푸시‑기반쓰기 시 계산: 업데이트가 즉시 전파됨set() → propagate → compute → effect
풀‑기반쓰기 시 마크, 읽기 시 계산set() → markDirty ⏸ read() → if dirty → compute → effect

핵심 인사이트: 두 모델 모두 “신호를 푸시”합니다 — 푸시는 계산을 푸시하고, 풀은 더러운 마크를 푸시합니다.

타임라인 다이어그램

푸시‑기반

push flow

풀‑기반

pull flow

장단점

측면푸시‑기반풀‑기반
읽기 지연가장 낮음 — 항상 최신 상태첫 번째 읽기 시 재계산이 트리거될 수 있음
쓰기 비용잠재적으로 높음: O(depth × writes)낮음: 대부분 O(depth × 1) (더러운 마크만)
과잉 계산높음 — 읽히지 않아도 계산함낮음 — 실제 읽을 때만 계산
배치 처리어려움 — 작업이 이미 수행됨자연스러움 — 나중에 한 번에 플러시 가능
디버그 가시성의존 체인이 즉시 확장됨풀링이 발생할 때 DevTools가 필요
최적 사용 사례높은 빈도의 쓰기, 낮은 읽기 (예: 협업 앱의 커서 동기화)낮은 쓰기, 높은 읽기 (대시보드, 차트)

참고: 풀‑기반 시스템도 마킹 단계에서 의존 그래프를 탐색하지만, 재계산은 하지 않으며 dirty = true만 설정합니다.

어떻게 선택할까?

사용 사례권장 모델이유
실시간 협업, 게임 상태 동기화푸시즉시 반영이 필요하고, 불필요한 재계산을 피하기보다 빠른 읽기가 중요
대규모 대시보드, 데이터 시각화쓰기는 드물고 읽기가 빈번 — 실제 필요한 것만 계산
타임라인 / 스크롤 기반 애니메이션풀 + 스케줄러풀은 작업을 연기하고, 스케줄러는 프레임당 한 번씩 재계산 보장
데이터 파이프라인 (비싼 연산을 여러 번 재사용)푸시‑온‑커밋한 번 미리 계산하고 이후에 재사용

React는 기본적으로 풀 + 스케줄러이며, 그래서 배치가 현재와 같이 동작합니다.
RxJS와 MobX는 전형적인 푸시‑온‑커밋 예시입니다.

흔한 오해

  • “풀은 전체 그래프를 스캔한다!”
    아니오 — 풀은 값이 읽힐 때 관련된 의존 체인만 위쪽으로 확인합니다. 전체 트리를 순회하지 않습니다.

  • “푸시는 항상 계산을 낭비한다.”
    결과가 즉시 소비될 것이 보장될 때는 그렇지 않습니다 (예: 커서 이동). 낮은 읽기 지연이 추가 쓰기 비용을 능가할 수 있습니다.

  • “푸시 vs 풀은 상호 배타적이다.”
    대부분의 최신 시그널 시스템은 하이브리드 방식을 사용합니다:
    푸시 → 쓰기 시 더러운 플래그 전파
    → 읽기 시 필요할 때만 계산.
    이렇게 하면 반응성 및 지연성을 모두 얻을 수 있습니다.

  • “왜 세밀한 시스템도 풀을 필요로 할까?”
    이유는 알 수 없기 때문입니다:

    • 이 값이 언제든 읽히게 될까?
    • 언제 읽히게 될까?
      풀은 “데이터가 바뀌었다”(더럽게 표시)와 “이걸 가지고 뭘 해야 할까?”(읽을 때 계산)를 분리합니다.

결론

왜 세밀한 반응성에 푸시 vs 풀 논의가 필요한가?

코스(거친) 반응성 시스템(예: React의 Virtual DOM)에서는 전체 트리를 diff하는 것이 추상화 수준이 충분합니다.
하지만 시그널 기반 시스템에서는 단일 set()이 수백 개의 작은 파생으로 퍼질 수 있습니다.

계산 시점을 선택하면 다음에 영향을 미칩니다:

  • 전체 연산 비용(성능)
  • 인터랙션 지연(UI 부드러움)
  • 스케줄링 동작(진동 및 프레임 손실 방지)

푸시와 풀을 이해하면 다양한 반응성 프레임워크를 평가할 때 필요한 사고 모델과 용어를 갖추게 됩니다.

다음 주제

다음 글에서는 주요 프레임워크들이 어떻게 반응성 시스템을 설계했는지, 그리고 왜 그런 선택을 했는지 살펴볼 예정입니다.

Back to Blog

관련 글

더 보기 »