피드백과 복구
모든 상태 변화가 이해되고 다음 행동으로 이어지게 합니다.
목적
피드백은 시스템이 사용자의 행동을 받았는지, 무엇이 달라졌는지, 다음에 무엇을 할 수 있는지 알려준다. Dot은 성공만 축하하지 않는다. 진행, 빈 상태, 오류, 부분 성공, 복구까지 하나의 연속된 고리로 설명한다.
좋은 피드백은 사용자가 시스템을 추측하지 않게 하며, 실패했을 때도 작업과 자신감을 보존한다.
피드백의 위치
| 종류 | 사용 조건 | 위치 |
|---|---|---|
| inline | 특정 필드·행·검증 결과에 고정된 상태 | 해당 대상 바로 가까이 |
| toast | 짧고 비차단적인 완료·변경 결과 | overlay 영역 |
| dialog | 데이터 손실, 되돌릴 수 없음, 현재 흐름 중단 | focus가 제한된 overlay |
| page state | 주 데이터를 사용할 수 없는 loading·empty·error | 원래 콘텐츠 frame |
| persistent status | 장시간 작업, 저장·검증·발행 단계 | PageHeader meta 또는 WorkflowBar |
중요한 결과를 toast에만 두지 않는다. 다음 판단에 필요한 상태는 콘텐츠 안에 지속적으로 남긴다.
상태의 전체 흐름
모든 비동기 작업은 필요한 범위에서 다음 상태를 정의한다.
- idle: 실행 전 조건과 행동을 이해할 수 있다.
- pending: 어떤 작업이 진행 중이고 중복 실행이 막혔는지 알 수 있다.
- success: 무엇이 완료됐고 화면의 어떤 값이 바뀌었는지 알 수 있다.
- partial success: 성공·실패 범위와 다음 조치를 구분한다.
- error: 끝내지 못한 일, 영향, 복구 행동을 제공한다.
- stale 또는 interrupted: 결과가 오래됐거나 연결이 끊긴 상태를 숨기지 않는다.
상태 전환 때 콘텐츠 높이와 위치를 불필요하게 크게 바꾸지 않는다.
로딩
- 즉시 끝나는 작업에는 불필요한 화면 전환이나 장식적 로딩을 만들지 않는다.
- 목록·카드가 처음 로드될 때는 최종 구조와 닮은
Skeleton을 사용한다. - 사용자가 시작한 저장·검증은 trigger 레이블과 persistent status에서 진행 중임을 보여준다.
- 화면 전체를 사용할 수 없는 경우에만 전체 loading state를 쓴다.
- 이미 보이는 데이터를 갱신할 때는 기존 내용을 지우지 않고 갱신 상태와 마지막 갱신 시각을 표시한다.
- 장시간 AI 처리에는
DotMotion의processing을 사용할 수 있지만 텍스트 상태, 취소 가능 여부, 실패 fallback을 함께 제공한다. - 정확한 진행률을 알 수 없는데 임의의 percent를 보여주지 않는다.
빈 상태
EmptyState는 다음 순서로 작성한다.
- 무엇이 없는지
- 왜 비어 있거나 어떤 가치를 만들 수 있는지
- 사용자가 할 수 있는 다음 행동
빈 상태의 종류를 구분한다.
- 처음 시작: 아직 만든 항목이 없음 → 생성 행동 제공
- 필터 결과 없음: 원본은 있지만 조건에 맞지 않음 → 필터 해제 또는 수정
- 권한·범위 없음: 접근 가능한 데이터가 없음 → 이유와 요청 경로
- 완료된 상태: 처리할 예외가 없음 → 정상임을 알려주고 다음 확인 시점 제공
캐릭터는 넓고 드문 처음 시작이나 의미 있는 완료 상태에서만 사용할 수 있다. 반복되는 필터 결과 없음에는 사용하지 않는다.
오류 문장
오류는 다음 세 부분으로 쓴다.
- 무엇을 끝내지 못했는지
- 현재 데이터나 작업에 어떤 영향이 있는지
- 사용자가 지금 할 수 있는 복구 행동
예:
커리큘럼을 저장하지 못했어요. 입력한 내용은 이 화면에 남아 있어요. 연결 상태를 확인한 뒤 다시 저장해주세요.
학습 알림 24건 중 3건을 보내지 못했어요. 실패한 대상만 확인해 다시 발송할 수 있어요.
기술 코드와 요청 ID는 지원에 필요할 때만 보조 정보로 제공한다. 사용자가 해결할 수 없는 내부 오류를 행동 지시처럼 쓰지 않는다.
복구 행동
- 입력·선택·scroll을 가능한 한 보존한다.
- 재시도가 안전하면 실패한 범위만 다시 실행한다.
- 자동 재시도 중이라면 횟수보다 현재 상태와 수동 행동 가능 시점을 알려준다.
- 충돌이 생기면 조용히 덮어쓰지 않고
내 변경 유지,최신 내용 불러오기, 변경 비교처럼 이해 가능한 선택을 제공한다. - 권한 문제는 무한 재시도 대신 필요한 권한과 요청 경로를 알려준다.
- 삭제된 대상은 돌아갈 목록과 대체 대상을 제안할 수 있다.
- 복구할 수 없는 경우에도 사용자의 다음 안전한 목적지를 제공한다.
부분 성공
일괄 작업과 AI 처리에서는 부분 성공을 독립 상태로 다룬다.
- 성공·실패·건너뜀 수를 분리한다.
- 실패 원인별로 대상을 찾을 수 있게 한다.
- 성공한 항목을 다시 실행하지 않아도 되는지 명확히 한다.
전체 성공처럼 오해하게 만드는 toast를 보내지 않는다.- 다음 행동은
실패한 3건 확인,실패한 항목만 다시 시도처럼 범위를 말한다.
완료와 다음 방향
완료 피드백은 한 고리가 끝났음을 알리고 다음 배움이나 운영 행동을 밝힌다.
- 저장 완료 뒤에는 검증이 필요하면 다음 행동을 연결한다.
- 발행 완료 뒤에는 적용 시점과 영향을 확인하게 한다.
- 학습 완료 뒤에는 다음 학습의 시점 또는 돌아갈 위치를 제안한다.
- AI 검토 완료 뒤에는 확정·적용 상태와 남은 미검토 항목을 보여준다.
완료를 과도한 애니메이션으로 축하하지 않는다. 사용자의 실제 성취와 다음 방향을 짧고 구체적으로 보여준다.
Flexible, Link, Glow의 적용
- Flexible: 오류 종류, 사용자 권한, 작업 규모에 따라 복구 행동을 조정하되 메시지 구조는 유지한다.
- Link: 사용자의 원래 행동, 진행 상태, 결과, 복구, 다음 단계를 끊지 않는다.
- Glow: 불확실한 순간에 다음 안전한 행동을 은은하게 안내하거나 의미 있는 완료를 표시할 때만 쓴다.
예외와 피해야 할 사용
- 모든 성공에 toast를 쌓지 않는다.
- 오류를
문제가 발생했습니다한 문장으로 끝내지 않는다. - 실패 후 폼을 초기화하거나 dialog를 자동으로 닫지 않는다.
- color, icon, 캐릭터만으로 상태를 설명하지 않는다.
- 빈 상태와 오류 상태를 같은 문구와 그림으로 처리하지 않는다.
- 이미 보이는 데이터를 background refresh 중이라는 이유로 skeleton으로 교체하지 않는다.
- 시스템이 모르는 진행률이나 완료 시간을 약속하지 않는다.
- 사용자가 해결할 수 없는 오류에
다시 시도만 무한히 제공하지 않는다.
상태와 접근성
- 진행 중인 container에는 필요한 경우
aria-busy를 사용한다. - 짧은 상태 변화는 적절한 live region으로 알리되 중복 메시지를 피한다.
- 긴 오류는 focus가 이동하지 않아도 heading과 관계를 통해 찾을 수 있어야 한다.
- field error는 control의 accessible description 관계에 연결한다.
- toast는 읽을 시간을 보장하고 중요한 행동을 시간 제한 toast에만 두지 않는다.
- dialog는 focus trap, 초기 focus, trigger 복귀를 지킨다.
- reduced motion에서도 성공·실패·진행 상태를 텍스트로 이해할 수 있어야 한다.
- 오류 focus 이동은 제출 후 첫 오류처럼 사용자의 다음 행동을 실제로 돕는 경우에만 한다.
검토 질문
- idle부터 success·error까지 필요한 상태가 모두 정의됐는가?
- 사용자는 진행 중인 대상과 행동을 알 수 있는가?
- 오류 문장이 실패한 일, 영향, 복구 행동을 포함하는가?
- 실패 후 입력과 선택이 보존되는가?
- 부분 성공을 전체 성공처럼 오해할 가능성이 없는가?
- toast에만 남아 사라지는 중요한 상태는 없는가?
- empty, filtered empty, permission empty, error를 구분했는가?
- 완료가 다음 배움 또는 관리 행동과 연결되는가?
- keyboard와 screen reader에서도 상태 변화와 오류 관계가 전달되는가?
- 캐릭터와 Glow가 상태 설명을 대체하지 않고 의미 있는 안내만 하는가?