<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ko"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://krenin88.github.io/devlog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://krenin88.github.io/devlog/" rel="alternate" type="text/html" hreflang="ko" /><updated>2026-10-25T03:17:30+09:00</updated><id>https://krenin88.github.io/devlog/feed.xml</id><title type="html">Krenin의 devlog</title><subtitle>앱과 게임을 Claude로 만듭니다. 개발하면서 겪은 일을 기록합니다.</subtitle><author><name>Krenin</name></author><entry><title type="html">agent-device로 테스트 자동화 (1)</title><link href="https://krenin88.github.io/devlog/2026/10/25/agent-device-test-automation-1.html" rel="alternate" type="text/html" title="agent-device로 테스트 자동화 (1)" /><published>2026-10-25T00:00:00+09:00</published><updated>2026-10-25T00:00:00+09:00</updated><id>https://krenin88.github.io/devlog/2026/10/25/agent-device-test-automation-1</id><content type="html" xml:base="https://krenin88.github.io/devlog/2026/10/25/agent-device-test-automation-1.html"><![CDATA[<p>agent-device는 AI 에이전트가 iOS·Android 앱을 직접 조작하고 화면을 확인하게 해 주는 Callstack의 오픈소스 도구입니다. 이 글은 Flutter 앱 테스트에 agent-device를 고른 이유, Claude Code에 붙이는 설치 과정, 기본 사용 흐름, Flutter에서 주의할 점을 정리합니다. 기준 버전은 0.21.31(2026-10-24)입니다.</p>

<h2 id="왜-agent-device인가">왜 agent-device인가</h2>

<p>Flutter는 한 코드로 Android와 iOS 앱을 함께 만듭니다. 그래서 기기 테스트 도구도 iOS까지 다뤄야 했습니다. 처음 검토한 Google Artemis는 자연어로 앱을 테스트하는 에이전트지만 Android만 지원하고, iOS는 로드맵에만 올라 있습니다.</p>

<p>함께 비교한 후보는 다음과 같습니다.</p>

<ul>
  <li><strong>mobile-mcp</strong>는 iOS·Android의 탭·스크린샷·접근성 트리를 MCP(AI가 외부 도구를 부르는 연결 방식)로 제공합니다. 다만 세션, 리플레이, 증거 저장이 없어서 확인한 흐름을 CI로 넘기기 어렵습니다.</li>
  <li><strong>Maestro</strong>는 YAML로 흐름을 고정하는 E2E 테스트 프레임워크이고 MCP도 있습니다. 에이전트가 화면을 탐색하는 쪽보다 정해진 흐름을 반복하는 회귀 테스트에 맞습니다.</li>
  <li><strong>Arbigent</strong>는 Maestro 위에 얹은 AI 에이전트입니다. 작성자 스스로 속도 1/5, 신뢰성 3/5로 평가했고 YAML 형식이 바뀌는 중이었습니다.</li>
  <li><strong>상용 서비스</strong>(Drizz, Minitap 등)는 실기기 팜과 자연어 에이전트를 묶어 팔지만, 공개된 자료가 벤더 자료뿐이라 비용과 품질을 확인할 수 없었습니다.</li>
</ul>

<p>agent-device는 iOS·Android의 시뮬레이터와 실기기를 한 도구로 다루고, 문서에 Flutter 지원이 명시돼 있습니다. CLI와 MCP를 둘 다 제공하고, 확인한 흐름을 <code class="language-plaintext highlighter-rouge">.ad</code> 스크립트로 저장해 다시 돌리거나 Maestro YAML로 내보낼 수 있습니다. 라이선스는 MIT입니다.</p>

<h2 id="설치">설치</h2>

<p>CLI를 전역으로 설치하고 환경을 점검합니다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm i <span class="nt">-g</span> agent-device@latest
agent-device doctor
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">doctor</code>는 연결된 기기, 툴체인, 개발 서버 상태를 점검합니다. 이번 환경에서는 쓰지 않는 플랫폼(HarmonyOS, Vega) 경고만 남고 통과했습니다.</p>

<p>Claude Code에는 MCP 서버로 등록합니다. <code class="language-plaintext highlighter-rouge">-s user</code>로 등록하면 모든 프로젝트에서 같은 도구가 보입니다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>claude mcp add <span class="nt">-s</span> user agent-device <span class="nt">--</span> agent-device mcp
</code></pre></div></div>

