이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • 일관성 선택은 화면 문구, 버튼 상태, 재시도 흐름을 어떻게 바꾸는가?
  • 사용자가 stale data, pending, conflict를 납득하려면 API가 무엇을 알려줘야 하는가?
  • UX 보상은 정합성을 낮추는 핑계가 아니라 어떤 설계 계약이어야 하는가?

개요

일관성 선택은 백엔드 내부 구현으로 끝나지 않는다. 사용자는 방금 저장한 값이 안 보이면 저장 실패로 느낀다. 상태가 뒤로 가면 시스템을 믿지 못한다. 결과를 알 수 없는데 다시 시도하라는 버튼만 보이면 중복 요청이 생긴다. 따라서 eventual consistency를 선택했다면 UX도 함께 설계해야 한다.

이 문서가 바꾸는 설계 축

바뀌는 내용
성능optimistic UI와 stale read는 빠른 반응을 만들지만 보정 흐름이 필요하다.
정합성pending, stale, conflict 상태를 드러내 사용자 오해를 줄인다.
장애 격리일부 화면을 지연/축소해 핵심 쓰기 경로를 보호한다.
운영 복잡도UX 문구, API 상태 코드, CS 대응, 지표 해석을 같이 관리해야 한다.

UX 상태를 나눈다

상태의미사용자 표현
Confirmed원본에 반영 완료저장됨, 결제 완료
Pending접수됐지만 확정 전처리 중, 잠시 후 확인
Stale오래된 값을 보여줌최신 정보가 아닐 수 있음
Conflict사용자의 변경과 다른 변경이 충돌다시 확인 후 저장
Failed확실한 실패실패 사유와 재시도 가능 여부
백엔드가 이 상태를 구분하지 않으면 프론트는 모두 성공이나 실패로 뭉갠다.
그 결과 사용자는 불필요하게 새로고침하거나 같은 요청을 반복한다.

대표 시나리오

프로필 사진 변경을 예로 든다. 이미지 업로드는 성공했지만 썸네일 생성과 CDN 반영은 비동기다.

단계상태UX
원본 업로드 완료Confirmed새 사진 저장 완료
썸네일 생성 중Pending일부 화면은 이전 사진일 수 있음
CDN 반영 지연Stale곧 반영 예정
변환 실패Failed다시 업로드 요청
이 기능에 강한 일관성을 요구하면 사용자는 썸네일까지 완성될 때까지 기다려야 한다.
eventual을 택하면 저장 응답은 빨라지지만 처리 중 상태와 실패 보정이 필요하다.

API 계약

응답은 단순 성공보다 상태를 알려줘야 한다.

POST /profile/photo
202 Accepted
{
  "uploadId": "up-10",
  "status": "PROCESSING",
  "confirmedFields": ["original"],
  "pendingFields": ["thumbnail", "cdn"],
  "retryAfterMillis": 1000
}

프론트는 이 응답으로 버튼을 비활성화하거나 진행 상태를 보여줄 수 있다. 200 OK만 주면 사용자는 모든 화면에 즉시 반영됐다고 기대한다.

Optimistic UI

좋아요 버튼은 optimistic UI가 적합할 수 있다. 사용자가 누른 즉시 count를 올리고, 서버 실패 시 되돌린다.

client_count = server_count + 1
server_result = pending
failure => client_count rollback

하지만 결제, 재고, 권한 변경에는 위험하다. optimistic UI는 틀려도 되돌릴 수 있는 행동에만 쓴다. 되돌릴 수 없는 행동은 pending이나 명시적 확인이 더 안전하다.

Stale 표시

stale data를 숨기지 말아야 하는 화면이 있다. 관리자 통계, 재고 대시보드, 정산 화면은 freshness가 의사결정에 직접 영향을 준다.

GET /admin/inventory-summary
200 OK
{
  "dataFreshness": "2026-07-01T10:00:00+09:00",
  "lagSeconds": 45,
  "isRealtime": false
}

반면 일반 상품 추천 목록은 freshness를 굳이 노출하지 않아도 된다. 사용자가 이 값으로 중요한 결정을 하는지에 따라 표시 수준을 정한다.

Conflict UX

동시 수정이 가능한 기능은 conflict를 제품 흐름으로 다뤄야 한다.

