JKVideo는 React Native + Expo로 개발된 서드파티 Bilibili(중국 최대 동영상 플랫폼) 클라이언트로, DASH 적응형 스트리밍, 실시간 탄막(danmaku), WBI 서명, 라이브 스트리밍을 지원하며 Android/iOS/Web 크로스플랫폼으로 동작합니다. 97.3% TypeScript로 작성된 타입 안전 모바일 아키텍처의 실전 레퍼런스입니다.
Bilibili는 중국의 유튜브입니다. 공식 앱은 광고가 많고 무겁습니다. JKVideo는 공식 앱이 하는 일을 오픈소스로 다시 만든 것인데, 단순 클론이 아니라 DASH 스트리밍, 탄막, 오프라인 다운로드, LAN 공유까지 갖춘 기능 완성형 앱입니다.
특히 WBI 서명이라는 Bilibili의 API 보안 메커니즘을 외부 라이브러리 없이 순수 TypeScript로 역공학한 부분이 가장 큰 학습 가치를 가집니다.
React Native 0.83 + Expo SDK 55 + React 19 조합으로 실제 동작하는 앱을 구현한 오픈소스 레퍼런스가 극히 드뭅니다. DASH MPD, WBI 서명 같은 복잡한 기술을 다루는 예제도 없었습니다.
97.3% TypeScript, React 19 최신 훅, expo-router v4 파일 기반 라우팅, Zustand 8개 분리 스토어 —실전 앱이면서 학습 자료로도 훌륭합니다.
| 비교 대상 | JKVideo 장점 |
|---|---|
| Bilibili 공식 앱 | 광고 없음, 오픈소스, 경량화 |
| 기존 RN 비디오 앱 | DASH MPD 직접 생성, ExoPlayer 네이티브 디코딩 |
| WebView 기반 래퍼 | 네이티브 비디오 플레이어로 고품질(4K HDR/Dolby) 지원 |
| Flutter 비디오 앱 | Expo 생태계 활용, 웹 플랫폼 동시 지원 |
| 기술 | 버전 | 역할 |
|---|---|---|
| React Native | 0.83.2 | 크로스플랫폼 모바일 UI |
| React | 19.2.0 | UI 라이브러리 (최신 훅 포함) |
| Expo SDK | 55 | 빌드/배포/네이티브 모듈 관리 |
| TypeScript | 5.9.2 | 정적 타입 시스템 (97.3%) |
| 기술 | 역할 |
|---|---|
| expo-router v4 | 파일 시스템 기반 라우팅 (Next.js App Router 스타일) |
| Zustand 5.x | 경량 전역 상태관리 (8개 분리 스토어: auth·download·live·settings·video·playProgress·playUrlCache·visibleBigKey) |
| AsyncStorage | 로컬 데이터 영속화 |
| 기술 | 역할 |
|---|---|
| react-native-video 6.x | 네이티브 비디오 플레이어 (ExoPlayer/AVPlayer) |
| react-native-webview | 웹 폴백 플레이어 |
| react-native-pager-view | 스와이프 탭 네비게이션 |
| react-native-qrcode-svg | QR 코드 생성 (LAN 공유) |
| 기술 | 역할 |
|---|---|
| Axios | HTTP 클라이언트 (인터셉터로 쿠키/WBI 자동 주입) |
| pako | zlib 압축 해제 (탄막 XML 디코딩) |
| @sentry/react-native | 에러 모니터링 |
| react-native-static-server | LAN 파일 서빙 (내장 HTTP 서버) |
모든 API 호출이 services/bilibili.ts 단일 파일에 집중됩니다. Axios 인터셉터로 모든 요청에 인증 정보를 자동 주입합니다.
// 요청 인터셉터 — 모든 API 호출에 쿠키 자동 주입
api.interceptors.request.use(async (config) => {
const sessdata = await getSecure('SESSDATA'); // 네이티브: expo-secure-store, 웹: AsyncStorage 폴백
if (Platform.OS === 'web') {
config.headers['X-Sessdata'] = sessdata;
} else {
config.headers['Cookie'] = `SESSDATA=${sessdata};`;
}
return config;
});
Bilibili 웹 API 보안 메커니즘을 외부 crypto 의존성 없이 순수 TypeScript MD5로 구현합니다.
Bilibili API가 반환하는 비디오/오디오 분리 스트림을 로컬에서 MPD XML로 조합하여 ExoPlayer에 제공합니다. 스트리밍 프로토콜을 직접 구현하는 드문 사례입니다.
| 기능 | 네이티브 (Android/iOS) | 웹 |
|---|---|---|
| 인증 | Cookie 헤더 직접 삽입 | 커스텀 X-Sessdata 헤더 + 프록시 |
| 비디오 | react-native-video (DASH) | WebView HTML5 video (MP4 폴백) |
| 탄막 압축 | pako.inflate → inflateRaw | 프록시가 서버에서 해제 |
| 이미지 | 직접 로드 | localhost:3001 프록시 (안티핫링크 우회) |
고빈도 업데이트(progress)는 AsyncStorage 쓰기를 하지 않고, 상태 변화(status/localUri)만 영속화하는 선택적 영속화 패턴을 적용합니다.
expo-router v4의 동적 라우팅(app/video/[bvid].tsx), React 19 최신 훅 사용 패턴, EAS Build로 클라우드 빌드 파이프라인 구축법을 배웁니다.
DASH 프로토콜 작동 원리, MPD XML 명세 구조, 비디오/오디오 분리 스트림을 MPD로 합치는 기법, react-native-video의 ExoPlayer/AVPlayer 백엔드 활용, 화질별 스트림 선택 로직을 배웁니다.
Bilibili WBI 서명 알고리즘 전체 구조, 순열 테이블 기반 키 파생, 순수 TypeScript MD5(외부 의존성 없이 bitwise 연산), 12시간 TTL + stale 폴백 캐싱 전략을 배웁니다.
XML 기반 탄막 파싱, pako를 이용한 zlib 압축 해제, 5레인 스크롤 탄막 렌더링(타임라인 동기화), 라이브 탄막 1.5초 폴링, 2000개 초과 시 자동 프루닝으로 메모리 관리합니다.
expo-file-system 기반 백그라운드 다운로드, 다운로드 큐 관리, react-native-static-server로 내장 HTTP 서버 구동, LAN QR 코드 공유 패턴을 배웁니다.
| 항목 | 최소 | 권장 |
|---|---|---|
| Node.js | 18.x | 20.x+ |
| RAM | 4GB | 8GB+ |
| 디스크 | 5GB | 10GB+ |
| OS | macOS/Windows/Linux | macOS (iOS 빌드) |
| Android | API 24+ (Android 7.0) | API 30+ |
| iOS | iOS 15+ | iOS 17+ |
expo-router + Zustand로 간단한 비디오 브라우저를 만들어봅니다.
npx create-expo-app@latest --template tabs
동적 라우트([id].tsx) 구현, Zustand 스토어 1개로 검색 히스토리 관리, 탭 네비게이션 + 동적 상세 페이지 + 상태 영속화까지 완성이 목표입니다.
JKVideo의 buildMpd.ts를 이해하고 다중 화질 Representation 지원을 추가합니다. Node.js로 테스트 MPD 파일을 생성해 VLC/mpv로 재생해봅니다.
wbi.ts의 MD5 구현을 한 줄씩 따라 치며 bitwise 연산을 이해합니다. RFC 1321 테스트 벡터로 MD5 결과를 검증하고, 실제 Bilibili nav API를 호출해 WBI 키를 추출합니다.
탄막 XML 파서 구현, Animated API로 수평 스크롤 애니메이션, 5레인 충돌 회피 알고리즘, 비디오 타임라인 동기화. 100개+ 탄막이 끊김 없이 스크롤하면 완성입니다.
react-native-static-server로 내장 HTTP 서버를 구동하고, 기기 IP+포트로 QR 코드를 생성합니다. 같은 네트워크 PC 브라우저에서 모바일 다운로드 비디오를 스트리밍 재생하면 완성입니다.
| 키워드 | 설명 |
|---|---|
| DASH | Dynamic Adaptive Streaming over HTTP. 비디오/오디오 분리 스트림으로 전송, 네트워크 상황에 따라 화질을 동적 전환하는 프로토콜 |
| MPD | Media Presentation Description. DASH의 XML 매니페스트. 사용 가능한 스트림의 URL/코덱/대역폭 정보 기술 |
| ExoPlayer | Google의 Android용 미디어 플레이어. DASH/HLS/SmoothStreaming 등 적응형 스트리밍을 네이티브 지원 |
| 탄막 (Danmaku) | 弾幕. 비디오 위에 떠다니는 실시간 댓글 시스템. 니코니코 동화에서 시작, Bilibili에서 대중화 |
| WBI | Web-API Encryption. Bilibili 웹 API의 요청 서명 메커니즘. MD5 해싱으로 위변조 방지 |
| SESSDATA | Bilibili의 세션 쿠키. 로그인 시 발급, 이후 모든 인증 API 요청에 사용 |
| pako | JavaScript용 zlib 구현. inflate/deflate 압축을 브라우저/Node.js에서 처리 |
| Zustand | 독일어로 "상태". 경량 React 상태관리. Redux 대비 보일러플레이트 적고 훅 기반 |
| expo-router | Expo의 파일 시스템 기반 라우팅. app/ 디렉토리 구조가 곧 URL 경로 |
| HLS | HTTP Live Streaming. Apple의 적응형 스트리밍. DASH와 경쟁, iOS 네이티브 지원 |
| 안티핫링크 | Referer 검증으로 외부 사이트에서의 직접 이미지 링크를 차단하는 기법 |
| EAS Build | Expo Application Services. 클라우드에서 Android APK/iOS IPA를 빌드하는 CI 서비스 |