<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>AI on To the Moon</title><link>https://junstellar.github.io/tags/ai/</link><description>Recent content in AI on To the Moon</description><generator>Hugo -- gohugo.io</generator><language>ko-kr</language><lastBuildDate>Tue, 08 Sep 2026 11:00:00 +0900</lastBuildDate><atom:link href="https://junstellar.github.io/tags/ai/index.xml" rel="self" type="application/rss+xml"/><item><title>마법의 수 7±2 — 지능검사가 진짜 쓰는 기억력 검사를 만들었습니다</title><link>https://junstellar.github.io/p/digit-span-test/</link><pubDate>Tue, 08 Sep 2026 11:00:00 +0900</pubDate><guid>https://junstellar.github.io/p/digit-span-test/</guid><description>&lt;p&gt;&lt;a class="link" href="https://junstellar.github.io/lab/?lang=ko" &gt;생각별 연구소&lt;/a&gt;에 네 번째 테스트를 추가했습니다. 이번 건 시리즈의 방향을 한 번 다잡고 만든 물건이라, 그 이야기부터 해야겠습니다.&lt;/p&gt;
&lt;p&gt;👉 결과물 먼저: &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/tools/digit-span/?lang=ko" &gt;숫자 기억력 테스트&lt;/a&gt;&lt;/strong&gt; — 3분이면 끝납니다.&lt;/p&gt;
&lt;h2 id="게임-말고-검사를-만들기로-했습니다"&gt;게임 말고, 검사를 만들기로 했습니다
&lt;/h2&gt;&lt;p&gt;테스트가 세 개쯤 쌓이니 갈림길이 보였습니다. 재미 위주의 미니게임을 늘릴 수도 있고, 진짜 인지검사 쪽으로 갈 수도 있었죠. 저는 후자를 골랐습니다. 재미있는 게임은 &lt;a class="link" href="https://junstellar.github.io/games/" &gt;AI 게임 랩&lt;/a&gt;에 이미 많으니까, 연구소는 이름값을 하기로요. 그래서 기준을 정했습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;신경심리·지능검사에서 실제로 쓰이는 과제만&lt;/strong&gt; 만든다&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;발표된 연구 참고치가 있는 과제만&lt;/strong&gt; 만든다&lt;/li&gt;
&lt;li&gt;결과는 &lt;strong&gt;지능검사와 같은 형식의 성적&lt;/strong&gt;으로 낸다&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이 기준으로 고른 첫 과제가 **숫자 폭(Digit Span)**입니다. 숫자가 한 자리씩 나타났다 사라지고, 본 순서대로 — 그다음엔 거꾸로 — 입력하는 검사. 표준화 지능검사들이 &lt;strong&gt;작업기억 소검사로 실제 채택하고 있는&lt;/strong&gt; 그 과제입니다.&lt;/p&gt;
&lt;h2 id="마법의-수-72"&gt;마법의 수 7±2
&lt;/h2&gt;&lt;p&gt;이 과제가 유명한 건 1956년 심리학자 조지 밀러의 논문 덕분입니다. 사람이 한 번에 붙잡을 수 있는 정보의 개수가 대략 &lt;strong&gt;7개, 오차 ±2&lt;/strong&gt; 라는 관찰 — 이른바 &amp;lsquo;마법의 수&amp;rsquo;죠. 이후 연구들에서 성인의 정방향 숫자 폭은 평균 6&lt;del&gt;7자리, **거꾸로 외우는 역방향은 4&lt;/del&gt;5자리**로 보고됩니다.&lt;/p&gt;
&lt;p&gt;정방향과 역방향의 차이가 이 검사의 핵심입니다. 정방향은 단순 &lt;strong&gt;저장&lt;/strong&gt; 용량을 재지만, 역방향은 정보를 붙잡은 채 &lt;strong&gt;조작&lt;/strong&gt;해야 합니다. 머릿속에 적어둔 숫자를 거꾸로 읽어내는 동안 원본이 증발하지 않게 버텨야 하죠. 그래서 역방향이 두 자리쯤 짧은 게 정상이고, 이 두 값을 나란히 보는 게 작업기억을 보는 정석입니다.&lt;/p&gt;
&lt;h2 id="실제-검사의-규칙을-그대로"&gt;실제 검사의 규칙을 그대로
&lt;/h2&gt;&lt;p&gt;만들면서 지킨 절차들입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;1초에 한 자리&lt;/strong&gt; — 실제 검사의 제시 속도. 더 빠르면 지각 검사가 되고, 느리면 암송 전략이 끼어듭니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;자리수당 2회 시도, 2연속 실패면 중단&lt;/strong&gt; — 실제 검사의 중단 규칙입니다. 한 번 실수로 끝나지 않되, 한계에 도달하면 깔끔하게 멈춥니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;연속 같은 숫자 금지&lt;/strong&gt; — &amp;ldquo;9, 9&amp;rdquo; 같은 우연한 청킹 찬스를 배제합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;제시 중 탭을 벗어나면 그 시도는 무효&lt;/strong&gt; — 브라우저가 백그라운드 탭의 타이머를 늦추는 바람에 숫자가 느리게 나오는 왜곡을 막습니다. 만들다 직접 발견한 함정이었어요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="성적표는-지능검사-형식으로"&gt;성적표는 지능검사 형식으로
&lt;/h2&gt;&lt;p&gt;이번 테스트의 진짜 목표는 여기였습니다. 지능검사의 소검사들은 원점수를 &lt;strong&gt;평균 10, 표준편차 3의 환산점수&lt;/strong&gt;(1~19점)로 바꿔서 읽습니다. 10점이면 딱 평균, 13점이면 상위 16% 즈음 — 어떤 소검사든 같은 잣대로 읽히는 형식이죠.&lt;/p&gt;
&lt;p&gt;그래서 결과 화면이 이렇게 나옵니다: &lt;strong&gt;환산점수 11/19 · 평균 · 백분위 약 63%&lt;/strong&gt; — 정방향·역방향 각각의 환산점수와 분류(평균 하·평균·평균 상·우수…)도 함께요.&lt;/p&gt;
&lt;p&gt;다만 실제 지능검사의 규준표는 비공개 상업 자료라 쓸 수 없습니다. 이 환산은 &lt;strong&gt;연구 문헌의 성인 평균·표준편차를 같은 형식으로 옮긴 참고치&lt;/strong&gt;이고, 결과 화면에도 그렇게 적었습니다. 대신 이 시리즈의 다음 큰 그림이 여기 걸려 있습니다 — 방문자들의 익명 점수가 쌓이면, 참고치를 &lt;strong&gt;우리 표본의 진짜 백분위로 교체&lt;/strong&gt;하는 것. 그 준비로 이번 테스트부터 점수 계산부를 나중에 그대로 전송할 수 있는 형태로 설계해뒀습니다.&lt;/p&gt;
&lt;h2 id="연구소-근황"&gt;연구소 근황
&lt;/h2&gt;&lt;p&gt;현재까지 연구소에 추가된 테스트는 4개입니다 — &lt;a class="link" href="https://junstellar.github.io/p/kids-iq-test/" &gt;IQ 테스트&lt;/a&gt;, &lt;a class="link" href="https://junstellar.github.io/p/big5-test/" &gt;Big Five&lt;/a&gt;, &lt;a class="link" href="https://junstellar.github.io/p/reaction-test/" &gt;반응속도&lt;/a&gt;, 그리고 숫자 폭. 다음 예정은 🎨 스트룹 테스트이고, 그 뒤로 공간 회전과 패턴 기억을 계획하고 있습니다. 다 갖춰지면 작업기억·처리속도·공간·언어를 아우르는 &lt;strong&gt;인지 프로필 배터리&lt;/strong&gt;가 됩니다.&lt;/p&gt;
&lt;h2 id="해보기"&gt;해보기
&lt;/h2&gt;&lt;p&gt;👉 &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/tools/digit-span/?lang=ko" &gt;숫자 기억력 테스트 하러 가기&lt;/a&gt;&lt;/strong&gt; — 정방향과 역방향, 3분.&lt;/p&gt;
&lt;p&gt;당신의 마법의 수는 몇입니까? 저는 역방향에서 머리가 하얘지는 경험을 하실 거라고 장담합니다.&lt;/p&gt;</description></item><item><title>AI로 게임 만들기 #11 — 60초 스네이크를 만들었습니다 (공개빌드 #11)</title><link>https://junstellar.github.io/p/ai-game-lab-build-11/</link><pubDate>Sun, 06 Sep 2026 09:00:00 +0900</pubDate><guid>https://junstellar.github.io/p/ai-game-lab-build-11/</guid><description>&lt;img src="https://junstellar.github.io/p/ai-game-lab-build-11/snake-play.webp" alt="Featured image of post AI로 게임 만들기 #11 — 60초 스네이크를 만들었습니다 (공개빌드 #11)" /&gt;&lt;p&gt;AI 게임 랩 열 번째 게임으로 스네이크를 만들었습니다. 점수 나는 방식 하나만 바꿨는데 게임이 꽤 달라졌습니다.&lt;/p&gt;
&lt;p&gt;시간은 60초로 고정입니다. 그 안에 최대한 높은 점수를 내면 됩니다. 부딪혀도 판이 끝나지 않아요. 몸이 처음 길이인 세 칸으로 돌아가고 1.2초 멈췄다가 이어집니다. 그동안에도 시계는 흘러가니 결국 누구나 60초를 씁니다. 랭킹으로 겨루려면 쓴 시간이 같아야 해서 이렇게 잡았습니다.&lt;/p&gt;
&lt;p&gt;조작은 방향키나 WASD, 휴대폰에서는 화면을 쓸면 됩니다. 격자는 16×16이고 뱀은 초당 여덟 칸씩 움직입니다.&lt;/p&gt;
&lt;p&gt;👉 &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/snake60/" &gt;60초 스네이크 하러 가기&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="점수-규칙"&gt;점수 규칙
&lt;/h2&gt;&lt;p&gt;보통 스네이크는 먹이를 먹으면 점수가 오릅니다. 여기서도 먹이가 30점이긴 한데, 그보다 중요한 건 머리가 지나가는 칸의 좌우가 얼마나 막혔는지입니다.&lt;/p&gt;
&lt;p&gt;옆이 트여 있으면 0점입니다. 한쪽에 제 몸이 있으면 1점. 양옆이 다 막힌 한 칸 틈을 지나가면 15점이 들어오고, 여기에 HEAT라는 배수가 최대 세 배까지 붙습니다. HEAT는 좁은 틈을 연달아 통과할 때만 올라가고, 넓은 데로 나오면 금방 식습니다.&lt;/p&gt;
&lt;p&gt;&lt;img alt="옆이 어떻게 막혔는지에 따라 점수가 갈린다. 벽만 있으면 0점이다" class="gallery-image" data-flex-basis="513px" data-flex-grow="213" height="860" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-11/snake-rule.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-11/snake-rule_hu_f5036dfb72dc9fd5.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-11/snake-rule_hu_a4451cc669ffafec.webp 1600w, https://junstellar.github.io/p/ai-game-lab-build-11/snake-rule.webp 1840w" width="1840"&gt;&lt;/p&gt;
&lt;p&gt;바깥 벽은 세지 않습니다. 벽에만 붙어서 달려서는 아무 점수도 안 나와요. 왜 그렇게 했는지는 아래에 적었습니다.&lt;/p&gt;
&lt;p&gt;좁은 틈은 몸이 길어야 생깁니다. 그래서 먹이도 결국 필요해요. 먹이 하나에 몸이 네 칸 자라고, 좁은 틈에 놓인 먹이를 먹으면 5배인 150점이 들어옵니다. 한 판에서 제일 크게 버는 순간이 여기입니다.&lt;/p&gt;
&lt;p&gt;&lt;img alt="똬리 튼 몸 사이 한 칸 틈을 빠져나오는 중. HEAT가 2.8배까지 올라 42점이 붙었다" class="gallery-image" data-flex-basis="200px" data-flex-grow="83" height="1030" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-11/snake-play.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-11/snake-play_hu_65d8e86d4fa342b8.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-11/snake-play.webp 860w" width="860"&gt;&lt;/p&gt;
&lt;h2 id="왜-이렇게-만들었나"&gt;왜 이렇게 만들었나
&lt;/h2&gt;&lt;p&gt;스네이크를 그냥 만들면 두 가지가 걸렸습니다.&lt;/p&gt;
&lt;p&gt;하나는 스네이크가 순수 실력 게임이라는 점입니다. 잘하는 사람이 항상 이깁니다. 게임 랩 게임들은 하루 한 판씩 남들과 점수를 겨루는 형태라, 실력 순서가 그대로 고정되면 못하는 사람은 영영 못 이깁니다. 운이 좀 섞여야 뒤집힐 여지가 생깁니다.&lt;/p&gt;
&lt;p&gt;다른 하나는 60초라는 시간입니다. 1분 안에는 몸이 별로 안 길어집니다. 스네이크가 조여드는 느낌은 몸이 화면을 채우기 시작할 때 오는데, 거기까지 갈 시간이 없어요.&lt;/p&gt;
&lt;p&gt;그래서 점수를 지나간 자리의 위험도로 옮겼습니다. 안전하게만 돌면 점수가 안 나오게 만들어두면, 짧은 시간에도 위험을 감수할 이유가 생기니까요. (&lt;a class="link" href="https://junstellar.github.io/p/ai-game-lab-build-10/" &gt;지난 편&lt;/a&gt;에서는 주사위 게임을 두 번 만들었다 두 번 다 버렸는데, 이번 건 남았습니다.)&lt;/p&gt;
&lt;h2 id="봇을-풀어놨더니-테두리만-돌더군요"&gt;봇을 풀어놨더니 테두리만 돌더군요
&lt;/h2&gt;&lt;p&gt;밸런스를 감으로 잡고 싶지 않아서 자동 플레이어를 만들었습니다. 겁쟁이, 먹이만 쫓는 놈, 위험하게 노는 놈, 무모한 놈. 넷을 각각 80판씩 돌렸습니다.&lt;/p&gt;
&lt;p&gt;처음 버전에서는 바깥 벽도 막힌 면으로 셌습니다. 옆이 막혀 있으면 위험한 거라고 봤거든요.&lt;/p&gt;
&lt;p&gt;결과가 이상했습니다. 테두리를 따라 60초 내내 뱅뱅 도는 것만으로 2,800점이 나왔고, 그동안 한 번도 부딪히지 않았습니다. 안전하면 점수가 없게 만들겠다고 해놓고 가장 안전한 길을 최고점으로 만들어둔 셈이었습니다.&lt;/p&gt;
&lt;p&gt;이유는 간단했습니다. 벽은 움직이지 않습니다. 벽에 붙어 달리는 건 그림으로는 아슬아슬해 보여도 실제로는 아무 일도 일어나지 않아요. 저는 &amp;ldquo;옆이 막혀 있다&amp;quot;를 위험이라고 착각하고 있었습니다.&lt;/p&gt;
&lt;p&gt;그래서 막힌 면 중에 제 몸이 최소 하나는 있어야 점수를 주도록 고쳤습니다.&lt;/p&gt;
&lt;h2 id="이번엔-자기-몸-옆에-딱-붙어서"&gt;이번엔 자기 몸 옆에 딱 붙어서
&lt;/h2&gt;&lt;p&gt;고치고 다시 돌렸습니다. 순서는 맞게 나왔어요. 위험하게 노는 봇이 겁쟁이보다 훨씬 높았습니다. 그런데 그 옆 칸의 숫자를 보고 또 걸렸습니다. 평균 2,911점을 내면서 &lt;strong&gt;부딪힌 횟수가 0.0번&lt;/strong&gt;이었거든요. 480칸 중 393칸에서 점수를 뽑았는데도 말이죠.&lt;/p&gt;
&lt;p&gt;자기 몸 옆에 나란히 붙어 달리는 게 안정적인 자세였습니다. 몸이 제 뒤를 그대로 따라오니까 옆은 계속 막혀 있는데, 정작 부딪힐 일은 없습니다. 벽으로 하던 짓을 몸으로 하고 있었을 뿐이었어요.&lt;/p&gt;
&lt;p&gt;이번에는 규칙 대신 배점을 손봤습니다. 나란히 달리기는 1점 고정으로 낮추고 HEAT가 안 붙게 했습니다. HEAT는 양옆이 다 막힌 좁은 틈에서만 오릅니다.&lt;/p&gt;
&lt;p&gt;돌이켜보면 두 경우 다 같은 착각이었습니다. 벽도, 나란히 가는 제 몸도, 옆에 있기는 한데 저를 쫓아오지는 않습니다. 위험한 건 옆이 막혀 있는 상태가 아니라 제가 다음에 갈 자리를 누가 먼저 차지할 수 있느냐였습니다.&lt;/p&gt;
&lt;h2 id="없는-점수를-쫓던-봇"&gt;없는 점수를 쫓던 봇
&lt;/h2&gt;&lt;p&gt;중간에 이상한 일도 있었습니다. 봇 하나가 네 칸을 뱅뱅 돌면서 아무것도 안 했습니다. 로그를 한 수씩 찍어보니 매번 옆이 막혔다고 판단하는데 실제 점수는 0이었어요.&lt;/p&gt;
&lt;p&gt;게임 쪽 문제가 아니었습니다. 봇이 후보 칸의 위험도를 이동하기 전 상태로 재고 있었습니다. 뱀은 앞으로 가면 꼬리가 한 칸 빠져나가죠. 그러니까 지금은 막혀 있어도 머리가 도착할 때쯤에는 비어 있을 칸이었던 겁니다. 봇은 있지도 않은 점수를 보고 제자리를 돌고 있었습니다.&lt;/p&gt;
&lt;p&gt;로그를 안 찍었으면 멀쩡한 게임을 뜯어고쳤을 겁니다.&lt;/p&gt;
&lt;h2 id="봇이-사람보다-잔인한-테스터인-이유"&gt;봇이 사람보다 잔인한 테스터인 이유
&lt;/h2&gt;&lt;p&gt;이 두 가지를 봇으로 찾은 데는 이유가 있습니다. 봇에게는 부끄러움이 없습니다.&lt;/p&gt;
&lt;p&gt;사람은 테두리만 60초 도는 짓을 잘 안 합니다. 이길 수 있어도 안 해요. 이렇게 이기면 재미없잖아, 하는 감각이 있으니까요. 제가 직접 백 판을 했어도 못 찾았을 겁니다. 자기 게임은 자기가 의도한 방식으로 플레이하게 되거든요. 봇은 그런 게 없고 점수만 봅니다.&lt;/p&gt;
&lt;p&gt;나중에 찾아보니 게임 디자인에서는 오래된 이야기더군요. 게임 디자이너 소렌 존슨이 남긴 말인데, 기회만 주면 플레이어가 게임에서 재미를 최적화해 없애버린다는 겁니다. 플레이어가 최적화하는 건 재미가 아니라 승리니까요.&lt;/p&gt;
&lt;h2 id="숫자로-본-결과"&gt;숫자로 본 결과
&lt;/h2&gt;&lt;p&gt;전략별로 80판씩 돌린 최종 수치입니다.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;전략&lt;/th&gt;
					&lt;th&gt;평균&lt;/th&gt;
					&lt;th&gt;상위 10%&lt;/th&gt;
					&lt;th&gt;최고&lt;/th&gt;
					&lt;th&gt;부딪힘&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;트인 곳만 달림&lt;/td&gt;
					&lt;td&gt;1,155&lt;/td&gt;
					&lt;td&gt;1,465&lt;/td&gt;
					&lt;td&gt;2,248&lt;/td&gt;
					&lt;td&gt;1.1번&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;먹이만 쫓음&lt;/td&gt;
					&lt;td&gt;1,949&lt;/td&gt;
					&lt;td&gt;2,627&lt;/td&gt;
					&lt;td&gt;3,788&lt;/td&gt;
					&lt;td&gt;1.0번&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;몸 옆 나란히&lt;/td&gt;
					&lt;td&gt;1,682&lt;/td&gt;
					&lt;td&gt;2,138&lt;/td&gt;
					&lt;td&gt;2,635&lt;/td&gt;
					&lt;td&gt;0.0번&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;좁은 틈 사냥&lt;/td&gt;
					&lt;td&gt;1,935&lt;/td&gt;
					&lt;td&gt;2,390&lt;/td&gt;
					&lt;td&gt;6,671&lt;/td&gt;
					&lt;td&gt;2.9번&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;노린 데까지는 왔습니다. 몸 옆에 나란히 달리기가 그냥 잘 두는 것보다 낮아졌으니까요. 위험을 하나도 안 지는 전략이 최적해 자리에서 내려온 겁니다.&lt;/p&gt;
&lt;p&gt;다만 좁은 틈 사냥이 압도적으로 유리하지는 않습니다. 평균이나 상위 10%로 보면 먹이만 착실히 쫓는 쪽과 비슷해요. 다른 건 분포입니다. 6,671점 같은 판은 틈을 노린 쪽에서만 나오는데, 그 대신 열에 한 번쯤은 부딪힙니다.&lt;/p&gt;
&lt;p&gt;궁금해서 위험 감수 정도만 바꿔가며 한 번 더 재봤습니다. 앞을 안 보고 틈에 들이받는 쪽은 중앙값이 1,755점, 적당히 조심하는 쪽은 1,747점으로 거의 같았습니다. 최고 기록만 7,981점 대 2,872점으로 크게 갈렸고요. 죽는 게 이득은 아니라는 뜻입니다. 부딪히면 몸이 초기화되는데 이 게임에서 몸은 점수를 뽑는 도구라서, 1.2초 멈추는 것보다 몸을 다시 키우는 10여 초가 훨씬 비쌉니다. 크게 터지는 판이 죽을 자리에 들어간 쪽에서만 나올 뿐입니다.&lt;/p&gt;
&lt;h2 id="몇-점이면-잘한-걸까"&gt;몇 점이면 잘한 걸까
&lt;/h2&gt;&lt;p&gt;봇 기준으로는 트인 곳으로만 다니면 1,100점대, 먹이를 착실히 쫓으면 1,900점대가 나옵니다. 좁은 틈까지 노리면 운이 따를 때 6,000점을 넘고요.&lt;/p&gt;
&lt;p&gt;다만 봇은 매 수마다 남은 공간을 전부 계산해서 움직입니다. 사람은 그렇게 못 하니 실제로는 더 낮게 나올 겁니다. 저는 2,000점 넘기기가 쉽지 않았습니다.&lt;/p&gt;
&lt;p&gt;해보실 때 참고하실 만한 것 몇 가지입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;처음 10초쯤은 먹이를 먹어 몸을 키우는 게 낫습니다. 몸이 짧으면 좁은 틈 자체가 생기지 않습니다.&lt;/li&gt;
&lt;li&gt;HEAT는 좁은 틈을 연달아 지나야 오릅니다. 한 번 통과하고 넓은 데로 나오면 금방 식어요.&lt;/li&gt;
&lt;li&gt;후반에 부딪히면 회복할 시간이 없습니다. 몸이 길어졌을 때가 제일 벌기 좋으면서 제일 위험합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="그림-없이-만들기"&gt;그림 없이 만들기
&lt;/h2&gt;&lt;p&gt;이 게임에는 이미지 파일이 한 장도 없습니다. 뱀도 먹이도 파티클도, 소리까지 전부 코드로 만들었고 파일 하나에 44KB입니다.&lt;/p&gt;
&lt;p&gt;지난 편에서 &amp;ldquo;그래픽을 줄이자&amp;quot;를 &amp;ldquo;화면이 안 움직여도 된다&amp;quot;로 잘못 읽고 게임을 두 개 버렸던 터라 이번엔 신경을 썼습니다. 도형과 선만으로도 움직일 건 다 움직이더군요.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a class="link" href="https://junstellar.github.io/games/snake60/" &gt;60초 스네이크&lt;/a&gt; 한 판 해보시고 몇 점 나왔는지 알려주세요. 혹시 제가 아직 못 막은 지름길을 찾으시면 그것도 알려주시고요. 바로 막겠습니다. 🐍&lt;/p&gt;
&lt;p&gt;다음 편은 새 게임을 들고 오겠습니다.&lt;/p&gt;</description></item><item><title>AI로 게임 만들기 #10 — 두 번 만들고 두 번 버렸습니다 (공개빌드 #10)</title><link>https://junstellar.github.io/p/ai-game-lab-build-10/</link><pubDate>Sat, 29 Aug 2026 09:00:00 +0900</pubDate><guid>https://junstellar.github.io/p/ai-game-lab-build-10/</guid><description>&lt;img src="https://junstellar.github.io/p/ai-game-lab-build-10/dice-push.webp" alt="Featured image of post AI로 게임 만들기 #10 — 두 번 만들고 두 번 버렸습니다 (공개빌드 #10)" /&gt;&lt;p&gt;&lt;a class="link" href="https://junstellar.github.io/p/ai-game-lab-build-9/" &gt;지난 편&lt;/a&gt;의 오디세이아에 이어, 열 번째 게임을 만들었습니다. &lt;strong&gt;두 번 만들었고, 두 번 다 버렸습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;그래서 이번 편에는 새 게임이 없습니다. 대신 &lt;strong&gt;두 번 물린 이야기&lt;/strong&gt;를 씁니다.&lt;/p&gt;
&lt;p&gt;버린 두 개는 지우지 않고 그대로 올려뒀습니다. 게임 랩 목록에는 없지만 주소로는 들어갈 수 있어요. 읽으시다가 궁금하면 직접 눌러보세요.&lt;/p&gt;
&lt;p&gt;👉 &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/dice-yacht/" &gt;첫 번째 시제품 — 오늘의 주사위&lt;/a&gt;&lt;/strong&gt; · &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/dice-push/" &gt;두 번째 시제품 — 한 번 더&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="열-번째는-그림-없는-게임으로-가려-했습니다"&gt;열 번째는 그림 없는 게임으로 가려 했습니다
&lt;/h2&gt;&lt;p&gt;지금까지 만든 게임들은 &lt;a class="link" href="https://junstellar.github.io/p/claude-local-image-generation/" &gt;ComfyUI&lt;/a&gt;로 그린 이미지를 씁니다. 그런데 이미지는 유한합니다. 아무리 많이 그려도 언젠가 나왔던 게 또 나오고, 그때부터 흥미가 떨어지죠.&lt;/p&gt;
&lt;p&gt;그래서 이번엔 &lt;strong&gt;그림 없이 만들자&lt;/strong&gt;고 정했습니다. 주사위 눈까지 CSS 점으로 찍는 게임. 이 판단 자체는 지금도 맞다고 생각합니다.&lt;/p&gt;
&lt;h2 id="첫-번째--완성했는데-손이-안-갔습니다"&gt;첫 번째 — 완성했는데 손이 안 갔습니다
&lt;/h2&gt;&lt;p&gt;야추(Yacht) 방식으로 만들었습니다. 주사위 다섯 개를 세 번까지 굴려 열두 개 족보를 채우는 게임.&lt;/p&gt;
&lt;p&gt;잘 돌아갔습니다. 족보 채점은 경계값까지 열여덟 가지를 검증했고, 보너스도 라운드 진행도 정확했습니다. 파일 하나 21KB, 그림 한 장 안 썼습니다.&lt;/p&gt;
&lt;p&gt;그리고 열었는데 — &lt;strong&gt;손이 안 갔습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="첫 번째 시제품 — 열두 칸에 숫자를 채우는 화면" class="gallery-image" data-flex-basis="208px" data-flex-grow="86" height="1040" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-10/dice-yacht.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-10/dice-yacht_hu_764d70869ad5e92a.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-10/dice-yacht.webp 904w" width="904"&gt;&lt;/p&gt;
&lt;p&gt;처음엔 설명이 부족한 줄 알았습니다. 점수판에 &lt;code&gt;포카드&lt;/code&gt; &lt;code&gt;풀하우스&lt;/code&gt; &lt;code&gt;S.스트레이트&lt;/code&gt;라고만 적혀 있었으니까요. &lt;strong&gt;뭘 만들어야 그 점수가 나는지가 화면 어디에도 없었습니다.&lt;/strong&gt; 야추를 아는 사람만 읽을 수 있는 화면이었죠.&lt;/p&gt;
&lt;p&gt;그런데 규칙을 다 정리하고 나서도 그대로였습니다. 문제는 설명이 아니었습니다.&lt;/p&gt;
&lt;h2 id="야추엔-긴장이-없습니다"&gt;야추엔 긴장이 없습니다
&lt;/h2&gt;&lt;p&gt;&lt;img alt="긴장 곡선 비교 — 야추는 한 판 내내 평평하고, 푸시-유어-럭은 굴릴 때마다 치솟았다 터진다" class="gallery-image" data-flex-basis="469px" data-flex-grow="195" height="940" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-10/dice-tension.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-10/dice-tension_hu_41997b6820b72825.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-10/dice-tension_hu_d8ebc0ef7fee0d74.webp 1600w, https://junstellar.github.io/p/ai-game-lab-build-10/dice-tension.webp 1840w" width="1840"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;잃을 게 없습니다.&lt;/strong&gt; 매 라운드 무조건 점수를 챙기고 넘어갑니다. 0점을 박아도 그냥 다음 라운드죠. 위험이 없으면 긴장도 없습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;결정이 스릴이 아니라 숙제입니다.&lt;/strong&gt; &amp;ldquo;5의 합 20점이냐 포카드 23점이냐&amp;quot;는 심장이 뛰는 선택이 아니라 계산 문제입니다. 최적해가 있어서, 익숙해지면 기계적으로 두게 됩니다.&lt;/p&gt;
&lt;p&gt;그리고 결정적으로 — &lt;strong&gt;야추는 원래 여럿이 둘러앉아 떠들면서 하는 게임입니다.&lt;/strong&gt; 재미의 절반이 사람인데, 혼자 폰으로 하면 그게 빠지고 &lt;strong&gt;엑셀 채우기&lt;/strong&gt;만 남습니다.&lt;/p&gt;
&lt;h2 id="두-번째--이번엔-숫자로-맞췄습니다"&gt;두 번째 — 이번엔 숫자로 맞췄습니다
&lt;/h2&gt;&lt;p&gt;갈아엎었습니다. 같은 주사위인데 매 순간 결정이 있는 방식으로요.&lt;/p&gt;
&lt;p&gt;굴려서 점수 나는 눈만 챙기고, 남은 걸로 &lt;strong&gt;또 굴릴지 멈출지&lt;/strong&gt; 고릅니다. 굴렸는데 점수 나는 눈이 하나도 없으면 &lt;strong&gt;그 라운드는 전부 소멸.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="두 번째 시제품 — 한 번 더 굴릴지 멈출지 고르는 화면" class="gallery-image" data-flex-basis="230px" data-flex-grow="95" height="880" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-10/dice-push.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-10/dice-push_hu_ba7b3e9764c84bed.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-10/dice-push.webp 844w" width="844"&gt;&lt;/p&gt;
&lt;p&gt;이번엔 감으로 안 맞추고 &lt;strong&gt;자동 플레이어를 3천 판씩 돌렸습니다.&lt;/strong&gt; 겁쟁이(한 번 챙기고 바로 멈춤), 신중(터질 확률 30%에서 멈춤), 욕심(45%까지), 무모(터질 때까지).&lt;/p&gt;
&lt;p&gt;주사위 다섯 개일 땐 &lt;strong&gt;라운드당 굴림이 평균 1.8회&lt;/strong&gt;였습니다. 두 번 굴리면 라운드가 끝나니 긴장이 쌓일 틈이 없었죠. &lt;strong&gt;여섯 개로 늘리자 최적점이 생겼습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;전략&lt;/th&gt;
					&lt;th&gt;평균 점수&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;겁쟁이&lt;/td&gt;
					&lt;td&gt;1058&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;신중&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;1343&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;욕심&lt;/td&gt;
					&lt;td&gt;1082&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;무모&lt;/td&gt;
					&lt;td&gt;0&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;덜 굴려도 손해, 더 굴려도 손해. 원하던 모양이었습니다.&lt;/p&gt;
&lt;h2 id="짐작이-또-틀렸습니다"&gt;짐작이 또 틀렸습니다
&lt;/h2&gt;&lt;p&gt;라운드가 아직 짧게 느껴져서, 파클(Farkle)의 고전 규칙을 떠올렸습니다. &lt;strong&gt;&amp;ldquo;최소 300점을 넘겨야 멈출 수 있다.&amp;rdquo;&lt;/strong&gt; 강제로 더 굴리게 만드니 긴장이 늘겠거니 했죠.&lt;/p&gt;
&lt;p&gt;재보니 정반대였습니다.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;겁쟁이&lt;/th&gt;
					&lt;th&gt;신중&lt;/th&gt;
					&lt;th&gt;격차&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;최소 없음&lt;/td&gt;
					&lt;td&gt;1058&lt;/td&gt;
					&lt;td&gt;1343&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;+27%&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;최소 300점&lt;/td&gt;
					&lt;td&gt;1282&lt;/td&gt;
					&lt;td&gt;1321&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;+3%&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;최소 점수는 긴장을 더한 게 아니라 &lt;strong&gt;결정을 없앴습니다.&lt;/strong&gt; 모두를 같은 플레이로 몰아버려서, 잘 두든 못 두든 점수가 비슷해졌습니다. 그대로 넣었으면 게임을 망칠 뻔했습니다.&lt;/p&gt;
&lt;h2 id="다-맞췄는데-재미가-없었습니다"&gt;다 맞췄는데 재미가 없었습니다
&lt;/h2&gt;&lt;p&gt;확률까지 버튼에 그대로 띄웠습니다. &amp;ldquo;한 번 더 — 2개 · 터질 확률 44%&amp;rdquo; 옆에 &amp;ldquo;멈추기 — 550점 확정&amp;rdquo;. 확률을 보여주니 결정이 진짜 결정이 되더군요.&lt;/p&gt;
&lt;p&gt;그리고 다시 열었는데 — &lt;strong&gt;여전히 재미가 없었습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;여기서 접었습니다. 규칙을 통째로 바꿔서 두 번 만들었는데 두 번 다 안 걸렸으면, 문제는 규칙이 아니라 &lt;strong&gt;장르&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;h2 id="답은-이미-제-게임-목록에-적혀-있었습니다"&gt;답은 이미 제 게임 목록에 적혀 있었습니다
&lt;/h2&gt;&lt;p&gt;버리기로 하고 나서야 지금까지 만든 걸 다시 봤습니다.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;게임&lt;/th&gt;
					&lt;th&gt;재미의 정체&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;🗑️ &lt;a class="link" href="https://junstellar.github.io/games/paper-hoop/" &gt;종이 농구&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;당겨서 놓고 → &lt;strong&gt;날아가는 걸 본다&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🎣 &lt;a class="link" href="https://junstellar.github.io/games/deep-fishing/" &gt;심해 낚시&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;챔질하는 &lt;strong&gt;찰나의 손맛&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🪐 &lt;a class="link" href="https://junstellar.github.io/games/space-merge/" &gt;우주 진화&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;떨어뜨리면 &lt;strong&gt;터지듯 합쳐진다&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🍰 &lt;a class="link" href="https://junstellar.github.io/games/stack-tower/" &gt;디저트 탑&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;타이밍&lt;/strong&gt; 맞춰 딱&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;제가 제일 아끼는 것들은 죄다 &lt;strong&gt;손과 눈으로 하는 게임&lt;/strong&gt;이었습니다. 결과가 0.5초 안에 몸으로 옵니다.&lt;/p&gt;
