세션 화면 디자인 개편

This commit is contained in:
Yun Chan 2026-07-31 09:19:42 +09:00
parent 04a5f485ed
commit 12bdc59497
13 changed files with 1430 additions and 386 deletions

View file

@ -606,6 +606,8 @@
<p class="dg-note">153차 적용(2026-07-30): 관리자 AI 엔진 설정을 자유 문자열 입력에서 게이트웨이 capability 기반 선택으로 바꿨다. <code>claude_cli</code>·<code>claude_api</code>·<code>codex_cli</code>·<code>agy_cli</code>·<code>openai</code>·<code>solar</code> 6종을 같은 계약으로 관리하고, <code>GET /v1/capabilities</code><code>GET /admin/engine-capabilities</code>가 설치·인증 상태, 사용 가능 모델, 모델별 추론 강도, 기본값을 전달한다. 관리자 콘솔과 설정은 provider·모델·추론 강도를 드롭다운으로 제한하며 Codex 기본은 <code>gpt-5.6-terra / medium</code>, Agy 기본은 <code>gemini-3.6-flash-high / high</code>다. 연결 주소를 바꾸면 이전 주소의 catalog를 즉시 폐기하고, 편집 중인 새 URL을 <code>engine_url</code> query로 넘겨 목록을 다시 확인하기 전에는 저장할 수 없다. PATCH도 제안된 게이트웨이의 capability를 다시 확인해 연결 불가·존재하지 않는 모델·지원하지 않는 강도를 422로 막고 durable 설정을 보존한다. 기존에는 <code>engine_mode</code>가 사실상 설정 표시값에 가까웠으나 이제 generate/stream/readiness가 실제 provider 실행기로 라우팅되며 <code>reasoning_effort</code>도 DB·API·게이트웨이까지 영속된다. Codex app-server <code>model/list</code>에서 실제 모델을 조회하고 Terra/Medium 생성 응답 <code>OK</code>, Agy <code>models</code>에서 실제 모델을 조회하고 Gemini 3.6 Flash/High 생성 응답 <code>VIGNETTE_ISOLATED_OK</code>를 격리 작업 디렉터리에서 실측했다. Anthropic API는 현재 키가 없어 의도대로 선택 불가이며 live 동일성은 B2에 남긴다. 임시 신규 게이트웨이/API 연결에서는 Codex·Agy 선택 저장과 Claude CLI 원복을 확인했고, backend 전체 <code>466 passed</code>, web build, 관리자 AI desktop/mobile <code>1+1</code>, admin AI 전수 <code>7</code>, settings 레이아웃·모바일 <code>3</code>이 통과했다. 현재 로컬 9099도 신규 게이트웨이 코드로 구동되어 Codex <code>7개 / Terra / medium</code>, Agy <code>11개 / Gemini 3.6 Flash High / high</code> live catalog를 반환하고, 로컬 API의 관리자 capability 라우트는 미인증 요청을 401로 차단한다. 121차의 “고장 URL을 저장해 실패 row 생성” E2E는 새 fail-closed 계약과 충돌해 저장 거부·기존 설정 보존 증거로 대체했고, 평가 재시도 UI 회귀는 route fixture spec이 계속 소유한다. <b>현재 상태</b>: 로컬 작업트리·게이트웨이 검증 완료, 공개 웹 배포 전이다.</p>
<p class="dg-note">154차 적용(2026-07-30): 153차 AI 엔진 capability 변경을 공개 런타임까지 반영했다. API PID <code>11788</code>과 gateway PID <code>44488</code>를 신규 코드로 교체하고 Cloudflare Pages production <code>2f52f8e3-f992-441f-ada4-f6396cbb1a2b</code>를 게시했다. 최종 custom domain은 <code>index-B54GMBSf.js</code><code>AdminAi-ClAJvCvx.js</code><code>application/javascript</code> 200으로 제공한다. 최초 production <code>a3819e6e</code> 검증에서 5세대 전 엔트리 <code>index-Cwtfyfq5.js</code>의 보존 누락을 발견했기 때문에, <code>scripts/preserve-pages-assets.ps1</code>가 최근 production preview의 HTML·JS·CSS 의존 그래프를 재귀적으로 수집하고 MIME을 검사해 현재 dist에 병합하도록 고친 뒤 최종 배포로 교체했다. custom domain에서 현재와 이전 4세대 엔트리가 모두 JavaScript 200이며, lazy 청크 첫 요청 강제 실패도 문서 재로드 1회 뒤 1/1 복구됐다. 실제 Google 슈퍼 관리자 사용자 행에 연결한 15분 임시 세션으로 공개 <code>/admin/ai</code>에서 공급자 6개, Codex <code>7개 / gpt-5.6-terra / medium</code>, Agy <code>11개 / gemini-3.6-flash-high / high</code>를 확인했고 저장 없이 세션을 즉시 삭제했다. 공개 health는 <code>status=ok</code>, <code>environment=prod</code>, <code>db=true</code>, <code>engine=true</code>이며 auth config 200, 비인증 personas·admin capability 401, watchdog <code>LastTaskResult=0</code>다. 153차의 공개 배포 전 상태 표기는 이 증거로 대체한다.</p>
<p class="dg-note">155차 적용(2026-07-31): 세션 텍스트 입력이 AI 생성·TTS 종료까지 잠기고 답변 표시 뒤에도 evaluator가 끝날 때까지 다음 전송이 약 24초 묶이던 학습 UX를 실제 브라우저와 gateway/API 계량으로 분해했다. textarea는 회기 종료·일시정지에서만 잠기며 생성·음성 준비·재생 중에는 다음 질문 초안을 계속 작성·보존하고, 내담자 응답 저장 직후에는 음성 재생과 무관하게 다음 전송을 허용한다. 관리자 DB의 Agy/Gemini 설정은 evaluator/review에 유지하되 실시간 client 역할은 <code>VIGNETTE_LIVE_CLIENT_PROVIDER=claude_cli</code> 전용 lane으로 분리해 회기별 <code>claude -p</code> 프로세스를 재사용한다. stream learner/client turn과 결정론 상태를 먼저 durable 저장하고 SSE <code>done</code>을 보낸 뒤 fast-loop 평가는 백그라운드에서 같은 learner turn normalized row에 붙이며 평가 기반 코칭 충전도 한 번만 적용한다. Agy fallback도 <code>stream-json</code> delta를 실제 SSE token으로 전달한다. 로컬 음성은 <code>-UseHiggsVoice</code>로 설치된 <code>higgs-audio-v3-tts-4b</code>를 loopback 상주시켜 P1 실제 답변을 24kHz WAV로 합성하며 저장소의 무참조 synthetic seed만 reference로 허용하고 non-dev는 fail-closed한다. <b>실증</b>: 변경 전 full API 완료 24.17/24.57초, 변경 후 실제 UI 첫 응답·전송 해제 9.58초와 상주 연속 턴 6.96초, 생성 중 초안 enabled/보존, TTS 중 보내기 enabled. Higgs full API 362,924 bytes/7.56초 WAV, provider/model 헤더 확인. backend 432/432, gateway 44/44, typecheck·API types·build, session stream 1/1, voice skip/draft 1/1 통과. <b>배포·공개 실증</b>: commit <code>c7883434</code>를 origin/master에 push하고 Cloudflare Pages production <code>505155c4-01f3-4eac-ad59-c2d323a0bb51</code>로 게시했다. custom/preview 모두 <code>index-BU5JjRnR.js</code>를 JavaScript 200으로 서빙하고 최근 5개 배포의 의존 자산을 보존한다. 공개 API는 prod·db=true·engine=true, auth config Google configured/dev login disabled, 비인증 personas·voice speech 401, Google 시작 302다. 실제 공개 로그인 1280×720 렌더에서 버튼 2개 enabled, 브라우저 warning/error 0을 확인했다. 공개 음성은 라이선스 경계상 <code>openai / gpt-4o-mini-tts</code>를 유지한다.</p>
<p class="dg-note">156차 적용(2026-07-31): 활성 상담 세션이 전역 테마와 무관하게 <code>.sx-page--active</code>에서 다크 팔레트를 다시 정의해, 라이트 설정에서도 화면은 다크인데 공통 <code>Surface inset</code>인 AI 코치만 밝게 뜨는 혼합 테마 결함을 수정했다. 세션 전용 <code>--bg-surface</code>/<code>--text-*</code>/<code>--accent</code> 덮어쓰기를 제거하고 전역 <code>data-theme</code>·<code>tokens.css</code>·공통 <code>Surface</code>를 그대로 사용해 라이트는 종이·글래스, 다크는 차콜·세이지로 함께 전환한다. 우측 레일은 <b>AI 코치 → 현재 신호 → 음성·AI 응답 상태</b> 순으로 재배치하고 의미 없는 equalizer 막대와 중복 화면 모드 행을 제거했다. 700px 이하 높이에서는 발화 예시·보조 흐름을 먼저 접고 코치 판단과 근거/이력 액션을 보존하며, 기존 사각지대였던 881~1180px에서도 코칭 모드에 AI 코치 카드만 전폭으로 남긴다. <b>검증</b>: <code>npm run check:design-ssot</code>, typecheck, build 통과, 세션 전수 desktop/mobile <code>30 passed / 2 skipped</code>, session-layout <code>8/8</code>, 7폭 layout-visual-gate <code>15/15</code>, 라이트 1366×640 코치·1024×640 압축 코치 및 다크 1440/1024/390 캡처 직접 확인. 로컬 작업트리 기준이며 배포 전이다.</p>
<p class="dg-note">157차 적용(2026-07-31): 사용자 제공 1536×1024 활성 세션 시안을 실제 세션 화면에 반영했다. 데스크톱을 <b>좌측 단일 컨텍스트 레일 326px / 중앙 인물 무대+자막 / 우측 단일 라이브 코칭 레일 357px / 하단 116px 제어바</b>로 재구성하고, 상태 신호 모드에서도 AI 코치의 개입 시점·남은 기회·코칭 포인트를 미리 이해할 수 있게 했다. 제공된 수채화 잎 5장은 BEN2로 투명 분리한 뒤 600×800 이하 알파 WebP로 최적화해 합계 약 158KB로 줄였고, 좌우 캔버스와 아바타 뒤에 장식 계층으로 연결했다. 새 E2E는 1408px 상단/본문, 1468px 제어바, 326/357px 레일, WebP 응답과 computed background 연결을 1536×1024에서 고정한다. 1366×768 이하에서는 스테이지를 압축해 자막이 주 작업영역을 잃지 않게 했다. <b>검증</b>: <code>npm run check:design-ssot</code>, typecheck, build 통과, 세션 전수 desktop/mobile <code>32 passed / 2 skipped</code>, session-layout <code>8/8</code>, 7폭 layout-visual-gate <code>15/15</code>, 1536×1024 라이트 캡처 직접 확인. 로컬 작업트리 기준이며 배포 전이다.</p>
<div class="dg-principles" aria-label="디자인 생성 가드레일">
<div><b>래스터만 사용</b><span>이미지 생성 도구 산출물은 PNG 기반 시안이다. SVG·벡터·와이어프레임·로고 시트로 해석하지 않는다.</span></div>
<div><b>기능 우선</b><span>메인 라우트의 실제 액션과 정보 구조를 먼저 반영한다. 장식은 기능을 가리지 않는 수준에서만 쓴다.</span></div>
@ -648,7 +650,7 @@
</article>
<article class="dg-card">
<img src="design-concepts/generated/03-session-responsive-v2-dark-unified.png" alt="라이브 상담 세션 PC 태블릿 모바일 다크 테마 디자인 시안" loading="lazy">
<div class="dg-card-body"><b>라이브 상담 세션</b><span>내담자 무대, 자막, 입력, 라이브 신호, 안전 상태를 다크 몰입형 훈련 화면으로 구성.</span><code>03-session-responsive-v2-dark-unified.png</code></div>
<div class="dg-card-body"><b>라이브 상담 세션</b><span>내담자 무대, 자막, 입력, AI 코치, 안전 상태를 전역 라이트·다크 테마와 같은 토큰으로 구성.</span><code>03-session-responsive-v2-dark-unified.png</code></div>
</article>
<article class="dg-card">
<img src="design-concepts/generated/04-session-review-responsive-v2-dark-unified.png" alt="회기 리뷰 PC 태블릿 모바일 다크 테마 디자인 시안" loading="lazy">
@ -955,7 +957,7 @@
<div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>엄격한 레이아웃 시각 게이트 구축 · 병렬 수정 · 적대적 재검수</b><p>핸드오프가 요구한 7개 권장 너비(390/720/861/900/1024/1280/1440) 시각 수용을 자동 게이트로 고정했다. 현재는 빈 회기리뷰 전용 화면까지 9개 화면을 각 너비에서 렌더링해 가로 overflow 0, 컨트롤 하드클립/텍스트클립 0, 다크 테마 캡처를 강제하고 화면당 7장 총 63장 풀페이지 스크린샷을 남긴다. 2026-06-28 당시 화면별 1에이전트 병렬 시각 리뷰로 49장 직접 판독 → 6개 병렬 수정 에이전트(파일 비중첩)가 결함 수정 → 화면별 적대적 재검수로 해소·무회귀를 확정했다.</p></div><div><b>산출물</b><p><code>apps/web/e2e/layout-visual-gate.spec.ts</code>, <code>node_modules/.tmp/layout-gate/*.png</code> 63장, 2× workflow(review/verify)·6× fix agent</p></div><div><b>검증</b><p>최신 게이트 9 passed(재스크린샷). 해소된 핵심 결함: 학습자홈 1280/1440 3열 가운데 컬럼 붕괴(critical → 2열 분기 상향 + word-break:keep-all), 교수 'API 404' raw 배너 제거, 리뷰 빈상태 위계 역전과 1280 sparse third-column, 관리자 스켈레톤, 설정 와이드 좌측 데드존 제거, 세션 모바일 44px 터치타깃. 2026-06-28 적대적 재검수 7/7 accept, regression 0.</p></div></div>
<div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>레이아웃 cosmetic minor 폴리시 일괄 처리</b><p>적대적 재검수 잔여 cosmetic minor를 5개 병렬 폴리시 에이전트로 처리했다. 학습자홈 헬퍼문구 고아 글자, 세션 1024 일시정지 아이콘 정리 + 보내기 버튼 대비 강화, 설정 2x2 칩 행 높이 균일, 교수 검토 큐 카드 갭 제거, 관리자 2열 높이 동기화를 적용했다.</p></div><div><b>산출물</b><p>page-by-page polish patches(LearnerHome/Session/Settings/Professor/Admin)</p></div><div><b>검증</b><p><code>npm run typecheck</code> OK, 당시 cosmetic 시각 게이트 7 passed, 최신 통합 시각 게이트 9 passed, 레이아웃 E2E desktop/mobile 54 passed, session-layout 8/8 — 회귀 0.</p></div></div>
<div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>회기 아카이브 저장/복원 API</b><p><code>/learn/history</code><code>보관됨</code>을 실제 학습자별 저장 상태로 연결했다. 종료 회기는 <code>POST /sessions/{id}/archive</code>/<code>restore</code>로 보관·복원하고, <code>app.session_archive_state</code>는 보기 상태만 저장한다. 진행 중 회기는 보관하지 못하며, 보관은 transcript/review/share/audit evidence를 삭제하지 않는다.</p></div><div><b>산출물</b><p><code>SessionArchiveResponse</code>, <code>LearnerSessionSummary.archived</code>, <code>LearnerDashboardOverview.archived_sessions</code>, <code>session_archive_state</code> RLS, <code>LearnerHome</code> 보관/복원 버튼</p></div><div><b>검증</b><p><code>python -B -m pytest app/ -q</code> 178 passed, <code>python -B -m pytest engine_gateway/ -q</code> 11 passed, <code>npm run check:api-types</code>, <code>npm run typecheck</code>, <code>npm run build</code>, <code>npx playwright test e2e/learner.spec.ts --project=chromium-desktop --workers=1</code> 6 passed.</p></div></div>
<div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>세션 종료 UX와 다크 테마 SSOT</b><p>세션 하단의 드래그형 종료 컴포넌트를 명시 확인 다이얼로그로 바꿔 모바일 오발동과 제스처 실패를 줄였다. Topbar/Settings의 theme 저장·초기화는 <code>lib/theme.ts</code> 단일 경로로 합치고, 저장값이 없으면 dark 기본값을 앱 부팅 전에 적용한다. API 기본 preference <code>system</code>은 Settings에서 light로 오해하지 않고 현재 초기 테마를 따른다. active session 스테이지 상태 배지는 아바타와 겹치지 않는 3행 구조로 정리했다.</p></div><div><b>산출물</b><p><code>Session.tsx</code>, <code>AvatarPreview.tsx</code>, <code>session.css</code>, <code>avatar-expression.spec.ts</code>, <code>session-layout.spec.ts</code></p></div><div><b>검증</b><p>세션 종료·다크 테마·3행 스테이지 구조를 시각 회귀 묶음으로 확인했다. 래스터 파츠 실험 자산은 후속 결정 전 보존 상태다.</p></div></div>
<div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>세션 종료 UX와 테마 SSOT</b><p>세션 하단의 드래그형 종료 컴포넌트를 명시 확인 다이얼로그로 바꿔 모바일 오발동과 제스처 실패를 줄였다. 테마 저장·초기화·OS 추종은 <code>lib/theme.ts</code> 단일 경로가 소유하며, 활성 세션도 전역 <code>data-theme</code><code>tokens.css</code> 의미 토큰을 그대로 따른다. 세션 전용 다크 팔레트 재정의는 제거했고 active session 스테이지 상태 배지는 아바타와 겹치지 않는 3행 구조로 유지한다.</p></div><div><b>산출물</b><p><code>Session.tsx</code>, <code>AvatarPreview.tsx</code>, <code>session.css</code>, <code>full-sweep-session.spec.ts</code>, <code>session-layout.spec.ts</code></p></div><div><b>검증</b><p>라이트·다크 세션, 종료 UX, 짧은 높이·중간 폭 AI 코치 가시성, 3행 스테이지 구조를 시각 회귀 묶음으로 확인했다.</p></div></div>
<div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>래스터 아바타 비활성화·SVG 리그 복귀</b><p>사용자 시각 피드백에 따라 서연 P1과 P4~P7의 생성 이미지/PSD 파츠 연결을 전부 제거했다. 제품·학습자 홈·세션·dev 미리보기는 기존 SVG 도형 기반 파라미터 리그만 렌더링한다.</p></div><div><b>산출물</b><p><code>ClientAvatar.tsx</code>, <code>persona.ts</code>, <code>Session.tsx</code>, <code>LearnerHome.tsx</code>, <code>AvatarPreview.tsx</code>, <code>avatar-expression.spec.ts</code></p></div><div><b>검증</b><p>typecheck/build 통과, 아바타·세션 desktop/mobile 18 passed, 전체 레이아웃 시각 게이트 12 passed, 커스텀 도메인 P1 공개 번들 desktop/mobile 2 passed. <code>data-render-mode="svg"</code>, 래스터 DOM 0개, SVG primitive·neck·표정 전환을 확인했다. 래스터 렌더러와 자산은 재검토용으로 삭제하지 않았다.</p></div></div>
<div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>잔여(비차단) — 공용 셸 단일 항목 → 처리</b><p>축소 사이드바 세로 구분선이 본문 전체 높이까지 닿지 않던 건을 <code>components/shell/shell.css</code>에서 처리했다. <code>.vg-nav border-right</code> 제거 후 <code>.vg-shell__body</code> 컨테이너 배경 하어라인으로 본문 그리드 전체 높이 구분선을 그리고, 그리드·구분선 폭을 <code>--nav-cur</code>로 동기화. learner-home 로딩 스켈레톤 밀도도 실제 카드 구조 모사로 보강했다.</p></div><div><b>판정</b><p>구현 완료. 검증: <code>npm run typecheck</code> PASS + <code>vite build</code> PASS. 전 페이지 시각 회귀 게이트는 web+api+DB 스택으로 이 워크스테이션 미실행 — 스택 가용 시 1회 시각 확인 권장.</p></div></div>
<div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>디자인 SSOT 리팩터링 · 인증 화면 복구</b><p>테마 알림은 <code>lib/theme.ts</code>/<code>lib/useTheme.ts</code>, 앱 크롬은 <code>AppShell</code>, 글래스 표면은 <code>Surface</code>가 각각 단독 소유한다. 로그인·온보딩·승인대기는 <code>AuthShell</code>의 100vw/100dvh 캔버스를 공유하고, 학습·세션·리뷰·교수자·관리자·페르소나·설정의 카드/인셋은 공통 Surface variant로 이관했다. <code>shell.css</code>의 페이지 클래스 나열과 <code>!important</code> 표면 덮어쓰기는 제거했다. 라이트 Surface도 2중 반투명 광원, 24px backdrop blur, 굴절 hairline을 공통 토큰으로 사용하고 모바일 셸의 보태니컬 배경을 유지한다.</p></div><div><b>검증</b><p><code>npm run check:design-ssot</code>, typecheck/build 통과. 인증 라이트·다크 × 390/1280 시각 게이트 1 passed, 14개 화면 × 7개 폭 레이아웃 게이트 14 passed, 학습·세션·리뷰 desktop/mobile 46 passed, 로그인→온보딩→학습 desktop/mobile 2 passed. 라이트 글래스의 gradient/blur/hairline/shadow와 모바일 배경 자산은 computed style 단언으로 고정했다.</p></div></div>