고민보단 실천을

모바일 오프라인 동기화 설계: 충돌 해결과 재시도로 사용자 데이터 지키는 방법 본문

카테고리 없음

모바일 오프라인 동기화 설계: 충돌 해결과 재시도로 사용자 데이터 지키는 방법

Just-Do-It 2026. 4. 11. 14:59

모바일 오프라인 동기화 설계: 충돌 해결과 재시도로 사용자 데이터 지키는 방법

모바일은 네트워크가 끊기는 순간이 예외가 아니라 기본값에 가깝다. 그래서 오프라인 동기화는 기능이 아니라 제품 신뢰성 문제다.

중급 설계에서는 로컬 저장, 큐잉, 재시도, 충돌 해결 전략을 한 묶음으로 다뤄야 한다. 하나만 빠져도 사용자는 데이터를 잃었다고 느낀다.

왜 지금 이 주제가 중요한가

  • 다중 디바이스와 간헐적 연결은 충돌을 필연적으로 만든다.
  • 단순 last-write-wins는 구현은 쉽지만 사용자가 실제로는 데이터를 잃을 수 있다.
  • 백그라운드 동기화는 OS 제약과 배터리 정책에 크게 영향을 받는다.

핵심 설계 포인트

  • 로컬에는 변경 로그(queue)를 저장하고 서버 반영 전까지 상태를 추적한다.
  • 각 변경에는 client-generated id, version, updatedAt, actor를 포함한다.
  • 충돌 해결은 자동 병합 가능한 필드와 사용자 선택이 필요한 필드를 구분한다.
  • 재시도는 지수 백오프와 네트워크 상태 감지와 함께 설계한다.

예시 구성

{
"recordId": "todo-104",
"clientMutationId": "ios-42-00017",
"baseVersion": 12,
"changes": { "title": "출장 준비" }
}

적용 순서(실무 플로우)

설계 자체보다도 '작게 도입하고 관측하면서 확장하는 순서'가 운영 성공률을 좌우한다.

  1. 오프라인 편집이 필요한 핵심 엔터티를 먼저 제한적으로 선택한다.
  2. 로컬 큐 스키마와 상태(queued, syncing, synced, conflict)를 정의한다.
  3. 서버는 버전 충돌을 감지하고 machine-readable 응답을 내려주게 만든다.
  4. 앱은 충돌 UI와 자동 병합 규칙을 분리해서 구현한다.
  5. 동기화 실패/충돌 지표를 앱 버전별로 추적하며 정책을 조정한다.

운영 체크포인트

  • 로컬 큐는 앱 강제 종료 후에도 복구돼야 한다.
  • 충돌 응답 포맷을 프론트와 백엔드가 합의하지 않으면 UX가 무너진다.
  • 백그라운드 동기화는 OS 정책에 따라 지연될 수 있음을 제품 팀과 공유한다.

운영 지표/알람 추천

  • 동기화 성공률과 conflict 발생 비율
  • 재시도 횟수와 배터리/네트워크 사용량
  • 오프라인 큐 적체 시간과 복구 시간
  • 앱 버전별 동기화 오류 상관관계

빠른 점검 명령/쿼리

# 오프라인 큐 길이, 최근 sync failure, conflict 로그 확인
# 동일 레코드가 서로 다른 디바이스에서 수정된 샘플을 재현
# 백그라운드 재시도 주기와 배터리 제한 정책을 점검

구조화 로그 필드 추천

  • traceId/requestId/eventId처럼 흐름을 이어주는 키를 남긴다.
  • endpoint/topic/flag/version 등 주제별 핵심 차원을 구조화한다.
  • 실패 이유(reasonCode)와 재시도 횟수(retryAttempt)를 분리한다.
  • 민감정보는 마스킹하고, payload는 샘플링 또는 요약 저장한다.
{
"level": "WARN",
"message": "sync conflict detected",
"deviceId": "ios-42",
"recordId": "todo-104",
"status": "CONFLICT",
"retryAttempt": 1,
"latencyMs": 92
}

테스트 케이스 샘플

테스트 케이스(최소 3종):
1) 정상: 기대한 성공 경로와 상태 전이가 유지되는지
2) 실패: 다운스트림 오류/잘못된 입력이 예측 가능한 에러로 떨어지는지
3) 동시성/재시도: 같은 요청 또는 이벤트가 반복돼도 부작용이 없는지

추가(가능하면):
- 장애 복구: 프로세스 재시작 후 중간 상태를 정상 회복하는지
- 부하: p95/p99와 queue/pool saturation이 임계값 안에 드는지

