웹 브라우저 스트리밍 접근 제어 (Web Streaming)
문서 정보
- 작성일: 2026-03-09
- 최종 업데이트: 2026-03-09
- 버전: v2.1.0 (v8 정책 + 헤더 기반 판별 반영)
웹 브라우저 영상 스트리밍은 PRO 멤버십 전용입니다. 모바일 앱에서는 모든 멤버십 티어에서 영상 시청이 가능합니다. WEB_STREAMING: { enabled: boolean } 으로 제어됩니다.
목차
핵심 개념
WEB_STREAMING Feature Policy는 사용자의 멤버십 등급(passCode)에 따라 웹 브라우저에서의 영상 스트리밍 시청 가능 여부를 제어합니다.
| 용어 | 설명 |
|---|---|
| WEB_STREAMING | FeaturePolicy의 root-level boolean 플래그 |
| 플랫폼 | 클라이언트 유형 — WEB(웹 브라우저), MOBILE_APP(모바일 앱) |
| WebStreamingGuard | 웹 스트리밍 접근을 검증하는 NestJS Guard |
왜 필요한가?
PRO 멤버십의 차별화 기능으로 웹 브라우저 영상 시청을 제공합니다. 모바일 앱은 모든 티어에서 시청 가능하지만, 웹은 PRO만 가능합니다.
정책 구조
// FeaturePolicy 내 다른 boolean 플래그와 동일한 패턴
WEB_STREAMING: {
enabled: boolean;
}
패스별 정책
| 패스 | WEB_STREAMING | 설명 |
|---|---|---|
| FREE | false | 웹 스트리밍 불가 |
| PLUS | false | 웹 스트리밍 불가 |
| PRO | true | 웹 스트리밍 가능 |
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.ts | GET /tab-session/events | SSE 탭 세션 연결 |
user-schedule.controller.ts | GET /user-schedules/:uuid/videos/:timeSlot | 영상 URL |
my-kok.controller.ts | GET /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 연결 시) |
향후 확장
웹 제한이 필요한 새 기능이 추가될 경우:
pass.core.ts에FEATURE_KEY.WEB_*상수 추가 (예:WEB_UPLOAD)pass-policy.constants.ts에서 패스별 정책 추가- 전용 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) - 다중 탭 제한