AI Chat
대화형 AI 표면 — 헤더·메시지 스레드·입력 + 대화 상태 매트릭스(빈·대기·생성 중·오류)와 스트리밍 시각 언어. 챗 전용 접근성(role=log·부분 텍스트 비낭독)을 갖춥니다.
마지막 업데이트 2026-07-08
데모#
상단 상태 컨트롤로 빈 화면·대기·생성 중·오류를 전환해 대화 매트릭스를 검수합니다. 추천 칩이나 입력으로 메시지를 보내면 생각 중 → 부분 텍스트 스트리밍 → 안착까지 실제로 재생됩니다(중단 가능). 실제 LLM 호출은 없습니다(UI Reference).
안녕하세요, WIZ 워크스페이스 어시스턴트입니다. 문서 검색·작성·요약을 도와드릴 수 있어요.
회의록 3건을 확인했어요. 공통 결론은 “신규 입사자 첫 주 완료율 개선”이고, 핵심 액션은 ① 체크리스트 자동화 ② 멘토 배정 ③ 2주차 피드백 세션입니다.
정리했어요.
① 체크리스트 자동화
- 담당: 김도현 (플랫폼팀)
- 마감: 6월 28일
선행 작업으로 HR 데이터 연동(이수아, 6월 21일)이 필요해요. 자세한 내용은 온보딩 문서를 참고하세요.
구성#
| 영역 | 역할 |
|---|---|
| 헤더 | 어시스턴트 정체성(Avatar·이름) + 현재 상태(상태 도트·라벨) |
| 스레드 | ChatThread(role="log"+aria-live, 자동 하단 스크롤) 안에 ChatMessage — 어시스턴트 버블 없는 전폭 텍스트 + MessageActions 액션바, 사용자 우측 회색 버블(GPT-5 비대칭) |
| 빈 화면 | 인사 + 추천 프롬프트 칩 — 막다른 길 대신 다음 행동 제시 |
| 생성 중 | thinking 도트 → 부분 텍스트 + 캐럿 스트리밍, 입력은 중단 버튼 |
| 오류 | FormMessage(error)(Tier 2) + 다시 시도 |
| 입력 | ChatComposer — 실제 ComposerBar + +메뉴·모델 선택·마이크·음성/전송/중단(쇼케이스와 같은 컴포저) |
상태#
대화는 행복 경로 하나가 아니라 상태 매트릭스로 다룹니다. 데모의 상태 컨트롤로 전환합니다.
| 상태 | 트리거 | 표현 |
|---|---|---|
| 빈 화면 | 첫 진입, 0턴 | 인사 + 추천 프롬프트 칩(클릭 시 전송) |
| 대기 | 입력 대기·응답 완료 | 대화 스레드 + 입력 활성, 헤더 “대기 중” |
| 생성 중 | 전송 직후 | thinking 도트 → 부분 텍스트 스트리밍(캐럿) + 헤더 도트 펄스 + 스레드 aria-busy, 입력은 중단 |
| 오류 | 응답 실패 | FormMessage(error)(Tier 2) + 다시 시도 |
“생각 중”은 독립 상태가 아니라 생성 중의 첫 페이즈입니다 — 부분 텍스트가 도착하면 같은 버블에서 도트가 텍스트로 교체되고, 중단하면 누적분이 잘린 채 확정됩니다.
thinking 인디케이터는 최종 답변이 렌더될 바로 그 슬롯에, 최종 답변과 같은 타이포그래피 컨텍스트로 표시합니다. 별도 카드·독립 스피너·본문 텍스트 슬롯을 넘는 장식 요소를 만들지 않습니다 — 이 스레드는 GPT-5식 비대칭이라 어시스턴트 아바타가 없으므로, 인디케이터의 크기 상한은 본문 텍스트 행높이입니다. 첫 토큰이 도착하면 인디케이터는 그 자리에서 부분 텍스트로 교체됩니다 — 요소를 제거 후 재삽입하는 것이 아니라 내용을 치환하는 것이며, 그래서 도트→텍스트 전환에 레이아웃 이동이 없습니다.
진행 상태(activity) 표시#
“문서 검색 중” 같은 진행 단계 라벨은 파이프라인의 실제 단계 이벤트가 있을 때만 표시합니다. 실제 신호가 없으면 일반 thinking 인디케이터만 유지합니다. 장식 목적의 상시·순환 라벨은 금지합니다 — 사용자가 시스템 상태를 신뢰하는 근거를 훼손하기 때문입니다.
| 규칙 | 근거 |
|---|---|
| 실제 이벤트에 바인딩 | 라벨은 백엔드 단계 이벤트가 도착했을 때만 나타난다 — 상태를 지어내지 않는다 |
| 없으면 침묵 | 신호가 없으면 thinking 인디케이터만 유지 — 거짓 진행 표시보다 정직한 침묵 |
| 표시 형태 | thinking 인디케이터에 인접한 단일 라인, 이벤트마다 텍스트 교체(fade). 여러 단계를 누적해 보여야 하면 접이식 영역(예: Accordion)으로 올린다 |
이 규율은 ChatMessage의 activityLabel prop으로 구현됩니다 — 실제 단계
이벤트가 있을 때만 라벨을 넘기고(없으면 미지정=침묵), 실패·타임아웃은 activityTone="error"로 실제 상태를
드러냅니다. 웹·Flutter(WizChatMessage) 동일. 별도 aria-live는 두지 않고 ChatThread의 role="log"가 낭독합니다.
같은 정직성 규칙이 헤더 상태 라벨에도 적용됩니다 — 시간 기반 가짜 완료 타이머나 백엔드와 무관한 고정 문구가 아니라 실제 상태를 반영합니다.
메시지 위계 — 3-tier#
메시지 위계가 챗에서 어떻게 나타나는지.
| Tier | 범위 | 컴포넌트 | 이 패턴에서 |
|---|---|---|---|
| 1 필드 | 입력 | 전송 버튼 비활성 | 빈 메시지 전송 차단(ComposerBar 내장) — 토스트 대신 차단형 |
| 2 폼·제출 | 한 번의 전송·응답 | FormMessage | 응답 생성 실패 — role="alert" + 다시 시도 |
| 3 페이지 | 화면·세션 | Alert·Toast | 연결 끊김·서비스 점검 등 전역 장애(여기선 미사용) |
적응형 동작#
사용자 버블은 최대폭 80%로 우측에 모이고(ChatMessage), 어시스턴트 메시지는 전폭으로 읽힙니다.
ChatThread는 새 메시지·스트리밍마다 자동으로 하단 스크롤합니다(포커스 이동 없음). 데모의 기기
컨트롤(데스크탑·태블릿·모바일)로 채팅 프레임 폭을 제약해 각 기기 레이아웃을 미리볼 수 있고, 터치
기기에서는 실제 기기처럼 스크롤바 거터를 숨깁니다.
접근성#
| 계약 | 구현 |
|---|---|
| 메시지 로그 | 메시지만 담는 role="log" + aria-live="polite" — 시간순 추가를 polite 전달(assertive는 읽던 내용을 끊어 부적합). 오류 alert·추천 칩은 로그 밖 형제로 둬 이중 낭독 방지 |
| 화자 구분 | 각 메시지에 보조기술 전용 화자 접두(“나:/어시스턴트:”, ChatMessage 내장) — 시각은 정렬·표면으로 구분(GPT-5식 — 스레드 내 아바타 없음) |
| 스트리밍 낭독 | 부분 텍스트 버블은 aria-hidden + 스레드 aria-busy="true" — 토큰마다 낭독(잼) 방지, 완성 메시지만 1회 낭독 |
| 생각 중 | 도트는 aria-hidden, 진행은 aria-busy로 전달 |
| 오류 전달 | FormMessage(error)가 role="alert"로 즉시 낭독 |
| 입력 | aria-label="메시지 입력", Enter 전송·Shift+Enter 줄바꿈, 한글 조합 중 Enter 무시 |
| 포커스 | 전송 후 입력창 포커스 유지 — 새 메시지가 포커스를 빼앗지 않음 |
| 모션 | thinking 도트·캐럿·펄스·메시지 등장은 transform/opacity만, prefers-reduced-motion에서 정지(텍스트·상태 라벨 유지) |
생성 모션 레시피(부록)#
패턴의 상태·전이 정의는 위 그대로이고, 아래는 권장 모션 레시피입니다 — opt-in이며 토큰·컴포넌트를
바꾸지 않습니다. WDS 모션 원칙(컴포지터 친화 속성만, prefers-reduced-motion에서 정지)을 따릅니다.
| 페이즈 | 레시피 | 속성 | 비고 |
|---|---|---|---|
| submit | 컴포저 텍스트 → 사용자 버블 morph | View Transitions(useViewTransition) | 미지원·reduced-motion이면 즉시 스왑 |
| thinking | 3-dot(기존) | transform·opacity | 기본 채택 |
| streaming | 부분 텍스트 blur-in(첫 등장 1회) + 말미 캐럿 | filter: blur·transform·opacity | 컴포지터 친화 · 아래 데모 반영 |
| done | 캐럿 소멸 → 액션바 fade-up | opacity·transform | |
| error | FormMessage settle-in | transform·opacity | 기존 Tier 2 등장 모션만 |
컴포지터 친화 주의(교정): 라벨 shimmer(background-clip: text gradient sweep)와 컴포저 border shine(이중
background)은 background-position/background를 애니메이트하므로 컴포지터가 아니라 페인트 단계를
밟습니다 — “transform·opacity·filter만”이라는 모션 규칙 밖입니다. 두 레시피는 제품 소유 선택 장식으로
분류하고, WDS 정본은 transform/opacity/filter 레시피만 권장합니다. 반짝임 질감이 필요하면 텍스트 색을
바꾸지 않는 opacity 펄스나 마스크 transform 슬라이드로 대체하세요.
가드레일(필수):
- 1초 미만 로딩 표시 금지 — 첫 토큰이 1초 안에 도착하면 thinking 인디케이터를 건너뜁니다(플리커 방지).
prefers-reduced-motion— blur·stagger·shine 전부 비활성, 정적 라벨 +opacity스텝만 유지.- 성능 — 새로 도착한 부분만 애니메이트하고
animationend에will-change를 해제합니다. 이미 렌더된 본문을 재애니메이트하지 않습니다. - 접근성 — 스트리밍 부분 텍스트는
aria-hidden, 완성 메시지만 1회 낭독, 스레드aria-busy유지(위 접근성 표와 동일).
Flutter 대응은 선택입니다 — blur-in은 ImageFiltered+AnimatedOpacity로 옮길 수 있으나, 이 부록은 신규
컴포넌트·토큰이 아닌 웹 모션 가이드이므로 패리티 게이트 대상이 아닙니다(web-only 지침).
사용 컴포넌트#
ChatThread · ChatMessage · MessageActions(대화 표면 — GPT-5 비대칭, @wds/ui-web 정식 컴포넌트) ·
ChatComposer(실제 ComposerBar 합성 — 쇼케이스와 공유) · Avatar · Button(다시 시도) ·
FormMessage(오류, Tier 2) · SegmentedButton(데모 컨트롤). 대화 메시지·
스레드·액션바는 이제 실제 컴포넌트이고, 이 패턴은 그것들을 조합합니다. 실제 LLM 호출은 없습니다 — UI Reference입니다.