작성 요령
- 새 이슈는 마지막 번호 +1로 추가
- 결정 완료 시 →
livepack-v4-issues-done.md로 이동 (결론 요약 포함)- 공유 완료 / 결정 대기 시 →
livepack-v4-issues-pending.md로 이동- 이동 후 이 문서에서 해당 항목 삭제, 남은 항목 번호 순서대로 리셋
- 이동 대상 문서에서도 번호는 기존 마지막 번호 +1로 순서대로 부여
IVS Chat에는 ChatRoom 전체 연결 해제 API가 없음. DisconnectUser는 userId를 지정해야 하고 한 명씩만 가능.
| 방안 | 설명 | 장단점 |
|---|---|---|
| A: 클라이언트 처리 | 플레이어가 방송 종료 감지 → WebSocket 직접 닫음 | 추가 API 불필요, 클라이언트 의존 |
| B: SendEvent 신호 | 방송 종료 webhook에서 SendEvent("app:live:ended") 전송 → 클라이언트 수신 후 닫음 |
서버에서 신호, 더 확실 |
status !== LIVE이면 거부되므로 재접속은 불가session_duration_minutes, 기본 60분) 시 WebSocket 자동 종료 → 채팅 끊김chatToken(AES-256 암호화, 만료 24시간)으로 IVS 토큰을 재발급받을 수 있으나, 재발급 API가 없음/api/live/chat/token) — player는 HTML 응답 전용이므로 API는 player-api에서 처리DynamoDB LiveWatchesManager-V4의 동시 시청자(cvc)가 시청자 전원 이탈 시 0으로 내려가지 않음.
Flink 10초 텀블링 윈도우에서 이벤트가 0건이면 DynamoDB에 쓸 데이터가 없어 업데이트가 발생하지 않음.
Peak Lambda(peak_{beid})도 동일 — 이벤트 기반이라 이벤트 없으면 트리거 안 됨.
| 방안 | 설명 | 장단점 |
|---|---|---|
| A: Flink 타이머 | 윈도우에 이벤트 없으면 0으로 PUT | Flink 수정 필요, 가장 정확 |
| B: CMS 클라이언트 | 일정 시간(30초) 값 변화 없으면 0 표시 | Flink 수정 불필요, 부정확할 수 있음 |
| C: Lambda TTL | DynamoDB TTL 설정 + 조회 시 만료 확인 | 삭제까지 지연 있음 |
timeoutSeconds 내 재연결 시 같은 녹화로 병합_sync)에서 disconnected 감지 즉시 방송 종료 처리 → 재연결 시 새 이벤트로 분리됨webhook/cloudflare.js, _sync (Workers Cron), _status (CMS 호출)live_input.connected/disconnected 이벤트를 실시간 수신 가능$20/월)wecandeo.net)이 Free 플랜 → 사용 불가| 방안 | 설명 | 장단점 |
|---|---|---|
A: Workers Cron + _sync 대기 |
disconnected 시 disconnected_at 기록, 다음 polling에서 timeout_seconds 경과 확인 후 종료 |
무료, 최대 1분 지연 (polling 주기), polling 주기 < timeout이면 부정확 |
| B: CF Notifications Webhook | connected/disconnected 실시간 수신 → 정확한 timeout 대기 |
Pro 플랜 필요 ($20/월), 실시간 |
timeout_seconds: 30초, Workers Cron polling 주기: 1분webhook/cloudflare-live.js)는 구현 완료, disconnected_at 컬럼 추가 완료방안 B 채택 — 2026-09-21 Pro 플랜 업그레이드 후 Notifications webhook 동작 확인. destination …/webhook/cloudflare/live, 정책 live-webhook, cf-webhook-auth 검증 추가
실측: connected/disconnected 즉시 수신, video.ready는 끊김 + timeoutSeconds(30초) 뒤 도착 → 우리 확정 종료와 항상 경합
수정: disconnected_at 기록은 webhook 전담. _sync(Workers Cron)·_status(CMS 폴링)는 disconnected_at + timeout_seconds 경과 시에만 endLiveStream(endTime=disconnected_at) (타이머·안전망). video.ready는 채널의 RECORDING 행에만 매칭, fallback 제거 (이전 방송 녹화 덮어쓰기 사고 재발 방지)
Workers Cron(1분) 제거 → EventBridge Scheduler wcdLivepackLiveSync (5분) 로 안전망 통합 (2026-09-22). livepack-api channel.syncAll()이 IVS + CF 채널을 프로바이더 API로 대조:
disconnected_at 없으면 프로바이더 시각(CF statusEnteredAt)으로 보정 기록 → 상한(CF timeout_seconds, IVS 녹화 300초, IVS 녹화 없음 즉시) 경과 시 endLiveStream(endTime=disconnected_at)확정 종료 지연은 CMS 열려 있으면 _status 폴링이, 아니면 최대 5분 뒤 Scheduler가 처리. end_time은 disconnected_at 기준이라 duration 영향 없음
timeoutSeconds 초과 재연결 실측 (2026-09-22, 채널 15): 끊김 01:29:11 → video.ready#1 01:29:50 (CF가 첫 구간을 별도 영상으로 확정) → 재연결 01:29:55 → 종료 01:30:38 → video.ready#2 01:31:15. 즉 CF는 유예 시간 초과 시 영상을 분리한다
connected가 "already LIVE"로 이벤트를 유지하고 새 RECORDING 행을 안 만듦 → video.ready#2가 "RECORDING row not found"로 유실 (video e4d1b57848d1d39c83f4818d99dfeb4c, 37초)handleConnected에서 LIVE + disconnected_at + timeout_seconds 경과면 IVS 유예 시간 초과와 동일하게 endLiveStream(endTime=disconnected_at) 후 새 이벤트 + 새 RECORDING 행 생성. 유예 시간 이내면 기존대로 유지video.ready 매칭을 "최신 RECORDING 행" → video.created에 start_time이 가장 가까운 RECORDING 행(findRecordingByChannelIdNearest)으로 변경. 실측 created는 우리 connected 처리보다 약 10초 먼저 찍힘 (00:48:03 vs 00:48:14). CF RECORDING 행 INSERT 시 start_time(Notifications updated_at) 기록유예 시간 이내 재연결 실측 (2026-09-22, 채널 15, v72 배포 후): 끊김 01:48:44 → 재연결 01:49:01 (11초) → reconnect recovered (waited 11/30s), 이벤트·녹화 행 유지 → 종료 01:50:44. 우리 쪽은 정상
d1b4395f…가 끊김 후 live-inprogress로 18분 멈춤 (modified 갱신 없음). 같은 채널에서 방송을 다시 시작한 시점(02:08:52 새 영상 생성)에야 이전 영상이 확정됨(readyToStreamAt 02:08:40). 과거 55건은 영상 길이와 무관하게 끊김 후 30~45초 확정 → 이 건만 비정상. 이 영상만 유예 시간 이내 재연결이 있었음 (샘플 1건, 인과 미확정, 재현 테스트 필요)RECORDING 상태로 장시간 남은 행 감지·알림유예 시간 이내 재연결 반복 테스트 (2026-09-22, v72) — 우리 쪽 처리는 4건 모두 정상. CF 확정 소요는 일관성 없음:
| 이벤트 | 재연결 | GOP 에러 | 끊김 → video.ready |
비고 |
|---|---|---|---|---|
| 116 | 11초 | 있음 | 18분 (다음 방송 시작 시 확정) | PRISM Simple |
| 118 | 19초 | 있음 | 10분 (다음 방송 시작 시 확정) | PRISM Simple |
| 119 | 28초 | 있음 | 3분 21초 (자체 확정) | 키프레임 2초 설정 |
| 120 | 15초 | 있음 | 34초 (정상) | 키프레임 2초 + PRISM 재시작 |
video.ready가 새 방송 RECORDING 행 생성 0.7초 뒤에 도착 → created 최근접 매칭이 이전 이벤트(118)에 정확히 붙임 (구 "최신 행" 로직이면 오매칭)syncAll이 webhook보다 먼저 끊김을 감지한 경합 발생(04:23:51 vs 04:23:55) → CF statusEnteredAt으로 보정 기록, webhook은 원본 유지. 설계대로 동작live-inprogress 고착, video.ready 미도착 또는 1시간 뒤 도착, "될 때도 있고 안 될 때도 있음"). 해결책 없음 → CF 지원 문의 대상ERR_GOP_OUT_OF_RANGE: 5세션 연속 방송 시작 +107120초에 1회 발생. 키프레임 2초 고정 + PRISM 재시작 후에도 동일 → PRISM 설정과 무관하거나 미반영. CF 문서: 키프레임 28초 고정(automatic/variable 금지), 대시보드 Live inputs → Metrics 탭 키프레임 그래프가 "일정한 수평선"이어야 함. 확정 지연과의 인과는 없음(에러 있어도 재연결 없으면 정상 확정)
유예 시간 초과 재연결 검증 완료 (2026-09-22 05:02, v72): 끊김 05:01:37 → video.ready#1 05:02:13 (rec 180 ↔ ev 122) → 재연결 05:02:22 (45초) → reconnected after timeout (39/30s) → ev 122 종료(end_time=05:01:37) + ev 123 / rec 181 생성 → 종료 05:04:41 → video.ready#2 05:05:18 (rec 181 ↔ ev 123). 이벤트 2건 / 녹화 2건 분리, 유실 없음
유예 시간 이내 재연결 추가 2건(121: 25초, 122 이전 세션 120: 24초)도 정상 확정. 재연결 세션 6건 중 고착 2건(116·118)은 오전에만 발생, 이후 재현 안 됨
8d9766c9…, 고착 영상 uid·시각 첨부)RECORDING 상태 장기 잔류 행 감지·알림 (CF 고착 vs 우리 버그 구분용)RECORDING 행은 start_time이 null → findRecordingByChannelIdNearest에서 후순위. 같은 채널에 새 행이 있으면 오매칭 가능하므로 배포 시점에 걸쳐 있던 방송은 확인 필요timeout_seconds 확정 (현재 30초)livepack-cf-stream-sync 삭제 필요 (npx wrangler delete --name livepack-cf-stream-sync) — 코드는 제거됨, 배포된 Worker는 수동 삭제video.ready가 없으므로 확정 종료가 Scheduler(최대 5분)에만 의존Session Ended → Stream End가 7초 내 즉시 발생. recordingReconnectWindowSeconds는 Stream End를 지연시키지 않음Recording End만 recordingReconnectWindowSeconds 이상 지연됨 (문서: "delayed by at least recordingReconnectWindowSeconds"). 테스트에서는 끊김 후 약 90초Stream Start 발생. 녹화는 같은 세션으로 병합되고 Recording End의 recording_session_stream_ids에 병합된 stream_id 목록이 담김Starvation Start/End는 detail-type이 IVS Stream Health Change — 현재 EventBridge Rule이 구독하지 않아 수신 안 됨 (데이터 부족 신호라 끊김 판단에는 미사용)_status 폴링(GET /api/channel/status)이 Stream End보다 먼저 끊김을 감지해 종료 처리하던 경로도 존재| 이벤트 | 처리 |
|---|---|
Stream End (녹화 채널) |
live_channel.disconnected_at = NOW()만 기록, 종료 처리 안 함 |
Stream End (녹화 없음) |
Recording End가 오지 않으므로 즉시 확정 종료 (기존 동작) |
Stream Start (LIVE + 진행 중 이벤트 존재) |
재연결 → 새 live_event 만들지 않고 provider_event_id를 새 stream_id로 교체, disconnected_at 초기화 |
Recording Start (RECORDING 녹화 존재) |
병합 세션이므로 live_recording 중복 INSERT 안 함 |
Recording End / Recording End Failure |
disconnected_at이 있으면 확정 종료. end_time/edate/DynamoDB endDate = disconnected_at (대기 시간 duration 미포함) |
_status 폴링 (IVS 녹화 채널, DB LIVE, 프로바이더 IDLE) |
종료하지 않고 disconnected_at 기록, reconnecting: true 응답. 300초 넘게 Recording End가 안 오면 강제 종료 (안전장치) |
utils/endLiveStream.js3elOeyh4O4OJ (reconnect window 30초), 채널 23 적용, 템플릿 IvsRecordingConfigurationArn 갱신2026-09-21 테스트는 전부 v67(Lambda 49) 에서 실행됨 — prefix 기준 매칭(v68, Lambda 50 06:14 UTC publish) 이전. 26초 재연결(ev 106)에서 IVS가 병합하지 않고 새 prefix 세션을 열었는데 구 로직이 기존 행(163)에 붙여 두 번째 세션 녹화(332초)가 유실됐을 가능성 있음. 6초 재연결(ev 107)은 병합돼 정상
2026-09-22 유예 시간 이내 재연결 (v72, 채널 23, ev 124): Recording Start가 Stream Start보다 2초 먼저 도착 → 재시도 루프로 ev 124에 rec 182 INSERT → 끊김 05:15:39 → 재연결 05:15:53 (14초, reconnected, stream_id 교체) → IVS 병합 (Recording Start 없음) → 종료 05:18:16 → Recording End 05:19:46 (끊김 +90초, session_ids 2개, 234초) → end_time=05:18:16 종료, rec 182 변환 큐 등록. 이벤트 1 : 녹화 1, 정상
2026-09-22 유예 시간 초과 재연결 (v72, ev 125): 끊김 05:23:29 → 재연결 05:24:09 (40초) → reconnected (이벤트 유지, 설계와 다름) → 새 prefix Recording Start → rec 184 INSERT → Recording End #1 05:24:59 (끊김 +90초, "keep live", rec 183 큐 등록) → 종료 05:25:14 → Recording End #2 05:26:44 → ev 125 종료, rec 184 큐 등록. S3에 prefix 2개(58초·62초) 각각 완전. 이벤트 1 : 녹화 2, 유실 없음
disconnected_at + live_channel_ivs.recording_reconnect_window(0이면 기본 30초, CF의 timeout_seconds || 30과 같은 패턴. 컬럼명은 프로바이더 옵션명 유지) 경과면 endLiveStream(endTime=disconnected_at) 후 새 이벤트. 이어 오는 새 prefix Recording Start는 새 이벤트에 붙음. Recording End는 prefix의 녹화가 진행 중 이벤트 소속이 아니면(이미 종료된 이전 이벤트) 이벤트를 건드리지 않고 녹화 행만 정리유예 시간 초과 재연결 검증 완료 (2026-09-22 05:53, v73 / Lambda 54, ev 126→127): 끊김 05:52:24 → 재연결 05:53:08 (44초) → reconnected after window (44/30s) → ev 126 종료(end_time=05:52:24) + ev 127 / rec 186 생성 → Recording End #1 05:53:54 → prefix belongs to ended event_id=126 (active=127), keep live, rec 185 큐 등록 → 종료 05:55:17 → Recording End #2 05:56:47 → ev 127 종료, rec 186 큐 등록. 이벤트 2건 / 녹화 2건, CF와 동일. webhook 인라인 쿼리 → repository 이관 후 회귀 없음
live_channel_ivs.recording_reconnect_window가 0으로 저장됨 (channel.js가 값을 안 넘김) → RecordingConfiguration 값과 맞춰 저장하도록 보완 필요 (현재는 0이면 기본 30초)recording_session_id 저장 검토)alarm)과 유형(alarm_type)은 videopack DB에만 있었고, livepack은 같은 테이블에 product_type_id=2로 저장lib/video.js가 Secrets Manager 우회로 videopack DB 접속trg_account_insert 트리거로 자동 등록. V4는 account/videopack/livepack이 별도 클러스터라 트리거 불가 → videopack은 admin productupdate + 서브계정 생성 시 앱 코드로 보정, livepack은 아무것도 없음_SERVICE/_alarm_service는 product_type을 받지만 조회는 전부 videopack 테이블(//TODO 추후 Livepack 지원), alarm-application(Procedure)도 product_type 미전달5000 방송시간 사용량의 자식 5001~5012가 실제로는 시스템 메일(회원가입·결제 안내). 방송시간 하위 유형 자리 없음alarm / alarm_type 테이블 신설 (videopack과 동일 구조, product_type_id=2 고정)repository/livepack/alarm/{index,type}.js 신설 — getAlarmList(uid), getListByGid(gid, typeId?), insert(gid, uid, typeId), insertAll(gid, uid), deleteType(uid), DEFAULT_ALARM_TYPES, type.getAlarmTypeList(), type.getByIds(ids)repository/alarm/{index,type}.js → wecandeo-db repository.livepack.alarm 위임 래퍼로 교체 (videopack DB 직접 쿼리 제거 → alarm 관련 Secrets Manager 우회 접속 제거)service/alarm.js[1000, 6000] + MediaLive 채널 보유 시 3000 (방송시간 유형 5000 → 6000)deleteType(uid) 후 선택 유형 insert(gid, uid, typeId). gid는 auth 토큰, uid는 본인 gid 소속 관리자만, 유형은 노출 유형(1000/3000/6000)으로 제한checked=false로 반환 (조회 시 lazy 등록 안 함)channel.getMedialiveCountByGid, account.getAdminList(신설) 사용service/player.js의 repository.videopack.cdn.service.get(gid) 1곳 (alarm 무관, 유지)alarm / alarm_type DDL + 유형 시드(1000/3000/6000/6001/6002)insertAll(gid, uid) 호출 (Owner 계정), 서브관리자 생성 시 보정product_service 보유 gid 대상 1회성 insertAll 스크립트 필요 여부 결정product_type=2일 때 livepack DB alarm/product_service 조회 분기 + 템플릿 LIVEPACK_* env, 만료(1000) 메일 발송 경로(livepack 프로시저 → lambda_sync 가능 여부)