← Start here · Infra note

▶ Run it live — 정적 페이지가 서버 앱을 띄우는 원리

HTML 문서는 그저 글자인데, 버튼 하나로 진짜 앱이 페이지 안에서 실행됩니다. 비밀은 페이지가 아니라 런처(launcher) — 요청이 오면 컨테이너를 깨우고, 준비되면 URL을 돌려주고, 놀고 있으면 꺼버리는 작은 API예요.

🧩 구성 요소 3개: docs/run.js(버튼) · infra/launcher/(스포너 API) · k3s의 scale-to-zero Deployment. 배포 절차는 infra/README.md.

1 · 왜 신개념처럼 보이나

"HTML 안에서 실행"은 착시예요 — 실행은 서버에서, 페이지는 깨우고·기다리고·담아 보여줄 뿐. 그런데 그 세 동작이면 정적 문서가 살아있는 데모가 됩니다.

보통 문서 사이트와 데모 서버는 별개죠. 여기선 문서의 버튼이 곧 데모의 전원 스위치입니다. 항상 켜두면 돈과 메모리가 새고, 항상 꺼두면 데모가 죽어있는데 — 필요할 때만 깨우는 게 정답이에요. 서버리스(scale-to-zero)의 아이디어를 교실 크기로 줄인 겁니다.

2 · The flow — 공을 따라가 보세요

초록 공이 가장 흔한 길(클릭→기동→폴링→준비)을 돕니다. 앱이 이미 깨어 있으면 노란 공이 지름길(즉시 ready)로 빠져요.

flowchart LR CLICK(["click: Run it live"]) --> LAUNCH["POST /launch {app}"] LAUNCH --> WARM{"already running?"} WARM -->|no| SCALE["kubectl scale --replicas=1"] SCALE --> POLL["poll GET /status/{app}
every 2.5s"] POLL --> READY["{state: ready, url}"] WARM -->|yes| READY READY --> EMBED(["iframe shows the app
right in this page"]) EMBED -. "idle 20 min → replicas=0" .-> CLICK class LAUNCH core;

점선이 마지막 조각이에요: 아무도 안 쓰면 리퍼(reaper)가 replicas=0으로 되돌립니다 — "쓸 때만 존재하는 앱". 다음 클릭이 다시 깨우니 데모는 영원히 안 죽어요.

3 · 세 조각의 역할

who does what
  • docs/run.js (브라우저) — 버튼을 그리고, /launch를 쏘고, /status를 폴링하고, ready가 오면 iframe으로 그 URL을 페이지 안에 담아 보여줘요. 런처가 없으면 "run it locally"로 안내하는 안전한 기본값.
  • 런처 API (infra/launcher/app.py, ~120줄 Flask) — 허용된 앱 이름만 받아 kubectl scale을 대신 눌러주는 손. 권한은 RBAC으로 이 네임스페이스의 deployment scale만.
  • k3s Deployment (replicas: 0) — 앱의 본체. 0이면 존재하지 않고(비용 0), 1이면 몇 초 만에 살아나요. Ingress가 agent.bit-habit.com으로 연결.
🔒 안전 설계: 런처는 시크릿도 못 읽고 파드도 못 만들어요 — 오직 정해진 deployment의 replicas 숫자만 만질 수 있습니다(5D의 "위험한 도구를 애초에 안 주기"와 같은 원리를 인프라에 적용한 것).

4 · 터미널 앱을 페이지에 심는 법 (ttyd)

5D의 agent는 웹앱이 아니라 터미널 REPL이에요. 래퍼를 새로 짜는 대신 ttyd진짜 터미널을 웹소켓으로 서빙합니다 — 학생이 로컬에서 보는 그 세션 그대로.

flowchart LR BROWSER["iframe in the docs page"] -->|websocket| TTYD["ttyd -W
terminal-over-web, one binary"] TTYD --> REPL["python agent.py
the real classroom artifact"] REPL --> DB[("data.db
read-only SQLite")] class TTYD core;

컨테이너도 학생이 받는 그 zip에서 빌드해요(infra/apps/agent/Dockerfile) — 라이브 데모와 수업 자료가 절대 어긋날 수 없는 구조입니다.

5 · The key idea

정적 페이지 + 작은 스포너 API + scale-to-zero = 문서가 곧 실행 환경. 페이지는 실행하지 않아요 — 깨우고, 기다리고, 담아서 보여줄 뿐입니다.

"HTML 안에서 앱이 실행된다"에서 실제로 코드가 도는 곳은?

페이지가 하는 일은 ① /launch 호출 ② /status 폴링 ③ iframe 표시, 셋뿐이에요. 실행은 k3s가 깨운 컨테이너에서 일어나고, ttyd가 그 터미널을 웹소켓으로 페이지까지 흘려보냅니다. "실행처럼 보이는 것"과 "실행되는 곳"을 가르는 게 이 패턴 이해의 전부예요.