← Start here · Infra note
HTML 문서는 그저 글자인데, 버튼 하나로 진짜 앱이 페이지 안에서 실행됩니다. 비밀은 페이지가 아니라 런처(launcher) — 요청이 오면 컨테이너를 깨우고, 준비되면 URL을 돌려주고, 놀고 있으면 꺼버리는 작은 API예요.
docs/run.js(버튼) · infra/launcher/(스포너 API) ·
k3s의 scale-to-zero Deployment. 배포 절차는 infra/README.md.
"HTML 안에서 실행"은 착시예요 — 실행은 서버에서, 페이지는 깨우고·기다리고·담아 보여줄 뿐. 그런데 그 세 동작이면 정적 문서가 살아있는 데모가 됩니다.
보통 문서 사이트와 데모 서버는 별개죠. 여기선 문서의 버튼이 곧 데모의 전원 스위치입니다. 항상 켜두면 돈과 메모리가 새고, 항상 꺼두면 데모가 죽어있는데 — 필요할 때만 깨우는 게 정답이에요. 서버리스(scale-to-zero)의 아이디어를 교실 크기로 줄인 겁니다.
초록 공이 가장 흔한 길(클릭→기동→폴링→준비)을 돕니다. 앱이 이미 깨어 있으면 노란 공이 지름길(즉시 ready)로 빠져요.
점선이 마지막 조각이에요: 아무도 안 쓰면 리퍼(reaper)가 replicas=0으로 되돌립니다 —
"쓸 때만 존재하는 앱". 다음 클릭이 다시 깨우니 데모는 영원히 안 죽어요.
docs/run.js (브라우저) — 버튼을 그리고, /launch를 쏘고, /status를 폴링하고, ready가 오면 iframe으로 그 URL을 페이지 안에 담아 보여줘요. 런처가 없으면 "run it locally"로 안내하는 안전한 기본값.infra/launcher/app.py, ~120줄 Flask) — 허용된 앱 이름만 받아 kubectl scale을 대신 눌러주는 손. 권한은 RBAC으로 이 네임스페이스의 deployment scale만.replicas: 0) — 앱의 본체. 0이면 존재하지 않고(비용 0), 1이면 몇 초 만에 살아나요. Ingress가 agent.bit-habit.com으로 연결.5D의 agent는 웹앱이 아니라 터미널 REPL이에요. 래퍼를 새로 짜는 대신 ttyd로 진짜 터미널을 웹소켓으로 서빙합니다 — 학생이 로컬에서 보는 그 세션 그대로.
컨테이너도 학생이 받는 그 zip에서 빌드해요(infra/apps/agent/Dockerfile) —
라이브 데모와 수업 자료가 절대 어긋날 수 없는 구조입니다.
정적 페이지 + 작은 스포너 API + scale-to-zero = 문서가 곧 실행 환경. 페이지는 실행하지 않아요 — 깨우고, 기다리고, 담아서 보여줄 뿐입니다.
"HTML 안에서 앱이 실행된다"에서 실제로 코드가 도는 곳은?
페이지가 하는 일은 ① /launch 호출 ② /status 폴링 ③ iframe 표시, 셋뿐이에요.
실행은 k3s가 깨운 컨테이너에서 일어나고, ttyd가 그 터미널을 웹소켓으로 페이지까지 흘려보냅니다.
"실행처럼 보이는 것"과 "실행되는 곳"을 가르는 게 이 패턴 이해의 전부예요.