정의
협업·오프라인 앱에서 "내가 타이핑하면 서버 응답을 기다리지 않고 즉시 화면에 반영"하고 싶은데, 동시에 다른 사람의 변경이나 서버의 최종 결과와도 어긋나지 않게 맞추고 싶을 때 쓰는 동기화 패턴이다. 핵심 아이디어는 내 변경을 일단 화면에 먼저 그려 두되, 그것을 정확히 되돌릴 수 있는 "반대 편집"을 같이 보관해서, 나중에 서버 결과가 오면 내 변경을 잠깐 되감았다가 서버 것을 먼저 반영한 뒤 내 변경을 다시 올리는 것이다.
정확히 말하면, Optimistic outbox rebase는 클라이언트가 서버 응답을 기다리지 않고 로컬 state에 patch(상태를 부분적으로 바꾸는 변경 조각)를 먼저 적용(optimistic, 성공을 낙관하고 미리 반영하는 것)한 뒤, outbox(아직 서버에 확정되지 않은 내 변경을 모아 두는 대기 큐)에 pending(확정 대기 중)으로 저장한다. 원격 patch가 도착하면 pending을 역순 rollback(되돌림) → remote를 authoritative(서버가 정한 최종 권위) 순서로 apply → pending을 forward re-apply하여 수렴(모두가 같은 결과에 도달)한다. 각 optimistic step은 inverse patch(그 변경을 정확히 되돌리는 반대 편집)를 함께 보관해야 rollback이 가능하다. 이렇게 되감고 다시 올리는 과정을 rebase라 한다.
왜 필요한가
협업·오프라인 우선 UX는 "내 타이핑이 즉시 화면에 반영"되는 것을 전제로 한다. 그러나 네트워크 지연·재정렬·충돌로 로컬 base(내 화면이 기준으로 삼은 상태)와 서버 진실이 어긋난다. throw-only 정책(충돌 시 그냥 에러 내고 버림)은 사용자 편집을 잃게 만들고, silent merge(아무 알림 없이 조용히 합침)는 디버깅을 불가능하게 한다. inverse를 갖춘 outbox rebase는 UX와 수렴성 사이의 실용적 중간 지점이다.
동작 원리
- Optimistic apply —
{ patches, inverse }를 생성하고 state에 patches 적용, outbox push. - Remote arrive — 서버가 부여한 monotonic(단조 증가, 항상 커지기만 하는) seq(sequence, 변경마다 매기는 순번) 순으로 remote patch 적용(merge 권위).
- Rebase
- pending을 역순으로 inverse 적용(rollback)
- remote patch를 seq 순 apply
- pending을 다시 forward apply(inverse 재계산 필요할 수 있음)
- Dropped — 재적용 실패(anchor 삭제 등)는 swallow하지 않고 호출자/UI에 보고.
| 역할 | 일반적 책임 |
|---|---|
| seq | merge 순서의 단일 권위 |
| HLC/timestamp | HLC(Hybrid Logical Clock, 실제 시각과 논리 카운터를 섞어 순서를 매기는 시계) — audit·로컬 정렬 메타(merge 권위 아님) |
| inverse | rollback·rebase의 연료 |
| tie-break | 순번이 같을 때(동시 insert) 결정적 순서를 정하는 규칙 |
실무 적용
순서 의도는 integer index(0,1,2… 정수 인덱스)보다 neighbor anchor(앞뒤 이웃 항목을 기준점으로 삼음) + fractional position key(항목 사이에 무한히 끼워 넣을 수 있는 소수 정렬 키) + client/op tie-break 조합이 동시 insert에 유리하다.
트레이드오프
- optimistic UI는 빠르지만 충돌 UI(누가 이겼는지, dropped 알림) 설계가 필요하다.
- inverse 계산 비용·정확성이 전제 — inverse bug는 rebase 후 조용한 corruption.
- CRDT/full OT(여러 사용자의 동시 편집을 충돌 없이 합치는 본격 협업 알고리즘) 대비 구현은 가볍지만, 서버 seq 정책과 클라이언트 outbox 계약이 단단해야 한다.
사용하면 안 되는 경우
- 모든 연산이 서버 authoritative이고 latency가 허용되는 내부 admin-only 폼(optimistic 이득이 작음).
- inverse를 신뢰할 수 없거나 patch가 비가역-only인 도메인.
흔한 실수
- optimistic만 하고 inverse 저장 안 함 → rebase 불가.
- wall-clock timestamp만으로 merge → 비결정적 순서.
- rebase 실패 silent drop → “내 편집이 사라졌는데 왜?” 추적 불가.
- rebase 중 undo stack record on → 히스토리 오염.
관련 개념
- two-stack-inverse-undo — inverse patch mental model
- fractional-index-ordering — anchor 기반 순서
- race-safe-async-ui-requests — stale response와의 유사한 concurrency 문제