Cheondi
개발 · Unity

같은 데이터를 받아도 갱신 순서가 결과를 바꿨다

목록과 상세 화면의 갱신 순서 때문에 이전 값이 남는 문제를 데이터 의존성으로 정리한 기록입니다.

  • #unity
  • #state
  • #refresh

계좌를 바꾸면 목록은 새 데이터로 바뀌는데 상세 패널에는 이전 계좌의 값이 잠깐 남았다. 조금 뒤에는 정상으로 돌아와 네트워크가 느린 문제처럼 보였다. 요청 응답을 로그로 확인하니 데이터 자체는 맞았다. 같은 데이터를 받았지만 어느 화면을 먼저 비우고 그리느냐에 따라 결과가 달랐다.

기존 코드는 계좌 변경, 목록 응답, 선택 이벤트가 각각 화면 갱신을 호출했다. 빠른 응답과 느린 응답의 도착 순서가 바뀌면 마지막으로 그린 것이 최신이라는 보장이 없었다.

화면 사이의 의존성을 적었다

상세 패널은 선택된 목록 항목이 있어야 그릴 수 있었다. 그런데 목록이 바뀌기 전에 상세를 갱신하면 이전 선택을 사용했다.

계좌 변경
 -> 이전 선택 해제
 -> 목록 로딩 표시
 -> 새 목록 적용
 -> 유효한 항목 선택
 -> 상세 갱신

순서를 코드에 드러내니 상세 화면이 목록 변경 이벤트를 임의로 따라가는 구조보다 이해하기 쉬웠다.

응답에는 세대 번호를 붙였다

사용자가 계좌를 빠르게 두 번 바꾸면 먼저 보낸 요청이 나중에 도착할 수 있었다. 요청을 취소할 수 있으면 취소하고, 적용 단계에서도 현재 요청 세대와 같은지 확인했다.

int generation;

async Task ChangeAccount(int accountId)
{
    int current = ++generation;
    ClearSelection();
    var items = await LoadItems(accountId);

    if (current != generation) return;
    ApplyList(items);
    SelectFirstValidItem();
    RefreshDetail();
}

이 값은 최신 결과인지 확인하는 용도라 서버의 데이터 버전과는 다른 개념이었다.

이전 값을 남길지 비울지도 정책이었다

로딩 중에 이전 목록을 그대로 보여주면 화면이 덜 흔들리지만 다른 계좌의 정보로 오해할 수 있었다. 이 화면에서는 정확성이 더 중요해 선택과 상세를 즉시 비우고 로딩 상태를 표시했다. 화면마다 같은 선택이 정답은 아니었다.

빈 결과와 오류도 새 계좌의 상태로 확정해야 했다. 실패했다고 이전 계좌 데이터를 다시 보여주면 소속이 모호해졌다.

비동기는 도착 순서까지 설계해야 했다

API 호출 코드를 처음 배울 때는 요청과 응답만 생각했다. 여러 화면과 이벤트가 연결되니 시작한 순서와 끝난 순서가 다를 수 있다는 점이 더 중요했다.

이 문제를 해결하며 화면 갱신을 단순한 Refresh() 호출 묶음으로 보지 않게 됐다. 어떤 데이터가 먼저 준비돼야 하는지, 오래된 결과를 어떻게 버릴지, 로딩 중 무엇을 보여줄지를 함께 정해야 했다. 같은 값도 순서가 다르면 다른 화면이 됐다.