<p>agent-device 저장소에는 에이전트용 스킬 세 개(<code class="language-plaintext highlighter-rouge">agent-device</code>, <code class="language-plaintext highlighter-rouge">ios-simulator</code>, <code class="language-plaintext highlighter-rouge">android-emulator</code>)가 있습니다. 공식 안내는 <code class="language-plaintext highlighter-rouge">npx skills add callstack/agent-device</code>이지만, Claude Code 자동 모드에서는 안전 검사에 막혔습니다. 이때는 저장소를 clone한 뒤 <code class="language-plaintext highlighter-rouge">skills/</code> 아래 세 폴더를 <code class="language-plaintext highlighter-rouge">~/.claude/skills/</code>로 복사하면 됩니다.</p>

<p>되돌릴 때는 <code class="language-plaintext highlighter-rouge">claude mcp remove agent-device -s user</code>, <code class="language-plaintext highlighter-rouge">npm uninstall -g agent-device</code>를 실행하고 스킬 폴더 세 개를 지웁니다.</p>

<h2 id="기본-사용-흐름">기본 사용 흐름</h2>

<p>기본 흐름은 기기 준비, 앱 열기, 조작, 확인, 종료입니다. 기기는 <code class="language-plaintext highlighter-rouge">--platform</code>과 <code class="language-plaintext highlighter-rouge">--device</code>로 고릅니다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>agent-device boot <span class="nt">--platform</span> android <span class="nt">--device</span> <span class="s2">"&lt;에뮬레이터 이름&gt;"</span> <span class="nt">--headless</span>
agent-device <span class="nb">install</span> /절대/경로/app-debug.apk <span class="nt">--platform</span> android <span class="nt">--device</span> <span class="s2">"&lt;에뮬레이터 이름&gt;"</span>
agent-device open &lt;bundle-id&gt; <span class="nt">--platform</span> android <span class="nt">--device</span> <span class="s2">"&lt;에뮬레이터 이름&gt;"</span> <span class="nt">--foreground</span>
agent-device press @e5 <span class="nt">--settle</span>
agent-device fill @e11 <span class="s2">"텍스트"</span> <span class="nt">--settle</span>
agent-device <span class="nb">wait </span>text <span class="s2">"&lt;기대 문구&gt;"</span>
agent-device screenshot /절대/경로/home.png
agent-device close
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">open</code>은 화면을 읽어 버튼·입력란 같은 요소마다 <code class="language-plaintext highlighter-rouge">@e5</code> 같은 참조를 붙여 돌려줍니다. <code class="language-plaintext highlighter-rouge">press</code>·<code class="language-plaintext highlighter-rouge">fill</code>에 <code class="language-plaintext highlighter-rouge">--settle</code>을 붙이면 화면이 멈출 때까지 기다린 뒤 바뀐 부분만 돌려줘서, 에이전트는 전체 화면을 다시 읽지 않고 다음 대상을 고를 수 있습니다.</p>

<p>여기서 자주 틀리는 점이 세 가지 있습니다.</p>

<ul>
  <li><strong>참조는 화면이 바뀌면 무효가 됩니다.</strong> 조작할 때마다 돌아온 차이에서 새 참조를 고릅니다.</li>
  <li><strong>스크린샷은 확인이 아닙니다.</strong> 결과는 <code class="language-plaintext highlighter-rouge">wait text</code>나 <code class="language-plaintext highlighter-rouge">wait &lt;selector&gt;</code>로 확인하고, 스크린샷은 증거로만 남깁니다.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">install</code>에는 절대 경로를 줍니다.</strong> 상대 경로는 agent-device 데몬의 작업 폴더 기준으로 풀려서 “App binary not found”가 납니다.</li>
</ul>

<p>Flutter 앱 기준으로, 설치할 앱은 iOS 시뮬레이터용은 <code class="language-plaintext highlighter-rouge">flutter build ios --simulator --debug</code>, Android용은 <code class="language-plaintext highlighter-rouge">flutter build apk --debug</code>로 빌드합니다.</p>

<h2 id="flutter에서-주의할-점">Flutter에서 주의할 점</h2>

<p>agent-device는 화면을 접근성 트리로 읽습니다. 그래서 Flutter에서는 글자가 있는 위젯만 기본으로 잡히고, 아이콘 버튼이나 컨테이너는 보이지 않습니다. 이런 위젯에는 <code class="language-plaintext highlighter-rouge">Semantics(identifier:)</code>나 <code class="language-plaintext highlighter-rouge">semanticLabel</code>을 붙여야 selector로 찾을 수 있습니다. Flutter의 <code class="language-plaintext highlighter-rouge">Key</code>는 접근성 트리에 나오지 않습니다.</p>

