2026.09.05 · 한국예술종합학교 · 2회차

바이브 코딩의
지형지물

1 · 챗봇지난주 그 사이트 — 열어서 뜯어본다 · 3–23분
2 · 랜드스케이프바이브 코딩 어휘 — 거기서 나온 단어로 지도를 세운다 · 23–48분
3 · 사주 웹앱실습 1 — 그 지도 위에서 만든다 · 48–76분
4 · 슈팅게임실습 2 — 만들고, 고친다 · 76–110분

수강생은 따라 치지 않습니다 · 마지막에 각자 폰으로 접속합니다

P01 · 오늘의 지도 · 3분 · 2회차 120분 = 오프닝 3 · 챗봇 20 · 랜드스케이프 25 · 사주 28 · 슈팅 34 · 회수+과제 7 · 버퍼 3

가져갈 문장 — "오늘도 문법은 안 가르칩니다. 지형을 그립니다."

수요일 육성 그대로: "한 시간 만에 코딩을 알 리가 없습니다. 중국어 모르는 사람한테 한 시간 만에 중국어 못 가르쳐요." [10:02]

"그런데 중국 여행에는 중국어보다 — 몇 개의 성이 있고, 어떤 제스처가 기분 나쁘고, 공안은 무서운 사람이라는 정보가 더 중요할 수 있어요. 오늘은 그겁니다. 중국 옆에 한국, 오른쪽에 동해." [10:34] [54:14]

오늘 순서: 지난주 그 사이트를 연다 → 거기서 나온 단어로 지도를 세운다 → 그 위에서 두 개를 만든다. 여러분은 따라 치지 않습니다. 마지막에 폰만 꺼내세요.

화면은 4챕터 제목만 세로로. 그림 없음. 3분.

챗봇 사이트 소개
① 첫 화면 — "모든 스토리"

팩 카드 · 포스터

② 팩 선택 후 오프닝

이야기가 시작되는 첫 장면

③ 대화 + 배경·캐릭터

장면에 맞는 배경과 캐릭터 이미지가 뜬다

④ 선택지 A/B/C

다음 행동을 고른다

Story Deck — 대화형 스토리 아카이브

Story Deck — Interactive Story Archive
주소localhost:3010 · Next.js · 로컬
red_riding_hood(빨간 모자) · broken_hallway — 2개

지난주에 딸깍으로 만든 그 사이트. 오늘은 열어서 뜯어봅니다.

이야기 하나가 폴더 하나로 들어가 있고, 그 폴더를 AI가 읽으면서 연출을 한다.

P02 · 3분 · 붙는 단어 없음(아직) · 준비물 스크린샷은 아침 실제 Chrome에서

"지난주에 딸깍으로 만든 그 사이트입니다. 오늘은 이걸 열어서 뜯어봅니다."

40~50대 영화인이 보고 있다는 걸 잊지 않는다: "이야기 하나가 폴더 하나로 들어가 있고, 그 폴더를 AI가 읽으면서 연출을 합니다."

붙는 단어: 없음(아직). 이름을 얹기 전에 그림부터. 3분.

⚠ 준비물 — 스크린샷은 아침에 실제 Chrome에서 찍는다. 09.02 핸드오버 §7-7: 자동화 브라우저에서는 포스터·씬 이미지가 안 실렸다. 육안 확인이 곧 준비다.

