본문으로 건너뛰기

웹 브라우저 스트리밍 접근 제어 (Web Streaming)

문서 정보

  • 작성일: 2026-03-09
  • 최종 업데이트: 2026-03-09
  • 버전: v2.1.0 (v8 정책 + 헤더 기반 판별 반영)

TL;DR

웹 브라우저 영상 스트리밍은 PRO 멤버십 전용입니다. 모바일 앱에서는 모든 멤버십 티어에서 영상 시청이 가능합니다. WEB_STREAMING: { enabled: boolean } 으로 제어됩니다.


목차

  1. 핵심 개념
  2. 왜 필요한가?
  3. 정책 구조
  4. 동작 방식
  5. 구현 구조
  6. 다중 탭 제한과의 관계
  7. 향후 확장
  8. FAQ

핵심 개념

WEB_STREAMING Feature Policy는 사용자의 멤버십 등급(passCode)에 따라 웹 브라우저에서의 영상 스트리밍 시청 가능 여부를 제어합니다.

용어설명
WEB_STREAMINGFeaturePolicy의 root-level boolean 플래그
플랫폼클라이언트 유형 — WEB(웹 브라우저), MOBILE_APP(모바일 앱)
WebStreamingGuard웹 스트리밍 접근을 검증하는 NestJS Guard

왜 필요한가?

PRO 멤버십의 차별화 기능으로 웹 브라우저 영상 시청을 제공합니다. 모바일 앱은 모든 티어에서 시청 가능하지만, 웹은 PRO만 가능합니다.


정책 구조

// FeaturePolicy 내 다른 boolean 플래그와 동일한 패턴
WEB_STREAMING: {
enabled: boolean;
}

패스별 정책

패스WEB_STREAMING설명
FREEfalse웹 스트리밍 불가
PLUSfalse웹 스트리밍 불가
PROtrue웹 스트리밍 가능
설계 원칙

DOWNLOAD: { enabled: boolean }, WORKOUT_CONDITION: { enabled: boolean } 등 기존 FeaturePolicy 패턴과 100% 동일합니다. 업계 표준(Netflix/Spotify)의 tier-based boolean entitlement 패턴을 따릅니다.


동작 방식

플랫폼 판별

x-token-guard-key 헤더 존재 여부 + 값 검증으로 서버가 자동 판별합니다. 클라이언트의 추가 작업은 필요 없습니다.

조건판별 결과
x-token-guard-key 헤더 값 일치모바일 앱 → 통과
x-token-guard-key 헤더 없음 또는 값 불일치웹 브라우저 → PRO 체크
왜 헤더 기반인가?

SSE(EventSource)는 커스텀 헤더 전송을 지원하지 않습니다. 모바일 앱은 이 헤더를 항상 전송하므로, 헤더 존재 여부만으로 앱/웹을 확실히 구분할 수 있습니다. 또한 헤더 값을 서버의 TOKEN_GUARD_KEY와 비교하여 임의의 값으로 앱을 사칭하는 것도 차단합니다. 기본값이 WEB(제한적)이므로 우회가 구조적으로 불가능합니다.

검증 흐름

1. @RequiresWebStreaming() 데코레이터 적용
2. WebStreamingGuard 실행
3. x-token-guard-key 헤더 값을 TOKEN_GUARD_KEY와 비교
4. 값 일치(MOBILE_APP) → 무조건 통과 (DB 조회 없음)
5. 값 불일치 또는 헤더 없음(WEB) → Entitlement 조회 → passCode 결정
6. getFeatureDefault(WEB_STREAMING, passCode).enabled 확인
7. false이면 403 Forbidden 반환

접근 제어 매트릭스

멤버십웹 스트리밍앱 스트리밍
FREE
PLUS
PRO

구현 구조

SSOT (Single Source of Truth)

src/modules/pass/constants/pass-policy.constants.ts
→ PASS_FEATURE_POLICIES[passCode][FEATURE_KEY.WEB_STREAMING]

Guard + Decorator

src/modules/usage-control/guards/platform-access.guard.ts    — WebStreamingGuard
src/modules/usage-control/guards/platform-access.decorator.ts — @RequiresWebStreaming()

적용 엔드포인트

컨트롤러엔드포인트보호 대상
tab-sse.controller.tsGET /tab-session/eventsSSE 탭 세션 연결
user-schedule.controller.tsGET /user-schedules/:uuid/videos/:timeSlot영상 URL
my-kok.controller.tsGET /my-koks/:uuid마이콕 영상 URL

사용 예시

@RequiresWebStreaming()
@Sse('events')
async events() { ... }

@RequiresWebStreaming()
@Get(':userScheduleUuid/videos/:timeSlot')
async getScheduleVideos() { ... }

에러 응답

{
"statusCode": 403,
"message": "웹 브라우저 시청은 PRO 멤버십 전용입니다",
"errorCode": "WEB_STREAMING_NOT_ALLOWED"
}

다중 탭 제한과의 관계

웹 스트리밍 접근 제어와 다중 탭 제한은 같은 SSE 엔드포인트(/tab-session/events)에서 함께 동작합니다.

요청 → WebStreamingGuard (웹 스트리밍 권한 검증) → TabLimitService (동시 탭 수 검증) → SSE 스트림
기능역할시점
Web Streaming웹 스트리밍 허용 여부Guard (SSE 연결 전)
Multi-Tab동시 탭 수 제한Service (SSE 연결 시)

향후 확장

웹 제한이 필요한 새 기능이 추가될 경우:

  1. pass.core.tsFEATURE_KEY.WEB_* 상수 추가 (예: WEB_UPLOAD)
  2. pass-policy.constants.ts에서 패스별 정책 추가
  3. 전용 Guard + Decorator 생성 또는 기존 패턴 확장

현재는 WEB_STREAMING 하나만 필요하므로 단순 boolean으로 충분합니다. 웹 제한 기능이 3개 이상 늘어나는 시점에서 플랫폼별 객체(web: { streaming, upload, chat })로 리팩토링을 검토합니다.


FAQ

Q1: 기존 모바일 앱은 영향받나요?

A: 아닙니다. 앱은 이미 x-token-guard-key 헤더를 전송하고 있으므로 자동으로 MOBILE_APP으로 판별되어 Guard를 건너뜁니다. DB 조회도 발생하지 않습니다.

Q2: v7 action-based에서 왜 boolean으로 변경했나요?

A: v7은 login, payment, streaming 3개 액션을 정의했지만, Guard가 실제로 검증하는 것은 streaming 하나뿐이었습니다. 사용되지 않는 액션을 위한 복잡한 구조는 YAGNI 위반이므로, 업계 표준(Netflix/Spotify)과 동일한 boolean entitlement 패턴으로 단순화했습니다.

Q3: DOWNLOAD과 뭐가 다른가요?

A: 구조적으로 동일합니다. 둘 다 { enabled: boolean } 패턴이며, 패스별로 허용 여부가 다릅니다. 차이는 Guard가 플랫폼(WEB/MOBILE_APP)을 추가로 확인한다는 점뿐입니다.


관련 문서

  • ADR-017: 웹 브라우저 영상 스트리밍 접근 제어 (docs/maintained/pass/adr/017-platform-access-web-streaming.md)
  • ADR-007: 헤더 기반 플랫폼 판별 (docs/maintained/usage-control/adr/007-web-streaming-header-based-detection.md)
  • Pass Policy SSOT (docs/maintained/pass/PASS-POLICY-SYNC-STRATEGY.md)
  • 다중 탭 제한