&lt;p&gt;주사위는 야추든 파클이든 &lt;strong&gt;머리로 숫자를 판단하는 게임&lt;/strong&gt;입니다. 화면에 움직이는 게 없어요. 제가 뭘 만들면 재밌어하는지는 이미 만들어놓은 것들에 다 적혀 있었는데, 정작 저만 그걸 안 보고 있었습니다.&lt;/p&gt;
&lt;h2 id="그래픽-줄이자가-화면이-안-움직인다가-됐습니다"&gt;&amp;ldquo;그래픽 줄이자&amp;quot;가 &amp;ldquo;화면이 안 움직인다&amp;quot;가 됐습니다
&lt;/h2&gt;&lt;p&gt;이번에 제가 세운 조건은 **&amp;ldquo;이미지 에셋을 줄이자&amp;rdquo;**였습니다. 그런데 만들다 보니 어느새 **&amp;ldquo;화면이 움직이지 않아도 된다&amp;rdquo;**로 바뀌어 있었습니다.&lt;/p&gt;
&lt;p&gt;이 둘은 전혀 다릅니다. &lt;strong&gt;그림 파일이 없는 것과 화면이 정지해 있는 건 다른 문제입니다.&lt;/strong&gt; 도형과 선만으로도 얼마든지 움직일 수 있는데, 저는 한 번에 둘 다 없애버렸습니다.&lt;/p&gt;
&lt;p&gt;바이브 코딩을 하면서 계속 겪는 일인데, &lt;strong&gt;조건을 애매하게 잡으면 결과는 가장 극단으로 갑니다.&lt;/strong&gt; &amp;ldquo;줄이자&amp;quot;라고 하면 &amp;ldquo;없애자&amp;quot;가 되어 돌아옵니다. 이번엔 그걸 눈으로 확인할 때까지 몰랐습니다.&lt;/p&gt;
&lt;h2 id="이번에-배운-것"&gt;이번에 배운 것
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;재미는 측정이 안 됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;지난 편들에서 저는 측정으로 많은 걸 잡았습니다. &lt;a class="link" href="https://junstellar.github.io/p/ai-game-lab-build-6/" &gt;병합할수록 공간이 남는&lt;/a&gt; 구조적 결함도, &lt;a class="link" href="https://junstellar.github.io/p/ai-game-lab-build-8/" &gt;배경이 아니라 대비가 문제였던 것&lt;/a&gt;도, &lt;a class="link" href="https://junstellar.github.io/p/ai-game-lab-build-9/" &gt;화면이 시키는 대로 하면 지는&lt;/a&gt; 판도 숫자가 알려줬습니다. 이번에도 측정은 제대로 작동했습니다. 밸런스는 잡혔고, 확률은 정확했고, 틀린 짐작도 잡아냈습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;그런데 그게 재미를 만들어주진 않았습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;숫자로 답할 수 있는 건 &lt;em&gt;어떻게 만들 것인가&lt;/em&gt;까지였습니다. &lt;em&gt;무엇을 만들 것인가&lt;/em&gt;는 재보기 전에 정해야 하는 문제였고, 저는 거기서 두 번 틀렸습니다.&lt;/p&gt;
&lt;p&gt;그래서 다음부터는 순서를 바꾸기로 했습니다. &lt;strong&gt;&amp;ldquo;이 게임의 재미는 정확히 이 순간이다&amp;quot;를 한 문장으로 먼저 적고 시작합니다.&lt;/strong&gt; 그 문장이 안 써지면 만들지 않습니다. 완성한 뒤에 재미를 묻는 건 늦습니다. 두 번 버리고서 알았습니다.&lt;/p&gt;
&lt;h2 id="목록을-다시-짰습니다"&gt;목록을 다시 짰습니다
&lt;/h2&gt;&lt;p&gt;다음에 만들 후보에서 &lt;strong&gt;머리로 계산하는 게임을 통째로 걷어냈습니다.&lt;/strong&gt; 주사위도, 카드 배치도, 암산도 뺐습니다. 대신 &lt;strong&gt;그림 없이도 화면이 움직이는 게임&lt;/strong&gt;으로 다시 채웠습니다.&lt;/p&gt;
&lt;p&gt;버린 두 개가 아깝진 않습니다. 두 번 만들어보고서야 &lt;strong&gt;제가 뭘 만들면 안 되는지&lt;/strong&gt; 알았으니까요. 아홉 개를 만드는 동안 몰랐던 걸, 열 번째를 버리면서 알았습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;버린 시제품 둘은 그대로 열어뒀습니다. 어디가 재미없는지 직접 확인해 보셔도 좋고요. 저는 두 번째가 그래도 좀 낫다고 생각하는데, 어느 쪽도 다시 켜고 싶진 않더군요.&lt;/p&gt;
&lt;p&gt;👉 &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/dice-yacht/" &gt;오늘의 주사위 (첫 번째)&lt;/a&gt;&lt;/strong&gt; · &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/dice-push/" &gt;한 번 더 (두 번째)&lt;/a&gt;&lt;/strong&gt; · &lt;a class="link" href="https://junstellar.github.io/games/" &gt;AI 게임 랩 전체 보기&lt;/a&gt;&lt;/p&gt;</description></item><item><title>당신의 반응속도는 몇 ms? — 150년 된 심리학 과제를 웹에 옮겼습니다</title><link>https://junstellar.github.io/p/reaction-test/</link><pubDate>Mon, 24 Aug 2026 20:10:00 +0900</pubDate><guid>https://junstellar.github.io/p/reaction-test/</guid><description>&lt;p&gt;&lt;a class="link" href="https://junstellar.github.io/lab/?lang=ko" &gt;생각별 연구소&lt;/a&gt;의 세 번째 테스트에는 문항이 하나도 없습니다. 읽을 문제도, 고를 보기도 없어요. 요구하는 건 단 하나 — &lt;strong&gt;초록불이 켜지는 순간, 최대한 빨리 클릭하는 것.&lt;/strong&gt; 반응속도 테스트입니다.&lt;/p&gt;
&lt;p&gt;👉 결과물 먼저: &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/tools/reaction/?lang=ko" &gt;반응속도 테스트&lt;/a&gt;&lt;/strong&gt; — 1분이면 끝납니다.&lt;/p&gt;
&lt;h2 id="사실은-심리학에서-가장-오래된-측정입니다"&gt;사실은 심리학에서 가장 오래된 측정입니다
&lt;/h2&gt;&lt;p&gt;규칙은 유치할 정도로 단순합니다. 어두운 화면을 보고 기다리다가, &lt;strong&gt;초록색 ●로 바뀌는 순간 최대한 빨리 클릭.&lt;/strong&gt; 다섯 번 반복해서 평균을 냅니다.&lt;/p&gt;
&lt;p&gt;그런데 이 유치한 과제의 족보가 화려합니다. &amp;lsquo;자극이 나타나면 최대한 빨리 반응하기&amp;rsquo;는 **단순 반응시간(Simple Reaction Time)**이라는 이름으로 실험심리학에서 150년 넘게 쓰여온, 사실상 이 학문의 출발점에 있는 측정 과제입니다. 사람의 마음을 숫자로 재겠다는 시도가 여기서 시작됐죠. 연구에서 젊은 성인의 시각 단순 반응시간은 보통 &lt;strong&gt;200~300ms&lt;/strong&gt; 범위로 보고됩니다. 이 참고치가 결과 화면의 기준이 됩니다.&lt;/p&gt;
&lt;h2 id="문항이-없는-대신-밀리초와-싸웠습니다"&gt;문항이 없는 대신, 밀리초와 싸웠습니다
&lt;/h2&gt;&lt;p&gt;만드는 건 반나절이었지만, 이번 테스트의 재미는 디테일에 있었습니다. 측정 단위가 밀리초라서, 평소에는 신경도 안 쓰던 것들이 전부 오차가 되기 때문입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. click 대신 pointerdown&lt;/strong&gt; — 웹에서 흔히 쓰는 click 이벤트는 버튼을 &amp;lsquo;눌렀다 뗄 때&amp;rsquo; 발생합니다. 손가락을 떼는 시간까지 기록에 포함되는 거죠. 그래서 &lt;strong&gt;누르는 순간 발생하는 pointerdown&lt;/strong&gt;으로 측정해 수십 ms의 억울한 손해를 없앴습니다. PC에서는 스페이스바로도 반응할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 대기 시간은 1.5~4초 무작위&lt;/strong&gt; — 대기 시간이 일정하면 사람은 반응하는 게 아니라 리듬을 타고 예측합니다. 매번 다른 길이로 기다리게 하고, 초록불 전에 누르면 &lt;strong&gt;파울&lt;/strong&gt; 처리해 그 라운드는 기록에서 뺐습니다. 실제 반응시간 실험들이 쓰는 방식 그대로입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 색만 바뀌면 안 됩니다&lt;/strong&gt; — 빨강→초록 색 변화에만 의존하면 색각 이상이 있는 분들에게 불공평합니다. 그래서 색이 바뀔 때 &lt;strong&gt;모양도 ■에서 ●로 함께&lt;/strong&gt; 바뀝니다. 작은 배려지만 이런 게 쌓여야 도구답다고 생각합니다.&lt;/p&gt;
&lt;h2 id="그리고-정직-코너"&gt;그리고, 정직 코너
&lt;/h2&gt;&lt;p&gt;이 시리즈의 전통이 된 부분입니다. 결과 화면에 이렇게 적어뒀습니다 — &lt;strong&gt;온라인 측정에는 모니터 주사율과 입력 장치의 지연이 20~50ms가량 더해질 수 있다&lt;/strong&gt;고요. 60Hz 모니터는 화면을 바꾸는 데만 최대 17ms가 걸립니다. 그래서 이 기록은 절대값을 겨루는 공인 기록이 아니라, &lt;strong&gt;같은 기기에서 반복 측정하며 변화를 보는 참고 기록&lt;/strong&gt;입니다. 게임 전 워밍업 체크나 가족 대결용으로는 충분하고요.&lt;/p&gt;
&lt;h2 id="연구소-근황"&gt;연구소 근황
&lt;/h2&gt;&lt;p&gt;이것으로 &lt;a class="link" href="https://junstellar.github.io/p/kids-iq-test/" &gt;어린이 IQ 테스트&lt;/a&gt;, &lt;a class="link" href="https://junstellar.github.io/p/big5-test/" &gt;Big Five 성격 테스트&lt;/a&gt;에 이어 세 번째 테스트가 연구소에 입주했습니다. 남은 대기자는 두 팀 — 🔢 숫자 기억력(작업기억), 🎨 스트룹 테스트입니다. 이번에도 개발은 Claude Code와 함께했고, 시리즈 공용 프레임 덕분에 커피 두 잔 마시는 사이에 4개 언어 버전까지 나왔습니다.&lt;/p&gt;
&lt;h2 id="해보기"&gt;해보기
&lt;/h2&gt;&lt;p&gt;👉 &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/tools/reaction/?lang=ko" &gt;반응속도 테스트 하러 가기&lt;/a&gt;&lt;/strong&gt; — 5라운드, 1분이면 됩니다.&lt;/p&gt;
&lt;p&gt;참, 제 기록은 &lt;strong&gt;평균 257ms(빠름)&lt;/strong&gt;, 최고 190ms였습니다. 재미있는 건 라운드별 기록인데 — 308, 260, 256, 271, 190. 첫 라운드보다 마지막 라운드가 100ms 넘게 빨라졌습니다. 본문에 쓴 &amp;ldquo;절대값보다 반복 측정의 변화를 보라&amp;quot;는 말이 제 기록에서 바로 증명된 셈이죠.&lt;/p&gt;
&lt;p&gt;가족이나 친구와 같은 기기로 번갈아 재보세요. 같은 화면, 같은 지연 조건이니 이건 꽤 공정한 대결입니다. 누가 이겼는지는… 각자 확인해보시길.&lt;/p&gt;</description></item><item><title>MBTI 말고 Big Five — 심리학이 진짜 쓰는 성격 테스트를 만들었습니다</title><link>https://junstellar.github.io/p/big5-test/</link><pubDate>Sat, 22 Aug 2026 10:50:00 +0900</pubDate><guid>https://junstellar.github.io/p/big5-test/</guid><description>&lt;p&gt;&lt;a class="link" href="https://junstellar.github.io/p/kids-iq-test/" &gt;지난번에 중1 딸을 위해 IQ 테스트를 만들고&lt;/a&gt; 나서 욕심이 생겼습니다. 테스트 하나로 끝낼 게 아니라, &lt;strong&gt;&amp;ldquo;연구 근거가 있는 테스트&amp;quot;만 모아 시리즈로 만들어 보자.&lt;/strong&gt; 그 두 번째 작품이 이번 글의 주인공, Big Five 성격 테스트입니다.&lt;/p&gt;
&lt;p&gt;👉 결과물 먼저: &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/tools/big5/?lang=ko" &gt;Big Five 성격 테스트&lt;/a&gt;&lt;/strong&gt; · 시리즈 허브: &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/lab/?lang=ko" &gt;생각별 연구소&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="왜-하필-성격-테스트인가"&gt;왜 하필 성격 테스트인가
&lt;/h2&gt;&lt;p&gt;주변에 성격 유형 테스트 안 해본 사람이 없을 정도로 유형 테스트는 이미 놀이 문화가 됐죠. 저도 재미있게 봅니다. 다만 한 가지 아쉬운 점이 있었어요. &lt;strong&gt;정작 심리학 연구 논문에서 성격을 측정할 때 쓰는 모델은 따로 있다&lt;/strong&gt;는 겁니다.&lt;/p&gt;
&lt;p&gt;바로 **Big Five(성격 5요인)**입니다. 사람의 성격을 유형으로 나누는 대신, 다섯 개의 축 위 어디쯤에 있는지를 봅니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;외향성&lt;/strong&gt; — 사교성, 활력, 자기표현&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;우호성&lt;/strong&gt; — 공감, 배려, 협력&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;성실성&lt;/strong&gt; — 계획, 질서, 꾸준함&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;정서 안정성&lt;/strong&gt; — 평정심, 스트레스 대처&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;개방성&lt;/strong&gt; — 상상력, 지적 호기심&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;수십 년간 축적된 연구가 뒷받침하는, 성격심리학의 사실상 표준 모델입니다. &amp;ldquo;이걸 직접 만들면 시리즈 2호로 딱이겠다&amp;rdquo; 싶었습니다.&lt;/p&gt;
&lt;h2 id="이번엔-라이선스-반전이-반대로-왔습니다"&gt;이번엔 라이선스 반전이 반대로 왔습니다
&lt;/h2&gt;&lt;p&gt;IQ 테스트 때를 기억하시는 분이 있을지 모르겠습니다. 그때는 &amp;ldquo;공개 자료&amp;quot;인 줄 알았던 연구 문항(Ch-ICAR)이 알고 보니 &lt;strong&gt;연구 전용 라이선스&lt;/strong&gt;라서, 문항 76개를 전부 자작해야 했죠.&lt;/p&gt;
&lt;p&gt;이번엔 정반대였습니다. **IPIP(International Personality Item Pool)**라는 국제 성격 문항 저장소가 있는데, 공식 사이트에 이렇게 명시되어 있습니다 — 문항과 척도는 퍼블릭 도메인이며, &lt;strong&gt;허락도 비용도 없이 어떤 목적으로든&lt;/strong&gt; 복사·수정·번역해 쓸 수 있다고요. 상업적 이용까지 포함입니다.&lt;/p&gt;
&lt;p&gt;심리학계가 &amp;ldquo;성격 측정 도구가 너무 비싸고 폐쇄적이니, 누구나 쓸 수 있는 공개 문항을 만들자&amp;quot;며 수십 년간 쌓아온 자산입니다. 덕분에 이번에는 문항을 자작할 필요 없이, &lt;strong&gt;연구에서 실제로 쓰는 문항 원문을 그대로&lt;/strong&gt; 쓸 수 있었습니다. 저는 Goldberg의 Big Five 마커 50문항판(요인당 10문항)을 골라 한국어·영어·일본어·중국어 4개 언어로 준비했습니다.&lt;/p&gt;
&lt;h2 id="만들면서-신경-쓴-것들"&gt;만들면서 신경 쓴 것들
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;1. 50문항을 5분 안에&lt;/strong&gt; — 문항 수가 많으면 이탈합니다. 그래서 답을 고르면 자동으로 다음 문항으로 넘어가는 방식으로 만들었습니다. 버튼 한 번이 문항 하나라서, 50문항이 생각보다 금방 끝납니다. PC에서는 키보드 1~5로도 입력됩니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 역채점 문항&lt;/strong&gt; — &amp;ldquo;말수가 적은 편이다&amp;quot;에 &amp;lsquo;매우 그렇다&amp;rsquo;라고 답하면 외향성 점수는 깎여야겠죠. IPIP 문항의 절반 가까이가 이런 역방향 문항이라, 채점 로직에서 뒤집어 계산합니다. 아무거나 찍으면 서로 상쇄돼서 극단적인 결과가 안 나오는, 검사 설계의 오래된 지혜입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. &amp;lsquo;신경성&amp;rsquo;을 &amp;lsquo;정서 안정성&amp;rsquo;으로&lt;/strong&gt; — Big Five의 넷째 요인은 원래 신경성(Neuroticism)이라고 부르는데, 결과지에 &amp;ldquo;당신은 신경성이 높습니다&amp;quot;라고 쓰면 기분이 좋지 않죠. 방향을 뒤집어 정서 안정성으로 표기했습니다. 그리고 &lt;strong&gt;다섯 축 모두, 낮은 쪽도 강점으로 서술했습니다.&lt;/strong&gt; 우호성이 낮으면 &amp;ldquo;협상과 경쟁에 강하다&amp;rdquo;, 정서 안정성이 낮으면 &amp;ldquo;공감과 위기 감지의 재능&amp;quot;처럼요. 실제로 Big Five에는 좋은 점수·나쁜 점수가 없습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. 점수에 대한 정직함&lt;/strong&gt; — 이 테스트의 0&lt;del&gt;100 점수는 모집단과 비교한 백분위가 아니라, 이론적 범위(요인당 10&lt;/del&gt;50점) 안에서의 위치입니다. 규준 데이터 없이 백분위인 척하는 건 IQ 테스트 때부터 지켜온 원칙에 어긋나서, 결과 화면에 계산 방식을 그대로 적어뒀습니다.&lt;/p&gt;
&lt;h2 id="그리고-연구소를-차렸습니다"&gt;그리고 연구소를 차렸습니다
&lt;/h2&gt;&lt;p&gt;테스트가 두 개가 되니 블로그 도구 목록에 하나씩 쌓는 게 부담스러워졌습니다. 그래서 &lt;a class="link" href="https://junstellar.github.io/games/" &gt;AI 게임 랩&lt;/a&gt;처럼 **&lt;a class="link" href="https://junstellar.github.io/lab/?lang=ko" &gt;생각별 연구소&lt;/a&gt;**라는 허브를 만들어 시리즈를 모았습니다. 지금은 어린이 IQ 테스트와 Big Five 두 개가 입주해 있고, 다음 입주 예정자들이 대기 중입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;⚡ &lt;strong&gt;반응속도 테스트&lt;/strong&gt; — 화면이 바뀌는 순간을 잡아라&lt;/li&gt;
&lt;li&gt;🔢 &lt;strong&gt;숫자 기억력 테스트&lt;/strong&gt; — 점점 길어지는 숫자열 기억하기 (작업기억)&lt;/li&gt;
&lt;li&gt;🎨 &lt;strong&gt;스트룹 테스트&lt;/strong&gt; — 글자의 뜻과 색이 싸울 때, 주의력 버티기&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이번에도 개발은 Claude Code와 함께했습니다. IQ 테스트 때 만든 다국어 프레임과 화면 구조를 재활용해서, 문항 수집·번역을 빼면 하루가 채 안 걸렸어요. 시리즈의 좋은 점이 이겁니다 — &lt;strong&gt;하나 만들 때마다 다음 것이 쉬워집니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="해보기"&gt;해보기
&lt;/h2&gt;&lt;p&gt;👉 &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/tools/big5/?lang=ko" &gt;Big Five 성격 테스트 하러 가기&lt;/a&gt;&lt;/strong&gt; — 50문항, 5분이면 됩니다.&lt;/p&gt;
&lt;p&gt;참, 저는 개방성이 높고 성실성이 중간으로 나왔습니다. 계획을 세우는 건 좋아하는데 정리정돈 문항에서 다 깎였습니다. 물건을 제자리에 두는 것부터 연습해야겠습니다.&lt;/p&gt;</description></item><item><title>아빠표 IQ 테스트 — 중1 딸을 위한 IQ 테스트를 직접 만들었습니다</title><link>https://junstellar.github.io/p/kids-iq-test/</link><pubDate>Wed, 19 Aug 2026 21:00:00 +0900</pubDate><guid>https://junstellar.github.io/p/kids-iq-test/</guid><description>&lt;p&gt;어느 날 중1 딸이 진지한 얼굴로 말했습니다. &lt;strong&gt;&amp;ldquo;아빠, 나 천재인 것 같아. 아이큐 검사 해보고 싶어.&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;보통의 아빠라면 &amp;ldquo;그래 그래&amp;rdquo; 하고 넘어가거나 인터넷에서 무료 테스트를 검색해줬겠죠. 그런데 저는 그만 이렇게 대답해버렸습니다. &lt;strong&gt;&amp;ldquo;검사? 아빠가 만들어줄게.&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;내뱉고 나서 조사를 시작했습니다. 이 글은 그 큰소리의 뒷수습 기록입니다.&lt;/p&gt;
&lt;p&gt;👉 결과물 먼저: &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/tools/iq-test/?lang=ko" &gt;생각별 IQ 연구소 — 어린이 IQ 테스트&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="진짜-iq-검사는-만만한-게-아니었습니다"&gt;진짜 IQ 검사는 만만한 게 아니었습니다
&lt;/h2&gt;&lt;p&gt;찾아보니 제대로 된 지능검사(웩슬러 아동용 같은)는 &lt;strong&gt;전문가가 일대일로 실시하는 유료 검사&lt;/strong&gt;였습니다. 개인이 웹에서 흉내 낼 수 있는 영역이 아니에요. 반대로 인터넷에 널린 무료 IQ 테스트들은 문항 출처도, 채점 근거도 불분명한 게 대부분이었고요.&lt;/p&gt;
&lt;p&gt;그러다 **ICAR(International Cognitive Ability Resource)**라는 걸 알게 됐습니다. 연구자들이 공동으로 만들어 쓰는 공개형 인지능력 검사 문항 저장소인데, 여기에 **어린이용 버전(Ch-ICAR)**이 있었습니다. 2025년에 벨기에 연구진이 만 10~14세 학생 &lt;strong&gt;820명&lt;/strong&gt;으로 검증한 27문항짜리 검사예요. 영역 구성도 명확했습니다.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;영역&lt;/th&gt;
					&lt;th&gt;문항 수&lt;/th&gt;
					&lt;th&gt;실제 정답률&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;언어추론&lt;/td&gt;
					&lt;td&gt;8&lt;/td&gt;
					&lt;td&gt;44%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;행렬추론&lt;/td&gt;
					&lt;td&gt;6&lt;/td&gt;
					&lt;td&gt;31%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;수열추론&lt;/td&gt;
					&lt;td&gt;7&lt;/td&gt;
					&lt;td&gt;46%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;도형유추&lt;/td&gt;
					&lt;td&gt;6&lt;/td&gt;
					&lt;td&gt;37%&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;전체 평균이 &lt;strong&gt;27점 만점에 10.76점&lt;/strong&gt;. 또래 아이들이 절반도 못 맞추는, 꽤 어려운 검사라는 뜻입니다. 이거다 싶었죠.&lt;/p&gt;
&lt;h2 id="공개와-마음대로-써도-됨은-다르더군요"&gt;&amp;lsquo;공개&amp;rsquo;와 &amp;lsquo;마음대로 써도 됨&amp;rsquo;은 다르더군요
&lt;/h2&gt;&lt;p&gt;그런데 반전이 있었습니다. ICAR는 스스로 공개 자료라고 소개하지만, 실제 문항을 받으려면 &lt;strong&gt;연구자 등록과 신청&lt;/strong&gt;이 필요하고, 라이선스도 **과학적 이용 전용(Scientific Use License)**이었습니다. 이유가 인상적이었어요 — 문항과 정답이 인터넷에 공개되면 검사 자체가 망가지기 때문입니다. 정답을 검색할 수 있는 IQ 테스트는 더 이상 검사가 아니니까요.&lt;/p&gt;
&lt;p&gt;그래서 방향을 정했습니다. &lt;strong&gt;원문항은 쓰지 않는다. 대신 연구가 검증한 &amp;lsquo;구조&amp;rsquo;를 그대로 따른다.&lt;/strong&gt; 영역 구성(8·6·7·6), 27문항, 난이도 오름차순 배치, 채점 방식까지요. 문항은 전부 직접 만들었습니다.&lt;/p&gt;
&lt;h2 id="1차-완성--그리고-딸의-심판"&gt;1차 완성 — 그리고 딸의 심판
&lt;/h2&gt;&lt;p&gt;시제품은 AI 코딩으로 하루 만에 나왔습니다. 처음엔 OpenAI Codex로 뼈대를 만들었고, 이후 개편은 Claude Code와 진행했어요. 보호자 확인, 27문항, 영역별 결과, 벨기에 표본 통계로 환산한 참고점수까지. 블로그 &lt;a class="link" href="https://junstellar.github.io/%eb%8f%84%ea%b5%ac/" &gt;도구&lt;/a&gt; 메뉴에 올리고 딸에게 당당하게 내밀었습니다.&lt;/p&gt;
&lt;p&gt;결과는— &lt;strong&gt;전부 다 맞혔습니다.&lt;/strong&gt; 딸은 &amp;ldquo;거봐, 나 천재 맞잖아&amp;quot;라며 의기양양했고요.&lt;/p&gt;
&lt;p&gt;기뻐해야 할 순간인데 뭔가 이상했습니다. 실제 검사는 또래 평균 정답률이 40%인데 만점이 나왔다? 문제를 다시 보니 답이 나왔습니다. &lt;strong&gt;제가 만든 문항이 너무 쉬웠던 겁니다.&lt;/strong&gt; &amp;ldquo;2, 4, 6, 8 다음은?&amp;rdquo; 수준이었으니 중1이면 당연히 만점이죠. 천재 판정이 아니라 출제 오류였습니다.&lt;/p&gt;
&lt;h2 id="2차-개편--진짜-검사처럼-어렵게"&gt;2차 개편 — 진짜 검사처럼 어렵게
&lt;/h2&gt;&lt;p&gt;그래서 논문을 다시 파고들어 실제 Ch-ICAR의 형식을 최대한 따라 만들었습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;연령별 2세트&lt;/strong&gt; — 만 10&lt;del&gt;11세 기본 세트 / 만 12&lt;/del&gt;14세 심화 세트. 심화 세트에는 대우 명제, 삼단논법, &amp;ldquo;모레가 목요일의 이틀 전이라면 오늘은?&amp;rdquo; 같은 요일 계산 퍼즐, 피보나치·소수·교대수열이 들어갑니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;행렬·도형 문제를 SVG로&lt;/strong&gt; — 텍스트 기호(●▲) 대신 코드로 도형을 그려서, 실제처럼 3×3 행렬추론과 도형 유추 문제를 만들었습니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;8지선다&lt;/strong&gt; — 실제 Ch-ICAR가 보기 8개(정답 없음·모르겠음 포함)를 쓰길래 똑같이 보기 7개 + &amp;lsquo;모르겠어요&amp;rsquo;로 맞췄습니다. 찍어서 맞출 확률이 25%에서 12.5%로 떨어집니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;수열은 주관식&lt;/strong&gt; — 실제 검사처럼 보기 없이 직접 입력합니다. 모바일 키보드가 화면을 가리는 문제는 &lt;strong&gt;화면 안에 숫자 키패드를 직접 그려서&lt;/strong&gt; 해결했어요. PC에서는 키보드로도 입력됩니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;문제은행 76문항, 매회 무작위 출제&lt;/strong&gt; — 문항을 76개(기본 36 + 심화 40)로 늘리고, 매번 영역 비율을 지키며 27문항을 무작위로 뽑습니다. 보기 순서도 섞여서 두 번째 풀 때 답을 외워봐야 소용없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;전부 서버 없이 브라우저 안에서만 동작합니다. 답은 어디로도 전송되지 않아요.&lt;/p&gt;
&lt;h2 id="점수에-대해서는-정직하게"&gt;점수에 대해서는 정직하게
&lt;/h2&gt;&lt;p&gt;만들면서 가장 신경 쓴 부분은 사실 문항보다 &lt;strong&gt;점수의 정직함&lt;/strong&gt;입니다. 이 테스트의 점수는 벨기에 연구표본의 평균과 표준편차를 빌려 평균 100, 표준편차 15 형태로 환산한 &lt;strong&gt;오락·교육용 참고점수&lt;/strong&gt;입니다. 한국 어린이 규준으로 표준화한 IQ가 아니고, 전문 지능검사를 대신할 수도 없습니다. 그래서 결과 화면에 이 한계를 그대로 적어뒀고, 시작 전에 보호자 확인도 받게 했습니다.&lt;/p&gt;
&lt;p&gt;아이가 받아 든 점수 하나에 일희일비하기보다, &lt;strong&gt;어떤 영역 문제를 재미있어하고 잘 푸는지&lt;/strong&gt; 함께 들여다보는 용도로 쓰시면 딱 좋습니다.&lt;/p&gt;
&lt;h2 id="써보기"&gt;써보기
&lt;/h2&gt;&lt;p&gt;👉 &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/tools/iq-test/?lang=ko" &gt;생각별 IQ 연구소 열기&lt;/a&gt;&lt;/strong&gt; — 상단 메뉴 &lt;strong&gt;도구&lt;/strong&gt;에서도 들어올 수 있습니다.&lt;/p&gt;
&lt;p&gt;이제 딸에게 심화 세트를 다시 내밀 차례입니다. 이번에도 만점이 나온다면… 그때는 진짜로 천재를 의심해봐야겠죠.&lt;/p&gt;</description></item><item><title>AI로 게임 만들기 #9 — 〈오디세이〉 보기 전 30분, 보고 나서 30분. 버튼 하나로 겪는 오디세우스 (공개빌드 #9)</title><link>https://junstellar.github.io/p/ai-game-lab-build-9/</link><pubDate>Wed, 19 Aug 2026 20:00:00 +0900</pubDate><guid>https://junstellar.github.io/p/ai-game-lab-build-9/</guid><description>&lt;img src="https://junstellar.github.io/p/ai-game-lab-build-9/hero.webp" alt="Featured image of post AI로 게임 만들기 #9 — 〈오디세이〉 보기 전 30분, 보고 나서 30분. 버튼 하나로 겪는 오디세우스 (공개빌드 #9)" /&gt;&lt;p&gt;&lt;a class="link" href="https://junstellar.github.io/p/ai-game-lab-build-8/" &gt;지난 편&lt;/a&gt;의 심해 낚시에 이어, 아홉 번째 게임은 &lt;strong&gt;오디세이아&lt;/strong&gt;입니다. 트로이에서 이타카까지 20년, 그 여정의 여섯 장면을 &lt;strong&gt;버튼 하나로&lt;/strong&gt; 직접 겪는 게임이에요. ⛵&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;영화 보러 가기 전에 하면 예습이고, 보고 나서 하면 복습입니다.&lt;/strong&gt; 저는 보고 나서 만들었으니 후자로 시작한 셈이네요.&lt;/p&gt;
&lt;p&gt;👉 &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/odyssey/" &gt;오디세이아 직접 하러 가기&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="3시간이-하나도-안-지루했습니다"&gt;3시간이 하나도 안 지루했습니다
&lt;/h2&gt;&lt;p&gt;놀란의 〈오디세이〉를 봤습니다. &lt;strong&gt;너무 재밌었습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;일단 &lt;strong&gt;배역이 너무 잘 맞았어요.&lt;/strong&gt; 매트 데이먼의 오디세우스도 그렇고, 페넬로페로 나온 &lt;strong&gt;앤 해서웨이&lt;/strong&gt;가 정말 예쁘게 나왔습니다. 20년을 기다리는 사람의 얼굴이 그래야 할 것 같은 얼굴이었어요.&lt;/p&gt;
&lt;p&gt;한 가지 걸린 건, 하필 그 전 주에 스파이더맨을 봤다는 겁니다. 텔레마코스로 &lt;strong&gt;톰 홀랜드&lt;/strong&gt;가 나오고 아테나로 &lt;strong&gt;젠데이아&lt;/strong&gt;가 나오는데, 머리로는 아는데 자꾸 피터랑 MJ가 겹쳐서 그 대목만 몰입이 좀 깨졌습니다. 😅 (젠데이아의 아테나는 실제로 신인지 오디세우스의 죄책감이 만든 환영인지 열어두고 찍었다더군요. 그건 좋았습니다.)&lt;/p&gt;
&lt;p&gt;그래도 &lt;strong&gt;오디세우스의 서사가 장황할 만큼 멋지게 펼쳐져서&lt;/strong&gt; 3시간이 전혀 지루하지 않았습니다. 극장을 나오는데 오히려 여운이 남았어요.&lt;/p&gt;
&lt;h2 id="그런데-같이-본-사람은-온도가-달랐습니다"&gt;그런데 같이 본 사람은 온도가 달랐습니다
&lt;/h2&gt;&lt;p&gt;나오면서 이야기하다가 알았습니다. &lt;strong&gt;저는 원작 줄거리를 알고 갔고, 같이 본 사람은 몰랐습니다.&lt;/strong&gt; 같은 장면을 보고도 남는 게 달랐어요.&lt;/p&gt;
&lt;p&gt;세이렌 앞에서 왜 자기만 묶여 있는지, 눈앞의 소를 왜 안 잡아먹는지, 마지막에 활을 당기는 게 왜 그렇게 중요한 장면인지 — 알고 보면 &amp;ldquo;아, 저게 그거구나&amp;quot;인데 모르고 보면 그냥 지나갑니다.&lt;/p&gt;
&lt;p&gt;원작을 읽고 가라고 하기엔 부담스럽고, 줄거리 요약을 읽으라고 하기엔 &lt;strong&gt;읽어도 안 남습니다.&lt;/strong&gt; 남의 이야기를 요약해서 읽으면 30분 뒤에 잊어버려요.&lt;/p&gt;
&lt;p&gt;그래서 &lt;strong&gt;직접 겪게 만들면 남지 않을까&lt;/strong&gt; 싶었습니다.&lt;/p&gt;
&lt;h2 id="30분짜리-버튼-하나"&gt;30분짜리, 버튼 하나
&lt;/h2&gt;&lt;p&gt;만든 규칙은 단순합니다.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;스테이지&lt;/td&gt;
					&lt;td&gt;6편&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;조작&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;스페이스 · 클릭 · 탭&lt;/strong&gt; — 셋 다 같은 동작&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;한 편&lt;/td&gt;
					&lt;td&gt;5분 이내&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;전체&lt;/td&gt;
					&lt;td&gt;30분 이내&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;PC·모바일&lt;/td&gt;
					&lt;td&gt;동일&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;버튼을 하나로 묶은 건 제약이 아니라 &lt;strong&gt;선택&lt;/strong&gt;이었습니다. 조작을 배우는 데 시간을 쓰면 이야기가 들어올 자리가 없어집니다. 누를 줄 아는 사람이면 6편을 다 갈 수 있어야 했어요.&lt;/p&gt;
