계좌 목록이 비어 있을 때 주문 화면에서 null 예외가 발생했다. 첫 번째 계좌가 항상 존재한다고 가정하고 선택하는 코드가 원인이었다. 예외를 막고 빈 배열을 반환하자 화면은 멈추지 않았지만 사용자는 버튼만 비활성인 이유를 알 수 없었다.
데이터가 없다는 결과에도 여러 원인이 있었고 다음 행동이 달랐다.
빈 상태를 하나로 묶지 않았다
아직 로딩 중인지, 정말 계좌가 없는지, 권한이 없는지, 조회가 실패했는지를 구분했다.
public abstract record AccountViewState
{
public record Loading : AccountViewState;
public record Ready(IReadOnlyList<Account> Items) : AccountViewState;
public record NoneAvailable : AccountViewState;
public record Forbidden : AccountViewState;
public record Error(bool CanRetry) : AccountViewState;
}
빈 배열은 NoneAvailable로, 통신 실패는 Error로 바꿔 서로 다른 화면을 그렸다.
팝업은 다음 행동을 제공해야 했다
“계좌가 없습니다”만 보여주고 닫기 버튼을 두면 사용자는 다시 같은 화면으로 돌아왔다. 계좌를 만들거나 지원 경로로 이동할 수 있는 경우에는 해당 행동을 제공했고, 앱 안에서 해결할 수 없다면 이유와 돌아갈 화면을 안내했다.
주문 버튼뿐 아니라 빠른 주문, 차트 메뉴 등 모든 진입점에서 같은 규칙을 사용했다. 한 화면만 막으면 다른 경로에서 다시 예외가 났다.
상태가 바뀌면 자동으로 회복했다
다른 화면에서 계좌가 추가된 뒤 돌아오면 이전 빈 상태를 계속 보여주는 문제가 있었다. 화면 재진입이나 계좌 변경 이벤트에서 목록을 다시 확인하고, 유효한 계좌가 생기면 정상 상태로 전환했다.
반대로 마지막 계좌가 비활성화되면 현재 선택과 관련 캐시를 정리했다. 빈 상태는 앱 시작에만 결정되는 값이 아니었다.
예외 방어에서 제품 흐름으로 넓어졌다
처음 수정 목표는 null 예외를 없애는 것이었다. 사용자의 입장에서 보면 중요한 건 왜 진행할 수 없고 다음에 무엇을 해야 하는지였다.
이후 빈 배열을 만나면 단순히 if (items.Count == 0) return;으로 끝내지 않는다. 빈 이유와 가능한 다음 행동, 다른 진입점의 일관성을 함께 본다. 빈 화면도 정상적으로 설계해야 하는 기능 상태였다.