ComponentsP3 본문

ComposerBar

ChatGPT식 슬롯 기반 컴포저 셸 — 좌측 액션·중앙 textarea·우측 액션. 알약/박스 형태, 고정 지오메트리. Enter 전송·한글 IME 보호·자동 높이.

마지막 업데이트 2026-06-24

한눈에#

ChatGPT식 입력창을 위한 슬롯 기반 컴포저 셸입니다. 좌측 액션(+)·중앙 textarea·우측 액션(마이크·음성·전송)을 슬롯으로 조립합니다. 셸은 알약(pill)·박스(boxed) 형태와 고정 height/radius/spacing을 토큰으로 못 박아 흔들리지 않고, 버튼은 IconButton을 슬롯에 넣어 재사용합니다.

빠른 동작 칩·+ 메뉴로 모드 활성 · 모델 선택 · Enter 전송 · 버튼 hover 시 단축키 툴팁

  • 좌측·우측 액션 슬롯
  • 알약(pill) 셸·고정 지오메트리
  • Enter 전송·Shift+Enter 줄바꿈
  • 한글 IME 보호·자동 높이

입력하면 음성 버튼이 전송으로 바뀝니다 — 첨부·마이크·음성은 슬롯의 IconButton

사용 시점#

좌/우 액션이 있는 ChatGPT식 입력창이면 ComposerBar — 전송 버튼 하나면 충분하면 아래 AiChatInput 프리셋을 쓰세요(같은 셸 위에 단일 전송을 미리 엮은 것).

권장 — 이렇게 쓰세요

지양 — 이러지 마세요

빠른 동작 칩·+ 메뉴로 모드 활성 · 모델 선택 · Enter 전송 · 버튼 hover 시 단축키 툴팁

쓴다첨부·음성 등 여러 액션을 가진 ChatGPT식 컴포저(슬롯으로 버튼 구성)

대신 Textarea액션 없는 일반 여러 줄 텍스트 입력

Enter 전송 · Shift+Enter 줄바꿈 · 한글 조합 중 Enter는 무시됩니다

프리셋전송 버튼 하나면 — AiChatInput 프리셋(아래 「프리셋」 절)

해부#

셸과 동작만 컴포넌트가 소유하고, 액션 버튼은 소비자가 슬롯에 주입합니다. 이 분리로 버튼 상태(hover·focus·disabled)는 IconButton을 그대로 재사용하고, 셸은 지오메트리만 책임집니다.

슬롯prop권장
좌측 액션leadingAction<IconButton variant="subtle" size="sm">(첨부 +)
중앙 입력(내장)제어형 textarea — value/onValueChange
우측 액션trailingActions마이크 ghost · 주 액션 solid(음성↔전송)

예시 API의 variant="filled"는 WDS IconButton에서 채움 정도에 따라 subtle(중립 채움) 또는 solid(브랜드 채움)로 매핑합니다 — WDS는 ChatGPT의 검정 대신 브랜드 토큰을 씁니다.

제어형 계약 — 중앙 입력은 제어형 textarea입니다(value/onValueChange). 입력 상태는 소비자가 소유하고, 전송 후 입력 비우기도 소비자가 onSubmit에서 value''로 되돌려 수행합니다. onSubmit은 키보드 Enter 전용 보조 경로이고, 실제 전송 버튼은 슬롯의 IconButton이 담당합니다.

변형·상태#

동작은 하나의 어휘로 통일하되, 셸 형태variant로 제공합니다.

variant형태정렬쓰는 곳
pill (기본)완전 둥근 알약 (radius.full)세로 가운데단행 중심 ChatGPT식 컴포저
boxed둥근 박스 (radius.xl)액션 하단 정렬여러 줄로 자라는 컴포저

두 형태 모두 min-heightcontrol-height-lg로 고정해 단행 높이가 내용과 무관하게 일정하고(흔들림 제거), textarea는 내용에 맞춰 자동 높이로 자랍니다(약 7줄 상한, 초과분은 내부 스크롤 · 폭은 부모 100%).

상태 — 포커스는 셸 :focus-within(포커스 링), disabled는 입력과 슬롯 액션을 함께 차단합니다. 우측 주 액션은 입력 유무로 음성↔전송이 토글됩니다(아이콘/라벨/동작만 바뀌는 단일 안정 요소 — 포커스 보존, WCAG 2.4.3).

프리셋 — AiChatInput (단일 전송)#

전송 버튼 하나만 필요하면 ComposerBar를 직접 조립하지 않고 AiChatInput 프리셋을 씁니다 — onSend만 주면 됩니다. 내부에서 ComposerBar 셸을 사용하며, 단일 전송 버튼 + 스트리밍 중단 + 입력 상태 내부 관리(비제어)를 미리 엮었습니다. 여러 액션이 필요하면 위의 ComposerBar를 직접 쓰세요.