&lt;p&gt;대신 &lt;strong&gt;편마다 그 버튼의 의미를 바꿨습니다.&lt;/strong&gt; 같은 버튼인데 손맛이 다릅니다.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;편&lt;/th&gt;
					&lt;th&gt;장면&lt;/th&gt;
					&lt;th&gt;버튼의 의미&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;1&lt;/td&gt;
					&lt;td&gt;키클롭스의 동굴&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;순간에 탭&lt;/strong&gt; — 거인의 손이 비켜난 찰나&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;2&lt;/td&gt;
					&lt;td&gt;아이올로스의 바람 자루&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;누르고 있다 놓기&lt;/strong&gt; — 참을수록 빨리 간다&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;3&lt;/td&gt;
					&lt;td&gt;세이렌의 노래&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;버티기&lt;/strong&gt; — 놓으면 진다 (2편의 정반대)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;4&lt;/td&gt;
					&lt;td&gt;스킬라와 카리브디스&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;틈에 연타&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;5&lt;/td&gt;
					&lt;td&gt;헬리오스의 소&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;배분 탭&lt;/strong&gt; — 누구를 먹일 것인가&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;6&lt;/td&gt;
					&lt;td&gt;이타카의 활&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;당겼다 정확히 놓기&lt;/strong&gt; — 단 한 발&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;3편이 특히 마음에 듭니다. 2편은 &lt;strong&gt;누르면 나아가고 놓으면 안전&lt;/strong&gt;한데, 3편은 &lt;strong&gt;누르면 안전하고 놓으면 집니다.&lt;/strong&gt; 같은 버튼의 의미가 뒤집혀요.&lt;/p&gt;
&lt;p&gt;&lt;img alt="세이렌 — 돛대에 묶인 오디세우스, 귀를 막은 부하들, 위쪽에 밀려올 노래가 미리 보인다" class="gallery-image" data-flex-basis="321px" data-flex-grow="134" height="820" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-9/sirens.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-9/sirens_hu_aaedcaea0245d616.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-9/sirens.webp 1100w" width="1100"&gt;&lt;/p&gt;
&lt;h2 id="여섯-장면이-곧-줄거리다"&gt;여섯 장면이 곧 줄거리다
&lt;/h2&gt;&lt;p&gt;이 게임의 목적은 재미보다 &lt;strong&gt;&amp;ldquo;이 여섯 개만 알면 된다&amp;rdquo;&lt;/strong&gt; 입니다. 보기 전이면 이걸 알고 극장에 들어가는 거고, 보고 난 뒤면 &amp;ldquo;아 그게 이거였구나&amp;quot;를 손으로 다시 확인하는 거예요. 그래서 편마다 신화가 실제로 말하는 것을 그대로 규칙으로 만들었습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1편 · 키클롭스&lt;/strong&gt; — 눈먼 거인이 동굴 입구를 손으로 훑습니다. 양 배에 매달려 나가야 해요. 거인이 눈이 멀었다는 걸 &lt;strong&gt;설명하지 않습니다.&lt;/strong&gt; 감긴 외눈과 더듬는 손으로 보여줍니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2편 · 바람 자루&lt;/strong&gt; — 이타카가 눈앞에 보이는 열흘째 새벽에 잠듭니다. &lt;strong&gt;이건 실패가 아니라 이야기입니다.&lt;/strong&gt; 피할 수 없어요. 부하들이 자루를 열고 배가 왔던 길을 되밀려 갑니다. 진행 게이지가 95%에서 20%대로 빨려 내려가는 걸 보고 있어야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3편 · 세이렌&lt;/strong&gt; — 세이렌은 미녀의 유혹이 아니라 &lt;em&gt;&amp;ldquo;네가 모르는 것을 알려주마&amp;rdquo;&lt;/em&gt; 라고 부릅니다. &lt;strong&gt;지식에 대한 갈망&lt;/strong&gt;이 이 장면의 진짜 유혹이에요.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4편 · 스킬라&lt;/strong&gt; — 키르케의 조언은 냉정합니다. &lt;em&gt;&amp;ldquo;여섯을 잃더라도 스킬라 쪽에 붙어라. 카리브디스는 배째로 삼킨다.&amp;rdquo;&lt;/em&gt; 그리고 오디세우스는 이 사실을 &lt;strong&gt;부하들에게 말하지 않았습니다.&lt;/strong&gt; 말했다면 아무도 노를 젓지 않았을 테니까요.&lt;/p&gt;
&lt;p&gt;&lt;img alt="스킬라 — 여섯 머리가 내려온다. 아래쪽은 카리브디스" class="gallery-image" data-flex-basis="321px" data-flex-grow="134" height="820" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-9/scylla.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-9/scylla_hu_4a50cda8679725e8.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-9/scylla.webp 1100w" width="1100"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5편 · 헬리오스의 소&lt;/strong&gt; — 굶주림을 못 막으면 부하들이 소를 잡아먹고 제우스의 벼락에 전멸합니다. &lt;strong&gt;이게 오디세우스가 혼자 돌아온 이유입니다.&lt;/strong&gt; 신화대로 지는 게 기본 결말이고, 아주 좁은 길로 살려내면 &lt;em&gt;&amp;ldquo;호메로스에서는 일어나지 않은 일입니다&amp;rdquo;&lt;/em&gt; 라는 카드가 뜹니다.&lt;/p&gt;
&lt;p&gt;&lt;img alt="헬리오스의 소 — 언덕 위의 황금 소, 여섯 개의 굶주림 막대" class="gallery-image" data-flex-basis="321px" data-flex-grow="134" height="820" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-9/cattle.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-9/cattle_hu_5eb9e46f5631bcc1.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-9/cattle.webp 1100w" width="1100"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6편 · 이타카의 활&lt;/strong&gt; — 20년 만에 거지꼴로 돌아옵니다. 페넬로페의 조건은 &lt;em&gt;&amp;ldquo;오디세우스의 활에 시위를 얹고 도끼 열두 자루를 화살 하나로 꿰뚫는 자&amp;rdquo;&lt;/em&gt;. 그 활은 오디세우스만 당길 수 있습니다. &lt;strong&gt;활을 당기는 순간이 곧 정체를 밝히는 순간입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="이타카의 활 — 도끼 열두 자루가 원근으로 뚫려 있다" class="gallery-image" data-flex-basis="321px" data-flex-grow="134" height="820" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-9/bow.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-9/bow_hu_f7b56a6e54e4c81d.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-9/bow.webp 1100w" width="1100"&gt;&lt;/p&gt;
&lt;h2 id="부하-600명이-여섯-편을-관통합니다"&gt;부하 600명이 여섯 편을 관통합니다
&lt;/h2&gt;&lt;p&gt;이게 이 게임의 뼈대입니다. &lt;strong&gt;600명으로 출항해서, 각 편에서 잃은 만큼 줄어든 채로 다음 편에 갑니다.&lt;/strong&gt; 결과 카드마다 마지막 줄에 남은 인원이 적혀요.&lt;/p&gt;
&lt;p&gt;그리고 끝까지 가면 이렇게 끝납니다.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;호메로스의 오디세우스는 &lt;strong&gt;혼자&lt;/strong&gt; 돌아왔습니다.
당신은 &lt;strong&gt;N명&lt;/strong&gt;을 데려왔습니다.&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;&lt;img alt="에필로그 — 여섯 줄로 정리되는 당신의 항해" class="gallery-image" data-flex-basis="321px" data-flex-grow="134" height="820" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-9/epilogue.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-9/epilogue_hu_21b89ef591488588.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-9/epilogue.webp 1100w" width="1100"&gt;&lt;/p&gt;
&lt;p&gt;에필로그의 여섯 줄은 &lt;strong&gt;당신이 실제로 겪은 것&lt;/strong&gt;을 적습니다. 남의 이야기를 요약하면 에필로그가 아니니까요.&lt;/p&gt;
&lt;h2 id="만들면서--게임이-플레이어에게-거짓말을-하고-있었다"&gt;만들면서 — 게임이 플레이어에게 거짓말을 하고 있었다
&lt;/h2&gt;&lt;p&gt;여기부터가 이번 편에서 제일 많이 배운 부분입니다.&lt;/p&gt;
&lt;p&gt;세 번이나, &lt;strong&gt;화면이 시키는 대로 하면 지는 게임&lt;/strong&gt;을 만들고 있었습니다. 그것도 매번 다른 형태로요.&lt;/p&gt;
&lt;h3 id="첫-번째--버텨라"&gt;첫 번째 — &amp;ldquo;버텨라&amp;rdquo;
&lt;/h3&gt;&lt;p&gt;2편 마지막, 졸음이 몰려오는 구간에서 화면에 &lt;strong&gt;&amp;ldquo;버텨라 / 누르고 있어라&amp;rdquo;&lt;/strong&gt; 가 떴습니다. 그런데 정책별로 돌려서 재보니:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;시킨 대로&lt;/th&gt;
					&lt;th&gt;도달&lt;/th&gt;
					&lt;th&gt;잃은 부하&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;버틴다&lt;/td&gt;
					&lt;td&gt;5.8%&lt;/td&gt;
					&lt;td&gt;36명&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;그냥 손 뗀다&lt;/td&gt;
					&lt;td&gt;8.9%&lt;/td&gt;
					&lt;td&gt;30명&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;시킨 대로 한 쪽이 두 지표 모두 나빴습니다.&lt;/strong&gt; 버티는 동안에도 돛 부하가 살아 있어서, 순순히 따르면 돛이 찢어졌던 거예요.&lt;/p&gt;
&lt;h3 id="두-번째--연타해-젓는다"&gt;두 번째 — &amp;ldquo;연타해 젓는다&amp;rdquo;
&lt;/h3&gt;&lt;p&gt;4편 화면 아래에 &lt;strong&gt;&amp;ldquo;스페이스 또는 화면을 연타해 젓는다&amp;rdquo;&lt;/strong&gt; 라고 적혀 있었습니다. 저는 그 말을 그대로 믿고 연타했고, 계속 죽었습니다. 노는 열심히 젓는데 한쪽 노잡이가 죄다 잡혀가더군요.&lt;/p&gt;
&lt;p&gt;원인을 찾으려고 &lt;strong&gt;연타만 하는 봇&lt;/strong&gt;을 만들어 붙였습니다. 연타만 하면 &lt;strong&gt;9.5초 만에 노잡이 8명이 전부 끌려가고&lt;/strong&gt;, 그 뒤 7초 동안 아무것도 못 한 채 소용돌이에 삼켜졌습니다. 그리고 결과 카드가 이렇게 말했어요.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;&amp;ldquo;노를 멈춘 대가다 — 쉬면 끌려간다&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;한 번도 안 쉰 사람한테요. &lt;strong&gt;안내도 틀렸고 꾸중도 틀렸습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;고친 건 세 가지입니다. 안내를 규칙의 나머지 절반까지 적고(&lt;em&gt;&amp;ldquo;머리가 물러난 사이에만 연타 · 내려오면 멈춘다&amp;rdquo;&lt;/em&gt;), 신호를 &lt;strong&gt;&amp;ldquo;저어라 → 멈춰라&amp;rdquo;&lt;/strong&gt; 두 박자로 가르치고, 시작 직후 1.15초 동안 머리를 멈춰 둬서 첫 수업을 읽을 시간을 줬습니다.&lt;/p&gt;
&lt;p&gt;&lt;img alt="스킬라 — 멈춰라가 뜨는 순간. 아래 안내가 규칙의 나머지 절반을 말한다" class="gallery-image" data-flex-basis="321px" data-flex-grow="134" height="820" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-9/scylla-stop.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-9/scylla-stop_hu_69999ec5ef6304d5.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-9/scylla-stop.webp 1100w" width="1100"&gt;&lt;/p&gt;
&lt;h3 id="세-번째--설계에-없던-죽음"&gt;세 번째 — 설계에 없던 죽음
&lt;/h3&gt;&lt;p&gt;그런데 더 이상한 걸 발견했습니다. &lt;strong&gt;노잡이를 다 잃으면 죽는 규칙은 설계 어디에도 없었습니다.&lt;/strong&gt; 설계서에는 &lt;em&gt;&amp;ldquo;잘하면 2~3명, 못하면 여섯 이상&amp;rdquo;&lt;/em&gt; 이라고만 적혀 있었어요.&lt;/p&gt;
&lt;p&gt;생각해보면 당연합니다. &lt;strong&gt;배에 부하가 570명이나 타고 있는데 노 자리 여덟 개가 비었다고 배가 죽는 게 말이 되나요.&lt;/strong&gt; 잡혀간 자리는 다른 사람이 채우면 됩니다.&lt;/p&gt;
&lt;p&gt;그래서 자리를 채우게 하고, 지는 길은 카리브디스 하나만 남겼습니다. 같은 연타를 다시 돌리니 &lt;strong&gt;살아서 해협을 통과&lt;/strong&gt;했습니다. 51명을 잃고요.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;손실은 점수지 사망이 아니었습니다.&lt;/strong&gt; 신화의 스킬라도 여섯을 물어 가고 배는 지나갑니다.&lt;/p&gt;
&lt;h2 id="계측이-없었으면-셋-다-못-잡았다"&gt;계측이 없었으면 셋 다 못 잡았다
&lt;/h2&gt;&lt;p&gt;세 번 모두 &lt;strong&gt;제 느낌으로는 안 보였습니다.&lt;/strong&gt; &amp;ldquo;버텨라&amp;quot;는 제가 쓴 문구였고, &amp;ldquo;연타&amp;quot;도 제가 쓴 문구였고, 죽음 규칙도 제가 넣은 겁니다. 만든 사람은 규칙을 아니까 시키는 대로 안 합니다.&lt;/p&gt;
&lt;p&gt;잡아낸 방법은 전부 같았습니다. &lt;strong&gt;정책을 여러 개 만들어서 결과를 표로 뽑는 것.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;시킨 대로 하는 봇 / 안 하는 봇 → 어느 쪽이 나은가&lt;/li&gt;
&lt;li&gt;사람처럼 마구 누르는 봇 → 몇 초 만에 죽나&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;특히 마지막이 중요했습니다. 원래 있던 자동 플레이 함수는 &lt;strong&gt;&amp;ldquo;완벽한 봇&amp;rdquo;&lt;/strong&gt; 이라 안전할 때만 노를 저었습니다. 그래서 4편을 100번 돌려도 문제가 안 보였어요. &lt;strong&gt;사람이 실제로 하는 짓을 흉내내는 봇을 따로 만들고 나서야&lt;/strong&gt; 9.5초 전멸이 나왔습니다.&lt;/p&gt;
&lt;h2 id="그리고-1편을-깼는데-에필로그가-떴다"&gt;그리고 1편을 깼는데 에필로그가 떴다
&lt;/h2&gt;&lt;p&gt;발행 직전에 나온 버그입니다. 1편을 클리어했더니 2편이 아니라 &lt;strong&gt;&amp;ldquo;이 이야기의 나머지는 극장에서 이어집니다&amp;rdquo;&lt;/strong&gt; 가 떴어요.&lt;/p&gt;
&lt;p&gt;원인은 두 겹이었습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;첫째, 캐시.&lt;/strong&gt; 프리뷰 서버가 캐시 헤더를 안 보내서 브라우저가 며칠 전 빌드를 붙들고 있었습니다. 그 빌드는 지금은 지운 옛 파일을 참조하던 버전이라 2~6편을 못 읽었어요.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;둘째, 그리고 이쪽이 진짜 문제.&lt;/strong&gt; 부팅 코드가 &lt;strong&gt;못 읽은 편을 조용히 건너뛰고 남은 것만으로 항로를 짰습니다.&lt;/strong&gt; 그래서 1편짜리 항해가 만들어졌고, 1편을 깨는 순간 &amp;ldquo;마지막 편을 끝냈다&amp;quot;가 되어 에필로그가 뜬 겁니다.&lt;/p&gt;
&lt;p&gt;모자란 채로 굴러가느니 &lt;strong&gt;멈추고 무엇이 없는지 말하는 게&lt;/strong&gt; 낫습니다. 이렇게 바꿨어요.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;스테이지 모듈이 모자랍니다 (5/6) — 없는 것: st3.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;브라우저 캐시를 비우고 새로고침하세요.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="숫자"&gt;숫자
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;코드&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;13,882줄&lt;/strong&gt; (파일 9개)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;외부 에셋&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;0개&lt;/strong&gt; — 도형·소리 전부 코드가 만듭니다&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;스테이지&lt;/td&gt;
					&lt;td&gt;6편, 각자 자기 파일 하나가 통째로 소유&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;한 번도 안 틀렸을 때&lt;/td&gt;
					&lt;td&gt;약 5분&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;처음 하면&lt;/td&gt;
					&lt;td&gt;재시도 포함 25~30분&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;콘솔 에러&lt;/td&gt;
					&lt;td&gt;0&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;편마다 파일 하나가 장면·규칙·게이지·결과를 전부 갖게 한 게 이번 구조의 핵심입니다. 3편을 고치다 4편이 깨지는 일이 없었어요.&lt;/p&gt;
&lt;h2 id="개발-후기"&gt;개발 후기
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;&amp;ldquo;영화를 볼 때 알면 좋은 것&amp;quot;을 만들려니 게임 디자인이 거꾸로 됐습니다.&lt;/strong&gt; 보통은 재미가 먼저고 이야기가 따라오는데, 이번엔 &lt;strong&gt;꼭 알아야 하는 여섯 장면이 먼저&lt;/strong&gt; 정해져 있었어요. 그 여섯 개를 각각 어떤 손맛으로 만들 것인가가 전부였습니다.&lt;/p&gt;
&lt;p&gt;그래서 2편이 제일 어려웠습니다. &lt;strong&gt;이타카가 눈앞인데 잠드는 장면&lt;/strong&gt;은 플레이어가 이길 수 없어야 하거든요. 이길 수 없는 판을 &amp;ldquo;재미없다&amp;quot;가 아니라 &amp;ldquo;분하다&amp;quot;로 만들어야 했습니다. 지금은 잠들기 직전까지 진행 게이지가 95%까지 차오르고, 그게 20%대로 빨려 내려가는 걸 지켜보게 됩니다.&lt;/p&gt;
&lt;p&gt;그리고 하나 더. &lt;strong&gt;잘 만든 검사 도구가 오히려 눈을 가릴 수 있습니다.&lt;/strong&gt; 이 게임에는 처음부터 자동 플레이 봇이 붙어 있었는데, 그게 &amp;ldquo;완벽한 봇&amp;quot;이었어요. 규칙을 아는 채로 안전할 때만 눌렀습니다. 그래서 4편을 몇 번을 돌려도 &lt;strong&gt;전부 통과&lt;/strong&gt;했습니다. 정작 화면에 적힌 안내를 그대로 따르는 봇은 없었습니다.&lt;/p&gt;
&lt;p&gt;세 번 연속 같은 종류의 문제가 나온 게 우연이 아니라고 생각합니다. 검사는 &lt;strong&gt;&amp;ldquo;규칙대로 하면 되나&amp;rdquo;&lt;/strong&gt; 가 아니라 &lt;strong&gt;&amp;ldquo;화면에 적힌 대로 하면 되나&amp;rdquo;&lt;/strong&gt; 를 물어야 했습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;이 게임은 여기서 끝이 아닙니다. &lt;strong&gt;애정을 갖고 계속 손볼 생각이에요.&lt;/strong&gt; 이번에 세 번이나 &amp;ldquo;화면이 시키는 대로 하면 지는&amp;rdquo; 걸 잡았는데, 아직 못 찾은 게 더 있을 거라고 봅니다. 편마다 손맛을 다듬고, 모바일도 더 만져서 조금씩 패치해 나가겠습니다.&lt;/p&gt;
&lt;p&gt;아직 안 보셨다면 &lt;strong&gt;극장 가기 전에 30분만&lt;/strong&gt; 써보세요. 여섯 장면이 나올 때 &amp;ldquo;아, 저거&amp;quot;가 되면 성공입니다.&lt;/p&gt;
&lt;p&gt;이미 보셨다면 &lt;strong&gt;보고 나서 30분&lt;/strong&gt;도 괜찮습니다. 저는 이쪽이었는데, 만들면서 오히려 영화가 더 오래 남았어요. 세이렌 앞에서 오디세우스가 왜 그러고 있었는지는 직접 밧줄을 붙잡아 보고 나서야 몸으로 알겠더군요. 😄&lt;/p&gt;
&lt;p&gt;👉 &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/odyssey/" &gt;오디세이아 하러 가기&lt;/a&gt;&lt;/strong&gt; · &lt;a class="link" href="https://junstellar.github.io/games/" &gt;AI 게임 랩 전체 보기&lt;/a&gt;&lt;/p&gt;</description></item><item><title>AI로 게임 만들기 #8 — 심해 1,200m까지 내려가서 물고기 27종 도감 채우기 (공개빌드 #8)</title><link>https://junstellar.github.io/p/ai-game-lab-build-8/</link><pubDate>Mon, 10 Aug 2026 09:00:00 +0900</pubDate><guid>https://junstellar.github.io/p/ai-game-lab-build-8/</guid><description>&lt;img src="https://junstellar.github.io/p/ai-game-lab-build-8/deep-fishing-poster.webp" alt="Featured image of post AI로 게임 만들기 #8 — 심해 1,200m까지 내려가서 물고기 27종 도감 채우기 (공개빌드 #8)" /&gt;&lt;p&gt;&lt;a class="link" href="https://junstellar.github.io/p/ai-game-lab-build-7/" &gt;지난 편&lt;/a&gt;의 조차장에 이어, 여덟 번째 게임은 &lt;strong&gt;심해 낚시&lt;/strong&gt;입니다. 수면에서 &lt;strong&gt;1,200m 심연까지&lt;/strong&gt; 내려가 물고기를 낚고, &lt;strong&gt;27종 도감&lt;/strong&gt;을 채우는 게임이에요.&lt;/p&gt;
&lt;p&gt;게임 랩에 처음 생긴 **&amp;ldquo;모으는 재미&amp;rdquo;**입니다. 앞의 게임들은 판이 끝나면 기록만 남는데, 이건 잡은 물고기가 &lt;strong&gt;영구히 쌓여요.&lt;/strong&gt;&lt;/p&gt;
&lt;video autoplay loop muted playsinline preload="metadata" poster="/p/ai-game-lab-build-8/deep-fishing-poster.webp" style="width:100%;max-width:420px;display:block;margin:1.2rem auto;border-radius:14px;box-shadow:0 6px 24px rgba(0,0,0,.25)"&gt;
 &lt;source src="https://junstellar.github.io/p/ai-game-lab-build-8/deep-fishing-play.mp4" type="video/mp4"&gt;
&lt;/video&gt;
&lt;p&gt;👉 &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/deep-fishing/" &gt;심해 낚시 직접 하러 가기&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="어떤-게임이냐면"&gt;어떤 게임이냐면
&lt;/h2&gt;&lt;p&gt;탭 세 번이면 한 마리입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;① &lt;strong&gt;깊이 정하기&lt;/strong&gt; — 움직이는 막대를 탭. 깊을수록 희귀하고 어렵습니다&lt;/li&gt;
&lt;li&gt;② &lt;strong&gt;챔질&lt;/strong&gt; — 물고기가 다가와 미끼를 &lt;strong&gt;툭툭 건드리다가&lt;/strong&gt;(가짜) &lt;strong&gt;확 무는 순간&lt;/strong&gt; 탭&lt;/li&gt;
&lt;li&gt;③ &lt;strong&gt;파이팅&lt;/strong&gt; — 누르면 감기고, 떼면 풀립니다. &lt;strong&gt;장력을 초록 구간에&lt;/strong&gt; 두세요&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;한 판은 캐스팅 12번. &lt;strong&gt;멸치부터 🐉크라켄까지 27종&lt;/strong&gt;이 있고, 가운데를 잘 유지할수록 &lt;strong&gt;정확도 보너스&lt;/strong&gt;(최대 +85%)가 붙습니다.&lt;/p&gt;
&lt;p&gt;&lt;img alt="도감 — 잡은 물고기는 컬러, 못 잡은 건 실루엣" class="gallery-image" data-flex-basis="138px" data-flex-grow="57" height="1252" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-8/deep-fishing-dex.webp" width="720"&gt;&lt;/p&gt;
&lt;h2 id="물고기-27종은-comfyui로-직접-그렸다"&gt;물고기 27종은 ComfyUI로 직접 그렸다
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://junstellar.github.io/p/claude-local-image-generation/" &gt;예전에 노트북에 설치해둔 &lt;strong&gt;ComfyUI&lt;/strong&gt;&lt;/a&gt;로 27종을 전부 뽑았습니다. 이번에도 생성이 끝이 아니었어요 — 고래상어는 흰 배경 박스를 달고 나오고, 가오리는 모래바닥까지 딸려왔고, 방어는 두 마리가 겹쳐 나왔습니다.&lt;/p&gt;
&lt;p&gt;배경색을 자동 감지해 투명 처리하고, 물체에 딱 맞게 다시 자르고, 그래도 안 되는 건 프롬프트를 바꿔 다시 뽑았어요. &lt;strong&gt;ComfyUI가 그려준 걸 게임에 맞게 다듬는 게 절반의 일&lt;/strong&gt;이라는 게 이번에도 확인됐습니다.&lt;/p&gt;
&lt;h2 id="첫-버전은-허공에-대고-챔질하는-것-같았다"&gt;첫 버전은 허공에 대고 챔질하는 것 같았다
&lt;/h2&gt;&lt;p&gt;일단 돌아가게 만들어놓고 직접 해봤는데, 이상했습니다. 화면엔 &lt;strong&gt;줄 하나만 덩그러니&lt;/strong&gt; 있고, 아무것도 안 보이는데 &amp;ldquo;지금 탭!&amp;ldquo;이라는 글자만 떴거든요. 뭘 보고 채라는 건지 알 수가 없었어요.&lt;/p&gt;
&lt;p&gt;그래서 &lt;strong&gt;물고기를 실제로 보여주기로&lt;/strong&gt; 했습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;물고기가 &lt;strong&gt;옆에서 헤엄쳐 다가오고&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;미끼를 &lt;strong&gt;툭툭 건드립니다&lt;/strong&gt;(찌가 살짝 흔들림 = 가짜 입질)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;확 무는 순간&lt;/strong&gt; 찌가 쑥 들어가고 물보라가 터집니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이러니 &amp;ldquo;너무 일찍 채면 실패&amp;quot;라는 규칙이 &lt;strong&gt;눈으로 납득되는 규칙&lt;/strong&gt;이 됐어요. 가짜와 진짜를 구분하는 게 실력이 되고요.&lt;/p&gt;
&lt;h2 id="물고기에-검은-배경이-붙은-것-같았는데-재보니-아니었다"&gt;물고기에 검은 배경이 붙은 것 같았는데, 재보니 아니었다
&lt;/h2&gt;&lt;p&gt;계속 해보니 어떤 물고기는 &lt;strong&gt;검은 배경이 붙어 있는 것처럼&lt;/strong&gt; 보였습니다. 배경 제거가 덜 됐나 싶었죠. 그래서 27종을 전부 계측해봤습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;배경이 남은 스프라이트는 하나도 없었습니다.&lt;/strong&gt; 제가 짚은 원인이 틀렸던 거예요.&lt;/p&gt;
&lt;p&gt;진짜 원인은 따로 있었습니다. &lt;strong&gt;물고기와 물의 밝기가 너무 비슷했던 겁니다.&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;물고기&lt;/th&gt;
					&lt;th&gt;물고기 밝기&lt;/th&gt;
					&lt;th&gt;그 깊이의 물 밝기&lt;/th&gt;
					&lt;th&gt;차이&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;바다거북&lt;/td&gt;
					&lt;td&gt;127&lt;/td&gt;
					&lt;td&gt;130&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;흡혈오징어&lt;/td&gt;
					&lt;td&gt;45&lt;/td&gt;
					&lt;td&gt;20&lt;/td&gt;
					&lt;td&gt;25&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;심해는 물 밝기가 20 아래인데 심해어도 어둡습니다. &lt;strong&gt;검은 배경에 검은 물체&lt;/strong&gt;였던 거죠.&lt;/p&gt;
&lt;p&gt;그래서 &lt;strong&gt;바늘에 심해 랜턴&lt;/strong&gt;을 달았습니다. 미끼 주변을 비추니 물고기가 보이고, 심해 낚시 분위기도 살았어요. 물고기 외곽에 은은한 발광 테두리도 더했고요.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;감으로 스프라이트를 다시 뽑았으면 시간만 날렸을 겁니다. 재보니 문제는 완전히 다른 곳에 있었어요.&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="줄은-그대로인데-물고기만-올라갔다"&gt;줄은 그대로인데 물고기만 올라갔다
&lt;/h2&gt;&lt;p&gt;끌어올릴 때 뭔가 따로 노는 느낌이라 코드를 열어봤더니, 줄 끝이 화면 중앙에 &lt;strong&gt;고정&lt;/strong&gt;돼 있었습니다. 물고기만 위로 움직이니 둘이 분리돼 보인 거예요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;줄 끝을 &lt;strong&gt;물고기 입에 고정&lt;/strong&gt;했습니다. 올라오면 줄도 같이 짧아져요&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;장력에 따라 줄 모양이 변합니다&lt;/strong&gt; — 팽팽하면 직선, 느슨하면 축 처진 곡선, 한계 근처면 &lt;strong&gt;붉게 굵어짐&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;끌어올린 만큼 &lt;strong&gt;깊이 숫자도 줄고&lt;/strong&gt;, 덤으로 &lt;strong&gt;물이 밝아집니다&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="잡은 순간 — 정확도 보너스까지" class="gallery-image" data-flex-basis="138px" data-flex-grow="57" height="1252" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-8/deep-fishing-catch.webp" width="720"&gt;&lt;/p&gt;
&lt;h2 id="그래도-뭔가-계속-아쉬웠다--세-번째-진단"&gt;그래도 뭔가 계속 아쉬웠다 — 세 번째 진단
&lt;/h2&gt;&lt;p&gt;여기까지 고쳤는데도 뭔가 계속 아쉬웠습니다. 이번엔 감으로 짚지 않고 &lt;strong&gt;처음부터 코드를 뒤졌어요.&lt;/strong&gt; 세 가지가 나왔습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;① 물고기가 헤엄치지 않았다.&lt;/strong&gt; 코드에 &lt;strong&gt;회전이 단 한 곳도 없었습니다.&lt;/strong&gt; 정지 이미지가 미끄러져 다니는 상태였죠. 헤엄칠 때 몸을 기울이고 발버둥칠 때 몸을 확 트는 것만 넣었는데 완전히 달라졌습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;② 심해로 가는 여정이 없었다.&lt;/strong&gt; 하강 코드가 이랬어요.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;curDepth&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetDepth&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;0.012&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 항상 1.4초
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;100m든 1,200m든 하강 시간이 똑같았습니다.&lt;/strong&gt; 심해 낚시인데 심해로 가는 느낌이 0이었죠. 깊이 비례 하강(최대 3.5초)으로 바꾸고, 배경이 위로 흐르게 하고, &lt;strong&gt;&lt;code&gt;200m 황혼대&lt;/code&gt; &lt;code&gt;500m 심해층&lt;/code&gt; &lt;code&gt;900m 심연&lt;/code&gt;&lt;/strong&gt; 이정표가 스쳐 지나가게 했습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;③ 줄이 허공에서 시작했다.&lt;/strong&gt; 배도 수면도 없었어요. &lt;strong&gt;배와 물결&lt;/strong&gt;을 넣고 줄이 배에서 내려오게 했습니다. 깊어지면 수면이 어둠 속으로 사라지고, 끌어올리면 다시 나타나요.&lt;/p&gt;
&lt;p&gt;&lt;img alt="내려가는 중 — 구간 이정표가 스쳐 지나간다" class="gallery-image" data-flex-basis="164px" data-flex-grow="68" height="1050" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-8/deep-fishing-descent.webp" width="720"&gt;&lt;/p&gt;
&lt;h2 id="크기가-무게를-말해준다"&gt;크기가 무게를 말해준다
&lt;/h2&gt;&lt;p&gt;물고기 크기를 &lt;strong&gt;희귀도&lt;/strong&gt; 기준으로 잡았더니 &lt;strong&gt;1kg 복어가 900kg 고래상어만 하게&lt;/strong&gt; 보였습니다. 무게(로그) 기준으로 바꾸고, &lt;strong&gt;입질 전에 무게를 미리 굴려서&lt;/strong&gt; 보이는 크기와 실제 잡는 무게를 일치시켰어요. 이제 화면만 봐도 대물인지 잔챙이인지 압니다.&lt;/p&gt;
&lt;h2 id="밀당을-넣었다가-게임을-망칠-뻔했다"&gt;밀당을 넣었다가 게임을 망칠 뻔했다
&lt;/h2&gt;&lt;p&gt;마지막에 &lt;strong&gt;물고기가 아래로 파고드는 순간&lt;/strong&gt;(손을 떼고 버텨야 하는)을 넣었습니다. 그런데 시뮬레이션을 돌려보니:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;넣기 전&lt;/th&gt;
					&lt;th&gt;넣은 직후&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;포획률&lt;/td&gt;
					&lt;td&gt;83%&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;33%&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;포획률이 반토막 났고&lt;/strong&gt; 파이팅이 교착에 빠져 아예 안 끝나는 경우까지 생겼습니다. 저항력·진행도 손실·발동 횟수를 모두 낮춰 되돌렸어요. 지금은 긴장감만 주고 난이도는 그대로입니다.&lt;/p&gt;
