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

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

기술 문서 기반의 책임 경계

작업 당시에는 눈앞의 오류를 먼저 고쳤지만, 이후 기술 문서와 맞춰 보며 확인 순서를 조금 더 구체화했다.

Unity 실행 순서 문서는 Awake, OnEnable, Start, Update 같은 엔진 이벤트가 어느 구간에서 호출되는지 설명한다. 하지만 로딩·성공·빈 결과·실패 같은 업무 상태의 순서까지 대신 정해 주지는 않는다. 두 종류의 순서를 섞어 생각하면 콜백이 한 프레임 늦게 왔다는 이유만으로 이전 화면 상태가 다시 살아날 수 있다.

그래서 수명주기 메서드는 상태 전이를 직접 흩어 놓는 장소보다, 한 곳에 모인 전이 함수를 호출하는 경계로 쓰는 편이 추적하기 쉬웠다.

다음에 서버 설정 동기화의 시작 같은 문제를 보면 아래 순서부터 확인하려고 한다.

  • 요청 DTO와 화면 모델의 분리
  • HTTP 성공과 업무 성공의 구분
  • 취소·타임아웃·재시도 정책
  • 예시 도메인으로 재현 가능한 로그

새 응답을 적용하기 전에 요청 식별자와 현재 선택 조건이 여전히 같은지 확인해야 오래된 응답을 버릴 수 있다.

공식 참고 자료