실제 동작 — 화면과 로그
$ tail -n 20 logs/sessions/2026-09-04_16-53_red_riding_hood_session-3882/events.jsonl
{"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…
  1. 팩을 열고 대화를 두세 줄만 굴린다 — 배경이 바뀌고 캐릭터가 뜬다
  2. 옆 터미널을 가리킨다 — 로그가 흐른다. 화면 뒤에서 누가 일하고 있다
  3. 선택지를 하나 고른다 — 이 선택이 어딘가에 적힌다. 기억
P03 · 5분 · 화면(프론트엔드) ● · 서버·콘솔·로그 ● · 실패 시 P02 스크린샷으로 대체하고 P04로

두 창을 나란히 — 브라우저(사이트) + 옆에 터미널 로그.

① 팩을 열고 대화를 두세 줄만 굴린다. 배경이 바뀌고 캐릭터가 뜨는 걸 본다.

② 옆 터미널을 가리킨다. 로그가 흐른다. "화면 뒤에서 누가 일하고 있죠. 무슨 일을 했는지 여기 남습니다."

③ 선택지를 하나 고른다. "이 선택이 어딘가에 적힙니다. 기억이죠." [03:38]

붙는 단어(이름만 얹는다, 정의 안 함): 화면(프론트엔드) ● · 서버·콘솔·로그 ● — 5분.

⚠ 실패 시 — 로컬 서버가 안 뜨면 P02 스크린샷으로 대체하고 P04로 간다. 어젯밤 확인: 맥에 3010이 이미 떠 있다(+ 3011·3013·3020 중복 서버 — 아침에 하나만 남길 것).

오른쪽 로그 박스는 실제 logs/sessions/2026-09-04_16-53_red_riding_hood_session-3882/events.jsonl 마지막 20줄(폭에 맞춰 잘라 표시). 라이브에서는 진짜 터미널을 띄운다.

아키텍처 — 화면 · 서버 · 팩
Frontend
화면
앞무대
←→
Backend
서버
뒤 — 기억이 남는다
←→
Story pack
팩 = 이야기 폴더
재료

실행되면 서버 안에 기억이 남는다. LLM은 기억력이 좁아서 서버는 팩에서 필요한 장만 읽는다 — 현재 챕터와 옆 챕터만.

오늘의 포인트 — 준비된 리소스를 LLM이 에이전틱하게 골라 쓴다팩 안의 배경·캐릭터·음악·효과음은 이름표 붙은 목록(04_resources.txt)으로 들어 있고, 매 턴 무엇을 꺼내 쓸지는 LLM이 정한다 — 사람이 "이 장면엔 이 그림"이라고 일일이 안 붙인다. 주방을 꾸려 놓은 것.

알아서 하는 게 아니라, 수칙을 읽고 일하는 작업 도우미다.

P04 · 5분 · 프론트엔드/백엔드 ● · 서버 ● · 컨텍스트(기억력) ○

상자 셋. 그 이상 그리지 않는다.

"화면 / 화면을 띄우는 서버 / 스토리팩. 실행되면 서버 안에 기억이 남습니다." [00:41]

"LLM은 기억력이 좁아요. 처음엔 똑똑하다가 엉뚱한 소리를 하는 이유가 그겁니다. 그래서 필요한 장만 읽게 짰어요." [01:41] — 현재 챕터와 옆 챕터만 준다.

오늘의 포인트: 팩 안에는 배경·캐릭터·음악·효과음이 이름표 붙은 목록으로 들어 있고(04_resources.txt), 매 턴 어떤 걸 꺼내 쓸지는 LLM이 정한다. 사람이 "이 장면엔 이 그림"이라고 일일이 안 붙인다. "재료를 갖춰 놓으면 알아서 꺼내 씁니다. 으레 식탁 위에 포크와 나이프가 있죠. 에이전트가 눈 뜨자마자 '아, 여기서 밥을 푸는구나' 알도록 주방을 꾸려 놓은 겁니다." [13:29]

한 줄로: "이건 알아서 하는 게 아니고 수칙을 읽고 일하는 작업 도우미예요." [46:35]

붙는 단어: 프론트엔드/백엔드 ● · 서버 ● · 컨텍스트(기억력) ○ — 5분.

스토리팩의 구조
public/game-packs/red_riding_hood/팩 하나 = 이야기 하나
manifest.json제목·모델·캐릭터·엔딩
poster.webp
prompts/
system/ 00_response_format · 01_gm_role · 02_character · 03_current_state← 안 건드림
content/ 00_opening · 01_worldview · 02_characters · 04_resources · story/← 이야기
resources/
backgrounds/ 5장 · characters/ 4장 · scenes/ 11장 · music/ 9 · effects/ 13 · endings/ 4
resource_list.json← 이 전부를 만들어 낸 명세

내용과 뼈대를 분리한 폴더 구조

본다에이전트는 폴더를 열면 이름과 구조만 본다 — _template이 있으면 "여러 개 붙는 구조구나"를 이름만으로 안다
따로내용과 뼈대가 따로 — 내용만 갈아끼운다. "고등학교 일진의 모험담" 한 문장이 주인공이 된 이유
반대엉망진창 다운로드 폴더에 "분석해 줘" — 그다음부터 엉망진창으로 일한다

구조화돼 있으면 LLM이 그걸 자산화해서 이미지든 뭐든 해준다.

P05 · 4분 · 프로젝트·폴더·파일 ● · 컨텍스트 ○ · CLAUDE.md ○

폴더 트리 한 장. 실제 경로 그대로. 옆에 packs/_template/(빈 틀)도 보인다.

"에이전트는 폴더를 열면 이름하고 구조만 봅니다. _template이 있으면 '아, 여러 개 붙는 구조구나'를 이름만으로 압니다." [20:44]

"내용하고 뼈대가 따로 있으니까 내용만 갈아끼울 수 있어요. 지난주에 '고등학교 일진의 모험담' 한 문장 던졌더니 주인공이 만들어졌잖아요. 그게 되는 이유가 이 폴더예요." [11:37]

반대 사례(웃긴 대목): "엉망진창인 다운로드 폴더 잡고 '분석해 줘' 하면, 그다음부터 엉망진창으로 일합니다." [44:55]

결론 한 줄: "구조 자체가 자산입니다. 구조화돼 있으면 LLM이 그걸 자산화해서 이미지든 뭐든 해줍니다." [44:25]

CLAUDE.md가 보이면 한 마디만: "네오가 깨어나 처음 읽는 글." [12:05]

붙는 단어: 프로젝트·폴더·파일 ● · 컨텍스트 ○ · CLAUDE.md ○ — 4분.

에셋 생성 — 명세 하나에서 이미지·음악·효과음으로
resources/resource_list.json명세 한 파일
"style_guide"화풍 · 품질 · 금지어
"backgrounds" [ ] { id, name, description, prompt, mood }
id: "bg_forest_fork" name: "숲의 두 번째 길"
"characters" [ ] id: "char_red" name: "빨간 모자"
"scenes" [ ] · "endings" [ ]
"music_descriptions" [ ] id: "bgm_calm_joyful_sunlit_morning"
"sound_effects" [ ] id: "sfx_red_boots_forest_steps"
npm run pack:images · pack:audio · pack:video
resources/실제로 생성된 파일
backgrounds/ 5 characters/ 4 scenes/ 11
music/ 9 effects/ 13 endings/ 4
01
명세
배경·캐릭터·씬·음악·효과음·엔딩을 한 파일에 적는다
02
Dry Run
--dry-run — 몇 건이 생성될지만 출력. 돈이 안 나간다
03
병렬
명시하지 않으면 한 장씩 만든다
이미지 제공자를 갈아끼운다--provider gemini(기본) · byteplus(Seedream). 텍스트 하나가 여러 양태로 뻗는다.
$ tail logs/sessions/…_red_riding_hood_session-3882/events.jsonl — 두 번째 턴, LLM이 목록에서 골라 낸 것
{"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":"…
연출을 정하고(gm_direction) → 음악과 효과음을 걸고(staging_command) → 배경을 고르고(action_executed) → 기억에 적는다(story_update).
골라 쓴 trackId · sfxId · imageId는 전부 resource_list.json에 있던 id다.
P06 · 3분 · API ● · API 키·비용 ● · 명세·제작 목록 ○ · Dry Run·병렬 ○ · 라이브 생성은 안 한다 — 사전 생성 결과를 연다

왼쪽은 명세, 오른쪽은 절차, 아래는 실제 로그. 라이브 생성은 하지 않는다 — 이미 나온 결과를 연다.

"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분.

챕터 1 마무리 — 에이전트를 무엇으로 볼 것인가
“에이전트를 바이브 코딩·웹사이트 만드는 걸로만 보지 마세요.
구조화된 재료로 프로젝트에 필요한 리소스를
생성하는 도구로 보세요.”
P07 · 1분 · 붙는 단어 없음 · 한 문장만 읽고 챕터 2로

문장 하나만. 설명을 덧붙이지 않는다.

"에이전트를 바이브 코딩·웹사이트 만드는 걸로만 보지 마세요. 구조화된 재료로 프로젝트에 필요한 리소스를 생성하는 도구로 보세요." [37:07]

1분 → 챕터 2(랜드스케이프)로 넘어간다.

지도 한 장 — 단어를 놓을 세 칸
Local
내 컴퓨터
  • 브라우저 · 화면
  • 폴더 · 파일 · 구조
  • 로컬 · 내 기억
Server
남의 컴퓨터
  • 서버 · 배포
  • 주소 · IP · 도메인 · DNS
  • (오늘 우리 서버는 없다)
External
바깥
  • API · 열쇠 · 비용
  • ← 오픈소스로 받아온 것
IP 주소172.66.47.83기계가 쓰는 번지수. 남의 컴퓨터가 인터넷 어디에 있는지를 숫자로 적은 것
도메인srl-saju-fortune.pages.dev사람이 외우는 이름. 번지수 대신 부르는 상호
DNS이름 → 번지수이름을 번지수로 바꿔 주는 전화번호부. 주소창에 이름을 치면 먼저 여기에 물어본다
시간을 쓴다 이름만 스친다 오늘 안 꺼낸다 · 질문이 나오면 그때 한 마디
P08 · 3분 · 주소 ● · IP ○ · 도메인 ○ · DNS ○ · 다음 일곱 장이 놓일 세 칸을 세운다

챕터 목적 — "꽌시가 무슨 말인지 몰라도 되지만 그런 개념이 있다는 건 알아야 합니다." [10:19] 외우는 게 아니다. 다음에 만났을 때 "아, 그거"가 되면 된다.

"용어를 알아야 한다는 것에 사로잡히기 쉬운데, 그보다 관계가 중요하고, 그 이전에 구조화하는 사고방식이 중요합니다." [10:49]

원칙 — 정의를 먼저 말하지 않는다. 방금 P03~P06에서 본 화면에 이름을 얹거나, 곧 실습에서 만날 자리를 예고한다. 장마다 "오늘 어디서"가 있는 이유.

세 칸을 손으로 훑으면서 한 번만 걷는다. 왼쪽 = 내 컴퓨터, 가운데 = 남의 컴퓨터, 오른쪽 = 바깥.

주소 · IP · DNS — 가운데 칸에서 한 번 짚는다. "남의 컴퓨터에 올렸으면 찾아갈 방법이 있어야 하죠. 기계는 숫자로 찾습니다 — 그게 IP 주소, 번지수예요. 근데 사람이 172.66.47.83을 어떻게 외웁니까. 그래서 이름을 붙입니다 — 그게 도메인이에요. 그 이름을 번지수로 바꿔 주는 전화번호부DNS고요."

이어서: "여러분이 주소창에 이름을 치면, 브라우저가 먼저 DNS한테 물어봅니다 — '이 이름 번지수가 뭐야?' 번지수를 받아서 그제야 그 컴퓨터에 갑니다. 이게 매번 일어나요."

회수 예고: P21·P28에서 *.pages.dev 주소가 생기는 순간 "이름 하나를 빌린 것"이라고 다시 짚는다. 도메인 구입·연결은 오늘 안 한다 — 질문이 나오면 "그 이름을 사서 이 번지수에 연결하는 것"까지만.

3분.

컴퓨터에 말을 거는 방법
용어말 한 줄영화 쪽 비유등급
CLI (명령줄)마우스로 고르는 대신 글자로 지시하는 창메뉴판을 손가락으로 고르기 vs 주방에 대고 말로 주문하기
터미널그 창을 띄우는 프로그램인터폰 단말기
명령어그 창에 치는 한 줄“3번 조명 20% 다운”
왜 굳이 글자로 치는가
  1. 손이 빠르다
  2. 기록이 남는다 — 같은 주문을 적어두면 다음에 그대로 다시 쓴다
  3. AI가 대신 칠 수 있다

그래서 오늘 강사가 터미널에 치는 건 대부분 한국어다.

오늘 어디서P17 사주 라이브에서 터미널을 켜는 순간 — 그때 이 장을 30초로 되짚는다
P09 · 4분 · CLI(명령줄) ○ · 터미널 ○ · 명령어 ○ · P17에서 30초 회수

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 기준으로 알려줘.이 한 줄이 없으면 에이전트가 맥 명령어를 던지고, 학생은 “안 되는데요”에서 멈춘다.
오늘 어디서P23 슈팅 라이브에서 명령어를 칠 때 — 맥과 윈도우가 갈리는 자리를 그 화면에서 다시 짚는다
P10 · 4분 · 윈도우/맥 ● · PC ○ · 운영체제(OS) ○ · 아이폰/안드로이드폰 ○

인터넷 안내문 대부분이 맥 기준이다. 윈도우 쓰는 사람은 읽을 때마다 한 번씩 번역해야 하는데, 그 번역을 지금은 AI가 해준다.

실전 규칙 하나(이 장에서 가져갈 유일한 것): "나는 윈도우 11을 쓰고 있어. 명령어는 PowerShell 기준으로 알려줘." — 이 한 줄이 없으면 에이전트가 맥 명령어를 던지고, 학생은 "안 되는데요"에서 멈춘다. 수요일 막힘의 상당수가 여기였을 것.

폰은 만드는 쪽이 아니라 보는 쪽이다. 오늘 만드는 건 전부 웹. 폰은 결과를 확인하는 자리에만 등장한다(P13에서 이어짐).

P23에서 회수할 말: "저는 맥이라 이렇게 치는데 윈도우는 다릅니다."

붙는 단어: 윈도우/맥 ● · PC ○ · 운영체제(OS) ○ · 아이폰/안드로이드폰 ○ — 4분.

코드가 사는 곳
실제 화면 Phaser 리포 — 파일 목록(README.md · LICENSE.md · package.json)과 README 본문

방금 P05에서 본 그 폴더가 이것이다. 새로 설명할 게 없다 — 본 것에 이름만 얹는다.

프로젝트
하나의 작품을 이루는 전체 작업 폴더 — 작품 하나 = 폴더 하나
폴더 / 파일
이름을 왜 이렇게 붙였나 — 사람도 기계도 알아보라고. saju-calc는 계산만, gemini-client는 AI한테 전화 거는 것만
리포지토리(repo)
그 폴더를 통째로 담아 관리하는 상자. 프로젝트와 거의 같은 말
소스 코드
사람이 읽고 쓰는 원본 글 — 촬영 원본
오늘 안 꺼낸다 · 질문이 나오면 그때 한 마디
패키지 / 라이브러리남이 만들어 둔 부품. 직접 안 만들고 갖다 쓴다
package.json이 작품에 어떤 부품이 들어가는지 적은 명세서 — 제작 목록
설치(install)그 명세서를 보고 부품을 실제로 내려받는 일
Node이 코드를 실제로 돌리는 엔진 — 코드가 필름이면 이건 영사기
오늘 어디서P05(방금) · P17 사주 폴더 트리 · P24 슈팅 assets/sprites/enemy_weak/…
P11 · 3분 · 프로젝트 ● · 폴더/파일 ● · 리포지토리 ○ · 소스 코드 ○ · 패키지 · package.json · 설치 · Node –

방금 P05에서 본 폴더가 이것이다. 새로 설명할 게 없다.

"작품 하나가 폴더 하나입니다. 그리고 그 폴더 안의 이름들이 곧 설명서예요 — saju-calc는 계산만, gemini-client는 AI한테 전화 거는 것만." (P17 예고)

화면의 리포는 P22에서 쓸 게임 엔진 Phaser의 것이다. 파일 목록 맨 위 세 줄만 짚는다 — README.md(첫 화면 설명문) · LICENSE.md(조건) · package.json(부품 명세서). 셋 다 다음 장에서 다시 나온다.

아래 흐린 네 개(패키지 · package.json · 설치 · Node)는 오늘 안 꺼낸다. 패키지/라이브러리는 P18에서 만세력 붙일 때 한 마디만 나온다.

붙는 단어: 프로젝트 ● · 폴더/파일 ● · 리포지토리 ○ · 소스 코드 ○ — 3분.

시간을 기록하고, 남과 나눈다
실제 화면 한 줄이 한 번의 저장 — 무엇을 · 누가 · 언제 · 어느 지점(02d8931)
실제 화면 MIT 라이선스 — 되는 것 / 안 되는 것 / 지킬 것
실제 화면 무료 게임 에셋 — Kenney

기록 · Git

Git
프로젝트가 언제 어떻게 바뀌었는지 전부 기록하는 시스템 — final_02, final_real, final_진짜최종을 없애주는 물건
커밋 “여기까지를 한 지점으로 저장” — 편집 타임라인의 마커
브랜치 본선을 안 건드리고 따로 떠서 시도 — 감독판 따로 뽑기
되돌리기 어제 상태로 통째로 복귀 — 백업 릴 꺼내기

공유 · GitHub · 오픈소스

GitHub
Git으로 관리하는 프로젝트를 인터넷에 올려두는 대표 창고 — 위 세 화면이 전부 여기다
오픈소스
남이 만든 코드를 공개해 누구나 쓰게 한 것. 공짜지만 조건은 있다 — 무료 소스 라이브러리 · 플러그인. 두 실습 모두 여기에 걸린다
라이선스
그 조건. 상업적으로 써도 되는지, 출처를 밝혀야 하는지
README 프로젝트 첫 화면의 설명문. 처음 온 사람이 읽는 글

Git은 오늘 이름만 꺼낸다 — 고칠 때마다 올리고, 그 기록을 훨씬 촘촘하게 해주는 물건. 라이선스는 작품에 쓰시는 분들에게 실제로 걸린다.

오늘 어디서P18 만세력 한 줄(오픈소스 · 라이선스) · P22 엔진 · 그림 · 소리(오픈소스 두 번째) · P28 다시 올릴 때(Git 한 마디)
P12 · 3분 · Git ○ · GitHub ○ · 오픈소스 ○ · 라이선스 ○ · 커밋 · 브랜치 · 되돌리기 · README –

Git은 오늘 이름만: "고칠 때마다 올립니다. 이 기록을 훨씬 촘촘하게 해주는 게 Git이에요. 최종_진짜최종 없애주는 물건." diff · merge · rebase는 없다.

왼쪽 화면을 손으로 훑으며: "한 줄이 한 번의 저장이에요. 누가 언제 뭘 고쳤는지가 다 남습니다."

오픈소스는 이 청중에게 무료 소스 라이브러리 · 플러그인이라고 하면 바로 붙는다. 오른쪽 Kenney 화면이 P22에서 실제로 쓸 그림·소리다.

라이선스는 한 줄만: "작품에 쓰시는 분들은 이게 실제로 걸립니다." 가운데 카드에서 초록 체크(되는 것)와 빨간 엑스(안 되는 것)만 손가락으로 짚는다. (§결정 3 — 한 줄에서 멈춘다.)

붙는 단어: Git ○ · GitHub ○ · 오픈소스 ○ · 라이선스 ○ — 3분.

웹이 돌아가는 구조 — 그리고 폰
실제 화면 같은 코드 · 같은 주소 — PC는 좌우가 남고, 폰은 꽉 찬다. 로컬↔배포는 말로 하면 5분, 폰으로 보이면 20초
실제 화면 폴더를 끌어다 놓으면 <PROJECT_NAME>.pages.dev 주소가 생긴다 — 이게 “올린다”
프론트엔드
화면
백엔드
서버
남의 컴퓨터
로컬
내 컴퓨터 안
배포
올린다
주소(도메인/URL)
빌린 이름
반응형
폰 화면과 PC 화면
· 브라우저 · 웹앱 주소만 있으면 폰 브라우저에서 그냥 열린다(설치 없음) · 네이티브 앱 앱스토어에서 받아 설치, 심사와 등록비
오늘 어디서P21 사주 QR(로컬 실패 → 배포 성공 20초) · P28 슈팅 QR(반응형) · P29 “뒤가 없다”
P13 · 5분 · 프론트엔드 · 백엔드 · 서버 · 로컬 · 배포 · 주소 ● · 웹 · 브라우저 · 웹앱 · 반응형 ○ · 네이티브 앱 –

앞 넷은 P04 그림 그대로. "화면은 앞, 서버는 뒤."

로컬↔배포는 말로 하면 5분, 폰으로 보이면 20초. 여기서는 예고만 한다. P21에서: localhost 주소로 열린 페이지 → 같은 주소를 폰에 친다 → 안 열린다 → 배포한 주소를 친다 → 열린다. "'내 컴퓨터 안에만 있다'와 '남의 컴퓨터에 올라가 있다'의 차이가 그 두 화면에 다 들어 있습니다. 정의를 먼저 말하지 않아도 돼요."

왼쪽 두 장은 같은 페이지를 PC와 폰에서 각각 연 것이다. PC에서는 좌우가 검게 남고 폰에서는 꽉 찬다 — "같은 코드인데 화면 폭에 맞춰 다시 배치됩니다. 그게 반응형이에요."

왜 챗봇 사이트는 오늘 못 올리나: "뒤(서버)와 기억이 필요한 물건이라서요. 오늘 만드는 둘은 화면만 있는 것이라 드래그로 올라갑니다."

아래 여덟 칸은 오른쪽 단어(수업에서 쓸 말)만 읽는다. 정의를 소리 내어 다 읽지 않는다. 5분.

원 표(11행) 정의 — 물어보면 이걸로 답한다. 웹: 주소 하나로 남의 컴퓨터에 있는 걸 내 브라우저에서 여는 방식 · 브라우저: 그걸 여는 프로그램(크롬·사파리) · 프론트엔드: 관객이 보는 화면 · 백엔드: 화면 뒤에서 실제로 일을 처리하는 쪽 · 서버: 그 뒤쪽 일을 맡아 항상 켜져 있는 컴퓨터 · 로컬: 내 컴퓨터 안, 나만 본다 · 배포: 남의 컴퓨터에 올려서 남도 보게 하는 것 · 주소(도메인/URL): 그렇게 올린 것을 찾아가는 이름 · 웹앱: 주소만 있으면 폰 브라우저에서 그냥 열리는 것, 설치 없음 · 네이티브 앱: 앱스토어에서 받아 설치하는 것, 심사와 등록비 · 반응형: 같은 페이지가 PC 화면과 폰 화면에 각각 맞춰 다시 배치되는 것.

프로그램끼리 말하는 법 — API · 열쇠 · 상자
실제 화면 Gemini API 문서 — 왼쪽 API 키, 가운데 키 만들기 버튼, 오른쪽 목차에 환경 변수 사용(권장) · 유출 대응 체크리스트
API
두 프로그램 사이에 뚫린 창구. 정해진 방식으로 요청하면 정해진 방식으로 답이 온다 — 다른 회사 서비스한테 전화 거는 것
API 키
그 창구를 쓸 자격을 증명하는 출입증. 돈이 달려 있다집 열쇠 · 은행 열쇠
.env.local
그 출입증을 넣어두는 서랍. 코드와 따로 둔다 — 상자

키를 누가 가져가면 여러분인 것처럼 행세해서 AI 모델을 불러제낀다 — 한 번 부를 때마다 삼백 원, 오백 원, 칠백 원이 뜯겨 나간다. 노출되면 원칙은 로테이션, 새로 만드는 것.

키는 상자에, 상자는 내 컴퓨터에, 상자는 절대 같이 보내지 않는다.env는 environment의 줄임말, .local은 로컬 세팅 — 키나 환경 설정을 담는 상자.
오늘 어디서P06(방금, 명세 → API) · P20 사주 키 입력(각자 자기 열쇠) · P25 슈팅 그림 교체(같은 열쇠 다시)
P14 · 3분 · API ● · API 키 ● · .env.local ●

"API 키는 키예요. 집 열쇠, 은행 열쇠와 같아요. 누가 가져가면 여러분인 것처럼 행세해서 AI 모델을 불러제낍니다. 한 번 부를 때마다 삼백 원, 오백 원, 칠백 원 돈이 뜯겨 나가요." [30:21]

화면 오른쪽 목차를 손가락으로 짚는다 — "환경 변수 사용(권장), 그리고 유출 대응 체크리스트. 만든 사람들도 이게 새어 나간다는 걸 아니까 문서에 이렇게 적어둔 거예요."

".env는 environment의 줄임말, .local은 로컬 세팅. 키나 환경 설정을 담는 상자." [34:44]

규칙 한 줄로 접는다: 키는 상자에, 상자는 내 컴퓨터에, 상자는 절대 같이 보내지 않는다.

노출되면: "원칙은 로테이션. 새로 만드는 겁니다." [33:53]⚠ 수요일 키는 실제로 노출됐고, 오늘 새 키로 왔다(§준비물).

태도 한 마디: "저는 아직도 일레븐랩스 홈페이지에서 어디서 효과음 만드는지 몰라요. 키 던지고 API · 모델 · Best Practice 찾아오라고 시킵니다." [49:00] — P15의 Best Practice로 이어진다.

문제를 볼 때 — 콘솔과 로그

P03에서 화면 옆으로 흐르던 게 이것이다.

ElementsConsoleSourcesNetworkPerformance
error만세력 계산기를 불러오지 못했습니다app.js:42
logprofile loadedapp.js:18
warn키가 없습니다app.js:57
>
실제로 찍힌 세 줄 srl-saju-fortune.pages.dev 콘솔 — 빨간 줄 하나, 노란 줄 하나, 나머지는 그냥 흔적
콘솔 / 로그
프로그램이 무슨 일을 했는지 줄줄이 남기는 흔적
에러
그중 빨간 줄. 대부분 어디가 잘못됐는지 이미 적혀 있다 — 오른쪽 app.js:42몇 번째 줄인지까지 알려준다

안 될 때 강사가 보는 곳도 여기다. 빨간 줄을 복사해서 그대로 AI에게 던지면 된다.

검색어 뒤에 Best Practice를 붙인다[찾는 것] best practice우리말로 “정석”. 좋은 정보가 그 키워드 아래 잘 모여 있다.
오늘 어디서P27 슈팅 콘솔에서 숫자 바꾸기 — 콘솔이 무서운 곳이 아니라는 걸 여기서 뒤집는다 · 그리고 뭔가 깨질 때마다
P15 · 1분 · 콘솔/로그 ○ · 에러 ○ · 챕터 회수 한 호흡 = 화면 · 서버 · 팩 · 열쇠 · 주소

P03에서 옆에 흐르던 게 이것. "안 될 때 저는 여기를 봐요. 빨간 줄 복사해서 그대로 AI한테 던지면 됩니다."

화면의 세 줄은 수요일 사주앱에서 실제로 찍힌 것이다. 빨간 줄 = 만세력 계산기를 못 불러왔다(P18에서 붙일 그 부품), 노란 줄 = 키가 없다(P14의 그 열쇠). "무슨 말인지 몰라도 됩니다. 이 줄을 그대로 복사하면 돼요."

그리고 Best Practice: "검색어에 Best Practice를 붙이세요. 우리말로 정석. 좋은 정보가 그 키워드 아래 잘 모여 있습니다." [49:31]

1분. 짧게 끊고 넘어간다.

랜드스케이프 회수(한 호흡) — 다섯 단어: 화면 · 서버 · 팩 · 열쇠 · 주소. 나머지는 이 다섯에 매달린다. 여기까지가 48분 지점이어야 한다.

크롬 개발자 도구 — 폰 화면을 노트북에서 본다
기기 목록 = Responsive · 1440 × 900
기기 목록 = iPhone · 375 × 812

폰을 꺼내지 않고 확인한다

앞에서 본 반응형내 노트북에서 바로 확인하는 방법이다. 배포하기 전에도 된다.

1페이지에서 F12⌘ ⌥ I
2기기 모드 Ctrl Shift M⌘ ⇧ M
3위쪽 목록에서 기기를 고른다 — iPhone·Galaxy·iPad, 또는 Responsive로 폭을 직접 끈다
4회전 아이콘으로 가로·세로를 바꾼다
주소창에 내 컴퓨터 주소(localhost)를 넣고도 된다. 폰을 꺼내지 않고, 배포하지 않고, 여기서 먼저 본다.
P13b · 3분 · 개발자 도구 ○ · 반응형(회수) · 오늘 어디서 — 사주앱·슈팅게임을 QR로 열기 전에 이 화면으로 먼저 확인한다 · P15 콘솔이 같은 창이다

이 장은 학생이 집에서 실제로 쓸 수 있는 유일한 도구다. "폰으로 확인해 보세요"는 말이 쉽지, 배포 전에는 못 한다. 그런데 이건 지금 당장 된다.

"여러분 노트북에서 F12 한 번 누르면 창이 하나 열려요. 무섭게 생겼는데 오늘은 그중 딱 하나만 씁니다 — 폰 화면처럼 보기."

시연: 슈팅게임을 띄운 채로 F12 → Ctrl+Shift+M → 기기 목록에서 iPhone 선택. 같은 페이지가 그 자리에서 폰 모양으로 바뀐다. 여기서 한 번 웃음이 나온다.

이어서: "제가 아까 QR로 보여드린 것도 이겁니다. 다만 그건 진짜 폰이고, 이건 흉내예요. 흉내로 90%는 잡힙니다. 나머지 10%는 손가락 크기하고 실제 속도라서, 그것만 진짜 폰으로 봅니다."

붙는 단어: 반응형 · 폰 화면과 PC 화면 · (덤으로) 개발자 도구가 P15 콘솔과 같은 창이라는 것 — 다음 장에서 회수한다. 3분.

오픈소스의 시작 — 고칠 수 없는 프로그램을 만난 1980년
리처드 스톨먼Richard Stallman
MIT 인공지능 연구소 연구원. GNU 프로젝트자유소프트웨어재단(FSF)을 시작한 사람.
사진 Patafisik / CC BY-SA 4.0 / Wikimedia Commons
“언론의 자유(free speech) 할 때의 free이지, 공짜 맥주(free beer) 할 때의 free가 아니다.”원문 “think of ‘free’ as in ‘free speech,’ not as in ‘free beer.’” — gnu.org

1980년, MIT 인공지능 연구소에 새 프린터가 들어옵니다. 제록스 9700. 종이가 자주 걸렸는데, 걸렸다는 사실을 알 방법이 없었습니다. 스톨먼은 이전 프린터에서는 그 알림 기능을 직접 만들어 붙였던 사람입니다. 이번에는 그럴 수 없었어요 — 소스코드(프로그램의 설계도. 사람이 읽고 고칠 수 있는 원본 텍스트)를 주지 않았기 때문입니다.

그 코드를 가진 카네기멜런의 한 교수를 찾아갑니다. 그리고 거절당해요. 이유는 “제록스에 안 주기로 약속했다” — 오늘날의 비밀유지계약(NDA)입니다. 그가 아무 말 없이 돌아선 그 일이, 이 뒤의 40년을 만듭니다.

1983.09.27유즈넷(초창기 인터넷 게시판)에 GNU 프로젝트를 공개 선언 — “완전히 자유로운 운영체제를 만들겠다”
1985.03GNU Emacs 첫 배포(3.20)와 GNU 선언문 — 1976년 자기가 만들었던 편집기를, 이번엔 아무도 잠글 수 없게 처음부터 다시 썼다
1985.10.04자유소프트웨어재단(FSF) 설립
1989GPL 1.0 — 카피레프트를 법으로 못 박은 첫 라이선스 (2.0 = 1991.06 · 3.0 = 2007.06)
카피레프트 copyleft

저작권(copyright)을 뒤집은 말장난입니다. 저작권을 없애는 게 아니라, 저작권을 이용해서 자유를 강제하는 겁니다. “가져다 마음대로 고쳐 써라. 대신 고친 것도 똑같이 열어라.” 영화로 치면 — 이 시나리오 마음대로 각색해도 되는데, 각색본도 남이 각색할 수 있게 공개해야 한다는 조건입니다.

자유 0쓸 자유어떤 목적으로든 마음대로 돌린다
자유 1뜯어볼·고칠 자유소스코드 접근이 전제 조건
자유 2나눠줄 자유그대로 복사해 남에게 준다
자유 3고친 걸 나눠줄 자유내 개조본도 남에게 준다
P15b · 4분 · 소스코드 ● · 오픈소스 ● · 카피레프트 ○ · 라이선스 ○ · 사진은 CC BY-SA — 크레딧 줄을 지우지 않는다 · 근거 gnu.org

사진 한 장과 일화 하나로 4분. 연도를 외우게 하는 장이 아니다. “고치고 싶은데 고칠 수가 없었다”는 감정 하나만 남기면 된다.

읽어도 되는 문장 — “1980년, MIT 인공지능 연구소에 새 프린터가 들어왔습니다. 종이가 자주 걸렸는데 연구소가 다른 층이라, 한참 뒤에 가보면 아무것도 안 뽑혀 있는 거죠. 스톨먼은 이전 프린터에서는 이 문제를 직접 고쳤던 사람입니다. 그런데 새 프린터는 그게 안 됐어요. 설계도를 안 줬거든요.”

“코드를 가진 교수를 찾아갑니다. 그리고 거절당해요. ‘제록스에 안 주기로 약속했다.’ 요즘 말로 NDA입니다. 당시엔 이게 소프트웨어 업계에 막 등장한 새 관행이었어요.”

⚠ 교수 이름(로버트 스프롤)은 정황 근거만 있고 1차 문헌이 단정하지 않는다 — “카네기멜런의 한 교수”로만 말한다.

free 두 가지 뜻은 말로 한 번 더 풀어 준다. “돈 받고 팔아도 됩니다. 문제는 값이 아니라 통제권이에요.” 자유 4가지는 번호가 0번부터 시작한다는 것 자체가 이 동네 문화라고 한 마디 붙이면 웃는다.

전환 — “여기까지는 한 사람의 원칙 이야기입니다. 이게 어떻게 산업이 됐을까요.”

사진 저작권: Patafisik, CC BY-SA 4.0, via Wikimedia Commons. 크레딧 줄은 법적 의무라 화면에 남겨 둔다.

성당이 아니라 시장 — 열어 놓고 만드는 방식이 이겼다
리누스 토르발스Linus Torvalds
1991년 헬싱키의 대학생. 리눅스 커널을 만들었고, 2005년에는 Git도 직접 만든다.
사진 Krd 촬영 / Von Sprat 크롭
CC BY-SA 4.0 / Wikimedia Commons
에릭 S. 레이먼드Eric S. Raymond
1997년 『성당과 시장』을 발표해 개발 방식 자체를 논쟁으로 만들었다. 1998년 OSI 공동 설립·초대 회장.
사진 jerone2 원본 / Bilby 2차 저작
CC BY-SA 2.0 / Wikimedia Commons
크리스틴 피터슨Christine Peterson
1998년 2월, “오픈소스(open source)”라는 이름을 떠올린 사람. 당시 포어사이트 연구소 상임이사.
사진 Randy Stewart
CC BY-SA 2.0 / Wikimedia Commons
1991.08.25헬싱키의 한 대학생이 게시판에 글을 올린다 — “그냥 취미로 만드는 거고, GNU처럼 크고 전문적이진 않을 것” · 그게 리눅스
1997.05.27『성당과 시장』 — 성당은 소수의 장인이 문 닫고 몇 년을 짓고, 시장은 공개된 채로 시끄럽게 자란다 · “눈이 충분히 많으면 모든 버그는 얕다”
1998.01.22넷스케이프가 자기 브라우저의 소스코드를 공개하겠다고 발표한다 — 당시 업계에 충격
1998.02캘리포니아 팰로앨토 회의에서 이 물건에 “오픈소스”라는 새 이름이 붙는다 (회의 날짜는 출처마다 2월 3일·5일로 갈린다) → 그달 말 OSI 설립
1998.03.31 →
2004.11.09
mozilla.org 소스 공개 → 파이어폭스 1.0 · 넷스케이프라는 회사는 사라졌지만, 공개된 코드는 살아남아 브라우저 독점을 깼다
이름을 바꾼 이유

“free software”는 기업 임원에게 ‘공짜’로 들리고, 철학·정치 운동처럼 들렸습니다. 같은 물건에 붙인 다른 이름이에요. 스톨먼 쪽은 자유를, 레이먼드 쪽은 잘 돌아가는 개발 방법을 앞세웠고, 두 진영은 지금도 사이가 좋지 않습니다.

P15c · 4분 · 오픈소스 ● · 리눅스 ○ · 커널 –(이름만) · 명명 날짜는 “1998년 2월”까지만 · 사진 3장 CC BY-SA · 근거 opensource.org/history

사진 셋으로 사람 이름을 붙여 두고, 아래 연표는 손가락으로 짚으며 넘긴다. 4분 안에 다섯 줄이다 — 한 줄에 45초.

성당 vs 시장은 영화 비유로 바꾸면 바로 붙는다. “성당식은 완성될 때까지 아무에게도 안 보여주는 감독판입니다. 시장식은 러프컷을 매일 올려놓고 누구나 코멘트를 달게 하는 방식이에요. 상식적으로는 전자가 좋을 것 같은데, 소프트웨어에서는 후자가 이겼습니다.”

“눈이 충분히 많으면 모든 버그는 얕다” — “시사회에 100명이 앉아 있으면 편집 실수는 반드시 누군가 잡아냅니다.”

순서가 핵심이다. 넷스케이프가 먼저 열었고(1월), 이름은 그 뒤에 붙었다(2월). 회사가 실용적인 사업 판단으로 코드를 여는 시대가 오자 “다른 이름이 필요하다”가 된 것이다.

⚠ 명명 회의 날짜는 OSI 공식(2/3)과 피터슨 본인 회고(2/5)가 갈린다 — “1998년 2월”로만 말한다. 또 피터슨이 직접 제안한 게 아니라, 토드 앤더슨이 회의 중 그 표현을 끼워 넣어 퍼뜨렸다는 대목까지는 여유 있을 때만.

⚠ 피터슨 사진은 원본이 512×542로 작아 확대돼 흐리다. 그래서 셋 중 가장 작게 배치했다 — 화질 얘기는 굳이 하지 않는다.

전환 — “그래서, 지금 그게 어디까지 왔을까요.”

지금 어디에 있고, 누가 떠받치는가
500
TOP500 슈퍼컴퓨터가 한 대도 빠짐없이 리눅스
2017.11 이후 계속
92%
서버 운영체제가 파악되는 웹사이트 중 유닉스 계열
W3Techs 2026.09.04
1.8억
GitHub 개발자 — 1초에 한 명씩 새로 들어온다
GitHub Octoverse 2025
436만
npm에 올라와 있는 자바스크립트 부품
2026.09.05 조회
리눅스 커널 4,098만 줄 · 안드로이드는 리눅스 커널 위에서 돈다 — 활성 기기 30억 대(구글 공식 발표, 2021) · PyPI 파이썬 부품 88.7만 개 · GitHub 저장소 6.3억 개
떠받치는 쪽은 위태롭다
left-pad2016.0311줄짜리 부품 하나가 사라지자 리액트·바벨·웹팩 빌드가 줄줄이 멈췄다. 메타·페이팔·넷플릭스가 영향을 받았고, 복구에 약 두 시간이 걸렸다.
xz-utils2024.03사실상 한 사람이 관리하던 압축 도구. 3년에 걸쳐 신뢰를 쌓은 가짜 협력자가 전 세계 리눅스 서버의 뒷문을 열 뻔했다. 접속이 평소보다 0.5초 느린 걸 이상하게 여긴 엔지니어 한 명이 막았다.
60%Tidelift 2024메인테이너 조사에서 무보수라고 답한 비율. 앞선 조사에서는 58%가 관리하던 프로젝트를 그만뒀거나 그만둘까 생각했다고 답했다.
영화 쪽은 이미 이 위에 서 있다
『플로우』2025.03무료 3D 툴 블렌더만으로 만든 첫 오스카 수상작(장편애니메이션상). 제작비 €3.5M — 같은 부문 『와일드 로봇』은 US$78M이었다. 그 블렌더 자체가 2002년 망한 회사의 소프트웨어를 사용자들이 €100,000를 모아 사서 푼 결과물이다.
ASWF2018.08오스카를 주는 그 아카데미가 리눅스 재단과 함께 세운 아카데미 소프트웨어 재단. 설립 근거는 영화 산업의 약 84%가 이미 오픈소스를 쓰고 있다는 조사 결과였다. OpenEXR(HDR 이미지) · OpenColorIO(색 관리) · OpenVDB(연기·물·불)가 여기 있다.
FFmpeg2000.12유튜브·VLC·크롬·블렌더가 안에 넣어 쓰는 영상 변환 엔진. NASA는 화성 탐사선에 넣었다. 이름은 몰라도 매일 쓰고 있다.
부품 — 만세력은 직접 안 만든다. 한 줄로 불러온다(MIT)
라이선스 — 사용 조건표. MIT는 저작권 표시만 요구한다
에셋 — 코드만이 아니다. 그림·소리도 풀린다(kenney.nl)
P15d · 5분 · 라이선스 ● · 패키지·부품 ● · 메인테이너 ○ · 다빈치 리졸브는 오픈소스가 아니다 — 색보정은 OpenColorIO로 · 수치 기준일 2026.09.05

세 덩어리다. 위 = 얼마나 크냐, 가운데 왼쪽 = 얼마나 위태롭냐, 가운데 오른쪽 = 왜 당신 얘기냐, 아래 = 오늘 만들 앱 안에 이미 들어 있다. 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 — 암벽등반으로 이해하는 버전 관리
2005.04.03비트키퍼 무료 사용권이 끊기자 토르발스가 직접 만들기 시작한다
2005.04.07착수 나흘 만에 Git이 Git 자신의 소스코드를 관리하기 시작
2008.04.10GitHub 공개 — Git을 쓰는 사람들이 모이는 광장
2018.10.26마이크로소프트가 GitHub를 75억 달러에 인수 완료
확보물에 로프를 거는 순간 = 커밋
아래로 이어진 로프 전체 = 히스토리 · 말뚝 간격 = 커밋 간격
같은 벽의 다른 루트 = 브랜치
암벽등반Git
벽을 오른다작업을 진행한다
확보물(말뚝)을 박고 로프를 건다커밋 — “여기까지 됐다”고 못을 박는다
떨어져도 마지막 말뚝까지만 떨어진다되돌리기 — 마지막 커밋 지점으로
아래로 이어진 로프 전체히스토리(로그) — 지나온 자리가 다 남는다
같은 벽의 다른 루트브랜치 — 원본을 건드리지 않고 딴 길을 시도
두 루트가 정상에서 만난다머지 — 갈라졌던 작업을 하나로 합친다
실제 화면은 이렇게 생겼다 — 메시지 · 작성자 · 날짜 · 해시가 한 줄씩 아래로 쌓인다. “Merge pull request”라고 적힌 줄이 두 루트가 만난 자리다.
P15e · 5분 · Git ● · 커밋 ● · 되돌리기 ● · 히스토리 ● · 브랜치 ○ · 머지 ○ · GitHub ○ · 표를 먼저 세우고 사진으로 확인시킨다 — 명령어는 치지 않는다

이 장이 이 챕터의 목적지다. 비유가 주인공이고 연혁은 배경이다 — 위 네 칸은 15초에 훑고, 표와 사진에 4분을 쓴다.

강사 대사 — “커밋을 자주 박을수록 떨어질 때 덜 떨어집니다. 말뚝을 10미터마다 박으면 10미터를 떨어지고, 1미터마다 박으면 1미터만 떨어져요. final_최종_진짜최종이 사라지는 이유가 이겁니다. 파일 이름으로 버전을 관리할 필요가 없어져요.”

사진 셋을 순서대로 짚는다 — ① 손이 퀵드로를 거는 순간이 커밋. ② 로프가 아래로 계속 이어지고 말뚝마다 걸려 있는 게 히스토리. 오른쪽 확대 컷에서 말뚝 간격을 짚으면 “자주 박아라”가 한 번에 붙는다. ③ 아래에서 같이 출발해 중간에 갈라지는 두 루트가 브랜치다.

“분산”은 한 문장이면 된다 — “모든 참여자가 전체 역사를 통째로 갖고 있습니다. 회사 서버가 날아가도 아무나 한 명의 노트북으로 완전히 복구돼요. 인터넷이 끊겨도 혼자 계속 기록할 수 있고요.”

GitHub 한 줄 정리 — “Git은 도구고, GitHub는 그 도구를 쓰는 사람들이 모인 광장입니다. 2018년에 마이크로소프트가 그 광장을 75억 달러에 샀어요. 한때 ‘오픈소스는 암이다’라고 했던 그 회사가요.”

오른쪽 GitHub 캡처는 겁을 주려고 띄우는 게 아니다. “한 줄에 하나씩, 누가·언제·무엇을 고쳤는지가 적혀 있을 뿐입니다. 여러분이 오늘 만들 앱도 이렇게 남습니다.”

⚠ 라이브로 git 명령을 치지 않는다. 이 장은 개념만이고, 실제 커밋·배포는 뒤 챕터에서 결과 화면으로만 보인다.

실습 1 · 사주 웹앱 — 한 장 기획서

이것부터 만드는 이유 — 규칙 설명이 필요 없고(각자 자기 생년월일), 결과가 개인적이라 예순 명이 각자 폰에서 자기 것을 본다. 오늘 세운 단어가 이 앱 하나에 거의 다 들어 있다.

누가 보나
폰을 든 사람
앱스토어도 가입도 없이 주소 하나로 들어온다
어떤 경험
넣으면 → 여덟 글자 → 뜻
이름·생년월일·시각을 넣으면 사주 여덟 글자가 나오고, 한자를 누르면 뜻이 붙는다. 원하면 AI가 문장으로 풀어준다
반드시
열쇠가 없어도 여기까지는 나온다
여덟 글자와 한자 뜻은 키 없이 본다. 내 정보는 내 폰에만 남는다
하지 말 것
우리 서버를 두지 않는다
생년월일을 어디로도 안 보낸다 — AI 문장을 누른 그 순간만 구글로 직접 간다
사람이 정할 것은 대상 · 경험 · 목표 · 금지 넷 — 나머지는 도우미가 한다6개월 전이었다면 훨씬 세부적으로 정의해야 했다. 지금은 생각하는 바를 자연어로 최대한 뱉어 보는 쪽이 더 중요하다.
P16 · 2분 · 현장 제작 경계 — P17까지 빈 폴더에서 라이브 · P18 "한 줄 붙이기"까지 되면 최상 · 그 뒤는 완성본을 연다

개발 문서가 아니라 디자인 수칙 한 장으로 읽는다. 네 칸만 소리 내어 짚고 넘어간다.

"사람이 정할 건 대상·경험·목표·금지예요. 나머지는 도우미가 합니다." [47:05]

"6개월 전이면 훨씬 세부적으로 정의해야 한다고 했을 텐데, 지금은 생각하는 바를 자연어로 최대한 뱉어 보는 것이 더 중요해요." [18:32]

왜 이것부터인가 — 규칙 설명이 필요 없고(자기 생년월일), 결과가 개인적이라 60명이 각자 폰에서 자기 것을 본다. 그리고 이 앱 하나에 오늘 단어가 거의 다 들어 있다.

⚠ 현장 제작 경계 — P17까지 빈 폴더에서 라이브. P18 "한 줄 붙이기"까지 되면 최상. 그 뒤는 완성본을 연다. 2분.

라이브 ① — 빈 폴더에서 화면 뼈대까지

이름만 봐도 읽히는 폴더 구조

saju-webapp/빈 폴더에서 시작
index.html화면 한 장
css/style.css보이는 꼴
js/app.js전체를 잇는다
js/profile-storage.js기억 — 폰에만
js/saju-calc.js여덟 글자
js/gemini-client.js구글 AI에 전화
data/hanja-glossary.json한자 뜻 표
  1. 터미널을 켠다 — 무섭게 생겼지만 치는 건 한국어다. 첫 문장에 "나 윈도우 11이야"를 붙인다 (P09·P10 회수 30초)
  2. 앞 장의 기획 한 장을 그대로 에이전트에게 준다 → 화면 뼈대와 입력 폼이 나온다(이름·생년월일·시각·모름 체크)
  3. 폴더 트리를 잠깐 세운다 — 이름만 봐도 무엇을 하는 파일인지 읽힌다
  4. 브라우저로 연다. 폼이 뜬다 — 이 화면이 앞이고, 오늘은 뒤에 우리 서버가 없다
P17 · 8분 · CLI ○(회수) · 윈도우/맥 ● · 프로젝트·폴더·파일 ● · 프론트엔드 ● · 실패 시 완성본 폴더를 열어 트리만 보이고 P18로

화면 셋을 나란히 — 터미널 + 에디터 + 브라우저. 빈 폴더에서 시작한다.

① 터미널을 켜면서 P09 회수 30초: "무섭게 생겼는데 제가 치는 건 한국어예요." 이어서 P10 회수: "저는 맥이라 이렇게 치는데 윈도우는 다릅니다. 그래서 첫 문장에 '나 윈도우 11이야'."

② P16 기획 한 장을 그대로 준다. 화면 뼈대와 입력 폼(이름·생년월일·시각·모름 체크)이 나온다.

③ 트리를 세우고 — "이름 보면 뭐 하는 놈인지 알죠." P05 폴더 구조 이야기와 같은 자리다.

④ 브라우저로 연다. "여러분이 지금 보는 이 화면이 이에요. 오늘은 뒤에 우리 서버가 없어요."

⚠ 실패 시 — 에이전트가 늦으면 완성본 폴더를 열어 트리만 보이고 P18로 넘어간다. 8분을 넘기지 않는다.

멈추는 자리 — 만세력은 직접 만들지 않는다

남이 만들어 공개한 부품을 한 줄로 불러온다

js/saju-calc.js import { calculateSaju, lunarToSolar } from "https://cdn.jsdelivr.net/npm/@fullstackfamily/manseryeok@1.0.8/dist/index.mjs";
대조 11990-05-15 14:30 → 경오 신사 경진 계미
대조 22000-01-15 14:20 → 기묘 정축 임신 정미 — 연주가 2000년이 아니라 1999년 것으로 나오는 게 정상. 입춘을 지나야 그 해가 시작된다

음력과 절기 계산은 직접 만들면 며칠이 걸린다. 그런데 누가 이미 만들어서 공개해 뒀다 — 공짜인데 조건이 있고, 그 조건을 적어 둔 것이 라이선스다. 작품에 쓰는 사람에게는 이게 실제로 걸린다.

한자를 누르면 뜻이 붙는 것은 표 하나다 — data/hanja-glossary.json, 천간 10자 + 지지 12자 = 22자 고정. AI가 아니다.

P18 · 5분 · 오픈소스 ○ · 라이선스 ○(한 줄) · 패키지·CDN –(이름만) · 실패 시 CDN이 막히면 화면이 통째로 빈다 — 그 장면을 그대로 보여주고 완성본 캡처로

"음력이랑 절기를 계산해야 하는데 이건 제가 못 만듭니다. 만들면 며칠 걸려요. 근데 누가 이미 만들어서 공개해놨습니다. 공짜인데 조건이 있어요. 그 조건 적어놓은 게 라이선스고, 작품에 쓰시는 분들은 이게 실제로 걸립니다."

한 줄 붙인다 → 이름·생년월일 넣는다 → 여덟 글자가 나온다. 사람 앞에서 대조할 세트(REPLAY §3, 독립 라이브러리와 교차 검증 완료): 1990-05-15 14:30 → 경오 신사 경진 계미 / 2000-01-15 14:20 → 기묘 정축 임신 정미. 새벽 검증 추가분: 홍길동/1980-05-15 → 경신 신사 무자 기미.

연주가 1999년 것으로 나오는 대목에서 — "입춘을 지나야 그 해가 시작됩니다. 사주 특유의 규칙이에요."

"30초 만에 됐죠. 남이 만든 부품을 갖다 쓴 겁니다."

한자 뜻은 — "이건 표 하나예요. AI 아니에요."

⚠ 실패 시 — CDN이 막히면 화면이 통째로 빈다(어젯밤 재현: 헤더만 남고 본문 없음, 에러 문구도 없음). 그 순간을 그대로 보여주고 "바깥 부품을 못 불러오면 이렇게 됩니다" → 완성본 캡처로. §준비물에 벤더링 항목 있음.

기억 — 새로고침해도 남는다
여기서부터 완성본을 연다

브라우저가 가진 기억, 그 바깥의 서버

개발자도구 ▸ Application ▸ Local Storage ▸ 이 사이트 saju_profile ← 이름 · 생년월일 · 시각 (방금 넣은 것) saju_gemini_key ← 뒤에 넣을 열쇠도 같은 칸에 앉는다
남는다저장 → 새로고침 → 그대로 있다. 이 화면이 기억하고 있다
안 나간다그 기억은 본인 폰 안에만 있고 어디로도 안 나간다
없다옆사람 폰엔 없다 — 남도 보게 하려면 남의 컴퓨터에 있어야 한다. 그게 서버, 거기 쌓아 두는 게 데이터베이스. 오늘은 거기까지 안 간다

최소 생존 버전은 여기까지다 — 입력 폼 → 저장 → 여덟 글자 → 키 입력 → "오늘의 운세" 한 버튼 → 배포. 그 아래는 다 자를 수 있다.

P19 · 4분 · 로컬스토리지·기억 ● · 로컬 vs 서버 ● · 데이터베이스 –(한 마디) · 시간이 밀렸으면 P18부터 이미 완성본

저장 → 새로고침 → 그대로 있다를 눈앞에서 한 번. 그리고 개발자도구 Application 탭을 열어 saju_profile 키를 가리킨다.

"방금 페이지를 다시 불렀는데 아까 적은 게 그대로 있죠. 이 화면이 기억하고 있는 거예요. 이 기억은 여러분 폰 안에만 있고 어디로도 안 나가요."

"옆사람 폰엔 없어요. 남도 보게 하려면 남의 컴퓨터에 있어야 합니다. 그게 서버고, 거기 쌓아두는 게 데이터베이스예요. 오늘은 거기까지 안 갑니다."

여기서 완성본으로 전환한다(시간이 밀렸으면 P18부터 이미 완성본). REPLAY §0 최소 생존 버전 = 입력 폼 → 저장 → 여덟 글자 → 키 입력 → "오늘의 운세" 한 버튼 → 배포. 그 아래는 다 자를 수 있다.

키 이름 두 개는 실제 코드 그대로다 — saju_profile(profile-storage.js) · saju_gemini_key(app.js). 4분.

열쇠 — 각자 자기 것
① 주제 8개
② 키 입력 화면 — 값은 가려진다

구글 AI를 부를 때 신분을 증명하는 것

주제오늘의 운세 · 올해 총운 · 재물 · 애정 · 직업·일 · 인간관계 · 건강 · 이름 풀이 — 여덟 개
열쇠강사 열쇠가 아니라 각자 자기 열쇠. 입력칸은 가려지는 타입이고, 값은 화면에 띄우지 않는다
저장이 열쇠도 앞 장의 기억 창에 앉는다 — 본인 폰 브라우저에만. 딴 데로 안 간다
없으면키 없이 눌러도 앱이 죽지 않고 안내 문구가 뜬다. 한도가 넘으면 "1분 쉬었다 다시" 버튼이 뜬다
수업 소재 — 답이 "오늘은 2026년 9월"에서 잘렸다모델이 눈에 안 보이는 "생각"에 토큰을 먼저 쓰는데(thoughtsTokenCount 479), 출력 예산 500을 거의 다 먹은 것. thinkingConfig: { thinkingBudget: 0 } + 예산 1024로 고쳤다(a8f1218). 에러도 안 나고 그냥 답이 짧게 나온다 — 모르면 프롬프트를 탓하게 된다. 라이브에서 답이 이상하게 짧으면 이 설정부터 본다.
P20 · 4분 · API ● · API 키 ● · .env.local ●(오늘 이 앱엔 상자가 없다 — 브라우저에 저장) · 실패 시 키 한도·API 오류 → 어제 받아둔 샘플 응답 텍스트

"사주 풀이 문장은 우리가 쓰는 게 아니라 구글 AI한테 전화를 걸어서 받아 와요. 전화 걸 때 '저예요' 증명하는 게 이 열쇠예요. 제 열쇠가 아니에요. 각자 자기 열쇠. 그리고 이 열쇠도 아까 그 기억 창에 저장되고, 딴 데로는 안 가요."

키 없이 누르면 앱이 안 죽고 안내 문구가 뜬다(어젯밤 확인). 한도가 넘으면 "1분 쉬었다 다시" 버튼이 뜬다. 강사 키로 오늘의 운세 한 번 받아서 보인다.

발급 절차는 다음 장(P20b)으로 넘긴다 — "무료로 2분이면 됩니다. 단톡방에 올려둘게요."

수업 소재(원격 실측, a8f1218에서 수정됨) — 처음엔 답이 "오늘은 2026년 9월"에서 잘렸다. gemini-2.5-flash가 눈에 안 보이는 "생각"에 토큰을 먼저 쓰는데(thoughtsTokenCount 479) 출력 예산 500을 거의 다 먹은 것. thinkingConfig: { thinkingBudget: 0 } + 예산 1024로 잡았다. "에러도 안 나고 그냥 답이 짧게 나와요. 모르면 프롬프트를 탓하게 됩니다. 이런 게 실무예요." — 라이브 중 답이 이상하게 짧으면 이 설정부터.

⚠ 남은 확인 하나 — 배포 주소에서 실제 제미나이 호출은 아직 아무도 안 돌렸다(원격은 키 입력이 안전 분류기에 막힘, 맥은 키 없음). 아침에 폰으로 주소 열고 본인 키 넣어 "오늘의 운세" 1회 — 30초. 그게 실제 사용 경로다(§준비물 1).

⚠ 실패 시 — 키 한도·API 오류가 나면 어제 미리 받아둔 샘플 응답 텍스트로. "이 AI도 하루 먹을 양이 정해져 있어요." 4분.

부록 · 구글 제미나이 API 키 발급 절차 (학생 배포용)
구글 키는 한 번 부를 때마다 돈이 나갈 수 있는 열쇠다. 대신 오늘 수업 범위는 대화·사주 문장까지 무료 한도로 끝나고, 결제가 필요한 건 이미지 생성뿐 — 전원 A단계까지면 충분하다.

어디까지 하느냐에 따라 갈린다

어디까지필요한 것시간
사주 여덟 글자 · 한자 풀이없음없음
사주 AI 문장 (8주제)A단계 키2분무료 한도
스토리팩 대화(플레이)A단계 키 + .env.local3분무료 한도
이미지 생성 (팩 에셋 · 게임 적 그림)A + B단계7분최소 $10 선불

A · 키 발급 — 무료 2분

  1. aistudio.google.com/apikey → 구글 계정으로 로그인 → 약관 동의처음 쓰는 계정이면 Google Cloud 프로젝트와 키가 자동으로 만들어진다. 이미 Cloud를 쓰던 계정은 자동 생성이 안 된다 → Projects 화면에서 프로젝트를 먼저 가져온(import) 뒤 만든다
  2. Create API key → 키가 화면에 뜬다
  3. 복사한다. 창을 닫아도 같은 페이지에서 다시 볼 수 있다OpenAI·Anthropic은 한 번만 보여주지만 AI Studio는 아니다

지킬 것 셋

  1. 키를 채팅창 · 단톡방 · 화면 공유에 붙이지 않는다. 붙였다면 샌 것으로 친다
  2. 샌 것 같으면 AI Studio에서 지우고 새로 만든다(로테이션). 고치는 게 아니라 새로 만드는 것
  3. 폰에 넣은 키는 그 브라우저에만 남는다. 남의 폰에서 쓰지 않는다

어디에 넣나 — 오늘은 두 갈래

물건넣는 곳어디에 저장되나
사주앱오늘 QR앱 안 키 입력 화면에 붙여넣기본인 폰 브라우저에만(로컬스토리지). 우리 서버는 없다. 딴 데로 안 간다
스토리팩 킷집에서.env.example을 복사 → 이름을 .env.local로 → GOOGLE_API_KEY= 오른쪽에 붙이고 저장내 컴퓨터의 상자. 서버가 켜져 있었다면 완전히 끄고 다시 켠다 — 켜진 채로는 새 키를 안 읽는다

변수명이 둘이다 — 구글 문서는 GEMINI_API_KEY라고 쓰지만 우리 팩이 읽는 이름은 GOOGLE_API_KEY. 구글 SDK는 둘 다 받고 둘 다 있으면 GOOGLE_API_KEY가 우선이라 이 이름 하나로 양쪽이 맞는다.

B · 유료 전환 — 이미지까지 할 때만 7분

  1. aistudio.google.com/projects → 내 프로젝트 행 Billing Tier 열 → Set up billing
  2. 결제 계정을 붙인다(있으면 선택, 없으면 국가·연락처·결제수단 입력). 수업 비용을 분리하려면 Add new billing account
  3. Prepay(선불)로 간다 — 2026-03-23부터 신규는 이게 기본. 잔액이 0이 되면 그냥 멈춘다
  4. aistudio.google.com/billingBuy credits최소 $10
  5. Setup auto-reload를 켜지 않는다. 그러면 충전액이 그대로 상한이 된다 — 가장 확실한 안전장치. 이미 켰다면 Manage auto-reload → Monthly Limit
  6. aistudio.google.com/spendMonthly spend cap을 프로젝트에 걸어 둔다지금 상태 보는 법 — Projects의 Billing Tier 열: Set up billing(결제 안 붙음 = 무료만) · Set up Prepay(계정은 붙었고 선불 설정 남음) · No credits(잔액 0 = 유료 호출 멈춤)

함정 넷

변수명이 두 개다 — GEMINI_API_KEY / GOOGLE_API_KEY
우리 팩은 GOOGLE_API_KEY. 이거 하나로 양쪽 다 된다
켜져 있던 서버가 옛 키를 물고 있다
완전히 끄고 다시 켠다(수요일 실제로 걸린 지점)
"키를 지금 안 복사하면 끝"이라고 안내한다
틀렸다. API Keys 페이지에서 다시 본다
기존 Google Cloud 사용자는 키 버튼이 안 뜬다
Projects에서 프로젝트를 먼저 가져온다
P20b · 1분(P20에 흡수) · API 키 ● · 상자(.env.local) ● · 로테이션 ○ · 비용·Dry Run ○(회수) · 무료 한도 횟수는 단정하지 않는다 — 본인 것은 aistudio.google.com/rate-limit 에서 본다 · 근거 ai.google.dev(api-key · quickstart · billing) 2026.09.05 재확인

P20 끝에 띄우고 한 마디만 — "키는 무료로 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키_발급안내와 일치.

배포 — 주소가 생기고 예순 명이 들어온다
폰에서 연 화면
QR
https://srl-saju-fortune
.pages.dev
QR 이미지는 당일 생성기 탭에서 만들어 이 자리에 띄운다 · 주소를 직접 쳐도 된다
폰 카메라로 찍는다

내 컴퓨터 안에서 남의 컴퓨터 위로

  1. 폰에서 localhost:8080을 친다 → 안 열린다. 아직 강사 컴퓨터 안에만 있다 (P13에서 예고한 20초)
  2. 폴더를 통째로 드래그해서 올린다(Cloudflare Pages) — 남의 컴퓨터에 올리고 이름 하나를 빌린 것
  3. 주소가 뜬다 — srl-saju-fortune.pages.dev
  4. QR을 띄운다. 지금부터 각자 폰으로 들어온다 — 앱스토어도 가입도 필요 없다
키 없이여덟 글자와 한자 뜻은 열쇠 없이도 본다. AI 문장은 키를 넣으면 되고, 발급은 집에서(P20b)
개인정보생년월일은 강사가 만든 서버로 절대 안 간다 — 애초에 그런 서버가 없다. AI 문장을 누르는 그 순간만 구글로 직접 간다
P21 · 5분 · 로컬 vs 배포 ● · 배포·호스팅·주소 ● · 웹앱·기기 ○ · QR은 당일 생성기 탭에서 · 실패 시 사전 배포본 https://srl-saju-fortune.pages.dev 로

순서는 넷 — ① 폰에서 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분.

부록 · Cloudflare 계정과 API 토큰 발급 절차 (학생 배포용)
구글 키와 다른 점 하나 — Cloudflare Pages는 돈이 안 달려 있다(무료 플랜, 정적 사이트 무제한). 하지만 토큰이 새면 남이 내 사이트를 덮어쓰거나 지울 수 있다. 그래서 여전히 열쇠고, 상자에 둔다. 오늘 강사는 ①(드래그) 또는 ②(wrangler pages deploy)로 한다.

갈래가 셋이다

어디까지필요한 것시간열쇠
① 폴더를 드래그해서 올린다Cloudflare 계정(이메일)3분없음
터미널에서 올린다 (wrangler login)계정 + Node5분없음브라우저 로그인 한 번
AI 에이전트가 대신 올린다계정 + API 토큰 + Account ID7분있음이 페이지의 이유

A · 계정 — 모두 공통 3분

  1. dash.cloudflare.comSign up → 이메일·비밀번호 → 인증 메일 확인
  2. 도메인을 추가하라는 화면은 건너뛴다(Skip). 오늘은 빌린 이름(*.pages.dev)만 쓴다

B · ① 드래그로 올리기 — 열쇠 없음

  1. 왼쪽 메뉴 Workers & PagesCreatePages 탭 → Upload assets
  2. 프로젝트 이름을 정한다 — 영문 소문자·숫자·하이픈. 이게 그대로 주소가 된다(이름.pages.dev). 나중에 바꾸기 번거로우니 처음에 정한다
  3. 폴더를 통째로 창에 드래그. 브라우저가 폴더 드래그를 거부하면 zip으로 압축해 올린다
  4. Deploy site → 주소가 뜬다. 폰으로 열어본다
  5. 고쳤으면 같은 프로젝트 화면에서 Create new deployment → 다시 드래그. 주소는 그대로

C · ③ 에이전트용 열쇠 — API 토큰 7분

  1. 오른쪽 위 프로필 아이콘 → My ProfileAPI TokensCreate Token
  2. 템플릿 말고 맨 아래 Create Custom Token템플릿 "Edit Cloudflare Workers"는 권한이 더 넓다 — 필요한 만큼만 준다
  3. Token name: hanyejong-pages 같은 알아볼 이름
  4. Permissions: Account · Cloudflare Pages · Edit — 이 한 줄이면 된다
  5. Account Resources: Include · 내 계정
  6. Continue to summaryCreate Token
  7. 토큰은 이 화면에서 한 번만 보인다. 복사해서 바로 상자(E)에 넣는다. 화면을 닫으면 다시 못 본다 — 잃으면 새로 만든다(로테이션)

D · Account ID — ③에만 필요

  1. Workers & Pages 첫 화면 오른쪽에 Account ID가 보인다(32자 16진수). 복사②로 로그인한 상태라면 터미널에서 npx wrangler whoami로도 나온다

E · 상자에 넣고 에이전트에게 시키기

프로젝트 폴더의 .env.local — 값은 여기 안 적는다
CLOUDFLARE_API_TOKEN=(C에서 복사한 토큰)
CLOUDFLARE_ACCOUNT_ID=(D에서 복사한 ID)
나는 윈도우 11이야. .env.local에 있는 Cloudflare 토큰으로 site 폴더를 Pages에 올려줘. 프로젝트 이름은 내이름-saju. 토큰 값은 화면에 출력하지 마.
npx wrangler pages deploy site --project-name=내이름-saju

함정 넷

토큰이 생성 직후 한 번만 보인다
바로 .env.local로. 놓쳤으면 지우고 새로 만든다
프로젝트 이름 = 주소. 오타가 영구 주소가 된다
처음에 정한다. 예: hong-saju
고친 게 바로 안 보인다(캐시)
시크릿 창으로 열거나 하드 리프레시(Ctrl+Shift+R). 수업 중 미리 겪어 둔다
wrangler login이 브라우저 창을 못 띄운다(윈도우)
터미널에 뜬 URL을 복사해 직접 연다
P21b · 1분(P21에 흡수) · 배포·호스팅·주소 ● · API 키·상자 ●(회수) · 로테이션 ○ · 근거 Cloudflare Pages 문서(Direct Upload · 토큰 = Account·Cloudflare Pages·Edit) · 볼트 사주 04 §5와 일치

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 배포 절차와 일치.

무엇을 만드나 — STELLAR CUTTER
player_ship.png
enemy_scout.png
enemy_bomber.png
item_weapon_viper.png
boss_body.png · 1024×64 — 보스 몸통은 이 한 줄짜리 그림 하나다

부품은 전부 남이 만든 것 — 오픈소스 두 번째

부품어디서조건
엔진 Phaser 3CDN 한 줄오픈소스
그림 기체·적·총알·폭발·아이템Kenney.nlCC0 (표기·허락 없이 써도 됨)
소리 BGM 6 · 효과음 12OpenGameArt (SketchyLogic 등)CC0

게임 엔진도, 그림도, 소리도 남이 만들어 공개해 둔 것이다. 이게 없으면 이 게임은 못 만든다.

오픈소스가 나오는 두 번째 자리다. 아까는 계산기 하나였고, 이번엔 엔진·그림·소리가 전부다.

조건이 표기인 것도 섞여 있다 — assets/CREDITS.txt가 그 표기를 모아 둔 파일이다.

P22 · 1분 · 오픈소스 ○ · 라이선스 ○ · 화면 = 타이틀 캡처 + 부품 목록, CREDITS.txt를 잠깐 띄운다

완성본 타이틀 화면 한 장 + 부품 목록. 1분.

"게임 엔진도 남이 만든 겁니다. 그림하고 소리도요. 이거 없으면 이 게임 못 만들어요."

오픈소스 두 번째 — 아까는 계산기 하나, 이번엔 엔진·그림·소리 전부.

assets/CREDITS.txt를 잠깐 띄운다: "조건이 '표기'인 것도 있어요. 이 파일이 그겁니다."

붙는 단어: 오픈소스 ○ · 라이선스 ○

⚠ 스프라이트 실물은 img/sprites/의 진짜 파일이다(32~104px). 확대해서 보여주는 것이라 각이 진다 — 원래 그렇다고만 말하고 넘어간다.

boss_body.png가 1024×64인 것을 여기서 한 번 보여 두면, P24의 "보스가 찌그러져 보인다"가 설명 없이 이해된다.

라이브 제작 ② — 검은 화면에서 총알이 나가기까지
  1. 빈 폴더에 index.html 하나 + Phaser CDN 한 줄 → 검은 화면이 뜬다
  2. 기체 스프라이트 하나 + 방향키 이동 → 움직인다
  3. 스페이스 → 총알이 나간다
  4. (시간 되면) 적이 내려오고, 맞으면 터지고, 점수가 오른다

REPLAY 실측으로 1~3단계는 85분이 걸렸다. 오늘 무대에서 하는 것은 골격까지 10분이다.

파일 이름은 사람도 읽고 기계도 읽는다
player_ship.png
누구의(player) + 무엇(ship) — 내가 조종하는 기체
enemy_scout.png
누구의(enemy) + 무엇(scout) — 적 정찰기. 이름만 보고 어느 편인지 안다
bullet_player_viper.png
무엇(bullet) + 누구의(player) + 어떤 무기(viper) — 세 토막으로 읽힌다
item_weapon_viper.png
같은 viper가 아이템 이름에도 붙는다 — 무기와 아이템이 이름으로 이어진다
P23 · 10분 · 윈도우/맥 ● · 파일 이름·구조 ● · 소스 코드 ○ · 실패 시 2번에서 막히면 완성본으로 바로 전환, 골격은 캡처 3장으로 대체

화면 셋을 나란히 — 터미널 + 에디터 + 브라우저.

REPLAY 1~2단계, 필요하면 3단계까지. 4번은 시간이 될 때만.

명령어를 칠 때 P10(기기와 운영체제 — 같은 말을 다르게 친다)을 한 번 더 회수한다.

파일 이름을 보이면서: "이름을 왜 이렇게 붙였을까요. 사람도 알아보고 기계도 알아보라고요."

붙는 단어: 윈도우/맥 ● · 파일 이름·구조 ● · 소스 코드 ○ — 10분.

⚠ 시간 관리 — REPLAY 실측은 1~3단계 85분이다. 오늘은 골격만 10분이 목표다. 2번(이동)에서 막히면 붙들지 말고 완성본으로 전환하고, 골격은 미리 찍어 둔 캡처 3장으로 대체한다.

⚠ bullet_player_viper.png는 화면에서 점처럼 작아 스프라이트 실물을 안 띄운다(그 줄만 그림 없음). 대신 이름이 세 토막이라는 것만 짚는다.

완성본 — 배포된 주소를 연다

완성본은 배포된 주소에서 그대로 돈다

https://srl-hanyejong-shmup.pages.dev
규모js/ 파일 11개 · 1,300줄
내용6종 · 무기 4종 · 폭탄 2종 · 보스 3페이즈 · 사운드
에셋assets/sprites/ enemy_weak/ · enemy_mid/ · boss/
확인어젯밤 라이브 200 · 로컬과 SHA-256 일치 — 6c95de8 완성 커밋이 그대로 배포돼 있다

새 탭에 주소를 치고, 강사가 30초 플레이한다.

코드 폴더를 잠깐 띄우되 읽지는 않는다 — 이렇게 살이 붙었다는 것만 본다. 이걸 다 라이브로 하면 오늘은 라이브 코딩만 보다가 끝난다.

라이브로 만든 골격(P23)과 같은 물건이다. 그 위에 시간을 더 부은 것뿐이다.

P24 · 2분 · 붙는 단어 없음 · 주소는 어젯밤 라이브 확인(200)

"이제 어젯밤에 미리 다 만들어둔 완성본을 열겠습니다."

"이렇게 살이 붙었다는 것만 보세요. 이걸 다 라이브로 하면 오늘 라이브 코딩만 보다가 끝납니다."

━━━ ⚠ 시연 자세 (새벽 실측) — 이 세 가지를 지키지 않으면 무대에서 멈춘다 ━━━

⚠ ① 하단 중앙을 클릭하고, 커서를 거기 둔 채 마우스로 조작한다. 키보드만 쓰면 기체가 좌상단(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 · 93×84
  1. 이미지 생성 서비스에 새 그림을 시킨다 — 다른 회사 서비스에 전화를 거는 일이다. 아까 그 열쇠를 여기서 또 쓴다
  2. 받은 그림으로 같은 파일을 덮어쓴다 — 이름도 자리도 그대로다
  3. 하드 리프레시로 다시 불러온다 → 다른 색 적이 내려온다
일반 새로고침으로는 그림이 안 바뀐다Ctrl+Shift+R Cmd+Shift+R브라우저는 한 번 받은 그림을 기억해 두려고 한다(캐시). 강제로 다시 받아오라고 알려주는 것이 하드 리프레시다. 대본을 쓰다가 실제로 한 번 걸렸던 자리다(REPLAY C-1) — 무대에서도 걸린다고 보고 들어간다.
P25 · 8분 · API 키 ●(회수) · 캐시 –(한 마디) · 실패 시 생성 API가 막히면 미리 만들어 둔 3색 버전 중 하나로 파일만 교체 + 하드 리프레시

순서: 이미지 생성 → assets/sprites/enemy_weak/enemy_scout.png 덮어쓰기 → 하드 리프레시(Ctrl+Shift+R / Cmd+Shift+R) → 다른 색 적이 내려온다.

"지금 얘한테 그림 하나를 새로 그려 달라고 시킬 건데, 다른 회사 서비스한테 전화를 거는 거예요. 아까 그 열쇠, 여기서 또 씁니다." — P20 API 키 회수.

함정을 숨기지 말고 그대로 보여준다. 일반 새로고침으로는 안 바뀐다. "브라우저는 그림을 한 번 보면 기억해 두려고 해서, 강제로 다시 불러오라고 알려줘야 해요. 그게 방금 누른 새로고침이에요."

⚠ REPLAY C-1: 대본을 쓰다가 실제로 한 번 걸렸다. 무대에서 당황하지 말고 그 자리에서 설명 재료로 쓴다.

붙는 단어: API 키 ●(회수) · 캐시 –(한 마디) — 8분.

라이브 변경 ② — 적 한 종류를 추가한다
site/js/difficulty.js — SHMUP.ENEMY_BASE 에 항목 하나 추가
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'
},
같은 파일 — SHMUP.WAVES 의 types 에 한 단어
types: [ … , 'ufo' ]   // 단어 하나만 더한다

저장 → 새로고침. 이건 일반 새로고침으로 된다 — 그림이 아니라 코드라서.

변경 범위 — 건드릴 데와 안 건드릴 데
건드린다
site/js/difficulty.js
적의 목록. 이름·체력·속도 같은 수치가 모여 있다 — 여기에 항목 하나를 더한다
안 건드린다
site/js/enemies.js
적을 실제로 움직이는 코드. 오늘 한 줄도 건드리지 않는다

건드릴 데와 안 건드릴 데를 먼저 말하는 것이 프롬프트를 잘 쓰는 것보다 중요하다.

게임을 움직이는 큰 코드는 이 목록만 읽고 "적이 몇 종류 있구나"를 안다. 그래서 적 하나 추가는 게임 전체를 다시 짜는 일이 아니라 목록에 한 줄 더하는 일이 된다.

P05에서 본 구조 자체가 자산이다의 회수 지점이다.

P26 · 6분 · 변경 범위 ● · 구조=자산(회수) · 시간이 밀리면 제일 먼저 자르는 장 · 실패 시 7종 버전 별도 배포 주소로 대체

먼저 변경 범위를 말한다: "건드릴 데하고 안 건드릴 데를 먼저 말합니다. 이게 프롬프트 잘 쓰는 것보다 중요해요." — enemies.js(움직이는 코드)는 한 줄도 안 건드린다.

"이 목록 하나만 봐주세요. 게임을 움직이는 큰 코드는 이 목록만 읽고 '적이 몇 종류 있구나' 하거든요. 그래서 적 하나 추가하는 게 게임 전체를 다시 짜는 일이 아니라, 이 목록에 한 줄 더하는 일이 돼요." — P05 "구조 자체가 자산"의 회수.

붙는 단어: 변경 범위 ● · 구조=자산(회수) — 6분. 시간이 밀리면 제일 먼저 자르는 장이다.

⚠ 계획서 불일치 — 볼트 04_수업활용_리허설.mdenemies.json을 가정했지만 실제 구현엔 그 파일이 없다. 무대에서는 이 문서/REPLAY.md만 본다. 학생이 파일명을 물으면 difficulty.js라고 답한다.

⚠ 실패 시 — REPLAY는 "7종 버전 별도 배포 주소"를 대체로 적었다. 그 주소가 실제로 있는지 아침에 확인(준비물 항목).

라이브 변경 ③ — 숫자 두 개, 그리고 보스를 앞당기는 한 줄
게임이 돌아가는 채로 개발자도구 콘솔(F12)
콘솔 — 새로고침 없이 다음 프레임부터 바뀐다
SHMUP.LIVE_TUNE.enemySpeedMultiplier = 3.0   // 적 속도 3배
SHMUP.LIVE_TUNE.spawnInterval = 0.3          // 등장 간격 30%

영구히 바꾸려면 difficulty.js 맨 위 LIVE_TUNE 세 값을 고치고 저장 → 새로고침.

보스를 보이려면 — 시작 전에 콘솔 한 줄
시작 전 — 15초에 보스, 20초 안에 격파
SHMUP.BOSS_WARN_SEC = 10; SHMUP.BOSS_START_SEC = 15; SHMUP.BOSS.hp = 80;

같은 장의 연장이라 자연스럽다 — 숫자를 세 개 더 바꿨더니 보스가 15초에 온다.

기본보스는 고정 105초에 나온다
격파등장 뒤 2~3.5분이 더 든다 — 호밍니들 113초 · 바이퍼 178초
합계자연 플레이로 보스를 보려면 4~5분. 34분짜리 챕터에서 낼 시간이 아니다
시연위 한 줄이면 15초에 등장 · 20초 안에 격파

치기 전에 먼저 묻는다 — 이 두 숫자가 바뀌면 게임이 어떻게 달라질 것 같은지.

그리고 방금 그것을 무섭게 보이던 콘솔에서 했다. P15의 뒤집기다.

P27 · 4분 · 소스 코드 ● · 콘솔 ○(무섭지 않다) · 실패 시 콘솔이 막히면(교내 정책) 파일 수정 + 새로고침

치기 전에 묻는다: "숫자 두 개만 바꿀 거예요. 하나는 적이 내려오는 속도, 하나는 나오는 간격. 이게 이렇게 바뀌면 게임이 어떻게 달라질 것 같으세요?"

바뀐 뒤: "숫자 하나 바꿨는데 게임이 달라지죠. 그리고 방금 그 무서운 콘솔에서 한 거예요." — P15(문제를 볼 때)의 뒤집기.

영구히 바꾸려면 difficulty.js 맨 위 LIVE_TUNE 세 값 → 저장 → 새로고침.

보스 시연 (실측 검증) — 보스는 고정 105초에 나오고 잡는 데 2~3.5분이 더 든다(호밍니들 113초 · 바이퍼 178초). 자연 플레이로는 4~5분. 그래서 시작 전에 콘솔 한 줄을 미리 친다. "숫자 세 개 더 바꿨더니 보스가 15초에 옵니다."

⚠ 보스전에 들어가면 3번 키(펄스레이저)를 쓰지 않는다 — 사거리가 안 닿아 데미지 0이다(P24 시연 자세 ②).

붙는 단어: 소스 코드 ● · 콘솔 ○(무섭지 않다) — 4분.

재배포 — 같은 주소, 예순 대의 폰
재배포 — 고친 것을 같은 주소에 덮어쓴다
$ npx wrangler pages deploy site

폰에서 열었을 때 — 세로 화면

QR — 현장에서 띄운다
https://srl-hanyejong-shmup.pages.dev

같은 주소가 PC에서도 폰에서도 열린다. 60명 최초 로드가 1인당 2.1MB — QR은 쉬는 시간에 미리 열게 하면 안전하다.

폰에서 되는 것 · 안 되는 것 (새벽 실측)
  • 탭 → 시작
  • 드래그하면 기체가 손가락을 1:1로 따라온다
  • 자동 발사 · 격추 · 점수 · 게임오버 후 탭 재시작
  • 375×812에서 가로 스크롤 0 · 가로모드도 잘림 없음(세로 권장)
  • 폭탄 2종 · 무기 전환은 폰에서 안 된다SPACE · 우클릭 · 숫자키 전용(player.js:35,88,93-96)
  • 하단 안내문("방향키/WASD+마우스 · SPACE · 우클릭")은 폰에선 전부 틀린 말이다
결과폰 유저는 목숨 3 · 방어 0 · 1인당 40~90초면 끝난다 — 금방 죽는 게 정상이다
이유터치 전용 코드는 한 줄도 없다. 포인터 추적 덕에 "우연히" 되는 구조
P28 · 3분 · 재배포·Git ○ · 반응형·기기 ○ · 플레이는 P29·P30 말하는 동안 계속 · 실물 iOS Safari·Android Chrome은 미검증(에뮬레이션 통과)

"고칠 때마다 올립니다. 이 기록을 훨씬 촘촘하게 해주는 게 Git이에요." — P12 회수, 여기까지만.

"같은 주소가 PC에서도 폰에서도 열립니다." → 예순 명이 각자 폰으로 한 판. 시간이 남으면 적 하나를 더 라이브로 추가한다.

폰 안내 문구: "폰에서는 손가락으로 끌면 됩니다. 폭탄은 PC에서만요. 금방 죽는 게 정상이에요."

⚠ 하단 안내문이 폰에서는 틀린 말이다("방향키/WASD+마우스 · SPACE · 우클릭"). 학생이 그걸 보고 헤매기 전에 먼저 말해 준다.

⚠ 실물 iOS Safari·Android Chrome은 미검증이다(에뮬레이션만 통과). 아침에 폰 1대로 연타·핀치 줌을 확인한다.

⚠ 60명 최초 로드 = 1인당 2.1MB. QR을 쉬는 시간에 미리 열게 하면 동시 접속 부하를 피할 수 있다.

붙는 단어: 재배포·Git ○ · 반응형·기기 ○ — 3분.

마무리 — 점수가 남지 않는 이유

뒤가 없다 — 그래서 점수가 안 남는다

지금게임오버 화면의 점수는 새로고침하면 사라진다
이유이 게임엔 뒤(백엔드)가 없다 — 화면만 있는 물건이다
남기려면남의 컴퓨터(서버)와 거기 쌓아 두는 곳(데이터베이스)이 필요하다 — 거기부터는 다른 얘기다

챗봇은 뒤가 있어서 기억이 남았다. 그래서 오늘 그것만 드래그로 못 올렸다 — P04의 상자 셋, P13의 "화면은 앞, 서버는 뒤".

사주 웹앱의 기억은 각자 폰 안에만 있었다. 옆사람 폰엔 없었다 — P19.

오늘 만진 셋의 차이가 여기서 한 문장으로 묶인다. 뒤가 있느냐 없느냐.

P29 · 1분 · 백엔드 ●(회수) · 데이터베이스 –

게임오버 화면. 점수. 새로고침하면 사라진다. 1분. 짧게 끊는다.

"점수가 저장이 안 되죠. 이 게임엔 뒤가 없어요. 그래서 점수가 안 남습니다. 남기려면 서버가 필요하고, 그때부터 다른 얘기가 됩니다. 아까 챗봇은 뒤가 있어서 기억이 남았고, 그래서 드래그로 못 올린 거예요."

P04(화면·서버·팩) · P13(웹이 돌아가는 구조) · P19(기억은 내 폰 안에만)를 한 문장으로 묶는 장이다. 새로 설명하지 않는다.

붙는 단어: 백엔드 ●(회수) · 데이터베이스 – — 1분.

학생들은 이 말을 듣는 동안에도 폰으로 계속 게임을 하고 있다(P28). 그대로 두고 P30으로 넘어간다.

회수 — 오늘 만든 것을 지도에 되돌려 놓는다
Local
내 컴퓨터
  • 사주앱 화면 · 내 사주(기억)
  • 게임 코드 · difficulty.js
  • 브라우저 · 폴더 · 파일
Server
남의 컴퓨터
  • 두 주소 · pages.dev
  • 올리면 남도 본다
  • 점수는 여기 없다
External
바깥
  • 구글 AI · 이미지 생성
  • 열쇠 · 비용
  • ← 만세력 · Phaser · Kenney
01
화면은 앞, 서버는 뒤.
02
로컬은 나만, 올리면 남도.
03
열쇠는 상자에.
04
남이 만든 걸 갖다 쓴다. 조건은 확인한다.
05
만드는 것보다 고치는 게 실력이다.
P30 · 5분 · 회수 — P08 지도에 오늘 만든 것을 얹는다 · 새 단어 없음

P08에서 봤던 그 지도 그대로. 이번엔 칸마다 오늘 만든 것이 들어가 있다. 새 단어를 얹지 않는다 — 이미 나온 것만 제자리에 놓는다.

왼쪽부터 짚는다. 내 컴퓨터 — 사주앱 화면과 내 사주(기억), 게임 코드와 difficulty.js. 남의 컴퓨터 — 두 주소(pages.dev). "올렸으니 남도 봅니다. 다만 점수는 여기 없어요 — 각자 폰에만 있습니다." 바깥 — 구글 AI, 이미지 생성, 열쇠와 비용. 그리고 왼쪽으로 들어온 것: 만세력·Phaser·Kenney.

회수 문장 다섯을 하나씩 읽는다. 설명을 덧붙이지 않는다.

클로징: "어떤 시스템과 스토리텔링이 합쳐져 돌아가는 구조를 만드는 것, 전체가 그렇게 어려운 과업은 아닙니다." [27:39]

5분.

과제 — 다음 주까지 해 볼 것
  1. 오늘 만든 것 중 하나를 자기 재료로 바꿔 본다사주앱의 주제 버튼 하나 / 게임의 적 그림 하나
  2. 안 되면 어디까지 됐는지 캡처를 남긴다환경 문제는 창작 실패가 아니다
  3. 주소가 나오면 단톡방에
  4. (선택) AI에게 첫 문장으로 내 환경을 말하는 습관 — “나는 윈도우 11이야.”
실습 1 · 사주 웹앱https://srl-saju-fortune.pages.dev
실습 2 · 슈팅게임https://srl-hanyejong-shmup.pages.dev
P31 · 2분 · 두 주소는 단톡방에 · 안 되면 캡처를 남긴다

네 줄만 읽고 끝낸다.

① 오늘 만든 것 중 하나를 자기 재료로 바꿔 본다 — 사주앱의 주제 버튼 하나, 게임의 적 그림 하나. 둘 다 안 해도 된다. 하나면 된다.

② 안 되면 어디까지 됐는지 캡처를 남긴다. 환경 문제는 창작 실패가 아니다. "실패해도 어디까지 됐는지 말할 수 있으면 됩니다."

③ 주소가 나오면 단톡방에.

④ (선택) P10에서 본 실전 규칙 — AI에게 첫 문장으로 내 환경을 말하는 습관. "나는 윈도우 11이야."

두 주소는 화면에 띄운 채로 두고, 단톡방에도 올린다. 2분.

1 / 0
← → 이동  ·  N 노트  ·  O 목차  ·  F 전체화면  
발표자 노트