&lt;p&gt;이번에도 &lt;strong&gt;&amp;ldquo;재미있을 것 같아서&amp;rdquo; 넣은 기능이 게임을 망가뜨릴 뻔한 걸 숫자가 잡아줬습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="이제-게임-랩이-8개"&gt;이제 게임 랩이 8개
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;게임&lt;/th&gt;
					&lt;th&gt;성격&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;🎨&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/guess-image/" &gt;AI 그림 맞히기&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;머리 (퀴즈)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🟩&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/hangul-word/" &gt;한글 단어 맞히기&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;습관 (데일리)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🍰&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/stack-tower/" &gt;디저트 탑 쌓기&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;손맛 (아케이드)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🕹️&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/dodge/" &gt;낙하 회피&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;반사신경 (액션)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🗑️&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/paper-hoop/" &gt;종이 농구&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;조준 (물리)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🪐&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/space-merge/" &gt;우주 진화&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;최고점 (병합 퍼즐)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🚂&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/shunting/" &gt;조차장&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;논리 (3D 퍼즐)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🎣&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/deep-fishing/" &gt;심해 낚시&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;수집 (도감)&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="이번-편에서-배운-것"&gt;이번 편에서 배운 것
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;&amp;ldquo;뭔가 어색하다&amp;quot;는 느낌은 그 자체로는 정보가 아닙니다. 하지만 그 아래엔 항상 구체적인 원인이 있어요.&lt;/strong&gt; 이번엔 세 번에 걸쳐 파고들었고, 그때마다 코드에서 원인을 찾았습니다 — 회전이 없었고, 하강 시간이 고정이었고, 줄 끝 좌표가 고정이었죠. 전부 &lt;strong&gt;한두 줄짜리 원인&lt;/strong&gt;이었습니다.&lt;/p&gt;
&lt;p&gt;그리고 한 번은 &lt;strong&gt;제가 짚은 원인이 틀렸어요.&lt;/strong&gt; &amp;ldquo;배경 제거가 실패했나?&amp;rdquo; 싶었는데 재보니 대비 문제였습니다. 고치기 전에 재보는 게 훨씬 빨랐습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/deep-fishing/" &gt;심해 낚시 한 판&lt;/a&gt;&lt;/strong&gt; 해보시고 몇 종이나 잡았는지 알려주세요. 저는 아직 &lt;strong&gt;크라켄&lt;/strong&gt;을 못 봤습니다. 🐉&lt;/p&gt;
&lt;p&gt;&lt;em&gt;다음 편엔 아홉 번째 게임을 들고 오겠습니다.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>AI로 게임 만들기 #7 — 화차를 밀고 당기는 3D 조차장, 에이전트 60명이 놓친 것 (공개빌드 #7)</title><link>https://junstellar.github.io/p/ai-game-lab-build-7/</link><pubDate>Sat, 08 Aug 2026 14:00:00 +0900</pubDate><guid>https://junstellar.github.io/p/ai-game-lab-build-7/</guid><description>&lt;img src="https://junstellar.github.io/p/ai-game-lab-build-7/hero.webp" alt="Featured image of post AI로 게임 만들기 #7 — 화차를 밀고 당기는 3D 조차장, 에이전트 60명이 놓친 것 (공개빌드 #7)" /&gt;&lt;p&gt;&lt;a class="link" href="https://junstellar.github.io/p/ai-game-lab-build-6/" &gt;지난 편&lt;/a&gt;의 우주 진화에 이어, 일곱 번째 게임은 &lt;strong&gt;조차장&lt;/strong&gt;입니다. 화차를 밀고 당겨 정해진 순서로 열차를 편성하는 3D 퍼즐이에요. 🚂&lt;/p&gt;
&lt;p&gt;👉 &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/shunting/" &gt;조차장 직접 하러 가기&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이번 편은 게임 소개보다 &lt;strong&gt;만드는 과정에서 크게 헤맨 이야기&lt;/strong&gt;가 절반입니다. AI 에이전트를 60명 넘게 붙였는데 점수가 오히려 내려간 라운드가 있었고, 그 원인이 제가 예상한 것과 전혀 달랐거든요.&lt;/p&gt;
&lt;h2 id="시작은-남이-만든-게임을-본-것"&gt;시작은 남이 만든 게임을 본 것
&lt;/h2&gt;&lt;p&gt;7월 말에 &lt;strong&gt;클로드 오브 듀티(Claude of Duty)&lt;/strong&gt; 소식을 봤습니다. Matt Shumer가 Claude Opus 5에 프롬프트 하나를 던져서 브라우저 FPS를 통째로 받아냈다는 것이었습니다. Three.js와 WebGL2로 돌아가고, 약 55,000줄에 11개 서브시스템, 그리고 &lt;strong&gt;외부 에셋이 하나도 없습니다&lt;/strong&gt;. 텍스처도 메시도 사운드도 전부 코드가 런타임에 만듭니다. 공개 5일 만에 깃허브 별 2,381개가 붙었습니다.&lt;/p&gt;
&lt;p&gt;부러웠다기보다 &lt;strong&gt;되는지 궁금했습니다.&lt;/strong&gt; 저도 해보고 싶었습니다.&lt;/p&gt;
&lt;p&gt;다만 FPS를 따라 만들 생각은 없었습니다. 같은 걸 하나 더 만드는 건 의미가 없고, 무엇보다 저는 총 쏘는 게임을 잘 모릅니다. 그래서 소재를 찾다가 네이버 블로그에서 게임 광고를 하나 봤습니다. 주차장에 차가 빽빽하게 들어차 있고 화살표로 빼내는, 흔한 모바일 퍼즐 광고였습니다.&lt;/p&gt;
&lt;p&gt;저게 무슨 장르인지 물어봤더니 언블록 퍼즐(러시아워 계열)과 소트 퍼즐이 한 광고에 섞여 있는 거라고 하더군요. 그래서 &amp;ldquo;주차 말고 다른 형태로 5개만 뽑아봐라&amp;quot;고 했습니다. 조차장, 광선 미로, 나사 풀기, 엘리베이터 배차, 실타래 풀기가 나왔습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;조차장&lt;/strong&gt;을 골랐습니다. 국내 브라우저 게임에 거의 없는 소재였고, 물리 엔진 없이 순수 로직으로 끝나는 게 마음에 들었습니다.&lt;/p&gt;
&lt;p&gt;미술 방향은 &amp;ldquo;떠 있는 디오라마&amp;quot;로 잡았습니다. 조차장이 땅에서 뜯겨 나온 흙덩이 위에 얹혀 하늘에 떠 있습니다. 위는 잔디와 자갈, 아래는 암반 지층과 늘어진 뿌리. 카메라 화각을 24°로 좁게 잡아서 실제 크기의 세계가 아니라 &lt;strong&gt;손바닥에 올려놓은 모형처럼&lt;/strong&gt; 보이게 했습니다. 맨 위 표지 이미지가 그 결과입니다.&lt;/p&gt;
&lt;h2 id="던진-프롬프트"&gt;던진 프롬프트
&lt;/h2&gt;&lt;p&gt;이게 전부입니다. 이 다음부터는 에이전트들이 알아서 했습니다.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;조차장 아이디어를 가지고 해당 분야 최고의 게임을 만들어 달라.&lt;/p&gt;
&lt;p&gt;모든 면에서 완벽하고 &lt;strong&gt;시각적으로 아름다워야&lt;/strong&gt; 하며, 텍스처부터 물리 효과를 비롯해 생각할 수 있는 모든 요소를 &lt;strong&gt;AAA급 품질&lt;/strong&gt;로 구현해야 합니다. 여러 하위 에이전트를 병렬로 투입하고, 각 에이전트가 개별 요소를 하나씩 담당하게 하여 게임을 완벽하게 만들어 주세요. 각 요소를 반복해서 개선하고, &lt;strong&gt;별도의 하위 에이전트가 시각적으로 검토&lt;/strong&gt;하여 AAA급 품질에 도달했는지 확인하게 하세요. 검토 에이전트는 &lt;strong&gt;매우 엄격하게&lt;/strong&gt; 평가해야 하며, 결과물이 &lt;strong&gt;AAA급으로 보이지 않는다면&lt;/strong&gt; 만족할 때까지 계속 개선해야 합니다.&lt;/p&gt;
&lt;p&gt;각 하위 에이전트가 실제 &lt;code&gt;oskarstalberg.com/Townscaper&lt;/code&gt; 나 &lt;code&gt;krunker.io&lt;/code&gt; 이런 게임들과 비교했을 때 그 품질에 완전히 감탄할 때까지 멈추지 마세요. &lt;strong&gt;실제 게임 화면과 나란히 놓고 블라인드 테스트&lt;/strong&gt;를 진행하여 어느 쪽이 더 좋아 보이는지 판단할 수 있을 정도여야 합니다. 구현 방법에 제한은 없고 현재 PC의 모든 자원을 이용해도 좋으니 완벽해질 때까지 반복해서 개선하세요. 하위 에이전트들을 병렬로 적극 활용하고 최고 수준의 코딩 역량을 투입하세요. 이 개발에 필요한 모든 권한을 승인한다.&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;읽어보면 아시겠지만 이 프롬프트에서 품질을 가리키는 단어는 &lt;strong&gt;전부 보는 것에 관한 것&lt;/strong&gt;입니다. 시각적으로 아름답게, 시각적으로 검토, 아름답게 보이지 않는다면, 나란히 놓고 어느 쪽이 더 &lt;strong&gt;좋아 보이는지&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;플레이할 수 있어야 한다&amp;quot;는 말은 한 번도 없습니다. 이 사실이 나중에 정확히 그대로 돌아옵니다.&lt;/p&gt;
&lt;h2 id="어떤-게임인가"&gt;어떤 게임인가
&lt;/h2&gt;&lt;p&gt;조차장은 화물열차를 재배열하는 곳입니다. 도착한 화차를 목적지별로 갈라서 다시 편성합니다. 이 작업을 &lt;strong&gt;입환(入換, shunting)&lt;/strong&gt; 이라고 부릅니다.&lt;/p&gt;
&lt;p&gt;게임은 그걸 그대로 옮겼습니다. 화면 아래에 주문서가 있습니다. &amp;ldquo;출발선에 이 순서로&amp;rdquo; — 유개차, 유조차, 호퍼차. 측선에 흩어진 화차를 그 순서대로 출발선에 세우면 클리어입니다.&lt;/p&gt;
&lt;p&gt;&lt;video src="play.mp4" poster="poster.webp" autoplay muted loop playsinline controls preload="metadata" style="width:100%;max-width:960px;border-radius:12px;display:block;margin:1.4rem auto 0.6rem;"&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;첫 레벨 클리어 전체(3배속). 측선으로 가서 물고 → 출발선에 밀어 넣고 → 분리하고 → 기관차만 빠져나옵니다. 3수.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;조작은 &lt;strong&gt;두 가지뿐&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;선로를 탭한다.&lt;/strong&gt; 기관차가 그 선로로 갑니다. 도착하면 거기 서 있던 화차 &lt;strong&gt;전부와 자동으로 연결&lt;/strong&gt;됩니다. 이게 1수입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;연결기를 자른다.&lt;/strong&gt; 편성 어디서든 끊을 수 있습니다. 끊긴 뒤쪽은 그 자리에 남고, 기관차는 앞쪽만 데리고 갑니다. &lt;strong&gt;수를 소모하지 않습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;끝입니다. 밀기 버튼도, 당기기 버튼도, 속도 조절도 없습니다.&lt;/p&gt;
&lt;h2 id="규칙은-두-개뿐인데-왜-어려운가"&gt;규칙은 두 개뿐인데 왜 어려운가
&lt;/h2&gt;&lt;p&gt;측선은 &lt;strong&gt;막다른 길&lt;/strong&gt;입니다. 한쪽 끝에 차막이가 있어서 드나드는 문이 하나뿐입니다. 자료구조로 말하면 &lt;strong&gt;스택&lt;/strong&gt;입니다. 먼저 넣은 화차가 가장 안쪽에 갇히고, 꺼낼 때는 나중에 넣은 것부터 나옵니다.&lt;/p&gt;
&lt;p&gt;그래서 순서를 바꾸려면 &lt;strong&gt;다른 측선을 임시 보관소로 써야 합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;두 번째 레벨이 이 개념만 가르칩니다. 이름은 &amp;ldquo;거꾸로 선 두 량&amp;quot;입니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;시작: 측선1 │ 🟥 🩶 목표: 출발선 │ 🩶 🟥
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;빨강과 회색이 순서대로 서 있는데 주문서는 반대입니다. 그냥 물고 가서 놓으면 순서가 그대로입니다. 하나를 다른 측선에 잠시 내려놓고, 다른 하나를 먼저 출발선에 넣고, 내려놓은 걸 다시 가져와야 합니다. 이게 &lt;strong&gt;par 5&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;여기에 제약이 하나 더 붙습니다. &lt;strong&gt;인상선(引上線)&lt;/strong&gt; — 측선 사이를 오갈 때 반드시 거쳐야 하는 짧은 선로입니다. 여기 들어갈 수 있는 량 수가 정해져 있습니다.&lt;/p&gt;
&lt;p&gt;8레벨부터 이 값이 3으로 조여집니다. 화차 5량을 한꺼번에 끌고 나올 수 없다는 뜻입니다. &lt;strong&gt;나눠서 옮겨야 합니다.&lt;/strong&gt; 이때부터 난이도가 확 오릅니다.&lt;/p&gt;
&lt;p&gt;그리고 마지막 한 가지. 화차를 다 놓아도 &lt;strong&gt;기관차가 출발선에 남아 있으면 끝나지 않습니다.&lt;/strong&gt; 실제 철도에서 기관차를 분리하고 되돌아가는 동작이라 그렇게 만들었는데, 이게 나중에 문제가 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img alt="선로 이름표와 하단 편성 바" class="gallery-image" data-flex-basis="321px" data-flex-grow="134" height="1044" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-7/ui.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-7/ui_hu_584ed401ccc8068c.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-7/ui.webp 1400w" width="1400"&gt;
&lt;em&gt;선로마다 번호·이름·현재량/용량이 붙어 있고, 하단 편성 바에서 가위(✂)로 원하는 지점을 자릅니다.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="14개-레벨"&gt;14개 레벨
&lt;/h2&gt;&lt;p&gt;레벨은 손으로 짰고, 각 레벨의 &lt;strong&gt;par(최소 이동 수)는 전부 탐색으로 검증&lt;/strong&gt;했습니다. 하드코딩한 값과 실제 최적해가 하나도 어긋나지 않습니다.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;구간&lt;/th&gt;
					&lt;th&gt;화차&lt;/th&gt;
					&lt;th&gt;par&lt;/th&gt;
					&lt;th&gt;가르치는 것&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;1~3&lt;/td&gt;
					&lt;td&gt;2~3량&lt;/td&gt;
					&lt;td&gt;3~6&lt;/td&gt;
					&lt;td&gt;조작, 후입선출, 부분 분리&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;4~7&lt;/td&gt;
					&lt;td&gt;4~5량&lt;/td&gt;
					&lt;td&gt;6~9&lt;/td&gt;
					&lt;td&gt;측선 3개 활용&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;8~11&lt;/td&gt;
					&lt;td&gt;5~6량&lt;/td&gt;
					&lt;td&gt;10~13&lt;/td&gt;
					&lt;td&gt;인상선 3량 제약&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;12~14&lt;/td&gt;
					&lt;td&gt;6~8량&lt;/td&gt;
					&lt;td&gt;14~19&lt;/td&gt;
					&lt;td&gt;측선 용량 비대칭&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;이름도 붙였습니다. 아침 첫 입환, 거꾸로 선 두 량, 자갈 열차, 짧은 인상선, 노을 진 조차장, 막차 준비, 야간 조성, 마지막 열차. &lt;strong&gt;시간대가 아침에서 밤으로 흐릅니다.&lt;/strong&gt; 뒤로 갈수록 해가 지고 마지막 레벨들은 가로등이 켜진 밤입니다.&lt;/p&gt;
&lt;p&gt;풀이가 막히면 힌트가 최적 경로의 다음 한 수를 알려줍니다. 최적임을 보장하려고 매번 탐색을 돌리는데, 마지막 레벨(8량)에서 1.5초가 걸렸습니다. 게임이 멈춘 것처럼 보입니다. 상태를 정수로 인코딩하고 역방향 인덱스를 캐싱해서 &lt;strong&gt;최대 블로킹을 7.9밀리초로&lt;/strong&gt; 줄였습니다.&lt;/p&gt;
&lt;h2 id="에셋-0개로-만들기"&gt;에셋 0개로 만들기
&lt;/h2&gt;&lt;p&gt;클로드 오브 듀티에서 제일 인상적이었던 게 이 부분이라 그대로 따라 했습니다. 이미지 파일도, 사운드 파일도, 3D 모델 파일도 없습니다. &lt;strong&gt;전부 코드가 만듭니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;텍스처.&lt;/strong&gt; 자갈은 보로노이 셀을 깔고 셀마다 밝기를 흩뜨립니다. 침목은 방향성 노이즈로 나뭇결을 내고 능선 노이즈로 갈라짐을 넣습니다. 화차 도장은 베이스 컬러 위에 패널 라인·용접 비드·리벳 격자를 높이맵으로 얹고, 모서리에서 페인트가 벗겨져 프라이머와 녹이 드러나게 하고, 리벳에서 녹물이 아래로 흘러내리게 하고, 하단 30%에 흙먼지를 깝니다. 이 높이맵을 소벨 필터로 미분해 노멀맵을 뽑습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;지오메트리.&lt;/strong&gt; 레일은 실제 59kg 레일 단면(넓은 저부 → 잘록한 복부 → 버섯 머리)을 그려서 선로 곡선을 따라 압출합니다. 분기기는 텅레일·크로싱·가드레일·전환간까지 실제 부품으로 만들어서 전환할 때 블레이드가 움직입니다. 화차는 6종인데 &lt;strong&gt;색을 지워도 실루엣만으로 구별&lt;/strong&gt;되게 했습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;소리.&lt;/strong&gt; 연결 충격음은 노이즈 버스트를 340Hz·820Hz·1900Hz 밴드패스 세 개에 병렬로 통과시켜 금속 공진을 만들고, 60Hz 사인 감쇠로 저역 &amp;lsquo;쿵&amp;rsquo;을 얹습니다. 디젤 엔진은 톱니 오실레이터 스택에 실린더 발화 리듬을 진폭 변조로 걸었습니다. 15종을 이렇게 합성했습니다.&lt;/p&gt;
&lt;p&gt;의존성은 &lt;strong&gt;Three.js 하나&lt;/strong&gt;뿐이고 그마저 파일 하나로 넣어뒀습니다. 빌드 스텝이 없습니다. HTML 파일을 열면 그냥 돕니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;움직임&lt;/strong&gt;에도 공을 들였습니다. 연결기마다 유격이 있어서 기관차가 출발하면 뒤 차량이 자기 앞 유격이 다 풀린 뒤에야 끌려옵니다. 정지할 때는 반대로 뒤에서부터 밀려와 닫힙니다. 파도가 편성을 타고 흐릅니다.&lt;/p&gt;
&lt;h2 id="그런데-3라운드가-수렴하지-않았다"&gt;그런데 3라운드가 수렴하지 않았다
&lt;/h2&gt;&lt;p&gt;여기까지가 의도였습니다. 실제로는 이랬습니다.&lt;/p&gt;
&lt;p&gt;설계 문서를 쓰고 에이전트 13명에게 모듈을 하나씩 맡겨 동시에 짰습니다. 약 24,000줄이 나왔습니다. 그다음 &amp;ldquo;매우 엄격한 아트 디렉터&amp;rdquo; 심사 에이전트를 붙여 통과할 때까지 고치고 다시 심사받는 루프를 돌렸습니다.&lt;/p&gt;
&lt;p&gt;3라운드, 에이전트 33명 투입. 점수는 이렇게 움직였습니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1라운드 4.18 / 10
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2라운드 4.92
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;3라운드 4.50 ← 오히려 내려감
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="심사위원-3명이-똑같이-오진했다"&gt;심사위원 3명이 똑같이 오진했다
&lt;/h3&gt;&lt;p&gt;세 라운드 내내 반복된 지적이 있었습니다.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&amp;ldquo;그림자가 없다. 앰비언트 오클루전이 화면에서 사라졌다. 물체가 전부 떠 보인다.&amp;rdquo;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;그래서 3라운드 내내 AO 반경과 그림자 bias를 만졌습니다. 점수는 안 올랐습니다.&lt;/p&gt;
&lt;p&gt;이상해서 직접 계측했습니다. 기능을 껐다 켜면서 화면이 얼마나 바뀌는지 픽셀 단위로 셌습니다.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;토글&lt;/th&gt;
					&lt;th&gt;변화 픽셀&lt;/th&gt;
					&lt;th&gt;최대 차이&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;SSAO 끔 → 켬&lt;/td&gt;
					&lt;td&gt;19.9%&lt;/td&gt;
					&lt;td&gt;164&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;그림자 끔 → 켬&lt;/td&gt;
					&lt;td&gt;12.5%&lt;/td&gt;
					&lt;td&gt;248&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;둘 다 멀쩡히 돌고 있었습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;진짜 원인은 최종 합성의 톤 커브였습니다. 히스토그램을 재보니 &lt;code&gt;p99 = 179&lt;/code&gt;, 클리핑 0.000%. 씬에서 &lt;strong&gt;가장 밝은 픽셀이 70% 회색&lt;/strong&gt;이었습니다. 순백이 없으니 AO와 그림자가 만든 대비가 전부 뭉개진 것입니다.&lt;/p&gt;
&lt;p&gt;심사위원들은 &lt;strong&gt;증상은 정확히 봤지만 원인을 틀렸습니다.&lt;/strong&gt; 저는 그 진단을 그대로 브리핑에 넣어 33명을 엉뚱한 곳으로 보냈습니다.&lt;/p&gt;
&lt;h3 id="그리고-아무도-게임을-해보지-않았다"&gt;그리고 아무도 게임을 해보지 않았다
&lt;/h3&gt;&lt;p&gt;시각 심사를 마치고 게임을 열었습니다. 첫 반응이 이랬습니다.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&amp;ldquo;게임 방법을 모르겠는데&amp;rdquo;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;이어서 &amp;ldquo;클리어 방법이 뭔데&amp;rdquo;, &amp;ldquo;기관차를 어떻게 빼는데&amp;rdquo;, &amp;ldquo;미션이 뭐였는데&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;화면 텍스트를 전부 덤프해봤습니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;LV 01 | 아침 첫 입환 | 0 / par 3 | 편성 목표 | 0/2 | 서 동
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;규칙이 어디에도 없었습니다.&lt;/strong&gt; 레벨 힌트 텍스트는 데이터에 있었고 튜토리얼 함수도 구현돼 있었는데, 화면까지 오지 못했습니다.&lt;/p&gt;
&lt;p&gt;심사 에이전트 9명이 3라운드에 걸쳐 12개 축으로 채점했습니다. 형태, 재질, 텍스처, 라이팅, 그림자, AO, 색, 구도, 환경, 포스트, UI, 통일감. &lt;strong&gt;UI 축에서 6~7점을 줬습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;아무도 &amp;ldquo;규칙을 모르는 사람이 이걸 플레이할 수 있나&amp;quot;를 묻지 않았습니다.&lt;/p&gt;
&lt;p&gt;여기서 앞의 프롬프트로 돌아갑니다. 저는 &amp;ldquo;시각적으로 검토하라&amp;quot;고 했고, &amp;ldquo;AAA급으로 보이지 않는다면 계속 개선하라&amp;quot;고 했습니다. &lt;strong&gt;에이전트들은 정확히 시킨 대로 했습니다.&lt;/strong&gt; 12개 축은 그 프롬프트를 충실히 옮긴 것이었고, 그래서 12개 축 전부가 보는 것에 관한 것이었습니다.&lt;/p&gt;
&lt;p&gt;빠진 건 에이전트의 성실성이 아니라 &lt;strong&gt;제 질문&lt;/strong&gt;이었습니다.&lt;/p&gt;
&lt;p&gt;검증 방식을 바꿨습니다. 소스 코드 열람을 금지한 &amp;ldquo;처음 보는 플레이어&amp;rdquo; 에이전트를 붙였습니다. 화면 스크린샷과 화면 텍스트만 보고 실제로 클리어해야 통과. 붙이자마자 진짜 문제가 쏟아졌습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;화차 분리가 3D 공간의 10px짜리 투명한 구를 클릭하는 게 유일한 방법이었습니다. 모바일에선 불가능합니다.&lt;/li&gt;
&lt;li&gt;숫자키 매핑이 레벨마다 바뀌었습니다. 어떤 레벨에선 &lt;code&gt;4&lt;/code&gt;가 출발선인데 다른 레벨에선 인상선이었습니다.&lt;/li&gt;
&lt;li&gt;&amp;ldquo;기관차를 빼야 끝난다&amp;quot;는 규칙을 알려주지 않아 화차를 다 놓고도 막혔습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;고친 뒤 붙인 테스터 2명은 소스 없이 3개 레벨을 전부 클리어했습니다. 한 명은 par로 풀었습니다.&lt;/p&gt;
&lt;p&gt;&lt;img alt="단계별 튜토리얼 코치 카드" class="gallery-image" data-flex-basis="321px" data-flex-grow="134" height="1044" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-7/tutorial.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-7/tutorial_hu_7159519e46a8cb31.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-7/tutorial.webp 1400w" width="1400"&gt;
&lt;em&gt;단계별 코치 카드. 손가락 커서가 다음에 누를 곳을 짚고, 건너뛰기 버튼이 항상 있습니다.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="결과"&gt;결과
&lt;/h2&gt;&lt;p&gt;병목을 계측으로 가르고 나니 나머지는 기계적이었습니다.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;전&lt;/th&gt;
					&lt;th&gt;후&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;모바일 fps (중급 폰)&lt;/td&gt;
					&lt;td&gt;6.9&lt;/td&gt;
					&lt;td&gt;약 26&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;드로우콜&lt;/td&gt;
					&lt;td&gt;2,670&lt;/td&gt;
					&lt;td&gt;280&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;품질 전환 정지&lt;/td&gt;
					&lt;td&gt;10,299ms&lt;/td&gt;
					&lt;td&gt;500ms&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;힌트 응답&lt;/td&gt;
					&lt;td&gt;499ms (그마저 오답)&lt;/td&gt;
					&lt;td&gt;7.9ms&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;줌 한 칸&lt;/td&gt;
					&lt;td&gt;거리의 0.12%&lt;/td&gt;
					&lt;td&gt;10%&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;드로우콜이 2,670이었던 이유는 풀 뭉치 227개와 잡초 206개가 &lt;strong&gt;개별 메시&lt;/strong&gt;로 흩어져 각각 본 패스와 그림자 패스로 두 번씩 그려졌기 때문입니다. 인스턴싱으로 묶으니 280이 됐습니다.&lt;/p&gt;
&lt;p&gt;여기서도 계측이 방향을 정했습니다. 해상도를 4분의 1로 줄여도 fps가 안 올랐습니다. 픽셀이 아니라 CPU 문제라는 뜻이었고, 그래서 후처리가 아니라 드로우콜을 팠습니다.&lt;/p&gt;
&lt;p&gt;&lt;img alt="AI 게임 랩 허브 — 구분선 아래 가로로 긴 카드" class="gallery-image" data-flex-basis="227px" data-flex-grow="94" height="1267" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-7/hub.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-7/hub_hu_22ba7f2d2545bbb.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-7/hub.webp 1200w" width="1200"&gt;
&lt;em&gt;미니게임과 결이 달라서 구분선 아래에 가로로 길게 따로 놓았습니다.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="개발-후기"&gt;개발 후기
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;만드는 데 든 시간보다 진단하는 데 든 시간이 훨씬 길었습니다.&lt;/strong&gt; 24,000줄이 나오는 데는 반나절이 안 걸렸습니다. 그런데 &amp;ldquo;왜 안 예쁜가&amp;quot;를 알아내는 데 사흘이 걸렸고, 그중 이틀은 틀린 진단을 좇느라 썼습니다.&lt;/p&gt;
&lt;p&gt;에이전트를 많이 붙이면 빨라집니다. 그건 사실입니다. 13명이 동시에 짜서 하루 만에 게임 하나가 나왔습니다. 문제는 &lt;strong&gt;틀린 방향으로도 똑같이 빨라진다&lt;/strong&gt;는 것입니다. &amp;ldquo;그림자가 없다&amp;quot;는 오진 하나로 33명이 사흘 치 작업을 엉뚱한 곳에 쏟았습니다. 사람 혼자였으면 세 번째 시도쯤에 &amp;ldquo;이상한데?&amp;rdquo; 하고 멈췄을 텐데, 에이전트는 시킨 대로 끝까지 갑니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;그래서 계측이 전부였습니다.&lt;/strong&gt; 이 프로젝트에서 잘 풀린 순간은 전부 제가 직접 숫자를 재서 브리핑에 넣었을 때였습니다. &amp;ldquo;AO를 켜고 끄면 화면의 19.9%가 바뀐다&amp;rdquo;, &amp;ldquo;해상도를 1/4로 줄여도 fps가 안 오른다&amp;rdquo; — 이런 문장 하나가 들어가면 한 라운드에 해결됐습니다. 반대로 &amp;ldquo;화면이 칙칙해 보인다&amp;rdquo; 같은 인상을 넘기면 몇 라운드를 헤맸습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI 심사는 문제 발견에는 좋고 원인 진단에는 나쁩니다.&lt;/strong&gt; 심사위원들이 &amp;ldquo;뭔가 잘못됐다&amp;quot;고 한 건 매번 맞았습니다. 다만 &amp;ldquo;왜&amp;quot;는 매번 틀렸습니다. 이 둘을 구분해서 쓰면 꽤 쓸 만한 도구입니다. 구분하지 않으면 비싼 소음입니다.&lt;/p&gt;
&lt;p&gt;그리고 제일 뼈아팠던 것. &lt;strong&gt;사용자 한 명이 심사 에이전트 9명을 이겼습니다.&lt;/strong&gt; &amp;ldquo;게임 방법을 모르겠는데&amp;rdquo; 한마디가 아홉 번의 심사보다 정확했습니다. 그 한마디를 듣기 전까지 저는 이 게임이 &amp;ldquo;보기엔 좀 아쉽지만 게임으로는 되는&amp;rdquo; 상태라고 믿고 있었습니다. 사실은 정반대였습니다. &lt;strong&gt;보기엔 그럭저럭이고 게임으로는 안 되는&lt;/strong&gt; 상태였습니다.&lt;/p&gt;
&lt;p&gt;클로드 오브 듀티에 대한 평가 중에 이런 게 있었습니다. &amp;ldquo;돌아가긴 하는데 학생 프로젝트처럼 보이지, 출시할 물건 같지는 않다.&amp;rdquo; 직접 해보니 그 말이 정확히 무슨 뜻인지 알겠습니다. &lt;strong&gt;돌아가는 것과 완성된 것 사이의 거리가 생각보다 멉니다.&lt;/strong&gt; 그리고 그 거리는 코드량으로 좁혀지지 않습니다.&lt;/p&gt;
&lt;h2 id="남은-것"&gt;남은 것
&lt;/h2&gt;&lt;p&gt;아직 안 끝났습니다. 모바일 부팅이 79초 걸립니다. 부팅 직후 검은 화면이 한 번 번쩍이는데 원인만 알고 안 고쳤습니다. 열차의 무게감 연출도 절반쯤입니다. 사운드는 전부 코드로 합성했는데 &lt;strong&gt;아직 아무도 귀로 들어본 적이 없습니다&lt;/strong&gt; — 파형은 측정했지만 그건 다른 문제입니다.&lt;/p&gt;
&lt;p&gt;그래도 게임은 돌아가고, 규칙을 모르는 사람이 클리어할 수 있습니다. 거기까지는 왔습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/shunting/" &gt;👉 조차장 플레이하기&lt;/a&gt;&lt;/strong&gt; · &lt;a class="link" href="https://junstellar.github.io/games/" &gt;AI 게임 랩 전체 보기&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="참고"&gt;참고
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.navsplace.net/claude-of-duty-single-prompt-fps-20260726/" target="_blank" rel="noopener"
 &gt;Claude of Duty: 55,000 lines of 3D game code from a single prompt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://enterprisedna.co/resources/ai-pulse/ai-pulse-2026-07-27-claude-of-duty-one-shot-fps-demo-gets-a-skeptical-reality-ch/" target="_blank" rel="noopener"
 &gt;&amp;ldquo;Claude of Duty&amp;rdquo; one-shot FPS demo gets a skeptical reality check&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://explainx.ai/blog/claude-of-duty-opus-5-procedural-fps-july-2026" target="_blank" rel="noopener"
 &gt;Claude of Duty — Opus 5 Procedural FPS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>AI로 게임 만들기 #6 — 먼지에서 은하까지, 합쳐서 진화시키는 퍼즐 (공개빌드 #6)</title><link>https://junstellar.github.io/p/ai-game-lab-build-6/</link><pubDate>Mon, 03 Aug 2026 09:00:00 +0900</pubDate><guid>https://junstellar.github.io/p/ai-game-lab-build-6/</guid><description>&lt;p&gt;&lt;a class="link" href="https://junstellar.github.io/p/ai-game-lab-build-5/" &gt;지난 편&lt;/a&gt;의 종이 농구에 이어, 여섯 번째 게임은 &lt;strong&gt;우주 진화&lt;/strong&gt;입니다. 같은 천체끼리 합치면 한 단계씩 진화하는 낙하 퍼즐이에요. ✨먼지에서 시작해서 🌌은하까지 갑니다.&lt;/p&gt;
&lt;p&gt;이번 편의 진짜 이야기는 게임 자체보다 &lt;strong&gt;&amp;ldquo;너무 쉽다&amp;quot;는 피드백을 어떻게 잡았나&lt;/strong&gt;입니다. 결론부터 말하면, 감으로 고치지 않고 &lt;strong&gt;시뮬레이션을 돌렸어요.&lt;/strong&gt;&lt;/p&gt;
&lt;video autoplay loop muted playsinline preload="metadata" poster="/p/ai-game-lab-build-6/space-merge-play-poster.webp" style="width:100%;max-width:420px;display:block;margin:1.2rem auto;border-radius:14px;box-shadow:0 6px 24px rgba(0,0,0,.25)"&gt;
 &lt;source src="https://junstellar.github.io/p/ai-game-lab-build-6/space-merge-play.mp4" type="video/mp4"&gt;
