수강생은 따라 치지 않습니다 · 마지막에 각자 폰으로 접속합니다
가져갈 문장 — "오늘도 문법은 안 가르칩니다. 지형을 그립니다."
수요일 육성 그대로: "한 시간 만에 코딩을 알 리가 없습니다. 중국어 모르는 사람한테 한 시간 만에 중국어 못 가르쳐요." [10:02]
"그런데 중국 여행에는 중국어보다 — 몇 개의 성이 있고, 어떤 제스처가 기분 나쁘고, 공안은 무서운 사람이라는 정보가 더 중요할 수 있어요. 오늘은 그겁니다. 중국 옆에 한국, 오른쪽에 동해." [10:34] [54:14]
오늘 순서: 지난주 그 사이트를 연다 → 거기서 나온 단어로 지도를 세운다 → 그 위에서 두 개를 만든다. 여러분은 따라 치지 않습니다. 마지막에 폰만 꺼내세요.
화면은 4챕터 제목만 세로로. 그림 없음. 3분.
팩 카드 · 포스터
이야기가 시작되는 첫 장면
장면에 맞는 배경과 캐릭터 이미지가 뜬다
다음 행동을 고른다
지난주에 딸깍으로 만든 그 사이트. 오늘은 열어서 뜯어봅니다.
이야기 하나가 폴더 하나로 들어가 있고, 그 폴더를 AI가 읽으면서 연출을 한다.
"지난주에 딸깍으로 만든 그 사이트입니다. 오늘은 이걸 열어서 뜯어봅니다."
40~50대 영화인이 보고 있다는 걸 잊지 않는다: "이야기 하나가 폴더 하나로 들어가 있고, 그 폴더를 AI가 읽으면서 연출을 합니다."
붙는 단어: 없음(아직). 이름을 얹기 전에 그림부터. 3분.
⚠ 준비물 — 스크린샷은 아침에 실제 Chrome에서 찍는다. 09.02 핸드오버 §7-7: 자동화 브라우저에서는 포스터·씬 이미지가 안 실렸다. 육안 확인이 곧 준비다.
{"seq":18,"type":"user_message","timestamp":"2026-09-04T16:54:59.862Z","data":{"text":"…
{"seq":19,"type":"pipeline_start","timestamp":"2026-09-04T16:54:59.863Z","data":{"turnIn…
{"seq":20,"type":"quick_direction","timestamp":"2026-09-04T16:54:59.863Z","phase":"gm","…
{"seq":21,"type":"bgm_update","timestamp":"2026-09-04T16:54:59.863Z","phase":"action","d…
{"seq":22,"type":"sfx_play","timestamp":"2026-09-04T16:54:59.864Z","phase":"action","dat…
{"seq":23,"type":"visual_action","timestamp":"2026-09-04T16:54:59.866Z","phase":"action"…
{"seq":24,"type":"story_update","timestamp":"2026-09-04T16:54:59.866Z","data":{"result":…
{"seq":25,"type":"npc_dialogue","timestamp":"2026-09-04T16:54:59.866Z","phase":"npc","da…
{"seq":26,"type":"suggestions_shown","timestamp":"2026-09-04T16:55:06.005Z","data":{"rep…
{"seq":27,"type":"pipeline_done","timestamp":"2026-09-04T16:55:06.006Z","data":{"progres…
{"seq":28,"type":"user_message","timestamp":"2026-09-04T16:55:06.007Z","data":{"text":"…
{"seq":29,"type":"pipeline_start","timestamp":"2026-09-04T16:55:06.008Z","data":{"turnIn…
{"seq":30,"type":"quick_direction","timestamp":"2026-09-04T16:55:13.168Z","phase":"gm","…
{"seq":31,"type":"bgm_update","timestamp":"2026-09-04T16:55:13.169Z","phase":"action","d…
{"seq":32,"type":"sfx_play","timestamp":"2026-09-04T16:55:13.169Z","phase":"action","dat…
{"seq":33,"type":"visual_action","timestamp":"2026-09-04T16:55:13.170Z","phase":"action"…
{"seq":34,"type":"npc_dialogue","timestamp":"2026-09-04T16:55:13.172Z","phase":"npc","da…
{"seq":35,"type":"story_update","timestamp":"2026-09-04T16:55:13.173Z","data":{"result":…
{"seq":36,"type":"suggestions_shown","timestamp":"2026-09-04T16:55:13.173Z","data":{"rep…
{"seq":37,"type":"pipeline_done","timestamp":"2026-09-04T16:55:13.174Z","data":{"progres…두 창을 나란히 — 브라우저(사이트) + 옆에 터미널 로그.
① 팩을 열고 대화를 두세 줄만 굴린다. 배경이 바뀌고 캐릭터가 뜨는 걸 본다.
② 옆 터미널을 가리킨다. 로그가 흐른다. "화면 뒤에서 누가 일하고 있죠. 무슨 일을 했는지 여기 남습니다."
③ 선택지를 하나 고른다. "이 선택이 어딘가에 적힙니다. 기억이죠." [03:38]
붙는 단어(이름만 얹는다, 정의 안 함): 화면(프론트엔드) ● · 서버·콘솔·로그 ● — 5분.
⚠ 실패 시 — 로컬 서버가 안 뜨면 P02 스크린샷으로 대체하고 P04로 간다. 어젯밤 확인: 맥에 3010이 이미 떠 있다(+ 3011·3013·3020 중복 서버 — 아침에 하나만 남길 것).
오른쪽 로그 박스는 실제 logs/sessions/2026-09-04_16-53_red_riding_hood_session-3882/events.jsonl 마지막 20줄(폭에 맞춰 잘라 표시). 라이브에서는 진짜 터미널을 띄운다.
실행되면 서버 안에 기억이 남는다. LLM은 기억력이 좁아서 서버는 팩에서 필요한 장만 읽는다 — 현재 챕터와 옆 챕터만.
알아서 하는 게 아니라, 수칙을 읽고 일하는 작업 도우미다.
상자 셋. 그 이상 그리지 않는다.
"화면 / 화면을 띄우는 서버 / 스토리팩. 실행되면 서버 안에 기억이 남습니다." [00:41]
"LLM은 기억력이 좁아요. 처음엔 똑똑하다가 엉뚱한 소리를 하는 이유가 그겁니다. 그래서 필요한 장만 읽게 짰어요." [01:41] — 현재 챕터와 옆 챕터만 준다.
오늘의 포인트: 팩 안에는 배경·캐릭터·음악·효과음이 이름표 붙은 목록으로 들어 있고(04_resources.txt), 매 턴 어떤 걸 꺼내 쓸지는 LLM이 정한다. 사람이 "이 장면엔 이 그림"이라고 일일이 안 붙인다. "재료를 갖춰 놓으면 알아서 꺼내 씁니다. 으레 식탁 위에 포크와 나이프가 있죠. 에이전트가 눈 뜨자마자 '아, 여기서 밥을 푸는구나' 알도록 주방을 꾸려 놓은 겁니다." [13:29]
한 줄로: "이건 알아서 하는 게 아니고 수칙을 읽고 일하는 작업 도우미예요." [46:35]
붙는 단어: 프론트엔드/백엔드 ● · 서버 ● · 컨텍스트(기억력) ○ — 5분.
구조화돼 있으면 LLM이 그걸 자산화해서 이미지든 뭐든 해준다.
폴더 트리 한 장. 실제 경로 그대로. 옆에 packs/_template/(빈 틀)도 보인다.
"에이전트는 폴더를 열면 이름하고 구조만 봅니다. _template이 있으면 '아, 여러 개 붙는 구조구나'를 이름만으로 압니다." [20:44]
"내용하고 뼈대가 따로 있으니까 내용만 갈아끼울 수 있어요. 지난주에 '고등학교 일진의 모험담' 한 문장 던졌더니 주인공이 만들어졌잖아요. 그게 되는 이유가 이 폴더예요." [11:37]
반대 사례(웃긴 대목): "엉망진창인 다운로드 폴더 잡고 '분석해 줘' 하면, 그다음부터 엉망진창으로 일합니다." [44:55]
결론 한 줄: "구조 자체가 자산입니다. 구조화돼 있으면 LLM이 그걸 자산화해서 이미지든 뭐든 해줍니다." [44:25]
CLAUDE.md가 보이면 한 마디만: "네오가 깨어나 처음 읽는 글." [12:05]
붙는 단어: 프로젝트·폴더·파일 ● · 컨텍스트 ○ · CLAUDE.md ○ — 4분.
--dry-run — 몇 건이 생성될지만 출력. 돈이 안 나간다--provider gemini(기본) · byteplus(Seedream). 텍스트 하나가 여러 양태로 뻗는다.{"seq":2,"type":"gm_direction","timestamp":"2026-09-04T16:55:08.154Z","turnIndex":2,"phase":"gm","data":{"direction":{"narrativeDirection":"빨간 모자가 숲의 입구에서 갈림길에 도달하여 주변을 탐…
{"seq":3,"type":"staging_command","timestamp":"2026-09-04T16:55:08.154Z","turnIndex":2,"phase":"gm","data":{"emulatorCommand":{"trackId":"bgm_calm_joyful_sunlit_morning","volume":0.6,"fa…
{"seq":4,"type":"staging_command","timestamp":"2026-09-04T16:55:08.155Z","turnIndex":2,"phase":"gm","data":{"emulatorCommand":{"sfxId":"sfx_red_boots_forest_steps","volume":0.5,"delay":0…
{"seq":5,"type":"action_executed","timestamp":"2026-09-04T16:55:08.155Z","turnIndex":2,"data":{"action":{"action":"select","imageId":"bg_forest_fork","displayStyle":"inline"},"result":{}…
{"seq":7,"type":"story_update","timestamp":"2026-09-04T16:55:10.116Z","turnIndex":2,"data":{"variables":{"setFlags":[{"key":"stage_2_entered","value":true}],"incrementCounters":[{"key":"…왼쪽은 명세, 오른쪽은 절차, 아래는 실제 로그. 라이브 생성은 하지 않는다 — 이미 나온 결과를 연다.
"resource_list가 만들어졌다는 게 되게 중요한 발견이에요. 요소를 다 명세화하고 API로 한 번에 생성해 낼 수 있다는 건 엄청난 겁니다. CG·게임·영화로 치면 이 명세가 그대로 제작 목록이에요." [36:02]
같은 명세로 이미지도, 효과음도, 음악도 나왔다. 이 앱엔 pack:images · pack:audio · pack:video 명령이 있고, 이미지 제공자는 Gemini·BytePlus Seedream 등으로 갈아끼운다. 텍스트 하나가 여러 양태로 뻗는다.
절차는 셋 — 명세 → Dry Run(돈 안 나감) → 병렬. "피자 박스를 열 명이 접으면 빨리 끝나죠. 명시 안 하면 한 장씩 합니다." [41:12] [50:21]
정직하게 한 마디: "수요일에 무료 등급이 소진돼서 주인공 한 장에서 멈췄죠. 실무에서 매일 일어나는 일입니다." [52:20]
아래 로그 박스는 실제 events.jsonl 두 번째 턴이다. 사람이 "이 장면엔 이 음악"이라고 붙인 게 아니라, LLM이 목록에서 골랐다. (seq 6 npc_dialogue는 지면상 생략 — 라이브에서는 터미널에서 그대로 보인다.)
붙는 단어: API ● · API 키·비용 ● · 명세·제작 목록 ○ · Dry Run·병렬 ○ — 3분.
문장 하나만. 설명을 덧붙이지 않는다.
"에이전트를 바이브 코딩·웹사이트 만드는 걸로만 보지 마세요. 구조화된 재료로 프로젝트에 필요한 리소스를 생성하는 도구로 보세요." [37:07]
1분 → 챕터 2(랜드스케이프)로 넘어간다.
챕터 목적 — "꽌시가 무슨 말인지 몰라도 되지만 그런 개념이 있다는 건 알아야 합니다." [10:19] 외우는 게 아니다. 다음에 만났을 때 "아, 그거"가 되면 된다.
"용어를 알아야 한다는 것에 사로잡히기 쉬운데, 그보다 관계가 중요하고, 그 이전에 구조화하는 사고방식이 중요합니다." [10:49]
원칙 — 정의를 먼저 말하지 않는다. 방금 P03~P06에서 본 화면에 이름을 얹거나, 곧 실습에서 만날 자리를 예고한다. 장마다 "오늘 어디서"가 있는 이유.
세 칸을 손으로 훑으면서 한 번만 걷는다. 왼쪽 = 내 컴퓨터, 가운데 = 남의 컴퓨터, 오른쪽 = 바깥.
주소 · IP · DNS — 가운데 칸에서 한 번 짚는다. "남의 컴퓨터에 올렸으면 찾아갈 방법이 있어야 하죠. 기계는 숫자로 찾습니다 — 그게 IP 주소, 번지수예요. 근데 사람이 172.66.47.83을 어떻게 외웁니까. 그래서 이름을 붙입니다 — 그게 도메인이에요. 그 이름을 번지수로 바꿔 주는 전화번호부가 DNS고요."
이어서: "여러분이 주소창에 이름을 치면, 브라우저가 먼저 DNS한테 물어봅니다 — '이 이름 번지수가 뭐야?' 번지수를 받아서 그제야 그 컴퓨터에 갑니다. 이게 매번 일어나요."
회수 예고: P21·P28에서 *.pages.dev 주소가 생기는 순간 "이름 하나를 빌린 것"이라고 다시 짚는다. 도메인 구입·연결은 오늘 안 한다 — 질문이 나오면 "그 이름을 사서 이 번지수에 연결하는 것"까지만.
3분.
| 용어 | 말 한 줄 | 영화 쪽 비유 | 등급 |
|---|---|---|---|
| CLI (명령줄) | 마우스로 고르는 대신 글자로 지시하는 창 | 메뉴판을 손가락으로 고르기 vs 주방에 대고 말로 주문하기 | ○ |
| 터미널 | 그 창을 띄우는 프로그램 | 인터폰 단말기 | ○ |
| 명령어 | 그 창에 치는 한 줄 | “3번 조명 20% 다운” | ○ |
그래서 오늘 강사가 터미널에 치는 건 대부분 한국어다.
CLI는 이 방에서 가장 많이 멈추는 단어다(08.29 점심 진단). 그래서 정의보다 왜 굳이 글자로 치는가를 먼저 말한다 — ① 손이 빠르다 ② 기록이 남는다(같은 주문을 적어두면 다음에 그대로 다시 쓴다) ③ AI가 대신 칠 수 있다.
화면을 가리키며: "무섭게 생겼는데 제가 여기 치는 건 한국어예요."
여기서 시간을 더 쓰지 않는다. §결정 2 — 5분 스침이냐 15분이냐 중 이 문서는 4분 + P17에서 30초 회수로 잡았다. 실물 터미널은 P17에서 본다.
붙는 단어: CLI ○ · 터미널 ○ · 명령어 ○ — 4분.
| 용어 | 말 한 줄 | 등급 |
|---|---|---|
| PC | 원래는 개인용 컴퓨터 전부. 한국에선 보통 윈도우 컴퓨터. 맥도 엄밀히는 PC | ○ |
| 운영체제(OS) | 컴퓨터를 굴리는 바탕 규칙. 윈도우 · macOS · iOS · 안드로이드가 각각 하나 | ○ |
| 윈도우 / 맥 | 수강생 다수는 윈도우, 강사는 맥 | ● |
| 아이폰 / 안드로이드폰 | iOS · 안드로이드가 도는 손안의 컴퓨터 | ○ |
| 갈리는 지점 | 윈도우 수강생 다수 | 맥 강사 |
|---|---|---|
| 명령창 이름 | PowerShell, 명령 프롬프트 | 터미널 |
| 폴더 주소 | C:\Users\상훈\작업 | /Users/sanghun/작업 |
| 설치 | 설치 파일 받아 실행 | 명령어 한 줄인 경우가 많음 |
인터넷 안내문 대부분이 맥 기준이다. 윈도우 쓰는 사람은 읽을 때마다 한 번씩 번역해야 하는데, 그 번역을 지금은 AI가 해준다.
실전 규칙 하나(이 장에서 가져갈 유일한 것): "나는 윈도우 11을 쓰고 있어. 명령어는 PowerShell 기준으로 알려줘." — 이 한 줄이 없으면 에이전트가 맥 명령어를 던지고, 학생은 "안 되는데요"에서 멈춘다. 수요일 막힘의 상당수가 여기였을 것.
폰은 만드는 쪽이 아니라 보는 쪽이다. 오늘 만드는 건 전부 웹. 폰은 결과를 확인하는 자리에만 등장한다(P13에서 이어짐).
P23에서 회수할 말: "저는 맥이라 이렇게 치는데 윈도우는 다릅니다."
붙는 단어: 윈도우/맥 ● · PC ○ · 운영체제(OS) ○ · 아이폰/안드로이드폰 ○ — 4분.
방금 P05에서 본 그 폴더가 이것이다. 새로 설명할 게 없다 — 본 것에 이름만 얹는다.
saju-calc는 계산만, gemini-client는 AI한테 전화 거는 것만assets/sprites/enemy_weak/…방금 P05에서 본 폴더가 이것이다. 새로 설명할 게 없다.
"작품 하나가 폴더 하나입니다. 그리고 그 폴더 안의 이름들이 곧 설명서예요 — saju-calc는 계산만, gemini-client는 AI한테 전화 거는 것만." (P17 예고)
화면의 리포는 P22에서 쓸 게임 엔진 Phaser의 것이다. 파일 목록 맨 위 세 줄만 짚는다 — README.md(첫 화면 설명문) · LICENSE.md(조건) · package.json(부품 명세서). 셋 다 다음 장에서 다시 나온다.
아래 흐린 네 개(패키지 · package.json · 설치 · Node)는 오늘 안 꺼낸다. 패키지/라이브러리는 P18에서 만세력 붙일 때 한 마디만 나온다.
붙는 단어: 프로젝트 ● · 폴더/파일 ● · 리포지토리 ○ · 소스 코드 ○ — 3분.
final_02, final_real, final_진짜최종을 없애주는 물건Git은 오늘 이름만 꺼낸다 — 고칠 때마다 올리고, 그 기록을 훨씬 촘촘하게 해주는 물건. 라이선스는 작품에 쓰시는 분들에게 실제로 걸린다.
Git은 오늘 이름만: "고칠 때마다 올립니다. 이 기록을 훨씬 촘촘하게 해주는 게 Git이에요. 최종_진짜최종 없애주는 물건." diff · merge · rebase는 없다.
왼쪽 화면을 손으로 훑으며: "한 줄이 한 번의 저장이에요. 누가 언제 뭘 고쳤는지가 다 남습니다."
오픈소스는 이 청중에게 무료 소스 라이브러리 · 플러그인이라고 하면 바로 붙는다. 오른쪽 Kenney 화면이 P22에서 실제로 쓸 그림·소리다.
라이선스는 한 줄만: "작품에 쓰시는 분들은 이게 실제로 걸립니다." 가운데 카드에서 초록 체크(되는 것)와 빨간 엑스(안 되는 것)만 손가락으로 짚는다. (§결정 3 — 한 줄에서 멈춘다.)
붙는 단어: Git ○ · GitHub ○ · 오픈소스 ○ · 라이선스 ○ — 3분.
앞 넷은 P04 그림 그대로. "화면은 앞, 서버는 뒤."
로컬↔배포는 말로 하면 5분, 폰으로 보이면 20초. 여기서는 예고만 한다. P21에서: localhost 주소로 열린 페이지 → 같은 주소를 폰에 친다 → 안 열린다 → 배포한 주소를 친다 → 열린다. "'내 컴퓨터 안에만 있다'와 '남의 컴퓨터에 올라가 있다'의 차이가 그 두 화면에 다 들어 있습니다. 정의를 먼저 말하지 않아도 돼요."
왼쪽 두 장은 같은 페이지를 PC와 폰에서 각각 연 것이다. PC에서는 좌우가 검게 남고 폰에서는 꽉 찬다 — "같은 코드인데 화면 폭에 맞춰 다시 배치됩니다. 그게 반응형이에요."
왜 챗봇 사이트는 오늘 못 올리나: "뒤(서버)와 기억이 필요한 물건이라서요. 오늘 만드는 둘은 화면만 있는 것이라 드래그로 올라갑니다."
아래 여덟 칸은 오른쪽 단어(수업에서 쓸 말)만 읽는다. 정의를 소리 내어 다 읽지 않는다. 5분.
원 표(11행) 정의 — 물어보면 이걸로 답한다. 웹: 주소 하나로 남의 컴퓨터에 있는 걸 내 브라우저에서 여는 방식 · 브라우저: 그걸 여는 프로그램(크롬·사파리) · 프론트엔드: 관객이 보는 화면 · 백엔드: 화면 뒤에서 실제로 일을 처리하는 쪽 · 서버: 그 뒤쪽 일을 맡아 항상 켜져 있는 컴퓨터 · 로컬: 내 컴퓨터 안, 나만 본다 · 배포: 남의 컴퓨터에 올려서 남도 보게 하는 것 · 주소(도메인/URL): 그렇게 올린 것을 찾아가는 이름 · 웹앱: 주소만 있으면 폰 브라우저에서 그냥 열리는 것, 설치 없음 · 네이티브 앱: 앱스토어에서 받아 설치하는 것, 심사와 등록비 · 반응형: 같은 페이지가 PC 화면과 폰 화면에 각각 맞춰 다시 배치되는 것.
키를 누가 가져가면 여러분인 것처럼 행세해서 AI 모델을 불러제낀다 — 한 번 부를 때마다 삼백 원, 오백 원, 칠백 원이 뜯겨 나간다. 노출되면 원칙은 로테이션, 새로 만드는 것.
.env는 environment의 줄임말, .local은 로컬 세팅 — 키나 환경 설정을 담는 상자."API 키는 키예요. 집 열쇠, 은행 열쇠와 같아요. 누가 가져가면 여러분인 것처럼 행세해서 AI 모델을 불러제낍니다. 한 번 부를 때마다 삼백 원, 오백 원, 칠백 원 돈이 뜯겨 나가요." [30:21]
화면 오른쪽 목차를 손가락으로 짚는다 — "환경 변수 사용(권장), 그리고 유출 대응 체크리스트. 만든 사람들도 이게 새어 나간다는 걸 아니까 문서에 이렇게 적어둔 거예요."
".env는 environment의 줄임말, .local은 로컬 세팅. 키나 환경 설정을 담는 상자." [34:44]
규칙 한 줄로 접는다: 키는 상자에, 상자는 내 컴퓨터에, 상자는 절대 같이 보내지 않는다.
노출되면: "원칙은 로테이션. 새로 만드는 겁니다." [33:53] — ⚠ 수요일 키는 실제로 노출됐고, 오늘 새 키로 왔다(§준비물).
태도 한 마디: "저는 아직도 일레븐랩스 홈페이지에서 어디서 효과음 만드는지 몰라요. 키 던지고 API · 모델 · Best Practice 찾아오라고 시킵니다." [49:00] — P15의 Best Practice로 이어진다.
P03에서 화면 옆으로 흐르던 게 이것이다.
app.js:42가 몇 번째 줄인지까지 알려준다안 될 때 강사가 보는 곳도 여기다. 빨간 줄을 복사해서 그대로 AI에게 던지면 된다.
P03에서 옆에 흐르던 게 이것. "안 될 때 저는 여기를 봐요. 빨간 줄 복사해서 그대로 AI한테 던지면 됩니다."
화면의 세 줄은 수요일 사주앱에서 실제로 찍힌 것이다. 빨간 줄 = 만세력 계산기를 못 불러왔다(P18에서 붙일 그 부품), 노란 줄 = 키가 없다(P14의 그 열쇠). "무슨 말인지 몰라도 됩니다. 이 줄을 그대로 복사하면 돼요."
그리고 Best Practice: "검색어에 Best Practice를 붙이세요. 우리말로 정석. 좋은 정보가 그 키워드 아래 잘 모여 있습니다." [49:31]
1분. 짧게 끊고 넘어간다.
랜드스케이프 회수(한 호흡) — 다섯 단어: 화면 · 서버 · 팩 · 열쇠 · 주소. 나머지는 이 다섯에 매달린다. 여기까지가 48분 지점이어야 한다.
앞에서 본 반응형을 내 노트북에서 바로 확인하는 방법이다. 배포하기 전에도 된다.
이 장은 학생이 집에서 실제로 쓸 수 있는 유일한 도구다. "폰으로 확인해 보세요"는 말이 쉽지, 배포 전에는 못 한다. 그런데 이건 지금 당장 된다.
"여러분 노트북에서 F12 한 번 누르면 창이 하나 열려요. 무섭게 생겼는데 오늘은 그중 딱 하나만 씁니다 — 폰 화면처럼 보기."
시연: 슈팅게임을 띄운 채로 F12 → Ctrl+Shift+M → 기기 목록에서 iPhone 선택. 같은 페이지가 그 자리에서 폰 모양으로 바뀐다. 여기서 한 번 웃음이 나온다.
이어서: "제가 아까 QR로 보여드린 것도 이겁니다. 다만 그건 진짜 폰이고, 이건 흉내예요. 흉내로 90%는 잡힙니다. 나머지 10%는 손가락 크기하고 실제 속도라서, 그것만 진짜 폰으로 봅니다."
붙는 단어: 반응형 · 폰 화면과 PC 화면 · (덤으로) 개발자 도구가 P15 콘솔과 같은 창이라는 것 — 다음 장에서 회수한다. 3분.
1980년, MIT 인공지능 연구소에 새 프린터가 들어옵니다. 제록스 9700. 종이가 자주 걸렸는데, 걸렸다는 사실을 알 방법이 없었습니다. 스톨먼은 이전 프린터에서는 그 알림 기능을 직접 만들어 붙였던 사람입니다. 이번에는 그럴 수 없었어요 — 소스코드(프로그램의 설계도. 사람이 읽고 고칠 수 있는 원본 텍스트)를 주지 않았기 때문입니다.
그 코드를 가진 카네기멜런의 한 교수를 찾아갑니다. 그리고 거절당해요. 이유는 “제록스에 안 주기로 약속했다” — 오늘날의 비밀유지계약(NDA)입니다. 그가 아무 말 없이 돌아선 그 일이, 이 뒤의 40년을 만듭니다.
저작권(copyright)을 뒤집은 말장난입니다. 저작권을 없애는 게 아니라, 저작권을 이용해서 자유를 강제하는 겁니다. “가져다 마음대로 고쳐 써라. 대신 고친 것도 똑같이 열어라.” 영화로 치면 — 이 시나리오 마음대로 각색해도 되는데, 각색본도 남이 각색할 수 있게 공개해야 한다는 조건입니다.
사진 한 장과 일화 하나로 4분. 연도를 외우게 하는 장이 아니다. “고치고 싶은데 고칠 수가 없었다”는 감정 하나만 남기면 된다.
읽어도 되는 문장 — “1980년, MIT 인공지능 연구소에 새 프린터가 들어왔습니다. 종이가 자주 걸렸는데 연구소가 다른 층이라, 한참 뒤에 가보면 아무것도 안 뽑혀 있는 거죠. 스톨먼은 이전 프린터에서는 이 문제를 직접 고쳤던 사람입니다. 그런데 새 프린터는 그게 안 됐어요. 설계도를 안 줬거든요.”
“코드를 가진 교수를 찾아갑니다. 그리고 거절당해요. ‘제록스에 안 주기로 약속했다.’ 요즘 말로 NDA입니다. 당시엔 이게 소프트웨어 업계에 막 등장한 새 관행이었어요.”
⚠ 교수 이름(로버트 스프롤)은 정황 근거만 있고 1차 문헌이 단정하지 않는다 — “카네기멜런의 한 교수”로만 말한다.
free 두 가지 뜻은 말로 한 번 더 풀어 준다. “돈 받고 팔아도 됩니다. 문제는 값이 아니라 통제권이에요.” 자유 4가지는 번호가 0번부터 시작한다는 것 자체가 이 동네 문화라고 한 마디 붙이면 웃는다.
전환 — “여기까지는 한 사람의 원칙 이야기입니다. 이게 어떻게 산업이 됐을까요.”
사진 저작권: Patafisik, CC BY-SA 4.0, via Wikimedia Commons. 크레딧 줄은 법적 의무라 화면에 남겨 둔다.
“free software”는 기업 임원에게 ‘공짜’로 들리고, 철학·정치 운동처럼 들렸습니다. 같은 물건에 붙인 다른 이름이에요. 스톨먼 쪽은 자유를, 레이먼드 쪽은 잘 돌아가는 개발 방법을 앞세웠고, 두 진영은 지금도 사이가 좋지 않습니다.
사진 셋으로 사람 이름을 붙여 두고, 아래 연표는 손가락으로 짚으며 넘긴다. 4분 안에 다섯 줄이다 — 한 줄에 45초.
성당 vs 시장은 영화 비유로 바꾸면 바로 붙는다. “성당식은 완성될 때까지 아무에게도 안 보여주는 감독판입니다. 시장식은 러프컷을 매일 올려놓고 누구나 코멘트를 달게 하는 방식이에요. 상식적으로는 전자가 좋을 것 같은데, 소프트웨어에서는 후자가 이겼습니다.”
“눈이 충분히 많으면 모든 버그는 얕다” — “시사회에 100명이 앉아 있으면 편집 실수는 반드시 누군가 잡아냅니다.”
순서가 핵심이다. 넷스케이프가 먼저 열었고(1월), 이름은 그 뒤에 붙었다(2월). 회사가 실용적인 사업 판단으로 코드를 여는 시대가 오자 “다른 이름이 필요하다”가 된 것이다.
⚠ 명명 회의 날짜는 OSI 공식(2/3)과 피터슨 본인 회고(2/5)가 갈린다 — “1998년 2월”로만 말한다. 또 피터슨이 직접 제안한 게 아니라, 토드 앤더슨이 회의 중 그 표현을 끼워 넣어 퍼뜨렸다는 대목까지는 여유 있을 때만.
⚠ 피터슨 사진은 원본이 512×542로 작아 확대돼 흐리다. 그래서 셋 중 가장 작게 배치했다 — 화질 얘기는 굳이 하지 않는다.
전환 — “그래서, 지금 그게 어디까지 왔을까요.”
세 덩어리다. 위 = 얼마나 크냐, 가운데 왼쪽 = 얼마나 위태롭냐, 가운데 오른쪽 = 왜 당신 얘기냐, 아래 = 오늘 만들 앱 안에 이미 들어 있다. 5분이면 아래 사진 석 장은 가리키기만 해도 된다.
숫자를 감각으로 바꾸는 문장 — “GitHub에 1초마다 개발자 한 명이 새로 들어옵니다. 이 강의 90분 동안 5,400명이 들어와요.”
left-pad — “11줄 때문에요. 리액트가, 바벨이 멈췄습니다.” xz — “한 사람의 우연한 호기심이 인터넷을 구했습니다. 반대로 말하면, 인터넷이 한 사람의 우연한 호기심에 매달려 있었다는 뜻입니다.”
영화 쪽 — “여러분이 오스카 시상식에서 본 그 영화, 아무나 지금 무료로 내려받을 수 있는 프로그램으로 만들었습니다.” / “오픈소스는 실리콘밸리 얘기가 아닙니다. 여러분에게 상을 주는 그 아카데미가 2018년에 직접 오픈소스 재단을 차렸어요. 조사해 보니 영화 산업의 84%가 이미 오픈소스로 굴러가고 있었거든요.” / “영상 파일 포맷 바꿔본 적 있으시죠. 그거 열에 아홉은 FFmpeg가 한 겁니다.”
⚠ “다빈치 리졸브가 오픈소스”는 오류다 — 말하지 않는다. 무료 버전이 있을 뿐이다. 색보정 쪽 오픈소스 사례는 OpenColorIO로 말한다.
⚠ 안드로이드 “39억~42억 대”는 업계 추정치다. 화면에 쓴 30억 대는 구글이 공식 확인한 마지막 숫자(2021.05, Google I/O)다. 굳이 최신처럼 말하지 않는다. / xz의 0.5초는 발견자 본인 메일과 보안업계 보도 기준이고, 위키피디아 본문에는 밀리초 수치가 없다.
닫는 한 줄 — “‘오픈소스는 암’이라던 회사가 20년 뒤 그 광장을 75억 달러에 샀습니다. 그런데 그 광장을 떠받치는 사람들 중 60%는 여전히 무보수입니다.” (75억 달러 이야기는 다음 장에서 다시 나온다 — 여기서는 한 문장만.)
| 암벽등반 | Git |
|---|---|
| 벽을 오른다 | 작업을 진행한다 |
| 확보물(말뚝)을 박고 로프를 건다 | 커밋 — “여기까지 됐다”고 못을 박는다 |
| 떨어져도 마지막 말뚝까지만 떨어진다 | 되돌리기 — 마지막 커밋 지점으로 |
| 아래로 이어진 로프 전체 | 히스토리(로그) — 지나온 자리가 다 남는다 |
| 같은 벽의 다른 루트 | 브랜치 — 원본을 건드리지 않고 딴 길을 시도 |
| 두 루트가 정상에서 만난다 | 머지 — 갈라졌던 작업을 하나로 합친다 |
이 장이 이 챕터의 목적지다. 비유가 주인공이고 연혁은 배경이다 — 위 네 칸은 15초에 훑고, 표와 사진에 4분을 쓴다.
강사 대사 — “커밋을 자주 박을수록 떨어질 때 덜 떨어집니다. 말뚝을 10미터마다 박으면 10미터를 떨어지고, 1미터마다 박으면 1미터만 떨어져요. final_최종_진짜최종이 사라지는 이유가 이겁니다. 파일 이름으로 버전을 관리할 필요가 없어져요.”
사진 셋을 순서대로 짚는다 — ① 손이 퀵드로를 거는 순간이 커밋. ② 로프가 아래로 계속 이어지고 말뚝마다 걸려 있는 게 히스토리. 오른쪽 확대 컷에서 말뚝 간격을 짚으면 “자주 박아라”가 한 번에 붙는다. ③ 아래에서 같이 출발해 중간에 갈라지는 두 루트가 브랜치다.
“분산”은 한 문장이면 된다 — “모든 참여자가 전체 역사를 통째로 갖고 있습니다. 회사 서버가 날아가도 아무나 한 명의 노트북으로 완전히 복구돼요. 인터넷이 끊겨도 혼자 계속 기록할 수 있고요.”
GitHub 한 줄 정리 — “Git은 도구고, GitHub는 그 도구를 쓰는 사람들이 모인 광장입니다. 2018년에 마이크로소프트가 그 광장을 75억 달러에 샀어요. 한때 ‘오픈소스는 암이다’라고 했던 그 회사가요.”
오른쪽 GitHub 캡처는 겁을 주려고 띄우는 게 아니다. “한 줄에 하나씩, 누가·언제·무엇을 고쳤는지가 적혀 있을 뿐입니다. 여러분이 오늘 만들 앱도 이렇게 남습니다.”
⚠ 라이브로 git 명령을 치지 않는다. 이 장은 개념만이고, 실제 커밋·배포는 뒤 챕터에서 결과 화면으로만 보인다.
이것부터 만드는 이유 — 규칙 설명이 필요 없고(각자 자기 생년월일), 결과가 개인적이라 예순 명이 각자 폰에서 자기 것을 본다. 오늘 세운 단어가 이 앱 하나에 거의 다 들어 있다.
개발 문서가 아니라 디자인 수칙 한 장으로 읽는다. 네 칸만 소리 내어 짚고 넘어간다.
"사람이 정할 건 대상·경험·목표·금지예요. 나머지는 도우미가 합니다." [47:05]
"6개월 전이면 훨씬 세부적으로 정의해야 한다고 했을 텐데, 지금은 생각하는 바를 자연어로 최대한 뱉어 보는 것이 더 중요해요." [18:32]
왜 이것부터인가 — 규칙 설명이 필요 없고(자기 생년월일), 결과가 개인적이라 60명이 각자 폰에서 자기 것을 본다. 그리고 이 앱 하나에 오늘 단어가 거의 다 들어 있다.
⚠ 현장 제작 경계 — P17까지 빈 폴더에서 라이브. P18 "한 줄 붙이기"까지 되면 최상. 그 뒤는 완성본을 연다. 2분.
화면 셋을 나란히 — 터미널 + 에디터 + 브라우저. 빈 폴더에서 시작한다.
① 터미널을 켜면서 P09 회수 30초: "무섭게 생겼는데 제가 치는 건 한국어예요." 이어서 P10 회수: "저는 맥이라 이렇게 치는데 윈도우는 다릅니다. 그래서 첫 문장에 '나 윈도우 11이야'."
② P16 기획 한 장을 그대로 준다. 화면 뼈대와 입력 폼(이름·생년월일·시각·모름 체크)이 나온다.
③ 트리를 세우고 — "이름 보면 뭐 하는 놈인지 알죠." P05 폴더 구조 이야기와 같은 자리다.
④ 브라우저로 연다. "여러분이 지금 보는 이 화면이 앞이에요. 오늘은 뒤에 우리 서버가 없어요."
⚠ 실패 시 — 에이전트가 늦으면 완성본 폴더를 열어 트리만 보이고 P18로 넘어간다. 8분을 넘기지 않는다.
음력과 절기 계산은 직접 만들면 며칠이 걸린다. 그런데 누가 이미 만들어서 공개해 뒀다 — 공짜인데 조건이 있고, 그 조건을 적어 둔 것이 라이선스다. 작품에 쓰는 사람에게는 이게 실제로 걸린다.
한자를 누르면 뜻이 붙는 것은 표 하나다 — data/hanja-glossary.json, 천간 10자 + 지지 12자 = 22자 고정. AI가 아니다.
"음력이랑 절기를 계산해야 하는데 이건 제가 못 만듭니다. 만들면 며칠 걸려요. 근데 누가 이미 만들어서 공개해놨습니다. 공짜인데 조건이 있어요. 그 조건 적어놓은 게 라이선스고, 작품에 쓰시는 분들은 이게 실제로 걸립니다."
한 줄 붙인다 → 이름·생년월일 넣는다 → 여덟 글자가 나온다. 사람 앞에서 대조할 세트(REPLAY §3, 독립 라이브러리와 교차 검증 완료): 1990-05-15 14:30 → 경오 신사 경진 계미 / 2000-01-15 14:20 → 기묘 정축 임신 정미. 새벽 검증 추가분: 홍길동/1980-05-15 → 경신 신사 무자 기미.
연주가 1999년 것으로 나오는 대목에서 — "입춘을 지나야 그 해가 시작됩니다. 사주 특유의 규칙이에요."
"30초 만에 됐죠. 남이 만든 부품을 갖다 쓴 겁니다."
한자 뜻은 — "이건 표 하나예요. AI 아니에요."
⚠ 실패 시 — CDN이 막히면 화면이 통째로 빈다(어젯밤 재현: 헤더만 남고 본문 없음, 에러 문구도 없음). 그 순간을 그대로 보여주고 "바깥 부품을 못 불러오면 이렇게 됩니다" → 완성본 캡처로. §준비물에 벤더링 항목 있음.
최소 생존 버전은 여기까지다 — 입력 폼 → 저장 → 여덟 글자 → 키 입력 → "오늘의 운세" 한 버튼 → 배포. 그 아래는 다 자를 수 있다.
저장 → 새로고침 → 그대로 있다를 눈앞에서 한 번. 그리고 개발자도구 Application 탭을 열어 saju_profile 키를 가리킨다.
"방금 페이지를 다시 불렀는데 아까 적은 게 그대로 있죠. 이 화면이 기억하고 있는 거예요. 이 기억은 여러분 폰 안에만 있고 어디로도 안 나가요."
"옆사람 폰엔 없어요. 남도 보게 하려면 남의 컴퓨터에 있어야 합니다. 그게 서버고, 거기 쌓아두는 게 데이터베이스예요. 오늘은 거기까지 안 갑니다."
여기서 완성본으로 전환한다(시간이 밀렸으면 P18부터 이미 완성본). REPLAY §0 최소 생존 버전 = 입력 폼 → 저장 → 여덟 글자 → 키 입력 → "오늘의 운세" 한 버튼 → 배포. 그 아래는 다 자를 수 있다.
키 이름 두 개는 실제 코드 그대로다 — saju_profile(profile-storage.js) · saju_gemini_key(app.js). 4분.
"사주 풀이 문장은 우리가 쓰는 게 아니라 구글 AI한테 전화를 걸어서 받아 와요. 전화 걸 때 '저예요' 증명하는 게 이 열쇠예요. 제 열쇠가 아니에요. 각자 자기 열쇠. 그리고 이 열쇠도 아까 그 기억 창에 저장되고, 딴 데로는 안 가요."
키 없이 누르면 앱이 안 죽고 안내 문구가 뜬다(어젯밤 확인). 한도가 넘으면 "1분 쉬었다 다시" 버튼이 뜬다. 강사 키로 오늘의 운세 한 번 받아서 보인다.
발급 절차는 다음 장(P20b)으로 넘긴다 — "무료로 2분이면 됩니다. 단톡방에 올려둘게요."
수업 소재(원격 실측, a8f1218에서 수정됨) — 처음엔 답이 "오늘은 2026년 9월"에서 잘렸다. gemini-2.5-flash가 눈에 안 보이는 "생각"에 토큰을 먼저 쓰는데(thoughtsTokenCount 479) 출력 예산 500을 거의 다 먹은 것. thinkingConfig: { thinkingBudget: 0 } + 예산 1024로 잡았다. "에러도 안 나고 그냥 답이 짧게 나와요. 모르면 프롬프트를 탓하게 됩니다. 이런 게 실무예요." — 라이브 중 답이 이상하게 짧으면 이 설정부터.
⚠ 남은 확인 하나 — 배포 주소에서 실제 제미나이 호출은 아직 아무도 안 돌렸다(원격은 키 입력이 안전 분류기에 막힘, 맥은 키 없음). 아침에 폰으로 주소 열고 본인 키 넣어 "오늘의 운세" 1회 — 30초. 그게 실제 사용 경로다(§준비물 1).
⚠ 실패 시 — 키 한도·API 오류가 나면 어제 미리 받아둔 샘플 응답 텍스트로. "이 AI도 하루 먹을 양이 정해져 있어요." 4분.
| 어디까지 | 필요한 것 | 시간 | 돈 |
|---|---|---|---|
| 사주 여덟 글자 · 한자 풀이 | 없음 | — | 없음 |
| 사주 AI 문장 (8주제) | A단계 키 | 2분 | 무료 한도 |
| 스토리팩 대화(플레이) | A단계 키 + .env.local | 3분 | 무료 한도 |
| 이미지 생성 (팩 에셋 · 게임 적 그림) | A + B단계 | 7분 | 최소 $10 선불 |
aistudio.google.com/apikey → 구글 계정으로 로그인 → 약관 동의처음 쓰는 계정이면 Google Cloud 프로젝트와 키가 자동으로 만들어진다. 이미 Cloud를 쓰던 계정은 자동 생성이 안 된다 → Projects 화면에서 프로젝트를 먼저 가져온(import) 뒤 만든다| 물건 | 넣는 곳 | 어디에 저장되나 |
|---|---|---|
| 사주앱오늘 QR | 앱 안 키 입력 화면에 붙여넣기 | 본인 폰 브라우저에만(로컬스토리지). 우리 서버는 없다. 딴 데로 안 간다 |
| 스토리팩 킷집에서 | .env.example을 복사 → 이름을 .env.local로 → GOOGLE_API_KEY= 오른쪽에 붙이고 저장 | 내 컴퓨터의 상자. 서버가 켜져 있었다면 완전히 끄고 다시 켠다 — 켜진 채로는 새 키를 안 읽는다 |
변수명이 둘이다 — 구글 문서는 GEMINI_API_KEY라고 쓰지만 우리 팩이 읽는 이름은 GOOGLE_API_KEY. 구글 SDK는 둘 다 받고 둘 다 있으면 GOOGLE_API_KEY가 우선이라 이 이름 하나로 양쪽이 맞는다.
aistudio.google.com/projects → 내 프로젝트 행 Billing Tier 열 → Set up billingaistudio.google.com/billing → Buy credits → 최소 $10aistudio.google.com/spend → Monthly spend cap을 프로젝트에 걸어 둔다지금 상태 보는 법 — Projects의 Billing Tier 열: Set up billing(결제 안 붙음 = 무료만) · Set up Prepay(계정은 붙었고 선불 설정 남음) · No credits(잔액 0 = 유료 호출 멈춤)GEMINI_API_KEY / GOOGLE_API_KEYP20 끝에 띄우고 한 마디만 — "키는 무료로 2분이면 만듭니다. 이 한 장을 단톡방에 올려두겠습니다." 시간 1분(P20에 흡수). 읽어 주지 않는다.
이 수업 권장 — 대화·사주 문장까지는 A만(카드 등록 없음). 전원 여기까지면 충분하다. 이미지까지 해보고 싶은 분만 A + B, $10 충전 · auto-reload 끄기. 실사용은 팩 하나 $3 안쪽이지만 최소 충전액이 $10이라 못 내린다. 대신 선불 자체가 하드캡이라 후불 카드보다 안전하다.
"무료로 몇 번 되는지"는 단정하지 않는다. 구글이 숫자를 문서에서 뺐고 계정마다 다르다. 본인 것은 aistudio.google.com/rate-limit 에서 본다. 수요일에 무료 한도가 소진돼 주인공 한 장에서 멈춘 게 이 때문이다.
근거 — ai.google.dev(api-key · quickstart "Google AI Studio automatically creates a default project and API key for new users" · billing "minimum of $10 in prepaid credits", 상태 3종) 2026.09.05 재확인. 볼트 02_보강_2026.09.02의 구글API키_획득절차 · 학생용_무료API키_발급안내와 일치.
순서는 넷 — ① 폰에서 localhost:8080 → 안 열린다 ② 드래그로 올린다(Cloudflare Pages) ③ 주소가 뜬다 ④ QR.
①에서 — "지금은 저만 볼 수 있어요. 아직 제 컴퓨터 안에만 있거든요." (P13에서 예고했던 20초를 여기서 회수한다.)
②③ — "남의 컴퓨터에 올립니다. 이름 하나를 빌린 거예요."
④ — "지금부터 여러분 폰에 이 주소를 찍어 보세요. 각자 자기 생년월일을 넣으면 자기 사주 여덟 글자가 나와요. 앱스토어도 가입도 필요 없어요." 키 없는 학생도 여덟 글자·한자는 본다. AI 문장은 "키 넣으면 됩니다. 발급은 집에서."
개인정보는 정확하게 — "여러분 생년월일은 저희가 만든 서버로는 절대 안 가요. 애초에 그런 서버가 없거든요. AI 문장 누르는 순간만 구글로 직접 갑니다."
⚠ 주소 — 반드시 https://srl-saju-fortune.pages.dev. 계획 문서들이 적어 둔 saju-webapp.pages.dev는 존재하지 않는 주소다. QR도 이 주소로 만든다.
⚠ 실패 시 — 라이브 배포가 막히면 사전 배포본 https://srl-saju-fortune.pages.dev 로 간다(200 응답, 로컬 a8f1218과 SHA 일치). 5분.
wrangler pages deploy)로 한다.| 어디까지 | 필요한 것 | 시간 | 열쇠 |
|---|---|---|---|
| ① 폴더를 드래그해서 올린다 | Cloudflare 계정(이메일) | 3분 | 없음 |
② 터미널에서 올린다 (wrangler login) | 계정 + Node | 5분 | 없음브라우저 로그인 한 번 |
| ③ AI 에이전트가 대신 올린다 | 계정 + API 토큰 + Account ID | 7분 | 있음이 페이지의 이유 |
dash.cloudflare.com → Sign up → 이메일·비밀번호 → 인증 메일 확인*.pages.dev)만 쓴다이름.pages.dev). 나중에 바꾸기 번거로우니 처음에 정한다hanyejong-pages 같은 알아볼 이름Account · Cloudflare Pages · Edit — 이 한 줄이면 된다Include · 내 계정npx wrangler whoami로도 나온다프로젝트 폴더의 .env.local — 값은 여기 안 적는다 CLOUDFLARE_API_TOKEN=(C에서 복사한 토큰) CLOUDFLARE_ACCOUNT_ID=(D에서 복사한 ID)
npx wrangler pages deploy site --project-name=내이름-saju
hong-sajuCtrl+Shift+R). 수업 중 미리 겪어 둔다wrangler login이 브라우저 창을 못 띄운다(윈도우)P21 끝에 띄우고 한 마디만 — "집에서 하실 분은 이 한 장이면 됩니다. 단톡방에 올려두겠습니다." 시간 1분(P21에 흡수). 읽어 주지 않는다.
③(에이전트가 대신 올린다)은 "에이전트한테 '올려줘'까지 시키는" 사람의 길이다. 오늘 강사는 ①(P21 드래그) 또는 ②(REPLAY의 wrangler pages deploy)로 한다.
구글 키와의 차이를 한 번 짚는다 — Cloudflare Pages는 돈이 안 달려 있다(무료 플랜, 정적 사이트 무제한). 그런데도 열쇠인 이유는 토큰이 새면 남이 내 사이트를 덮어쓰거나 지울 수 있어서다.
근거 — Cloudflare Pages 문서: Direct Upload(드래그 · wrangler pages deploy), CI용 토큰 = Account · Cloudflare Pages · Edit, 환경변수 CLOUDFLARE_API_TOKEN · CLOUDFLARE_ACCOUNT_ID. 볼트 사주 04 §5 배포 절차와 일치.
| 부품 | 어디서 | 조건 |
|---|---|---|
| 엔진 Phaser 3 | CDN 한 줄 | 오픈소스 |
| 그림 기체·적·총알·폭발·아이템 | Kenney.nl | CC0 (표기·허락 없이 써도 됨) |
| 소리 BGM 6 · 효과음 12 | OpenGameArt (SketchyLogic 등) | CC0 |
게임 엔진도, 그림도, 소리도 남이 만들어 공개해 둔 것이다. 이게 없으면 이 게임은 못 만든다.
오픈소스가 나오는 두 번째 자리다. 아까는 계산기 하나였고, 이번엔 엔진·그림·소리가 전부다.
조건이 표기인 것도 섞여 있다 — assets/CREDITS.txt가 그 표기를 모아 둔 파일이다.
완성본 타이틀 화면 한 장 + 부품 목록. 1분.
"게임 엔진도 남이 만든 겁니다. 그림하고 소리도요. 이거 없으면 이 게임 못 만들어요."
오픈소스 두 번째 — 아까는 계산기 하나, 이번엔 엔진·그림·소리 전부.
assets/CREDITS.txt를 잠깐 띄운다: "조건이 '표기'인 것도 있어요. 이 파일이 그겁니다."
붙는 단어: 오픈소스 ○ · 라이선스 ○
⚠ 스프라이트 실물은 img/sprites/의 진짜 파일이다(32~104px). 확대해서 보여주는 것이라 각이 진다 — 원래 그렇다고만 말하고 넘어간다.
boss_body.png가 1024×64인 것을 여기서 한 번 보여 두면, P24의 "보스가 찌그러져 보인다"가 설명 없이 이해된다.
REPLAY 실측으로 1~3단계는 85분이 걸렸다. 오늘 무대에서 하는 것은 골격까지 10분이다.
화면 셋을 나란히 — 터미널 + 에디터 + 브라우저.
REPLAY 1~2단계, 필요하면 3단계까지. 4번은 시간이 될 때만.
명령어를 칠 때 P10(기기와 운영체제 — 같은 말을 다르게 친다)을 한 번 더 회수한다.
파일 이름을 보이면서: "이름을 왜 이렇게 붙였을까요. 사람도 알아보고 기계도 알아보라고요."
붙는 단어: 윈도우/맥 ● · 파일 이름·구조 ● · 소스 코드 ○ — 10분.
⚠ 시간 관리 — REPLAY 실측은 1~3단계 85분이다. 오늘은 골격만 10분이 목표다. 2번(이동)에서 막히면 붙들지 말고 완성본으로 전환하고, 골격은 미리 찍어 둔 캡처 3장으로 대체한다.
⚠ bullet_player_viper.png는 화면에서 점처럼 작아 스프라이트 실물을 안 띄운다(그 줄만 그림 없음). 대신 이름이 세 토막이라는 것만 짚는다.
새 탭에 주소를 치고, 강사가 30초 플레이한다.
코드 폴더를 잠깐 띄우되 읽지는 않는다 — 이렇게 살이 붙었다는 것만 본다. 이걸 다 라이브로 하면 오늘은 라이브 코딩만 보다가 끝난다.
라이브로 만든 골격(P23)과 같은 물건이다. 그 위에 시간을 더 부은 것뿐이다.
"이제 어젯밤에 미리 다 만들어둔 완성본을 열겠습니다."
"이렇게 살이 붙었다는 것만 보세요. 이걸 다 라이브로 하면 오늘 라이브 코딩만 보다가 끝납니다."
━━━ ⚠ 시연 자세 (새벽 실측) — 이 세 가지를 지키지 않으면 무대에서 멈춘다 ━━━
⚠ ① 하단 중앙을 클릭하고, 커서를 거기 둔 채 마우스로 조작한다. 키보드만 쓰면 기체가 좌상단(12,12)에 박힌다 — player.js:59가 키 입력 200ms 뒤 마우스 포인터를 따라가는데, 포인터가 캔버스에 안 들어왔으면 (0,0)이라 거기로 간다. 화면 안내문의 "방향키"를 믿고 키만 누르면 무조건 터진다.
⚠ ② 보스전에서 3번 키(펄스레이저) 금지. 사거리 225px(pierceDuration 0.25s × 900px/s)라 보스(y≤196)에 안 닿는다 — 실측 데미지 0. 보스전 중엔 잡몹이 안 나와(spawner.js:36) 아이템으로 무기 교체도 불가능하다. 그 자리에서 시연이 멈춘다.
⚠ ③ 보스 그림이 찌그러져 보이는 건 말로 넘긴다. 1024×64 그림을 120×100으로 눌러서다(boss.js:25). 빔에서 티가 난다. → "보스는 그림이 아직 못생겼습니다. 이것도 이따 고치는 목록에 올립니다."
붙는 단어 없음. 2분.
순서: 이미지 생성 → assets/sprites/enemy_weak/enemy_scout.png 덮어쓰기 → 하드 리프레시(Ctrl+Shift+R / Cmd+Shift+R) → 다른 색 적이 내려온다.
"지금 얘한테 그림 하나를 새로 그려 달라고 시킬 건데, 다른 회사 서비스한테 전화를 거는 거예요. 아까 그 열쇠, 여기서 또 씁니다." — P20 API 키 회수.
함정을 숨기지 말고 그대로 보여준다. 일반 새로고침으로는 안 바뀐다. "브라우저는 그림을 한 번 보면 기억해 두려고 해서, 강제로 다시 불러오라고 알려줘야 해요. 그게 방금 누른 새로고침이에요."
⚠ REPLAY C-1: 대본을 쓰다가 실제로 한 번 걸렸다. 무대에서 당황하지 말고 그 자리에서 설명 재료로 쓴다.
붙는 단어: API 키 ●(회수) · 캐시 –(한 마디) — 8분.
ufo: {
key: 'ufo', name: 'UFO',
hp: 2, speed: 90, bulletSpeed: 200,
fireInterval: 2.0, score: 200,
pattern: 'straightDown',
attack: 'straight', tier: 'weak',
sprite: 'enemy_scout'
},types: [ … , 'ufo' ] // 단어 하나만 더한다저장 → 새로고침. 이건 일반 새로고침으로 된다 — 그림이 아니라 코드라서.
건드릴 데와 안 건드릴 데를 먼저 말하는 것이 프롬프트를 잘 쓰는 것보다 중요하다.
게임을 움직이는 큰 코드는 이 목록만 읽고 "적이 몇 종류 있구나"를 안다. 그래서 적 하나 추가는 게임 전체를 다시 짜는 일이 아니라 목록에 한 줄 더하는 일이 된다.
P05에서 본 구조 자체가 자산이다의 회수 지점이다.
먼저 변경 범위를 말한다: "건드릴 데하고 안 건드릴 데를 먼저 말합니다. 이게 프롬프트 잘 쓰는 것보다 중요해요." — enemies.js(움직이는 코드)는 한 줄도 안 건드린다.
"이 목록 하나만 봐주세요. 게임을 움직이는 큰 코드는 이 목록만 읽고 '적이 몇 종류 있구나' 하거든요. 그래서 적 하나 추가하는 게 게임 전체를 다시 짜는 일이 아니라, 이 목록에 한 줄 더하는 일이 돼요." — P05 "구조 자체가 자산"의 회수.
붙는 단어: 변경 범위 ● · 구조=자산(회수) — 6분. 시간이 밀리면 제일 먼저 자르는 장이다.
⚠ 계획서 불일치 — 볼트 04_수업활용_리허설.md는 enemies.json을 가정했지만 실제 구현엔 그 파일이 없다. 무대에서는 이 문서/REPLAY.md만 본다. 학생이 파일명을 물으면 difficulty.js라고 답한다.
⚠ 실패 시 — REPLAY는 "7종 버전 별도 배포 주소"를 대체로 적었다. 그 주소가 실제로 있는지 아침에 확인(준비물 항목).
SHMUP.LIVE_TUNE.enemySpeedMultiplier = 3.0 // 적 속도 3배 SHMUP.LIVE_TUNE.spawnInterval = 0.3 // 등장 간격 30%
영구히 바꾸려면 difficulty.js 맨 위 LIVE_TUNE 세 값을 고치고 저장 → 새로고침.
SHMUP.BOSS_WARN_SEC = 10; SHMUP.BOSS_START_SEC = 15; SHMUP.BOSS.hp = 80;
같은 장의 연장이라 자연스럽다 — 숫자를 세 개 더 바꿨더니 보스가 15초에 온다.
치기 전에 먼저 묻는다 — 이 두 숫자가 바뀌면 게임이 어떻게 달라질 것 같은지.
그리고 방금 그것을 무섭게 보이던 콘솔에서 했다. P15의 뒤집기다.
치기 전에 묻는다: "숫자 두 개만 바꿀 거예요. 하나는 적이 내려오는 속도, 하나는 나오는 간격. 이게 이렇게 바뀌면 게임이 어떻게 달라질 것 같으세요?"
바뀐 뒤: "숫자 하나 바꿨는데 게임이 달라지죠. 그리고 방금 그 무서운 콘솔에서 한 거예요." — P15(문제를 볼 때)의 뒤집기.
영구히 바꾸려면 difficulty.js 맨 위 LIVE_TUNE 세 값 → 저장 → 새로고침.
보스 시연 (실측 검증) — 보스는 고정 105초에 나오고 잡는 데 2~3.5분이 더 든다(호밍니들 113초 · 바이퍼 178초). 자연 플레이로는 4~5분. 그래서 시작 전에 콘솔 한 줄을 미리 친다. "숫자 세 개 더 바꿨더니 보스가 15초에 옵니다."
⚠ 보스전에 들어가면 3번 키(펄스레이저)를 쓰지 않는다 — 사거리가 안 닿아 데미지 0이다(P24 시연 자세 ②).
붙는 단어: 소스 코드 ● · 콘솔 ○(무섭지 않다) — 4분.
$ npx wrangler pages deploy site
폰에서 열었을 때 — 세로 화면
같은 주소가 PC에서도 폰에서도 열린다. 60명 최초 로드가 1인당 2.1MB — QR은 쉬는 시간에 미리 열게 하면 안전하다.
"고칠 때마다 올립니다. 이 기록을 훨씬 촘촘하게 해주는 게 Git이에요." — P12 회수, 여기까지만.
"같은 주소가 PC에서도 폰에서도 열립니다." → 예순 명이 각자 폰으로 한 판. 시간이 남으면 적 하나를 더 라이브로 추가한다.
폰 안내 문구: "폰에서는 손가락으로 끌면 됩니다. 폭탄은 PC에서만요. 금방 죽는 게 정상이에요."
⚠ 하단 안내문이 폰에서는 틀린 말이다("방향키/WASD+마우스 · SPACE · 우클릭"). 학생이 그걸 보고 헤매기 전에 먼저 말해 준다.
⚠ 실물 iOS Safari·Android Chrome은 미검증이다(에뮬레이션만 통과). 아침에 폰 1대로 연타·핀치 줌을 확인한다.
⚠ 60명 최초 로드 = 1인당 2.1MB. QR을 쉬는 시간에 미리 열게 하면 동시 접속 부하를 피할 수 있다.
붙는 단어: 재배포·Git ○ · 반응형·기기 ○ — 3분.
챗봇은 뒤가 있어서 기억이 남았다. 그래서 오늘 그것만 드래그로 못 올렸다 — P04의 상자 셋, P13의 "화면은 앞, 서버는 뒤".
사주 웹앱의 기억은 각자 폰 안에만 있었다. 옆사람 폰엔 없었다 — P19.
오늘 만진 셋의 차이가 여기서 한 문장으로 묶인다. 뒤가 있느냐 없느냐.
게임오버 화면. 점수. 새로고침하면 사라진다. 1분. 짧게 끊는다.
"점수가 저장이 안 되죠. 이 게임엔 뒤가 없어요. 그래서 점수가 안 남습니다. 남기려면 서버가 필요하고, 그때부터 다른 얘기가 됩니다. 아까 챗봇은 뒤가 있어서 기억이 남았고, 그래서 드래그로 못 올린 거예요."
P04(화면·서버·팩) · P13(웹이 돌아가는 구조) · P19(기억은 내 폰 안에만)를 한 문장으로 묶는 장이다. 새로 설명하지 않는다.
붙는 단어: 백엔드 ●(회수) · 데이터베이스 – — 1분.
학생들은 이 말을 듣는 동안에도 폰으로 계속 게임을 하고 있다(P28). 그대로 두고 P30으로 넘어간다.
P08에서 봤던 그 지도 그대로. 이번엔 칸마다 오늘 만든 것이 들어가 있다. 새 단어를 얹지 않는다 — 이미 나온 것만 제자리에 놓는다.
왼쪽부터 짚는다. 내 컴퓨터 — 사주앱 화면과 내 사주(기억), 게임 코드와 difficulty.js. 남의 컴퓨터 — 두 주소(pages.dev). "올렸으니 남도 봅니다. 다만 점수는 여기 없어요 — 각자 폰에만 있습니다." 바깥 — 구글 AI, 이미지 생성, 열쇠와 비용. 그리고 왼쪽으로 들어온 것: 만세력·Phaser·Kenney.
회수 문장 다섯을 하나씩 읽는다. 설명을 덧붙이지 않는다.
클로징: "어떤 시스템과 스토리텔링이 합쳐져 돌아가는 구조를 만드는 것, 전체가 그렇게 어려운 과업은 아닙니다." [27:39]
5분.
네 줄만 읽고 끝낸다.
① 오늘 만든 것 중 하나를 자기 재료로 바꿔 본다 — 사주앱의 주제 버튼 하나, 게임의 적 그림 하나. 둘 다 안 해도 된다. 하나면 된다.
② 안 되면 어디까지 됐는지 캡처를 남긴다. 환경 문제는 창작 실패가 아니다. "실패해도 어디까지 됐는지 말할 수 있으면 됩니다."
③ 주소가 나오면 단톡방에.
④ (선택) P10에서 본 실전 규칙 — AI에게 첫 문장으로 내 환경을 말하는 습관. "나는 윈도우 11이야."
두 주소는 화면에 띄운 채로 두고, 단톡방에도 올린다. 2분.