PatternsP4 본문

AI Chat

대화형 AI 표면 — 헤더·메시지 스레드·입력 + 대화 상태 매트릭스(빈·대기·생성 중·오류)와 스트리밍 시각 언어. 챗 전용 접근성(role=log·부분 텍스트 비낭독)을 갖춥니다.

마지막 업데이트 2026-07-08

데모#

상단 상태 컨트롤로 빈 화면·대기·생성 중·오류를 전환해 대화 매트릭스를 검수합니다. 추천 칩이나 입력으로 메시지를 보내면 생각 중 → 부분 텍스트 스트리밍 → 안착까지 실제로 재생됩니다(중단 가능). 실제 LLM 호출은 없습니다(UI Reference).

상태
기기
WIZ 어시스턴트대기 중
어시스턴트:

안녕하세요, WIZ 워크스페이스 어시스턴트입니다. 문서 검색·작성·요약을 도와드릴 수 있어요.

나: 이번 분기 온보딩 프로젝트 회의록 3건 요약해줘.
어시스턴트:

회의록 3건을 확인했어요. 공통 결론은 “신규 입사자 첫 주 완료율 개선”이고, 핵심 액션은 ① 체크리스트 자동화 ② 멘토 배정 ③ 2주차 피드백 세션입니다.

나: 그중 ① 체크리스트 자동화 담당자랑 마감일만 표로 정리해줄래?
어시스턴트:

정리했어요.

① 체크리스트 자동화

  • 담당: 김도현 (플랫폼팀)
  • 마감: 6월 28일

선행 작업으로 HR 데이터 연동(이수아, 6월 21일)이 필요해요. 자세한 내용은 온보딩 문서를 참고하세요.

AI Chat — 상태 매트릭스 + 스트리밍. Enter 전송 · Shift+Enter 줄바꿈

구성#

영역역할
헤더어시스턴트 정체성(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)으로 올린다

이 규율은 ChatMessageactivityLabel prop으로 구현됩니다 — 실제 단계 이벤트가 있을 때만 라벨을 넘기고(없으면 미지정=침묵), 실패·타임아웃은 activityTone="error"로 실제 상태를 드러냅니다. 웹·Flutter(WizChatMessage) 동일. 별도 aria-live는 두지 않고 ChatThreadrole="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컴포저 텍스트 → 사용자 버블 morphView Transitions(useViewTransition)미지원·reduced-motion이면 즉시 스왑
thinking3-dot(기존)transform·opacity기본 채택
streaming부분 텍스트 blur-in(첫 등장 1회) + 말미 캐럿filter: blur·transform·opacity컴포지터 친화 · 아래 데모 반영
done캐럿 소멸 → 액션바 fade-upopacity·transform
errorFormMessage settle-intransform·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초 미만 로딩 표시 금지 — 첫 토큰이 1초 안에 도착하면 thinking 인디케이터를 건너뜁니다(플리커 방지).
  2. prefers-reduced-motion — blur·stagger·shine 전부 비활성, 정적 라벨 + opacity 스텝만 유지.
  3. 성능 — 새로 도착한 부분만 애니메이트하고 animationendwill-change를 해제합니다. 이미 렌더된 본문을 재애니메이트하지 않습니다.
  4. 접근성 — 스트리밍 부분 텍스트는 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입니다.