From 18b8ef3ef22f9bf15c000705b831414de0ddd20e Mon Sep 17 00:00:00 2001 From: Yun Chan Date: Fri, 26 Jun 2026 00:24:18 +0900 Subject: [PATCH] =?UTF-8?q?fix(web):=20=EC=83=81=EB=8B=B4=EC=84=B8?= =?UTF-8?q?=EC=85=98=20=EB=9D=BC=EC=9D=B4=EB=B8=8C=20=EC=97=B0=EA=B2=B0=20?= =?UTF-8?q?=E2=80=94=20start=20session=5Fid=EB=A5=BC=20turn=EC=97=90=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9(liveSessionId)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 버그: handleStart가 sessionApi.start 결과 session_id 미저장, handleSend가 URL :sessionId(임의값)로 turn 호출 → 백엔드 세션 불일치 → mock 폴백 - 수정: liveSessionId state에 start 결과 저장, turn/consume이 이걸 사용 - consumeClientReply: 동기 client_reply 있으면 SSE 스킵하고 타이핑 표시 - persona_code seoyeon→P1(시드 페르소나) - puppeteer 실화면 검증: 회기시작→말걸기→서연 실응답 확인 --- .gitignore | 1 + apps/web/src/pages/Session.tsx | 23 +++++++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index db523b4..0be601d 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ postgres-data/ *.out apps/api/gateway.restart.* apps/api/e2e_*.py +apps/web/_pptr_check.cjs diff --git a/apps/web/src/pages/Session.tsx b/apps/web/src/pages/Session.tsx index ff27a2a..a3a6d80 100644 --- a/apps/web/src/pages/Session.tsx +++ b/apps/web/src/pages/Session.tsx @@ -144,6 +144,8 @@ export default function Session() { // ── 세션 진행 상태 ── const [started, setStarted] = useState(false); + // start 가 백엔드에서 발급한 진짜 세션 id (turn/stream 은 이걸 써야 함. URL :sessionId 는 라우팅용) + const [liveSessionId, setLiveSessionId] = useState(null); const [starting, setStarting] = useState(false); const [startError, setStartError] = useState(null); @@ -272,8 +274,9 @@ export default function Session() { setStarting(true); setStartError(null); try { - // persona_code 는 데모 고정. 백엔드 연결 시 케이스 라우팅으로 대체. - const res = await sessionApi.start("seoyeon", "humanistic"); + // persona_code 는 데모 고정(P1 시드 페르소나=서연). 백엔드 연결 시 케이스 라우팅으로 대체. + const res = await sessionApi.start("P1", "humanistic"); + setLiveSessionId(res.session_id); // ★ 진짜 세션 id 저장 — turn 이 이걸 써야 라이브 setStage(res.stage); } catch { // 백엔드 미가동/스텁 → 데모 모드(에러로 막지 않음). 입력 가능한 데모 동작 유지. @@ -345,7 +348,13 @@ export default function Session() { setOpenness((o) => clamp01(o + 0.04)); }; - // SSE 시도 + // 백엔드 turn 이 동기 client_reply(서연 실응답)를 줬으면 그걸 타이핑으로 표시(실서버 우선). + if (seedText) { + demoType(seedText, id, finalize); + return; + } + + // (client_reply 없을 때만) SSE 시도 → 실패 시 mock 폴백 try { streamRef.current?.close(); streamRef.current = openSessionStream(sessionId, { @@ -398,7 +407,9 @@ export default function Session() { let clientReply: string | null = null; try { - const res = await sessionApi.turn(sessionId, text); + // start 가 발급한 진짜 세션 id 로 turn (없으면 데모 폴백). URL :sessionId 아님. + if (!liveSessionId) throw new Error("no live session"); + const res = await sessionApi.turn(liveSessionId, text); setStage(res.stage); setOpenness(res.effective_openness); clientReply = res.client_reply; @@ -406,7 +417,7 @@ export default function Session() { setSafety("방금 발화에서 위기 신호가 감지됐어요. 안전을 우선해 머물러 주세요."); } } catch { - // 백엔드 미가동 → 데모 응답(끊김 방지) + // 백엔드 미가동/세션없음 → 데모 응답(끊김 방지) clientReply = null; } finally { setSending(false); @@ -414,7 +425,7 @@ export default function Session() { // 잠깐 'thinking' 후 내담자 응답 스트림 window.setTimeout(() => consumeClientReply(clientReply), 420); - }, [composeText, sending, paused, elapsed, sessionId, consumeClientReply]); + }, [composeText, sending, paused, elapsed, liveSessionId, consumeClientReply]); const onComposeKey = (e: ReactKeyboardEvent) => { if (e.key === "Enter" && !e.shiftKey) {