모니터 탭 화면·오디오 녹화 구현 계획
마지막 업데이트 2026-10-12
카드뷰·포커스뷰 탭을 한 번 캡처하고 단일 MediaRecorder로 녹화. 탭 오디오·진행자 마이크 정책, 청크 저장·업로드, 오류 복구와 저사양 성능 검증 계획.
01 · 목표와 상태
상태: 구현 전 제안. 사용자 요청은 카드뷰·포커스뷰 화면·오디오 녹화이며, 진행자 마이크 포함 여부와 저장 보존 기간은 미확정이다. 이 문서는 설계 검토용으로 런타임 수정 승인을 대신하지 않는다. 이슈 ID는 미배정이다.
02 · 현재 구조와 적용 지점
| 확인한 경로 · PPI 저장소 기준 | 역할과 변경 계획 |
|---|---|
apps/web/app/monitor-dashboard/[group]/page.tsx | 그룹별 room/session을 모아 카드뷰 표시. 카드뷰와 포커스뷰의 라우트 전환에도 유지되는 공통 상위 레이아웃 또는 세션 서비스에서 녹화 수명을 소유한다. 정확한 공통 조상과 포커스뷰 연결 방식은 구현 전 확인한다. 카드별 녹화기를 만들지 않는다. |
apps/web/screens/monitor-dashboard-group-screen.tsx | 대시보드 헤더·카드 영역·알림 패널 구성. 헤더에 시작·종료·저장 상태 UI를 추가할 후보 지점. |
apps/web/hooks/mediasoup/use-mediasoup-consumer.ts | 학생 마이크와 AI 오디오 수신 스트림 분리. 1차 탭 오디오 녹화에서는 consumer를 추가하거나 수신 경로를 수정하지 않는다. |
apps/socket/src/recording/recordingManager.ts | 학생·AI 오디오 stem 저장·시간 정렬·합성. 기존 서버 녹음과 별도 기능으로 다룬다. 카드 UI 녹화 기능으로 간주하지 않는다. |
새 기능 모듈 후보: apps/web/features/monitor/tab-recording/. 아래 책임 분리는 제안이며 새 파일·API는 아직 존재하지 않는다.
03 · 녹화 흐름과 오디오 정책
모니터 탭 → getDisplayMedia → 화면 트랙 1개 ───────────┐
캡처된 탭 오디오 ──────────┬→ 오디오 트랙 1개 ──────┤
진행자 마이크 (옵션) → 믹서 ┘ ↓
MediaRecorder 1개
↓
청크 임시 저장 → 순차 업로드
↓
서버 조립·검증 → 저장 완료- 탭에서 재생되는 학생·AI·수업 자료 소리를 기록한다. 카드 음소거·볼륨 변경이 녹음에 반영된다. 네 학생 모든 발화 보존은 별도 정책으로 제외한다.
- 진행자 마이크는 탭 오디오에 자동 포함된다고 가정하지 않는다. 옵션을 켜면 기존 공유 가능한 마이크를 우선 재사용한다. 사용할 스트림이 없으면 사용자 조작으로 한 번만 확보한다.
- 마이크 미포함 모드는 캡처 오디오를 직접 사용한다. 포함 모드만 AudioContext 하나와 MediaStreamAudioDestinationNode 하나로 믹싱한다. 출력은 스피커로 연결하지 않아 로컬 피드백을 만들지 않는다.
- 탭 오디오와 동일한 원격 수신 오디오를 함께 믹싱하지 않는다. 마이크가 이미 탭에 재생되는지 확인해 중복도 방지한다. 헤드셋 환경과 스피커 재유입을 별도로 검증한다.
- 녹화 종료 시 소유한 캡처 트랙·마이크만 정리한다. 수업용 공유 마이크·consumer 트랙은 stop하지 않는다. 믹서 입력 교체 시 출력 트랙은 유지한다.
04 · 시작·종료와 오류 처리
- 시작: 버튼 클릭 안에서 getDisplayMedia 호출. 현재 탭 선택·탭 오디오 공유를 안내한다. preferCurrentTab 등은 힌트이며 현재 탭을 강제로 선택하지 못한다. displaySurface와 오디오 트랙 존재를 검사한다. 현재 탭 검증이 지원되지 않으면 미리보기로 선택 대상을 확인한다. 잘못된 대상·오디오 없음은 녹화 시작 전 재선택 안내.
- 준비: 지원 MIME을 isTypeSupported로 검사하고 생성·시작 오류도 처리한다. 캡처 화면 1280×720·최대 15fps를 요청하고 실제 getSettings와 선택 코덱을 진단에 기록한다. 설정이 달라지면 실제 값을 기준으로 품질을 평가한다.
- 녹화: 화면 트랙 하나·오디오 트랙 하나로 MediaRecorder 하나 생성. start(5000)을 출발값으로 사용한다. 청크 시간을 개수로 계산하지 않고 단조 시계로 경과 시간을 표시한다.
- 종료: 종료·브라우저 공유 중단·화면 트랙 ended를 같은 종료 함수로 합친다. 중복 stop을 방지하고 마지막 dataavailable과 저장 완료를 기다린다. finalize 성공 전에는 ‘저장 완료’를 표시하지 않는다.
- 실패: 권한 취소는 대기 상태로 복귀. 코덱·장치·저장 오류는 원인에 맞게 표시. 업로드만 실패하면 보관된 청크를 재시도한다. 저장 공간 고갈 등 보존 불가 상태는 녹화를 종료하고 확보된 부분과 누락 여부를 알린다.
- 수명: 같은 탭의 카드뷰 ↔ 포커스뷰 전환은 녹화기를 유지하고 하나의 영상에 연속 기록한다. 새 탭으로 이동한 화면은 기존 탭 캡처에 포함되지 않으므로 같은 탭 전환을 지원 범위로 둔다. 모니터 밖 SPA 경로 이탈은 useOverlay로 종료·저장 안내. 탭 닫기·새로고침 시 비동기 종료나 마지막 청크 보존은 보장할 수 없다. 저장된 청크 복구와 불완전 녹화 표시를 제공한다. 브라우저 권한 재요청 없이 자동 녹화 재개하지 않는다.
포커스뷰 진입 경로: apps/web/app/monitor-dashboard/[group]/[roomId]/page.tsx. 기존 화면을 캡처하므로 녹화 전용 카드 화면을 재합성하지 않는다.
상태 전이: idle → selecting → recording → stopping → saving → completed. 오류·재시도 상태는 보존 여부와 미업로드 청크 수를 함께 가진다. 프레임·청크 데이터는 React 상태에 넣지 않는다.
05 · 성능 예산과 저장 설계
| 초기 제안 | 검증할 항목 |
|---|---|
| 720p · 15fps · 영상 1.5~2.5Mbps | 카드 4개 글자·학생 얼굴 식별. 가독성 부족이면 1080p·15fps와 비교. |
| 오디오 96~128kbps | 학생·AI·진행자 음성의 명료도, 중복·누락·왜곡 여부. |
| 청크 목표 5초 | timeslice 지연·큰 청크 처리. 실제 청크 크기에 따라 큐 관리. |
| 저장량 약 0.9GB/시간 @ 합계 2Mbps | 비트레이트 목표는 실제 출력 크기를 보장하지 않음. 임시 공간·최대 수업 길이 산정. |
- Canvas 재합성·DOM 반복 이미지화·카드별 인코더·브라우저 FFmpeg WASM 변환은 추가하지 않는다. 인코더 하드웨어 가속은 보장하지 않는다.
- IndexedDB 임시 저장 후 업로드 동시성 1로 전송한다. 초기 메모리 대기 예산은 32MiB 제안, 예상 저장량을 바탕으로 quota를 확인한다. 둘 다 실제 장비 검증 후 조정한다.
- 네트워크 단절 시 저장 청크를 유지한다. 저장 지연으로 메모리 한도를 넘을 위험이 생기면 무한 버퍼링하지 않고 녹화를 종료한다. 녹화 일시정지로 수업 누락을 숨기지 않는다.
- 권한 있는 진행자에게 서버에서 recordingId·업로드 키를 발급한다. 청크 순번·크기·체크섬을 관리하고 재전송을 멱등 처리한다. finalize는 청크 누락을 검사한다.
- 청크는 독립 재생 파일이 아닐 수 있다. 순서대로 스트림을 복원하고 서버에서 필요 시 remux/변환한다. 최종 저장 확인 후 로컬 청크를 제거한다.
- 보존 기간·열람 권한·복구 유효기간·최대 녹화 길이는 구현 착수 전에 확정한다. 기존 녹음 저장 API를 재사용할 수 있는지는 별도 확인이 필요하다.
06 · 구현 순서와 검증 계획
- 설계 승인: 탭 오디오 정책, 진행자 마이크 기본값, 지원 브라우저, 저장 정책 확정. code-quality-guardian 기준 combined 작업으로 분류하고 관련 baseline·harness 확인.
- 로컬 프로토타입: 단일 탭 캡처·오디오·녹화 종료를 구현. 가짜 MediaRecorder·저장 adapter로 권한 취소, 중복 종료, 마지막 청크, 공유 마이크 보존을 먼저 테스트.
- 내구성 저장: 청크 저장·제한 큐·재시도·서버 finalize 계약 구현. 역순·중복 업로드·누락 청크·저장 공간 고갈을 테스트.
- UI: 카드뷰·포커스뷰 모두 시작·종료·진행 시간·저장·복구 상태를 표시한다. 뷰 전환 중 녹화기 유지·중복 생성 방지를 검증한다. modal/dialog는 공통 useOverlay 사용. 실제 backend·장치 없이 모든 상태를 Storybook fixture로 만들고 관련 정적 빌드 검증.
- 실장비 검증: Chrome/Edge 데스크톱을 초기 대상 후보로 두고 Windows/macOS 각각 탭 오디오·마이크·코덱 지원을 확인. 지원표가 확정되기 전 다른 브라우저를 지원한다고 표시하지 않음.
- 최종 검토: 영향 파일 범위 lint·typecheck·관련 테스트, guardian-gate 리뷰. 실제 장비 기록과 함께 제한 배포 후 확대.
| 검증 사례 | 합격 기준 · 제안 |
|---|---|
| 4:1 카드뷰·포커스뷰·두 뷰 전환, 영상·AI·학생 발화, 60분 녹화 | 전체 영상 재생 가능, 뷰 전환 전후 연속 기록·끝부분 포함, 오디오 중복·녹화로 추가된 누락 없음. 시작·중간·끝 A/V 오차 절대값 200ms 이내 목표. |
| 녹화 OFF/ON 동일 시나리오 각각 3회 | 대표 저사양 PC에서 수업 영상 dropped-frame 비율 증가 2%p 이내, 조작 p95 지연 증가 50ms 이내 목표. CPU/GPU·long task·오디오 상태도 기록. 합격값은 실제 baseline 후 승인. |
| 장시간 메모리·업로드 | 메모리 버퍼 예산 준수, 시간에 비례하는 RAM 증가 없음. 임시 디스크 증가는 미업로드 데이터량으로 설명 가능. |
| 네트워크 5분 단절·복구 | 임시 공간이 충분하면 청크 보존·재전송·완성 영상 정상. 부족하면 안전 종료·부분 녹화 안내. |
| 공유 중단·경로 이탈·탭 닫기·재접속 | 정상 stop은 마지막 청크 보존. 강제 이탈은 불완전 표시·저장된 청크 복구. 공유 수업 트랙과 세션 연결 유지. |
| 잘못된 공유 대상·오디오 미선택·권한 취소 | 완전한 녹화처럼 표시하지 않고 재선택·지원 안내. 장치나 권한 없이 UI fixture 검증 가능. |
07 · 확인된 근거와 미확정 항목
2026-10-12 저장소의 카드뷰 페이지, 그룹 화면, Mediasoup 수신 스트림과 서버 RecordingManager를 조회했다. 이 문서 작업에서 애플리케이션 코드는 수정하지 않았다. 녹화 프로토타입·성능 테스트·브라우저 호환 테스트는 아직 실행하지 않았다.
미확정: 실제 진행자 장비·브라우저 분포, 마이크 포함 기본값, 탭 음소거/출력 장치별 캡처 동작, 사용 가능한 저장 API, 인코더 가속 여부, 보존 기간·열람 정책. 성능 기준은 위 제안값으로 검토 후 확정한다.