본문으로 건너뛰기
Dot Design System
v0.3.1

로딩·빈 상태·오류

데이터 상태가 바뀌어도 같은 프레임과 다음 행동을 유지합니다.

목표

데이터의 준비 여부, 비어 있는 이유, 실패한 범위와 복구 행동을 정확히 구분한다. 사용자가 빈 화면을 해석하거나 같은 행동을 반복하지 않게 하고, 정상 흐름에서 예외 흐름으로 바뀌어도 위치와 입력 맥락을 보존한다.

상태를 먼저 분리한다

  • initial loading: 아직 데이터 유무를 판단할 수 없음
  • refreshing: 기존 데이터가 있고 새 값으로 갱신 중
  • empty first use: 아직 생성된 데이터가 없음
  • empty result: 검색·필터 조건에 맞는 데이터가 없음
  • partial: 일부 데이터만 준비되거나 일부 요청 실패
  • stale: 이전 데이터는 있지만 최신성을 보장할 수 없음
  • error recoverable: 사용자가 재시도하거나 입력을 고칠 수 있음
  • error blocked: 권한, 정책, 외부 장애 등 사용자가 즉시 해결할 수 없음

initial loading이 끝나기 전에 empty를 렌더하지 않는다. 요청 실패를 데이터 없음으로 바꾸지 않는다.

로딩 표현 선택

Skeleton

  • 결과 구조를 알고 있고 곧 같은 자리에 콘텐츠가 나타날 때 사용한다.
  • 실제 layout과 비슷한 수와 크기로 만든다.
  • 랜덤한 placeholder를 과도하게 반복하지 않는다.
  • 이전 데이터가 있으면 skeleton으로 모두 교체하기보다 기존 값을 유지하고 갱신 중임을 표시한다.

일반 진행 표시

  • 구조가 없거나 작은 독립 작업이 처리 중일 때 사용한다.
  • 처리 대상을 저장 중…, 목록을 불러오는 중…처럼 텍스트로 말한다.
  • 측정 가능한 작업은 spinner보다 progress와 남은 범위를 제공한다.

DotMotion

  • processing은 실제 데이터 탐색, 분석, 답변 생성 중에만 사용한다.
  • avatar는 실제 답변이나 안내가 진행되는 동안만 사용한다.
  • 일반 fetch, route 이동, 짧은 저장에 브랜드 모션을 사용하지 않는다.
  • motion 옆에 처리 대상과 다음 상태를 텍스트로 제공한다.

빈 상태 설계

최초 데이터 없음

  1. 무엇이 아직 없는지
  2. 만들면 어떤 가치가 생기는지
  3. 시작할 수 있는 주 행동 하나

검색·필터 결과 없음

  1. 어떤 조건에서 결과가 없는지
  2. 가장 비용이 낮은 조건 수정 방법
  3. 검색어 수정 또는 필터 초기화 행동

권한으로 보이지 않음

  • 실제 데이터가 없는 것처럼 말하지 않는다.
  • 볼 수 없는 이유와 권한 요청·담당자 확인 경로를 제공한다.

Dot character는 넓고 드문 최초 경험이나 의미 있는 다음 학습 방향을 제안할 때만 검토한다. 반복되는 table empty, 검색 결과 없음, 오류에는 장식적으로 사용하지 않는다.

오류 표현 선택

  • field inline: 사용자가 입력을 고칠 수 있는 오류
  • section inline: 한 영역만 실패하고 나머지 화면은 사용 가능
  • toast: 이미 끝난 행동의 짧은 실패이며 현재 맥락에서 복구 가능
  • dialog: 현재 결정이나 작업 손실을 막아야 함
  • full-frame error: 화면의 주 데이터를 전혀 사용할 수 없음

오류의 그릇은 기술 예외 종류가 아니라 사용자가 읽을 정보량, 심각도, 복구 행동과 지속 시간을 기준으로 선택한다.