&lt;/video&gt;
&lt;p&gt;👉 &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/space-merge/" &gt;우주 진화 직접 하러 가기&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="어떤-게임이냐면"&gt;어떤 게임이냐면
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;탭해서 천체를 떨어뜨리면&lt;/strong&gt;, 같은 것끼리 닿을 때 &lt;strong&gt;합쳐지며 진화&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;✨먼지 → ☄️운석 → 소행성 → 🌑달 → 화성 → 🌍지구 → 목성 → 🪐토성 → 갈색왜성 → ⭐항성 → 🌌&lt;strong&gt;은하&lt;/strong&gt; (11단계)&lt;/li&gt;
&lt;li&gt;연달아 합치면 &lt;strong&gt;🔥ON FIRE&lt;/strong&gt;(2배 점수), &lt;strong&gt;보관(HOLD)&lt;/strong&gt; 한 칸에 넣어뒀다 나중에 쓰기&lt;/li&gt;
&lt;li&gt;통 위로 넘치면 끝. 📅&lt;strong&gt;데일리 챌린지&lt;/strong&gt;는 그날 모두 같은 순서로 나옵니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="ON FIRE — 연쇄로 합치면 2배 점수" class="gallery-image" data-flex-basis="157px" data-flex-grow="65" height="1095" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-6/space-merge-fire.webp" width="720"&gt;&lt;/p&gt;
&lt;h2 id="천체는-우리-집-그래픽카드가-그렸다"&gt;천체는 우리 집 그래픽카드가 그렸다
&lt;/h2&gt;&lt;p&gt;11종 전부 &lt;a class="link" href="https://junstellar.github.io/p/claude-local-image-generation/" &gt;예전에 깔아둔 ComfyUI&lt;/a&gt;로 뽑았습니다. 그런데 생성만으로 끝이 아니었어요:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;배경이 회색인 이미지가 섞여 있어서, 원형으로 자르니 &lt;strong&gt;천체 주위에 회색 접시&lt;/strong&gt;가 생김 → 배경색을 자동 감지해 &lt;strong&gt;투명 처리&lt;/strong&gt;하고 물체에 딱 맞게 다시 크롭&lt;/li&gt;
&lt;li&gt;달이 &lt;strong&gt;초승달처럼 그림자&lt;/strong&gt;가 져서 혼자 어색함 → &amp;ldquo;정면 균일 조명&amp;quot;으로 다시 생성 (세 번 만에 성공)&lt;/li&gt;
&lt;li&gt;화성이 한 장에 &lt;strong&gt;네 개&lt;/strong&gt; 들어가 있기도 했고, 목성은 배경 불꽃까지 딸려와서 더 확대해 잘라냄&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI가 그려준 걸 그대로 쓰는 게 아니라, &lt;strong&gt;게임에 맞게 다듬는 작업이 절반&lt;/strong&gt;이더라고요.&lt;/p&gt;
&lt;h2 id="너무-쉽다--감이-아니라-숫자로-잡기"&gt;&amp;ldquo;너무 쉽다&amp;rdquo; — 감이 아니라 숫자로 잡기
&lt;/h2&gt;&lt;p&gt;처음 만들고 나서 &amp;ldquo;좀 쉬운데?&amp;ldquo;라는 얘기를 들었습니다. 예전 같으면 대충 값을 바꿔봤겠지만, 이번엔 &lt;strong&gt;게임을 자동으로 플레이시켜서 측정&lt;/strong&gt;했어요. 아무렇게나 던지는 플레이어와, 같은 천체 근처에 놓는 플레이어를 각각 여러 판 돌렸습니다.&lt;/p&gt;
&lt;p&gt;결과가 충격적이었어요:&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;아무렇게나 던져도 매판 9번째 단계까지 도달하고, 한 판이 156번이나 이어짐&lt;/strong&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;원인도 숫자로 나왔습니다. &lt;strong&gt;합칠 때마다 공간이 22%씩 남고 있었어요.&lt;/strong&gt; 반지름이 1.25배씩 커지게 해놨는데, 그러면 두 개(면적 2)가 합쳐져도 1.56밖에 안 되거든요. 즉 &lt;strong&gt;합칠수록 통이 비어서&lt;/strong&gt; 영원히 안 차는 구조였습니다. 면적이 유지되려면 √2 ≈ 1.41배여야 했어요.&lt;/p&gt;
&lt;h2 id="12가지-조합을-다-돌려보고-고름"&gt;12가지 조합을 다 돌려보고 고름
&lt;/h2&gt;&lt;p&gt;반지름·통 크기·낙하 분포를 조합한 &lt;strong&gt;12가지 설정을 전부 시뮬레이션&lt;/strong&gt;해서 제일 나은 걸 골랐습니다. 기준은 두 가지였어요 — &lt;em&gt;판이 너무 늘어지지 않을 것&lt;/em&gt;, 그리고 &lt;em&gt;잘하는 사람이 확실히 더 잘 나올 것&lt;/em&gt;.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;무작위 플레이&lt;/th&gt;
					&lt;th&gt;도달 단계&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;처음&lt;/td&gt;
					&lt;td&gt;156번 만에 종료&lt;/td&gt;
					&lt;td&gt;9단계&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;고친 뒤&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;63번&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;6단계&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;하드&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;48번&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;5단계&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;중간에 재밌는 일도 있었어요. 하드에서 &amp;ldquo;전략적으로 두는 쪽이 아무렇게나 던지는 쪽보다 못하다&amp;quot;는 이상한 결과가 나왔거든요. 표본을 3판에서 8판으로 늘려 다시 돌리니 **그냥 운빨(노이즈)**이었습니다. 3판만 보고 튜닝했으면 엉뚱하게 고칠 뻔했어요.&lt;/p&gt;
&lt;h2 id="하드는-3개를-모아야-합쳐집니다"&gt;하드는 3개를 모아야 합쳐집니다
&lt;/h2&gt;&lt;p&gt;난이도를 이지/노멀/하드로 나눴는데, &lt;strong&gt;하드는 규칙 자체가 다릅니다&lt;/strong&gt; — 2개가 아니라 &lt;strong&gt;3개가 붙어야&lt;/strong&gt; 합쳐져요. 트리플 타운 같은 방식이죠. 이거 하나로 점수가 1/4로 떨어질 만큼 어려워집니다.&lt;/p&gt;
&lt;p&gt;구현은 맞닿은 같은 등급 천체들을 &lt;strong&gt;하나의 덩어리로 묶어서&lt;/strong&gt; 3개 이상이면 합치는 방식이라, 일렬이든 삼각형이든 붙어 있기만 하면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img alt="게임 결과 화면 — 최고 단계와 기록" class="gallery-image" data-flex-basis="157px" data-flex-grow="65" height="1095" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-6/space-merge-result.webp" width="720"&gt;&lt;/p&gt;
&lt;h2 id="이번-편에서-배운-것"&gt;이번 편에서 배운 것
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;&amp;ldquo;재미있나?&amp;ldquo;는 취향이지만, &amp;ldquo;너무 쉬운가?&amp;ldquo;는 측정할 수 있습니다.&lt;/strong&gt; 게임을 스스로 플레이하게 만들어 몇 판 돌려보면 숫자가 나오고, 그 숫자를 보고 고치면 훨씬 빠르고 정확해요. 이번엔 그 덕에 **&amp;ldquo;합칠수록 공간이 남는다&amp;rdquo;**는, 그냥 플레이해서는 절대 몰랐을 원인을 찾았습니다.&lt;/p&gt;
&lt;h2 id="이제-게임-랩이-6개"&gt;이제 게임 랩이 6개
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;게임&lt;/th&gt;
					&lt;th&gt;성격&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;🎨&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/guess-image/" &gt;AI 그림 맞히기&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;머리 (퀴즈)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🟩&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/hangul-word/" &gt;한글 단어 맞히기&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;습관 (데일리)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🍰&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/stack-tower/" &gt;디저트 탑 쌓기&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;손맛 (아케이드)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🕹️&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/dodge/" &gt;낙하 회피&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;반사신경 (액션)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🗑️&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/paper-hoop/" &gt;종이 농구&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;조준 (물리)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🪐&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/space-merge/" &gt;우주 진화&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;최고점 (병합 퍼즐)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/space-merge/" &gt;우주 진화 한 판&lt;/a&gt;&lt;/strong&gt; 해보시고 어디까지 갔는지 알려주세요. 저는 아직 &lt;strong&gt;은하&lt;/strong&gt;를 못 봤습니다. 🌌&lt;/p&gt;
&lt;p&gt;&lt;em&gt;다음 편엔 일곱 번째 게임을 들고 오겠습니다.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>AI로 게임 만들기 #5 — 휴지통에 종이 던지기, 근데 '진짜 게임처럼' (공개빌드 #5)</title><link>https://junstellar.github.io/p/ai-game-lab-build-5/</link><pubDate>Tue, 28 Jul 2026 09:00:00 +0900</pubDate><guid>https://junstellar.github.io/p/ai-game-lab-build-5/</guid><description>&lt;p&gt;&lt;a class="link" href="https://junstellar.github.io/p/ai-game-lab-build-4/" &gt;지난 편&lt;/a&gt;까지 게임 랩엔 네 개가 쌓였습니다 — 🎨 그림 퀴즈, 🟩 한글 워들, 🍰 디저트 탑, 🕹️ 낙하 회피. 다섯 번째는 &lt;strong&gt;종이 농구&lt;/strong&gt;예요. 근데 이번 편은 좀 다릅니다. 새 게임을 하나 더 늘리기보다, &lt;strong&gt;게임 하나를 &amp;ldquo;그냥 되는&amp;rdquo; 상태에서 &amp;ldquo;진짜 게임처럼&amp;quot;까지 다듬는 것&lt;/strong&gt;에 집중했거든요.&lt;/p&gt;
&lt;p&gt;말보다 보는 게 빠르니 &lt;strong&gt;플레이 영상&lt;/strong&gt;부터 👇&lt;/p&gt;
&lt;video autoplay loop muted playsinline preload="metadata" poster="/p/ai-game-lab-build-5/paper-hoop-play-poster.webp" style="width:100%;max-width:420px;display:block;margin:1.2rem auto;border-radius:14px;box-shadow:0 6px 24px rgba(0,0,0,.25)"&gt;
 &lt;source src="https://junstellar.github.io/p/ai-game-lab-build-5/paper-hoop-play.mp4" type="video/mp4"&gt;
&lt;/video&gt;
&lt;p&gt;👉 &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/paper-hoop/" &gt;종이 농구 직접 하러 가기&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="어떤-게임이냐면"&gt;어떤 게임이냐면
&lt;/h2&gt;&lt;p&gt;3초컷입니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;구겨진 종이를 &lt;strong&gt;화면 아무 곳이나 당겼다 놓아&lt;/strong&gt; 휴지통에 골인 (공을 안 짚어도 돼요)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;가운데로 쏙 = 스월(2점)&lt;/strong&gt;, 림이나 백보드 맞고 들어가면 &lt;strong&gt;뱅크(1점)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;바람&lt;/strong&gt; 주의! 연속 골인하면 &lt;strong&gt;🔥 ON FIRE&lt;/strong&gt;(2배 점수)&lt;/li&gt;
&lt;li&gt;3번 놓치면 끝. 점수는 누적·최고 기록·연속 출석까지 다른 게임처럼 쌓여요&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="처음엔-게임-같지가-않았다"&gt;처음엔 &amp;ldquo;게임 같지가 않았다&amp;rdquo;
&lt;/h2&gt;&lt;p&gt;솔직히 첫 버전은 별로였어요. 발사가 너무 빨라서 종이가 &lt;strong&gt;번쩍 사라지는&lt;/strong&gt; 수준이라 손맛이 0이었죠. 그래서:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;비행을 &lt;strong&gt;2배 느리게&lt;/strong&gt;(궤적·난이도는 그대로, 보이는 속도만)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;잔상 꼬리&lt;/strong&gt; + 바닥 그림자 + 빗나가면 &lt;strong&gt;바닥에 튕기는&lt;/strong&gt; 마무리&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이것만 넣었는데 바로 &amp;ldquo;오, 게임이네&amp;rdquo; 소리가 나왔습니다. &lt;strong&gt;완성도의 8할은 손맛&lt;/strong&gt;이더라고요.&lt;/p&gt;
&lt;h2 id="그다음-고퀄로-끝까지-밀어봤다"&gt;그다음 &amp;lsquo;고퀄&amp;rsquo;로 끝까지 밀어봤다
&lt;/h2&gt;&lt;p&gt;여기서 멈춰도 됐는데, 어디까지 되나 궁금해서 다 넣어봤어요:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;🔊 &lt;strong&gt;사운드&lt;/strong&gt; — 에셋 파일 &lt;strong&gt;0개&lt;/strong&gt;. WebAudio로 발사 &amp;ldquo;휘익&amp;rdquo;, 스월 &amp;ldquo;사악~&amp;rdquo;, 뱅크 &amp;ldquo;통-딩&amp;quot;을 &lt;strong&gt;코드로 합성&lt;/strong&gt;했습니다&lt;/li&gt;
&lt;li&gt;🏀 &lt;strong&gt;물리&lt;/strong&gt; — 좌우 림에 튕겨 덜덜거리는 &lt;strong&gt;in&amp;amp;out&lt;/strong&gt;, 뒤 &lt;strong&gt;백보드 뱅크샷&lt;/strong&gt;, 입구엔 &lt;strong&gt;천 네트가 출렁&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;🔥 &lt;strong&gt;ON FIRE&lt;/strong&gt; — 연속 골인으로 게이지가 차고, 만렙이면 &lt;strong&gt;공에 불붙고 2배&lt;/strong&gt; (NBA Jam 오마주)&lt;/li&gt;
&lt;li&gt;🎯 &lt;strong&gt;3점 라인&lt;/strong&gt; — 먼 통일수록 고득점. **백스핀(마그누스)**으로 궤적이 살짝 휘어요&lt;/li&gt;
&lt;li&gt;✨ 그 외 히트스톱·카메라 펀치줌·리본 잔상 같은 자잘한 &lt;strong&gt;연출 주스&lt;/strong&gt;까지&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="우리-집-그래픽카드가-배경을-그렸다"&gt;우리 집 그래픽카드가 배경을 그렸다
&lt;/h2&gt;&lt;p&gt;제일 좋아하는 부분이에요. 배경 사무실 일러스트를 &lt;strong&gt;집에 있는 그래픽카드(ComfyUI/SDXL)로 직접 뽑아서&lt;/strong&gt; 깔았습니다. &lt;a class="link" href="https://junstellar.github.io/p/claude-local-image-generation/" &gt;예전에 노트북에 ComfyUI를 깔아둔 것&lt;/a&gt;을 이렇게 써먹네요. 브라우저에서 바로 돌아가는 가벼운 게임인데도 배경은 손그림풍이죠. 여기에 &lt;strong&gt;낮/밤 테마&lt;/strong&gt;까지 붙였어요.&lt;/p&gt;
&lt;p&gt;&lt;img alt="밤 테마 + ON FIRE — 불붙은 종이공" class="gallery-image" data-flex-basis="185px" data-flex-grow="77" height="930" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-5/paper-hoop-fire.webp" width="720"&gt;&lt;/p&gt;
&lt;h2 id="오래-붙잡는-장치들"&gt;오래 붙잡는 장치들
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;📅 &lt;strong&gt;데일리 챌린지&lt;/strong&gt; — 날짜 시드라 그날은 &lt;strong&gt;모두 같은 통·바람&lt;/strong&gt;. &amp;ldquo;오늘 몇 점?&amp;rdquo; 공유용&lt;/li&gt;
&lt;li&gt;🏅 &lt;strong&gt;업적&lt;/strong&gt; · 🖼️ &lt;strong&gt;결과를 이미지 카드로 공유&lt;/strong&gt; · ▶️ &lt;strong&gt;베스트 샷 리플레이&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;♿ &lt;strong&gt;접근성&lt;/strong&gt; — 멀미 방지(모션 축소 존중), 색약 안전 파워 표시&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="마지막에-배운-것-작은-ux가-게임을-살린다"&gt;마지막에 배운 것: 작은 UX가 게임을 살린다
&lt;/h2&gt;&lt;p&gt;다 만들고 플레이하다가 치명적인 걸 발견했어요. 슬링샷이 &lt;strong&gt;공(좌하단 구석) 기준&lt;/strong&gt;이라 뒤로 당길 공간이 없었던 거예요. 그래서 **&amp;ldquo;화면 아무 곳이나 짚고 상대적으로 당기기&amp;rdquo;**로 바꾸니 완전히 달라졌습니다. 화려한 기능 20개보다 &lt;strong&gt;이 한 줄짜리 조작 수정&lt;/strong&gt;이 체감이 훨씬 컸어요.&lt;/p&gt;
&lt;h2 id="이제-게임-랩이-5개"&gt;이제 게임 랩이 5개
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;게임&lt;/th&gt;
					&lt;th&gt;성격&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;🎨&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/guess-image/" &gt;AI 그림 맞히기&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;머리 (퀴즈)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🟩&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/hangul-word/" &gt;한글 단어 맞히기&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;습관 (데일리)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🍰&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/stack-tower/" &gt;디저트 탑 쌓기&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;손맛 (아케이드)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🕹️&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/dodge/" &gt;낙하 회피&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;반사신경 (액션)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🗑️&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/paper-hoop/" &gt;종이 농구&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;조준 (물리)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;머리·습관·손맛·반사신경·조준 — &lt;a class="link" href="https://junstellar.github.io/games/" &gt;게임 랩&lt;/a&gt;이 5색이 됐습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/paper-hoop/" &gt;종이 농구 한 판&lt;/a&gt;&lt;/strong&gt; 하고 몇 점인지 알려주세요. 스월로 3연속 넣으면 불붙습니다. 🔥&lt;/p&gt;
&lt;p&gt;&lt;em&gt;다음 편엔 여섯 번째 게임을 들고 오겠습니다.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>AI로 게임 만들기 #4 — 쏟아지는 걸 피하는 '낙하 회피', 그리고 플레이 영상까지 (공개빌드 #4)</title><link>https://junstellar.github.io/p/ai-game-lab-build-4/</link><pubDate>Tue, 21 Jul 2026 09:00:00 +0900</pubDate><guid>https://junstellar.github.io/p/ai-game-lab-build-4/</guid><description>&lt;p&gt;&lt;a class="link" href="https://junstellar.github.io/p/ai-game-lab-build-3/" &gt;지난 편&lt;/a&gt;까지 게임 랩엔 세 개가 쌓였습니다 — 🎨 그림 퀴즈(머리), 🟩 한글 워들(습관), 🍰 디저트 탑(손맛). 딱 하나 비어 있던 게 &lt;strong&gt;순수 액션&lt;/strong&gt;이었어요. 그래서 네 번째는 반사신경으로 하는 게임 — &lt;strong&gt;낙하 회피&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;말로 설명하는 것보다 보는 게 빠르니, 이번엔 &lt;strong&gt;10초 플레이 영상&lt;/strong&gt;부터 올립니다 👇&lt;/p&gt;
&lt;video autoplay loop muted playsinline preload="metadata" poster="/p/ai-game-lab-build-4/dodge-play-poster.webp" style="width:100%;max-width:420px;display:block;margin:1.2rem auto;border-radius:14px;box-shadow:0 6px 24px rgba(0,0,0,.25)"&gt;
 &lt;source src="https://junstellar.github.io/p/ai-game-lab-build-4/dodge-play.mp4" type="video/mp4"&gt;
&lt;/video&gt;
&lt;p&gt;👉 &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/dodge/" &gt;낙하 회피 직접 하러 가기&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="어떤-게임이냐면"&gt;어떤 게임이냐면
&lt;/h2&gt;&lt;p&gt;규칙은 3초컷입니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;🚀 로켓을 &lt;strong&gt;드래그(PC는 ← →)로 좌우&lt;/strong&gt; 이동&lt;/li&gt;
&lt;li&gt;위에서 쏟아지는 &lt;strong&gt;🪨 운석·💣 폭탄을 피하기&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;⭐ 별&lt;/strong&gt;을 먹으면 점수 폭발, &lt;strong&gt;💗 하트&lt;/strong&gt;를 먹으면 생명 회복(최대 5)&lt;/li&gt;
&lt;li&gt;하트 3개로 시작, 부딪히면 하나씩 깎이고 0이면 끝&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;갈수록 빨라집니다.&lt;/strong&gt; 점수 = 버틴 만큼 + 별&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;결과는 &lt;code&gt;🕹️ 낙하 회피 — N점!&lt;/code&gt;으로 공유되고, 최고 기록·연속 출석도 다른 게임들처럼 쌓여요. 조작이 한 손이라 폰에서 특히 잘 맞습니다.&lt;/p&gt;
&lt;h2 id="이번-편의-새로운-것--게임이-스스로-플레이하게-만들기"&gt;이번 편의 새로운 것 — 게임이 스스로 플레이하게 만들기
&lt;/h2&gt;&lt;p&gt;위 영상, 제가 직접 플레이한 게 아니라 &lt;strong&gt;게임이 자기 자신을 플레이한 걸 녹화&lt;/strong&gt;한 거예요. 이번에 처음 해본 건데 방법이 재밌었습니다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;게임에 &lt;strong&gt;간단한 회피 AI&lt;/strong&gt;를 붙였어요 — 매 순간 &amp;ldquo;제일 가까운 폭탄이 왼쪽에 있으면 오른쪽으로, 위험이 없으면 근처 별 쪽으로&amp;rdquo; 로켓을 움직이게&lt;/li&gt;
&lt;li&gt;그 상태로 게임을 돌리면서 &lt;strong&gt;캔버스 화면을 그대로 녹화&lt;/strong&gt;(브라우저의 화면 녹화 기능)&lt;/li&gt;
&lt;li&gt;나온 영상을 &lt;strong&gt;10초짜리 mp4&lt;/strong&gt;로 변환해서 이 글에 임베드&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;즉 &amp;ldquo;게임을 만든 김에, 그 게임한테 스스로 시연까지 시킨&amp;rdquo; 셈이에요. 앞으로 게임마다 이렇게 짧은 플레이 영상을 붙이면 글이 훨씬 생생해지겠더라고요.&lt;/p&gt;
&lt;h2 id="그리고-난이도-한-번-손봄"&gt;그리고 난이도 한 번 손봄
&lt;/h2&gt;&lt;p&gt;처음엔 좀 쉬웠어요(&amp;ldquo;이거 안 죽는데?&amp;rdquo;). 그래서 &lt;strong&gt;낙하 속도와 등장 빈도를 살짝 올려서&lt;/strong&gt;, 초반은 여유롭다가 중반부터 확 조여지게 바꿨습니다. 이런 자잘한 손맛 조정이 액션 게임에선 은근 중요하더라고요.&lt;/p&gt;
&lt;h2 id="이제-게임-랩이-4색-됐습니다"&gt;이제 게임 랩이 4색 됐습니다
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;게임&lt;/th&gt;
					&lt;th&gt;성격&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;🎨&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/guess-image/" &gt;AI 그림 맞히기&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;머리 (퀴즈)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🟩&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/hangul-word/" &gt;한글 단어 맞히기&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;습관 (데일리)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🍰&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/stack-tower/" &gt;디저트 탑 쌓기&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;손맛 (아케이드)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🕹️&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/dodge/" &gt;낙하 회피&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;반사신경 (액션)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;머리·습관·손맛·반사신경 — 이제 어떤 기분에 들어와도 하나쯤 걸리는 &lt;a class="link" href="https://junstellar.github.io/games/" &gt;게임 랩&lt;/a&gt;이 됐습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/dodge/" &gt;낙하 회피 한 판&lt;/a&gt;&lt;/strong&gt; 해보시고 몇 점 나오는지 알려주세요. 저는 별 먹으려다 폭탄에 자주 박습니다. 🚀&lt;/p&gt;
&lt;p&gt;&lt;em&gt;다음 편엔 다섯 번째 게임을 들고 오겠습니다.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>AI로 게임 만들기 #3 — 원버튼 '디저트 탑 쌓기', 막대기가 마카롱이 되기까지 (공개빌드 #3)</title><link>https://junstellar.github.io/p/ai-game-lab-build-3/</link><pubDate>Thu, 16 Jul 2026 20:00:00 +0900</pubDate><guid>https://junstellar.github.io/p/ai-game-lab-build-3/</guid><description>&lt;p&gt;&lt;a class="link" href="https://junstellar.github.io/p/ai-game-lab-build-2/" &gt;지난 편&lt;/a&gt;까지 만든 두 게임(그림 퀴즈, 한글 워들)을 놓고 보니 공통점이 있더라고요 — &lt;strong&gt;둘 다 머리 쓰는 게임&lt;/strong&gt;입니다. 라이브러리에 퀴즈만 쌓이면 재미없죠. 그래서 세 번째는 완전히 반대로, 아무 생각 없이 순전히 &lt;strong&gt;손맛&lt;/strong&gt;으로 하는 게임을 만들었습니다. 결과물은… &lt;strong&gt;디저트 탑 쌓기&lt;/strong&gt;가 됐어요. 왜 &amp;ldquo;디저트&amp;quot;인지는 뒤에서 이야기하겠습니다.&lt;/p&gt;
&lt;p&gt;👉 &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/stack-tower/" &gt;디저트 탑 쌓기 하러 가기&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="탑-쌓기-장르가-뭐냐면"&gt;&amp;lsquo;탑 쌓기&amp;rsquo; 장르가 뭐냐면
&lt;/h2&gt;&lt;p&gt;모바일 게임 좀 해보셨으면 &amp;lsquo;Stack&amp;rsquo;류 게임을 기억하실 거예요. 규칙은 이게 전부입니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;블록이 &lt;strong&gt;좌우로 왔다 갔다&lt;/strong&gt; 움직인다 → &lt;strong&gt;탭하면 뚝&lt;/strong&gt; 떨어진다&lt;/li&gt;
&lt;li&gt;아래 블록과 &lt;strong&gt;겹친 부분만 남고&lt;/strong&gt;, 튀어나온 부분은 싹둑 잘려나간다&lt;/li&gt;
&lt;li&gt;블록이 점점 좁아지다가, 완전히 빗나가면 끝 — &lt;strong&gt;몇 층까지 쌓았나?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;배우는 데 30초, 한 판에 30초. 그런데 &amp;ldquo;한 판만 더&amp;quot;가 끝없이 나오는 마성의 장르죠.&lt;/p&gt;
&lt;h2 id="이렇게-플레이합니다"&gt;이렇게 플레이합니다
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;화면 &lt;strong&gt;탭&lt;/strong&gt;(PC는 스페이스바) 한 번 = 디저트 낙하&lt;/li&gt;
&lt;li&gt;삐끗한 만큼 잘려서 탑이 점점 좁아집니다 — 착지할 때마다 크림이 찌익 튀고요&lt;/li&gt;
&lt;li&gt;오차 몇 픽셀 이내로 딱 맞추면 &lt;strong&gt;PERFECT&lt;/strong&gt; — 안 잘리고, &lt;strong&gt;체리나 딸기가 톡 하고 올라갑니다&lt;/strong&gt; (탑에 그대로 남아서, 퍼펙트 이력이 훈장처럼 쌓여요)&lt;/li&gt;
&lt;li&gt;연속 PERFECT면 좁아졌던 폭이 조금씩 &lt;strong&gt;회복&lt;/strong&gt;됩니다 (실수해도 컴백 가능)&lt;/li&gt;
&lt;li&gt;올라갈수록 점점 빨라집니다. 점수 = 층수&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt="디저트 탑 쌓기 플레이 화면" class="gallery-image" data-flex-basis="157px" data-flex-grow="65" height="1520" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-3/stack-dessert.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-3/stack-dessert_hu_b128a1dd5efd7cf5.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-3/stack-dessert.webp 1000w" width="1000"&gt;&lt;/p&gt;
&lt;p&gt;팬케이크, 마카롱, 생크림, 초콜릿… 층마다 다른 디저트가 올라오는데, 문제는 —&lt;/p&gt;
&lt;h2 id="젤리는-흔들립니다-"&gt;젤리는 흔들립니다 🍮
&lt;/h2&gt;&lt;p&gt;몇 층마다 한 번씩 &lt;strong&gt;젤리&lt;/strong&gt;가 나옵니다. 이 젤리, 착지한 뒤에도 탄성 때문에 &lt;strong&gt;좌우로 부르르 흔들려요.&lt;/strong&gt; 그리고 다음 블록의 판정은 흔들리고 있는 &lt;strong&gt;실시간 위치 기준&lt;/strong&gt;입니다. 흔들리는 젤리 위에 다음 디저트를 얹어야 하니, 타이밍을 완전히 빼앗기죠. 대신 그 위에 뭔가 얹히면 젤리는 그 자리에 눌려 고정됩니다. 이 게임의 시그니처 기믹이에요 — &amp;ldquo;아, 젤리다…&amp;rdquo; 하는 순간의 긴장감.&lt;/p&gt;
&lt;h2 id="막대기가-마카롱이-되기까지"&gt;막대기가 마카롱이 되기까지
&lt;/h2&gt;&lt;p&gt;사실 이번 편의 진짜 이야기는 이겁니다. 이 게임, &lt;strong&gt;세 번 변신했어요.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1차 — 색깔 막대기.&lt;/strong&gt; 로직을 다 만들고 첫 버전을 보여줬더니 돌아온 반응: &lt;em&gt;&amp;ldquo;좋은데, 탑 디자인이 너무 구리다.&amp;rdquo;&lt;/em&gt; …맞는 말이었습니다:&lt;/p&gt;
&lt;p&gt;&lt;img alt="1차 — 평평한 막대 탑" class="gallery-image" data-flex-basis="157px" data-flex-grow="65" height="1520" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-3/stack-before.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-3/stack-before_hu_f0af13ca931753df.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-3/stack-before.webp 1000w" width="1000"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2차 — 입체(2.5D).&lt;/strong&gt; 그래서 블록에 윗면·옆면을 붙여 입체로 갈아엎었습니다. 훨씬 나아졌죠:&lt;/p&gt;
&lt;p&gt;&lt;img alt="2차 — 2.5D 입체 탑" class="gallery-image" data-flex-basis="157px" data-flex-grow="65" height="1520" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-3/stack-after.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-3/stack-after_hu_284ac3b4a153d663.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-3/stack-after.webp 1000w" width="1000"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3차 — 디저트.&lt;/strong&gt; 그런데 여기서 &amp;ldquo;아예 디저트를 쌓으면 어때? 젤리는 흔들리고!&amp;ldquo;라는 아이디어가 나왔고 — 그 순간 이 게임이 흔한 Stack 클론에서 &lt;strong&gt;자기 정체성이 있는 게임&lt;/strong&gt;으로 바뀌었습니다. 파스텔 배경, 케이크 스탠드, 시럽 드립, 퍼펙트 체리, 그리고 흔들리는 젤리까지. 위의 플레이 화면이 그 결과예요.&lt;/p&gt;
&lt;p&gt;게임 로직은 처음부터 끝까지 그대로입니다. 바뀐 건 &lt;strong&gt;그리는 코드와 콘셉트&lt;/strong&gt;뿐인데, 게임의 인상은 매번 완전히 달라졌어요. 만들면서 배운 것: 규칙이 게임의 뼈대라면, &lt;strong&gt;콘셉트는 게임의 얼굴&lt;/strong&gt;이더라고요.&lt;/p&gt;
&lt;h2 id="이제-게임이-셋--성격이-다-다릅니다"&gt;이제 게임이 셋 — 성격이 다 다릅니다
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;게임&lt;/th&gt;
					&lt;th&gt;성격&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;🎨&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/guess-image/" &gt;AI 그림 맞히기&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;머리 (퀴즈)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🟩&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/hangul-word/" &gt;한글 단어 맞히기&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;습관 (데일리)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🍰&lt;/td&gt;
					&lt;td&gt;&lt;a class="link" href="https://junstellar.github.io/games/stack-tower/" &gt;디저트 탑 쌓기&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;손맛 (아케이드)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;ldquo;비슷한 게임 모음&amp;quot;이 아니라, 이제야 &lt;a class="link" href="https://junstellar.github.io/games/" &gt;게임 랩&lt;/a&gt;이 구색을 갖춘 느낌입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/stack-tower/" &gt;디저트 탑 한 판&lt;/a&gt;&lt;/strong&gt; 쌓아보시고 몇 층인지 알려주세요. 참고로 저는 젤리 위에서 자꾸 무너집니다. 🚀&lt;/p&gt;
