📚

CMS_네버슬립_사전 (UI Dictionary)

📚
CMS_네버슬립_사전 (UI Dictionary)
Default view
Title
Slug
Code Snippet
Accessibility
Difficulty
Subcategory
AntiPattern
Definition
Component Map
Status
Korean Note
Aliases
Related
One-line
Category
Visual
AI Prompt
thinking-panel
{message.reasoning && <Collapsible><CollapsibleTrigger>{`${duration}초 동안 생각함`}</CollapsibleTrigger><CollapsibleContent className="text-xs text-muted-foreground border-l pl-3">{message.reasoning}</CollapsibleContent></Collapsible>}
Collapsible은 aria-expanded와 동기화합니다. 추론 영역은 role="region"과 aria-labelledby로 묶어요. 추론은 답변에 비해 보조 정보이므로 스크린리더가 자동으로 읽지 않도록 aria-live는 두지 않는 게 일반적이에요.
중급
LLM
추론
투명성
추론과 답변을 같은 폰트, 같은 위계로 쭉 붙여 두면 사용자가 어디까지가 답이고 어디부터가 추론인지 헷갈려요. 추론은 항상 보조 위계로 시각적으로 한 단 낮춰서 보여 줍니다.
Claude의 Extended Thinking, OpenAI o1과 o3, DeepSeek R1, Cursor의 Composer가 도입한 추론 가시화 방식입니다. AI 답변 위쪽에 접힌 채로 'Thinking…' 또는 '6초 동안 생각함' 같은 라벨이 보이고, 클릭하면 모델이 머릿속에서 거친 체크리스트와 고민, 반박, 결론을 작은 회색 글씨로 펼쳐서 보여 줘요. 답변 본문과는 시각적으로 분명히 구분되도록 색을 옅게 하거나 들여쓰기, 다른 폰트로 위계를 잡습니다. 사용자가 답변에 의문이 생기면 추론을 펴서 직접 짚어 볼 수 있어요.
shadcn/ui Collapsible을 씁니다. Anthropic Claude API의 thinking blocks를 그대로 받아 렌더하고, Vercel AI SDK 5의 reasoning 필드를 활용해요. lucide-react ChevronDown과 Brain 아이콘을 함께 사용합니다.
Review
헤더 라벨은 '6초 동안 생각함'처럼 자연스러운 한국어가 좋습니다. 영어 'Thought for 6 seconds'를 그대로 직역하기보다 한국 사용자가 듣기 친숙한 표현이에요. '6초 추론'으로 더 줄일 수도 있지만 의미가 살짝 약해져서, 사전에서는 '생각함'을 권장합니다. 추론 텍스트가 길어지면 '… 더 보기'로 접어 주세요.
Thinking Panel, Reasoning Panel, 추론 노트, Extended Thinking
AI가 답을 내기 전에 어떤 흐름으로 생각했는지 작은 패널에 접어 두고 보여 주는 방식입니다. Claude Extended Thinking과 OpenAI o1이 자리 잡게 만든 추론 투명성 영역이에요.
AI 협업·생성형 UI
1. AI 메시지 위에 'Thinking 패널'을 만들어 주세요. 기본 접힘 상태이고 헤더에는 'XX초 동안 생각함' + chevron 아이콘.
2. 펼치면 모델 추론 텍스트를 옅은 회색 + 작은 폰트로 표시. 답변 본문과 시각적으로 분리 (얇은 좌측 border + 들여쓰기).
3. 추론 중에는 'Thinking...' + 점멸 dots 애니메이션. 추론이 끝나면 헤더 라벨이 'XX초 동안 생각함'으로 바뀝니다.
suggested-replies
{suggestions.map(q => <button onClick={() => append({role:'user', content:q})} className="rounded-full border px-3 py-1.5 text-sm hover:bg-muted">{q}</button>)}
각 칩은 <button>으로 감싸 키보드 포커스가 가능하게 합니다. 그룹 영역에는 role="group"과 aria-label="후속 질문"을 둬요. 칩 호버 시에만 정보를 노출하면 키보드 사용자에게는 안 보이므로, focus 상태에서도 동일하게 처리해야 합니다.
입문
LLM
유도
후속 질문을 답변 본문 안에 마크다운 리스트로 박아 두면 클릭할 수 있는 칩으로 보이지 않아요. 사용자는 그냥 텍스트로 읽고 지나칩니다.
Claude와 Perplexity, NotebookLM, Bing Copilot이 자리 잡게 만든 후속 질문 유도 방식입니다. 답변이 끝나는 자리에 '이런 것도 궁금하실 텐데요' 같은 작은 안내와 함께 질문 후보 칩 3~4개를 띄워 줘요. 사용자가 칩을 누르면 그 질문이 입력창에 채워지거나 바로 다음 턴으로 보내집니다. 첫 답변을 받자마자 다음에 뭘 더 물어보지 하고 잠시 멈추는 단절을 줄여 주는 게 이 방식의 강점이에요.
shadcn/ui Button(variant='outline', size='sm', rounded-full)을 씁니다. lucide-react Sparkles 아이콘을 함께 두면 좋아요. Vercel AI SDK의 useChat에서 messages.length가 바뀔 때마다 별도 endpoint로 후속 질문을 생성하거나, 같은 LLM 응답에 structured output으로 함께 받는 방식이 있습니다.
Review
한국어 후속 질문은 영어보다 평균 1.3배 정도 길어지기 쉽습니다. 칩 안 텍스트를 max-width로 잘라 주고 호버나 popover에서 전체를 볼 수 있게 두면 깔끔해요. 칩 개수는 모바일에서 2개, 데스크톱에서 4개 정도로 잡으면 화면이 답답하지 않습니다.
Suggested Replies, Follow-up Chips, 후속 질문 칩
AI 답변 아래에 다음 질문 후보 3~4개를 칩으로 보여 줘서, 사용자가 다음에 뭘 물어보면 좋을지 멈칫하지 않게 도와줍니다.
AI 협업·생성형 UI
1. AI 답변 마지막 메시지 아래에 후속 질문 칩 3~4개를 가로 wrap으로 보여 주세요. 칩은 둥근 모양(border-radius full), 옅은 회색 테두리, 호버 시 배경색.
2. 클릭하면 해당 질문 텍스트를 입력창에 자동 채우거나(편집 가능) 또는 바로 전송하는 두 옵션을 prop으로.
3. 칩 위에 작은 안내 라벨("이런 것도 궁금해요" 또는 "후속 질문")을 두고, 답변 본문과는 8px 정도 시각 분리.
tool-call-ui
<Collapsible><CollapsibleTrigger>{toolName} <Loader2 className="animate-spin" /></CollapsibleTrigger><CollapsibleContent><pre>{JSON.stringify(args)}</pre></CollapsibleContent></Collapsible>
도구 호출 영역에는 role="status"와 aria-live="polite"를 둡니다. 진행 중, 완료, 실패를 시각만 아니라 텍스트("검색 중", "완료", "실패")로도 명시해요. Collapsible은 aria-expanded 상태와 동기화합니다.
고급
LLM
에이전트
도구
도구 호출을 사용자에게 숨기고 '답변 생성 중…' 스피너만 보여 주면 에이전트가 무엇을 하는지 모르는 사용자는 신뢰를 잃습니다. 결과가 어떻게 나왔는지 확인할 방법도 없어져요.
Claude와 ChatGPT, Cursor, Vercel AI SDK에서 자리 잡은 에이전트 UI 방식입니다. 에이전트가 웹 검색이나 코드 실행, 파일 읽기, DB 쿼리 같은 도구를 부르면 그 과정을 검은 박스로 두지 않고 어떤 도구를 어떤 인자로 호출했는지, 결과가 무엇인지를 접을 수 있는 칩이나 카드로 펼쳐 보여 줘요. 진행 중에는 스피너가 돌고 완료되면 결과 미리보기가, 실패하면 에러 메시지가 자리에 들어옵니다. 에이전트가 안에서 무엇을 하고 있는지 사용자에게 그대로 보여 주기 때문에 자연스럽게 신뢰가 쌓여요.
Vercel AI SDK의 ToolUI 패턴을 권장해요. 접힘은 shadcn/ui Collapsible로 처리합니다. lucide-react Loader2, Check, X 아이콘과 Tailwind animate-spin을 함께 씁니다.
Review
도구명은 web_search나 run_python처럼 영어 그대로 두는 게 표준이지만, 그 옆에 '웹 검색'이나 '파이썬 실행' 같은 짧은 한국어 라벨을 부제로 적어 주면 한국 사용자에게 훨씬 친숙해요. 인자로 들어간 JSON은 접힘 안에 두고, 펼쳐도 너무 길면 '… 더 보기'로 잘라 줍니다.
Tool Call UI, Function Call Display, 도구 호출
에이전트가 검색이나 코드 실행, API 호출 같은 도구를 부를 때 어떤 작업을 하는지 칩 안에 도구명과 인자, 진행 상태를 띄워서 보여 주는 방식이에요.
AI 협업·생성형 UI
1. 에이전트가 도구를 부를 때 표시할 카드를 만들어 주세요. 좌측 도구 아이콘 + 가운데 도구명·인자 한 줄 요약 + 우측 진행 상태(스피너/체크/X).
2. 카드는 기본 접힌 상태이고, 클릭하면 펼쳐서 전체 입력 인자(JSON) + 결과를 코드 블록으로 보여줍니다.
3. 여러 도구가 순차 호출되면 카드를 세로로 stack. AI 답변과 같은 메시지 영역 안에 inline.
citation
<sup className="text-xs cursor-pointer text-blue-600" onClick={() => openSource(idx)}>{idx}</sup>
<sup>은 <button>이나 <a>로 감싸 키보드 포커스가 가능하게 만듭니다. aria-describedby로 출처 카드 ID를 연결하고, 출처 카드는 role="region"과 aria-labelledby로 묶어요. 시각만으로 의미가 전달되지 않도록 스크린리더가 '출처 1'이라고 읽어 주게 처리해야 합니다.
중급
LLM
검색
신뢰
답변 끝에 'Sources: 1, 2, 3' 같은 별도 섹션만 두고 본문에는 표시하지 않으면, 사용자는 어떤 문장이 어느 출처에서 왔는지 매핑할 수 없어요. 인라인으로 본문에 번호를 붙여 두는 방식이 훨씬 친절합니다.
Perplexity와 ChatGPT Search, Bing Copilot이 자리 잡게 만든, LLM 답변에 출처를 붙이는 방식입니다. 본문 안 작은 위첨자 번호(¹²³)로 어느 문장이 어떤 출처에서 왔는지 표시하고, 번호를 클릭하면 사이드바나 답변 하단에 출처 카드가 펼쳐져요. 카드에는 제목과 도메인, favicon, 발췌 1~2줄, 전체 URL이 들어갑니다. 사용자는 답변뿐 아니라 그 주장이 어디서 왔는지 그 자리에서 곧바로 확인할 수 있어요.
react-markdown의 remarkPlugins로 [^1] 형태를 위첨자로 변환해요. 사이드 패널은 자체 컴포넌트로 만들고, 모바일에서는 shadcn/ui Sheet(side='right')로 분기합니다. Vercel AI SDK의 generative UI 패턴과 결합하면 자연스럽게 묶입니다.
Review
한국어 본문에 위첨자 숫자를 붙이면 한자식 각주처럼 보여서 사용자에게 학술적이고 믿음직한 인상을 줍니다. 다만 폰트에 따라 한글 라인하이트와 어긋나서 본문 정렬이 흔들릴 수 있어요. line-height: 1.7과 vertical-align: super 정렬을 함께 쓰면 안전합니다.
Citation, Inline Citation, Source Card, 출처 카드
본문에 위첨자 번호(¹²³)로 출처를 표기하고, 번호를 클릭하면 사이드나 하단에 출처 카드(제목, URL, 발췌)가 펼쳐지는 방식입니다. AI 답변의 신뢰성을 한눈에 보여 줘요.
AI 협업·생성형 UI
1. LLM 답변에 인라인 인용 번호를 붙여 주세요. 본문 문장 끝에 작은 superscript 번호(¹²³)로 표시하고, 클릭하면 우측 사이드에 출처 카드 패널이 열립니다.
2. 출처 카드는 favicon + 도메인 + 제목(굵게) + 발췌 2줄 + '전체 보기' 링크로 구성합니다.
3. 같은 번호가 본문 여러 곳에서 등장할 수 있어야 하고(같은 출처 재인용), 사이드 패널의 해당 번호 위로 호버하면 본문 인용 위치가 하이라이트되도록 처리.
coupang-product-card
<div className="rounded-xl overflow-hidden"><div className="relative aspect-square"><img/><span className="absolute top-2 left-2 bg-blue-600 text-white text-[10px] px-1.5 py-0.5 rounded">로켓배송</span></div>…</div>
별점은 시각만 아니라 aria-label로 "별점 4.8(리뷰 1,234개)"처럼 명시해요. 취소선 원가는 aria-label="정가 25,800원", 할인가는 aria-label="할인가 19,800원"으로 분리해서 announce하면 좋습니다. 배지와 할인율은 색만으로 의미를 전달하지 않도록 텍스트도 함께 명시해야 해요.
중급
쿠팡
이커머스
카드
Amazon처럼 상품명을 3~4줄 길게 쓰고 가격을 작게 두면 한국 이커머스 사용자에게는 어색해요. 한국에서는 가격이 먼저 눈에 들어와야 익숙하기 때문에, 가격이 시각 위계의 1순위가 아니면 카드가 묻혀 보입니다.
쿠팡이 자리 잡게 만든 한국 모바일 이커머스 상품 카드입니다. 정사각형이나 4:5 비율 이미지의 왼쪽 위에 '로켓배송' 또는 '쿠팡' 배지가 붙고, 이미지 아래로 짧은 상품명 1~2줄과 노란 별이 함께 표시되는 별점·리뷰 수가 이어져요. 그다음 줄에는 빨간색으로 강조한 할인율과 굵은 검은색 할인가가 나오고, 회색 취소선이 그어진 원래 가격이 옆에 작게 따라옵니다. 카드 가장 아래에는 '내일(목) 도착 보장' 같은 배송 칩이 자리해요. 11번가나 G마켓, 옥션, SSG도 비슷한 변형을 씁니다. Amazon 카드와 비교하면 같은 면적에 들어가는 정보 양이 더 많아요.
자체 ProductCard 컴포넌트로 만들어요. shadcn/ui엔 직접 매칭되는 컴포넌트가 없습니다. lucide-react Star와 Truck 아이콘을 활용하고, 가격 정렬은 Tailwind tabular-nums로 맞춥니다.
Review
가격은 tabular-nums 폰트 변형을 적용해 자릿수가 맞아야 그리드에서 카드끼리 가격이 같은 자리에 깔끔하게 떨어집니다. '내일(목) 도착'처럼 요일을 한 글자로 줄이는 표기가 한국 표준이에요. 배송 칩 색상은 쿠팡은 파랑, 11번가는 빨강, SSG는 검정, 당근은 주황 식으로 브랜드마다 갈라집니다.
Coupang Product Card, Korean E-commerce Card, 한국 이커머스 카드
이미지와 로켓배송 같은 배지, 짧은 상품명, 별점과 리뷰 수, 할인율과 가격, '내일 도착' 같은 배송 칩이 한 카드에 빽빽하게 들어간 한국 이커머스의 익숙한 모양이에요.
국내 UI 패턴
1. 쿠팡식 상품 카드를 만들어 주세요. 이미지 위에 '로켓배송' 배지(파란 작은 칩, 좌상단). 이미지 아래엔 상품명 2줄까지, 별점(노란별+숫자) + 리뷰수.
2. 가격 줄: 좌측 빨강 할인율(예: '23%') + 굵은 검정 할인가('19,800원') + 우측 작은 회색 취소선 원가('25,800원').
3. 가장 아래에 '내일(목) 도착 보장' 같은 배송 칩 (밝은 파랑 배경 + 작은 트럭 아이콘).
naver-search-area
<Tabs defaultValue="all"><TabsList>{categories.map(...)}</TabsList></Tabs><Command>...</Command>
검색창은 form 안에서 role="search"로 둡니다. 자동완성은 aria-autocomplete="list"와 aria-controls로 결과 패널 ID를 연결해요. 화살표 키로 자동완성 항목을 탐색하고 Enter로 선택할 수 있도록 만듭니다.
중급
네이버
검색
포털
Google처럼 검색창 하나만 두고 카테고리는 결과 페이지에서만 보여 주면 한국 사용자에게 어색하게 느껴집니다. 한국 사용자는 무엇을 찾고 싶은지 먼저 정한 다음 검색창에 들어가는 경향이 있어, 진입 단계에 카테고리 탭이 있는 편이 훨씬 편해요.
네이버와 다음이 자리 잡게 만든 한국 검색 UI입니다. 검색창 위에 통합과 이미지, 블로그, 카페, 뉴스, 지도, 동영상 같은 영역 탭이 놓이고, 검색창에 입력하면 좌측에 검색어 자동완성이, 우측에 트렌드와 추천 키워드가 두 영역으로 갈라져 펼쳐져요. 영어권의 Google이나 DuckDuckGo가 검색창 하나만 두는 것과 다르고, 한국 사용자는 '먼저 영역을 고르고 검색한다'는 흐름에 익숙합니다.
shadcn/ui Tabs와 Command(cmdk) 조합을 권장해요. 자동완성 2단 분리는 Command의 CommandGroup 두 개로 만들 수 있어요. 모바일에서는 Sheet로 풀스크린 검색 모달 패턴을 적용합니다.
Review
데스크톱은 카테고리 탭이 검색창 위에 오고, 모바일은 검색창 아래에(또는 검색에 진입한 뒤 분리해서) 배치하는 변형이 흔해요. 자동완성 우측의 '인기 검색어'는 한국 포털의 오랜 특징이었지만, 최근에는 사회적 논란이 있어 일부 서비스가 제거했습니다. AI 검색 UI에서는 후속 질문 칩(Suggested Replies) 형태로 대체하는 걸 권장해요.
Naver Search Bar, 통합 검색, 카테고리 검색
검색창 위에 카테고리 탭(통합, 블로그, 이미지, 뉴스 등)을 두고, 입력하면 두 단으로 갈라진 자동완성을 보여 주는 한국 포털식 검색 영역이에요.
국내 UI 패턴
1. 네이버식 검색 영역을 만들어 주세요. 검색창 위에 카테고리 탭 5~7개(통합·블로그·이미지·뉴스·지도). 활성 탭은 텍스트 굵게 + 하단 초록 라인.
2. 검색창에 입력하면 좌측에 검색어 자동완성 8개, 우측에 '인기 검색어' 또는 '추천' 5개를 보여주는 2단 패널을 띄워 주세요.
3. 검색창 폭은 데스크톱 720px·모바일은 풀와이드. 좌측 검색 아이콘 + 우측 '검색' 버튼.
terms-multi-check
const allChecked = terms.every(t => t.checked); const onAllChange = (v) => setTerms(terms.map(t => ({...t, checked: v})));
각 체크박스에 명확한 label을 붙입니다. htmlFor 또는 wrapping으로 라벨 클릭만으로 토글이 가능하도록 만들어요. '전체 동의'는 aria-controls로 하위 ID 묶음을 가리키고, 일부만 체크된 indeterminate 상태도 시각과 aria 양쪽에서 함께 표현해야 합니다.
중급
가입
법적
체크박스
'전체 동의'를 누르면 모든 약관에 자동 동의가 되고 약관 전문은 작은 회색 hyperlink로만 두는 구성은 위험해요. 사용자가 무엇에 동의했는지 모르고, 법적으로도 신뢰 측면에서도 문제가 됩니다.
개인정보보호법과 정보통신망법 영향으로 한국 서비스는 약관을 항목별로 받아야 합니다. 그래서 최상단에 '전체 동의' 한 줄을 두고 그 아래에 개인정보와 이용약관, 마케팅, 제3자 제공 같은 4~6개 개별 항목을 두는 형태가 자리 잡았어요. 각 항목 옆에는 필수와 선택을 구분하는 칩이 붙고, 우측 '보기' 버튼을 누르면 약관 전문이 모달로 떠요. '전체 동의'는 모든 하위 체크를 한꺼번에 토글하는데, 필수만 자동 체크하고 선택은 따로 두는 변형도 있습니다.
shadcn/ui Checkbox와 자체 TermsAggregator 패턴을 조합해요. react-hook-form의 useFieldArray와 결합하는 방식을 권장합니다. Headless UI Switch는 체크박스 시각이 표준이라 부적합이에요.
Review
선택 약관(마케팅 동의)은 처음에 체크되지 않은 상태로 보여 주는 게 기본이에요. 미리 체크해 두면 다크 패턴으로 분류되고, 분쟁이 생기면 법적으로 불리해집니다. '필수' 라벨은 빨강, '선택'은 회색이 일반적이에요. 약관별로 동의한 시점과 이력은 백엔드에 따로 남겨야 분쟁이 생겼을 때 증빙이 가능합니다.
Terms Multi-Check, Consent Aggregator, 전체 동의
최상단 '전체 동의'와 그 아래에 필수·선택 라벨이 붙은 4~6개 개별 체크박스로 구성됩니다. 한국 회원가입에는 거의 빠지지 않고 등장해요.
국내 UI 패턴
1. 한국형 약관 다중 동의 UI를 만들어 주세요. 최상단 '전체 동의' (굵은 큰 텍스트, 큰 체크박스) + 하위 5개 개별 약관 (각 항목에 '필수' 또는 '선택' 칩 + '보기' 텍스트 버튼).
2. '전체 동의'를 클릭하면 모든 하위 체크가 토글되도록 처리. 하위 중 하나라도 안 체크되면 '전체 동의'도 자동 해제.
3. 약관 보기는 별도 모달로 띄우고, 모달 안에는 약관 전문 + '동의'/'동의 안 함' 버튼.
kakaotalk-bubble
<div className="flex justify-end items-end gap-1"><span className="text-yellow-600 text-xs">1</span><div className="bg-yellow-300 rounded-2xl px-3 py-2">…</div></div>
메시지 영역에는 role="log"와 aria-live="polite"를 둡니다. 새 메시지가 도착하면 스크린리더가 자동으로 announce해요. 안 읽음 숫자는 시각만 아니라 aria-label로 "안 읽음 N개"라고 명시해야 합니다.
입문
카카오톡
모바일
iMessage처럼 본인 버블을 파랑, 상대 버블을 회색으로 만들면 한국 사용자는 시선이 헷갈려요. '노랑 = 내 메시지'로 학습돼 있기 때문입니다.
카카오톡이 자리 잡게 만든 채팅 버블 형태입니다. 한국에서 만들어진 채팅 UI는 거의 모두 이 모양을 따라요. 좌측 정렬 버블은 흰색이고 우측은 카카오 옐로우입니다. 상대 메시지 위에는 닉네임이 한 줄 들어가고, 시간은 같은 사람의 마지막 버블에만 표시돼요. '안 읽음 N'은 작은 노란 숫자로 버블 옆에 붙습니다. iMessage(파란/회색 버블)와는 시각적으로 다른 모양으로 자리 잡았고, 한국 사용자는 노란 버블을 보면 자연스럽게 '내가 보낸 거'라고 받아들여요.
자체 ChatBubble 컴포넌트로 만드는 것이 좋아요. 기존 라이브러리(react-chat-elements 등)는 PC와 웹용이라 한국형 톤과 잘 맞지 않습니다. Tailwind 'bg-yellow-300' 변형으로 직접 구현하면 됩니다.
Review
AI 챗 UI를 만들 때 사용자 버블에 카카오 노랑을 쓰면 한국 사용자에게 친숙해요. 다만 AI 응답 버블은 흰색이나 연회색이 정석입니다. 노랑은 본인 메시지를 뜻하는 색이기 때문이에요. 시간은 '오후 3:24' 같은 한국식 표기가 자연스럽고, 24시간제는 거의 쓰지 않습니다.
KakaoTalk Bubble, Chat Bubble, 메시지 버블
좌측(상대) 흰 버블과 우측(본인) 노란 버블, 시간 표시, '안 읽음 N'(노랑 작은 숫자)이 함께 들어가는 한국형 채팅 버블이에요.
국내 UI 패턴
1. 카카오톡식 채팅 버블을 만들어 주세요. 좌측 흰 버블(상대), 우측 노란 버블(본인), 시간은 같은 사람의 마지막 버블에만 표시.
2. 상대 메시지는 위에 닉네임 + 좌측에 프로필 원형 아바타 32px. 우측 메시지엔 아바타 없음.
3. 안 읽음 표시는 우측 메시지의 좌측에 작은 노란 숫자 텍스트로 ("1" 또는 "2"). 메시지가 읽히면 사라짐.
kakao-alert
<AlertDialog><AlertDialogContent className="max-w-[320px] rounded-2xl">…<AlertDialogFooter className="grid grid-cols-2 gap-2">…</AlertDialogFooter></AlertDialogContent></AlertDialog>
role="alertdialog"와 함께 aria-labelledby(제목), aria-describedby(본문)를 연결해요. 자동 포커스는 destructive 액션이 아닌 '취소'에 두는 것이 안전합니다(실수 방지). Esc로 취소 동작이 일어나도록 합니다.
입문
카카오
다이얼로그
모바일
확인과 취소를 가로로 같은 색, 같은 위계로 두면 사용자가 '되돌릴 수 없는 동작'에 무의식적으로 탭하기 쉬워요. primary 액션 하나만 색으로 강조하면 됩니다.
카카오톡과 카카오뱅크가 정착시킨 한국 모바일 confirm 다이얼로그 형식이에요. 화면 가운데 정렬 카드, 14~16px 둥근 모서리, 본문 위 짧은 제목 1줄, 본문 1~2줄, 하단에 같은 폭 두 버튼(좌측은 회색 취소, 우측은 카카오 옐로우 확인)으로 구성됩니다. iOS의 stacked 액션 시트나 Material의 좌우 텍스트 버튼과는 시각적 위계가 달라요.
shadcn/ui AlertDialog(확정성 dialog)에 커스텀 색상을 입혀 쓰거나 Radix Dialog를 직접 씁니다. iOS Native UIAlertController와는 시각적으로 달라요. window.confirm()은 절대 사용하지 마세요.
Review
한국 사용자는 확인 버튼이 우측에 있다는 데 익숙합니다. macOS와 iOS는 우측, Windows는 좌측이라는 차이가 있는데, 한국 모바일에서는 거의 모든 앱이 우측을 써요. 위치를 뒤집으면 사용자가 실수하기 쉽습니다. 카카오는 확인 색을 노랑(브랜드)으로 쓰고 토스나 뱅크샐러드는 파랑을 쓰지만, 강조 색이 우측에 들어간다는 위치 룰이 가장 중요해요.
Kakao Alert, 한국형 다이얼로그, Confirm Dialog
둥근 흰 카드 위에 좌측 취소(회색)와 우측 확인(노랑) 두 버튼으로 구성된 한국 모바일 알림 다이얼로그입니다. iOS native나 Material 다이얼로그와는 다른 모양으로 자리 잡았어요.
국내 UI 패턴
1. 카카오식 alert 다이얼로그를 만들어 주세요. 화면 가운데 흰 카드 + 16px 둥근 모서리 + 제목·본문·하단 같은 폭 두 버튼(좌 회색 '취소' / 우 노란 '확인').
2. 다이얼로그 폭은 화면 80~85%, 좌우 padding은 24px, 버튼 높이는 48px로 만들어 주세요.
3. 다이얼로그가 dim된 배경 위에 떠 있고, 배경 클릭으로는 닫히지 않도록 처리합니다 (실수 방지).
empty-state
<EmptyState icon={SearchX} title="결과가 없어요" description="검색어를 다르게 써 볼까요?" action={<Button>필터 초기화</Button>} />
메시지 영역을 <div role="status">로 감싸고 aria-live="polite"를 두면 검색 결과가 바뀔 때 스크린리더가 자동으로 읽어 줘요. 액션 버튼이 있어도 자동 포커스는 검색창에 두는 게 좋습니다. 사용자가 곧바로 검색어를 다시 입력할 수 있어요.
입문
상태
마이크로카피
'데이터가 없습니다' 회색 텍스트만 띄워 두면 사용자에게 다음 행동을 못 알려 줘요. 0건 화면이 여러 곳에 있는데 모두 같은 일러스트와 같은 카피로 채우는 것도 피해 주세요. 화면마다 사용자가 떠올려야 하는 다음 행동이 다르기 때문이에요.
처음 들어온 사용자거나 검색 결과가 0건인 화면, 필터에 다 걸러진 화면, 권한이 없는 화면처럼 비어 있는 영역만 덜렁 보이면 사용자는 보통 화면이 잘못된 줄 알아요. 작은 일러스트나 아이콘과 짧은 한 줄, 다음 액션 버튼 하나만 채워 줘도 사용자가 무엇부터 하면 되는지 빠르게 감을 잡습니다. 검색 결과 0건 화면, 처음 진입한 빈 대시보드, 권한이 없는 화면, 에러로 막힌 화면은 모두 들어가는 카피와 액션이 달라야 해서, 한 컴포넌트로 모든 경우를 덮으려 하면 어떤 화면도 충분히 친절하지 못해요.
자체 <EmptyState /> 컴포넌트를 만드는 방식이 가장 깔끔합니다. shadcn/ui에는 Empty 전용이 없어요. Tremor Empty, NextUI Empty, Mantine Empty가 참고하기 좋아요. 일러스트가 부담스러우면 lucide-react 아이콘과 Tailwind 조합만으로 가볍게 만들면 됩니다.
Review
'결과가 없습니다'라고 마침표로 끝내기보다 '검색어를 조금 바꿔서 다시 찾아볼까요?'처럼 다음 행동 동사를 카피에 그대로 적어 주세요. 토스나 당근, 카카오의 Empty State는 일러스트가 작고(56~80px) 한국어 카피도 짧고 친근합니다. 영어권에서 자주 보이는 큰 일러스트와 긴 설명을 그대로 가져오면 한국 사용자에게는 무겁다는 인상을 줘요.
Empty State, 빈 화면, No Data State, Zero State
비어 있는 화면을 단순히 '없음'으로 두지 않고, 사용자가 다음에 뭘 하면 좋을지 한 줄로 안내해 주는 화면입니다.
피드백·상태
1. 검색 결과 0건일 때 보여줄 Empty State를 만들어 주세요. lucide 아이콘 1개 + 굵은 1줄 카피 + 작은 설명 + '필터 초기화' 버튼.
2. 신규 사용자가 처음 들어온 빈 대시보드를 만들어 주세요. 작업 시작 가이드 카드 3개와 '첫 프로젝트 만들기' primary 버튼.
3. 같은 EmptyState 컴포넌트가 케이스(no-results / first-time / no-permission / error)에 따라 props로 분기하도록 작성해 주세요.
streaming-text
const { messages } = useChat({ api: '/api/chat' }); /* 토큰 자동 스트리밍 */
스크린리더에 무한 announce가 가지 않게 aria-live="polite"를 적용하고 토큰 청크 단위로 throttle해요(250~500ms 단위로 합쳐서 announce). 코드블록은 완성된 후에만 <pre>에 넣어서 코드 영역 announce 폭주를 방지해요.
중급
LLM
마크다운
스트림이 끝날 때까지 빈 메시지로 두고 마지막에 한 번에 렌더하면 스트리밍의 가치를 통째로 버리게 됩니다. 마크다운 미완성 토큰을 그대로 react-markdown에 던져 깨진 표·코드블록을 깜빡이게 만드는 것도 같이 피해 주세요.
SSE나 Web Streams로 받은 토큰을 도착 즉시 화면에 이어 붙입니다. AI가 일하고 있다는 신호를 사용자가 즉시 받기 때문에, 같은 응답 시간이라도 체감 속도가 달라져요. 단순 텍스트라면 append만 해도 되지만 마크다운이나 코드블록은 미완성 토큰을 그대로 렌더하면 깨진 HTML이 보여요. 그래서 닫히지 않은 마크다운은 한 번 버퍼링한 다음 그려야 합니다.
Vercel AI SDK의 useChat / streamText, assistant-ui 라이브러리를 활용해요. react-markdown과 streaming-safe parser(예: marked-incomplete)를 함께 씁니다. caret 점멸은 Tailwind animate-pulse나 @keyframes로 처리해요.
Review
한국어는 자모 단위 토큰화가 모델마다 달라서 글자가 몇 자씩 점프하듯 나타나기도 해요. 사용자가 글자가 깨졌다고 오해하지 않도록 점멸 caret을 함께 보여 주고, 줄바꿈 직후에는 살짝 지연을 줘서 흐름을 부드럽게 만듭니다. '답변 중…' 같은 텍스트보다 시각적으로 깜빡이는 caret 하나가 한국 사용자에게 더 친숙해요.
Streaming Text, Token Streaming, 토큰 스트림, SSE Stream
LLM 응답을 한 번에 보여 주지 않고, 토큰이 도착하는 즉시 한 글자씩 화면에 흘려보내요. 사용자가 답변이 이미 시작됐다고 받아들이게 됩니다.
AI 협업·생성형 UI
1. SSE 스트림으로 받은 토큰을 React useState에 누적하면서 마크다운으로 렌더해 주세요. 코드블록(``` … ```)이 닫히기 전까지는 안의 텍스트를 plain으로만 보여주는 버퍼링 적용.
2. Vercel AI SDK의 useChat을 사용해서 스트리밍 챗 UI를 만들어 주세요. 답변 중에는 빈 메시지에 점멸 caret을 표시합니다.
3. 토큰 도착 사이에 살짝(20~40ms) 지연을 줘서 흐름을 부드럽게 만들어 주세요. 단, 줄바꿈 직후엔 더 긴 지연(100ms)으로 호흡 추가.
identity-verification-kr
// 본인확인 진입 (KCB 예시)
const openVerification = () => window.open('/api/auth/kcb/init', 'verify', 'width=480,height=640')
외부 모듈은 통제할 수 없어요. 진입 화면의 약관 체크박스는 라벨 클릭이 가능하고, '필수'/'선택' 텍스트를 노출하며, 키보드 탭 순서를 보장해요. 새 창 팝업으로 띄우면 스크린리더가 컨텍스트를 잃을 수 있어 가능하면 같은 창 redirect로 처리해요.
고급
인증
결제
가입
본인인증을 가입 단계 직전에 들이미는 것은 위험합니다. 사용자가 가치를 느끼기 전에 주민번호를 요구하면 이탈 1순위예요. 결제·금융 직전까지 미루고, 그 시점에 '왜 필요한지'를 명시해 주세요.
정보통신망법과 전자금융거래법 영향으로 거의 모든 한국 서비스가 이 흐름을 씁니다. KCB와 NICE, SCI 같은 본인확인기관에 위탁된 외부 페이지로 점프하거나 임베드되고, 사용자는 통신사 선택, 약관 다중 동의, 이름·주민번호·휴대폰 입력, 마지막에 6자리 SMS OTP 입력까지 30초 안에 끝내야 해요. 외부 모듈이라 디자인은 우리 손에 없지만, 진입 직전 화면과 실패한 뒤 어디로 가야 할지를 안내하는 부분은 우리가 책임져야 합니다.
외부 본인확인 모듈은 직접 만들지 않아요. KCB·NICE·SCI iframe 또는 redirect를 사용합니다. 진입과 리턴 UX만 다루고, shadcn/ui Dialog + Checkbox + Button + input-otp를 조합해요. 간편인증 대안으로는 카카오, 네이버, 통신사 PASS가 있습니다.
Review
만 14세 미만은 법정대리인 동의가 따로 필요해요. 외국인은 외국인등록번호 흐름이 따로 분기됩니다. 통신사 알뜰폰은 분류가 별도여서 SKT/KT/LGU+ 3사만 노출하면 알뜰폰 사용자가 막혀요. 최근에는 PASS·카카오·네이버 간편인증을 동등한 옵션으로 제시하는 게 표준이에요.
Identity Verification, 휴대폰 본인인증, KISA 본인확인, 본인확인 서비스
통신사 선택부터 약관 동의, 정보 입력, SMS 6자리 OTP까지 이어지는 한국 표준 본인확인 흐름이에요. 회원가입과 결제, 금융 진입에는 거의 빠지지 않고 등장합니다.
국내 UI 패턴
1. 한국형 본인인증 진입 화면을 만들어 주세요. 왜 인증이 필요한지 1줄, 어떤 정보를 사용하는지 chip 3개, '본인인증하기' CTA 1개로 구성합니다.
2. 통신사 선택 5개(SKT, KT, LGU+, 알뜰폰SKT, 알뜰폰KT/LGU+) → 약관 다중 체크박스(전체 + 5개 개별 + 필수/선택 라벨) → '인증번호 받기' 흐름의 placeholder UI를 만들어 주세요.
3. 외부 본인확인 모듈에서 돌아온 뒤 성공/실패 콜백을 받는 페이지를 만들어 주세요. 실패 시 '다시 시도' + '다른 방법으로 인증'(이메일·간편인증) 두 옵션 제시.
toss-fullwidth-cta
<div className="fixed inset-x-0 bottom-0 px-4 pb-[env(safe-area-inset-bottom)] bg-white"><Button size="lg" className="w-full h-14">다음</Button></div>
비활성 상태를 색만으로 표현하지 마세요(aria-disabled + 안 누르는 이유 텍스트 표시). 폼 검증 실패 시 버튼을 disabled로 두기보다, 누르면 첫 에러 필드로 스크롤+포커스를 이동시키는 편이 더 친절해요.
중급
토스
모바일
1차 액션이 하나가 아닌데 버튼을 2~3개 한 줄에 박아 두면 풀와이드 CTA의 명확성이 사라져요.
한국 핀테크 앱이 자리 잡게 만든 모양이에요. 폼이나 온보딩에서 다음 액션을 한 손 엄지로 지연 없이 누를 수 있게 도와줍니다. 1열 폼과 함께 자연스러운 흐름을 만들고, 키보드가 올라오면 버튼이 키보드 위로 따라 올라오는 동작이 가장 중요해요. 일반 sticky bottom과는 이 부분이 달라요. 한 화면에 1차 액션 하나만 노출하기 때문에 사용자가 다음에 뭘 할지 헷갈리지 않습니다.
shadcn/ui Button(size="lg")에 Tailwind 'fixed bottom-0 inset-x-0 px-4 pb-[env(safe-area-inset-bottom)]'를 적용합니다. 키보드 추적은 window.visualViewport의 'resize'와 'scroll' 이벤트로 처리해요.
Review
토스가 처음 자리 잡게 했지만 카카오뱅크와 뱅크샐러드, 쏘카, 당근까지 같은 모양으로 퍼졌어요. iOS Safari에서는 visualViewport 계산이 빠지면 버튼이 키보드에 가려지고, safe-area-inset-bottom 처리를 안 하면 노치 단말 하단에서 잘립니다. 글자 크기는 14px이 아니라 16~18px이 적당해요. 한국 모바일 사용자는 글자가 작은 CTA를 진지하지 않은 버튼으로 받아들이는 경향이 있습니다.
Full-width Sticky CTA, 토스 버튼, 키보드 위 고정 버튼, Bottom Action Button
화면 하단(키보드가 올라오면 키보드 바로 위)에 고정되는 단일 큰 액션 버튼이에요. 토스와 뱅크샐러드가 자리 잡게 만든 모양입니다.
국내 UI 패턴
1. 토스 앱처럼 화면 하단에 풀와이드 단일 CTA 버튼을 고정해 주세요. 키보드가 올라오면 키보드 바로 위에 붙도록 visualViewport API로 처리합니다.
2. height 56px, font 16px, border-radius 12px의 풀와이드 primary 버튼으로 만들어 주세요. 좌우 padding 16px, safe-area-inset-bottom 처리 포함.
3. 폼 검증이 통과되면 활성, 안 되면 비활성(60% opacity)으로 처리합니다.
bottom-sheet
<Sheet><SheetTrigger>열기</SheetTrigger><SheetContent side="bottom">…</SheetContent></Sheet>
role="dialog" + aria-modal="true"를 적용해요. Esc로 닫고, 포커스 트랩을 걸고, 첫 인터랙티브 요소에 자동 포커스를 주고, 닫힌 후에는 trigger로 포커스를 복귀시켜요.
입문
모바일
모달
옵션 6개 이상을 바텀 시트에 욱여넣으면 스크롤이 길어져 풀 페이지가 더 낫습니다.
모바일 화면 하단에서 슬라이드 업되는 모달 컨테이너예요. 옵션 선택, 폼 입력, 상세 보기처럼 기존 화면 컨텍스트를 유지한 채 보조 동작을 처리할 때 사용해요. iOS 액션 시트의 영향을 받았으며, Material Design에서는 Modal Bottom Sheet와 Standard Bottom Sheet로 구분해요. 한국 모바일 앱(카카오·당근·토스)은 거의 모든 옵션 메뉴를 바텀 시트로 처리해요.
shadcn/ui의 Sheet(side="bottom"), vaul Drawer, Material BottomSheet, Radix Dialog(커스텀 애니메이션)를 활용해요.
Review
카카오·당근·토스 모두 옵션 메뉴는 바텀 시트가 우선이에요. 안드로이드라고 풀스크린 다이얼로그를 쓰지 말고 일관되게 바텀 시트를 쓰면 한국 사용자에게 더 자연스러워요. swipe-down으로 닫는 동작을 빠뜨리지 마세요.
Bottom Sheet, ボトムシート, 액션 시트, Sheet
화면 하단에서 위로 올라오는 모달이에요. 모바일에서 일반 모달보다 친화적이고 전체 화면을 덜 가려요.
오버레이
1. 화면 하단에서 슬라이드 업되는 바텀 시트로 구현해 주세요. 드래그로 닫을 수 있고 배경에 dim 처리합니다.
2. shadcn/ui의 Sheet 컴포넌트를 side="bottom"으로 사용해 만들어 주세요.
3. iOS 액션 시트처럼 옵션 4~5개를 보여주는 바텀 시트로 작성해 주세요. 각 옵션은 좌측 아이콘 + 라벨 + 우측 화살표.