애드온 시스템 하나로 P2P, HTTP 스트림, 자막, 메타데이터를 전부 흡수하는 오픈소스 미디어 센터. 10.8k 스타, GPL-2.0, 2017년부터 7년째 살아남은 풀스택 웹앱의 구조를 한 번에 훑는다.
"한 마디로 무엇을 하는 프로젝트인가."
Stremio Web은 React 18 기반 웹앱이지만, 비즈니스 로직(라이브러리, 카탈로그, 인증, 애드온 통신)은 전부 Rust로 짠 stremio-core를 WebAssembly로 컴파일해서 Web Worker에서 돌린다. UI는 가볍게, 코어는 데스크탑·iOS·Android·TV에 그대로 재사용한다는 발상이 이 프로젝트의 정체성이다.
사용자 입장에선 그냥 "영화·드라마·라이브 채널을 찾아서 보는 깔끔한 앱"이다. 하지만 그 뒤를 들춰보면, UI 한 벌로 토렌트·HLS·HTTP·유튜브·자막 서비스를 전부 통합하는 애드온 마켓플레이스가 돌아간다. 마치 VS Code의 Extension Marketplace나 옵시디언의 커뮤니티 플러그인처럼.
트렌딩 이유, 경쟁 제품 대비 장점.
2026년 5월 23일 기준 TrendShift 일일 트렌딩 9위에 올라온 이유는 단순한 "오픈소스 넷플릭스 대안" 그 이상이다. 이 프로젝트가 보여주는 기술적 결단들이 흥미롭다.
스마트 TV(타이젠), Android, iOS, Windows/Mac/Linux 데스크탑(Electron 기반), 그리고 웹 — 모든 폼팩터가 같은 Rust 코어(stremio-core)를 쓴다. 웹에서는 wasm-bindgen으로 WASM 빌드, 네이티브에서는 그대로 Rust 라이브러리로 링크. "한 번 짜서 어디서나 돌린다"는 슬로건을 React Native나 Flutter가 아니라 Rust + WASM으로 실현한 케이스다.
Plex나 Jellyfin은 "내가 가진 미디어 파일"을 인덱싱하는 데 초점이 있지만, Stremio는 외부 소스를 어떻게 끌어올지를 표준화했다. 누구나 HTTP 서버 하나를 띄워 manifest를 노출하면 그게 곧 애드온이다. Cinemeta(메타데이터), OpenSubtitles(자막), Torrentio(P2P) 등 커뮤니티 애드온이 수천 개 존재.
웹앱 하나로 PWA(Progressive Web App) 설치, 오프라인 캐싱(Workbox), TV 리모컨 D-pad 네비게이션(spatial-navigation-polyfill), Chromecast 송출, HLS·DASH·MP4 재생까지 다 지원. 네이티브 앱 없이도 거의 동일한 경험을 준다 — 모바일 사파리에서도 풀스크린 PWA로 동작한다.
Jellyfin·Plex·Emby 같은 셀프호스팅 미디어 서버 트렌드와, Stremio의 애드온 생태계가 만나면서 "내 NAS에 띄운 Jellyfin을 Stremio 애드온으로 노출"하는 식의 조합이 유행 중. 이게 2026년 5월 TrendShift 진입의 원인 중 하나로 보인다.
백엔드 · 프론트엔드 · 인프라.
먼저 package.json과 Dockerfile, webpack 설정에서 추출한 스택을 영역별로 본다. 이 레포(stremio-web)는 사실상 프런트엔드 + 정적 호스팅 + WASM 코어 구조라 "백엔드"는 외부 의존 서비스(애드온 서버, 토렌트 스트리밍 서버 등)로 분리되어 있다.
.less 파일을 CSS Modules로 컴파일해서 클래스명 충돌을 막는다(localIdentName: '[local]-[hash:base64:5]'). 코드 비율은 JavaScript 48.1% / TypeScript 25.9% / Less 25.7% — 점진적으로 TS 마이그레이션 중인 상태다.Stremio/stremio-translations에서 git 의존성으로 가져온다.--nav-up 등으로 다음 포커스 대상을 명시.Stremio/stremio-core(★ 2.1k, Rust 99.8%)가 진짜 코어. 여기에 Elm Architecture(Msg → Update → Effect → View)가 적용돼 있다. stremio-core-web은 그 코어를 wasm-bindgen으로 감싸 자바스크립트에서 부를 수 있게 만든 브릿지. npm 패키지로 발행된다(@stremio/stremio-core-web@0.58.0).magnet:?xt=urn:btih:…).[commit_hash]/scripts/[name].js 형식으로 캐시 무효화를 git commit 단위로 한다.index.html은 max-age 7200초, 나머지 에셋은 약 30일(2629744초).stremio.github.io/stremio-web에 development 브랜치 자동 배포(GitHub Actions build.yml).시스템 구조도 + 핵심 설계 패턴.
Stremio Web의 진짜 흥미로운 점은 UI(React)와 상태(Rust WASM)가 메시지로만 대화한다는 분리에 있다. 보통의 React 앱이 Redux나 Zustand로 JS 안에서 상태를 관리하는 것과 정반대 방향이다. 아래는 전체 데이터 흐름을 ASCII로 그린 그림.
┌─────────────────────────────────────────────────────────────────┐
│ BROWSER │
│ │
│ ┌──────────────────────┐ ┌──────────────────────────┐ │
│ │ Main Thread (UI) │ │ Web Worker │ │
│ │ ────────────────── │ postMsg│ ───────────────────── │ │
│ │ React 18 Components │ ◀─────▶ │ stremio-core (WASM) │ │
│ │ Less / CSS Modules │ (Msg) │ ┌─────────────────────┐ │ │
│ │ i18next 번역 │ │ │ Context: 인증·애드온│ │ │
│ │ Sentry │ │ │ Library: 시청 목록 │ │ │
│ │ spatial-navigation │ │ │ Catalogs: 영화/TV │ │ │
│ │ stremio-video │ │ │ Streams: 재생 URL │ │ │
│ └──────────────────────┘ │ │ MetaDetails: 상세 │ │ │
│ │ │ └─────────────────────┘ │ │
│ │ HTMLVideoElement │ │ Effects │ │
│ ▼ │ ▼ │ │
│ ┌──────────────────────┐ │ ┌─────────────────────┐ │ │
│ │ <video> / HLS.js / │ │ │ Environment trait │ │ │
│ │ Chromecast 송출 │ │ │ (fetch + storage) │ │ │
│ └──────────────────────┘ │ └─────────────────────┘ │ │
│ └─────────│────────────────┘ │
└─────────────────────────────────────────────│───────────────────┘
│ HTTPS
▼
┌─────────────────────────────────────────────────┐
│ EXTERNAL SERVICES │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────┐ │
│ │ api. │ │ Cinemeta │ │ 사용자 │ │
│ │ strem.io │ │ (메타데이터)│ │ 제작 애 │ │
│ │ (인증·동기) │ │ │ │ 드온 N개│ │
│ └─────────────┘ └─────────────┘ └─────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────┐ │
│ │ OpenSubtit. │ │ Torrentio │ │ HLS/M3U │ │
│ │ (자막 애드온)│ │ (P2P 애드온)│ │ 스트림 │ │
│ └─────────────┘ └─────────────┘ └─────────┘ │
└─────────────────────────────────────────────────┘
레스토랑의 주방을 생각해보자. 손님(UI)이 "스테이크 미디엄으로요"라고 외치는 게 Msg. 주방장(Update)이 주문을 받아 "팬 굽기" 작업 지시를 내리는 게 Effect. 팬에서 고기가 익는 동안 주방장은 다음 주문을 받는다(비동기). 다 익으면 그릇에 담아 손님에게 내보내는 게 새 Model. 손님은 주방을 직접 들여다보지 않고 주문서·완성품으로만 상호작용한다.
fetch(url) -> Future<Response>, get_storage(key), set_storage(key, value), now() 같은 메서드를 가진다. 같은 stremio-core 코드를 웹에서는 WebEnv(fetch API + IndexedDB), Android에서는 JniEnv(OkHttp + SharedPreferences)로 갈아끼울 수 있다.이 패턴의 매력은 90%의 코드가 플랫폼 분기 없이 깔끔하다는 것. if cfg!(target_arch = "wasm32") { ... } 같은 너저분한 분기를 비즈니스 로직에서 추방하고, 환경 의존만 별도 모듈에 격리한다.
Stremio의 진짜 차별점. 누구나 HTTP 서버 하나를 띄우고 /manifest.json을 노출하면 그게 곧 애드온이다.
{
"id": "com.myaddon.movies",
"version": "1.0.0",
"name": "My Movies Addon",
"resources": ["catalog", "meta", "stream"],
"types": ["movie", "series"],
"catalogs": [{ "type": "movie", "id": "top" }],
"endpoint": "https://myaddon.example.com"
}
그러면 코어가 자동으로 /catalog/movie/top.json, /stream/movie/tt1234567.json 같은 엔드포인트를 호출해서 데이터를 끌어온다. RESTful 컨벤션을 강제하지만 매우 가볍다 — 정적 JSON 호스팅만 해도 카탈로그 애드온을 만들 수 있다.
메인 스레드는 "방송 진행자"고, Web Worker의 WASM 코어는 "방송 PD실"이다. 진행자(React)는 입(UI)만 움직이고, PD실(WASM)에서 큐 시트·자막·게스트 출연을 다 관리한다. 둘은 인터폰(postMessage)으로만 대화하고, 진행자가 멈춰서 자료 찾을 일이 없으니 화면이 끊기지 않는다(60fps 유지).
주요 폴더·파일의 역할.
stremio-web/ ├── src/ ← 프런트엔드 본체 │ ├── components/ ← 재사용 가능한 React 컴포넌트 │ ├── routes/ ← 페이지 단위 라우트 (Board, Discover, Detail …) │ ├── core/ ← stremio-core-web 어댑터 (CoreProvider, useCore, createTransport 등) │ ├── router/ ← 자체 라우터 (alias: stremio-router) │ ├── common/ ← 유틸·훅·아이콘·상수 │ ├── index.html ← HtmlWebpackPlugin 템플릿 │ └── index.js ← Webpack entry (main) + i18next 초기화 + React 앱 렌더링 ├── assets/ │ ├── favicons/ ← 16~512px 모든 사이즈 │ ├── images/ ← 정적 이미지 │ └── screenshots/ ← README용 .webp 스크린샷 ├── tests/ │ └── i18nScan.test.js ← 번역 키 누락 검사 ├── .well-known/ ← .well-known/apple-app-site-association 등 ├── .github/ │ ├── workflows/build.yml ← CI 빌드 + GitHub Pages 배포 │ └── ISSUE_TEMPLATE/ ← 이슈 폼 ├── http_server.js ← Express 정적 서버 (Docker용) ├── manifest.json ← PWA 매니페스트 ├── Dockerfile ← 멀티 스테이지 빌드 ├── webpack.config.js ← 빌드 설정의 심장부 ├── tsconfig.json ← TS 컴파일러 설정 ├── eslint.config.mjs ← ESLint 9 flat config ├── package.json └── pnpm-lock.yaml ← pnpm 락 파일
이 파일 하나만 읽어도 프로젝트의 빌드 전략을 거의 다 알 수 있다. entry가 2개(main과 worker), resolve.extensions에 .wasm 포함, alias로 stremio·stremio-router 단축 경로 지정, execSync('git rev-parse HEAD')로 빌드 시점 커밋 해시를 파일명에 박아 캐시 무효화.
Express 4 하나로 build/를 정적 서빙. index.html은 2시간만 캐시(빈번한 배포 대응), 나머지는 30일 캐시(파일명에 commit hash가 박혀있어 안전). 백엔드 API를 따로 두지 않고 클라이언트 사이드에서 외부 API를 직접 호출하는 SPA 모델이라 서버가 이렇게 단순할 수 있다.
각 기술에서 무엇을 배울 것 + 실습 아이디어.
이 레포는 "전부 다 따라하기"보다는 관심 있는 영역만 골라 깊이 파는 게 좋다. 영역별로 어떤 학습 가치가 있는지 정리한다.
stremio-core의 stremio-core-web 폴더를 열어보면 #[wasm_bindgen] 매크로로 Rust 함수를 JS에 노출하는 패턴을 볼 수 있다. JsValue로 데이터를 주고받고, serde-wasm-bindgen으로 직렬화. 실습: 간단한 Rust 함수(예: 토큰 카운터, 검색 인덱서)를 wasm-bindgen으로 빌드해서 React 앱에서 호출해보기.
webpack entry가 두 개(main + worker)라는 점이 결정적. 워커 안에서 WASM이 돌고, postMessage로 UI와 소통한다. 실습: 간단한 React 앱에서 정렬·암호화·이미지 처리 같은 무거운 작업을 Web Worker로 옮겨보고, Performance 탭에서 메인 스레드 점유율 변화를 측정.
workbox-webpack-plugin이 빌드 시점에 ServiceWorker를 자동 생성. maximumFileSizeToCacheInBytes: 20000000로 WASM 바이너리도 precache. skipWaiting, clientsClaim으로 새 SW가 즉시 활성화. 실습: Vite·CRA로 만든 기존 앱에 Workbox를 붙여 PWA화 + Lighthouse PWA 점수 100점 만들기.
stremio-core가 Rust로 짠 Elm 아키텍처가 너무 매력적이지만, 자바스크립트로도 비슷한 패턴을 짤 수 있다. 실습: Msg(액션) → Update(리듀서) → Effect(부수효과 명세) → EffectRunner(부수효과 실행) 4계층으로 작은 TODO 앱을 짜본다. Redux와의 차이를 체감.
Stremio가 단순한 manifest.json + RESTful 엔드포인트 컨벤션만으로 수천 개의 애드온을 받아들이는 비결을 분석. 실습: stremio-addon-sdk npm 패키지로 5분 만에 카탈로그 애드온 하나 만들기(예: "내 블로그 글을 영화처럼 리스트하는 더미 애드온"). Glitch·Replit에서 즉시 호스팅 가능.
thread-loader가 babel-loader·ts-loader·less-loader 등을 CPU 코어 수만큼 병렬 실행. os.cpus().length로 워커 수 결정. execSync('git rev-parse HEAD')로 commit 해시를 파일명에 박는 패턴은 CDN 캐시 무효화의 우아한 해법. 실습: 기존 webpack 프로젝트에 thread-loader 추가하고 빌드 시간 측정.
번역 리소스를 별도 레포(Stremio/stremio-translations)로 분리하고 git 의존성으로 가져오는 패턴. scan-translations 스크립트로 누락된 키를 CI에서 검사. 실습: react-i18next로 작은 앱에 한국어·영어·일본어 3개 언어 붙이고, "키 누락 자동 검출" 스크립트 작성.
개발용 · 사용자용 양쪽.
node:22-alpine이므로 22 권장.corepack enable로 활성화.git rev-parse HEAD를 호출. .git 폴더가 있어야 함.api.strem.io, 각종 애드온 서버에 HTTPS로 접속 가능해야 함. 사내 방화벽 환경에선 별도 프록시 설정 필요.커뮤니티 애드온은 누구나 만들 수 있어 보안·법적 회색지대가 존재한다. 특히 토렌트 P2P 애드온은 저작권 보호 콘텐츠를 노출할 수 있어 지역에 따라 불법일 수 있다. 학습용으로 자기가 만든 더미 애드온, 합법 콘텐츠 애드온(YouTube, Internet Archive 등)부터 시작하길 권장.
난이도별 5단계.
레포 클론 → docker build -t stremio-web . → docker run -p 8080:8080 stremio-web → http://localhost:8080 접속. 가입 없이 게스트 모드로 들어가서 Board, Discover, Search 페이지를 둘러보고, Cinemeta 애드온이 어떤 데이터를 보여주는지 네트워크 탭으로 관찰.
stremio-addon-sdk(npm)로 정적 영화 5개를 보여주는 미니 애드온을 작성. manifest.json + /catalog/movie/dummy.json만 노출. Vercel·Glitch에 배포한 뒤 Stremio Web에 등록해서 자기 카탈로그가 Discover에 나타나는지 확인.
@stremio/stremio-core-web npm 패키지를 일반 Node 스크립트에서 import해 카탈로그 fetch·인증 같은 코어 기능을 CLI로 호출. wasm-bindgen이 만들어낸 인터페이스를 직접 만져보면서 Rust↔JS 경계를 체감.
현재 React 18.3.1을 사용. src/components/에서 컴포넌트 하나(예: 카드)를 골라 React 19의 새 기능(use() 훅, Server Components 패턴 흉내)으로 다시 쓰고, 기존 동작과 1:1 매칭되는지 시각 회귀 테스트(Playwright + 스크린샷 비교)로 검증.
stremio-core를 클론한 뒤 Tauri 또는 Electron 데스크탑 앱에서 코어를 직접 링크. WebEnv 대신 DesktopEnv(reqwest + 파일시스템 + OS keychain)를 구현. 같은 비즈니스 로직이 웹/데스크탑에서 동일하게 동작하는 걸 확인하면 "Write Once, Run Anywhere"의 진짜 사례를 체험.
8주짜리 주차별 학습 계획.
이 레포를 발판으로 "풀스택 + WASM + 미디어"를 익히고 싶다면, 다음 8주 로드맵을 권장한다. 매 주 4~6시간 투자 기준.
create-react-app 대신 webpack을 처음부터 직접 설정해 React 18 앱을 띄워본다. entry, output, loaders, plugins 개념 체득. 산출물: Hello World 앱 + dev/prod 모드 분리.
ts-loader 설정, .less + CSS Modules로 클래스 충돌 방지. localIdentName 패턴 익히기. 산출물: TS 컴포넌트 5개 + 자체 디자인 토큰.
worker-loader 또는 webpack 5 네이티브 Web Worker로 별도 entry 빌드. postMessage 기반 RPC 패턴 직접 구현. 산출물: 무거운 정렬 작업을 워커에서 처리하는 데모.
wasm-pack new로 Rust → WASM 라이브러리 만들기. #[wasm_bindgen] 사용법, JS↔Rust 데이터 직렬화(serde-wasm-bindgen). 산출물: 간단한 Markdown → HTML 변환기.
Redux Toolkit 대신 직접 Msg/Update/Effect 4계층을 짠다. RxJS 또는 단순 EventEmitter 기반 EffectRunner. 산출물: 외부 API 호출을 Effect로 추상화한 TODO 앱.
Workbox 5/6/7 차이 정리, precache vs runtime cache 전략. Application 탭에서 Service Worker 라이프사이클 추적. 산출물: Lighthouse PWA 100점.
hls.js로 m3u8 재생, Media Source Extensions로 직접 비디오 청크 공급, Cast SDK로 Chromecast 송출. 산출물: 자체 미니 미디어 플레이어 컴포넌트.
HTTP manifest 컨벤션을 그대로 차용해 "내 앱의 외부 콘텐츠 소스"를 표준화. JSON 스키마 검증, CORS 처리, 사용자가 URL만 입력하면 등록되는 UX. 산출물: 작은 RSS 리더 + 사용자 정의 피드 소스 등록 기능.
프로젝트에 등장하는 주요 기술 용어 정리.
#[wasm_bindgen]을 붙이면 JS에서 호출 가능한 인터페이스 코드가 자동 생성된다. 마치 JNI(Java Native Interface)나 FFI(Foreign Function Interface)의 웹 버전..ts 파일들로 만들고, .m3u8이라는 텍스트 매니페스트로 묶는다. 네트워크가 느리면 자동으로 저화질 청크로 전환. Stremio가 다양한 화질 스트림을 다루는 핵심 포맷.<video> 태그에 비디오 청크를 직접 공급할 수 있게 해주는 브라우저 API. HLS.js, Shaka Player 등이 MSE 위에서 동작..card { ... }가 빌드 시 .card-aB12cD처럼 변환되어 모듈마다 격리된다. Stremio는 Less + CSS Modules 조합.resources(catalog/meta/stream/subtitles 중 무엇을 제공하는지), types(영화/시리즈/채널), catalogs(어떤 카탈로그를 노출하는지)를 명세. 코어는 이 매니페스트만 보고 적절한 엔드포인트를 호출.magnet:?xt=urn:btih:HASH&dn=NAME&tr=TRACKER 형태의 토렌트 식별자. 실제 .torrent 파일 없이도 P2P 네트워크에서 콘텐츠를 찾을 수 있다. Stremio의 P2P 애드온이 이걸 다룸.@sentry/browser를 한 줄 import만 해도 시작.t('hello')로 호출. 복수형(plural), 보간(interpolation), 네임스페이스(namespace) 지원. react-i18next로 React 훅 형태로 사용.공식 문서 · 관련 레포 · 학습 자료.
docker run -p 8080:8080로 Stremio Web을 띄우고 DevTools Network 탭에서 어떤 외부 API를 호출하는지 그려보기. 클라이언트만으로 동작하는 SPA의 실제 트래픽 패턴을 눈으로 확인.wasm-pack new hello로 기본 템플릿을 깐 뒤 greet(name: &str) 함수 하나만 노출. JS에서 import해서 alert으로 띄워보기. wasm-bindgen 경험치 +1.stremio-addon-sdk npm 패키지로 정적 매니페스트와 카탈로그 핸들러만 작성, Glitch에 배포. Stremio Web의 "Addons → Community → Add Addon"에 URL 붙여넣어 자기 카탈로그가 Discover에 뜨는지 확인.