--- date: 2026-06-25 type: design-concept project: 한신대학교 AI 심리상담 시뮬레이션 훈련 플랫폼 tags: - 디자인컨셉 - 디자인시스템 - UX - 아바타 - 한신대 status: 통합본 v1 --- # 한신대 AI 심리상담 시뮬레이션 플랫폼 — 통합 디자인 컨셉 문서 > **이 문서 하나로 사이트를 만들 수 있다.** 디자인 철학 → 디자인 시스템 토큰(실측값) → 가상 내담자 아바타(상세) → 상담 세션 화면 → 3역할 대시보드 → 컴포넌트·모션 → React 구현 노트 → 안티패턴 체크리스트까지, 추상적 형용사 없이 실제 hex·px·컴포넌트·인터랙션으로 적었다. > > 5개 영역 설계([design-language] / [design-system] / [avatar] / [session-ux] / [role-dashboards])를 단일 기준 문서로 종합한 것이다. 토큰 충돌 지점은 본 문서가 **단일 진실원본(SSOT)**으로 일원화했다. > > **재사용 자산 연계**: 윤찬 기존 디자인 시스템(`Resources/50_Tech_Dev/디자인/slide-base.css`)과 동일한 토큰 철학(무채 골격 + accent 교체). slide-base 표의 "한신대/의료" 빈 행을 본 문서의 `--accent`로 채운다. --- ## 0. 한 문장 컨셉과 SSOT 결정 ### 0.1 컨셉 **"불안한 사람을 다루는 사람을 위한, 차분한 임상 도구."** 상담 수련생(학습자)이 가상 내담자 AI와 음성으로 대면하고, 뒤에서 평가 AI가 조용히 관찰한다. 이 화면은 **두 종류의 신뢰**를 동시에 줘야 한다. 1. **임상적 신뢰감** — 학습자가 "이건 진지한 임상 훈련 장비다"라고 느낀다. 2. **교육적 따뜻함** — 연습 중 실수해도 안전하다고 느낀다. 자극적·게임적이면 안 되고, 차갑고 행정적이어도 안 된다. 정답은 그 사이의 **"조용한 따뜻함(quiet warmth)"** — 스펙트럼 위 **65% 임상 : 35% 따뜻함** 지점이다. > 화면은 "상담실"이지 "대시보드"가 아니다. 평가 정보는 상담실 벽에 걸린 은은한 시계처럼 곁에 있되, 내담자의 얼굴에서 시선을 빼앗지 않는다. ### 0.2 토큰 SSOT 결정 (5개 설계 영역의 색 통일) 5개 영역이 각각 미세하게 다른 그린(`#5B7A6B` / `#3E7A6E` / `#4F6B5D` / `#0E7C7B` / `#0F766E`)을 제안했다. 본 문서는 아래로 **확정 일원화**한다. | 용도 | 확정값 | 근거 | |---|---|---| | **메인 accent (전역 기본)** | `--accent: #3E7A6E` (세이지-틸) | 저채도·차분, 흰 배경 대비 약 5.0:1 (WCAG AA 통과). 흔한 메디컬 블루/형광 틸 회피. slide-base의 `#0F766E`보다 한 톤 더 낮춤. | | **역할별 보조 accent** | 학습자 `#3E7A6E` / 교수자 `#3A5BA0` / 관리자 `#5B5F6B` | 같은 골격, accent 한 토큰만 교체. 모두 저채도. | | **보조 온기 accent** | `--clay: #B0735C` (테라코타) | 양산형 메디컬 블루를 깨는 "사람이 만든 교육 도구" 온기. 저채도로만, 화면당 1곳. | 이유: **세이지-틸**은 임상적 차분함(자연·회복·안정)과 따뜻함(흙·식물)을 동시에 품으면서, 한국 상담/심리 분야에서 과용되지 않아 차별화된다. 의료 블루는 흔하고 차갑다. --- ## 1. 핵심 디자인 원칙 (신성불가침 5개) ### 원칙 1 — One Screen, One Intent (한 화면 한 의도) 상담 시뮬레이션은 본질적으로 집중을 요구한다. 학습자가 가상 내담자의 말에 귀 기울이는 순간 화면에 버튼 12개·카드 9개가 깔려 있으면 안 된다. 모든 화면은 그 순간의 **단 하나의 주된 행동**(말하기 / 듣기 / 피드백 읽기 / 세션 시작)을 중심으로 설계한다. 부차 정보는 위계 아래로. - *적용*: 세션 진행 화면 = 중앙에 내담자 발화 + 음성 비주얼 하나. 평가 피드백은 화면 가장자리 차분한 사이드 패널로, 절대 모달로 튀지 않는다. ### 원칙 2 — 위계는 색 블록이 아니라 타이포 weight + 여백으로 Stripe·Linear의 공통점: 테두리·색 박스로 구역을 나누지 않고 **여백과 글자 굵기로** 나눈다. 강조는 ①font-weight(400→600) ②accent 텍스트/언더라인 ③상단 kicker(소제목) ④accent dot 으로만. **border-left 강조선·좌측 사이드바 강조줄·색 박스 카드 격자 절대 금지.** - *적용*: 평가 피드백 "공감 반영: 우수"를 초록 배지로 칠하지 않는다. kicker("공감 반영") + 본문 한 줄 + 미세한 accent dot 한 점으로 위계를 만든다. ### 원칙 3 — 임상적 차분함 = 채도를 죽인 중성 베이스 + 절제된 단일 악센트 Calm/Headspace에서 배울 건 파스텔 그라데이션이 아니라 **고채도·고대비를 의도적으로 피하는 절제**다. 베이스는 따뜻한 회백(off-white)·차분한 슬레이트. 악센트는 **세이지-틸 단 하나**를 화면당 1군데만 배급(Linear의 "accent rationing"). 빨강·주황 경고색은 진짜 오류/위험에만. - *근거*: 불안한 내담자를 다루는 훈련 도구이므로 자극적 색은 학습자 정서·임상 톤 양쪽에 해롭다. 녹색은 우울·불안·스트레스 점수를 가장 많이 낮춘 색이며 "go/긍정" 신호로도 조용히 작동한다. ### 원칙 4 — 따뜻함은 "둥근 카드"가 아니라 "휴머니스트 타이포 + 따뜻한 중성톤 + 넉넉한 여백" 따뜻함을 둥근 모서리·말랑한 일러스트로 내면 임상 신뢰가 깨진다(애들 앱처럼 보임). 대신 **헤드라인에 휴머니스트 세리프 한 톤 옵션**(therapy 웹의 정설: serif=신뢰·인간미), 본문은 깔끔한 산세리프. 순백(#FFF) 대신 따뜻한 종이색, 순흑 대신 잉크 네이비-그린. 이 미세한 온기가 "행정 시스템"과 "사람을 위한 도구"를 가른다. - *주의*: 한글 본문은 Pretendard 단일 패밀리를 기본으로 한다(국문 상담 도메인 가독·중립). 세리프 헤드라인은 랜딩·표지 같은 희소 지점에서만 선택 적용(본명조). UI 전반은 산세리프 weight 위계로 통일. ### 원칙 5 — 평가 AI는 판사가 아니라 조용한 멘토 (Non-judgmental surfacing) 실시간 피드백이 빨간 X·점수 폭격·"틀렸습니다"로 뜨면 학습자는 위축된다. 피드백은 **관찰("이 지점에서 내담자가 방어적으로 반응했어요") → 제안("열린 질문으로 바꿔볼까요?")** 순서의 **서술형·저채도·비모달**로. 점수는 세션이 끝난 뒤 회고 화면에서만, 절대값이 아니라 성장 추세로. --- ## 2. 무드 좌표 (임상 ↔ 따뜻함 균형) 양 극단(차가운 EHR / 말랑한 명상앱) 사이, **65% 임상 : 35% 따뜻함**. | 축 | 임상 신뢰(차가움) ← | **→ 우리 좌표 ←** | → 따뜻함(부드러움) | |---|---|---|---| | 베이스색 | 순백·차가운 그레이 | **따뜻한 off-white `#FBFAF8` + 차분한 슬레이트** | 크림·파스텔 | | 악센트 | 임상 블루 | **세이지-틸 `#3E7A6E`** | 코랄·더스티 로즈 | | 헤드라인 | 기하 산세리프 | **산세리프 weight 위계 (희소 지점만 본명조 옵션)** | 손글씨·라운드 | | 모서리 | 직각 | **6~8px (거의 직각, 미세 라운드)** | 16px+ 둥근 | | 그림자 | 없음(헤어라인) | **1px 헤어라인 + 아주 약한 그림자** | 부드러운 큰 blur | | 모션 | 즉각·없음 | **200~320ms ease-out, 절제** | 느린 호흡 애니메이션 | > 단 하나의 예외 — 음성 듣기 중 마이크/오브의 **느린 호흡 펄스**. 이건 "지금 듣고 있다"는 정서적 안심 신호라 의도적으로 명상앱 호흡 모션을 차용한다. --- ## 3. 디자인 시스템 토큰 (그대로 `:root`에 붙여 쓰는 실측값) CSS 변수는 primitive(원시값) → semantic(의미 별칭) 2계층. 컴포넌트는 semantic만 참조 → 다크모드 1곳에서 스왑. ### 3.1 중성 베이스 (Neutral) — 골격 순백/순흑 금지. 따뜻한 종이색 + 잉크 네이비-그린. ```css :root { /* ── Primitive: Neutral (따뜻한 종이 + 쿨 슬레이트) ── */ --paper: #FBFAF8; /* 본문 배경: 따뜻한 종이색 (순백 아님) */ --paper-2: #F4F2EE; /* 카드/패널 미세 단차 (테두리 대신 톤차) */ --surface: #FFFFFF; /* 카드 표면 (캔버스가 종이색이라 분리됨) */ --neutral-100: #ECEFF1; /* 표면 hover / subtle fill */ --neutral-150: #E5E1DA; /* 카드 보더 baseline = hairline */ --neutral-200: #D5DBDE; /* divider 진함 */ --neutral-400: #98A4A9; /* placeholder / disabled */ --ink: #1C2A2A; /* 본문 텍스트: 잉크 네이비-그린 (순흑 아님) */ --ink-2: #5A6663; /* 보조 텍스트: 슬레이트 */ --ink-3: #93A09C; /* 캡션/메타 */ --hair: #E5E1DA; /* 1px 헤어라인 (구역 분리는 이걸로) */ } ``` ### 3.2 악센트 — 1주 + 1보조 (저채도) ```css :root { /* ── Sage-Teal (주 악센트 = 상담/신뢰), 화면당 1곳 배급 ── */ --accent: #3E7A6E; /* ★ 메인 악센트 (버튼/링크/포커스). 흰 배경 대비 5.0:1 AA */ --accent-deep: #316257; /* hover / 진한 강조 텍스트 */ --accent-tint: #EEF4F2; /* 악센트 배경 틴트 (콜아웃·선택·active) */ --accent-bright:#5F968B; /* dot·진행바 fill (화면 전용) */ /* ── Terracotta (보조 악센트 = 학습/온기), 저채도, 화면당 1곳 ── */ --clay: #B0735C; /* ★ 보조 악센트 (학습자 온기 포인트, 내담자 측 신호) */ --clay-deep: #94604C; --clay-tint: #F6F0EC; } ``` `--accent #3E7A6E`는 흰 배경 명도대비 약 5.0:1 → 본문 링크/버튼 텍스트 WCAG AA 통과. **2색을 동시에 큰 면적으로 쓰지 않는다** — sage가 전역 기본, clay는 학습자 모드/온기 포인트에서만 점·언더라인·소형 fill로. ### 3.3 의미색 (Semantic feedback) — solid + tint 페어 각 의미색은 아이콘/텍스트용 solid + 배경용 tint. tint 위 text는 모두 AA(4.5:1↑). ```css :root { /* 긍정 = 녹색 (상담 진전·라포 형성·가드레일 통과·잘함) */ --pos-text: #2C6E49; --pos-solid: #3B8C5E; --pos-tint: #E8F3EC; /* 주의 = 앰버 (라포 약화·과도한 침묵·페이싱) — 빨강 대신 머스터드 */ --warn-text: #8A5A00; --warn-solid:#C28A2C; --warn-tint: #FAF1E0; /* 위험 = 적색 (가드레일 위반·세션 강제중단·임상적 위급) — 오직 진짜 위급만 */ --crit-text: #A8352C; --crit-solid:#C4453A; --crit-tint: #FAEAE8; /* 정보 = 슬레이트블루 (평가 AI 피드백·시스템 안내, 중립) */ --info-text: #2E5A7A; --info-solid:#3C7299; --info-tint: #EAF1F6; --neutral-sig:#8895A2; /* 데이터 없음·미평가 */ } ``` **핵심 원칙: 평가 AI의 실시간 피드백 기본 톤은 `info`(슬레이트블루) 또는 `pos`(녹색)** 으로 — 학습자가 비난받는 느낌을 받지 않게. 빨강은 코칭 피드백에 절대 쓰지 않고, 가드레일/위급에만. ### 3.4 시맨틱 별칭 (라이트 모드) 컴포넌트는 primitive를 직접 부르지 않고 이 별칭만 참조. ```css :root { --bg-app: var(--paper); /* 앱 캔버스 */ --bg-surface: var(--surface); /* 카드/패널 */ --bg-surface-2: var(--paper-2); /* 중첩 표면/입력 */ --bg-tint: var(--accent-tint); /* 선택·강조 배경 */ --bg-stage: #1E2724; /* 내담자 아바타 영역 (깊은 차콜그린, 시선 집중) */ --text-strong: var(--ink); /* 제목 */ --text-body: var(--ink-2); /* 본문 보조 */ --text-muted: var(--ink-3); /* 캡션 */ --text-on-accent:#FBFAF8; /* accent 버튼 위 텍스트 */ --border-subtle: var(--neutral-150); --border-strong: var(--neutral-200); --border-focus: var(--accent); --focus-ring: color-mix(in srgb, var(--accent) 45%, transparent); } ``` ### 3.5 다크 모드 (gloom 회피 — 충분한 밝은 면 + 절제된 악센트) 배경은 한색 슬레이트(순흑 아님). 악센트는 명도를 올려 가독 확보. 운영 기본 표면은 dark이며, 저장 theme 값이 없거나 API preference가 `system`이면 light로 떨어지지 않고 앱 초기 dark 값을 따른다. dark 화면 안의 대형 카드/섹션은 순백·밝은 베이지 면을 쓰지 않는다. ```css [data-theme="dark"] { --bg-app: #131A1E; --bg-surface: #1A2429; --bg-surface-2: #222E34; --bg-tint: #1E2E2A; --bg-stage: #0E1614; --text-strong: #EAF0F1; --text-body: #C4CFD3; --text-muted: #88969C; --text-on-accent:#0F1A17; --border-subtle: #2A363C; --border-strong: #38454C; --border-focus: #6FB3A4; --accent: #6FB3A4; --accent-deep: #84C2B4; --accent-tint: #1E2E2A; --clay: #CC8F77; --pos-text: #7FD0A0; --pos-solid: #5FB682; --pos-tint: #16271D; --warn-text: #E2B566; --warn-solid:#D4A24A; --warn-tint: #2A2415; --crit-text: #E89086; --crit-solid:#D86459; --crit-tint: #2A1715; --info-text: #79B0D4; --info-solid:#5C97BE; --info-tint: #15222C; } ``` ### 3.6 타이포그래피 | 역할 | 폰트 | 이유 | |---|---|---| | **본문/UI 기본** | **Pretendard Variable** | 한글 UI 사실상 표준. 9 weight 가변, 숫자 정렬 우수. 임상 데이터·평가 점수 표기에 깔끔. | | **숫자·타임코드** | Pretendard + `tabular-nums` | 세션 타이머·점수는 정렬 필요 → `font-variant-numeric: tabular-nums`. | | (옵션) 랜딩 헤드라인 | Noto Serif KR (본명조) | 신뢰·인간미. **희소 지점만**(표지·히어로). UI 전반은 산세리프. | ```css :root { --font-sans: "Pretendard Variable", Pretendard, -apple-system, "Apple SD Gothic Neo", "Noto Sans KR", system-ui, sans-serif; --font-serif:"Noto Serif KR", "Source Serif 4", serif; /* 랜딩 헤드만 */ --font-num: "Pretendard Variable", "JetBrains Mono", ui-monospace, monospace; } body { font-family: var(--font-sans); -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; } .tabular { font-variant-numeric: tabular-nums; letter-spacing: -0.01em; } ``` CDN: `https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/variable/pretendardvariable.min.css` **타입 스케일** (기준 본문 16px / 1.6, 한글 자간 -0.01~-0.02em. 위계는 크기보다 weight로): | 토큰 | px / rem | line-height | weight | letter-spacing | 용도 | |---|---|---|---|---|---| | `--fs-display` | 40 / 2.5rem | 1.2 | 700 | -0.02em | 표지·랜딩 대제목 (희소) | | `--fs-h1` | 30 / 1.875rem | 1.3 | 700 | -0.02em | 페이지 제목 | | `--fs-h2` | 24 / 1.5rem | 1.35 | 600 | -0.015em | 섹션 제목 | | `--fs-h3` | 20 / 1.25rem | 1.4 | 600 | -0.01em | 카드/패널 제목 | | `--fs-lead` | 18 / 1.125rem | 1.6 | 400 | -0.01em | 리드 문장 | | `--fs-body` | 16 / 1rem | 1.6 | 400 | -0.01em | 본문 기본 | | `--fs-sm` | 14 / 0.875rem | 1.55 | 400 | 0 | 보조·폼 라벨 | | `--fs-xs` | 13 / 0.8125rem | 1.5 | 500 | 0.01em | 캡션·메타·배지 | | `--fs-kicker` | 12 / 0.75rem | 1.4 | 600 | 0.08em | 상단 kicker 소제목 | **강조 방식(철칙):** 색 블록·border-left 금지. ① weight 400→600 ② accent 텍스트 컬러 ③ 은은한 `--accent-tint` 배경 틴트 ④ 상단 kicker(작은 도트 동반). kicker가 양산형 좌측바를 대체하는 위계 신호다. ### 3.7 스페이싱 (8pt 기반, 넉넉하게) ```css :root { --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 64px; --sp-9: 96px; } ``` 원칙: **컴포넌트 내부 패딩은 8의 배수, 섹션 간 간격은 48~96px 넉넉히.** 불안 완화의 핵심은 여백. 카드 내부 패딩 기본 `--sp-5`(24px), 패널 `--sp-6`(32px), 읽기 영역 max-width 640~680px. ### 3.8 그리드·형태 ```css :root { --maxw: 1200px; /* 대시보드 콘텐츠 최대폭 */ --maxw-read: 680px; /* 읽기 영역 */ --nav-w: 240px; /* 좌측 네비 펼침 */ --nav-w-collapsed: 72px; /* 좌측 네비 아이콘 only */ --radius: 8px; /* 카드/버튼/입력 (거의 직각) */ --radius-sm: 6px; /* 입력·칩 */ --radius-lg: 12px; /* 패널 */ --gutter: 24px; } ``` - `border-radius`: 카드 8px, 버튼 8px, 입력 6px (거의 직각, 미세 라운드만). **원형은 음성 오브·아바타·dot만 예외.** - **깊이는 1px 헤어라인(`--hair`) + 화면용 아주 약한 그림자.** glow·gradient-text·큰 blur 금지. ```css :root { --shadow-sm: 0 1px 2px rgba(28,42,42,.05); --shadow-md: 0 4px 14px rgba(28,42,42,.07); /* 모달·팝오버만 */ } ``` ### 3.9 모션 ```css :root { --ease-out: cubic-bezier(.22,.61,.36,1); --ease-in-out: cubic-bezier(.45,.05,.55,.95); --dur-fast: 120ms; /* hover·포커스 */ --dur-base: 200ms; /* 버튼·토글·드롭다운 */ --dur-slow: 320ms; /* 패널 전환·모달 */ --dur-breathe: 3200ms; /* 음성 호흡 펄스 */ } @media (prefers-reduced-motion: reduce) { *,*::before,*::after { animation-duration:.01ms!important; transition-duration:.01ms!important; } } ``` 원칙: **차분·짧게·자연 이징.** 불안 도구이므로 튀는 스프링/바운스 금지. 화면 전환은 페이드+미세 슬라이드(translateY 8px 이하). 실시간 피드백 칩은 **자리 미리 확보 후 fade**(밀어내기/점프 금지 — 레이아웃 시프트가 불안 유발). `prefers-reduced-motion` 필수 대응. --- ## 4. 가상 내담자 아바타 (이 플랫폼의 심장) 학습자의 시선이 30~50분간 머무는 단 하나의 오브젝트. 산만하면 라포 연습을 방해하고, 과하면 실제 환자로 오인되며, 빈약하면 몰입이 깨진다. ### 4.1 추천: "반(半)구상 페르소나 카드 (Semi-figurative Persona Card)" 사실적 얼굴도, 추상 오브도 아닌 그 사이. **사실성 35~45%의 양식화 반구상 흉상 + 호흡하는 광배(aura)**. #### 표현 방식 비교 (왜 이걸 골랐나) | 방식 | 몰입 | 제작비용(20주) | 윤리/오인 위험 | 불쾌한 골짜기 | 결론 | |---|---|---|---|---|---| | A. 추상 오브 / 음성 비주얼라이저 | 낮음 | 매우 낮음(1주) | 없음 | 없음 | **폴백으로만**(미선택/로딩/에러) | | **B. 2D 양식화 일러스트(반구상)** | 높음 | 중간(3~6주) | 낮음 | 회피 가능 | **★ 1차 채택** | | C. 실사 / AI생성 얼굴 | 매우 높음 | 높음 | **높음** | **직격(최악)** | 탈락 | | D. Live2D / 3D | 매우 높음 | 매우 높음(6~10주+) | 중간 | 리깅 품질 의존 | 2차/장기 후보 | - **B를 고른 이유**: 사실성을 의도적으로 35~45%에 고정 → 불쾌한 골짜기 회피. 벡터라 페르소나별 파라미터(피부톤·헤어·눈 형태·연령감)를 **데이터로 찍어내 시스템화 가능**(페르소나 무한 확장). 눈·눈썹·입꼬리·어깨선 4채널만으로 저항/라포/정서를 은은히 표현. 윤찬 미니멀 톤과 정합. - **C 탈락**: 80% 사실성에서 미세 립싱크 오차가 더 크게 불쾌. 미성년·취약 내담자를 실사로 그리면 "이 아이 실존?" 윤리 문제 + 데이터 편향. - **A를 폴백으로 흡수**: ElevenLabs Orb 같은 성숙한 React 자산을 페르소나 미선택/로딩/에러 화면에 영구 잔존. #### 카드 구조 ``` ┌─────────────────────────────────────┐ │ [상단 kicker] 교육용 가상 내담자 │ ← 상시 라벨(오인 차단) │ │ │ ╭───────────────╮ │ │ │ 양식화 흉상 │ │ ← 어깨~머리, 평면 벡터 │ │ (얼굴 35% 사실) │ │ 배경에 호흡하는 광배 │ ╰───────────────╯ │ │ │ │ 서연 · 17세 · 고2 │ ← 페르소나 메타 1줄 │ ────────── │ │ 지금: 머뭇거리며 듣는 중 │ ← 상태 텍스트 1줄 └─────────────────────────────────────┘ ``` 핵심: **얼굴은 양식화하되 "사람"으로 읽히고, 절대 "실존 인물"로는 안 읽히게.** 코·주름·모공 없음. 눈·눈썹·입꼬리·어깨 각도가 정서를 나르는 4개 채널. ### 4.2 음성 발화 중 반응 — 상태 모델 (4-state) 음성 UI 표준 idle/listening/thinking/speaking을 내담자 맥락으로 재정의. | 상태 | 트리거 | 입 | 눈/눈썹 | 광배(aura) | 어깨 호흡 | |---|---|---|---|---|---| | **listening (학습자 발화 중)** | 마이크 입력 감지 | 닫힘, 미세 입꼬리 | **학습자 쪽 응시**, 깜빡임 4~6s | 차분 펄스 0.06 opacity, 5.5s | 진폭 1.5px, 4s | | **thinking (응답 생성 대기)** | STT 종료~TTS 시작 | 살짝 다묾 | 시선 살짝 아래/옆, 깜빡임 느려짐 | 안쪽 수축, 채도 -8% | 정지에 가깝게 | | **speaking (내담자 발화)** | TTS 오디오 재생 | **립싱크 진폭** | 정면 복귀, 눈썹 정서 반영 | 발화 진폭에 약동기(0.04~0.12) | 발화 리듬따라 미세 | | **idle (시작 전/일시정지)** | 무입력 | 닫힘 | 천천히 깜빡, 가끔 시선 이동 | 최소 펄스 | 느린 호흡 3.5s | ### 4.3 립싱크 — "정밀 음소 매칭 안 함"이 정답 35% 사실성 얼굴에 정밀 비짐(viseme) 매핑은 오히려 불쾌. **오디오 진폭(RMS) → 입 열림 높이** 1채널 매핑. ```ts // Web Audio AnalyserNode → 매 프레임 RMS → 입 SVG path 세로 스케일 const rms = getRMS(analyser); // 0~1 const mouthOpen = smooth(rms, 0.18); // 지수평활(튐 방지), τ≈180ms // ``` - 입 모양: 닫힘=얇은 호, 열림=타원. `scaleY`만 변형. **이빨·혀 절대 안 그림.** - **데드존**: rms < 0.04는 닫힘 고정 → 무음 구간 입 떨림 제거. - 평활 필수: raw RMS 직결하면 입이 경련함. τ=150~200ms. ### 4.4 모션 진폭 가이드 (산만함 차단의 핵심) | 모션 | 진폭 | 주기/속도 | 비고 | |---|---|---|---| | 어깨 호흡(idle) | translateY 1.5~2.5px | 3.5~5.5s ease-in-out 무한 | "있는 줄 모르게" | | 깜빡임 | scaleY 1→0.08→1 | 120ms, 간격 4~7s 랜덤 | 생체감의 80%가 여기서 | | 광배 펄스 | opacity ±0.04~0.08 | 5~6s | speaking 때만 오디오 동기 | | 시선 이동(saccade) | ±3px | 0.3s, 간격 6~10s | thinking에서 빈도↑ | | 립싱크 | scaleY 0.35~1.0 | 오디오 실시간 | 데드존+평활 | > 원칙: **동시에 움직이는 채널 ≤ 2개.** speaking이면 입+광배만. listening이면 깜빡임+호흡만. ### 4.5 정서 시각화 — 6파라미터 상태머신 valence/arousal 축을 아바타 파라미터로 인코딩. **과장 절대 금지**(만화적 눈물·홍조·땀방울 전부 금지). 상담 도구의 정서는 "감지되되 단언되지 않는" 수준. | 내담자 정서 | 눈/눈썹 | 입꼬리 | 자세(어깨선) | 광배 색조 | 호흡 | |---|---|---|---|---|---| | **중립/라포** | 눈썹 수평, 눈맞춤 | 미세 상향 | 정면, 약간 열림 | 세이지 `#3E7A6E` 4% | 안정 4s | | **우울(저각성·부정)** | 눈꺼풀 1~2px 하강, 시선 아래 | 수평~미세 하향 | 어깨 안으로·아래로 | 청회색 `#5B6B73` 5% | 느리고 얕게 5.5s | | **불안(고각성·부정)** | 깜빡임↑(2~4s), saccade↑ | 다묾, 긴장 | 약간 들썩 | 차가운 청 `#3B6E8F` 5%, 빠른 펄스 | 빠르고 얕게 3s | | **저항/방어** | 시선 회피(옆/아래 고정) | 굳게 닫힘 | 반쯤 돌아선 각도(turn 8°) | 중립 회색, 무펄스 | 정지에 가깝게 | 상태머신이 `(eyelidDrop, gazeAvert, shoulderTurn, breathRate, auraHue, blinkInterval)` 6개 숫자만 바꾸면 정서 전환. **라포가 쌓이면(세션 진행) 저항 페르소나가 shoulderTurn 8°→0°, gazeAvert→눈맞춤으로 서서히 완화** → 학습자가 "내가 라포를 만들었다"를 시각적으로 체감. **이게 이 도구의 교육적 핵심.** 정서 전환은 0.8~1.2s ease로 천천히, 동시 변하는 파라미터 ≤3개. ### 4.6 페르소나 파라미터 시스템 (새 페르소나 = 새 숫자 묶음) ```jsonc // persona/seoyeon.json — 청소년 페르소나 예시 { "id": "seoyeon", "label": "서연 · 17세 · 고2", "appearance": { "ageBand": "teen", // teen | youngAdult | adult | senior "skinTone": "#E8C9A8", "hair": { "style": "long-straight", "color": "#2B2723" }, "faceRatio": { "eyeSize": 1.15, "jawWidth": 0.92 }, // teen=큰 눈·좁은 턱 "outfit": "hoodie", // 실루엣만, 디테일 X "realism": 0.40 // 0.35~0.45 고정(불쾌한 골짜기 회피) }, "voice": { "provider": "openai", "preset": "soft-young-fem", "rate": 0.96 }, "baseAffect": "anxious", // 시작 정서 "resistance": 0.6, // 0~1, 라포로 감소 "auraHueShift": -6 } ``` #### 연령대 외형 규칙 | ageBand | 눈 크기 | 턱/얼굴 | 실루엣 | 광배 채도 | 음성 톤 | |---|---|---|---|---|---| | **teen** | 1.1~1.2× | 좁은 턱·둥근 | 후디·체크 | 약간 높음(생기) | 부드럽고 높은 | | **youngAdult** | 1.0× | 표준 | 캐주얼 | 중간 | 표준 | | **adult** | 0.95× | 각진 턱 | 셔츠·니트 | 낮음(차분) | 낮고 안정 | | **senior** | 0.92× | 부드러운 윤곽 | 단정 | 낮고 따뜻 | 느린 | - **미성년 페르소나는 realism을 0.35로 더 낮추고** `교육용 가상 내담자(미성년 시뮬레이션)` 라벨 강화 → 실존 미성년 오인 원천 차단. - 외형은 인종·특정 외모 stereotype 회피를 위해 **중립 톤 팔레트 3~4종**만 제공(편향 최소화). ### 4.7 아바타 구현 기술 — SVG 1차, Rive 2차 | 기술 | 적합 | 근거 | |---|---|---| | **SVG + CSS/Web Animations + Web Audio** | ★ 1차 | 페르소나=벡터 파라미터, 립싱크=RMS 1채널, 의존성 0, React 친화, 미니멀 정합 | | **Rive (상태머신)** | ★ 2차 고도화 | 상태머신이 .riv에 내장, React는 input만 wiring. Lottie 대비 파일 10~15× 작음 | | Lottie | 보조 | 정형 애니(로딩·전환)엔 OK, 실시간 오디오 반응엔 부적합 | | Live2D/3D | 장기 | 표현력 최고지만 20주·시스템화 불리 | #### 1차안 컴포넌트 구조 (SVG) ``` ├─ // 배경 광배: radial-gradient, opacity 펄스 ├─ // 어깨선, shoulderTurn 각도 ├─ │ ├─ │ ├─ // 정서별 각도 2~3px │ └─ // RMS→scaleY 립싱크 └─ // "교육용 가상 내담자" 상시 ``` ```tsx // 단일 rAF 루프, 의존성 0 function useAvatarMotion(state, affect, analyser) { const [frame, setFrame] = useState(initFrame(affect)); useEffect(() => { let raf, t0 = performance.now(); const loop = (t) => { const dt = (t - t0) / 1000; const breath = Math.sin(dt * (2*Math.PI/affect.breathPeriod)) * affect.breathAmp; const mouth = state==='speaking' ? smoothRMS(analyser) : 0; const blink = blinkSchedule(t, affect.blinkInterval); setFrame({ breath, mouth, blink, gaze: gazeFor(state) }); raf = requestAnimationFrame(loop); }; raf = requestAnimationFrame(loop); return () => cancelAnimationFrame(raf); }, [state, affect, analyser]); return frame; } ``` - **퍼포먼스**: SVG 변형은 `transform`(GPU 합성)만, layout 트리거 속성(width/top) 금지. 단일 rAF, 60fps 여유. - **RN 이전 대비**: SVG는 `react-native-svg`, Web Audio는 `react-native-audio-api`로 대체 → 로직 재사용. Rive도 RN 런타임 있음. - **접근성**: `prefers-reduced-motion` 켜지면 호흡·광배 정지, 상태는 텍스트로만. --- ## 5. 상담 세션 화면 (메인 화면) 핵심 긴장: **몰입 vs 평가 가시성.** 실시간 피드백이 라포 형성을 방해하면 안 되고, 평가가 사후로만 묶이면 학습 효과가 떨어진다. 해법 = **2단(two-tier) 피드백 모델** — 실시간은 "신호(signal)" 수준, 정밀 평가는 "회기말 리뷰"로. ### 5.1 3-column 골격 (데스크탑 ≥1280px) ``` ┌──────────────────────────────────────────────────────────────────────────┐ │ 상단 바 (56px) 좌:세션 제목/시나리오 중앙:회기 단계 우:경과시간·종료 │ ├──────────────────┬──────────────────────────────────┬──────────────────────┤ │ LEFT (320px) │ CENTER (flexible) │ RIGHT (300px) │ │ ───────────── │ ───────────────── │ ───────────── │ │ 회기 단계 트랙커 │ 내담자 아바타 / 음성 STAGE │ 피드백 신호 패널 │ │ (라포→탐색→ │ (어두운 배경, 중앙 집중) │ (은은한, 평시 접힘) │ │ 개입→정리) │ │ │ │ 내담자 컨텍스트 │ [ 아바타 + 음성 오브 ] │ ── 내담자 상태 ── │ │ 카드 │ │ ── 라이브 신호 ── │ │ │ 실시간 자막 (아래→위 흐름) │ (최대 1개, 페이드) │ │ │ │ ── 셀프 노트 ── │ ├──────────────────┴──────────────────────────────────┴──────────────────────┤ │ 하단 컨트롤 바 (80px) 마이크 · 일시정지 · [몰입│은은│코칭] · 회기 종료 확인 │ └──────────────────────────────────────────────────────────────────────────┘ ``` **비율 의도: 시각적 무게 = 주의 배분.** 내담자 stage 60%, 평가 패널 15%(평시 대부분 접힘). 화면은 대시보드가 아니라 상담실이다. **반응형:** - 태블릿(768~1279px): RIGHT 패널이 오버레이 drawer(평시 숨김, 우상단 신호 도트만). LEFT 280px. - 모바일/RN(<768px): 단일 컬럼. stage 상단 고정(40vh), 자막 스크롤, 단계는 상단 가는 진행선, 평가는 하단 시트(bottom sheet). 컨트롤 바 하단 고정. ### 5.2 중앙 STAGE — 음성 오브 (Voice Orb, 4상태) 자극적 막대 파형 대신 **차분한 호흡(breathing) 애니메이션**. 아바타(원형 220px) 둘레 오브 링. | 상태 | 시각 | 색 | 모션 | |---|---|---|---| | **내담자 말하는 중** | 따뜻한 링, 음량 따라 미세 진동 | `--clay` glow, 두께 4px | 음량 연동 amplitude(0.5~3px), 0% 형광 | | **학습자 말하는 중** | 세이지 링, 호흡 펄스 | `--accent` glow | 1.4s ease breathing scale 1.0→1.04 | | **대기/침묵(thinking)** | 가는 점선 매우 느리게 회전 | `--ink-3` | 8s/회전, 거의 안 보일 정도 | | **일시정지** | 반투명 오버레이 + "일시정지" | `--bg-stage` 60% | 정지 | ```css .orb { position: absolute; inset: -14px; border-radius: 50%; box-shadow: 0 0 0 3px var(--ring-color), 0 0 28px 4px color-mix(in srgb, var(--ring-color) 35%, transparent); transition: box-shadow 320ms ease; } .orb[data-state="learner"] { --ring-color: var(--accent); animation: breathe 1.4s ease-in-out infinite; } .orb[data-state="client"] { --ring-color: var(--clay); /* JS가 RMS 음량을 --amp로 주입 */ } @keyframes breathe { 0%,100%{ transform: scale(1) } 50%{ transform: scale(1.035) } } ``` 음량 연동은 JS `onVolumeChange`/RMS → CSS 변수 `--amp`로 box-shadow blur 미세 모듈레이션. **막대 파형(equalizer) 금지** — 너무 "녹음 앱"스럽고 자극적. 호흡하는 빛이 임상적이다. ### 5.3 실시간 자막 (Transcript) — 대본 스타일 음성이 주, 자막은 보조. stage 하단, **아래→위 흐름**. 채팅 버블 양산형 회피, 대본(script) 스타일: ``` ───────────────────────────────────── 내담자 14:32 요즘은 그냥… 아침에 일어나는 게 제일 힘들어요. 나(학습자) 14:33 아침에 일어나는 것조차 버겁게 느껴지시는군요. 그 마음을 조금 더 들려주실 수 있을까요? ───────────────────────────────────── ``` - 화자 라벨 12px 600 메타톤. 내담자 발화는 `--clay-tint` 옅은 배경 틴트 블록, 학습자 발화는 `--accent-tint` 틴트. **버블 외곽선·꼬리·border-left 없음.** 틴트 블록만으로 화자 구분. - 진행 중 발화(partial)는 `--ink-3` 흐릿하게 타이핑되다 확정되면 `--ink` 또렷하게. - 자동 스크롤, 위로 스크롤하면 자동스크롤 해제 + "최신으로" 작은 칩. ### 5.4 회기 단계 표시 (라포 → 탐색 → 개입 → 정리) LEFT 패널 세로 트랙커 + 상단 바 가로 미니. **현재 단계만 또렷, 나머지는 은은히** (단계 압박 방지). ``` 회기 진행 ← kicker ● 라포 형성 09:12 ← 완료: 채워진 sage 도트 + 소요시간 │ ◉ 탐색 진행중 ← 현재: 링+채움, accent, 굵게 │ ○ 개입 ← 예정: 빈 도트, ink-3 │ ○ 정리 ``` - 연결선 1px `--hair`, 완료 구간만 `--accent-bright`. - **자동 추적 기본, 수동 보정 가능.** 백그라운드 평가 AI가 대화로 현재 단계 추정해 `◉` 이동. 학습자가 직접 "다음 단계로" 가능(자각 촉진). 추정과 인식 어긋나면 회기말 리뷰에서 짚음. - **자극 배제:** 단계 전환 시 화면 전환·효과음·축하 애니 없음. 도트가 조용히 채워질 뿐(400ms ease). ### 5.5 실시간 피드백 — "거슬리지 않게" (이 설계의 심장) **3단계 강도 모델**을 학습자가 토글로 고른다 (하단 컨트롤 바 segmented control, 기본=은은한 신호): | 모드 | 실시간 표시 | 대상 | |---|---|---| | **몰입(Immersive)** | 라이브 신호 전혀 없음. 백그라운드 기록만. 모든 피드백 회기말. | 숙련·실전 압박 연습 | | **은은한 신호(Ambient)** ★기본 | 우측 패널에 **앰비언트 도트 1개만**, 색으로 톤만. 텍스트 없음(호버 시) | 일반 연습 | | **코칭(Coached)** | 발화 직후 짧은 텍스트 힌트 페이드인 | 초보·특정 기법 집중 | #### 앰비언트 신호 (기본 모드 핵심) 우측 패널에 **단 하나의 부드러운 도트**가 직전 발화 평가 톤만 색으로 전한다. 점수·숫자·순위 **없음.** ``` 라이브 신호 ← kicker ● 공감적 반영 ← pos 도트 + 한 단어. 6초 후 페이드. 최근 흐름 · · ● · ◦ · ● ← 최근 6개 발화 톤 미니 시퀀스(클릭 가능) ``` - 도트 색: `--pos-solid`(잘함) / `--warn-solid`(살펴볼 점) / `--ink-3`(중립). **빨강·점수 없음.** - 등장: 발화 종료+평가 완료 시 opacity 0→1, 350ms. **6초 후 자동 페이드** — 누적되면 대시보드가 됨. - 거슬림 최소화 3원칙: ①한 번에 1개 ②텍스트 기본 숨김(색만) ③시간 제한 페이드. 진동/소리/번쩍임 전무. 주변시로 "느낄" 수준. #### 코칭 모드 텍스트 힌트 ``` 코칭 힌트 ← kicker 방금 질문은 '예/아니오'로 닫혀 있었어요. 내담자가 더 말하도록 열어보면 어떨까요? 예: "그때 어떤 마음이 드셨는지 들려주세요." [ 알겠어요 ] ← 가볍게 dismiss ``` - `--accent-tint` 배경 틴트 블록(border 없음), 13~14px. 예시 문장 `--ink-2` 이탤릭. 한 번에 1개, 새 힌트 오면 이전 것 교체(stack 안 함). - **위험 신호만 예외적 즉시 표시(모드 무관):** 자해·위기 언급을 학습자가 놓치면 모드 무관하게 우측에 차분한(빨강 아닌 `--warn-solid`) 알림 — "안전 점검: 방금 위기 신호가 있었어요." 교육적으로 놓치면 안 되는 지점이라 항상 노출. #### 내담자 상태 미터 (우측 상단) — "반응 읽기 연습"의 거울 ``` 내담자 상태 감정 톤 불안 ▓▓▓▓░░ ← 가로 게이지, clay 계열, 라벨 텍스트 마음 열림 ▓▓░░░░ ← 라포 진척 대리지표 ``` 게이지는 매우 천천히(2s ease) — 실시간 흔들림이 산만하지 않게. 막대 라운드 없는 사각, 4px 높이. ### 5.6 하단 컨트롤 바 - **마이크 버튼(주 컨트롤):** 56px 원형. 학습자 말하는 중 = `--accent` 채움 + 호흡. 음소거 = 아웃라인. push-to-talk 또는 토글. 상태 텍스트 한 줄("듣고 있어요" / "마이크 꺼짐"). - **일시정지:** 타이머·평가·녹음 모두 정지. 정지 중 stage 차분한 오버레이. - **종료:** 오발동 방지 — 숨은 제스처가 아니라 명시적 **회기 종료** 버튼 + 확인 다이얼로그. 종료가 유일하게 진한 경고색(`--crit-solid`) 허용 영역. - 접근성: 키보드(Space=마이크, P=일시정지), `--focus-ring` 2px. 음성 상태는 색만이 아니라 텍스트 병기(색각 이상 대응). ### 5.7 회기 종료 후 리뷰 화면 (진짜 학습이 일어나는 곳) 실시간에선 일부러 참았던 정밀 평가를 여기서 푼다. 구조: **타임라인 + 기법 라벨 + 슈퍼바이저 코멘트 + 개선점.** 리뷰도 라이브 세션과 같은 dark 작업면을 유지한다. 저장된 발화가 없는 빈 상태에서도 흰 카드로 빠지지 않고, 근거 대기/평가 대기/워크시트 대기를 어두운 패널로 보여준다. ``` ┌──────────────────────────────────────────────────────────────────────────┐ │ 리뷰 · 시나리오:우울 호소 20대/회기1 오디오 다시듣기▷ PDF 내보내기 │ ├──────────────────────────────────────────────────────────────────────────┤ │ 한 줄 요약 (큰 문장 하나, 24px/600 — 에디토리얼 미니멀의 핵심) │ │ "라포는 안정적으로 형성됐지만, 탐색 단계에서 닫힌 질문이 반복돼 │ │ 내담자의 자기개방이 일찍 멈췄습니다." │ ├───────────────────────────────────────┬──────────────────────────────────┤ │ LEFT (62%) 타임라인 + 자막 │ RIGHT (38%) 분석 사이드 │ │ 회기 타임라인(가로 막대) │ ── 기법 사용 분포 ── │ │ 라포▏▏탐색▏▏▏▏▏개입▏▏정리▏ │ 반영적 경청 ▓▓▓▓▓▓ │ │ ▲ 마커(코멘트·기법·하이라이트) │ 개방형 질문 ▓▓ │ │ ─── 스크롤 자막(마커 연동) ─── │ 닫힌 질문 ⚠ ▓▓▓▓▓ (살펴볼 점) │ │ 나 14:33 ◆[감정 반영] … │ ── 잘한 순간 ── │ │ └ 슈퍼바이저: "좋은 반영…" │ ── 다음 회기 개선점(최대 3개) ── │ └───────────────────────────────────────┴──────────────────────────────────┘ ``` - **타임라인(가로):** 4단계 너비 비례 구간으로 색 농담 구분. 마커 = 기법 라벨·살펴볼 순간·잘한 순간·슈퍼바이저 코멘트(이모지 아님, SVG 글리프/도트). 클릭하면 좌측 자막 점프 + 오디오 그 시점으로. - **자막 + 인라인 분석:** 학습자 발화마다 기법 라벨 칩(`[반영적 경청]` `[개방형 질문]` `[닫힌 질문]`), `--accent-tint`/`--warn-tint` 배경, border 없음 11px. 라벨 아래 들여쓰기로 슈퍼바이저 코멘트(AI 자동 + 교수자 직접). AI코멘트 `--ink-2`, 사람 코멘트 `--accent` + "교수자" 태그. - **분석 사이드:** 기법 사용 분포(가로 게이지, 점수 아님 빈도+적절성, 과다·과소만 `--warn` 라벨). 잘한 것 먼저, 개선점 **최대 3개**(실행 가능 구체적, 각 항목 타임라인 마커 연결). 다음 회기 개선점은 "다음에 시도할 한 문장" 형태. - **톤:** 점수·등급·합불 **없음.** 상담 역량은 성장 곡선이지 시험이 아니다. 추세는 별도 "학습자 성장" 화면에서만, 단일 리뷰엔 절대점수 안 보임. PDF 내보내기(슈퍼바이저 제출), 오디오 다시듣기, 클립 공유. --- ## 6. 3역할 대시보드 (학습자 / 교수자 / 관리자) ### 6.1 4가지 설계 결정 (근거) | 결정 | 근거 | |---|---| | **레이더차트 금지, small-multiples 라인 채택** | 레이더는 항목 순서로 모양 왜곡 + 범주선 "증가/감소" 착시. Tufte small-multiples + 공통 축이 정확. 윤찬 "카드덤프 금지"와도 정합. | | **신뢰·차분 = 단일 accent + 넉넉한 여백 + 위계** | 의료 UI: blue=신뢰, green=정상범위, **red는 진짜 임상 긴급에만 예약**. whitespace·typography로 불안 완화. | | **교수자 화면은 "인지"가 아니라 "개입"까지** | 교사용 LA 대시보드 대부분이 awareness만 주고 actionable intervention이 없다는 비판 → 첫 화면을 "개입 필요 학생" 액션 큐로. | | **역할 전환 = 컨텍스트 라벨 + accent 토큰** | 과한 색·뱃지 남용은 양산형. 좌상단 역할 라벨 + accent 한 토큰 교체로 "지금 누구의 화면인지"만 조용히. | ### 6.2 역할별 accent 오버라이드 ```css body[data-role="learner"] { --accent:#3E7A6E; --accent-bright:#5F968B; --accent-tint:#EEF4F2; } /* 세이지-틸: 성장 */ body[data-role="instructor"] { --accent:#3A5BA0; --accent-bright:#5478C4; --accent-tint:#EAEFF7; } /* 인디고-블루: 감독 */ body[data-role="admin"] { --accent:#5B5F6B; --accent-bright:#7D818E; --accent-tint:#EEEFF1; } /* 슬레이트: 운영 */ ``` 세 역할 모두 채도를 낮춰 자극 없게. red(`--crit-solid`)는 어느 역할에서도 장식으로 안 쓴다 — 오직 "지금 개입해야 하는 학생/세션" 단 한 곳. ### 6.3 공통 앱 셸 (Shell) 같은 골격 공유, 좌측 네비 항목 + 좌상단 컨텍스트 라벨만 차이. 역할 전환 시 화면 리셋이 아니라 메뉴·accent만 바뀜. - 톱바 56px `background:var(--surface); border-bottom:1px solid var(--hair);` (그림자 없음). - 좌측 네비 기본 72px 아이콘 전용(Lucide stroke 1.75), hover/포커스 시 220px 슬라이드(160ms). 활성 = 아이콘 `color:var(--accent)` + **3px 강조바 금지** → 아이콘 배경 `var(--accent-tint)` `border-radius:8px` 알약형(네비 알약만 8px 관용). - **역할 컨텍스트 라벨**: 톱바 좌측, `font-family:var(--font-num); font-size:13px; letter-spacing:0.04em; color:var(--accent);` — "학습 대시보드"/"교수 콘솔"/"운영 콘솔". "지금 누구로 보고 있나"의 유일하고 조용한 신호. - **권한 표현 원칙: 권한 없으면 DOM에서 제거.** disabled 회색처리도 안 함 — 존재를 숨김. 화면이 깨끗해지고 "내가 못 하는 것" 노이즈 제거. 임상 데이터 프라이버시(관리자는 학생 개별 성장곡선·transcript 기본 접근 불가, 메뉴 자체 렌더 안 함). ### 6.4 학습자 대시보드 (홈 = "당신은 나아지고 있다") ``` COUNSELING PRACTICE · 4주차 ← kicker 수련생님, 지금까지 12회 연습했어요. ← h1 28px/700 최근 라포 형성이 눈에 띄게 안정됐습니다. ← lead, accent .em ┌─────────────────────────────┐ ┌───────────────────────────┐ ← cols 7:5 비대칭 │ 종합 상담 역량 │ │ 다음 연습 │ │ ╭───────╮ │ │ ▸ 불안 호소 내담자·중급 │ │ │ 72 │ +6 ▲ │ │ 이전 약점: 침묵 다루기 │ │ ╰───────╯ 지난주 대비 │ │ [ 연습 시작 → ] │ │ (단일 게이지, 둘레 progress)│ │ 추천 이유: 라포는 올랐지만 │ └─────────────────────────────┘ │ 위기개입 정확도 정체 │ └───────────────────────────┘ 역량별 추이 (최근 8회) ← kicker ┌────────┐┌────────┐┌────────┐┌────────┐ ← small multiples 4열, 공통 y축 0-100 │라포형성 ││기법사용 ││개입정확도││공감반영 │ │ ╱╲__╱ ││ __╱╲╱ ││ ╲__╱╲ ││ __╱╱ │ │ 82 ▲ ││ 68 → ││ 61 ▼ ││ 75 ▲ │ └────────┘└────────┘└────────┘└────────┘ 최근 피드백 모아보기 (테이블, 카드 아님) ← kicker 6/24 불안 내담자 │ "침묵 견디지 못하고 성급히 해결책 제시…" — 개입 ▸ 6/22 분노 내담자 │ "반영 정확. 다만 감정 명명이 늦음." — 공감 ▸ ``` - **종합 게이지(radar 대체):** 단일 원형. SVG `` 트랙 `stroke:var(--neutral-150)`, fill arc `stroke:var(--accent); stroke-linecap:round`. 중앙 숫자 48px/700. 변화량 `+6 ▲` = `--pos-solid`, 하락이면 `--warn-solid`(앰버, 빨강 아님). 아래 한 문장 자연어 요약. - **역량별 추이 = small multiples:** 4개 동일 크기 미니 라인. **공통 y축 0-100**(첫 카드만 0/50/100 옅게 표기). 라인 `stroke:var(--ink-2); stroke-width:1.5`. 마지막 점만 `r:3; fill:var(--accent)`. 카드 `background:var(--surface); border:1px solid var(--hair); border-radius:8px; padding:16px`. **상단 강조선·좌측바 금지.** 우하단 최신값+추세 화살표(▲`--pos`, →`--ink-3`, ▼`--warn`). 호버 툴팁, 클릭 시 세션 상세. - **피드백 모아보기 = 테이블, 카드 아님:** 날짜/내담자유형/인용 코멘트/태그/`▸`. zebra 없이 행 `border-bottom:1px solid` `--paper-2`. 행 hover `background:var(--accent-tint)`. 카드 격자로 흩뿌리지 않고 시간순 한 컬럼 위계. ### 6.5 교수자 콘솔 (홈 = "누구를 먼저 도와야 하는가") 핵심: **awareness가 아니라 triage.** 첫 화면 최상단이 "개입 필요 학생" 액션 큐. ``` 상담실습 II · 28명 담당 ← kicker 이번 주, 3명에게 개입이 필요합니다. ← h1, accent .em ┌──────────────────────────────────────────────────────────────┐ ← 개입 큐(최우선, 풀폭) │ ● 박서연 개입정확도 3주 연속 하락(61→52→44) 미연습 5일 ▸ │ 좌측 dot = --crit-solid │ ● 이도현 위기상담 시뮬 중도이탈 2회 마지막 6/20 ▸ │ │ ● 최민준 라포 점수 급락, 피드백 미열람 7건 ▸ │ └──────────────────────────────────────────────────────────────┘ 학생별 현황 [정렬: 위험도▾] [영역: 전체▾] 학생 세션 라포 기법 개입 공감 추세 마지막 상태 김수련 12 82↗ 68→ 61↘ 75↗ ▁▃▅▆▇ 6/24 양호 박서연 8 55↘ 49↘ 44↘ 58↘ ▇▅▃▂▁ 6/19 ● 개입 이도현 14 88↗ 79↗ 71→ 84↗ ▅▆▆▇▇ 6/23 양호 ``` - **개입 큐(triage):** 풀폭 패널. 각 행 좌측 dot `8px; background:var(--crit-solid)`(red의 유일한 정당한 사용처). 사유 = 수치 변화 + 행동 신호(미연습일·미열람·중도이탈)를 자연어로. 알고리즘이 뽑되 교수가 최종 판단하도록 "왜"를 노출. - **반 평균 역량 분포 = 가로 막대**(레이더 아님): fill `var(--accent)`, 트랙 `var(--paper-2)`. **최저 항목만** 우측 `← 취약` `color:var(--warn-solid)`. 나머지 차분한 무채. - **학생별 테이블:** sparkline(▁▃▅▆▇, 셀 안 7px SVG, 최근 8회 종합점수). 역량 셀 `82↗`(화살표 색만 시맨틱). 정렬 기본 위험도 순. 상태 "양호"는 `--ink-3` 무채, "● 개입"만 `--crit-solid` dot. red를 한 곳에 집중. 행 클릭 → 학생 드릴다운(학습자 화면 + 세션별 utterance-level 평가 타임라인). ### 6.6 관리자 콘솔 (홈 = "시스템이 건강한가 · 누가 무엇을 했는가") 핵심: **운영 신뢰성 + 감사.** 학습 내용엔 개입 안 하고 시스템·사용·교수활동 감사에 집중. 무채 슬레이트로 가장 차분. - **운영 KPI 5:** 사용자/강좌/세션주/가동%/API오류. mono 32px. 오류 0 초과 시 해당 KPI만 `--warn-solid`, 임계 초과 `--crit-solid`. 정상 땐 전부 무채 — "조용한 게 정상". - **세션 추이 = 단일 라인차트**(막대 아님). 피크 1점만 라벨. - **음성·AI 사용 + 비용:** OpenAI 음성 분/평가 토큰/추정 비용/예산 대비 게이지(80% 넘으면 `--warn-solid`). 운영비 가시화(윤찬 graphify 비용 통제 문화). - **교수 활동 감사 테이블:** 교수별 피드백 열람률(품질 신호)·시나리오 발행·마지막 활동. 저조 지표만 `← 저조` `--warn`. "⚠ 점검" = 관리자 리마인드 대상. - **Audit log:** 시간순 한 컬럼. 행위자·행위·대상. 색 없음(감사는 중립). 클릭 시 상세. ### 6.7 데이터 시각화 하드룰 (전 역할 공통) | 상황 | 채택 | 금지 | |---|---|---| | 한 사람 여러 역량 **추이** | small multiples 라인(공통 y축) | 레이더/스파이더 | | 여러 역량 **현재값 비교** | 가로 막대(공통 0-100), 최저값만 강조 | 레이더, 전항목 색칠 | | 한 지표 **시간 추이** | 단일 라인(피크/최신 1점만 라벨) | 3D·면적 그라데이션 | | 테이블 안 **추세 압축** | sparkline(7px) + 화살표 | 셀마다 미니 도넛 | | **종합 한 숫자** | 단일 원형 게이지 + 변화량 | 여러 도넛 격자 | **차트 공통 스타일:** 격자선 최소(y축 0/50/100 옅은 점선만, 세로 격자 없음). 라인 `stroke-width:1.5`, 점은 최신/이상치만. 색은 데이터 의미에만(추세 ↗`--pos`/↘`--warn`/→무채, 평상시 라인 `--ink-2` 무채 → 색 인플레이션 방지). 진입 라인 draw 280ms, 게이지 arc 400ms ease-out 1회만. 깜빡임·펄스·glow 없음. 모든 수치 `tabular-nums`. --- ## 7. 컴포넌트 원칙 (요약 카탈로그) 공통: **border-radius 절제(6~12px, 알약 금지), 그림자 절제(헤어라인 우선), border는 `--hair`.** 카드는 쓰되 균등 4-4-4 격자 남용 금지 → 비대칭·표·도식. ### 7.1 버튼 (위계 3단, 알약형 금지 radius 8px) | 종류 | 배경 | 텍스트 | 보더 | 용도 | |---|---|---|---|---| | Primary | `--accent` | `--text-on-accent` | none | 세션 시작·저장 (화면당 1~2개) | | Secondary | `--bg-surface` | `--accent` | 1px `--accent` | 보조 액션 | | Ghost | transparent | `--text-body` | none | 취소·텍스트 액션 | | Danger | `--crit-solid` | #fff | none | 세션 강제 종료 등 위급만 | ```css .btn { font: 600 var(--fs-sm)/1 var(--font-sans); padding: 10px 18px; border-radius: var(--radius); border:1px solid transparent; transition: background .16s ease, transform .12s ease; cursor:pointer; } .btn-primary { background: var(--accent); color: var(--text-on-accent); } .btn-primary:hover { background: var(--accent-deep); } .btn:active { transform: translateY(.5px); } /* 미세 누름, 과한 모션 X */ .btn:focus-visible { outline:none; box-shadow: 0 0 0 3px var(--focus-ring); } ``` ### 7.2 입력 (radius 6px, 포커스 시 보더색 + 얇은 ring, 좌측바 X) ```css .input { width:100%; background: var(--bg-surface-2); color: var(--text-body); border:1px solid var(--border-strong); border-radius: var(--radius-sm); padding:10px 12px; font:var(--fs-body)/1.5 var(--font-sans); transition:border-color .15s, box-shadow .15s; } .input:focus { border-color: var(--border-focus); outline:none; box-shadow: 0 0 0 3px var(--focus-ring); } .input[aria-invalid="true"] { border-color: var(--crit-solid); } ``` ### 7.3 카드 / 패널 (헤어라인 + 미세 그림자, 상단 강조선 금지) ```css .card { background: var(--bg-surface); border:1px solid var(--border-subtle); border-radius: var(--radius); padding: var(--sp-5); box-shadow: var(--shadow-sm); } .panel { background: var(--bg-surface); border:1px solid var(--border-subtle); border-radius: var(--radius-lg); padding: var(--sp-6); } .panel-tint { background: var(--bg-tint); border-color: transparent; } /* 강조 패널: 틴트 배경 */ ``` ### 7.4 평가 피드백 콜아웃 (이 플랫폼 핵심, 좌측 컬러바 금지 → 배경 틴트 + 도트 + kicker) ```css .callout { border-radius: var(--radius); padding: var(--sp-4) var(--sp-5); font-size: var(--fs-sm); line-height:1.6; } .callout .clh { font: 600 var(--fs-xs)/1.4; letter-spacing:.04em; display:flex; align-items:center; gap:8px; margin-bottom:6px; } .callout .clh::before { content:""; width:7px; height:7px; border-radius:50%; } /* dot, 바 아님 */ .callout.pos { background:var(--pos-tint); color:var(--pos-text); } .callout.pos .clh::before { background:var(--pos-solid); } .callout.info { background:var(--info-tint); color:var(--info-text); } .callout.info .clh::before { background:var(--info-solid); } .callout.warn { background:var(--warn-tint); color:var(--warn-text); } .callout.warn .clh::before { background:var(--warn-solid); } .callout.crit { background:var(--crit-tint); color:var(--crit-text); } /* 가드레일 위반만 */ .callout.crit .clh::before { background:var(--crit-solid); } ``` 피드백 강도 매핑: **잘함→`pos`(녹색), 일반 코칭/관찰→`info`(슬레이트), 페이싱·라포 주의→`warn`(앰버), 가드레일/위급→`crit`(적색).** 학습자 화면 기본 피드백은 info/pos가 다수가 되도록. ### 7.5 라이브 음성 오브 (§5.2 참조) 내담자 발화=sage, 학습자 발화=clay로 화자 구분(채도 낮게). 깜빡이는 빨강 금지 → 호흡 펄스. --- ## 8. React 구현 노트 ### 8.1 컴포넌트 트리 ``` // body[data-role] 설정, 네비·톱바 // 권한 없는 항목 필터링 후 렌더(DOM 제거) // mono accent 라벨 Hero1줄 + cols(Gauge, NextPractice) + MiniMultiples×4 + FeedbackTable ├ InterventionQueue + KPIs + ClassDistribution + StudentTable(sparkline) └ OpsKPIs + SessionTrend + UsageCost + FacultyAudit + AuditLog // CSS grid 320/1fr/300, RN은 Flex │ ├ // LEFT │ ├ // CENTER │ │ ├ // §4 아바타 │ │ ├ │ │ └ │ └ // RIGHT │ ├ │ ├ // 은은한 신호 │ ├ // 코칭 모드만 │ └ // 큰 한 문장 ├ ``` ### 8.2 상태 관리 - 음성 이벤트(`onModeChange`/`onVolumeChange`/partial transcript)는 OpenAI Realtime 스트림 → 단일 `SessionStore`(Zustand 권장)로 모아 `VoiceOrb`·`LiveTranscript`·`FeedbackRail`·`ClientAvatar` 구독. - 평가 AI는 **별도 채널(백그라운드)**로 발화 단위 신호를 throttle해서 push. `FeedbackRail`이 6초 페이드 큐로 소비. 평가와 음성 상태 표시는 분리(평가가 음성 UX를 막지 않게). - 차트 프리미티브 4종 먼저 구축: `` `` `` `` (§6.7 룰 내장, props=data+role토큰). 학습자 컴포넌트가 교수 드릴다운에서 재사용됨. ### 8.3 구현 순서 1. `tokens.css`(§3) + 앱 셸(§6.3) — 셸은 3역할 공통이므로 1회만. 2. 차트 프리미티브 4종. 3. 학습자 홈 → 교수 홈 → 관리자 홈 순(재사용 극대화). 4. 세션 화면: 폴백 추상 오브 먼저(음성 파이프라인·4-state 배선 검증) → 반구상 SVG 아바타 → 정서 6파라미터 → 페르소나 JSON. 5. 리뷰 화면 + utterance-level 평가 타임라인. 6. 역할 가드: 라우터에서 `role` 기반 메뉴·라우트 **렌더 제외**(disable 아님). 7. (여유) Rive 아바타 고도화. 안 되면 SVG 1차안으로 출시(폴백 안전). ### 8.4 RN 전환 대비 레이아웃 토큰은 절대 px 의존 최소화(rem·flex 우선). SVG→`react-native-svg`, Web Audio→`react-native-audio-api`, Rive→RN 런타임. 브레이크포인트 sm:640/md:768/lg:1024/xl:1280. --- ## 9. 안티패턴 체크리스트 (HARD NO — 출시 전 자가검증) 1. **border-left 강조선 / 좌측 사이드바 형태 강조줄** — 피드백·콜아웃·카드·활성표시에 색 막대 금지. 위계는 kicker+weight+여백+dot+배경틴트. 2. **이모지** — UI·일러스트·SVG 내부 장식까지 전부 금지. 아이콘은 Lucide(stroke 2, 직각). 평가 결과에 체크/엑스 이모지 금지. 추세는 ▲▼→ 기하 화살표. 3. **카드 덤프 / 균등 그리드 무한반복(4-4-4)** — 비대칭(7:5) 레이아웃·데이터 테이블·도식으로. 학습자 홈은 게이지+small-multiples+테이블, 교수 홈은 triage 큐+KPI+테이블. 4. **양산형 SaaS 랜딩** — "그라데이션 히어로 + 떠다니는 mockup + 3컬럼 feature 카드 + 보라 그라데이션 CTA" 공식 금지. 5. **과한 그라데이션·glow·gradient-text·큰 blur** — 깊이는 1px 헤어라인. 6. **둥근 모서리 과다(16px+)·말랑 일러스트** — 거의 직각(6~12px). 원형은 오브·아바타·dot만. 7. **게임화 폭격** — 점수 팡파레, 빨간 X, 레벨업 배지, 별점 5개 즉시. 평가는 서술형·비모달·회고 시점. 8. **순흑(#000)·순백(#FFF)** — `--ink:#1C2A2A` / `--paper:#FBFAF8`. 9. **고채도 메디컬 블루 단독 의존** — 세이지-틸로 차별화 + 온기. 10. **실시간 피드백 모달** — 상담 흐름 중 팝업 금지. 차분한 사이드 패널로. 11. **레이더/스파이더 차트** — small-multiples 라인 + 단일 게이지로. 12. **권한 없는 메뉴 disabled 회색 나열** — DOM에서 제거. 13. **빨강 장식 사용** — `--crit-solid`는 (교수)개입 큐·"개입" 상태·(관리자)임계 오류에만. 14. **One screen, one message 위반** — 각 화면 최상단 h1이 그 화면 단일 메시지("나아지고 있다"/"3명 개입 필요"/"정상 운영"/"듣고 있어요"). --- ## 10. 핵심 의사결정 5줄 (윤찬이 기억할 것) 1. **컬러: 세이지-틸 `#3E7A6E` 단일 accent + 테라코타 `#B0735C` 온기 보조.** 의료 블루 회피, 저채도로 임상+따뜻함 동시. 역할별 accent 한 토큰만 교체. 2. **아바타: 사실적 얼굴(오인·불쾌한 골짜기·편향)도 추상 오브(비언어 연습 불가)도 답 아님 → 사실성 35~45% 양식화 반구상 흉상 + 호흡 광배.** 눈·눈썹·입·어깨 4채널 × 정서 6파라미터. SVG 1차 → Rive 2차. 미성년은 realism 더 낮추고 `교육용 가상 내담자` 라벨 상시. 3. **세션 화면: 실시간 피드백은 "신호(색 도트, 6초 페이드, 1개)"까지만, 정밀 평가는 회기말 리뷰로.** 학습자가 모드(몰입/은은/코칭)를 고른다. 음성 상태는 막대 파형이 아니라 빛의 호흡(sage=학습자, clay=내담자). 4. **시각적 무게 = 주의 배분.** 내담자 stage 60%, 평가 패널 15%·평시 접힘. 화면은 대시보드가 아니라 상담실. 5. **대시보드: 레이더 금지 → small-multiples 라인 + 단일 게이지.** 점수·합불 없음(성장 추세만). 교수자 첫 화면은 triage 큐. 권한 없으면 DOM 제거. --- ## 참고 (실조사 출처) **디자인 언어·시스템** - Linear Brand Guidelines / Stripe Design / Calm·Headspace 색 (accent rationing, 저채도, 호흡 모션) - Fuzzy Math — Color Palettes of Mental Healthcare UI / Villa Healing Center — Calming Colors - Halo Lab — Digital Healthcare UX Trends 2026 / Progress — Color Psychology in Healthcare - Pretendard (orioncactus/pretendard) / az-loc — Best Fonts for CJK Websites 2026 - Aufait UX — Accessible Color Tokens / WCAG Contrast Guide 2026 **아바타** - Behavioral Sciences 2026 — Human-like/Animal-like/Object-like LLM Virtual Doctor Avatar - arXiv 2506.11890 — Graduated Realism (AI Avatars in VR Teacher Training) - PMC12692777 — AI Physician Avatars (윤리·편향·오인 위험) - NYU Steinhardt — AI Avatar for Social Work Counseling Practice - ElevenLabs UI Orb / orb-ui (4-state) / Rive vs Lottie 2026 / arXiv 2502.16038 (Valence-Arousal-Dominance) **세션·대시보드** - arXiv 2502.18673 — Scaffolding Empathy (utterance-level 피드백 학습효과 vs 인지부하) ← 2단 피드백 모델 근거 - arXiv 2505.02428 — LLM-Simulated Practice/Feedback for Counselors - assistant-ui / Vocalis / Agora (음성 상태 시각화) - Scott Logic·Observable — Radar Charts 비판 (small-multiples 대체 근거) - GoReact — Counseling Skills Rubric / Springer Open — LA Dashboard actionable intervention 비판 - Eleken·Fuselab — Healthcare UI/UX 2026 **vault 연계 자산** - `Resources/50_Tech_Dev/디자인/slide-base.css` (무채 골격·강조 위계 계승) - `Resources/50_Tech_Dev/디자인/[디자인 시스템] HTML 슬라이드·보고서 베이스 (2026-06).md` (한신대/의료 행에 `--accent:#3E7A6E` 채움)