Enter 전송 · Shift+Enter 줄바꿈 · 한글 조합 중 Enter는 무시됩니다

스트리밍은 소비자가 소유하는 상태입니다 — 전송 후 응답 스트림이 시작되면 state="streaming"으로 바꾸고, 종료/중단 시 "idle"로 되돌립니다.

AiChatInput 상태트리거표시
idle기본전송 버튼(↑) — 빈 입력이면 비활성
streaming소비자가 prop으로 전환중단 버튼(■) — onStop 호출
disableddisabled prop입력·전송 모두 차단

AiChatInput의 variant는 ComposerBar 셸로 매핑됩니다 — pill→알약, expanded→박스+3줄 resting, (미지정)→박스.

AiChatInput Props#

Prop타입기본값설명
onSend(message: string) => void전송 콜백 — trim된 비어있지 않은 메시지만 전달한다.
state'idle' | 'streaming'idleidle: 전송 가능 · streaming: 응답 생성 중(중단 버튼 노출)
onStop() => voidstreaming 중단 콜백
sendLabelstring전송전송 버튼 aria-label(기본 '전송').
stopLabelstring생성 중단중단 버튼 aria-label(기본 '생성 중단').
variant'pill' | 'expanded'시각 변형 — pill: 둥근 소형 바 · expanded: 큰 작성 표면 · 미지정: 기본 박스

그 외 표준 textarea 속성(placeholder·disabled·maxLength 등)을 그대로 전달합니다 — value/onChange는 내부에서 관리합니다.

플레이그라운드#

AiChatInput를 직접 조작해 보세요

속성·테마·토큰을 바꾸고 React·Flutter 코드를 확인하는 풀스크린 빌더로 엽니다.

빌더 열기

플레이그라운드는 넓은 작업 영역이 필요해 웹·태블릿에서 편집할 수 있어요.

API#

Prop타입기본값설명
valuestring제어 값 — 소비자가 상태를 소유한다(전송 후 비우기 등).
onValueChange(value: string) => void값 변경 콜백 — 제어형 계약.
onSubmit(value: string) => voidEnter 전송 콜백 — trim된 값을 전달(빈 값이면 호출하지 않음). Shift+Enter는 줄바꿈, 한글 IME 조합 확정 Enter는 무시한다. 값 비우기는 소비자가 onValueChange로 수행.
leadingActionReactNode좌측 액션 슬롯 — 예: + 첨부(IconButton).
trailingActionsReactNode우측 액션 슬롯 — 예: 마이크·음성·전송(IconButton). 배열로 넘길 때는 각 노드에 key를 부여하거나 Fragment로 감싼다.
variant'pill' | 'boxed'pill셸 형태 — pill(기본) · boxed.
minRowsnumberresting 최소 줄 수 — textarea가 비어도 이 줄 수만큼 높이를 확보한다(긴 프롬프트 작성용). 미지정이면 단행. 자동 높이 상한(약 7줄)은 그대로 적용. (AiChatInput expanded 프리셋이 사용)
aria-labelstring메시지 입력textarea 접근성 이름 — placeholder는 신뢰할 수 있는 접근성 이름이 아니므로 별도 제공. 기본값 '메시지 입력'.
textareaRefRef<HTMLTextAreaElement>textarea로 전달되는 ref(React 19 ref-as-prop).

그 외 표준 textarea 속성(id·name·placeholder·disabled·maxLength 등)을 그대로 전달합니다 — value/onChange/children 제외.

토큰#

component 토큰 없이 input.*과 semantic을 소비합니다(신설 기준 §4 미충족 — 셸은 input 어휘 재사용).

속성토큰
배경/보더/포커스input.bg · input.border · input.border-focus
높이 하한control-height-lg
라운드radius.full(pill) · radius.xl(boxed)
간격space-2(슬롯 gap) · space-1(패딩·액션 간격)

접근성#

키보드 계약 — textarea 표준 동작 위에 구현합니다.

입력결과
EnteronSubmit(value.trim()) — 빈 값이면 호출 안 함
Shift+Enter줄바꿈
한글 IME 조합 중 Enter무시(isComposing) — 마지막 글자만 전송되는 버그 차단
  • textarea에 aria-label 기본 제공('메시지 입력') — placeholder는 접근성 이름으로 신뢰할 수 없음
  • 슬롯 액션 버튼의 aria-label소비자 책임IconButtonaria-label을 필수로 강제합니다
  • 포커스 링은 셸 :focus-within — Input·AiChatInput과 동일 어휘
  • 상태 변화 안내는 소비자 책임: 전송 결과는 aria-live="polite" 영역으로(위 데모가 기준 구현 — 입력 에코는 비-라이브 노드에)