오류 메시지 구조

  1. 무엇을 끝내지 못했는지
  2. 현재 입력이나 저장 상태가 유지되는지
  3. 사용자가 지금 할 수 있는 행동
  4. 필요할 때만 안전한 범위의 원인과 기술 식별자
  • 같은 요청 재시도는 같은 조건과 입력으로 실행한다.
  • 문의가 필요하면 복사 가능한 오류 ID와 발생 시각을 보조 정보로 제공한다.
  • 사용자가 해결할 수 없는 오류에 다시 시도만 무한히 제공하지 않는다.

전환과 레이아웃

  • loading → content, content → refreshing, error → retry가 같은 frame에서 전환된다.
  • loading 요소가 실제 콘텐츠보다 크게 또는 작게 잡혀 layout shift를 만들지 않는다.
  • 요청이 빨리 끝났을 때 indicator가 짧게 번쩍이지 않도록 표현 시점을 조정할 수 있지만 결과 전달을 지연하지 않는다.
  • 실패 후 성공하면 오류를 제거하고 복구된 범위를 알린다.
  • route가 바뀌지 않는 재시도는 scroll과 focus를 보존한다.

좁은 화면

  • skeleton도 실제 모바일 record 구조를 반영한다.
  • full-frame error가 navigation과 back 경로를 가리지 않는다.
  • 긴 오류 세부 정보는 접을 수 있지만 핵심 상태와 복구 행동은 항상 보인다.
  • 빈 상태 character나 illustration 때문에 주 행동이 fold 아래로 밀리지 않게 한다.

접근성

  • initial loading container에 필요한 경우 aria-busy="true"를 제공한다.
  • 상태 텍스트는 role="status"와 polite live region을 사용한다.
  • 중요한 submit 실패는 role="alert"를 사용할 수 있으나 반복 요청마다 같은 문구를 과도하게 낭독하지 않는다.
  • skeleton은 접근성 트리에서 장식 처리하고 로딩 상태 텍스트를 별도로 제공한다.
  • focus를 spinner로 강제 이동하지 않는다.
  • 오류 발생 시 관련 field, 오류 summary 또는 error heading으로 의미 있게 이동한다.
  • retry button은 실패한 대상을 레이블에 포함한다.
  • reduced motion에서도 처리 상태와 결과를 동일하게 이해할 수 있어야 한다.

예제

사용 코드 · TSX
if (query.isPending) {
  return <RecordListSkeleton aria-label="실행 기록을 불러오는 중" />;
}

if (query.isError) {
  return (
    <EmptyState
      title="실행 기록을 불러오지 못했어요"
      description="현재 조건은 그대로 유지했어요. 잠시 후 다시 시도해보세요."
      action={<Button onClick={() => query.refetch()}>실행 기록 다시 불러오기</Button>}
    />
  );
}

if (query.data.length === 0) {
  return activeFilterCount > 0 ? <NoFilteredResults /> : <FirstRunEmptyState />;
}

AI 처리 중:

사용 코드 · TSX
<div role="status" aria-live="polite" className="flex items-center gap-3">
  <DotMotion variant="processing" size={64} />
  <span>학습 기록에서 다음 질문을 찾고 있어요.</span>
</div>

피해야 할 사용

  • 요청 완료 전 empty 표시
  • 오류를 빈 데이터나 0으로 대체
  • refresh 때 기존 데이터를 모두 skeleton으로 교체
  • 일반 fetch에 Dot processing motion 사용
  • 모든 오류를 toast 하나로 처리
  • 사용자가 해결할 수 없는 문제에 재시도만 제공
  • error 발생 시 입력, filter, scroll 초기화
  • motion 또는 색만으로 상태 전달

검토 체크

  • initial, refreshing, first empty, result empty, partial, stale, error를 구분한다.
  • loading 표현이 실제 결과 구조와 작업 성격에 맞다.
  • 이전 데이터와 사용자의 입력을 가능한 한 유지한다.
  • 오류 범위에 맞는 inline, toast, dialog, full frame을 선택했다.
  • 메시지가 실패 대상과 복구 행동을 말한다.
  • Dot motion은 실제 AI 답변·처리 맥락에만 사용한다.
  • layout shift와 짧은 indicator 깜박임을 줄였다.
  • focus, live region, reduced motion이 정의돼 있다.
  • 좁은 화면에서도 상태와 주 행동이 먼저 보인다.

관련 항목