트레이드오프/대안

  • 운영 복잡도를 줄이면 기능 유연성이 떨어질 수 있고, 반대도 마찬가지다.
  • 기본값은 출발점일 뿐이다. 실제 트래픽과 실패 패턴을 보고 다시 조정해야 한다.
  • 관측 없이 최적화하면 체감 개선과 회귀를 구분하기 어렵다.
  • 팀 경계가 많은 시스템일수록 인터페이스 계약과 문서가 코드만큼 중요하다.

성공 기준(SLO) 예시

  • 핵심 경로 에러율: 0.1% 이하
  • 핵심 요청/이벤트 p95 지연: 서비스 목표 내 유지
  • 중복 실행 또는 데이터 유실: 0건
  • 장애 감지 후 임시 조치까지 걸리는 시간: 10분 이내

자주 터지는 실수/트러블슈팅

  1. last-write-wins를 만능 해결책처럼 쓴다: 조용한 데이터 손실이 발생한다.
  2. 재시도만 계속하고 충돌 상태를 사용자에게 보여주지 않는다.
  3. 오프라인 큐를 메모리에만 둔다: 앱 종료 시 변경이 사라진다.

바로 적용 템플릿

오프라인 동기화 템플릿:
local queue 상태: queued/syncing/synced/conflict
clientMutationId + baseVersion
충돌 응답 포맷과 사용자 해결 UI
지수 백오프 + 네트워크 복귀 트리거

검증 방법

  • 두 대의 디바이스에서 같은 레코드를 수정해 충돌 처리 UX가 의도대로 동작하는지 확인한다.
  • 앱 강제 종료와 재실행 후에도 로컬 큐가 복구되고 재시도가 이어지는지 검증한다.

장애 대응 Runbook(초안)

  • 현상: 어떤 사용자/서비스/플랫폼에서 무엇이 깨졌는지 한 문장으로 정리한다.
  • 범위: 언제부터 시작됐고, 영향받은 비율과 핵심 경로를 적는다.
  • 증거: 로그 3줄, 지표 1개, 최근 배포/설정 변경 1개를 먼저 모은다.
  • 임시 조치: 차단, 롤백, 스위치 전환, 재시도 제한 중 무엇을 할지 결정한다.
  • 근본 원인: 계약, 타임아웃, 락, 캐시, 버전, 운영 절차 중 어디가 깨졌는지 좁힌다.
  • 재발 방지: 테스트, 알람, 문서, 기본값을 함께 수정한다.

리뷰 체크리스트

  1. 실패 시나리오가 문서와 코드에서 같은 의미로 정의돼 있다.
  2. 타임아웃/재시도/락/캐시 같은 보호 장치가 상호 충돌하지 않는다.
  3. 관측 지표와 상관관계 키가 있어 운영 중 재현이 가능하다.
  4. 롤백 또는 비상 스위치가 준비돼 있다.
  5. 최소 1개 이상의 동시성/부하/중간 실패 테스트가 자동화돼 있다.
  6. 공식 문서 링크와 팀 의사결정 근거가 남아 있다.

팀 문서 템플릿

팀 문서 템플릿(복붙용):
1) 목표/배경: 어떤 운영 비용 또는 장애를 줄이려는가
2) 범위: API/잡/토픽/디바이스/리전 중 어디까지 적용하는가
3) 규칙: 키, 상태, 버전, TTL, timeout, retry 기본값
4) 예외: 허용하지 않는 상황과 에러 코드/조치 기준
5) 운영: 대시보드, 알람, 소유 팀, 점검 주기
6) 장애 대응: 임시 조치, 롤백, 후속 공지 절차
7) 변경 이력: 언제 누가 왜 기본값을 바꿨는가

FAQ(자주 묻는 질문)

Q. 처음부터 완벽하게 설계해야 하나요?
A. 아니다. 핵심 경로 1개부터 적용하고, 운영 지표를 보며 기본값을 보정하는 편이 실제로 더 안전하다.

Q. "모바일 오프라인 동기화 설계: 충돌 해결과 재시도로 사용자 데이터 지키는 방법"를 도입했는데도 문제가 남아 있습니다. 어디부터 봐야 하나요?
A. 먼저 상관관계 키가 있는 로그와 지표로 실패 범위를 좁히고, 최근 배포/설정 차이를 확인한다. 대부분은 기본값보다 경계 조건에서 터진다.

Q. 팀 합의가 자꾸 흔들립니다. 무엇을 문서로 남겨야 하나요?
A. 상태 전이, 기본값, 예외 처리, 롤백 기준 네 가지는 반드시 남겨야 한다. 이 네 가지가 없으면 장애 때 판단이 흔들린다.

참고/출처

Comments