<p>접근성 트리의 라벨은 단말기의 언어 설정을 따라갑니다. 같은 앱이라도 언어가 영어로 설정된 단말기에서는 영어 라벨, 한국어로 설정된 단말기에서는 한국어 라벨로 읽힙니다. 글자로 찾으면 단말기 언어 설정에 따라 selector가 달라지므로, <code class="language-plaintext highlighter-rouge">identifier</code>로 찾는 편이 안정적입니다.</p>

<p>iOS에서는 AdMob 테스트 배너가 <code class="language-plaintext highlighter-rouge">platform_view</code>, <code class="language-plaintext highlighter-rouge">webview</code>, <code class="language-plaintext highlighter-rouge">link</code> 노드로 함께 잡힙니다. 리플레이 스크립트의 selector는 이 노드를 피해서 씁니다.</p>

<h2 id="참고">참고</h2>

<ul>
  <li>agent-device 저장소: <a href="https://github.com/callstack/agent-device">https://github.com/callstack/agent-device</a></li>
  <li>agent-device 에이전트 설정 문서: <a href="https://oss.callstack.com/agent-device/docs/agent-setup">https://oss.callstack.com/agent-device/docs/agent-setup</a></li>
  <li>Google Artemis: <a href="https://github.com/google/artemis">https://github.com/google/artemis</a></li>
  <li>Maestro Flutter 지원 문서: <a href="https://docs.maestro.dev/platform-support/flutter">https://docs.maestro.dev/platform-support/flutter</a></li>
</ul>]]></content><author><name>Krenin</name></author><category term="ai-agent" /><category term="claude" /><category term="agent-device" /><category term="flutter" /><category term="qa" /><summary type="html"><![CDATA[Flutter 앱 테스트에 agent-device를 고른 이유와 Claude Code 설치, 기본 사용 흐름, Flutter에서 주의할 점을 정리합니다.]]></summary></entry><entry><title type="html">하루 만에 만든 도마뱀 게임 데모, itch.io 공개까지</title><link href="https://krenin88.github.io/devlog/2026/10/12/lizard-ranch-demo.html" rel="alternate" type="text/html" title="하루 만에 만든 도마뱀 게임 데모, itch.io 공개까지" /><published>2026-10-12T00:00:00+09:00</published><updated>2026-10-12T00:00:00+09:00</updated><id>https://krenin88.github.io/devlog/2026/10/12/lizard-ranch-demo</id><content type="html" xml:base="https://krenin88.github.io/devlog/2026/10/12/lizard-ranch-demo.html"><![CDATA[<p>일요일 오후, 게임 템플릿을 하나 받아 왔습니다. 그게 1시 46분이었어요. 정신을 차려 보니 새벽 3시였고, 마지막 커밋 메시지는 “체험판(데모)”였습니다. 그 사이에 쌓인 커밋이 86개더라고요.</p>

<p>그렇게 나온 게 <strong>도마뱀 목장</strong>입니다. 채집지에 나가서 도마뱀을 기절시켜 잡고, 팔고, 교배해서 더 예쁜 무늬를 뽑는 게임이에요. 엔진은 Godot이고, 코드는 Claude Code에게 맡겼습니다. 저는 방향을 정하고 해 보고 에셋을 고르기만 했어요.</p>

<h2 id="5분-만에-돌아간-첫-루프">5분 만에 돌아간 첫 루프</h2>

<p>템플릿을 가져오고 5분 만에 “잡고-팔고-교배하는” 루프가 돌아갔습니다. 솔직히 여기까지는 예상했어요. 요즘 AI면 이 정도는 하니까요.</p>

<p>중요한 건 그다음 1분이었습니다. 루프 바로 다음 커밋이 <strong>봇이 게임을 대신 플레이하는 밸런스 시뮬레이터</strong>였거든요.</p>

<p>인크리멘탈 게임은 결국 숫자 놀음이라, 업그레이드 가격 하나만 건드려도 30분짜리 구간이 3시간으로 늘어나곤 합니다. 그걸 매번 제가 처음부터 해 볼 수는 없잖아요. 그래서 수치를 바꿀 때마다 봇을 돌리고, “봇 기준 엔딩까지 4시간 19분” 같은 숫자를 보고 판단했습니다. 나중에는 봇 성격도 다섯 가지로 나눴어요. 공격력부터 올리는 봇, 싼 것부터 사는 봇, 교배만 파는 봇 같은 식으로요.</p>

<p>봇 덕분에 버그도 하나 잡았습니다. 체험판 길이를 재는데 한 봇이 영영 안 끝나는 거예요. 들여다보니 체험판에서 잠긴 업그레이드를 계속 사려고 멈춰 있었습니다. 봇 쪽 문제긴 했지만, 잠금 규칙이 여기저기 흩어져 있다는 걸 그때 알게 됐어요.</p>

