카드뷰 세션 작업 변천사

마지막 업데이트 2026-07-22

기간. 2026-02-25 ~ 2026-05-25 주요 티켓. PPI-765 / 880 / 897 / 910 / 923 / 930 / 941 / 968 커밋. 50+ (revert 7회 포함) 작성. 2026-05-25
한 줄 요약. 3개월간 모니터 대시보드의 카드뷰(session-card)는 UI 통일 → 자리 안정화 → 카드별 볼륨 제어 → 시간종료/네비게이션 강화 순으로 진화했다. 핵심 영역(카드 자리 정렬·게스트 오디오 GainNode·세션 종료 흐름)에서 총 7건의 revert 사이클이 발생했고, 모두 오디오 트랙 교체/렌더 타이밍/시간 슬롯 정렬이라는 동일한 비동기 상태 문제에서 출발했다. 마지막에는 roomId 보존 캐시(id-017)와 새 MediaStream 객체 할당(id-029) 패턴으로 안정화되었으나, PPI-968 시간종료 버튼(id-037#644 볼륨 슬라이더 재도입은 또다시 revert되어 5월 말 기준 여전히 진행 중인 영역이다.

목차

  1. 1. 배경 - 카드뷰란
  2. 2. 큰 흐름 요약
  3. 3. UI 통일 / 통합 모니터링
  4. 4. 자리 안정화 (PPI-930)
  5. 5. 카드별 청취 볼륨 슬라이더
  6. 6. 시간/타이머/시간종료
  7. 7. 스텝 네비게이션·조건 표시
  8. 8. 발화/언어 감지 통합
  9. 9. 게스트/네트워크 인디케이터
  10. 10. 반응형·기타 UI 폴리싱
  11. 11. 부가 기능 (이슈/LogRocket/분기 이동)
  12. 12. 회고 - 반복된 패턴

🧭 1. 배경 - 카드뷰란

모니터 대시보드의 카드뷰 위치

치료사가 진행 중인 1:1 / 그룹 수업을 한 화면에서 동시 관찰하는 monitor-dashboard의 핵심 UI. 각 카드(SessionCard)는 한 명의 게스트(아동) 세션 하나를 담당한다.

왜 자주 손대는가

핵심 파일

📊 2. 큰 흐름 요약

그룹기간대표 PR패턴
UI 통일 2/28 ~ 3/24 #420 → #490 → #494 revert → #496 재도입 1차 시도 → 회귀 발견 → 버그 수정 포함 재도입
자리 안정화 4/29 ~ 5/19 #578 → #606 revert → #622 재도입 sticky 캐시 단독 → roomId 정렬 결합으로 보강
카드별 볼륨 5/1 ~ 5/24 #581 / #596 → #604·#605 revert → #638 → #644·#157d revert 4차례 revert. 트랙 교체 시 무음 회귀가 반복
시간/타이머 3/4 ~ 5/24 #430 → #434 revert / #495 / #646 → #649 revert 누적시간 정의·종료 흐름이 1:1뷰와 동기화 필요
스텝 네비게이션 3/26 ~ 4/27 #462, #570 안정 안착. 카드뷰만의 차별 기능
발화/언어 감지 3/26 ~ 4/9 #461, #480, #535(MFCC 전환) Whisper LID → MFCC 전환과 동기화
게스트/네트워크 3/19 ~ 4/15 #523, #547 안정 안착
이슈 스탬프 바 4/13 ~ 5/9 #555 → #607 revert 도입 후 사용 패턴 미스매치로 롤백

🧩 3. UI 통일 / 통합 모니터링

① 포커스뷰를 1:1 모니터링뷰로 통일

2/28 4ce88c32 REFACTOR

카드뷰에서 진입하는 포커스뷰와 대시보드에서 진입하는 1:1 모니터링뷰의 UI를 하나로 통합. 그룹 내 세션 전환 패널·뒤로가기·Open 버튼 동작을 모두 type=oneOnOne URL 파라미터 기반으로 일원화.

변경 핵심
  • Open 버튼을 window.open(새 탭)으로 변경하여 1:1 모니터링과 동일 진입
  • isOneOnOneSession 분기 dead code 삭제 (ActiveSessionsPanel, MinimizedSessionCard 등)
의미. 카드뷰가 단일 모니터링 UI의 표준이 되는 첫 신호. 이후 모든 작업이 "1:1뷰와 카드뷰 공통" 형태로 진행됨.

② PPI-765 통합 모니터링 + 1:1 카드 UI 통일

3/22 ded8660a FEAT
변경 핵심
  • ScheduledClass/Lesson에 담당 진행자(memberId/Name, groupMemberIds) 필드 추가
  • SessionCard(1:1) 3단계 색상·초록 dot·하단 바 재설계 (확인중·담당·내보내기)
  • LiveSessions 통합 그룹 카드 3영역 구조 (헤더+N/M+입장 / 인라인 세션 / 하단 바)
  • 색상 뱃지로 통합 모니터링 라벨 대체 (파란=확인중, 회색=담당)
  • broadcast-utils getRoomList가 group-monitoring-claim 피어를 그룹 룸 hosts에 포함
3/22. PPI-765 revert (id-038)

그룹 카드뷰 진입자(group-monitoring-claim)가 포커스뷰에서 "X님이 확인 중입니다" 오버레이를 잘못 트리거하는 회귀 발견. 단일 PR로 묶여 있어 통째로 revert.

③ PPI-765 재도입 + 포커스뷰 오버레이 버그 수정

3/24 id-018 FEAT

그룹 카드뷰 진입자를 hostsSummary에 포함하지 않도록 변경하여 포커스뷰 오버레이 오작동 해결. 대시보드 "확인 중" 뱃지는 별도 채널(connected-hosts-updated)에서 받으므로 영향 없음.

패턴. revert 후 며칠 내 같은 PR에 fix를 포개서 재도입. 큰 UI 변경은 SSOT 흐름과 broadcast 채널 분리가 함께 검증되어야 함.

④ 통합 모니터링 그룹 진행자 내보내기 (PPI-812)

3/4 d2325525 FEAT

그룹 카드뷰에서 다른 진행자를 내보내는 기능. id-031에서 1차 revert 후 id-049에서 reapply.

⑤ 카드뷰 그룹 모니터링 강제퇴장 + 수업종료

3/26 ec1e7ea1 FEAT
변경 핵심
  • SSOT 합성 훅 useLessonCompletionWithDeps 도입 (4개 CRUD 자동 주입)
  • LessonCompletionModals 통합 — 1:1뷰·카드뷰 간 중복 제거
  • CardEndMenu 컴포넌트 추가 (좌상단 종료 pill + 드롭다운)

⑥ 모니터 대시보드 1:1/그룹 분류 정합성 픽스 (PPI-947)

5/14 4b4ecb34 FIX

임시개방(isTempOpen)으로 1:1 운영 중인 그룹 사용자가 정상 그룹 카드로 분류되는 회귀. ScheduledSession 인터페이스에 isTempOpen 추가, routerManager.setGroupId 시그니처를 string | undefined로 확장.

📍 4. 자리 안정화 (PPI-930)

카드뷰의 가장 끈질긴 회귀 영역. 같은 그룹의 카드가 게스트 socket 진입·외부 API 상태 변화에 따라 자리를 옮기는 문제가 3개월 내내 다른 형태로 재발했다.

① 카드 순서 결정적 정렬 도입 (PPI-767과 함께)

4/13 4d774025 FIX

updateSessionJoinStatusunshift 리오더 로직 제거. mergedRooms 배열에 scheduledStartTime 오름차 + userName 한글 보조 정렬 추가. 게스트 접속 시 카드 위치 변경 버그 1차 차단.

② PPI-930 sticky 캐시 도입 (1차)

4/29 282821af FIX
문제

수업 시작 +20분 즈음 외부 PPI API가 status를 COMPLETED로 전이시키면 useScheduledSessions 폴링 응답에서 해당 세션이 누락 → mergedRooms 정렬 키가 (∞, userId UUID)로 점프하여 같은 시간 슬롯의 카드 자리가 뒤집힘.

해결 시도
// 1. includeCompleted=true 호출
// 2. sticky 캐시 (TTL 6분)으로 일시 누락된 비-terminal 세션 보존
// 3. terminal status는 즉시 제거
// 4. 정렬 comparator에 roomId tie-breaker, userName→userId 폴백 제거
5/12. PPI-930 1차 sticky 캐시 revert (id-001)

예상치 못한 동작 발견(폴 실패 시 화면 비는 케이스 등 추정). 단독 revert.

③ PPI-930 재도입 - roomId 정렬 + 보존 캐시 결합

5/19 id-017 FIX
접근 변경

두 가지 회귀를 함께 잡도록 통합.

  1. 게스트 socket 진입 시 자리 이동. mergedRooms가 정렬 없이 Map 삽입 순서(소켓 활성 룸 먼저 → 예약만 된 룸 뒤)에 의존 → roomId 사전순 정렬 추가. roomId = `${userId}_${lessonIndex}`는 회기당 영구 불변이라 자리 결정적 고정.
  2. 20분 시점 사라짐. sticky 캐시 재도입 (변수명 정리 — StickyEntry → RetainedSession, STICKY_TTL_MS → RETAINED_SESSION_TTL_MS).
  3. !response.ok / catch 분기에서 setScheduledSessions([]) 제거 — 폴 실패 한 사이클에도 화면이 비지 않도록 sticky 유지.
교훈. 정렬 안정성은 표시명 기반 키가 아니라 영구 불변 ID 기반 키(roomId)로 잡아야 함. sticky 캐시는 보조 도구이지 단독 해결책이 아님.

🔊 5. 카드별 청취 볼륨 슬라이더

3개월 변천사 중 가장 많은 revert(4회)가 발생한 영역. 모두 "트랙 교체 시 게스트 음성이 무음이 되는" 동일한 회귀에서 비롯됨.

① 1차 시도 - 카드별 개별 볼륨 슬라이더

5/1 249b9e16 FEAT
변경 핵심
  • 청취 중인 카드 pill 옆 0~300% 볼륨 슬라이더 (기본 100%, step=5)
  • Web Audio API GainNode로 100% 초과 증폭 (HTML audio.volume 한계 우회)
  • WebRTC remote stream의 Chrome bug(crbug.com/121673) 워크어라운드로 muted <audio>를 stream keepalive 용도로 유지
  • forwardRefresumeContext 노출하여 autoplay 정책 안정성 확보

② 볼륨 제어 분리 - 게스트 음성만 (PPI-941)

5/6 b040f293 FEAT

슬라이더가 AI(핑퐁이)·영상까지 함께 조절되는 회귀 발견 → 게스트 마이크만 별도 guestGain 노드로 분리. volumeToGain 함수로 piecewise 매핑 도입 (0~100%는 0~1, 100~300%는 1~5 최대 5배 증폭). volume/isMuted/isActive ref render-time 갱신으로 stale prop 회피.

5/7. PPI-941 + #581 동시 revert (id-039 / id-054)

실제 운영에서 카드 청취가 OFF→ON 되는 순간이나 mic-audio 트랙 교체 후 게스트 음성이 무음이 되는 회귀 발견. 두 PR 모두 통째로 revert. id-050(MediaStream 객체 재할당 fix)도 함께 id-014에서 revert되어 5/7에만 3건의 revert가 연쇄 발생.

③ 최종 재도입 - MediaStream 재할당 통합

5/24 id-029 FEAT
변경 핵심
  • 카드별 0~300% 볼륨 슬라이더 (청취중일 때만 노출, 청취 OFF시 hide, 재 ON시 마지막 값 복원)
  • Web Audio API GainNode, MAX_GAIN=5, 게스트 전용 guestGain 분리
  • mic-audio consumer 트랙 교체 시 새 MediaStream 객체 할당 (이전 PPI-943 fix를 본 PR에 통합). 이전 revert 사유였던 "게스트 음성 무음" 회귀 방지
  • SessionAudioPlayer가 render 시점 트랙 ID 변화를 감지해 source node를 재생성 (이중 방어). W3C 스펙상 script-mutation 경로에서 발화하지 않는 onaddtrack/onended 이벤트 의존성 제거
  • forwardRef + resumeContext()로 청취 토글 user gesture 내에서 AudioContext 활성화 보장 (Chrome autoplay 정책 대응)
해결 키. 트랙 교체 → 새 MediaStream 객체 할당 + SessionAudioPlayer render time 트랙 ID 감지. 단일 PR에 모든 방어 로직을 묶어 통합 검증.

④ #644 재도입 시도 - 다시 revert

5/24 ce5fd4c8 → id-003 REVERT

같은 날 별도 PR(#644)로 또 다른 형태의 카드별 청취 볼륨 슬라이더 도입이 시도되었으나 곧바로 revert. 5월 말 기준 이 영역은 여전히 진행 중이며 #638(id-029)이 현행 안정판.

6. 시간/타이머/시간종료

① 포커스뷰 헤더 타이머 비정상 값 + 카드뷰 레이아웃 (PPI-785)

3/4 6d78f3c9 FIX

포커스뷰 "경과 시간"이 좀비 세션 duration을 포함한 endedSessionsTotal을 사용해 1129204:28 같은 비정상 값 표시. 포커스뷰·카드뷰 모두 서버 관리 sessionSummary.totalDuration을 사용하도록 통일. 카드뷰 헤더 inline 타이머 제거 및 오른쪽 큰 타이머를 "경과 시간 → 누적 시간"으로 교체.

3/5. PPI-785 revert (id-024)

통일한 누적 시간 정의가 다른 화면과 어긋나는 회귀 의심. 단독 revert 후 별도 후속 PR(id-027, id-002)로 호스트 UI 누적 시간을 게스트 자동전환 판정값과 일치시키는 방향으로 천천히 재정비.

② 카드뷰 청취 버튼 기본값 ON (PPI-880)

3/24 7d17c75b FEAT

카드 진입 시 바로 해당 세션 오디오를 청취하도록 초기값을 OFF→ON. 카드 테두리 녹색 표시 + 비디오 mute 자동 해제. 안정 안착.

③ 시간종료 버튼 + 카드뷰 UI 정리 (PPI-968)

5/24 id-037 FEAT
변경 핵심
  • 호스트가 1:1뷰/카드뷰에서 시간종료 버튼으로 현재 활동 마무리
  • AI가 자연스러운 종료 멘트("대화 종료") 후 체크아웃 활동으로 자동 점프
  • Socket.io 이벤트 host-session-end-conversation 추가 (avatar-handlers.ts)
  • 카드뷰 컨트롤 4열 그리드로 확대, 아이콘 제거 (HeadphonesIcon, PauseCircleIcon)
  • InterruptModeButtonhideIcon prop → 카드뷰/1:1뷰 레이아웃 통일
  • 체크아웃 target. 활동 title에 "체크아웃" 포함하는 가장 첫 번째 활동으로 점프, 없으면 goToNextStep 폴백
  • 핸들러 권한 검증 (socket.rooms.has(roomId), role 검증)
5/24. PPI-968 revert (id-025)

시간종료 흐름이 카드뷰·1:1뷰·게스트 측에서 모두 동작해야 하는데, 어딘가에서 회귀 발견. 통째로 revert. 5월 말 기준 미해결 영역.

🗣 8. 발화/언어 감지 통합

① 전사 영어 발화 감지 시 토스트/하이라이트 (PPI-858)

3/26 1e449648 FEAT

STT 전사 결과에서 영어 키워드 감지 시 카드뷰 토스트 + 로그 하이라이트 표시.

② Whisper LID + MFCC 카드뷰 적용 + Worker 싱글톤 (PPI-869)

4/9 0396cfe0 FEAT
  • 카드뷰에 LanguageMonitorStatusIndicator 추가 (Whisper/MFCC 상태 시각화)
  • Whisper LID 감지 시 자동 응답 취소 (중복 emit 1.5초 throttle)
  • MFCC 감지 시 5초 인디케이터 표시 (focus 페이지 동일 정책)
  • Whisper Worker. 탭 단위 싱글톤 + refCount 기반 생명주기 관리
  • 모델 재로드. in-place warmup (terminate 없음), 캐시 중복 제거
  • 청취 OFF 카드 수정. 숨겨진 audio element로 remote AI 음성 디코딩 보장
효율. 카드 2개 기준 Worker 2→1, 모델 상주 메모리 2→1배.

③ MFCC 패턴 시퀀스로 전환 (관련)

5/7 3a5b9795 REFACTOR

Whisper LID 전체 제거 + MFCC 패턴 시퀀스 기반으로 영어 발화 감지 재설계. 별도 회고 문서 MFCC 패턴 감지 튜닝 변천사에서 상세히 다룬다.

④ 자동 발송 '응' → '음...' 변경 + 카드뷰 지원

2/26 ~ 4/14 737ce6fb / e6a541bb / 3c65db1d FEAT

(#399 PPI-675) 카드뷰에 '응' 자동 발송 토글 추가 → (#455 PPI-839) 자동 발송 제거하고 수동 발송만 → (#487 PPI-828) 문구를 "응"에서 "음..."으로 변경. 사용자 피드백을 따라 정책이 점진적으로 변화.

📡 9. 게스트/네트워크 인디케이터

① 게스트/호스트 네트워크 이상 감지 (PPI-263)

3/19 52952350 FEAT

게스트·호스트 양측의 네트워크 품질을 감지해 카드뷰에 빨강 오버레이/뱃지로 표시. 호스트 독립 계산 critical 레벨이 guestNetworkLevel에 반영되지 않던 버그도 함께 수정.

② 게스트 연결 상태 인디케이터 (PPI-910)

4/15 0249b7d3 FEAT
  • 카드뷰 SessionHeader에 연결 상태 표시 영역(Row 2) 추가
  • 아동/마이크/카메라, 핑퐁이/핑퐁이오디오, 게스트화면/게스트오디오/AI오디오/소켓 도트
  • 연결품질(G/H RTT), 패킷손실, 비트레이트 뱃지 (임계값 초과 시 주황 강조)
  • 항상 표시(호버 불필요) - LogRocket 영상 리뷰 대응
  • 가로 스크롤 없는 2줄 고정 레이아웃, bg-slate-50 배경으로 헤더와 시각적 구분
  • 서버. GUEST_TRANSPORT_STATE 이벤트를 카드뷰 그룹 모니터(monitor-group-{groupId})에도 relay
  • 뷰포트 870px 이하(125%+ 줌)에서 Dot·QualityBadge 크기 축소 (반응형)

③ m20 토스트 UUID_lessonIndex 노출 fix (PPI-921)

4/22 ad81de46 FIX
  • use-session-registration.ts. store에 저장하는 userName에서 _${lessonIndex} 접미사 제거
  • use-session-checker.ts. resolveDisplayName이 정확히 ${userId}_${lessonIndex} 패턴인 경우만 UUID fallback 분류 (stale 데이터 방어)
  • m5/m10/m20 토스트에서 displayName === userId인 경우 UUID 직접 노출 차단, "아동" fallback 사용
  • toast-container.tsx. wrapper pointer-events-auto → pointer-events-none, 각 토스트에 auto 부여

🎨 10. 반응형·기타 UI 폴리싱

① 반응형 UI 최적화 및 헤더 축소 (PPI-897)

3/13 304a9ae4 FEAT
  • 카드 높이를 dvh 기반으로 전환 — 화면 배율(80~125%)에 딱 맞게 조정
  • 미디어 영역 높이를 clamp+dvh로 부드럽게 스케일링
  • 전사 로그·활동명 글자 크기를 zoom 독립적 clamp+dvh로 통일
  • 고화면 배율 compact 모드 ([@media(max-height:870px)]) 패딩/여백 축소

② 카드뷰 음성 유저 청취 자동 활성화 제거 / 포커스뷰 진입 시 OFF

3/5 7a3b7a75 / aa51566d FIX

(#435) 카드뷰 음성 유저 청취 자동 활성화 제거. (#436) 카드뷰에서 포커스뷰 진입 시 청취 상태 OFF로 전환. 사용자가 동시에 같은 오디오를 두 채널에서 듣게 되는 케이스 방지.

③ 게스트 오디오 전송 테스트 + 오디오 레벨 표시

5/19 a7e8bbd6 FEAT

게스트 디버그 패널(테스트 모드)에서 mic-audio(440Hz), ai-audio(880Hz) 오실레이터 테스트 톤을 mediasoup으로 전송하는 버튼. 모니터 카드뷰에 mic/ai 스트림 수신 상태를 AiAudioEqualizer로 표시.

④ AI 오디오 이퀄라이저 + 발화 가시성 (PPI-885 / PPI-865)

3/10 / 3/12 47b13528 / 6ba2e592 FEAT

모니터에 AI 오디오 이퀄라이저 추가 + 활동 전환 시 녹음 잘림 수정. 발화 이퀄라이저 가시성 확보.

⑤ WebRTC PLC 오탐지 방지 (PPI-955 관련)

5/24 a47c16ee FIX

PLC 상태 샘플링 + 기계음 판정 시 PLC 활성 구간 제외. CV/ZCR/RMS 3중 필터. getAiAudioInboundStats 콜백으로 WebRTC stats 제공. 카드뷰 기계음 감지(id-005 PPI-627)의 후속 안정화.

11. 부가 기능 (이슈/LogRocket/분기 이동)

① 이슈 기록 스탬프 바 (PPI-767)

4/13 4d774025 FEAT
  • SessionCard 하단에 토글식 "이슈 기록하기" 스탬프 바
  • 토글 확장 시 6개 이슈 유형 버튼(화면/실행/접속/사운드/AI/기타)
  • 각 버튼 클릭 시 category="web" + 빈 message로 즉시 POST /api/issue-texts
  • voice_recognition 라벨만 카드뷰에서 "사운드"로 오버라이드 (포커스뷰는 유지)
5/9. PPI-767 revert (id-042)

기능 자체는 문제없으나, 실제 사용 패턴과 미스매치(추정). 5월 말 기준 재도입 미완.

② 모니터 카드뷰 진입 시 LogRocket 녹화 (PPI-915)

4/14 6036c11b FEAT

모니터 카드뷰 페이지 진입 시점에 LogRocket 녹화 시작. CS 대응·치료사 행동 분석용. PPI-910 게스트 연결 인디케이터와 함께 "사후 영상 리뷰" 인프라를 형성.

③ 조건별 분기 이동 + 전환 취소 (PPI-861)

3/14 ad3ce901 FEAT
  • 카드뷰 세션 이전/다음 단계 조건별 분기 처리
  • 이전/다음 버튼 아이콘을 ‹/› 텍스트로 교체
  • branchOrigin → lastStep 통합. 분기/순차/스텝목록 점프 모든 케이스 전환 취소 지원
  • 게스트 소켓 중복 리포트로 lastStep이 덮어쓰이는 버그 수정 (hasMoved 체크)
  • isReturningRef로 전환 취소 시 lastStep 갱신 루프 방지

④ 강제퇴장·수업종료 버튼 (#489)

3/26 ec1e7ea1 FEAT

위 ⑤ 섹션 항목 참조. 1:1뷰·카드뷰 간 SSOT 합성 훅 도입.

🔄 12. 회고 - 반복된 패턴

revert 사이클 7건 총정리

원본 PRrevert SHA재도입 SHA핵심 회귀 원인
#490 PPI-765 id-038 id-018 (+버그 수정) 그룹 카드뷰 진입자가 포커스뷰 오버레이 잘못 트리거
#578 PPI-930 id-001 id-017 (+roomId 정렬) sticky 캐시 단독으로는 자리 결정성 부족
#581 볼륨 id-054 id-029 트랙 교체 시 게스트 음성 무음
#596 PPI-941 id-039 id-029 (통합) 위와 동일 — 같은 날 함께 revert
#602 PPI-943 id-014 id-029 (흡수) MediaStream 객체 재할당이 단독 PR로는 회귀
#430 PPI-785 id-024 분산 후속 작업 누적 시간 정의가 다른 화면과 불일치
#555 PPI-767 id-042 미정 실제 사용 패턴 미스매치
#646 PPI-968 id-025 미정 (진행 중) 시간종료 흐름 다중 진입점 회귀
#644 볼륨 재도입 id-003 미정 (진행 중) #638 안정판 직후 추가 변경이 다시 revert

반복된 회귀 패턴 3가지

  1. 오디오 트랙 교체 시 무음. mic-audio consumer 트랙이 바뀔 때 기존 MediaStream·AudioContext·GainNode 체인이 stale 상태로 남음. 해결책은 새 MediaStream 객체 할당 + render time 트랙 ID 감지로 source node 재생성. 단일 fix만 따로 PR하면 다시 깨지므로 볼륨 슬라이더 PR에 통합 검증 필요.
  2. 카드 자리 정렬은 영구 불변 키로. userName·표시명·소켓 활성 상태처럼 변할 수 있는 키를 정렬 기준으로 쓰면 게스트 입장·외부 API 응답에 따라 카드가 이동. roomId(userId_lessonIndex) 사전순 정렬이 유일한 안정 해법.
  3. 다중 진입점 흐름은 SSOT 훅으로. 1:1뷰·카드뷰·포커스뷰·통합 모니터링이 같은 비즈니스 로직(수업종료/강제퇴장/시간종료)을 각자 호출하면 한쪽만 수정될 때 회귀. useLessonCompletionWithDeps·LessonCompletionModals처럼 합성 훅으로 모아야 PPI-968·#489 같은 작업이 안전.

위험 신호 (다음에도 조심해야 할 것)

5월 말 기준 진행 중 영역

MFCC 패턴 감지 알고리즘의 변천은 별도 문서 MFCC 패턴 감지 튜닝 변천사 참조.