&lt;p&gt;&lt;em&gt;다음 편엔 네 번째 게임을 들고 오겠습니다.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>@멘션 한 번이면 클로드가 답한다 — 디스코드로 만든 팀 AI 비서</title><link>https://junstellar.github.io/p/discord-claude-mention-bot/</link><pubDate>Tue, 14 Jul 2026 12:00:00 +0900</pubDate><guid>https://junstellar.github.io/p/discord-claude-mention-bot/</guid><description>&lt;h2 id="시작은-슬랙이었다"&gt;시작은 슬랙이었다
&lt;/h2&gt;&lt;p&gt;팀 프로젝트 관리용으로 &amp;ldquo;메신저에서 클로드를 태그로 불러 쓰는&amp;rdquo; 환경을 만들고 싶었다.
슬랙에 클로드를 붙이는 공식 기능(Claude in Slack, 이른바 클로드 태그)이 이미 있어서
그걸 쓰려고 했는데 — &lt;strong&gt;팀 플랜을 요구했다.&lt;/strong&gt; 혼자 쓰거나 소규모로 굴리는 입장에선
기능 하나 때문에 요금제를 통째로 올리는 게 납득이 안 됐다.&lt;/p&gt;
&lt;p&gt;그래서 &amp;ldquo;그럼 내가 쓰는 메신저에 직접 붙이자&amp;quot;로 방향을 틀었고, 후보를 좁혔다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;카카오톡&lt;/strong&gt; — 탈락. 일반 채팅방에 붙일 봇 API가 아예 없다.
오픈빌더는 1:1 채널 챗봇 전용이고, 비공식 자동화는 계정 정지 위험이 있다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;텔레그램&lt;/strong&gt; — 공식 봇 API가 가장 간단하고 진입장벽이 낮다. 다만 대화가 한 줄기라
여러 주제를 동시에 굴리면 금방 뒤엉킨다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;디스코드&lt;/strong&gt; — 최종 선택.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;디스코드를 고른 이유는 명확했다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;스레드 구조.&lt;/strong&gt; 멘션 하나에 스레드가 하나 생기니 주제별로 문맥이 자연스럽게 분리된다.
이게 뒤에 나올 &lt;code&gt;--resume&lt;/code&gt; 세션 매핑과 정확히 맞아떨어진다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;채널 분리.&lt;/strong&gt; 업무/메모/일정처럼 용도별 채널을 나눠 쓸 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;첨부파일과 코드블록을 제대로 다룬다.&lt;/strong&gt; 파일 올리고 바로 &amp;ldquo;이거 분석해줘&amp;quot;가 된다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;봇 권한을 세밀하게 준다.&lt;/strong&gt; 어떤 채널에서 뭘 할 수 있는지 서버 단위로 통제된다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;슬래시 명령이 1급 시민.&lt;/strong&gt; &lt;code&gt;/일정등록&lt;/code&gt; 같은 걸 자동완성·인자 검증까지 붙여 만들 수 있다.&lt;/li&gt;
&lt;li&gt;무료이고, PC·모바일 클라이언트가 다 있어서 나가서도 그대로 쓴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="핵심-아키텍처"&gt;핵심 아키텍처
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;디스코드 @멘션 → discord.py 봇 → claude -p (headless Claude Code) → 응답 전송
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;claude -p --output-format json&lt;/code&gt;은 응답과 함께 &lt;code&gt;session_id&lt;/code&gt;를 돌려준다.
이걸 디스코드 스레드 ID와 매핑해 저장해두고, 다음 메시지에 &lt;code&gt;--resume &amp;lt;session_id&amp;gt;&lt;/code&gt;를
붙이면 &lt;strong&gt;스레드마다 대화 문맥이 유지&lt;/strong&gt;된다.&lt;/li&gt;
&lt;li&gt;Claude Code에 구독(Pro/Max) 로그인이 되어 있으면 &lt;code&gt;claude -p&lt;/code&gt;는 구독량으로 처리되어
&lt;strong&gt;Anthropic API 결제가 필요 없다.&lt;/strong&gt; 슬랙 쪽에서 막혔던 요금 문제가 여기서 사라진다.&lt;/li&gt;
&lt;li&gt;headless 모드에서도 그 PC에 설정된 &lt;strong&gt;MCP 서버와 스킬을 전부 그대로 인식한다.&lt;/strong&gt;
내 PC에서 쓰던 환경이 그대로 디스코드로 나오는 셈이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="예상-못-한-수확-로컬-메모-동기화가-같이-풀렸다"&gt;예상 못 한 수확: 로컬 메모 동기화가 같이 풀렸다
&lt;/h2&gt;&lt;p&gt;옵시디언처럼 &lt;strong&gt;메모를 로컬 파일로 쌓아두고 있었는데&lt;/strong&gt;, 이걸 폰에서도 보려면
어느 서비스든 결국 유료 동기화 플랜을 권한다. 슬랙 때와 똑같은 벽이다.&lt;/p&gt;
&lt;p&gt;그런데 이 봇을 붙이고 나니 그 문제가 통째로 사라졌다.
메모 파일은 계속 내 PC에 로컬로 있고, 디스코드에서 멘션 한 번이면
클로드가 그 파일들을 읽어서 답한다. &lt;strong&gt;동기화를 할 필요가 없다 —
파일을 옮기는 게 아니라, 파일이 있는 곳에 질문을 보내는 구조이기 때문이다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;여기에 MCP를 얹으면 더 좋아진다. 레드마인 같은 사내 시스템을 MCP로 물려두면,
봇이 일정과 이슈를 조회해서 &lt;strong&gt;디스코드 채팅에 보기 좋게 정리해 뿌려준다.&lt;/strong&gt;
로컬 메모 + MCP 데이터를 한 자리에서 묶어 요약해주니, 별도 대시보드를 안 만들어도
&amp;ldquo;오늘 뭐 있지&amp;quot;가 채팅 한 줄로 끝난다.&lt;/p&gt;
&lt;h2 id="봇-기능"&gt;봇 기능
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;멘션 → 스레드 자동 생성 후 답변&lt;/li&gt;
&lt;li&gt;첨부파일 자동 다운로드 후 클로드가 Read 도구로 분석&lt;/li&gt;
&lt;li&gt;슬래시 명령(&lt;code&gt;/일정등록&lt;/code&gt;, &lt;code&gt;/일정목록&lt;/code&gt;, &lt;code&gt;/일정삭제&lt;/code&gt;)으로 일정 알림
(마감 전날 1회 + 당일 매시간 반복, 삭제 = 완료 처리)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="디스코드에서 슬래시 명령으로 일정을 등록하고, 봇이 마감일과 D-day를 정리해 보여주는 화면" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/images/discord-bot/claudetag-schedule-ko.png"&gt;&lt;/p&gt;
&lt;h2 id="보안-설계"&gt;보안 설계
&lt;/h2&gt;&lt;p&gt;봇이 있는 서버의 &lt;strong&gt;모든 멤버가 내 PC의 클로드를 쓰게 되므로&lt;/strong&gt; 도구를 잠가야 한다.
&lt;code&gt;--allowedTools&lt;/code&gt;를 읽기/검색 계열(Read, Glob, Grep, WebSearch, WebFetch)과
허용할 MCP 도구(&lt;code&gt;mcp__redmine__list_issues&lt;/code&gt; 등)로만 제한하고,
Bash·Write·Edit 같은 실행/쓰기 도구는 막았다.&lt;/p&gt;
&lt;h2 id="디스코드-봇-등록-절차"&gt;디스코드 봇 등록 절차
&lt;/h2&gt;&lt;p&gt;개발자 포털에서:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;New Application → Bot 메뉴에서 토큰 발급&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MESSAGE CONTENT INTENT 스위치 켜기&lt;/strong&gt; — 안 켜면 봇이 메시지를 못 읽는다. 최다 실수 포인트.&lt;/li&gt;
&lt;li&gt;OAuth2 URL Generator에서 &lt;code&gt;bot&lt;/code&gt; 스코프 + 필요한 권한 체크&lt;/li&gt;
&lt;li&gt;생성된 URL로 서버 초대&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;다른 클로드 구독자도 자기 토큰과 자기 구독으로 돌릴 수 있게
zip(봇 코드 + &lt;code&gt;.env.example&lt;/code&gt; + 설치 가이드 + &lt;code&gt;setup.bat&lt;/code&gt;)으로 묶어뒀다.
setup.bat이 Python(winget)·discord.py 설치와 설정 파일 생성까지 자동화하고,
계정이 필요한 부분(디스코드 토큰 발급, 클로드 로그인)만 수동으로 남는다.&lt;/p&gt;
&lt;h2 id="막혔던-점과-해결"&gt;막혔던 점과 해결
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;스토어(MSIX) 설치 Python은 AppData가 가상화&lt;/strong&gt;되어 &lt;code&gt;%APPDATA%\Claude\claude-code\&lt;/code&gt;의
claude.exe를 못 읽는다 (PowerShell에선 보이는데 Python에선 FileNotFoundError).
→ claude.exe를 AppData 밖 폴더로 복사해서 해결. 단독 실행파일이라 복사만으로 동작.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;배치 파일에 한글을 넣으면 cmd가 명령을 깨뜨린다&lt;/strong&gt; (&lt;code&gt;chcp 65001&lt;/code&gt; 이후 파서가 바이트
오프셋을 잘못 읽어 &lt;code&gt;'laudeBot'은(는)...&lt;/code&gt; 같은 유령 오류 발생). → bat 파일은 ASCII만 사용.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PowerShell 파이프로 한글을 넘기면 인코딩이 깨진다.&lt;/strong&gt; → 파이썬 subprocess에서
UTF-8 바이트로 stdin에 직접 쓰면 안전. 파이썬 실행도 &lt;code&gt;-u&lt;/code&gt;(unbuffered) + &lt;code&gt;PYTHONUTF8=1&lt;/code&gt; 필수
(안 하면 print가 버퍼에 갇혀 로그가 안 보이거나 이모지 출력에서 죽는다).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;봇을 AI 세션의 백그라운드 프로세스로 띄우면 세션이 끝날 때 봇도 같이 죽는다.&lt;/strong&gt;
→ bat 더블클릭으로 독립 콘솔에서 실행해야 한다. 콘솔 창 닫으면 봇 종료, 최소화는 OK.&lt;/li&gt;
&lt;li&gt;같은 봇 토큰으로 프로세스를 2개 띄우면 게이트웨이에 둘 다 붙어서 &lt;strong&gt;답장이 중복&lt;/strong&gt;된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="메모생각"&gt;메모/생각
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;결국 세 번의 &amp;ldquo;유료 플랜을 쓰세요&amp;quot;를 같은 방법 하나로 넘겼다.
슬랙 클로드 태그(팀 플랜), 메모 동기화(구독), API 결제 —
&lt;strong&gt;내 PC에서 도는 봇 하나&lt;/strong&gt;로 셋 다 정리됐다.&lt;/li&gt;
&lt;li&gt;핵심은 클로드를 어디 올려두는 게 아니라, 이미 내 PC에 갖춰둔 환경(MCP·스킬·로컬 파일)에
&lt;strong&gt;메신저라는 입구를 하나 뚫어준 것&lt;/strong&gt;이다. 그래서 새로 설정할 게 거의 없었다.&lt;/li&gt;
&lt;li&gt;다음에 해볼 것: 부팅 시 자동 시작 등록, 스레드별 도구 권한 분리.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>유튜브 AI 트렌드를 매일 밤 자동으로 받아보기 — YouTube API + Gemini + 클로드 3단 폴백</title><link>https://junstellar.github.io/p/youtube-ai-trend-report-automation/</link><pubDate>Sun, 12 Jul 2026 10:30:00 +0900</pubDate><guid>https://junstellar.github.io/p/youtube-ai-trend-report-automation/</guid><description>&lt;p&gt;AI 소식은 유튜브에 제일 먼저 올라오는데, 매번 손으로 검색해서 훑는 건 시간 낭비다. 그래서 만들었다 — &lt;strong&gt;매일 밤 11시에 자동으로 돌아서, 아침에 일어나면 어제 하루치 AI 트렌드 리포트가 마크다운으로 놓여 있는&lt;/strong&gt; 파이프라인. 파이썬으로 직접 구축하면서 배운 것들을 정리한다.&lt;/p&gt;
&lt;h2 id="전체-그림"&gt;전체 그림
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;매일 밤 11시 (Windows 작업 스케줄러)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;YouTube Data API → 최근 24시간 Claude·ChatGPT·Gemini 영상 검색
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;필터링 (AI 모델 이름 없는 영상 제거 + 차단 키워드)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;조회수 상위 N개 분석 — 3단 폴백
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ① Gemini가 영상 URL을 직접 시청 (기본)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ② 토큰 소진 시 → claude -p (자막+메타데이터 분석)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ③ 그래도 안 되면 → Gemini 텍스트 분석
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;로컬 마크다운 리포트 저장 (reports/trend-날짜.md)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;역할 분담이 핵심이다. &lt;strong&gt;&amp;ldquo;검색·발견&amp;quot;은 YouTube API가, &amp;ldquo;내용 분석&amp;quot;은 AI가&lt;/strong&gt; 맡는다. 검색 키워드는 3대 모델(Claude/ChatGPT/Gemini)을 뉴스·새 기능·활용법 앵글로 조합한 9개다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;KEYWORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;ChatGPT 업데이트&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Claude 업데이트&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Gemini 새 기능&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;OpenAI 발표&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;ChatGPT new feature&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Claude AI update&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Gemini update&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;ChatGPT 활용법&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Claude Code&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="가장-중요한-깨달음-gemini는-유튜브-url을-직접-먹는다"&gt;가장 중요한 깨달음: Gemini는 유튜브 URL을 직접 먹는다
&lt;/h2&gt;&lt;p&gt;처음엔 &amp;ldquo;영상 자막(CC)을 다운로드해서 분석해야 하나?&amp;ldquo;라고 생각했다. 그런데 여기 함정이 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;YouTube 공식 API의 자막 다운로드는 &amp;ldquo;내 채널 영상&amp;quot;만 된다.&lt;/strong&gt; 남의 영상 자막은 공식 API로 못 가져온다.&lt;/li&gt;
&lt;li&gt;그래서 보통 &lt;code&gt;youtube-transcript-api&lt;/code&gt; 같은 비공식 라이브러리를 쓰는데, 유튜브가 막으면 깨진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;그런데 Gemini API는 유튜브 URL 자체를 영상으로 그냥 받는다.&lt;/strong&gt; 자막을 내려받을 필요가 없다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;google&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;genai&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;google.genai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;genai&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;generate_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;gemini-2.5-flash&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Part&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_uri&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://www.youtube.com/watch?v=...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Part&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;이 영상 핵심 3줄 요약 + 다룬 AI 키워드 뽑아줘&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;이 한 방으로 &amp;ldquo;자막 다운로드&amp;rdquo; 단계가 통째로 사라졌다. 구조가 훨씬 단순해졌다.&lt;/p&gt;
&lt;h2 id="검색-노이즈-잡기-조회수순보다-관련도순"&gt;검색 노이즈 잡기: 조회수순보다 관련도순
&lt;/h2&gt;&lt;p&gt;처음엔 &lt;code&gt;order=viewCount&lt;/code&gt;(조회수순)로 검색했더니, &amp;ldquo;Gemini&amp;quot;라는 단어에 &lt;strong&gt;인도 텔루구 TV 채널 &amp;ldquo;Sun Gemini&amp;rdquo;&lt;/strong&gt;, 아랍어 음식 챌린지 같은 게 조회수 높다고 딸려왔다.&lt;/p&gt;
&lt;p&gt;해결책 두 가지:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;order=relevance&lt;/code&gt;(관련도순)로 검색&lt;/strong&gt; → 대형 비(非)AI 채널이 밀려남. 그런 다음 그 결과들 안에서 조회수로 다시 정렬.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;필터 두 겹 추가&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;차단 키워드(BLOCK): 제목/설명에 &lt;code&gt;Promo&lt;/code&gt;, &lt;code&gt;Serial&lt;/code&gt;, &lt;code&gt;NASA&lt;/code&gt; 등이 있으면 버림&lt;/li&gt;
&lt;li&gt;필수 키워드(REQUIRE): 제목/설명에 &lt;code&gt;claude / chatgpt / gpt / gemini / 클로드 / 제미나이&lt;/code&gt; 중 하나도 없으면 버림&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;haystack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1) 차단어 있으면 스킵&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;haystack&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;bw&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;BLOCK_WORDS&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2) AI 모델 이름 하나도 없으면 스킵&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;haystack&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rw&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;REQUIRE_ANY&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;특히 두 번째 필터가 강력했다. 기간을 24시간으로 좁히니 AI 영상이 적어서 &amp;ldquo;NASA Moon Base Update&amp;rdquo; 같은 게 조회수로 올라왔는데, &amp;ldquo;AI 모델 이름 없으면 제거&amp;rdquo; 필터 한 방에 정치·일반뉴스 노이즈가 싹 사라졌다.&lt;/p&gt;
&lt;h2 id="무료-티어의-벽-그리고-3단-폴백"&gt;무료 티어의 벽, 그리고 3단 폴백
&lt;/h2&gt;&lt;p&gt;Gemini 무료 티어는 &lt;strong&gt;분당 입력 25만 토큰&lt;/strong&gt; 제한이 있다. 그런데 영상은 텍스트와 달리 토큰을 엄청 먹는다(초당 약 100~300토큰). 영상 2개만 분석해도 &lt;code&gt;429 RESOURCE_EXHAUSTED&lt;/code&gt;가 터졌다.&lt;/p&gt;
&lt;p&gt;기본 대응은 이렇다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;저해상도 처리&lt;/strong&gt;로 토큰 절약: &lt;code&gt;media_resolution=&amp;quot;MEDIA_RESOLUTION_LOW&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;영상 사이 20초 대기&lt;/strong&gt;로 분당 한도 회피&lt;/li&gt;
&lt;li&gt;503 같은 일시 오류는 잠시 쉬고 재시도&lt;/li&gt;
&lt;li&gt;분석 개수(&lt;code&gt;TOP_N&lt;/code&gt;)를 무리하게 키우지 않기 (6~8개가 안전)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그래도 토큰이 소진되는 날이 있다. 그래서 &lt;strong&gt;분석 엔진을 3단 폴백으로&lt;/strong&gt; 만들었다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Gemini 영상 시청&lt;/strong&gt; — 기본. 영상을 직접 보니 가장 깊다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;claude -p&lt;/code&gt; (헤드리스 클로드)&lt;/strong&gt; — Gemini 토큰이 떨어지면, 로컬에 설치된 Claude Code CLI를 파이프라인이 직접 호출한다. 영상 대신 제목·설명·자막(최대 8,000자)을 표준입력으로 넘겨 같은 형식으로 분석시킨다. CLI에 로그인만 되어 있으면 별도 API 키 없이 작동.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gemini 텍스트 분석&lt;/strong&gt; — 클로드마저 안 되면 같은 텍스트 뭉치를 Gemini에 넘긴다. 텍스트라 토큰을 거의 안 쓴다.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ① Gemini 영상 시청 → 429면 즉시 폴백&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quota_hit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gemini_video&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;video&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;quota_hit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_text_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;video&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 제목+설명+자막&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;claude_headless&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;claude_exe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# ② claude -p&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gemini_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# ③ Gemini 텍스트&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;리포트에는 영상마다 어떤 엔진이 분석했는지(🎬 영상 시청 / 🤖 claude -p / 📝 텍스트) 표시되므로, 그날 토큰 사정이 어땠는지도 한눈에 보인다.&lt;/p&gt;
&lt;p&gt;교훈: &lt;strong&gt;영상 분석은 토큰이 비싸다.&lt;/strong&gt; &amp;ldquo;많이 보는 것&amp;quot;보다 &amp;ldquo;잘 추려서 몇 개만 깊게 보는 것&amp;quot;이 정답이고, 그마저 안 될 때를 대비한 값싼 차선책을 미리 깔아두면 파이프라인이 멈추지 않는다.&lt;/p&gt;
&lt;h2 id="자동-실행-windows-작업-스케줄러"&gt;자동 실행: Windows 작업 스케줄러
&lt;/h2&gt;&lt;p&gt;매일 밤 11시 실행은 PowerShell로 등록했다. PC가 꺼져 있었을 경우를 대비해 &lt;strong&gt;놓친 작업은 켜지면 바로 실행&lt;/strong&gt;(StartWhenAvailable) 옵션도 켰다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$action&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;New-ScheduledTaskAction&lt;/span&gt; &lt;span class="n"&gt;-Execute&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;run.bat&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;-WorkingDirectory&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$trigger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;New-ScheduledTaskTrigger&lt;/span&gt; &lt;span class="n"&gt;-Daily&lt;/span&gt; &lt;span class="n"&gt;-At&lt;/span&gt; &lt;span class="mf"&gt;23&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;00&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$settings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;New-ScheduledTaskSettingsSet&lt;/span&gt; &lt;span class="n"&gt;-StartWhenAvailable&lt;/span&gt; &lt;span class="n"&gt;-ExecutionTimeLimit&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;New-TimeSpan&lt;/span&gt; &lt;span class="n"&gt;-Hours&lt;/span&gt; &lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Register-ScheduledTask&lt;/span&gt; &lt;span class="n"&gt;-TaskName&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;YouTubeTrendReport&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;-Action&lt;/span&gt; &lt;span class="nv"&gt;$action&lt;/span&gt; &lt;span class="n"&gt;-Trigger&lt;/span&gt; &lt;span class="nv"&gt;$trigger&lt;/span&gt; &lt;span class="n"&gt;-Settings&lt;/span&gt; &lt;span class="nv"&gt;$settings&lt;/span&gt; &lt;span class="n"&gt;-Force&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="열흘-돌려본-결과"&gt;열흘 돌려본 결과
&lt;/h2&gt;&lt;p&gt;리포트가 매일 꼬박꼬박 쌓이고 있다. 영상별로 3줄 요약·다룬 키워드·실용 팁 1개가 정리되어 나오는데, 가장 큰 효과는 &lt;strong&gt;&amp;ldquo;지금 무슨 키워드가 이슈인지&amp;quot;를 아침 5분 안에 파악할 수 있다&lt;/strong&gt;는 것. 새 모델 출시 같은 큰 뉴스가 터진 날은 리포트 상위가 온통 그 얘기로 채워지니, 유튜브를 직접 뒤지지 않아도 흐름을 놓치지 않는다.&lt;/p&gt;
&lt;p&gt;실제로 이 리포트 덕에 잡은 주제로 글도 하나 썼다 — 최근에 올린 &lt;a class="link" href="https://junstellar.github.io/p/qwen3-tts-local-korean-narration/" &gt;무료 로컬 TTS 트렌드 + Qwen3-TTS 구축기&lt;/a&gt;의 트렌드 조사가 바로 이 파이프라인의 확장판이다.&lt;/p&gt;
&lt;h2 id="배운-것-요약"&gt;배운 것 요약
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;영상 분석은 Gemini에 URL을 직접 던지면 된다.&lt;/strong&gt; 자막 다운로드는 옛날 방식.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;검색은 관련도순 + 이중 필터&lt;/strong&gt;(차단어 + AI 모델명 필수)가 노이즈 제거의 핵심.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;무료 티어 토큰 한도&lt;/strong&gt;를 감안해 &amp;ldquo;적게, 깊게&amp;rdquo; 분석하고, &lt;code&gt;claude -p&lt;/code&gt; → 텍스트 분석으로 이어지는 &lt;strong&gt;3단 폴백&lt;/strong&gt;을 깔아 파이프라인이 멈추지 않게.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;API 키는 절대 코드/채팅에 넣지 말고 &lt;code&gt;.env&lt;/code&gt;에.&lt;/strong&gt; (git에서도 제외)&lt;/li&gt;
&lt;li&gt;자동화의 마지막 조각은 결국 &lt;strong&gt;스케줄러&lt;/strong&gt;다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;코드 전체(trend.py, config.py, 스케줄러 등록 안내)는 GitHub에 공개했다: &lt;strong&gt;&lt;a class="link" href="https://github.com/junstellar/youtube-ai-trend-report" target="_blank" rel="noopener"
 &gt;junstellar/youtube-ai-trend-report&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>ElevenLabs 구독 끊고 내 GPU로 — 무료 로컬 TTS 트렌드와 Qwen3-TTS 실전 구축기</title><link>https://junstellar.github.io/p/qwen3-tts-local-korean-narration/</link><pubDate>Sun, 12 Jul 2026 10:00:00 +0900</pubDate><guid>https://junstellar.github.io/p/qwen3-tts-local-korean-narration/</guid><description>&lt;p&gt;유료 TTS 구독(대표적으로 ElevenLabs)을 쓰다 보면 글자 수 제한과 월 요금이 계속 신경 쓰인다. 그런데 최근 한 달 사이 분위기가 확 바뀌었다. **&amp;ldquo;유료 구독 탈출 → 무료 로컬 TTS&amp;rdquo;**가 대세가 된 것. 이번 글은 두 부분으로 나뉜다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;트렌드 조사&lt;/strong&gt; — YouTube Data API로 최근 한 달(6/10~7/10)의 &amp;ldquo;무료 TTS&amp;rdquo; 영상 65건을 수집·선별해 지금 뭐가 뜨는지 정리&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;실전 구축&lt;/strong&gt; — 그중 오픈웨이트로 한국어가 가장 자연스러운 &lt;strong&gt;Qwen3-TTS 1.7B&lt;/strong&gt;를 게이밍 노트북(RTX 4060 Laptop 8GB)에 설치하고, 감정 태그를 입힌 &lt;strong&gt;11분짜리 세미나 내레이션&lt;/strong&gt;을 실제로 뽑아낸 과정&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;비용은 0원, 생성 무제한, 인터넷 없이 동작한다.&lt;/p&gt;
&lt;h2 id="무료-tts-트렌드-조사--요즘-뭐가-뜨나"&gt;무료 TTS 트렌드 조사 — 요즘 뭐가 뜨나
&lt;/h2&gt;&lt;p&gt;유튜브에서 최근 한 달 &amp;ldquo;무료 TTS&amp;rdquo; 관련 영상을 API로 긁어 65건을 모으고, 관련도 높은 20개(한국어 14, 해외 6)를 선별해 요약했다. 결론부터:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;이번 달 최대 이슈는 수퍼톤(Supertone)이다.&lt;/strong&gt; 6월 10일 웹 서비스 &lt;strong&gt;수퍼톤 플레이&lt;/strong&gt;(play.supertone.ai)를 출시했고(공식 소개 영상이 한 달 만에 5.7만 회), 동시에 로컬 모델 &lt;strong&gt;슈퍼토닉3를 MIT 라이선스로 무료 공개&lt;/strong&gt;했다. CPU만으로 한국어를 무제한 생성할 수 있어서 &amp;ldquo;ElevenLabs 해지했다&amp;quot;는 영상이 줄줄이 올라오는 중.&lt;/p&gt;
&lt;p&gt;자주 언급된 무료 도구를 성격별로 묶으면:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;분류&lt;/th&gt;
					&lt;th&gt;도구&lt;/th&gt;
					&lt;th&gt;특징&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;웹에서 바로&lt;/td&gt;
					&lt;td&gt;수퍼톤 플레이, 클로바더빙, Vrew&lt;/td&gt;
					&lt;td&gt;설치 없음, 한국어 더빙은 무료 범위로 충분&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;로컬 오픈소스&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;슈퍼토닉3&lt;/strong&gt;(MIT, 한국어 특화, CPU 가능), &lt;strong&gt;Qwen3-TTS&lt;/strong&gt;, VoxCPM2, OmniVoice&lt;/td&gt;
					&lt;td&gt;무제한 + 음성 복제 지원&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;개발자용 최저 진입장벽&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;Edge-TTS&lt;/strong&gt;(pip 한 줄, API 키 불필요), Google AI Studio Gemini Flash TTS(70+ 언어)&lt;/td&gt;
					&lt;td&gt;스크립트에 바로 붙이기 좋음&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;기타&lt;/td&gt;
					&lt;td&gt;VoiceBox(오프라인 Windows 앱), RVC(내 목소리 로컬 학습)&lt;/td&gt;
					&lt;td&gt;특수 용도&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;한 줄 요약: &lt;strong&gt;웹에서 바로 쓰려면 수퍼톤 플레이·클로바더빙, 개발자라면 Edge-TTS가 진입장벽 최저&lt;/strong&gt;다.&lt;/p&gt;
&lt;h2 id="qwen3-tts-17b-설치해서-직접-테스트해봤다"&gt;Qwen3-TTS 1.7B, 설치해서 직접 테스트해봤다
&lt;/h2&gt;&lt;p&gt;내 목적은 &amp;ldquo;세미나 영상 내레이션을 컷 단위로, 감정 지시까지 넣어서, 무제한으로 뽑기&amp;quot;였다. 프로그램에서 호출할 수 있고 말투 제어가 되는 로컬 모델이 필요했다.&lt;/p&gt;
&lt;p&gt;오픈웨이트 후보들을 비교한 결과:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Qwen3-TTS 1.7B ← 최종 선택&lt;/strong&gt; — Apache 2.0(상업 사용 자유), 한국어 억양·속도가 오픈웨이트 중 가장 자연스러웠다. 3초 샘플만으로 보이스 클로닝이 되고, 크로스링구얼(다른 언어 샘플로 한국어 발화) 오류율이 4.8%로 CosyVoice 3(14.4%)보다 크게 우수하다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fish Audio S2 Pro&lt;/strong&gt; — TTS Arena 기준 오픈웨이트 1위(전체 11위, Elo 1128.7)로 품질은 최상급. 하지만 &lt;strong&gt;상업 사용은 유료 라이선스&lt;/strong&gt;인 데다 모델이 무거워서 탈락.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;슈퍼토닉3&lt;/strong&gt; — MIT에 CPU만으로 돌아가는 건 매력적이지만, 이번엔 감정 지시(instruct) 제어가 필요해서 Qwen3-TTS로 갔다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="설치-windows-11--rtx-4060-laptop-8gb"&gt;설치 (Windows 11 + RTX 4060 Laptop 8GB)
&lt;/h2&gt;&lt;h3 id="1-가상환경--패키지"&gt;1. 가상환경 + 패키지
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt; &lt;span class="n"&gt;D:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;qwen3-tts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;&lt;span class="n"&gt;D:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;qwen3-tts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="n"&gt;-m&lt;/span&gt; &lt;span class="n"&gt;venv&lt;/span&gt; &lt;span class="n"&gt;venv&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;venv&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Scripts&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exe&lt;/span&gt; &lt;span class="n"&gt;-m&lt;/span&gt; &lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;-index-url&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;pytorch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;whl&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cu128&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;venv&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Scripts&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exe&lt;/span&gt; &lt;span class="n"&gt;-m&lt;/span&gt; &lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="nb"&gt;qwen-tts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;내 환경 기준: Python 3.14, torch 2.11+cu128, qwen-tts 0.1.1.&lt;/p&gt;
&lt;h3 id="2-모델과-화자"&gt;2. 모델과 화자
&lt;/h3&gt;&lt;p&gt;모델은 &lt;code&gt;Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice&lt;/code&gt; — 첫 실행 시 자동 다운로드된다. VRAM은 &lt;strong&gt;4.5GB 정도만 사용&lt;/strong&gt;해서 8GB GPU에 여유 있게 들어간다.&lt;/p&gt;
&lt;p&gt;내장 화자는 9명(aiden, dylan, eric, ono_anna, ryan, serena, sohee, uncle_fu, vivian)이고, 한국어는 &lt;strong&gt;sohee&lt;/strong&gt;가 담당이다.&lt;/p&gt;
&lt;h3 id="3-알아두면-좋은-것"&gt;3. 알아두면 좋은 것
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;flash-attn을 설치하지 않으면 생성 속도는 오디오 길이의 약 2배&lt;/strong&gt; (1분짜리 음성에 2분 소요). 배치로 밤에 돌리면 문제없는 수준.&lt;/li&gt;
&lt;li&gt;실행 시 SoX 관련 경고가 뜨는데 무해하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="핵심-패턴-모델을-상주-서버로-띄우기"&gt;핵심 패턴: 모델을 상주 서버로 띄우기
&lt;/h2&gt;&lt;p&gt;모델 로딩에 약 1분이 걸리므로, 스크립트를 실행할 때마다 로딩하면 낭비다. &lt;strong&gt;간단한 HTTP 서버로 한 번만 띄워두고 POST로 호출&lt;/strong&gt;하는 구조를 만들었다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;venv&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Scripts&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exe&lt;/span&gt; &lt;span class="n"&gt;tts_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# → http://127.0.0.1:8765 에서 대기&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;호출은 이렇게:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;POST&lt;/span&gt; &lt;span class="err"&gt;/tts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;texts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;여러분, 오늘 다룰 주제는 피지컬 AI입니다.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;speaker&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;sohee&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;instruct&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;자신감 있는 일타강사 말투로, 약간 도발적으로&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;filename&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;01_opener.wav&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;결과 WAV는 &lt;code&gt;output\&lt;/code&gt;에 저장된다. 핵심은 &lt;code&gt;instruct&lt;/code&gt; 필드 — &lt;strong&gt;말투를 자연어로 지시&lt;/strong&gt;할 수 있다는 점이 이 모델을 고른 결정적 이유다.&lt;/p&gt;
&lt;h2 id="실전-감정-태그-입힌-세미나-내레이션-23컷"&gt;실전: 감정 태그 입힌 세미나 내레이션 23컷
&lt;/h2&gt;&lt;p&gt;세미나 1회차 대본을 23개 컷(01_opener ~ 23_takeaway)으로 쪼개고, &lt;strong&gt;컷마다 감정 태그와 TTS용 instruct 문구를 JSON으로 관리&lt;/strong&gt;했다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;cut&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;05_quiz2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;emotion&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;장난스러운 도발&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;instruct&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;퀴즈를 내며 살짝 약올리는 말투로, 템포를 빠르게&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;감정 곡선은 이렇게 설계했다: &lt;strong&gt;도발 → 퀴즈 5연발 → 약올림 → 차분한 강의 → 정답 발표 → 심화 → 묵직한 마무리&lt;/strong&gt;. 단일 톤으로 11분을 읽으면 졸린데, 컷마다 instruct를 바꿔주니 확실히 &amp;ldquo;사람이 진행하는&amp;rdquo; 느낌이 산다.&lt;/p&gt;
&lt;p&gt;파이프라인은 3단계:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;컷별 대본 23개 + 감정 태그 JSON
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓ (gen_all_cuts.py — 컷마다 instruct 다르게 서버 호출)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;output\cuts\ 23개 WAV
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓ (60~90초 단위로 병합, 구성은 parts_manifest.json에 기록)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;output\parts\part_01.wav ~ part_10.wav (총 11.6분)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;이 구조의 장점은 &lt;strong&gt;수정이 컷 단위&lt;/strong&gt;라는 것. 특정 컷의 억양이 마음에 안 들면 그 컷만 instruct를 고쳐 재생성하고 해당 파트만 다시 병합하면 된다. 유료 서비스였다면 재생성할 때마다 글자 수가 차감됐을 텐데, 로컬이라 무한 리테이크가 공짜다.&lt;/p&gt;
&lt;h2 id="직접-들어보기--생성-샘플-3개"&gt;직접 들어보기 — 생성 샘플 3개
&lt;/h2&gt;&lt;p&gt;말로 설명하는 것보다 듣는 게 빠르다. 아래는 Qwen3-TTS(화자 sohee)로 뽑은 샘플들이다. 스타일별로 하나씩 — 뉴스 리딩, 다큐 내레이션, 감정 표현.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;샘플 1 — 뉴스 톤&lt;/strong&gt; · &amp;ldquo;오늘 서울의 낮 최고 기온은 33도까지 오르겠고, 내륙 곳곳에 소나기가 지나겠습니다.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;audio controls preload="metadata" src="https://junstellar.github.io/audio/qwen3-tts/sample1_news.wav" style="width:100%"&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;샘플 2 — 차분한 내레이션 톤&lt;/strong&gt; · &amp;ldquo;인공지능 기술은 지난 10년간 놀라운 속도로 발전해왔습니다. 이제 우리는 컴퓨터와 자연스럽게 대화하는 시대에 살고 있죠.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;audio controls preload="metadata" src="https://junstellar.github.io/audio/qwen3-tts/sample2_narration.wav" style="width:100%"&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;샘플 3 — 감정(놀람) 표현&lt;/strong&gt; · &amp;ldquo;정말요? 그게 진짜 된다고요? 와, 이건 생각보다 훨씬 대단한데요?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;audio controls preload="metadata" src="https://junstellar.github.io/audio/qwen3-tts/sample3_emotion.wav" style="width:100%"&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;같은 화자인데도 instruct 지시에 따라 톤이 이만큼 갈라진다. 특히 샘플 3처럼 감탄·놀람 같은 감정 표현이 어색하지 않다는 점이 세미나 내레이션에 쓸 수 있겠다고 판단한 근거였다.&lt;/p&gt;
&lt;h2 id="정리"&gt;정리
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;트렌드는 명확하다: &lt;strong&gt;무료 로컬 TTS가 유료 구독을 대체하는 중.&lt;/strong&gt; 웹은 수퍼톤 플레이·클로바더빙, 개발자는 Edge-TTS, 품질+제어가 필요하면 오픈웨이트 로컬 모델.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Qwen3-TTS 1.7B&lt;/strong&gt;는 Apache 2.0 + 한국어 자연스러움 + 자연어 말투 지시(instruct) + VRAM 4.5GB라는 조합으로, 8GB급 GPU에서 실용적인 최선이었다.&lt;/li&gt;
&lt;li&gt;상주 서버 + 컷별 감정 태그 JSON 패턴으로 &lt;strong&gt;11.6분 세미나 내레이션을 컷 단위 리테이크 가능한 형태&lt;/strong&gt;로 완성했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 글의 코드 전체(상주 서버, 컷별 생성·병합 스크립트, 스트리밍 페이지, 샘플 음성)는 GitHub에 공개했다: &lt;strong&gt;&lt;a class="link" href="https://github.com/junstellar/qwen3-tts-korean-narration" target="_blank" rel="noopener"
 &gt;junstellar/qwen3-tts-korean-narration&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;다음 과제는 두 가지다. flash-attn을 설치해 생성 속도를 끌어올리는 것, 그리고 3초 샘플 보이스 클로닝으로 내 목소리를 등록해보는 것. 진행되면 후속 글로 정리하겠다.&lt;/p&gt;</description></item><item><title>AI로 게임 만들기 #2 — 한글판 워들, 그리고 힌트 난이도로 세 번 갈아엎은 이야기 (공개빌드 #2)</title><link>https://junstellar.github.io/p/ai-game-lab-build-2/</link><pubDate>Sat, 11 Jul 2026 09:00:00 +0900</pubDate><guid>https://junstellar.github.io/p/ai-game-lab-build-2/</guid><description>&lt;p&gt;&lt;a class="link" href="https://junstellar.github.io/p/ai-game-lab-build-1/" &gt;지난 편&lt;/a&gt;에서 &amp;ldquo;게임을 하나씩 계속 얹겠다&amp;quot;고 했으니, 약속대로 &lt;strong&gt;두 번째 게임&lt;/strong&gt;을 만들었습니다. 이번엔 &lt;strong&gt;한글판 워들&lt;/strong&gt; — 매일 두 글자 단어 하나를 맞히는 게임이에요.&lt;/p&gt;
