사이드프로젝트 slecs

기기 교체 후 수신 메시지 복구를 위한 서버 동기화 구현기

목차

왜 만들었나

  • 기기 교체하면 이전 수신 내역이 통째로 날아갔음. 사용자 문의가 꾸준히 들어옴
  • 로컬에만 쌓아둔 데이터라 복구 경로가 아예 없었음
  • 서버에 보존돼 있는 원본을 끌어와 로컬과 병합하는 흐름이 필요했음

구조 잡기

레이어 역할
진입 화면 최초 진입 시 동기화 트리거
설정 화면 수동 재동기화 + 마지막 시각 노출
로컬 DB 메시지 캐시 + sync 메타데이터
동기화 모델 서버 응답을 로컬 스키마로 매핑

서버 호출 시그니처는 단순하게 잡았음. since 만 넘기고 증분으로 받음.

suspend fun sync(since: Long): Result<List<Message>>

진입 화면은 트리거만, 설정 화면은 상태 노출과 수동 버튼만. 동기화 자체 로직은 한 군데에 모아서 두 화면이 같은 함수만 호출하도록 했음. 처음엔 진입 화면 안에 코드를 다 박았다가 설정 화면에서 또 복붙하는 자신을 발견하고 바로 분리.

삽질 모음

  • 처음엔 진입 시 풀싱크를 돌렸음. 첫 화면이 5초 가까이 멈춰서 바로 컴플레인 들어옴. 백그라운드로 빼고 증분 동기화로 변경
  • 같은 메시지가 중복 인서트되면서 PK 충돌. upsert 로 바꿔서 정리
  • DB 마이그레이션 누락으로 기존 사용자가 앱 켜자마자 크래시. 컬럼 추가는 항상 디폴트값 + 폴백 같이 작성하는 습관 들임
  • 마지막 동기화 시각을 안 박아둬서 "왜 갱신 안 됐는지" 디버깅이 지옥이었음. 메타필드는 처음부터 박아두는 게 정답

배운 점

  • 첫 진입 UX 가 데이터 정합성보다 우선. 무거운 작업은 무조건 백그라운드로
  • 동기화 한 번 잘못 짜면 PK 충돌, 누락, 중복까지 한 세트로 옴. upsert + lastSyncedAt 조합이 표준이라 생각하기로 함
  • 사용자가 체감하는 동기화 = 즉시성. 프로그레스 표시 빠지면 "안 됨" 으로 인식함. 인디케이터는 기능이 아니라 필수
  • 기능 하나 추가했을 뿐인데 진입 화면, 설정 화면, 로컬 DB, 모델 4군데가 같이 흔들림. 다음에는 동기화 책임을 한 모듈로 미리 떼어놓고 시작할 예정

다음

댓글 0

첫 댓글 달아줘.