JIN.PROC // v3.1
~/ / AI 활용법 / 2026-04-29-ai-guide-9-ui-states

[AI 활용법 9] UI의 5가지 상태 — AI에게 화면을 시킬 때 빠뜨리는 것

idle · loading · empty · error · success. 빈 상태와 에러 회복이 가장 자주 빠진다

DATE 2026.04.29 UPDATED 2026.04.29 READ ~ 3 MIN WORDS 596

시리즈 9편. LLM에게 UI를 시키면 success state 만 잘 만든다. 진짜 UX는 그 외 4가지에 있다.

5가지 상태 — 외워야 한다

상태 설명 자주 빠지는 이유
idle 아무 일 없음, 시작 전 “보면 알지”
loading 데이터 가져오는 중 빠른 네트워크에서 안 보임
empty 결과가 0개 디자인 파일에 자료 가득
error 실패 행복 경로만 그림
success 결과 있음 항상 들어감

LLM에게 “이 페이지 만들어 줘”라고 하면 거의 항상 success 만 나온다. 명시적으로 5가지를 요구해야 한다.

Empty state — 가장 자주 빠진다

검색 결과 0개, 알림 0개, 첫 방문 — 이 화면이 빈 div 면 사용자는 고장난 줄 안다.

좋은 empty state의 4요소:

  1. 그림 — 비어 있음을 시각적으로
  2. 이유 — 왜 비어 있는가 (필터 때문? 첫 방문? 권한?)
  3. 다음 행동뭘 할 수 있나
  4. 차이첫 방문 빈 상태필터로 비워진 상태 는 다르다
function Empty({ reason }) {
  if (reason === "no-data") return (
    <div>
      <Illustration name="dawn" />
      <h3>아직 항목이 없어요</h3>
      <p>첫 항목을 만들어 보세요.</p>
      <Button>새로 만들기</Button>
    </div>
  );
  if (reason === "filtered") return (
    <div>
      <Illustration name="search" />
      <h3>조건에 맞는 결과가 없어요</h3>
      <Button variant="ghost" onClick={resetFilters}>필터 초기화</Button>
    </div>
  );
  return null;
}

같은 빈 화면 인데 사용자에게 주는 신호가 완전히 다르다.

Loading — 보이지 않는 게 더 무섭다

응답이 빠르면 사용자는 아무 일도 안 한 줄 안다. 보통:

  • < 100ms: 인디케이터 불필요 (사람의 인식 임계)
  • 100ms ~ 1s: 작은 인디케이터 (스피너, opacity 변화)
  • 1s ~ 10s: 명시적 progress (skeleton, %)
  • > 10s: 단계적 메시지 + cancel 버튼

Skeleton loader가 spinner보다 거의 항상 좋다. 레이아웃 점프(CLS) 도 막는다.

Error — 회복 방법까지

“오류가 발생했습니다”는 죽은 메시지 다. 사용자가 다음에 뭘 해야 하는지 까지 보여야 한다.

상황 메시지 + 행동
네트워크 끊김 “오프라인입니다 [재시도]”
권한 부족 “이 작업에는 관리자 권한이 필요합니다 [관리자에게 요청]”
입력 오류 필드 옆 에 인라인 (전역 alert X)
서버 5xx “잠시 후 다시 시도해 주세요. [지금 재시도]”

에러 메시지의 단위는 “무엇이 잘못됐고, 다음에 무엇을 하면 되는가”.

Optimistic update — 체감 속도

서버 응답을 기다리지 않고 UI를 먼저 바꾼다. 실패하면 되돌린다.

function toggleLike(id) {
  setLiked(id, true);                // optimistic
  api.like(id).catch(() => {
    setLiked(id, false);             // revert
    toast.error("좋아요를 처리하지 못했어요. 다시 시도해 주세요.");
  });
}

LazyClaude도 토글류 인터랙션에 이 패턴을 쓴다. 사용자에겐 즉시 반응한 것처럼 보인다.

Idle — 의도된 아무 일 없음

idle은 “데이터가 없다”가 아니다. 아직 시작 안 했다. 검색창에 아무것도 입력 안 했을 때, 폼이 비어 있을 때.

이 상태에서는 보통:

  • 안내 일러스트 + 짧은 도움말
  • 예시(suggestion) 칩
  • 팁 한 문장

idleempty 를 같은 화면으로 만들면 안 된다. 시작 안 함시작했는데 결과 없음 은 다른 신호.

폼 — 실시간 검증

LLM에게 폼을 시키면 제출 시 일괄 검증 만 만든다. 사용자는 다 채우고 에러 5개를 한꺼번에 본다 — 가장 짜증나는 패턴.

좋은 패턴:

  • blur 시 검증 — 한 필드를 떠나는 순간
  • success도 보이게 — ✓ 표시
  • 에러는 필드 옆 — 전역 alert X
  • submit 버튼은 검증 통과 시에만 활성

Disabled vs Loading vs Hidden

세 가지를 헷갈리지 않게:

상태 의미 사용처
Disabled 권한/조건 부족 tooltip으로 보이게
Loading 처리 중 spinner + 버튼은 disabled
Hidden 무관함 컨텍스트가 아님

“왜 누를 수 없지?”가 가장 나쁜 질문이다.

AI에게 명시할 프롬프트 템플릿

[UI 상태 5종 모두 구현 필수]
- idle:    시작 전, suggestion 노출
- loading: skeleton, 100ms 이상이면 명시적
- empty:   빈 이유 + 다음 행동
- error:   회복 방법
- success: 결과

[추가 요구]
- 모바일 (320px) 부터 동작
- 키보드만으로 모든 핵심 인터랙션
- 색상만으로 정보 전달 금지

이 템플릿을 시스템 프롬프트 에 박아 두면, “다 만들었다”의 정의가 달라진다.

한 줄 요약

success만 잘 그리는 UI는 데모 화면. 5가지를 그리는 UI가 제품 화면.


다음 편: AI 활용법 10 — 모바일 퍼스트와 접근성 이전 편: AI 활용법 8 — 성능 예산