[AI 활용법 9] UI의 5가지 상태 — AI에게 화면을 시킬 때 빠뜨리는 것
idle · loading · empty · error · success. 빈 상태와 에러 회복이 가장 자주 빠진다
시리즈 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요소:
- 그림 — 비어 있음을 시각적으로
- 이유 — 왜 비어 있는가 (필터 때문? 첫 방문? 권한?)
- 다음 행동 — 뭘 할 수 있나
- 차이 — 첫 방문 빈 상태 와 필터로 비워진 상태 는 다르다
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) 칩
- 팁 한 문장
idle 과 empty 를 같은 화면으로 만들면 안 된다. 시작 안 함 과 시작했는데 결과 없음 은 다른 신호.
폼 — 실시간 검증
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 — 성능 예산