&lt;p&gt;👉 &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/hangul-word/" &gt;한글 단어 맞히기 하러 가기&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="워들이-뭐냐면"&gt;워들이 뭐냐면
&lt;/h2&gt;&lt;p&gt;혹시 &amp;lsquo;워들(Wordle)&amp;rsquo; 아세요? 2022년 초에 전 세계를 휩쓴 단어 맞히기 게임이에요. 규칙은 단순합니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;숨겨진 &lt;strong&gt;다섯 글자 영어 단어&lt;/strong&gt;를 &lt;strong&gt;여섯 번&lt;/strong&gt; 안에 맞힌다&lt;/li&gt;
&lt;li&gt;한 번 추측할 때마다 글자마다 색으로 힌트를 준다 — 🟩(자리·글자 맞음) · 🟨(단어에 있지만 자리가 틀림) · ⬜(없음)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;하루에 딱 한 단어.&lt;/strong&gt; 모두가 같은 문제를 풀고, 결과를 이모지 격자로 공유&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 &amp;ldquo;하루 한 문제 + 색 힌트&amp;rdquo; 조합이 은근히 중독적이라 한때 어마어마하게 유행했죠. 저는 이걸 &lt;strong&gt;한글로&lt;/strong&gt; 만들어보고 싶었습니다.&lt;/p&gt;
&lt;h2 id="한글로-워들을-만들면"&gt;한글로 워들을 만들면
&lt;/h2&gt;&lt;p&gt;영어는 알파벳을 한 칸씩 맞히면 되는데, 한글은 어떻게 할까요? 핵심은 &lt;strong&gt;한글 한 글자가 자모로 쪼개진다&lt;/strong&gt;는 거예요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lsquo;버&amp;rsquo; = ㅂ + ㅓ&lt;/li&gt;
&lt;li&gt;&amp;lsquo;스&amp;rsquo; = ㅅ + ㅡ&lt;/li&gt;
&lt;li&gt;받침이 있으면 셋 — &amp;lsquo;박&amp;rsquo; = ㅂ + ㅏ + ㄱ&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그래서 영어 워들이 알파벳 칸을 색칠하듯, 한글 워들은 &lt;strong&gt;초성·중성·종성(자모) 한 칸씩&lt;/strong&gt; 색칠해서 알려줍니다. 화면은 이렇게 생겼어요:&lt;/p&gt;
&lt;p&gt;&lt;img alt="한글 단어 맞히기 시작 화면" class="gallery-image" data-flex-basis="269px" data-flex-grow="112" height="890" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-2/hangul-word-start.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-2/hangul-word-start_hu_fc1f965da2fa06ca.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-2/hangul-word-start.webp 1000w" width="1000"&gt;&lt;/p&gt;
&lt;h2 id="이렇게-플레이합니다"&gt;이렇게 플레이합니다
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;오늘의 두 글자 단어&lt;/strong&gt;가 정해져 있어요 (하루 한 번, 모두 같은 문제)&lt;/li&gt;
&lt;li&gt;두 글자를 입력하면, 자모마다 색이 칠해집니다 — 🟩 자리까지 맞음 / 🟨 있지만 다른 자리 / ⬜ 없음&lt;/li&gt;
&lt;li&gt;이 색 힌트를 보고 좁혀가며 &lt;strong&gt;여섯 번 안에&lt;/strong&gt; 맞히면 됩니다&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;예를 들어 정답이 &amp;lsquo;버스&amp;rsquo;일 때 &amp;lsquo;구름&amp;rsquo;을 넣으면, &amp;lsquo;름&amp;rsquo;의 ㅡ가 초록으로 뜹니다(정답 &amp;lsquo;스&amp;rsquo;의 ㅡ와 자리까지 일치). 이렇게 자모를 하나씩 맞춰가는 거예요:&lt;/p&gt;
&lt;p&gt;&lt;img alt="자모 색칠과 점진적 힌트" class="gallery-image" data-flex-basis="171px" data-flex-grow="71" height="1400" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-game-lab-build-2/hangul-word-play.webp" srcset="https://junstellar.github.io/p/ai-game-lab-build-2/hangul-word-play_hu_6e5c63f0be4681fb.webp 800w, https://junstellar.github.io/p/ai-game-lab-build-2/hangul-word-play.webp 1000w" width="1000"&gt;&lt;/p&gt;
&lt;h2 id="막히면-힌트가-하나씩-열립니다"&gt;막히면? 힌트가 하나씩 열립니다
&lt;/h2&gt;&lt;p&gt;여기가 이 게임에서 제일 공들인 부분이에요. 그냥 두 글자 맞히라고만 하면 시작 실마리가 없어서 너무 막막하고, 그렇다고 처음부터 설명을 다 주면 한 번에 맞혀버려서 싱겁더라고요. (사실 이 사이를 잡느라 몇 번을 갈아엎었습니다.)&lt;/p&gt;
&lt;p&gt;그래서 이렇게 했어요: &lt;strong&gt;시작은 힌트 없이. 한 번 틀릴 때마다 힌트가 하나씩, 점점 세게 열립니다.&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;큰 갈래&lt;/strong&gt; — &amp;ldquo;사람이 만든 것&amp;rdquo; (수십 개 중 하나, 거의 감 안 옴)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;분류&lt;/strong&gt; — &amp;ldquo;교통&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;설명&lt;/strong&gt; — &amp;ldquo;많은 사람이 함께 타는 대중교통&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;초성&lt;/strong&gt; — &amp;ldquo;ㅂ ㅅ&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;첫 글자&lt;/strong&gt; — &amp;ldquo;버_&amp;rdquo;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;위 스크린샷에서 보드 위에 쌓인 초록 상자들이 그거예요(세 번 틀려서 세 개 열린 상태). 덕분에 &lt;strong&gt;감 좋으면 힌트 없이 빨리 맞혀 고득점&lt;/strong&gt;, 막히면 틀릴수록 점점 좁혀지는 도움을 받습니다. 난이도를 플레이어가 스스로 조절하는 셈이죠.&lt;/p&gt;
&lt;h2 id="그리고-결과-공유"&gt;그리고 결과 공유
&lt;/h2&gt;&lt;p&gt;맞히고 나면 결과가 &lt;code&gt;🟩🟨⬜&lt;/code&gt; 이모지 격자로 만들어져서 한 번에 공유할 수 있어요. 정답은 안 보여주고 &amp;ldquo;몇 번 만에, 얼마나 헤맸는지&amp;quot;만 색으로 남기는 워들 방식 그대로입니다. 연속 출석과 누적 점수도 기기에 차곡차곡 쌓이고요.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;그래서 지금 &lt;a class="link" href="https://junstellar.github.io/games/" &gt;게임 랩&lt;/a&gt;엔 게임이 둘입니다 — 🎨 AI 그림 맞히기, 🟩 한글 단어 맞히기.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/hangul-word/" &gt;한글 단어 맞히기 한 판&lt;/a&gt;&lt;/strong&gt; 해보시고, 오늘 단어 몇 번에 맞혔는지 · 힌트가 적당한지 알려주세요. &amp;ldquo;이 단어 힌트가 이상하다&amp;rdquo; 하는 게 있으면 바로 고칩니다. 🚀&lt;/p&gt;
&lt;p&gt;&lt;em&gt;다음 편엔 세 번째 게임이나, 아니면 사람들이 실제로 하러 왔는지 이야기를 들고 오겠습니다.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>AI로 게임 만들어 돈 벌기, 진짜 될까? — 혼자 그림 퀴즈 게임 만든 이야기 (공개빌드 #1)</title><link>https://junstellar.github.io/p/ai-game-lab-build-1/</link><pubDate>Mon, 06 Jul 2026 22:00:00 +0900</pubDate><guid>https://junstellar.github.io/p/ai-game-lab-build-1/</guid><description>&lt;p&gt;얼마 전 유튜브를 보다가 우연히 흥미로운 이야기를 봤습니다. 어떤 사람이 &lt;strong&gt;AI로 며칠 만에 만든&lt;/strong&gt; 브라우저 비행 게임 하나로 &lt;strong&gt;큰돈을 벌었다&lt;/strong&gt;는 거예요. &lt;strong&gt;fly.pieter.com&lt;/strong&gt; 이라는 게임이었죠. 그래픽이 대단한 것도 아닌데, 게임 속 하늘에 뜬 &lt;strong&gt;광고판 자리를 팔아&lt;/strong&gt; 수익을 냈다고 하더라고요.&lt;/p&gt;
&lt;p&gt;만든 사람은 &lt;strong&gt;피터 레벨스(Pieter Levels)&lt;/strong&gt;, 네덜란드의 1인 개발자입니다. 투자도 직원도 없이 &lt;strong&gt;혼자&lt;/strong&gt; 여러 서비스(Nomad List, Remote OK, PhotoAI 등)를 만들어 운영하는 사람인데, 방식이 인상적이었어요 — &lt;strong&gt;혼자, 빠르게&lt;/strong&gt; 만들고, 첫날부터 매출을 내고, 기술은 극단적으로 단순하게 가고, 무엇보다 &lt;strong&gt;만드는 과정을 매일 공개(build in public)&lt;/strong&gt; 합니다. fly.pieter.com도 AI(이른바 &amp;lsquo;바이브 코딩&amp;rsquo;)로 뚝딱 만들어 그 과정을 공유하며 키운 게임이었죠.&lt;/p&gt;
&lt;p&gt;그 영상을 보고 나니 이런 생각이 들었습니다. &lt;em&gt;&amp;ldquo;나도 뭐라도 하나 더 해봐야겠다. 그럼… 게임을 한번 만들어볼까?&amp;rdquo;&lt;/em&gt; 이 글은 그 생각으로 실제로 만들어 본 게임 이야기입니다.&lt;/p&gt;
&lt;h2 id="내가-이-이야기에서-얻은-것"&gt;내가 이 이야기에서 얻은 것
&lt;/h2&gt;&lt;p&gt;솔직히 처음엔 &amp;lsquo;큰돈 벌었다&amp;rsquo;는 대목에 혹했습니다. 그런데 며칠 곱씹어 보니, 정작 제 마음에 남은 건 돈이 아니라 &lt;strong&gt;태도&lt;/strong&gt;였어요.&lt;/p&gt;
&lt;p&gt;거창한 계획이나 완벽한 결과물부터 준비하는 게 아니라, &lt;strong&gt;일단 작게 시작해서 만드는 과정을 공개하고, 거기서부터 한 걸음씩 쌓아 올린다&lt;/strong&gt; — 이 방식이요. 결과가 어떻게 되든, 시작을 해보고 그 과정을 기록으로 남기는 것만으로도 분명 남는 게 있겠다는 마음이 생겼습니다.&lt;/p&gt;
&lt;p&gt;마음을 그렇게 먹고 나니 방향은 분명해졌어요. 거창한 3D 게임 같은 건 한참 나중 일이고, 지금은 &lt;strong&gt;작고, 나만 할 수 있고, 재미있는&lt;/strong&gt; 걸 하나 만들어서 그 과정을 여기에 공개하기로요. 그렇게 만들기 시작했습니다.&lt;/p&gt;
&lt;h2 id="그래서-고른-전략--내-무기는-집에-있는-gpu"&gt;그래서 고른 전략 — 내 무기는 &amp;lsquo;집에 있는 GPU&amp;rsquo;
&lt;/h2&gt;&lt;p&gt;남들이 쉽게 못 하는 걸 해야 승산이 있죠. 저한테는 남들에게 없는 무기가 하나 있습니다. 바로 &lt;a class="link" href="https://junstellar.github.io/p/claude-local-image-generation/" &gt;지난 글에서 세팅한 &lt;strong&gt;집 게이밍 노트북의 로컬 이미지 생성기(ComfyUI)&lt;/strong&gt;&lt;/a&gt;예요.&lt;/p&gt;
&lt;p&gt;&amp;lsquo;AI 이미지로 하는 게임&amp;rsquo;은 보통 &lt;strong&gt;이미지 생성 API 비용&lt;/strong&gt; 때문에 개인이 하기 부담스럽습니다. 그런데 저는 RTX 4060 노트북으로 그림을 &lt;strong&gt;공짜로 무제한&lt;/strong&gt; 뽑을 수 있어요. 남들이 돈 때문에 못 하는 걸 전기값만 내고 하는 거죠. 이 무기를 살리는 게임이 뭘까 고민하다 나온 게 —&lt;/p&gt;
&lt;h2 id="첫-작품-ai-그림-맞히기"&gt;첫 작품: 「AI 그림 맞히기」
&lt;/h2&gt;&lt;p&gt;👉 &lt;strong&gt;지금 바로 플레이: &lt;a class="link" href="https://junstellar.github.io/games/guess-image/" &gt;AI 그림 맞히기&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;규칙은 단순합니다. &lt;strong&gt;AI가 그린 그림 한 장을 보고, 뭘 그린 건지 맞히면 됩니다.&lt;/strong&gt; 예를 들면 이런 그림들이에요.&lt;/p&gt;
&lt;p&gt;&lt;img alt="우주복 입은 고양이" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/games/guess-image/img/astro-cat.webp"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="초밥으로 만든 용" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/games/guess-image/img/sushi-dragon.webp"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="수박 속 바다" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/games/guess-image/img/watermelon-sea.webp"&gt;&lt;/p&gt;
&lt;p&gt;(정답은&amp;hellip; 직접 맞혀보세요 😏)&lt;/p&gt;
&lt;p&gt;처음엔 4지선다 객관식으로 만들었는데, 테스트하던 지인이 **&amp;ldquo;이건 우리집 거북이도 맞히겠다&amp;rdquo;**고 해서 갈아엎었습니다. 그래서 지금은 이렇게 됐어요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;한 문제에 기회 3번&lt;/strong&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;1차&lt;/strong&gt; — 그냥 직접 입력해서 맞히기 (&lt;code&gt;+3점&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;틀리면 &lt;strong&gt;힌트&lt;/strong&gt;를 주고 다시 (&lt;code&gt;+2점&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;또 틀리면 마지막으로 &lt;strong&gt;객관식&lt;/strong&gt; (&lt;code&gt;+1점&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;빨리 맞힐수록 고득점, 최종 &lt;strong&gt;총점 /15&lt;/strong&gt;로 마무리&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;연속 출석&lt;/strong&gt;과 &lt;strong&gt;누적 점수&lt;/strong&gt;가 기기에 쌓이고 (계정·로그인 없이)&lt;/li&gt;
&lt;li&gt;결과는 &lt;code&gt;🟩🟨🟧🟥&lt;/code&gt; 이모지로 만들어 &lt;strong&gt;한 번에 공유&lt;/strong&gt; (워들 방식)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;현재 문제는 &lt;strong&gt;30개&lt;/strong&gt;, 한 판에 그중 &lt;strong&gt;무작위 5개&lt;/strong&gt;가 나옵니다. 그래서 &amp;ldquo;다시 하기&amp;quot;를 누를 때마다 다른 그림이 나와요.&lt;/p&gt;
&lt;h2 id="이미지-30장-어떻게-만들었나--집-gpu로-공짜"&gt;이미지 30장, 어떻게 만들었나 — 집 GPU로 공짜
&lt;/h2&gt;&lt;p&gt;여기가 이 프로젝트의 핵심입니다. 문제로 쓸 그림 30장을 전부 &lt;strong&gt;집 노트북에서 직접&lt;/strong&gt; 뽑았어요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ComfyUI + SDXL로 프롬프트를 주고 그림 생성 (&lt;strong&gt;장당 약 30초&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;클라우드 API였으면 수십 장에 돈이 나갔겠지만, 로컬이라 &lt;strong&gt;전기값 빼고 0원&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;뽑은 PNG 30장(48MB)을 &lt;strong&gt;WebP로 변환&lt;/strong&gt;해서 총 &lt;strong&gt;2.2MB&lt;/strong&gt;로 압축 (웹에서 빠르게 뜨도록)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;마음에 안 드는 그림은 프롬프트만 고쳐서 다시 뽑으면 되니까, 문제 풀을 늘리는 비용이 사실상 공짜예요. &lt;strong&gt;&amp;ldquo;AI 이미지 게임&amp;quot;에 로컬 GPU가 왜 강력한 무기인지&lt;/strong&gt;가 여기서 드러납니다.&lt;/p&gt;
&lt;h2 id="왜-게임-하나가-아니라-게임-랩인가"&gt;왜 &amp;lsquo;게임 하나&amp;rsquo;가 아니라 &amp;lsquo;게임 랩&amp;rsquo;인가
&lt;/h2&gt;&lt;p&gt;이 게임은 사실 &lt;a class="link" href="https://junstellar.github.io/games/" &gt;&lt;strong&gt;AI 게임 랩&lt;/strong&gt;&lt;/a&gt;이라는 미니게임 &lt;strong&gt;라이브러리&lt;/strong&gt;의 1번 작품입니다. 게임 하나만 딸랑 만들지 않고 라이브러리로 만든 이유는 —&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;게임 하나가 안 터져도 &lt;strong&gt;라이브러리 자체가 자산&lt;/strong&gt;으로 남고&lt;/li&gt;
&lt;li&gt;**&amp;ldquo;AI로 게임을 계속 찍어내는 사람&amp;rdquo;**이라는 이야기가 공개빌드에 딱 맞기 때문입니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;구조도 그렇게 짰어요. 새 게임을 추가하려면 &lt;strong&gt;폴더 하나 복사 + 목록에 한 줄&lt;/strong&gt; 추가면 끝나고, 점수 공유·누적 기록 같은 공통 기능은 모든 게임이 나눠 쓰도록 만들었습니다. 앞으로 여기에 게임을 계속 얹을 겁니다.&lt;/p&gt;
&lt;h2 id="앞으로도-계속-쌓아-나가려고요"&gt;앞으로도 계속 쌓아 나가려고요
&lt;/h2&gt;&lt;p&gt;게임을 만드는 것보다, 사실 이걸 &lt;strong&gt;꾸준히 이어가는 것&lt;/strong&gt;이 더 어려운 일이겠죠. 그래서 저는 완성한 다음에 &amp;ldquo;이제 뭐 하지?&amp;ldquo;를 고민하는 대신, &lt;strong&gt;만드는 과정부터 이렇게 글로 남기기로&lt;/strong&gt; 했습니다. 이 글이 그 1편이고요.&lt;/p&gt;
&lt;p&gt;계획은 거창하지 않습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;만드는 과정을 계속 &lt;strong&gt;공개&lt;/strong&gt;하고 (공개빌드)&lt;/li&gt;
&lt;li&gt;게임에 &lt;strong&gt;공유 기능을 내장&lt;/strong&gt;해서 (플레이한 사람이 결과를 편하게 나눌 수 있게)&lt;/li&gt;
&lt;li&gt;게임을 &lt;strong&gt;하나씩 계속 얹어서&lt;/strong&gt; 라이브러리를 키운다&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;내세울 결과는 아직 없습니다. 이제 막 1편이니까요. 하지만 &lt;strong&gt;한 걸음씩 꾸준히 쌓아 나가는 것&lt;/strong&gt;, 그게 지금 저한테 제일 중요한 일입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;그러니 이 글의 마무리는 부탁으로 하겠습니다. 👇&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/games/guess-image/" &gt;「AI 그림 맞히기」 한 판 해보기&lt;/a&gt;&lt;/strong&gt; — 몇 점 나오는지, 어떤 그림에서 빵 터졌는지 알려주시면 다음 편에 반영하겠습니다. 여러분이 놀아주는 게, 이 실험의 진짜 연료입니다. 🚀&lt;/p&gt;
&lt;p&gt;&lt;em&gt;다음 편엔 두 번째 게임을 들고 오겠습니다.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>게이밍 노트북으로 나만의 LLM 돌리기 — llama.cpp로 완전 무료·오프라인 AI 만들기</title><link>https://junstellar.github.io/p/llama-cpp-local-llm/</link><pubDate>Mon, 06 Jul 2026 20:45:00 +0900</pubDate><guid>https://junstellar.github.io/p/llama-cpp-local-llm/</guid><description>&lt;p&gt;요즘 클로드나 챗GPT 안 쓰는 사람이 없죠. 그런데 이걸 &lt;strong&gt;인터넷도 없이, 요금도 없이, 내 노트북에서 직접&lt;/strong&gt; 돌릴 수 있다면 어떨까요? &amp;ldquo;그건 서버실에 GPU 잔뜩 있어야 하는 거 아냐?&amp;rdquo; 싶은데, 결론부터 말하면 &lt;strong&gt;집에 있는 게이밍 노트북 한 대로, 돈 한 푼 안 들이고&lt;/strong&gt; 됩니다. 지난번엔 ComfyUI로 &amp;lsquo;그림 그리는 로컬 서버&amp;rsquo;를 만들었는데, 이번엔 &lt;code&gt;llama.cpp&lt;/code&gt;로 **&amp;lsquo;말하고 글 쓰는 LLM&amp;rsquo;**을 내 GPU에 올려본 실전 기록입니다.&lt;/p&gt;
&lt;h2 id="왜-굳이-로컬로-돌릴까요"&gt;왜 굳이 로컬로 돌릴까요?
&lt;/h2&gt;&lt;p&gt;솔직히 똑똑함만 따지면 클라우드 AI가 위입니다. 그런데도 로컬로 돌리는 이유가 분명히 있어요.&lt;/p&gt;
&lt;p&gt;가장 큰 건 &lt;strong&gt;토큰을 무제한으로 쓸 수 있다&lt;/strong&gt;는 점입니다. 클라우드 API는 글자(토큰) 하나하나에 요금이 붙죠. 문서 수백 개를 요약하거나, 로그 수만 줄을 분류하거나, 밤새 뭔가를 돌리다 보면 요금이 은근히 무섭게 불어납니다. 로컬은 전기값 빼곤 0원이라, &amp;ldquo;이거 토큰 아까운데…&amp;rdquo; 하고 망설일 일이 없어요. 프롬프트를 마음껏 길게 쓰고, 마음에 들 때까지 몇 번이고 다시 돌려도 됩니다.&lt;/p&gt;
&lt;p&gt;여기에 이런 것들이 더 붙습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;속도 제한이 없어요.&lt;/strong&gt; API는 분당 요청 수에 제한이 걸려서 대량 작업을 하면 계속 기다려야 하는데, 내 GPU는 온전히 내 것이라 줄 설 일이 없습니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;데이터가 밖으로 안 나갑니다.&lt;/strong&gt; 회사 내부 문서, 개인정보, 아직 공개 안 한 코드처럼 외부에 올리기 껄끄러운 걸 다룰 때 이게 결정적이에요. 애초에 전송을 안 하니 유출될 게 없죠.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;인터넷이 필요 없습니다.&lt;/strong&gt; 비행기 안이든, 네트워크가 막힌 폐쇄망이든 그대로 돌아갑니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;모델이 갑자기 사라지지 않아요.&lt;/strong&gt; 클라우드 모델은 어느 날 구버전이 종료되곤 하는데, 내 디스크에 받아둔 파일은 영원히 그 버전 그대로입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;한마디로 **&amp;ldquo;똑똑함은 클라우드, 자유도·비용·프라이버시는 로컬&amp;rdquo;**입니다. 민감한 데이터를 다루거나 같은 작업을 잔뜩 반복할수록 로컬이 빛을 발해요.&lt;/p&gt;
&lt;h2 id="로컬-llm-뭘로-돌리죠--후보-3가지"&gt;로컬 LLM, 뭘로 돌리죠? — 후보 3가지
&lt;/h2&gt;&lt;p&gt;방법이 몇 가지 있는데, 셋을 비교해보고 골랐습니다.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;도구&lt;/th&gt;
					&lt;th&gt;장점&lt;/th&gt;
					&lt;th&gt;아쉬운 점&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Ollama&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;명령어 한 줄이면 바로 뜬다, 제일 쉬움&lt;/td&gt;
					&lt;td&gt;속은 결국 llama.cpp, 세밀한 튜닝엔 한 겹 껴 있음&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;LM Studio&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;예쁜 GUI, 클릭 몇 번&lt;/td&gt;
					&lt;td&gt;프로그램에서 불러다 자동화하기엔 무거움&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;llama.cpp ← 선택&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;엔진 그 자체&lt;/strong&gt;, VRAM 짜내기 최강, OpenAI 호환 API&lt;/td&gt;
					&lt;td&gt;명령어에 조금 익숙해져야 함&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;사실 위의 Ollama·LM Studio가 안에서 실제로 돌리는 &lt;strong&gt;엔진이 바로 llama.cpp&lt;/strong&gt;예요. GPU에 레이어를 몇 개나 올릴지까지 손으로 조절할 수 있어서 8GB 노트북에서 짜내기 가장 좋고, 딸려오는 &lt;code&gt;llama-server&lt;/code&gt;가 &lt;strong&gt;OpenAI랑 똑같은 형식의 API&lt;/strong&gt;를 열어줘서 어떤 앱이든 깔끔하게 붙습니다.&lt;/p&gt;
&lt;h2 id="전체-그림--원리는-간단합니다"&gt;전체 그림 — 원리는 간단합니다
&lt;/h2&gt;&lt;p&gt;원리는 ComfyUI 때랑 똑같아요. 무거운 계산은 로컬 GPU가 하고, 바깥 프로그램은 API로 부르기만 합니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;사용자/앱: &amp;#34;이 문단 요약해줘&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;llama-server (로컬, http://127.0.0.1:8080/v1) ← OpenAI 호환 API
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GGUF 모델이 RTX 4060에서 토큰을 하나씩 생성
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;스트리밍으로 답변이 흘러나옴
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;두 단어만 알고 가면 됩니다. &lt;strong&gt;llama.cpp&lt;/strong&gt;는 C/C++로 짠 LLM 추론 엔진이라 GPU가 약해도 모델을 CPU와 GPU에 나눠 올려 돌립니다. &lt;strong&gt;GGUF&lt;/strong&gt;는 llama.cpp 전용 모델 포맷인데, 원본 모델을 &lt;strong&gt;양자화&lt;/strong&gt;해서 용량과 VRAM 사용량을 확 줄여놓은 파일이에요.&lt;/p&gt;
&lt;h2 id="내-노트북으로-될까--사양부터-확인"&gt;내 노트북으로 될까? — 사양부터 확인
&lt;/h2&gt;&lt;p&gt;일단 될지부터 따져봤습니다. 지난 ComfyUI 글과 같은 HP Victus 16 노트북이에요.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;항목&lt;/th&gt;
					&lt;th&gt;필요 사양 (7~8B 모델)&lt;/th&gt;
					&lt;th&gt;내 노트북&lt;/th&gt;
					&lt;th&gt;판정&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;GPU VRAM&lt;/td&gt;
					&lt;td&gt;6GB 이상 (Q4 기준)&lt;/td&gt;
					&lt;td&gt;RTX 4060 Laptop 8GB&lt;/td&gt;
					&lt;td&gt;통과&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;RAM&lt;/td&gt;
					&lt;td&gt;16GB&lt;/td&gt;
					&lt;td&gt;16GB&lt;/td&gt;
					&lt;td&gt;통과&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;디스크&lt;/td&gt;
					&lt;td&gt;모델당 약 5GB&lt;/td&gt;
					&lt;td&gt;여유 충분&lt;/td&gt;
					&lt;td&gt;통과&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;VRAM 8GB면 &lt;strong&gt;7~8B급 모델을 Q4로 양자화했을 때 통째로 GPU에 올라갑니다.&lt;/strong&gt; 이 구간이 제일 빨라요. 13~14B급부터는 일부를 RAM으로 내려야 해서 속도가 뚝 떨어지고, 30B 이상은 이 노트북엔 좀 욕심입니다.&lt;/p&gt;
&lt;h2 id="모델-고르기--양자화가-절반입니다"&gt;모델 고르기 — &amp;lsquo;양자화&amp;rsquo;가 절반입니다
&lt;/h2&gt;&lt;p&gt;같은 모델이라도 **양자화(quantization)**를 어떻게 하느냐에 따라 용량·속도·품질이 갈립니다. 여기서 첫 삽질을 했어요. &amp;ldquo;이왕이면 고품질이지&amp;rdquo; 하고 Q8을 받았다가 &lt;strong&gt;VRAM이 부족해서 모델이 반쪽만 GPU에 올라가는&lt;/strong&gt; 상황을 만났거든요. 결국 8GB의 현실적인 정답은 **&lt;code&gt;Q4_K_M&lt;/code&gt;**이었습니다.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;양자화&lt;/th&gt;
					&lt;th&gt;7B 모델 용량&lt;/th&gt;
					&lt;th&gt;특징&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Q8_0&lt;/td&gt;
					&lt;td&gt;~7.5GB&lt;/td&gt;
					&lt;td&gt;원본에 제일 가깝지만 8GB엔 버겁다&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Q4_K_M&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;~4.5GB&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;속도·품질 균형, 로컬의 사실상 표준&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Q3_K_M&lt;/td&gt;
					&lt;td&gt;~3.5GB&lt;/td&gt;
					&lt;td&gt;더 가벼운 대신 품질 저하가 슬슬 느껴진다&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;모델은 7~8B급 instruct 계열(예: Qwen2.5-7B-Instruct, Llama-3.1-8B-Instruct 등)이면 무난합니다. GGUF 파일은 보통 Hugging Face의 &lt;code&gt;bartowski&lt;/code&gt; 같은 배포처에서 양자화별로 받을 수 있어요.&lt;/p&gt;
&lt;h2 id="설치하기-windows-11-기준"&gt;설치하기 (Windows 11 기준)
&lt;/h2&gt;&lt;h3 id="1-llamacpp-바이너리-받기"&gt;1. llama.cpp 바이너리 받기
&lt;/h3&gt;&lt;p&gt;소스를 직접 빌드해도 되지만, 제일 빠른 길은 &lt;strong&gt;미리 빌드된 CUDA 바이너리&lt;/strong&gt;를 받는 겁니다. &lt;a class="link" href="https://github.com/ggml-org/llama.cpp/releases" target="_blank" rel="noopener"
 &gt;GitHub 릴리스&lt;/a&gt;에서 &lt;code&gt;llama-bxxxx-bin-win-cuda-x64.zip&lt;/code&gt; 같은 파일을 받아 &lt;code&gt;C:\llama&lt;/code&gt;에 풀면 끝이에요.&lt;/p&gt;
&lt;h3 id="2-gguf-모델-다운로드"&gt;2. GGUF 모델 다운로드
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 7B급 Q4_K_M 모델 (~4.5GB) → C:\llama\models\ 에 저장&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exe&lt;/span&gt; &lt;span class="n"&gt;-L&lt;/span&gt; &lt;span class="n"&gt;-o&lt;/span&gt; &lt;span class="n"&gt;C:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;llama&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;model-q4_k_m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;gguf&lt;/span&gt; &lt;span class="p"&gt;^&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;https://huggingface.co/bartowski/Qwen2.5-7B-Instruct-GGUF/resolve/main/Qwen2.5-7B-Instruct-Q4_K_M.gguf&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="3-서버-실행"&gt;3. 서버 실행
&lt;/h3&gt;&lt;p&gt;여기서 딱 하나만 기억하면 됩니다. &lt;strong&gt;&lt;code&gt;-ngl&lt;/code&gt;&lt;/strong&gt; — GPU에 올릴 레이어 수예요. &lt;code&gt;-ngl 99&lt;/code&gt;는 &amp;ldquo;가능한 만큼 다 GPU에 올려라&amp;quot;라는 뜻이고, 7~8B Q4 모델은 8GB에 통째로 들어갑니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;C:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;llama&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;llama-server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exe&lt;/span&gt; &lt;span class="n"&gt;-m&lt;/span&gt; &lt;span class="n"&gt;C:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;llama&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;model-q4_k_m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;gguf&lt;/span&gt; &lt;span class="n"&gt;-ngl&lt;/span&gt; &lt;span class="mf"&gt;99&lt;/span&gt; &lt;span class="n"&gt;-c&lt;/span&gt; &lt;span class="mf"&gt;8192&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;-port&lt;/span&gt; &lt;span class="mf"&gt;8080&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-ngl 99&lt;/code&gt; : 전 레이어 GPU에 올리기 (VRAM 모자라면 숫자를 낮춰 일부만)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-c 8192&lt;/code&gt; : 컨텍스트 길이 8k 토큰&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--port 8080&lt;/code&gt; : 서버 포트&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="4-잘-도나-확인"&gt;4. 잘 도나 확인
&lt;/h3&gt;&lt;p&gt;브라우저에서 &lt;code&gt;http://127.0.0.1:8080&lt;/code&gt;을 열면 채팅 UI가 바로 뜹니다. GPU를 제대로 쓰는지는 서버 켤 때 로그의 &lt;code&gt;offloaded 29/29 layers to GPU&lt;/code&gt; 같은 줄로 확인해요. 저 숫자가 꽉 차 있으면 성공입니다.&lt;/p&gt;
&lt;h3 id="흔한-함정-3가지-미리-알려드려요"&gt;흔한 함정 3가지 (미리 알려드려요)
&lt;/h3&gt;&lt;p&gt;설치 자체는 쉬운데, 딱 이 세 군데서 사람들이 잘 걸립니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;함정 1 — CPU 빌드를 받아버림.&lt;/strong&gt; 릴리스에 파일이 여러 개라, 무심코 &lt;code&gt;win-cpu&lt;/code&gt; 빌드를 받으면 GPU가 놀고 답이 굼벵이처럼 느립니다. &lt;strong&gt;NVIDIA면 반드시 &lt;code&gt;cuda&lt;/code&gt; 빌드&lt;/strong&gt;를 받으세요. (AMD는 &lt;code&gt;vulkan&lt;/code&gt; 빌드)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;함정 2 — &lt;code&gt;-ngl&lt;/code&gt;을 안 줌.&lt;/strong&gt; 이 옵션을 빼먹으면 모델이 전부 CPU에서 돌아 엄청 느려집니다. &amp;ldquo;GPU 있는데 왜 이렇게 느리지?&amp;rdquo; 싶으면 십중팔구 이거예요.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;함정 3 — 욕심내서 큰 모델·높은 양자화.&lt;/strong&gt; 위에서 겪은 그 삽질입니다. VRAM을 넘기면 속도가 확 죽어요. 8GB면 7~8B Q4가 정답입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="앱이랑-붙이기--openai-호환-api"&gt;앱이랑 붙이기 — OpenAI 호환 API
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;llama-server&lt;/code&gt;가 진짜 편한 건, &lt;strong&gt;OpenAI랑 똑같은 형식의 API&lt;/strong&gt;를 열어준다는 점이에요. OpenAI 쓰던 코드에서 &lt;strong&gt;주소만 로컬로 바꾸면&lt;/strong&gt; 그대로 붙습니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;http://127.0.0.1:8080/v1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;local&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;local&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;안녕, 자기소개 해줘&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;문서 요약, 코드 자동완성, 번역 같은 걸 &lt;strong&gt;오프라인·무과금&lt;/strong&gt;으로 돌리는 나만의 백엔드가 이걸로 완성됩니다.&lt;/p&gt;
&lt;h2 id="어느-정도-빠를까요"&gt;어느 정도 빠를까요
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;7&lt;del&gt;8B Q4_K_M을 전 레이어 GPU에 올린 기준으로 **초당 40&lt;/del&gt;60토큰 정도**. 눈으로 따라 읽기 벅찰 만큼 빠릅니다.&lt;/li&gt;
&lt;li&gt;첫 응답까지는 모델을 VRAM에 올리는 시간 포함 몇 초.&lt;/li&gt;
&lt;li&gt;컨텍스트를 길게(예: 16k) 주면 VRAM이 부족해질 수 있는데, 그땐 &lt;code&gt;-ngl&lt;/code&gt; 숫자를 낮춰 일부를 RAM으로 내리면 됩니다. 조금 느려지는 대신 돌아는 가요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="솔직한-결론--어디까지-되고-어디부터-안-되나"&gt;솔직한 결론 — 어디까지 되고, 어디부터 안 되나
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;됩니다:&lt;/strong&gt; 공짜로, 인터넷 없이, 내 노트북에서 꽤 쓸 만한 LLM이 돌아갑니다. 요약·번역·분류·코딩 보조 같은 실무엔 7~8B로도 충분해요. 무엇보다 &lt;strong&gt;토큰 걱정 없이 마음껏&lt;/strong&gt; 굴릴 수 있는 게 크죠.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;한계:&lt;/strong&gt; 아무리 좋은 로컬 모델도 최상위 클라우드 모델(클로드 등)만큼 똑똑하진 않습니다. 복잡한 추론이나 긴 코드 설계는 아직 클라우드가 낫습니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;현실적인 조합:&lt;/strong&gt; 그래서 저는 &lt;strong&gt;민감한 데이터·대량 반복 작업은 로컬, 어려운 문제는 클라우드&lt;/strong&gt;로 나눠 쓰는 게 제일 낫다고 봐요. 둘은 경쟁이 아니라 역할 분담입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;돈 안 들이고 노트북 한 대로 여기까지 된다는 것 자체가, AI 문턱이 얼마나 낮아졌는지 보여주는 것 같습니다. 다음엔 이 로컬 LLM을 클로드 코드 같은 도구에 물려서 &lt;strong&gt;완전 오프라인 코딩 도우미&lt;/strong&gt;로 만들어볼 생각이에요.&lt;/p&gt;</description></item><item><title>집 컴퓨터로 AI 가상 인물 만들기 — 무료로 '말하는 아나운서' 영상까지</title><link>https://junstellar.github.io/p/ai-virtual-human/</link><pubDate>Sat, 04 Jul 2026 18:00:00 +0900</pubDate><guid>https://junstellar.github.io/p/ai-virtual-human/</guid><description>&lt;img src="https://junstellar.github.io/p/ai-virtual-human/cover.png" alt="Featured image of post 집 컴퓨터로 AI 가상 인물 만들기 — 무료로 '말하는 아나운서' 영상까지" /&gt;&lt;p&gt;요즘 유튜브나 뉴스에서 **AI가 만든 가상 인물(버추얼 휴먼)**이 부쩍 많이 보입니다. 실존하지 않는 사람이 아나운서처럼 또박또박 설명을 하죠. &amp;ldquo;저거 만들려면 비싼 서비스 써야 하는 거 아냐?&amp;rdquo; 싶은데, 결론부터 말하면 &lt;strong&gt;집에 있는 게이밍 노트북 한 대로, 돈 한 푼 안 들이고&lt;/strong&gt; 꽤 그럴듯하게 만들 수 있습니다. 이 글은 실제로 &amp;lsquo;아리&amp;rsquo;라는 가상 아나운서를 만들어 자기소개 영상까지 뽑아본 기록입니다.&lt;/p&gt;
&lt;h2 id="결과부터--아리의-자기소개"&gt;결과부터 — &amp;lsquo;아리&amp;rsquo;의 자기소개
&lt;/h2&gt;&lt;p&gt;말보다 영상이 빠르죠. 아래가 이번에 만든 결과물입니다. 실존하지 않는 사람이고, 목소리도 얼굴도 전부 AI로 생성했습니다.&lt;/p&gt;
&lt;video controls playsinline poster="cover.png" style="width:100%;max-width:520px;border-radius:14px;display:block;margin:1.2rem auto;"&gt;
 &lt;source src="ari-intro.mp4" type="video/mp4"&gt;
&lt;/video&gt;
&lt;p style="text-align:center;color:#888;font-size:0.9em;margin-top:-0.4rem;"&gt;"안녕하세요, 저는 AI로 생성된 아리입니다. 반갑습니다."&lt;/p&gt;
&lt;h2 id="가상-인물-영상은-3가지-재료로-만들어집니다"&gt;가상 인물 영상은 &amp;lsquo;3가지 재료&amp;rsquo;로 만들어집니다
&lt;/h2&gt;&lt;p&gt;챗GPT나 클로드 하나로 &amp;ldquo;영상 만들어줘&amp;rdquo; 하면 되는 게 아닙니다. 말하는 가상 인물은 &lt;strong&gt;세 가지를 각각 만들어서 합치는&lt;/strong&gt; 작업이에요.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;재료&lt;/th&gt;
					&lt;th&gt;하는 일&lt;/th&gt;
					&lt;th&gt;이번에 쓴 도구&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;① 얼굴&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;존재하지 않는 인물의 사진&lt;/td&gt;
					&lt;td&gt;SDXL (로컬 이미지 생성)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;② 목소리&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;대본을 사람 음성으로&lt;/td&gt;
					&lt;td&gt;edge-tts (무료)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;③ 입 움직임&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;음성에 맞춰 입을 립싱크&lt;/td&gt;
					&lt;td&gt;SadTalker (무료·로컬)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;셋 다 &lt;strong&gt;무료이고, 인터넷 없이 내 GPU에서&lt;/strong&gt; 돌아갑니다. 하나씩 볼게요.&lt;/p&gt;
&lt;h2 id="-얼굴--sdxl로-아리를-빚다"&gt;① 얼굴 — SDXL로 &amp;lsquo;아리&amp;rsquo;를 빚다
&lt;/h2&gt;&lt;p&gt;먼저 세상에 없는 얼굴이 필요합니다. 저는 로컬에서 SDXL(Stable Diffusion XL)로 &amp;ldquo;단정하고 신뢰감 있는 한국 여성 아나운서&amp;quot;를 여러 장 생성한 뒤, 마음에 드는 얼굴을 골랐습니다. 이때 &lt;strong&gt;시드(seed)를 고정&lt;/strong&gt;하면 같은 인물의 표정·각도만 바꿔가며 여러 컷을 뽑을 수 있어요. 이렇게 &amp;lsquo;아리&amp;rsquo;라는 고정 캐릭터가 탄생했습니다.&lt;/p&gt;
&lt;p&gt;&lt;img alt="AI로 생성한 가상 아나운서 ‘아리’의 얼굴" class="gallery-image" data-flex-basis="240px" data-flex-grow="100" height="1024" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-virtual-human/cover.png" srcset="https://junstellar.github.io/p/ai-virtual-human/cover_hu_ffca894a472976fd.png 800w, https://junstellar.github.io/p/ai-virtual-human/cover.png 1024w" width="1024"&gt;&lt;/p&gt;
&lt;h2 id="-목소리--edge-tts로-공짜-한국어-음성"&gt;② 목소리 — edge-tts로 공짜 한국어 음성
&lt;/h2&gt;&lt;p&gt;대본은 아주 짧게 잡았습니다. &lt;em&gt;&amp;ldquo;안녕하세요, 저는 AI로 생성된 아리입니다. 반갑습니다.&amp;rdquo;&lt;/em&gt; 이 문장을 마이크로소프트의 &lt;strong&gt;edge-tts&lt;/strong&gt;(무료 TTS)로 한국어 음성 파일로 만들었습니다. 여성 음성 &lt;code&gt;ko-KR-SunHiNeural&lt;/code&gt;을 쓰면 발음이 꽤 자연스럽습니다.&lt;/p&gt;
&lt;h2 id="-입-움직임--sadtalker로-립싱크"&gt;③ 입 움직임 — SadTalker로 립싱크
&lt;/h2&gt;&lt;p&gt;핵심은 여기입니다. &lt;strong&gt;SadTalker&lt;/strong&gt;는 사진 한 장과 음성 파일만 주면, 음성에 맞춰 입과 표정을 움직이는 영상을 만들어 주는 오픈소스 프로젝트예요. 완전 무료이고 내 GPU에서 돌아갑니다. 얼굴 사진 + 아까 만든 음성을 넣으면 끝.&lt;/p&gt;
&lt;p&gt;그런데 여기서부터 &lt;strong&gt;삽질&lt;/strong&gt;이 시작됐습니다. 그냥은 안 예쁘게 나오더라고요. 실제로 통했던 팁을 공유합니다.&lt;/p&gt;
&lt;h3 id="팁-1-소스-사진은-반드시-입을-다문-얼굴로"&gt;팁 1. 소스 사진은 반드시 &amp;lsquo;입을 다문&amp;rsquo; 얼굴로
&lt;/h3&gt;&lt;p&gt;처음엔 활짝 웃으며 이빨이 보이는 사진을 넣었더니, 입 주변이 뭉개지면서 &lt;strong&gt;에일리언처럼&lt;/strong&gt; 어색했습니다. 알고 보니 SadTalker는 &lt;strong&gt;입을 다문 무표정 사진&lt;/strong&gt;을 소스로 줄 때 립싱크가 훨씬 자연스럽게 나옵니다. 입 벌린 사진은 그 벌어진 입 위에 억지로 립싱크를 얹느라 망가지는 거죠.&lt;/p&gt;
&lt;p&gt;&lt;img alt="입 벌린 소스(왼쪽)는 립싱크가 어색하고, 입 다문 소스(오른쪽)가 훨씬 자연스럽다" class="gallery-image" data-flex-basis="489px" data-flex-grow="203" height="512" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/p/ai-virtual-human/src-comparison.png" srcset="https://junstellar.github.io/p/ai-virtual-human/src-comparison_hu_9d566e9c97ae602d.png 800w, https://junstellar.github.io/p/ai-virtual-human/src-comparison.png 1044w" width="1044"&gt;&lt;/p&gt;
&lt;p&gt;같은 인물(시드 고정)로 &lt;strong&gt;입 다문 버전&lt;/strong&gt;을 새로 뽑아 소스로 교체하니 확연히 좋아졌습니다.&lt;/p&gt;
&lt;h3 id="팁-2-expression_scale은-과하지-않게"&gt;팁 2. &amp;rsquo;expression_scale&amp;rsquo;은 과하지 않게
&lt;/h3&gt;&lt;p&gt;표정 강도 옵션(&lt;code&gt;expression_scale&lt;/code&gt;)을 1.2로 주면 입이 과장돼서 부자연스럽습니다. &lt;strong&gt;1.0 정도&lt;/strong&gt;가 딱 적당했어요.&lt;/p&gt;
&lt;h3 id="팁-3-어깨까지-나오게-하려면-_full-결과물을-쓰세요"&gt;팁 3. 어깨까지 나오게 하려면 &amp;lsquo;_full&amp;rsquo; 결과물을 쓰세요
&lt;/h3&gt;&lt;p&gt;SadTalker는 결과 영상을 두 개 만듭니다. 하나는 &lt;strong&gt;얼굴만 크롭한 버전&lt;/strong&gt;, 다른 하나는 파일명에 &lt;code&gt;_full&lt;/code&gt;이 붙은 &lt;strong&gt;원본 사진 전체(어깨·상반신 포함)에 얼굴을 합성한 버전&lt;/strong&gt;입니다. 얼굴만 크게 나와서 답답하다면 &lt;code&gt;_full&lt;/code&gt; 쪽을 쓰면 됩니다. 저도 이걸 몰라서 한참 헤맸어요.&lt;/p&gt;
&lt;h3 id="팁-4-설치-pytorch는-cu121-조합이-안전"&gt;팁 4. (설치) PyTorch는 cu121 조합이 안전
&lt;/h3&gt;&lt;p&gt;설치할 때 가장 흔한 함정이 GPU용 PyTorch입니다. 최신 그래픽 드라이버가 깔린 PC에서는 오래된 &lt;code&gt;cu118&lt;/code&gt;(torch 2.0.1) 조합이 &lt;code&gt;c10_cuda.dll&lt;/code&gt; 로딩 에러(WinError 127)를 내는 경우가 있어요. &lt;strong&gt;&lt;code&gt;torch 2.1.2 + torchvision 0.16.2&lt;/code&gt; (cu121)&lt;/strong&gt; 조합으로 설치하면 안정적이고, SadTalker가 요구하는 라이브러리와도 호환됩니다.&lt;/p&gt;
&lt;h2 id="어느-정도-gpu가-필요할까"&gt;어느 정도 GPU가 필요할까
&lt;/h2&gt;&lt;p&gt;무겁지 않습니다. &lt;strong&gt;VRAM 8GB면 충분&lt;/strong&gt;해요. 저는 노트북용 RTX 4060(8GB)으로 돌렸고, RTX 3070급이면 동일하게 됩니다. 짧은 6초 클립은 몇 분이면 나오고, 화질 보정(gfpgan)까지 켜도 부담 없습니다. 다만 &lt;strong&gt;8분짜리 긴 영상&lt;/strong&gt;을 통째로 립싱크하면 프레임이 수만 장이라 꽤 오래 걸리니, 긴 강의 영상은 발표자를 화면 한켠에 작게 넣는 식이 현실적입니다.&lt;/p&gt;
&lt;h2 id="솔직한-결론--어디까지-되고-어디부터-안-되나"&gt;솔직한 결론 — 어디까지 되고, 어디부터 안 되나
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;된다:&lt;/strong&gt; 공짜로, 내 PC에서, 세상에 없는 인물이 내 대본을 읽는 영상을 만들 수 있다. 개인 프로젝트·학습·프로토타입엔 충분하다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;한계:&lt;/strong&gt; 아무리 팁을 써도 입 주변은 &amp;lsquo;완벽하게 자연스럽다&amp;rsquo;까진 못 간다. 자세히 보면 티가 난다. &lt;strong&gt;방송급으로 매끈한 걸 원하면&lt;/strong&gt; HeyGen·Synthesia 같은 상업 웹서비스(유료)가 답이다. 이때도 얼굴은 지금처럼 SDXL로 만든 걸 아바타로 넣으면 된다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;라이선스 주의:&lt;/strong&gt; SadTalker는 본래 연구용으로 공개된 프로젝트라, 수익화 콘텐츠에 쓸 땐 라이선스를 한 번 확인하는 게 안전하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;돈 안 들이고 여기까지 만들 수 있다는 것 자체가, AI 도구가 얼마나 문턱을 낮춰놨는지 보여주는 것 같습니다. 다음엔 이 &amp;lsquo;아리&amp;rsquo;에게 슬라이드를 붙여 진짜 강의를 시켜볼 생각입니다.&lt;/p&gt;</description></item><item><title>클로드가 내 노트북에서 직접 그림을 그리게 만들기 — ComfyUI + SDXL 로컬 설치기</title><link>https://junstellar.github.io/p/claude-local-image-generation/</link><pubDate>Thu, 02 Jul 2026 23:30:00 +0900</pubDate><guid>https://junstellar.github.io/p/claude-local-image-generation/</guid><description>&lt;p&gt;클로드(Claude)는 텍스트 AI라서 스스로 그림을 그리지 못한다. 하지만 &lt;strong&gt;로컬 PC에서 돌아가는 이미지 생성 서버를 붙여주면&lt;/strong&gt;, &amp;ldquo;고양이 그려줘&amp;rdquo; 한마디로 클로드가 프롬프트를 다듬어 그림을 만들어내게 할 수 있다. 오늘은 게이밍 노트북(RTX 4060)에 ComfyUI + SDXL을 설치해서 이걸 실제로 구축한 과정을 정리한다. 비용은 0원, 인터넷 없이도 동작한다.&lt;/p&gt;
&lt;h2 id="어떤-도구로-그리게-할까--선택지-3개"&gt;어떤 도구로 그리게 할까 — 선택지 3개
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Automatic1111 WebUI (로컬)&lt;/strong&gt; — 전통적인 웹 UI라 직접 만지기엔 가장 쉽지만, 메모리 관리가 헐거워 VRAM 8GB에서 SDXL이 버겁다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;나노바나나 (클라우드, Gemini API)&lt;/strong&gt; — 설치 없이 API 키만 있으면 되고 품질도 좋지만, 장당 약 50원 + 인터넷 필수 + 워터마크가 박힌다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ComfyUI (로컬) ← 최종 선택&lt;/strong&gt; — 노드 UI가 낯설지만 어차피 클로드가 API로 호출하니 상관없다. 세 후보 중 &lt;strong&gt;VRAM 효율이 가장 좋고&lt;/strong&gt;(8GB 노트북에 결정적), 워크플로 전체가 JSON이라 &lt;strong&gt;프로그램이 호출하기 가장 깔끔하며&lt;/strong&gt;, 무료·무제한·오프라인이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="전체-구조"&gt;전체 구조
&lt;/h2&gt;&lt;p&gt;핵심 아이디어는 간단하다. 클로드는 &amp;ldquo;지휘&amp;quot;만 하고, 그림은 로컬 GPU가 그린다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;사용자: &amp;#34;노을 지는 서울 야경 그려줘&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;클로드: 한국어 요청 → 영어 프롬프트로 변환·보강
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ComfyUI 서버 (로컬, http://127.0.0.1:8188)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SDXL 모델이 RTX 4060에서 이미지 생성 (20~40초)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;클로드가 완성된 PNG를 채팅에 표시
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ComfyUI&lt;/strong&gt;: Stable Diffusion을 돌리는 오픈소스 프로그램. REST API를 제공해서 외부 프로그램(클로드)이 호출할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SDXL&lt;/strong&gt;: Stability AI의 이미지 생성 모델. 1024×1024 네이티브 해상도.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="내-pc로-가능할까--사양-확인"&gt;내 PC로 가능할까? — 사양 확인
&lt;/h2&gt;&lt;p&gt;설치 전에 확인한 최소 조건과 내 노트북(HP Victus 16) 사양:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;항목&lt;/th&gt;
					&lt;th&gt;필요 사양&lt;/th&gt;
					&lt;th&gt;내 노트북&lt;/th&gt;
					&lt;th&gt;판정&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;GPU VRAM&lt;/td&gt;
					&lt;td&gt;8GB 이상 권장 (SDXL)&lt;/td&gt;
					&lt;td&gt;RTX 4060 Laptop 8GB&lt;/td&gt;
					&lt;td&gt;통과&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;RAM&lt;/td&gt;
					&lt;td&gt;16GB&lt;/td&gt;
					&lt;td&gt;16GB&lt;/td&gt;
					&lt;td&gt;통과&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;디스크&lt;/td&gt;
					&lt;td&gt;약 20GB&lt;/td&gt;
					&lt;td&gt;351GB 여유&lt;/td&gt;
					&lt;td&gt;통과&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;VRAM 8GB가 SDXL의 사실상 마지노선이다. 6GB 이하면 더 가벼운 SD 1.5 모델을 쓰는 게 낫다.&lt;/p&gt;
&lt;h2 id="설치-과정-windows-11-기준"&gt;설치 과정 (Windows 11 기준)
&lt;/h2&gt;&lt;h3 id="1-comfyui-클론--가상환경"&gt;1. ComfyUI 클론 + 가상환경
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;clone&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;-depth&lt;/span&gt; &lt;span class="mf"&gt;1&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;comfyanonymous&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ComfyUI&lt;/span&gt; &lt;span class="n"&gt;C:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;ComfyUI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="n"&gt;-m&lt;/span&gt; &lt;span class="n"&gt;venv&lt;/span&gt; &lt;span class="n"&gt;C:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;ComfyUI&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;venv&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="2-pytorch-cuda-버전-설치"&gt;2. PyTorch (CUDA 버전) 설치
&lt;/h3&gt;&lt;p&gt;일반 &lt;code&gt;pip install torch&lt;/code&gt;는 CPU 버전이 설치되므로, &lt;strong&gt;반드시 CUDA 인덱스를 지정&lt;/strong&gt;해야 GPU를 쓴다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;C:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;ComfyUI&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;venv&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Scripts&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exe&lt;/span&gt; &lt;span class="n"&gt;-m&lt;/span&gt; &lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt; &lt;span class="n"&gt;torchvision&lt;/span&gt; &lt;span class="n"&gt;torchaudio&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;-index-url&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;pytorch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;whl&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cu128&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;설치 후 GPU 인식 확인:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;C:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;ComfyUI&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;venv&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Scripts&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exe&lt;/span&gt; &lt;span class="n"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;import torch; print(torch.cuda.is_available())&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# True 가 나와야 함&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="3-의존성--sdxl-모델-다운로드"&gt;3. 의존성 + SDXL 모델 다운로드
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;C:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;ComfyUI&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;venv&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Scripts&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exe&lt;/span&gt; &lt;span class="n"&gt;-m&lt;/span&gt; &lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;-r&lt;/span&gt; &lt;span class="n"&gt;C:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;ComfyUI&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;requirements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;txt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# SDXL base 모델 (~6.5GB) → models\checkpoints\ 에 저장&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exe&lt;/span&gt; &lt;span class="n"&gt;-L&lt;/span&gt; &lt;span class="n"&gt;-o&lt;/span&gt; &lt;span class="n"&gt;C:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;ComfyUI&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;checkpoints&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;sd_xl_base_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;safetensors&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;huggingface&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;stabilityai&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;stable-diffusion&lt;/span&gt;&lt;span class="n"&gt;-xl-base&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sd_xl_base_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;safetensors&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="4-서버-실행"&gt;4. 서버 실행
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;C:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;ComfyUI&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;venv&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Scripts&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exe&lt;/span&gt; &lt;span class="n"&gt;C:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;ComfyUI&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;py&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;-port&lt;/span&gt; &lt;span class="mf"&gt;8188&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;브라우저에서 &lt;code&gt;http://127.0.0.1:8188&lt;/code&gt;을 열면 노드 기반 UI가 뜨지만, 클로드 연동이 목적이라면 UI를 직접 만질 일은 거의 없다.&lt;/p&gt;
&lt;h2 id="클로드와-연결하기"&gt;클로드와 연결하기
&lt;/h2&gt;&lt;p&gt;ComfyUI의 API는 두 개 엔드포인트가 핵심이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;POST /prompt&lt;/code&gt; — 워크플로(JSON)를 제출하면 &lt;code&gt;prompt_id&lt;/code&gt;를 돌려준다&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /history/{prompt_id}&lt;/code&gt; — 생성이 끝나면 결과 이미지 파일명이 담겨 온다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이걸 호출하는 파이썬 스크립트(&lt;code&gt;generate.py&lt;/code&gt;)를 하나 만들고, 클로드 코드(Claude Code)에는 &lt;strong&gt;스킬(skill)&lt;/strong&gt; 하나를 등록했다. 스킬은 &amp;ldquo;이런 요청이 오면 이 스크립트를 실행해라&amp;quot;라는 지침서다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;name: draw-local
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;description: 사용자가 그림을 요청하면 로컬 ComfyUI로 생성. 한국어 프롬프트는 영어로 번역.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;C:\ComfyUI\venv\Scripts\python.exe C:\ComfyUI\generate.py &amp;#34;PROMPT&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;실행 후 SAVED &amp;lt;경로&amp;gt;가 출력되면 그 PNG를 채팅에 표시할 것.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;이제 클로드에게 &amp;ldquo;~그려줘&amp;quot;라고 하면: ① 서버가 꺼져 있으면 자동 기동 → ② 한국어를 영어 프롬프트로 번역·보강 → ③ 생성 → ④ 채팅에 그림 표시까지 알아서 한다.&lt;/p&gt;
&lt;h2 id="결과물--프롬프트-예시-3개"&gt;결과물 — 프롬프트 예시 3개
&lt;/h2&gt;&lt;p&gt;실제로 이 노트북에서 생성한 이미지들이다. 프롬프트를 어떻게 쓰느냐가 품질의 절반이다.&lt;/p&gt;
&lt;h3 id="예시-1--인물동물--조명-지정"&gt;예시 1 — 인물/동물 + 조명 지정
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;a cute orange cat sitting on a windowsill, warm sunlight, photorealistic
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img alt="창가의 고양이" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/images/comfyui/orange-cat.jpg"&gt;&lt;/p&gt;
&lt;h3 id="예시-2--풍경--분위기-키워드"&gt;예시 2 — 풍경 + 분위기 키워드
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;a serene mountain lake at sunrise, mist over the water, photorealistic
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img alt="새벽 산정호수" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/images/comfyui/mountain-lake.jpg"&gt;&lt;/p&gt;
&lt;h3 id="예시-3--실존-장소--시간대--스타일"&gt;예시 3 — 실존 장소 + 시간대 + 스타일
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Seoul city skyline at sunset, Namsan Tower and Han River, glowing orange and purple sky,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;city lights turning on at dusk, skyscrapers, photorealistic, cinematic lighting, high detail
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img alt="노을 지는 서울 야경" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://junstellar.github.io/images/comfyui/seoul-sunset.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;프롬프트 팁&lt;/strong&gt;: ① 영어로 쓸 것(SDXL은 영어 학습 비중이 압도적) ② &lt;code&gt;photorealistic&lt;/code&gt;, &lt;code&gt;cinematic lighting&lt;/code&gt; 같은 스타일 키워드를 뒤에 붙일 것 ③ 피하고 싶은 요소는 네거티브 프롬프트(&lt;code&gt;blurry, low quality, watermark...&lt;/code&gt;)로 분리할 것.&lt;/p&gt;
&lt;h2 id="실측-성능-rtx-4060-laptop-8gb"&gt;실측 성능 (RTX 4060 Laptop, 8GB)
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;첫 생성: 약 1분 (서버 기동 + 모델 VRAM 로딩 포함)&lt;/li&gt;
&lt;li&gt;이후: &lt;strong&gt;장당 20~40초&lt;/strong&gt; (1024×1024, 28스텝)&lt;/li&gt;
&lt;li&gt;스텝을 20으로 줄이면 ~24초, 품질 차이는 크지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="정리"&gt;정리
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;8GB VRAM 노트북이면 SDXL 로컬 생성이 충분히 실용적이다.&lt;/li&gt;
&lt;li&gt;클로드 연동의 핵심은 &amp;ldquo;ComfyUI API를 부르는 스크립트 + 스킬 등록&amp;rdquo; 두 가지뿐.&lt;/li&gt;
&lt;li&gt;클라우드 API(나노바나나 등) 대비 장점: 무료·무제한·오프라인. 단점: 품질과 텍스트 렌더링은 클라우드 모델이 우세.&lt;/li&gt;
&lt;li&gt;다음 단계: 나노바나나(Gemini 이미지 API) 연동을 추가해서 로컬/클라우드를 골라 쓰는 것도 고려 중.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>🫥 사진 속 얼굴, 서버 없이 브라우저에서 모자이크하기</title><link>https://junstellar.github.io/p/face-mosaic/</link><pubDate>Wed, 01 Jul 2026 07:00:00 +0900</pubDate><guid>https://junstellar.github.io/p/face-mosaic/</guid><description>&lt;p&gt;블로그에 작은 도구 하나를 추가했습니다. &lt;a class="link" href="https://junstellar.github.io/tools/face-mosaic/" &gt;얼굴 모자이크&lt;/a&gt; — 사진 속 얼굴을 자동으로 찾아 가려주는 웹 도구입니다.&lt;/p&gt;
&lt;p&gt;사진을 SNS나 블로그에 올릴 때, 같이 찍힌 사람의 얼굴을 가려야 할 때가 있죠. 보통은 앱을 깔거나, 얼굴 사진을 모르는 웹사이트에 업로드해야 합니다. 그런데 &lt;strong&gt;얼굴 사진을 남의 서버에 올린다는 게&lt;/strong&gt; 영 찜찜합니다. 그래서 &lt;strong&gt;사진이 아예 내 브라우저 밖으로 나가지 않는&lt;/strong&gt; 도구를 만들었습니다.&lt;/p&gt;
&lt;p&gt;서버 없이 얼굴을 찾을 수 있는 핵심은 &lt;strong&gt;온디바이스 AI&lt;/strong&gt;입니다. 구글의 &lt;a class="link" href="https://developers.google.com/mediapipe" target="_blank" rel="noopener"
 &gt;MediaPipe Face Detector&lt;/a&gt;라는 얼굴 검출 모델을 브라우저에서 직접 돌립니다. 모델 파일(약 200KB)만 한 번 내려받으면, 그다음부터는:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;사진을 캔버스에 올리고&lt;/li&gt;
&lt;li&gt;브라우저 안에서 얼굴 위치를 찾고&lt;/li&gt;
&lt;li&gt;그 영역에 효과를 입힙니다&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이 모든 게 &lt;strong&gt;내 컴퓨터 안에서&lt;/strong&gt; 일어납니다. 사진은 단 한 번도 서버로 전송되지 않아요.&lt;/p&gt;
&lt;p&gt;가리는 방식은 아래와 같이 &lt;strong&gt;4가지 방식으로 구현&lt;/strong&gt;했습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;모자이크&lt;/strong&gt; — 픽셀을 뭉개는 전통적 방식&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;블러&lt;/strong&gt; — 부드럽게 흐리기&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;검은 박스&lt;/strong&gt; — 완전히 가리기&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;이모지&lt;/strong&gt; — 😀🐱⭐ 로 덮기&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;강도와 가리는 범위도 슬라이더로 조절할 수 있습니다. 얼굴 검출 모델도 완벽하진 않아서 옆얼굴이나 작게 찍힌 얼굴은 놓치기도 하는데, 그럴 땐 &lt;strong&gt;마우스로 직접 영역을 드래그&lt;/strong&gt;해서 추가하거나, 잘못 잡힌 곳을 클릭해서 지울 수 있게 했습니다.&lt;/p&gt;
&lt;p&gt;사실 이 도구는 &lt;strong&gt;Claude Code로 개발했습니다.&lt;/strong&gt; 코딩을 잘 몰라도, &amp;ldquo;사진 얼굴을 모자이크하는 웹페이지를 만들어줘&amp;quot;라고 대화하듯 시키면 이 정도는 만들어집니다. 여러분도 충분히 직접 만들 수 있어요. 그리고 보안이라는 관점에서 보면, &lt;strong&gt;직접 만들어 쓰는 게 사실 가장 확실합니다.&lt;/strong&gt; 물론 그게 귀찮은 분들은 &lt;strong&gt;제가 만들어 둔 걸 그냥 써보세요.&lt;/strong&gt; 어차피 사진은 브라우저 밖으로 안 나가니까요.&lt;/p&gt;
&lt;h3 id="써보기"&gt;써보기
&lt;/h3&gt;&lt;p&gt;👉 &lt;strong&gt;&lt;a class="link" href="https://junstellar.github.io/tools/face-mosaic/" &gt;얼굴 모자이크 도구 열기&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;상단 메뉴의 &lt;strong&gt;도구&lt;/strong&gt;에서도 언제든 들어올 수 있습니다.&lt;/p&gt;</description></item><item><title>어떻게(How)는 풀렸고, 이제 뭘(What) 할지가 고민이다</title><link>https://junstellar.github.io/p/how-and-what/</link><pubDate>Fri, 26 Jun 2026 20:00:00 +0900</pubDate><guid>https://junstellar.github.io/p/how-and-what/</guid><description>&lt;p&gt;이 블로그에서 AI 이야기를 다뤄보기로 했다. 그런데 막상 그 첫 글을 뭘로 쓸지 한참 고민했다. 그래서 그냥, 요즘 내 머릿속을 제일 많이 차지하는 생각 — 내가 AI를 어떻게 보게 됐는지, 그 이야기부터 풀어보기로 했다.&lt;/p&gt;
&lt;h2 id="처음엔-진짜-못-쓸-줄-알았다"&gt;처음엔 진짜 못 쓸 줄 알았다
&lt;/h2&gt;&lt;p&gt;GPT가 처음 나왔을 때, 신기하긴 했지만 막상 시켜보면 영 허접했다. 그럴듯하게 말은 하는데 알맹이가 없고, 조금만 깊이 들어가면 엉뚱한 소리를 했다. &amp;ldquo;그냥 신기한 장난감이네&amp;rdquo; 하고 넘겼다. 이게 내 삶을 바꿀 거라곤 생각도 안 했다.&lt;/p&gt;
&lt;p&gt;그런데 체감으로 1년도 안 돼 세상이 바뀌어 있었다. GPT부터 클로드 코드까지 이것저것 써보는 사이, 그 허접하던 게 이제는 내가 생각한 거의 모든 걸 해낸다. 따라가기 벅찰 만큼 빠르다. 얼마 전까지 &amp;ldquo;못 쓰겠다&amp;quot;던 도구인데, 지금은 없으면 일을 어떻게 했나 싶다.&lt;/p&gt;
&lt;h2 id="how는-풀렸는데-이제-what이-숙제다"&gt;how는 풀렸는데, 이제 what이 숙제다
&lt;/h2&gt;&lt;p&gt;여기서 크게 느낀 게 있다.&lt;/p&gt;
&lt;p&gt;예전엔 늘 &amp;ldquo;이걸 &lt;em&gt;어떻게&lt;/em&gt; 하지?&amp;ldquo;가 벽이었다. 방법을 몰라서, 손이 안 따라줘서 멈췄다. 그런데 이제 how는 AI가 풀어준다. 말주변도 글재주도 없던 내가, 본업인 개발만큼이나 글도 그럴듯하게 쓰게 된 느낌이다.&lt;/p&gt;
&lt;p&gt;막상 그 벽이 사라지고 나니, 전혀 다른 질문이 남았다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;그래서 나는, 뭘(what) 해야 할까?&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이게 오히려 더 어렵다. 할 수 있는 게 많아질수록, 정작 뭘 해야 할지가 더 막막해진다. 게다가 묘한 기분도 든다 — 내가 이걸 할 수 있게 됐다는 건, 결국 남들도 다 할 수 있게 됐다는 뜻이니까. 누구나 같은 도구를 손에 쥔 시대에, 그럼 나는 뭘 해야 하나 싶어진다.&lt;/p&gt;
&lt;p&gt;사실 고백하자면 나는 원래 하고 싶은 건 많은데 아무것도 못 하는 사람이었다. &amp;ldquo;방법을 몰라서 못 했다&amp;quot;고 핑계 댔지만, 진짜 문제는 how가 아니라 what — 무엇을 할지, 그리고 그걸 시작할 의지 — 가 없었던 거다.&lt;/p&gt;
&lt;h2 id="그래서-같이-삽질하기로-했다"&gt;그래서, 같이 삽질하기로 했다
&lt;/h2&gt;&lt;p&gt;그런데 AI를 쓰면서 그 벽이 낮아졌다. 막막해서 시작도 못 하던 일을, 이제는 일단 부딪혀볼 수 있게 됐다. 그렇게 찾은 나의 첫 번째 what이 바로 이거다 — AI 이야기를 나누는 것.&lt;/p&gt;
&lt;p&gt;그런데 전문가들이 잘 정리해 둔 깔끔한 정답은 이미 세상에 차고 넘친다. 그래서 나는 반대로 가보려 한다. 잘한 것보다, 직접 해보다 삽질한 것·실패한 것·보기 좋게 망한 것들을 그대로 풀어놓는 쪽으로. 어차피 성공보다 실패가 더 많을 거고, 솔직히 그 과정이 더 재밌으니까.&lt;/p&gt;
&lt;p&gt;그냥 평범한 내가, 평범한 사람들 눈높이에서 부딪히고 깨지며 배운 걸 나누고 싶다. 전문가의 깊이는 없어도, 같은 자리에서 함께 헤매는 사람의 시선이라는 게 분명 쓸모가 있다고 믿는다. 나처럼 &amp;ldquo;신기한데 이걸 어떻게 쓰지?&amp;rdquo; 하던 사람과 그 시행착오를 같이 웃으며 즐길 수 있다면, 그걸로 충분하다.&lt;/p&gt;
&lt;p&gt;how는 AI가 풀어줬다. 이제 그 도구로 무엇을 나눌지, 여기서부터 시작한다.&lt;/p&gt;
&lt;p&gt;천천히, 그러나 분명히 — 투더문. 🚀&lt;/p&gt;</description></item></channel></rss>