Cheondi
개발 · API

처음으로 설정을 서버와 맞춰 봤다

기기 안에만 있던 설정을 서버와 동기화하며 충돌과 실패 정책을 처음 고민한 기록입니다.

  • #api
  • #sync
  • #settings

기기 안에 저장하던 설정을 다른 환경에서도 이어 쓰기 위해 서버와 맞추는 작업을 봤다. 처음에는 로그인할 때 내려받고 설정을 바꿀 때 올리면 된다고 생각했다. 두 기기에서 값을 바꾸거나 오프라인 상태에서 수정하면 어느 쪽이 최신인지 바로 모호해졌다.

동기화는 파일 복사보다 두 상태의 차이를 합의하는 과정이었다.

설정마다 동기화 범위가 달랐다

언어, 테마, 차트 표시 옵션처럼 사용자 경험에 영향을 주는 값은 서버 동기화 대상이 될 수 있었다. 창 크기나 기기 성능에 따른 옵션은 로컬에 남기는 편이 자연스러웠다.

public class SyncSettingsDto
{
    public string Language { get; init; }
    public string Theme { get; init; }
    public ChartOptions Chart { get; init; }
    public long Revision { get; init; }
}

로컬 설정 객체 전체를 보내지 않고 계약에 포함된 필드만 DTO로 만들었다.

최신이라는 말에 기준이 필요했다

기기 시각은 틀릴 수 있어서 로컬 수정 시간만 비교하기 어려웠다. 서버가 부여한 revision을 사용하고, 업데이트 요청에 내가 읽었던 revision을 함께 보냈다. 그 사이 다른 변경이 있으면 덮어쓰지 않고 충돌로 응답받았다.

GET settings -> revision 12
PUT settings (baseRevision: 12)
 -> success revision 13
 -> conflict current revision 14

초기 단계에서는 복잡한 자동 병합보다 새 서버 값을 보여주고 다시 선택하게 하는 안전한 정책을 택했다.

실패해도 사용 흐름은 유지했다

설정 저장 API가 실패했다고 사용자의 화면 변경을 즉시 되돌리면 불편했다. 로컬에는 변경을 반영하고 동기화 대기 상태로 표시한 뒤 연결이 회복되면 제한적으로 재시도했다.

로그인 직후 서버 설정을 받기 전 잠깐 기본값이 보이지 않도록 초기 로딩 순서도 조정했다. 서버를 기다릴 수 없는 상황에는 마지막 로컬 설정을 사용했다.

동기화는 성공과 실패 사이가 길었다

API 호출 한 번이 성공하는지만 보던 시기에는 동기화도 비슷하게 생각했다. 실제로는 오프라인 수정, 다른 기기 변경, 중복 재시도, 충돌이라는 중간 상태가 있었다.

이 작업으로 설정도 작은 분산 데이터라는 걸 배웠다. 어느 값을 공유할지, 누가 버전을 정할지, 실패 중 사용자에게 무엇을 보여줄지를 정해야 동기화가 됐다.