<h2 id="오후-2시-20분의-방향-전환">오후 2시 20분의 방향 전환</h2>

<p>처음 30분 동안 만든 건 탭과 버튼으로 된 화면이었습니다. 오후 2시 20분에 방향을 확 틀었습니다. 도트 그래픽으로 바꾸고, 채집은 직접 캐릭터를 움직이는 “런”으로, 업그레이드는 마인드맵처럼 뻗어 나가는 트리로요.</p>

<p>보통이면 이 시점에서 갈아엎는 게 무서웠을 텐데, 다시 만드는 비용이 워낙 싸니까 별로 망설이지 않았습니다. 이게 AI랑 같이 만들 때 제일 크게 달라진 점 같아요.</p>

<h2 id="그림은-ai가-뽑고-고르는-건-내가">그림은 AI가 뽑고, 고르는 건 내가</h2>

<p>도트 스프라이트는 PixelLab을 MCP로 연결해서 뽑았습니다. Claude가 후보를 몇 장 만들어 오면 제가 “3번, 근데 목 좀 줄여 줘” 하는 식으로 골랐어요. 고른 그림은 seed를 문서에 적어 뒀고요.</p>

<p>웃겼던 건 결국 종을 바꾼 이유들입니다.</p>

<ul>
  <li>도마뱀붙이라는 종을 32픽셀 옆모습으로 줄이니 다른 종이랑 구별이 안 돼서 스킨크라는 도마뱀으로 바꿨어요.</li>
  <li>날도마뱀은 날개 같은 피막이 32픽셀에선 안 나와서 목도리도마뱀으로 변경했구요.</li>
  <li>턱수염도마뱀은 걷는 모습이 아무리 봐도 강아지 같아서 뿔도마뱀으로 교체했습니다.</li>
</ul>

<p>원하는 그림이 나올 때까지 계속 다시 뽑는 대신, 도구가 잘 그리는 쪽으로 게임을 맞춘 거죠. 작은 게임에선 이게 훨씬 빨랐습니다.</p>

<h2 id="ai가-만든-걸-빼는-게-내-일이었다">AI가 만든 걸 빼는 게 내 일이었다</h2>

<p>저녁 7시쯤, 오후에 잘 만들어 둔 기능들을 줄줄이 뺐습니다. 자동 채집, 자동 판매, 오프라인 진행. 10분 뒤엔 환생이랑 명성 트리까지요.</p>

<p>이 게임은 “실패도 환생도 없이, 한 번 쭉 진행해서 4~6시간이면 끝나는 게임”으로 가자고 정했거든요. 그러니 방치 요소도 환생도 자리가 없었습니다. 만드는 속도가 빠르니 기능은 금방 쌓이는데, 뭘 뺄지는 결국 제가 정해야 하더라고요. 돌이켜 보면 첫날 제가 한 일의 대부분이 이 결정이었던 것 같습니다.</p>

<h2 id="그래도-공개까진-일주일">그래도 공개까진 일주일</h2>

<p>새벽 3시에 체험판 범위까지 잘라 놓긴 했지만, 실제로 공개한 건 10월 11일이었습니다. 그 사이엔 보스 넷을 넣고, 채집지 배경을 그리고, 체험판 잠금 버그를 고치고, itch.io 페이지에 올릴 스크린샷이랑 커버, 웹 빌드를 준비했어요.</p>

<p>그러니까 “하루 만에”는 데모를 만드는 데까지만 맞는 말입니다. 만드는 건 하루였는데 다듬고 내보내는 데는 일주일이 걸렸어요. 다음 게임도 아마 비슷하지 않을까 싶습니다.</p>

<p>혹시 시뮬레이터 없이 숫자 게임을 AI랑 만들고 계신다면, 루프 다음에 바로 봇부터 만들어 보시길 권하고 싶어요. 저한텐 그게 제일 좋은 결정이었습니다.</p>

<p>체험판은 itch.io에서 해 보실 수 있어요. 도마뱀 잡으러 오세요 🦎
👉 https://krenin.itch.io/reptile-catcher</p>]]></content><author><name>Krenin</name></author><category term="game" /><category term="claude" /><category term="gamedev" /><category term="godot" /><category term="indie" /><summary type="html"><![CDATA[일요일 오후, 게임 템플릿을 하나 받아 왔습니다. 그게 1시 46분이었어요. 정신을 차려 보니 새벽 3시였고, 마지막 커밋 메시지는 “체험판(데모)”였습니다. 그 사이에 쌓인 커밋이 86개더라고요.]]></summary></entry></feed>