409 Conflict
{
  "code": "VERSION_CONFLICT",
  "message": "다른 기기에서 먼저 수정된 내용이 있습니다.",
  "serverVersion": 18
}

문서 편집, 주소 변경, 관리자 설정은 conflict를 숨기면 마지막 저장이 이전 변경을 덮는다. 사용자에게 비교 화면을 줄지, 서버 값을 우선할지, client 값을 다시 제출하게 할지 정한다.

버튼과 재시도 정책

일관성 상태는 버튼 동작을 바꾼다.

상태버튼 정책
Pending같은 요청 재전송 방지, 상태 조회 버튼 제공
Unknown재결제 버튼 대신 결과 확인 버튼 제공
Stale새로고침 또는 최신화 시도
Conflict다시 불러오기, 병합, 덮어쓰기 선택
Retryable Failure제한된 재시도와 안내
결제 unknown 상태에서 “다시 결제” 버튼을 바로 노출하면 중복 결제가 생긴다.
UX는 정합성 보호 장치의 일부다.

화면별 일관성 예시

화면일관성 요구허용 UX
주문 완료강함확정 또는 pending 확인
주문 목록중간몇 초 지연 안내
좋아요약함optimistic UI
관리자 재고강함freshness와 lag 표시
추천 피드약함stale 표시 생략 가능
문서 편집강함conflict 해결 화면
한 서비스 안에서도 화면마다 요구가 다르다.
그래서 backend API는 화면의 기대를 알아야 한다.

운영 지표

UX까지 포함한 일관성 지표가 필요하다.

지표의미
pending_duration_p95사용자가 처리 중을 얼마나 오래 보는지
stale_view_ratestale 화면 노출 비율
conflict_rate동시 수정 충돌 빈도
optimistic_rollback_rateoptimistic UI 되돌림 비율
duplicate_retry_blocked중복 요청 차단 횟수
기술 지표인 replica lag가 낮아도 특정 화면의 stale_view_rate가 높으면 사용자 문제는 남아 있다.

개인 프로젝트 기준

  • eventual consistency를 쓰는 화면에는 pending/stale 문구를 한 줄이라도 둔다.
  • 결제나 주문의 unknown 상태에는 중복 요청 방지 버튼 정책을 둔다.
  • optimistic UI는 되돌릴 수 있는 행동에만 쓴다.
  • conflict 가능성이 있으면 409 응답과 사용자 안내를 만든다.
  • 포트폴리오에서는 “UX로 보상”이 아니라 어떤 상태를 어떻게 보여줬는지 설명한다.

기업 운영 기준

  • PM, 프론트, 백엔드가 pending/stale/conflict 용어를 같은 의미로 쓴다.
  • CS가 사용자 문의를 처리할 수 있게 상태 코드와 문구를 문서화한다.
  • pending이 오래 지속될 때 자동 보정, 알림, 수동 처리 기준을 둔다.
  • UX 상태별 metric과 error budget을 서비스 SLO에 연결한다.
  • A/B나 제품 실험이 정합성 경계를 약화시키지 않는지 리뷰한다.

위험 신호

  • eventual consistency를 선택했지만 화면은 항상 즉시 반영을 기대한다.
  • stale 데이터를 성공 응답으로 숨긴다.
  • pending 상태에서 사용자가 같은 요청을 반복할 수 있다.
  • conflict를 500이나 일반 실패로 처리한다.
  • optimistic UI를 결제, 주문, 권한 변경에 적용한다.
  • UX 문구가 기술 상태와 다르게 사용자에게 확정처럼 보인다.

확인 질문

확인 질문

  • 확인 질문: Eventual consistency를 UX로 보상한다는 말의 정확한 의미는 무엇인가?
    • 답변: 불일치를 숨기는 것이 아니라 pending, stale, conflict 상태를 명확히 보여 주고 안전한 다음 행동을 제공하는 것이다.
  • 확인 질문: Optimistic UI를 쓰면 안 되는 대표 영역은 무엇인가?
    • 답변: 결제, 주문, 재고, 권한처럼 되돌리기 어렵고 틀린 성공이 치명적인 영역이다.
  • 확인 질문: UX 지표가 필요한 이유는 무엇인가?
    • 답변: replica lag나 projection lag가 낮아도 사용자가 stale 화면이나 pending 상태를 오래 보면 실제 경험은 나쁠 수 있기 때문이다.