목록 아래까지 스크롤하면 다음 내역을 불러오는 기능이 있었다. 데이터가 많을 때는 자연스럽게 동작했다. 문제는 내역이 하나도 없는 계좌였다. 빈 화면인데 아래쪽 로딩 표시가 계속 돌고, 잠시 뒤 같은 요청을 다시 보내고 있었다.
처음에는 응답을 받지 못했다고 생각했다. 로그를 보니 요청과 응답은 계속 성공했다. 응답 배열이 비어 있었고, 화면은 그걸 “아직 첫 페이지가 오지 않았다”로 해석하고 있었다.
0개는 실패가 아니었다
기존 조건은 리스트 개수만 보고 있었다.
if (rows.Count == 0)
ShowLoading();
하지만 0은 아직 모른다는 뜻이 아니라 조회 결과가 정말 없다는 뜻일 수도 있다. 요청 상태와 결과 상태를 분리해야 했다.
if (state == RequestState.Loading)
ShowLoading();
else if (state == RequestState.Succeeded && rows.Count == 0)
ShowEmpty();
코드는 단순하지만 이 구분이 없으면 사용자는 기다려야 하는지, 볼 내용이 없는지 알 수 없다.
다음 페이지가 없다는 정보도 필요했다
무한 스크롤은 현재 목록뿐 아니라 더 가져올 데이터가 있는지도 알아야 했다. 빈 페이지가 왔는데 hasMore를 그대로 두면 바닥에 닿을 때마다 같은 요청이 반복됐다.
void ApplyPage(PageResult result)
{
rows.AddRange(result.items);
hasMore = result.items.Count == pageSize;
isLoading = false;
}
실제 API가 명시적인 hasNext를 준다면 그 값을 쓰는 편이 더 정확하다. 당시에는 기존 응답 규칙에 맞춰 마지막 페이지를 판단했다. 중요한 건 “빈 결과를 받았다”가 요청을 끝내는 상태 전환으로 이어져야 한다는 점이었다.
빈 화면에도 할 일이 있었다
로딩을 끄고 나니 화면이 텅 비었다. 기능적으로는 맞지만 사용자는 오류인지 정상인지 알 수 없었다. 내역이 없다는 안내, 새로고침 버튼, 잘못 남아 있던 이전 선택 상태를 함께 정리했다.
빈 상태는 아무것도 하지 않는 상태가 아니라 별도로 설계해야 하는 화면이었다. 로딩, 실패와 같은 레벨로 다뤄야 했다.
경계값을 먼저 시험하게 됐다
이후 목록 기능을 보면 많은 데이터보다 먼저 0개, 1개, 페이지 크기와 정확히 같은 개수를 시험한다. 평범한 데이터는 대부분 잘 동작하지만 경계값에서 상태 구분이 드러나기 때문이다.
데이터가 없는데 로딩이 끝나지 않았던 버그는 배열 처리가 어려워서 생긴 게 아니었다. 없음을 하나의 정상 결과로 정의하지 않았기 때문에 생겼다. 없는 데이터도 분명한 정보라는 걸 배운 작업이었다.