<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>blacknabis</title>
    <link>https://blacknabis.tistory.com/</link>
    <description>개발자의 취미 블로그</description>
    <language>ko</language>
    <pubDate>Sat, 30 May 2026 19:02:37 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>blacknabis</managingEditor>
    <item>
      <title>농장 경영 시뮬레이션 게임 만들기 - 1</title>
      <link>https://blacknabis.tistory.com/112</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;주말에 어떤것을 만들어볼까 하다가 농장 경영 힐링 게임을 만들면 좋겠다 생각해서 야금야금 작업을 시작.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 플랫폼&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타겟플랫폼은 1차적으로 PC환경의 Steam이 어떠할까 고민해서 넓은 화면에서 키보드 마우스를 사용하는 방식으로 진행 결정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;godot엔진, cocos2d-x, unity, unreal 엔진 중 요즘 자주 사용하는 엔진이 유니티라 취미 겸 공부 모적으로 Unity엔진으로 결정.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. isomatrix 방식&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;579&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgBl8Q/dJMcafl0Qkn/7xcyToYKt7Z1BbEsWx94xK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgBl8Q/dJMcafl0Qkn/7xcyToYKt7Z1BbEsWx94xK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgBl8Q/dJMcafl0Qkn/7xcyToYKt7Z1BbEsWx94xK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgBl8Q%2FdJMcafl0Qkn%2F7xcyToYKt7Z1BbEsWx94xK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;579&quot; height=&quot;367&quot; data-origin-width=&quot;579&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;413&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kXJOg/dJMcahD53eI/4UrQ5drdSrOAjMWPBUrq51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kXJOg/dJMcahD53eI/4UrQ5drdSrOAjMWPBUrq51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kXJOg/dJMcahD53eI/4UrQ5drdSrOAjMWPBUrq51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkXJOg%2FdJMcahD53eI%2F4UrQ5drdSrOAjMWPBUrq51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;568&quot; height=&quot;413&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;413&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;762&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dCXjD4/dJMcagSKXlN/zOrbtK4tyi9MU7C69TuOJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dCXjD4/dJMcagSKXlN/zOrbtK4tyi9MU7C69TuOJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dCXjD4/dJMcagSKXlN/zOrbtK4tyi9MU7C69TuOJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdCXjD4%2FdJMcagSKXlN%2FzOrbtK4tyi9MU7C69TuOJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1354&quot; height=&quot;762&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;762&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;828&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9SuSb/dJMcacQlk8P/lopPuAJzKXdvQ7KbswkAok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9SuSb/dJMcacQlk8P/lopPuAJzKXdvQ7KbswkAok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9SuSb/dJMcacQlk8P/lopPuAJzKXdvQ7KbswkAok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9SuSb%2FdJMcacQlk8P%2FlopPuAJzKXdvQ7KbswkAok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;811&quot; height=&quot;828&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;828&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;comfyui를 사용하였지만 최종적으로 프로토 외에 마음에 들지 않고 내가 원하는 세팅을 끝내기에는 시간이 소비되기에 pixellab으로 변경하고 구독하여서 mcp 연동. 최대한 자동화를 시도함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;647&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qFgWN/dJMcabYdj7G/Vp9s3GyOC1MbNuLkszrxk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qFgWN/dJMcabYdj7G/Vp9s3GyOC1MbNuLkszrxk0/img.png&quot; data-alt=&quot;탑다운, 타일 스타일로 구조 변경&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qFgWN/dJMcabYdj7G/Vp9s3GyOC1MbNuLkszrxk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqFgWN%2FdJMcabYdj7G%2FVp9s3GyOC1MbNuLkszrxk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1152&quot; height=&quot;647&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;647&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;탑다운, 타일 스타일로 구조 변경&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 시스템 다 붙이고, 타일 시스템붙이고, pixellab.ai mcp연동하여서 스프라이트 자동화, 그리고 기본 경작, 물주기 등등 캐릭터 이동하면서 기본 시스템 연동.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;299&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5rzxu/dJMcagFe2QY/1QUmBPlgdrTDawtoKKeuD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5rzxu/dJMcagFe2QY/1QUmBPlgdrTDawtoKKeuD1/img.png&quot; data-alt=&quot;자동화 해서 만든 플레이어 이미지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5rzxu/dJMcagFe2QY/1QUmBPlgdrTDawtoKKeuD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5rzxu%2FdJMcagFe2QY%2F1QUmBPlgdrTDawtoKKeuD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;614&quot; height=&quot;299&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;299&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;자동화 해서 만든 플레이어 이미지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;260&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bw0X5C/dJMcabjDjFQ/YTwyf9CghvmerYXQdANlkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bw0X5C/dJMcabjDjFQ/YTwyf9CghvmerYXQdANlkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bw0X5C/dJMcabjDjFQ/YTwyf9CghvmerYXQdANlkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbw0X5C%2FdJMcabjDjFQ%2FYTwyf9CghvmerYXQdANlkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;644&quot; height=&quot;260&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;260&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>AI/Unity</category>
      <category>claude code</category>
      <category>codex</category>
      <category>pixellab.ai</category>
      <category>Unity</category>
      <category>게임만들기</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/112</guid>
      <comments>https://blacknabis.tistory.com/112#entry112comment</comments>
      <pubDate>Sun, 19 Apr 2026 23:06:34 +0900</pubDate>
    </item>
    <item>
      <title>PixelLab.AI MCP 연동하기</title>
      <link>https://blacknabis.tistory.com/111</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;취미로 게임을 만들 던 중 스프라이트에 제작에 대한 시간이 소비되는걸 최대한 줄이고 싶었습니다. comfyui를 연동하긴 하였지만 로컬로 세팅하고 학습 데이터등을 가져왔지만 프로토타입으로는 만족이 되지만 조금 더 욕심이 나는데 이 시간을 조금 더 줄이고 자 이것저것 Pixel Art 자동화에 대해서 찾다가 가성비와 API지원이 되는것을 찾아보고 시험으로 PixelLab.AI을 선택하여서 연동하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. PixelLab 가입하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://pixellab.ai/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;http://pixellab.ai/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1776604448001&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;PixelLab - AI Generator for Pixel Art Game Assets&quot; data-og-description=&quot;Make your characters move Create character animations with text prompts, skeleton-based controls, or our automatic character creator. Generate walking, running, attacking, and custom animations for your sprite sheets.&quot; data-og-host=&quot;www.pixellab.ai&quot; data-og-source-url=&quot;http://pixellab.ai/&quot; data-og-url=&quot;https://www.pixellab.ai/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b6yTwG/dJMb86O3HuG/xvgKeJmMuCkHCoHWL0oXzK/img.jpg?width=617&amp;amp;height=756&amp;amp;face=0_0_617_756,https://scrap.kakaocdn.net/dn/ihBm9/dJMb85vQWaB/7eOmTnlPeC7yUTgioW7Vj1/img.jpg?width=617&amp;amp;height=756&amp;amp;face=0_0_617_756&quot;&gt;&lt;a href=&quot;http://pixellab.ai/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://pixellab.ai/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b6yTwG/dJMb86O3HuG/xvgKeJmMuCkHCoHWL0oXzK/img.jpg?width=617&amp;amp;height=756&amp;amp;face=0_0_617_756,https://scrap.kakaocdn.net/dn/ihBm9/dJMb85vQWaB/7eOmTnlPeC7yUTgioW7Vj1/img.jpg?width=617&amp;amp;height=756&amp;amp;face=0_0_617_756');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;PixelLab - AI Generator for Pixel Art Game Assets&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Make your characters move Create character animations with text prompts, skeleton-based controls, or our automatic character creator. Generate walking, running, attacking, and custom animations for your sprite sheets.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.pixellab.ai&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;946&quot; data-origin-height=&quot;615&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zVw1h/dJMcahc2WBZ/a2xLheln2jTJ20yURkWol1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zVw1h/dJMcahc2WBZ/a2xLheln2jTJ20yURkWol1/img.png&quot; data-alt=&quot;가장 저렴한 모델 구독 후 Account 정보&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zVw1h/dJMcahc2WBZ/a2xLheln2jTJ20yURkWol1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzVw1h%2FdJMcahc2WBZ%2Fa2xLheln2jTJ20yURkWol1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;946&quot; height=&quot;615&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;946&quot; data-origin-height=&quot;615&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;가장 저렴한 모델 구독 후 Account 정보&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. MCP 연동하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.pixellab.ai/mcp&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.pixellab.ai/mcp&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1776604664535&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Vibe Coding - PixelLab&quot; data-og-description=&quot;  Framework Agnostic Works with any game engine or framework. Unity, Unreal Engine, Godot, GameMaker Studio, Raylib, MonoGame, Defold, Love2D, or Pygame - your AI assistant adapts to your preferred tools.&quot; data-og-host=&quot;www.pixellab.ai&quot; data-og-source-url=&quot;https://www.pixellab.ai/mcp&quot; data-og-url=&quot;https://www.pixellab.ai/mcp&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.pixellab.ai/mcp&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.pixellab.ai/mcp&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Vibe Coding - PixelLab&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  Framework Agnostic Works with any game engine or framework. Unity, Unreal Engine, Godot, GameMaker Studio, Raylib, MonoGame, Defold, Love2D, or Pygame - your AI assistant adapts to your preferred tools.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.pixellab.ai&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;964&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vm9eA/dJMcabqoqhb/K0sF0PKVO9tnFplkS0qSN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vm9eA/dJMcabqoqhb/K0sF0PKVO9tnFplkS0qSN1/img.png&quot; data-alt=&quot;탭마다 종류별 MCP 연동 가이드가 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vm9eA/dJMcabqoqhb/K0sF0PKVO9tnFplkS0qSN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvm9eA%2FdJMcabqoqhb%2FK0sF0PKVO9tnFplkS0qSN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;964&quot; height=&quot;640&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;964&quot; data-origin-height=&quot;640&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;탭마다 종류별 MCP 연동 가이드가 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법 1. 수동&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 사용하는 AI(예 Claude COde, Codex)를 선택 후 설명에 나온대로 mcp를 등록해주면된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒤에 나오는 API의 키경우, &lt;a href=&quot;https://www.pixellab.ai/pixellab-api&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.pixellab.ai/pixellab-api&lt;/a&gt;나 &lt;a href=&quot;https://www.pixellab.ai/account&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.pixellab.ai/account&lt;/a&gt;에서 있지만 이 값은 해당 메뉴얼에서 자동으로 적용되는것으로 보임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법 2. 자동(에이전트에게 세팅 시키기)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. &lt;a href=&quot;https://www.pixellab.ai/pixellab-api&quot;&gt;https://www.pixellab.ai/pixellab-api&lt;/a&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;나&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://www.pixellab.ai/account&quot;&gt;https://www.pixellab.ai/account&lt;/a&gt;에서 API Key를 확인.&lt;br /&gt;2. AI에게 &quot;&lt;a href=&quot;https://www.pixellab.ai/mcp&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.pixellab.ai/mcp&lt;/a&gt; 링크를 읽고 Pixellab.ai mcp 연동해줘&quot; 후 확인한 API Key를 전달하거나 세팅해주면된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 사용방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트에게 pixellab.ai의 mcp를 사용하여서 이미지를(예시. 현재 사용되는 32픽셀의 농부의 4방향 애니메이션을 만들어줘) 요청하면 만들어준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DS6YG/dJMcahRDHJw/KuHa3drkCzSmz6BNkYZJ51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DS6YG/dJMcahRDHJw/KuHa3drkCzSmz6BNkYZJ51/img.png&quot; data-alt=&quot;실행결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DS6YG/dJMcahRDHJw/KuHa3drkCzSmz6BNkYZJ51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDS6YG%2FdJMcahRDHJw%2FKuHa3drkCzSmz6BNkYZJ51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;638&quot; height=&quot;352&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;352&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 후기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상업용으로는 사용하기에는 힘들지만 개인 개발자라면 특히나 아트직군이 아닐경우 많은 시간을 단축시켜주고 제일 싼 요금제의 경우 월 2천장이 사용가능하기 때문에 커피 마시는 값을 아껴서 한달정도는 사용해볼 만 한것 같습니다.&lt;/p&gt;</description>
      <category>AI/Etc</category>
      <category>pixellab.ai</category>
      <category>pixellab.ai mcp</category>
      <category>스프라이트만들기</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/111</guid>
      <comments>https://blacknabis.tistory.com/111#entry111comment</comments>
      <pubDate>Sun, 19 Apr 2026 22:27:13 +0900</pubDate>
    </item>
    <item>
      <title>안티그래비티 반응이 없을 때 대처.</title>
      <link>https://blacknabis.tistory.com/110</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 작업을 할 때 안티그래비티 입력 받응이 없어서 재설치도하고 이것저것 다 시도하닫가 실패하고 레딧을 검색..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.reddit.com/r/google_antigravity/comments/1sg0ghw/anitgravity_fails_to_respond_any_solution_to_this/?tl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.reddit.com/r/google_antigravity/comments/1sg0ghw/anitgravity_fails_to_respond_any_solution_to_this/?tl=ko&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1776527942200&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Reddit의 google_antigravity 커뮤니티: Anitgravity가 반응이 없는데, 해결 방법 없을까요?&quot; data-og-description=&quot;google_antigravity 커뮤니티에서 이 게시물을 비롯한 다양한 콘텐츠를 살펴보세요&quot; data-og-host=&quot;www.reddit.com&quot; data-og-source-url=&quot;https://www.reddit.com/r/google_antigravity/comments/1sg0ghw/anitgravity_fails_to_respond_any_solution_to_this/?tl=ko&quot; data-og-url=&quot;https://www.reddit.com/r/google_antigravity/comments/1sg0ghw/anitgravity_fails_to_respond_any_solution_to_this/?tl=ko&amp;amp;seeker-session=true&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b8QNQd/dJMb8Rj3Nlh/jP8pD6ZtKkEJJOk7IwgFWk/img.jpg?width=1120&amp;amp;height=584&amp;amp;face=0_0_1120_584,https://scrap.kakaocdn.net/dn/m0ni1/dJMb8SpJOrH/yPr7xLAv1AjZ6n3Kkwz2J1/img.jpg?width=1120&amp;amp;height=584&amp;amp;face=0_0_1120_584&quot;&gt;&lt;a href=&quot;https://www.reddit.com/r/google_antigravity/comments/1sg0ghw/anitgravity_fails_to_respond_any_solution_to_this/?tl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.reddit.com/r/google_antigravity/comments/1sg0ghw/anitgravity_fails_to_respond_any_solution_to_this/?tl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b8QNQd/dJMb8Rj3Nlh/jP8pD6ZtKkEJJOk7IwgFWk/img.jpg?width=1120&amp;amp;height=584&amp;amp;face=0_0_1120_584,https://scrap.kakaocdn.net/dn/m0ni1/dJMb8SpJOrH/yPr7xLAv1AjZ6n3Kkwz2J1/img.jpg?width=1120&amp;amp;height=584&amp;amp;face=0_0_1120_584');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Reddit의 google_antigravity 커뮤니티: Anitgravity가 반응이 없는데, 해결 방법 없을까요?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;google_antigravity 커뮤니티에서 이 게시물을 비롯한 다양한 콘텐츠를 살펴보세요&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.reddit.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.git/config 에서 extensions 하위에 내용을 제거하면 된다고함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;cs&quot; style=&quot;background-color: #f2f2f2; color: #333d42; text-align: start;&quot;&gt;&lt;code&gt;[extensions]
  worktreeConfig = true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘됨... 요즘 안티그래비티 토큰제한도그렇고 너무 마음에 안듬.&lt;/p&gt;</description>
      <category>AI/Etc</category>
      <category>Antigravity</category>
      <category>antigravity 무반응</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/110</guid>
      <comments>https://blacknabis.tistory.com/110#entry110comment</comments>
      <pubDate>Sun, 19 Apr 2026 00:58:50 +0900</pubDate>
    </item>
    <item>
      <title>원시인 모드 스킬</title>
      <link>https://blacknabis.tistory.com/109</link>
      <description>&lt;h1&gt;클로드 코드(Claude Code) 토큰 75% 절약하는 방법: '원시인 모드(Caveman)' 가이드&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 개발 도구를 사용하다 보면 끊임없이 소모되는 토큰 비용과 길고 장황한 답변이 부담스러울 때가 많습니다. 오늘은 클로드 코드(Claude Code), 커서(Cursor), 제미나이(Gemini) 등에서 &lt;b&gt;출력 토큰을 무려 평균 75%까지 줄여주는 재미있고 혁신적인 스킬, '원시인 모드(Caveman)'&lt;/b&gt; 에 대해 소개합니다.&lt;br /&gt;단순히 말을 짧게 하는 것을 넘어, 기술적인 정확도는 100% 유지하면서 비용과 속도까지 챙길 수 있는 강력한 플러그인입니다.&lt;br /&gt;출처: &lt;a href=&quot;https://github.com/JuliusBrussee/caveman&quot;&gt;GitHub - JuliusBrussee/caveman&lt;/a&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  원시인 모드(Caveman Mode)란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;왜 많은 단어를 쓰지? 적은 단어로도 충분한데!&quot;  &lt;br /&gt;원시인 모드는 AI가 불필요한 미사여구(pleasantries), 추임새, 문법적 허례허식을 빼고 &lt;b&gt;핵심 기술 정보만&lt;/b&gt; 원시인처럼 툭툭 내뱉게 만드는 플러그인입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비포 &amp;amp; 애프터 비교&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; ️ 일반 클로드 모드 (69 토큰)&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;React 컴포넌트가 다시 렌더링되는 이유는 렌더링 주기에마다 새로운 객체 참조를 생성하기 때문일 가능성이 높습니다. 인라인 객체를 prop으로 전달하면 React의 얕은 비교(shallow comparison)에서 매번 다른 객체로 인식되어 다시 렌더링을 유발합니다. 객체를 메모이제이션하려면 &lt;code&gt;useMemo&lt;/code&gt;를 사용하는 것을 권장합니다.&quot;&lt;br /&gt;&lt;b&gt;  원시인 모드 (19 토큰)&lt;/b&gt;&lt;br /&gt;&quot;렌더링마다 새 객체 참조. 인라인 객체 prop = 새 참조 = 재렌더링. &lt;code&gt;useMemo&lt;/code&gt;로 감싸라.&quot;&lt;br /&gt;결과는 똑같은 해결책입니다. 하지만 &lt;b&gt;75% 더 짧고, 약 3배 더 빠릅니다.&lt;/b&gt; 중요한 것은 기술적인 팩트와 처리해야 할 코드는 전혀 훼손되지 않는다는 점입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  원시인 모드 프롬프트가 주는 4가지 이점&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;엄청난 속도 향상&lt;/b&gt;: AI가 파싱하고 생성해야 할 토큰이 줄어드니 응답 속도가 훨씬 빠릅니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비용 절감&lt;/b&gt;: 출력 토큰을 평균 70% 이상 절약할 수 있어 API 비용이 크게 감소합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가독성 향상&lt;/b&gt;: 긴 설명이나 텍스트 장벽 없이 필요한 정답과 코드만 바로 확인할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;팩트 중심 출력&lt;/b&gt;: 2026년 발표된 연구 논문에 따르면 LLM에 간결성 제약을 걸었을 때 정확도가 오히려 개선되는 현상도 목격되었습니다. (군더더기로 인한 환각 억제)&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 4가지 강도(Intensity) 조절 기능&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어 창에서 원시인의 지능 퇴화(압축) 수준을 선택할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;  Lite (라이트 모드)&lt;/b&gt;: 정중함만 빼고 문장은 어느 정도 유지합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;  Full (풀 모드)&lt;/b&gt;: 기본 모드. 짧은 명사구 형태로 답합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;  Ultra (울트라 모드)&lt;/b&gt;: 인간어조차 최소화합니다. [원인] &amp;rarr; [결과]. 그리고 코드만 출력합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;  文言文 (문언문 모드)&lt;/b&gt;: 전통 한문(고대 중국어)으로 대답합니다. (토큰 효율적인 언어 압축 방식입니다)&lt;/li&gt;
&lt;li&gt;강도 조절 적용 형태:* &lt;code&gt;/caveman ultra&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 다양한 AI 어시스턴트에 설치하는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원시인 모드는 &lt;code&gt;npx skills&lt;/code&gt;를 지원하는 40개 이상의 다양한 AI 코드 에디터 및 CLI 에이전트와 원활하게 호환됩니다.&lt;br /&gt;&lt;b&gt;1. Claude Code (클로드 코드)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;claude plugin marketplace add JuliusBrussee/caveman &amp;amp;&amp;amp; claude plugin install caveman@caveman&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. Cursor (커서)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;npx skills add JuliusBrussee/caveman -a cursor&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. Windsurf (윈드서프)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;npx skills add JuliusBrussee/caveman -a windsurf&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. Gemini CLI (제미나이)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;gemini extensions install https://github.com/JuliusBrussee/caveman&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⚠️ 자동 시작 활성화 팁&lt;/b&gt;&lt;br /&gt;커서, 윈드서프 같은 도구는 스킬 파일만 복사되기 때문에 매번 채팅 시 &quot;원시인처럼 말해줘&quot; 라고 지정해야 합니다. 모든 대화 시작 시 항상 켜두고 싶다면, 프로젝트 내 룰 셋팅장 (&lt;code&gt;.cursor/rules/caveman.mdc&lt;/code&gt; 등)을 만들어 아래 내용의 프롬프트를 추가하면 좋습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Terse like caveman. Technical substance exact. Only fluff die. Drop: articles, filler (just/really/basically), pleasantries, hedging. Fragments OK. Short synonyms. Code unchanged. Pattern: [thing] [action] [reason]. [next step]. ACTIVE EVERY RESPONSE. No revert after many turns. No filler drift. Code/commits/PRs: normal. Off: &quot;stop caveman&quot; / &quot;normal mode&quot;.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  알아두면 유용한 3가지 부가 기능&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 말투만 교정하는 것을 넘어서서 코딩 생산성을 올려주는 커스텀 스킬명령어도 갖추고 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;/caveman:compress&lt;/code&gt;&lt;/b&gt;: 프로젝트 루트의 &lt;code&gt;CLAUDE.md&lt;/code&gt; 파일 등을 압축해 줍니다. 백업은 두면서 AI가 읽어들이는 시스템 지시사항을 토큰 친화적(원시인어)으로 압축하여 입력단 토큰까지 아껴줍니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;/caveman-commit&lt;/code&gt;&lt;/b&gt;: 가장 간결하고 직관적인 한 줄 커밋 메시지를 생성합니다. (예: &lt;code&gt;  bug: user null. Add guard.&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;/caveman-review&lt;/code&gt;&lt;/b&gt;: 구구절절 불필요한 설명 없이, 딱 한 줄로 문제점만 찌르는 마이크로 코드 리뷰를 해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;맺음말&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발을 할 때마다 &quot;네, 바로 도와드리겠습니다!&quot; 같은 AI의 반복적인 기계어 인사에 지쳤거나, 빠르게 소모되는 API 요금이 걱정된다면 '원시인 모드'를 반드시 도입해 보세요. 거추장스러운 수식어를 버리고 본질(코드 중심)에만 집중하는 극단적이면서도 가장 재치있는 토큰 관리 전략일 것입니다.&lt;br /&gt;  지금 바로 터미널을 여세요.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;npx skills add JuliusBrussee/caveman&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;499&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQAeBG/dJMcabRlfAA/K0rDeuZrJDAcmXTexeZsB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQAeBG/dJMcabRlfAA/K0rDeuZrJDAcmXTexeZsB0/img.png&quot; data-alt=&quot;영어만 지원해서 코덱스를 사용하여서 한국어 호환버전 작업 후 테스트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQAeBG/dJMcabRlfAA/K0rDeuZrJDAcmXTexeZsB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQAeBG%2FdJMcabRlfAA%2FK0rDeuZrJDAcmXTexeZsB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;499&quot; height=&quot;172&quot; data-origin-width=&quot;499&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;영어만 지원해서 코덱스를 사용하여서 한국어 호환버전 작업 후 테스트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>AI/skill,plugin,git모음</category>
      <category>AI비용절약</category>
      <category>caveman</category>
      <category>claudecode</category>
      <category>원시인모드</category>
      <category>클로드코드</category>
      <category>토큰절약</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/109</guid>
      <comments>https://blacknabis.tistory.com/109#entry109comment</comments>
      <pubDate>Sun, 12 Apr 2026 16:46:21 +0900</pubDate>
    </item>
    <item>
      <title>클로드 코드(Claude Code) 권한 설정 가이드: 위험천만한 바이패스 모드와 새로운 오토(Auto) 모드 해부ClaudeClaude Code</title>
      <link>https://blacknabis.tistory.com/108</link>
      <description>&lt;h1&gt;클로드 코드(Claude Code) 권한 설정 가이드&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Anthropic에서 선보인 강력한 터미널용 AI 코딩 어시스턴트인 &lt;b&gt;Claude Code&lt;/b&gt;를 사용하다 보면, 파일을 수정하거나 셸 명령어를 실행할 때마다 매번 &lt;code&gt;y/n&lt;/code&gt;을 묻는 '승인(Approval) 프롬프트'에 피로감을 느끼기 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 프롬프트 피로(Prompt fatigue)를 극복하기 위해 사용자들은 권한을 생략하는 옵션들을 찾게 됩니다. 이번 글에서는 극단적인 형태의 권한 생략인 &lt;b&gt;Bypass 모드(&lt;code&gt;--dangerously-skip-permissions&lt;/code&gt;)&lt;/b&gt;와 이를 안전하게 대체하는 스마트한 &lt;b&gt;오토(Auto) 모드&lt;/b&gt;의 차이점과 상세 작동 방식을 깊이 파헤쳐 보겠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 극단적 선택: &lt;code&gt;--dangerously-skip-permissions&lt;/code&gt; 플래그&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령어는 이름에서부터 느껴지듯 &lt;b&gt;&quot;위험을 감수하고 모든 권한 확인을 건너뛰겠다&quot;&lt;/b&gt;는 의미를 담고 있습니다. 커뮤니티에서는 흔히 &lt;b&gt;'YOLO(You Only Live Once) 모드'&lt;/b&gt; 또는 &lt;b&gt;'풀 바이패스(Bypass) 모드'&lt;/b&gt;라고 부릅니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;작동 방식 및 특징&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 플래그를 붙여 에이전트를 실행하면(&lt;code&gt;claude --dangerously-skip-permissions&lt;/code&gt;), Claude Code는 파일 읽기/쓰기, 셸 명령어 실행, 깃(Git) 커밋, 네트워크 요청 등 시스템 제어권이 필요한 모든 동작을 &lt;b&gt;사용자에게 묻지 않고 즉시 100% 실행&lt;/b&gt;합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 이 모드가 그토록 위험할까?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;브레이크 없는 스포츠카:&lt;/b&gt; AI가 문맥을 잘못 이해하여 &lt;code&gt;rm -rf&lt;/code&gt; 같은 치명적인 삭제 명령을 내리거나, 민감한 환경 설정 파일을 엉뚱하게 덮어써 버릴 때 사람이 멈출 수 있는 기회가 전혀 없습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;보안 취약점 노출 (Prompt Injection):&lt;/b&gt; 만약 분석하라고 던져준 외부 코드나 텍스트 안에 &quot;이 환경을 지워라&quot;라는 식의 악의적 프롬프트가 숨어있다면, 시스템이 사용자 모르게 이를 실행해 버릴 위험이 다분합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;언제 사용해야 할까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Anthropic은 이 모드를 일반적인 로컬 개발 컴퓨터(내 맥북, 내 윈도우 PC)에서 사용하는 것을 &lt;b&gt;절대 권장하지 않습니다&lt;/b&gt;. 이 모드는 다음과 같은 철저히 격리된 환경에서만 사용해야 합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;지워져도 상관없는 일회성 도커(Docker) 컨테이너 안에서 자동 리뷰를 돌릴 때&lt;/li&gt;
&lt;li&gt;비밀 키(Secret)나 프로덕션 데이터가 없는 CI/CD 파이프라인의 자동화 스크립트 용도&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 현명한 타협안: 오토(Auto) 모드 (Research Preview)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위험천만한 풀 바이패스 모드 대신, Anthropic이 최근 도입한 &lt;b&gt;&quot;Auto Mode&quot;는 자율성과 안정성의 균형을 맞춘 똑똑한 모드&lt;/b&gt;입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;어떻게 작동하나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오토 모드의 핵심은 내부에 숨겨진 &lt;b&gt;분류기 모델(Classifier Model)&lt;/b&gt;에 있습니다. Claude가 특정 명령을 내리면, 이 분류기 모델이 해당 명령의 '위험도'를 우선 평가합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;저위험 작업 (자동 승인):&lt;/b&gt; 단순한 파일 수정, 에러 로그 읽기, 프로젝트 내 검색 등은 사람의 개입 없이 AI가 즉시 실행합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;고위험 작업 (승인 요청):&lt;/b&gt; 대규모 파일 삭제, 시스템 권한(sudo) 요구, 낯선 외부 네트워크 통신 등은 분류기가 위험하다고 판단하여 기존처럼 사용자에게 &lt;code&gt;y/n&lt;/code&gt;을 물어봅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;오토 모드의 장점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피로감을 유발하는 자잘한 질문은 AI가 알아서 처리해 주면서도, 정말 큰일이 날 법한 순간에는 안전벨트를 꽉 잡아줍니다. 이를 통해 개발자는 코드의 흐름을 끊지 않고도 최소한의 안전망 위에서 작업할 수 있게 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고:&lt;/b&gt; 오토 모드는 현재 리서치 프리뷰(Research Preview) 단계이며, 주로 Team, Enterprise, API 결제 사용자 등을 중심으로 제공됩니다. 관리자 설정이나 초기 &lt;code&gt;claude&lt;/code&gt; 설정 플래그에서 활성화해야 터미널 단축키(&lt;code&gt;Shift+Tab&lt;/code&gt;)로 모드를 전환할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 한눈에 보는 비교 (Bypass vs Auto)&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;비교 항목&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;바이패스 모드 (&lt;code&gt;--dangerously-skip-permissions&lt;/code&gt;)&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;오토 모드 (Auto Mode)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;승인 방식&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;묻지도 따지지도 않고 무조건 실행&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;AI 분류기가 위험도를 평가하여 선별적 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;안전성&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;전혀 없음 (0%)&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;보통~높음 (치명적 오류는 막아줌)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;사용 목적&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;완전 자동화된 서버, 헤드리스(Headless) 환경&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;프로그래머의 일상적인 로컬 개발 환경&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;위험 수준&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;매우 높음 (치명적 데이터 유실 가능)&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;낮음~보통 (Research Preview)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 실전 가이드: 안전장치(&lt;code&gt;deny&lt;/code&gt; 룰) 설정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비록 오토 모드를 쓴다 하더라도 100% 안전을 보장하는 것은 아닙니다. 만약 무심코 바이패스 모드로 진입하게 될 상황을 대비해, &lt;b&gt;절대 실행 불가능한 명령의 블랙리스트&lt;/b&gt;를 만들어 두는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;~/.claude/settings.json&lt;/code&gt; 파일을 열고 &lt;code&gt;deny&lt;/code&gt; 옵션을 추가하세요.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;theme&quot;: &quot;dark&quot;,
  &quot;deny&quot;: [
    &quot;rm -rf *&quot;,
    &quot;sudo&quot;,
    &quot;drop database&quot;,
    &quot;npm publish&quot;
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/Etc</category>
      <category>AI 코딩</category>
      <category>auto mode</category>
      <category>Claude</category>
      <category>claude code</category>
      <category>cli</category>
      <category>바이패스 모드</category>
      <category>오토 모드</category>
      <category>클로드</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/108</guid>
      <comments>https://blacknabis.tistory.com/108#entry108comment</comments>
      <pubDate>Sun, 12 Apr 2026 16:37:52 +0900</pubDate>
    </item>
    <item>
      <title>Claude Code에서 Codex Plugin을 사용해보자!</title>
      <link>https://blacknabis.tistory.com/107</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;현재 Codex(Pro), Claude Codex(Pro), Antygravity(AIPro) 사용 중인데 클루드 코드의 토큰이 너무나도 빨리 소모가 되서 좋은 방법 없을까 하다가 해커톤에 1위 유저의 세팅공유와 만우절날 OpenAI에서 발표한 클루드코드에서 플러그인 추가가 기억나서 이참에 연동을 해보았습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;705&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ucrzc/dJMcagLUIhI/9apLU7IksivQSwaxnVOak0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ucrzc/dJMcagLUIhI/9apLU7IksivQSwaxnVOak0/img.png&quot; data-alt=&quot;https://x.com/dkundel/status/2038670330257109461&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ucrzc/dJMcagLUIhI/9apLU7IksivQSwaxnVOak0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fucrzc%2FdJMcagLUIhI%2F9apLU7IksivQSwaxnVOak0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;623&quot; height=&quot;705&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;705&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://x.com/dkundel/status/2038670330257109461&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/openai/codex-plugin-cc?tab=readme-ov-file#install&quot;&gt;https://github.com/openai/codex-plugin-cc?tab=readme-ov-file#install&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[GitHub - openai/codex-plugin-cc: Use Codex from Claude Code to review code or delegate tasks.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Use Codex from Claude Code to review code or delegate tasks. - openai/codex-plugin-cc&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;github.com](&lt;a href=&quot;https://github.com/openai/codex-plugin-cc?tab=readme-ov-file#install&quot;&gt;https://github.com/openai/codex-plugin-cc?tab=readme-ov-file#install&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub의 readme.md의 내용입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code에 마켓플레이스를 추가하세요:&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;/plugin marketplace add openai/codex-plugin-cc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플러그인을 설치하세요:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;/plugin install codex@openai-codex&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플러그인 다시 로드:&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/reload-plugins&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 다음 실행하세요:&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;/codex:setup&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1081&quot; data-origin-height=&quot;543&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bS3bu7/dJMcadBykEp/pymfWdxRLCbSAbIIuy5hV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bS3bu7/dJMcadBykEp/pymfWdxRLCbSAbIIuy5hV1/img.png&quot; data-alt=&quot;위 과정을 거치면 명령어가 추가된것을 확인 할 수 있습니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bS3bu7/dJMcadBykEp/pymfWdxRLCbSAbIIuy5hV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbS3bu7%2FdJMcadBykEp%2FpymfWdxRLCbSAbIIuy5hV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1081&quot; height=&quot;543&quot; data-origin-width=&quot;1081&quot; data-origin-height=&quot;543&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;위 과정을 거치면 명령어가 추가된것을 확인 할 수 있습니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blsFGN/dJMcajaJ2Mx/JwYtmnQqqsmmsgoOZoVDkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blsFGN/dJMcajaJ2Mx/JwYtmnQqqsmmsgoOZoVDkK/img.png&quot; data-alt=&quot;/codex:review 를 실행 하면 어떤 방식으로 진행할 지 선택지가 나온다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blsFGN/dJMcajaJ2Mx/JwYtmnQqqsmmsgoOZoVDkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblsFGN%2FdJMcajaJ2Mx%2FJwYtmnQqqsmmsgoOZoVDkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1098&quot; height=&quot;390&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;390&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;/codex:review 를 실행 하면 어떤 방식으로 진행할 지 선택지가 나온다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;일반 사용 흐름&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;출시 전 리뷰&lt;/b&gt;: /codex:review 단독 실행&lt;/li&gt;
&lt;li&gt;&lt;b&gt;문제를 Codex에 위임&lt;/b&gt;: /codex:rescue investigate why the build is failing in CI&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장시간 작업 시작 후 확인&lt;/b&gt;: --background로 실행 &amp;rarr; /codex:status &amp;rarr; /codex:result&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Codex 통합 구조&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;플러그인은 &lt;b&gt;Codex 앱 서버&lt;/b&gt;를 래핑하며, 환경에 설치된 전역 codex 바이너리를 활용&lt;/li&gt;
&lt;li&gt;별도 런타임 없이 동일한 Codex 설치&amp;middot;인증&amp;middot;레포지터리&amp;middot;로컬 환경 공유&lt;/li&gt;
&lt;li&gt;기본 모델 및 추론 노력(reasoning effort)은 ~/.codex/config.toml(사용자 레벨) 또는 .codex/config.toml(프로젝트 레벨)로 설정 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예시: model = &quot;gpt-5.4-mini&quot;, model_reasoning_effort = &quot;xhigh&quot;&lt;/li&gt;
&lt;li&gt;프로젝트 레벨 설정은 해당 프로젝트가 신뢰됨(trusted) 상태일 때만 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;완료된 위임 작업은 codex resume 로 Codex에서 직접 이어받기 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code 프로 플랜을 사용중이라 Opus를 사용하면 토큰의 거기 사용하기 불가능한 수준이였는데 이처럼 플러그인으로 사용하면 괜찮은거 같네요. 코드분석, 개선점을 실행하는데 5시간 한도 4%정도 사용되는군요&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfi8W9/dJMcajaJ3st/aCfT5bQBu64hsCKzjLXnU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfi8W9/dJMcajaJ3st/aCfT5bQBu64hsCKzjLXnU0/img.png&quot; data-alt=&quot;기존 프로젝트의 개선사항 리스트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfi8W9/dJMcajaJ3st/aCfT5bQBu64hsCKzjLXnU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcfi8W9%2FdJMcajaJ3st%2FaCfT5bQBu64hsCKzjLXnU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;746&quot; height=&quot;370&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기존 프로젝트의 개선사항 리스트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;967&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDIAEm/dJMcaaEV4lS/xeMGJcqCMPzEzDbqI3b580/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDIAEm/dJMcaaEV4lS/xeMGJcqCMPzEzDbqI3b580/img.png&quot; data-alt=&quot;코드작업은 소넷, 리뷰는 코덱스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDIAEm/dJMcaaEV4lS/xeMGJcqCMPzEzDbqI3b580/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDIAEm%2FdJMcaaEV4lS%2FxeMGJcqCMPzEzDbqI3b580%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;967&quot; height=&quot;351&quot; data-origin-width=&quot;967&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;코드작업은 소넷, 리뷰는 코덱스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 작업을 진행하였는데 토큰은 주간한도 38%정도 소모하였고 사용에 실제 체감이 많이 되었네요.&lt;/p&gt;</description>
      <category>AI/Etc</category>
      <category>claude code</category>
      <category>codex plugin</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/107</guid>
      <comments>https://blacknabis.tistory.com/107#entry107comment</comments>
      <pubDate>Sun, 12 Apr 2026 14:40:16 +0900</pubDate>
    </item>
    <item>
      <title>AI 소식 수집. 디스코드에 보내는 봇 제작</title>
      <link>https://blacknabis.tistory.com/106</link>
      <description>&lt;h1&gt;AI 소식을 매일 수집해 Discord로 보내주는 요약 봇을 만들었습니다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 AI 관련 소식은 올라오는 속도도 빠르고, 채널도 너무 다양합니다.&lt;br /&gt;OpenAI 공식 뉴스, Claude Code 릴리즈, 주요 X 계정까지 각각 직접 확인하다 보니 생각보다 반복 비용이 크다고 느꼈고, 그래서 필요한 정보만 모아서 매일 한 번에 보내주는 작은 요약 봇을 만들어보게 됐습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 만든 프로젝트는 &lt;code&gt;ai-signal-digest&lt;/code&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub:&lt;br /&gt;&lt;a href=&quot;https://github.com/blacknabis/ai-signal-digest&quot;&gt;https://github.com/blacknabis/ai-signal-digest&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로젝트는 기본적으로 아래 흐름으로 동작합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;config/sources.json&lt;/code&gt;에 등록된 소스를 읽습니다.&lt;/li&gt;
&lt;li&gt;RSS / HTML / X RSS-compatible feed에서 최신 항목을 수집합니다.&lt;/li&gt;
&lt;li&gt;SQLite 상태 저장소를 기준으로 이미 본 항목을 제외합니다.&lt;/li&gt;
&lt;li&gt;수집한 항목을 한국어 digest 형태로 정리합니다.&lt;/li&gt;
&lt;li&gt;Discord 웹훅으로 하루 1회 전송합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능 자체는 단순해 보이지만, 실제로는 &quot;운영 가능한 자동화&quot;가 되도록 구조를 나누는 데 조금 더 신경을 썼습니다. 한 파일에 전부 몰아넣기보다는, 수집과 상태 관리, 요약, 전송이 서로 독립적으로 바뀔 수 있도록 분리했습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 구성&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;cli.py&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행 진입점입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;service.py&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수집, dedupe, enrichment, render, delivery까지 전체 파이프라인을 오케스트레이션합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;state.py&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중복 방지를 위한 SQLite 상태 저장소입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;collectors/rss.py&lt;/code&gt;, &lt;code&gt;collectors/html.py&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소스 종류에 따라 수집 책임을 분리했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;enrichment.py&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한국어 fallback 요약과 선택형 번역 처리를 담당합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;digest.py&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Discord에 맞는 최종 메시지 포맷을 만듭니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;discord.py&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Discord 웹훅 전송과 메시지 chunking을 처리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 나눈 이유는 명확합니다. 예를 들어 X 수집 방식이 바뀌더라도 collector만 수정하면 되고, 요약 형식을 바꾸고 싶으면 digest/enrichment만 손보면 되도록 하기 위해서입니다. 실제로 만들다 보니 이런 분리가 나중 수정에 꽤 도움이 됐습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;상태 관리와 중복 방지&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태 저장은 SQLite를 사용했습니다.&lt;br /&gt;이 프로젝트에서 중요한 건 &quot;새 소식만 보내는 것&quot;이기 때문에, 단순히 마지막 실행 시각만 저장하는 방식보다 항목별 상태를 저장하는 쪽이 더 안전하다고 판단했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 &lt;code&gt;seen&lt;/code&gt;, &lt;code&gt;delivered&lt;/code&gt;, &lt;code&gt;suppressed&lt;/code&gt; 개념을 나눠서 관리합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;seen&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 번이라도 수집된 항목&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;delivered&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Discord로 실제 전송된 항목&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;suppressed&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫 실행 시 오래된 글처럼 의도적으로 제외한 항목&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 분리해두면 Discord 전송 실패가 생겨도 수집 결과를 잃지 않고, 이후 재시도도 더 자연스럽게 처리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 처음 실행할 때 기존 글이 한꺼번에 쏟아지는 문제를 막기 위해 bootstrap 보호 장치도 넣었습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;BOOTSTRAP_LOOKBACK_HOURS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BOOTSTRAP_MAX_ITEMS_PER_SOURCE&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두 값을 환경 변수로 빼서 운영과 테스트에서 유연하게 조정할 수 있도록 했습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;X 수집에서 신경 쓴 부분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수집 쪽에서는 X가 가장 손이 많이 갔습니다.&lt;br /&gt;처음부터 &lt;code&gt;x.com&lt;/code&gt; HTML을 직접 긁는 방식은 피하고, RSS-compatible feed를 통해 가져오는 구조로 잡았습니다. 현재는 로컬 RSSHub를 사용하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 RSSHub로 가져온 X 피드는 본문이 &lt;code&gt;description&lt;/code&gt; 안에 HTML 형태로 들어오는데, 그대로 쓰면 &lt;code&gt;&amp;lt;br&amp;gt;&lt;/code&gt;, 인용 블록, 이미지 태그, 영상 태그가 섞여 있어서 digest 품질이 좋지 않았습니다. 그래서 collector 단계에서 X 본문을 정리된 텍스트로 바꾸는 작업을 넣었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구체적으로는 아래처럼 처리했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;br&amp;gt;&lt;/code&gt;는 줄바꿈으로 유지&lt;/li&gt;
&lt;li&gt;인용문은 텍스트로 보존&lt;/li&gt;
&lt;li&gt;이미지 / 영상 태그는 제거&lt;/li&gt;
&lt;li&gt;&lt;code&gt;content_text&lt;/code&gt;에는 비교적 긴 본문 텍스트를 저장&lt;/li&gt;
&lt;li&gt;&lt;code&gt;content_preview&lt;/code&gt;는 Discord용 짧은 미리보기로 축약&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 바꾸고 나니 요약 단계에서 훨씬 안정적으로 본문을 활용할 수 있었습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;한국어 정리 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 OpenAI API를 붙여서 한국어 번역까지 자동으로 넣으려고 했습니다.&lt;br /&gt;그런데 실제로는 ChatGPT 구독과 API 사용이 별개이고, quota나 billing 이슈도 따로 관리해야 했습니다. 그래서 현재는 기본 동작을 &lt;code&gt;로컬 한국어 fallback 요약&lt;/code&gt;으로 두고, 필요할 때만 &lt;code&gt;ENABLE_OPENAI_TRANSLATION=true&lt;/code&gt;로 명시적으로 켜는 방식으로 바꿨습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fallback 쪽도 단순히 영어 문장을 그대로 붙이는 방식이 아니라, 용어 사전을 기반으로 조금 더 읽기 쉬운 한국어 digest가 되도록 보강했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 이런 식입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;agent orchestration&lt;/code&gt; -&amp;gt; &lt;code&gt;에이전트 오케스트레이션&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;coding workflow&lt;/code&gt; -&amp;gt; &lt;code&gt;코딩 워크플로&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;production operations&lt;/code&gt; -&amp;gt; &lt;code&gt;프로덕션 운영&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완전한 번역은 아니지만, 매일 보는 digest라는 목적에는 오히려 이 정도가 더 안정적이고 실용적이라고 느꼈습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Discord 출력 포맷&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 링크 모음은 실제로 잘 읽히지 않아서, Discord 메시지는 아래처럼 구조를 나눴습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오늘의 AI 새 소식&lt;/li&gt;
&lt;li&gt;요약&lt;/li&gt;
&lt;li&gt;핵심 하이라이트&lt;/li&gt;
&lt;li&gt;카테고리별 업데이트&lt;/li&gt;
&lt;li&gt;처리 참고&lt;/li&gt;
&lt;li&gt;실패한 소스&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 Discord 길이 제한 때문에 메시지가 길어질 경우를 대비해서 chunking도 넣었습니다. 처음에는 줄 중간이 잘리는 문제가 있었는데, 이후에는 가능한 한 줄 단위 경계를 유지하도록 다듬었습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;테스트와 운영&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트도 비교적 촘촘하게 넣으려고 했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RSS / HTML collector 테스트&lt;/li&gt;
&lt;li&gt;state store 테스트&lt;/li&gt;
&lt;li&gt;digest 렌더링 테스트&lt;/li&gt;
&lt;li&gt;enrichment 테스트&lt;/li&gt;
&lt;li&gt;service 오케스트레이션 테스트&lt;/li&gt;
&lt;li&gt;CLI 실행 테스트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 이번에는 X 본문 정리 로직도 테스트에 추가해서, HTML description이 텍스트로 잘 정리되는지 확인하도록 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영 방식은 최대한 단순하게 가져갔습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PowerShell 스크립트로 실행&lt;/li&gt;
&lt;li&gt;로컬 RSSHub 자동 기동&lt;/li&gt;
&lt;li&gt;Windows 작업 스케줄러 등록 가능&lt;/li&gt;
&lt;li&gt;테스트 시에는 운영 DB 대신 &lt;code&gt;test-state.db&lt;/code&gt; 사용&lt;/li&gt;
&lt;li&gt;OpenAI 번역은 기본 비활성화&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로젝트를 만들면서 다시 느낀 점은, 이런 자동화는 &quot;AI를 얼마나 화려하게 붙이느냐&quot;보다 &quot;매일 안정적으로 돌아가느냐&quot;가 훨씬 중요하다는 점이었습니다. 특히 정보 수집 도구는 완벽한 번역보다도, 놓치지 않고, 중복 없이, 읽기 쉬운 형태로 꾸준히 제공하는 쪽이 훨씬 가치가 있다고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 더 개선할 여지는 많습니다.&lt;br /&gt;예를 들어 중요도 판단 로직을 더 세밀하게 만들 수도 있고, fallback 요약 표현을 더 다듬을 수도 있고, 장기적으로는 NotebookLM 같은 도구와 연동해서 주간/월간 분석용 워크플로도 만들 수 있을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 지금 상태만으로도 &quot;매일 AI 소식을 한 번에 확인하는 도구&quot;로는 꽤 만족스럽게 동작하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 저장소는 아래에 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/blacknabis/ai-signal-digest&quot;&gt;https://github.com/blacknabis/ai-signal-digest&lt;/a&gt;&lt;/p&gt;</description>
      <category>AI/잡다한개발노트</category>
      <category>AI</category>
      <category>claude code</category>
      <category>discord</category>
      <category>OpenAI</category>
      <category>Python</category>
      <category>RSSHub</category>
      <category>Tistory</category>
      <category>X</category>
      <category>개발노트</category>
      <category>자동화</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/106</guid>
      <comments>https://blacknabis.tistory.com/106#entry106comment</comments>
      <pubDate>Sun, 12 Apr 2026 02:13:48 +0900</pubDate>
    </item>
    <item>
      <title> ️ Unity SRPG 개발 로그: 데이터 구조 대확장 &amp;mdash; 직업, 보스, 챕터, 조합 시스템</title>
      <link>https://blacknabis.tistory.com/105</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 &lt;b&gt;Claude&lt;/b&gt;와 함께 게임의 뼈대가 될 데이터 시스템을 대대적으로 확장했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능 구현보다 앞서 &lt;b&gt;데이터 구조가 탄탄해야&lt;/b&gt; 나중에 콘텐츠를 채울 때 손이 덜 가기 때문입니다. 신규 ScriptableObject 5개 신설, 기존 핵심 파일 5개 수정 &amp;mdash; 오늘 작업 기록을 남깁니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.   GridCell &amp;mdash; 비행/수중 이동 타입 지원&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRPG에서 비행 유닛(드래곤, 페가수스 등)이나 수중 유닛(해적, 인어 등)을 넣으려면 기존 &lt;code&gt;isWalkable&lt;/code&gt; 하나로는 부족합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  해결 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;GridCell.cs&lt;/code&gt;에 이동 타입별 프로퍼티를 추가했습니다.&lt;/p&gt;
&lt;pre class=&quot;fsharp&quot;&gt;&lt;code&gt;// 물 지형 여부 (수중 유닛 전용)
public bool isAquatic =&amp;gt; terrainData != null &amp;amp;&amp;amp; terrainData.type == TerrainType.Water;

// 비행 유닛: 점유 유닛만 막힘 (지형 무시)
public bool isWalkableForFlying =&amp;gt; occupyingUnit == null;

// 수중 유닛: 물/평지만 이동 가능
public bool isWalkableForAquatic =&amp;gt; occupyingUnit == null &amp;amp;&amp;amp;
    (terrainData == null || terrainData.type == TerrainType.Water || terrainData.type == TerrainType.Plain);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;GridManager.GetMovableCells()&lt;/code&gt; 도 오버로드를 추가해서 &lt;b&gt;Ground / Flying / Aquatic&lt;/b&gt; 세 가지 이동 타입에 따라 BFS 분기가 달라지도록 구현했습니다. 기존 코드와 완전 하위 호환됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   JobData ScriptableObject 신설 &amp;mdash; 직업 시스템 기반 설계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 &lt;code&gt;JobGrowthData&lt;/code&gt;(성장치 전용)만 있었는데, &lt;b&gt;직업 자체를 표현하는 데이터&lt;/b&gt;가 없었습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  새로 만든 것&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;JobData.cs&lt;/code&gt; &amp;mdash; 직업 한 개를 완전히 정의하는 ScriptableObject.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;기본 스탯&lt;/b&gt; (LV1 초기값 HP/MP/ATK/DEF/MATK/MDEF/SPD)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이동 타입&lt;/b&gt; (Ground / Flying / Aquatic)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;허용 장비 타입&lt;/b&gt; (검사는 검, 마법사는 스태프만 등)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스킬 습득 테이블&lt;/b&gt; &amp;mdash; 레벨 &amp;rarr; 스킬 매핑으로 레벨업 시 자동 습득&lt;/li&gt;
&lt;li&gt;&lt;b&gt;패시브 스킬 목록&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 &lt;code&gt;Unit.cs&lt;/code&gt;의 &lt;code&gt;jobData&lt;/code&gt; 필드에 연결하면, 레벨업 시 성장치와 스킬 습득이 &lt;code&gt;jobData&lt;/code&gt; 기준으로 자동 처리됩니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// Unit.cs &amp;mdash; LevelUp() 리팩토링
var growthData = jobData?.growthData ?? originData?.jobGrowthData;
var increases = stats.LevelUp(unitType, growthData);

// 레벨업 스킬 자동 습득
var newSkills = jobData.GetSkillsLearnedAtLevel(stats.level);
foreach (var skill in newSkills)
{
    if (!skills.Contains(skill)) skills.Add(skill);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 에디터에서 JobData 에셋을 만들어 직업 15개를 정의하면, 코드 수정 없이 모든 유닛에 적용됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   EnemyTemplateData + BossPhaseData 신설&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적 유닛마다 일일이 UnitData를 만드는 건 비효율적입니다. &lt;b&gt;템플릿 기반&lt;/b&gt;으로 구조를 바꿨습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;EnemyTemplateData&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;레벨 범위(min~max) 지정 &amp;rarr; 스폰 시 랜덤 레벨&lt;/li&gt;
&lt;li&gt;스킬 풀에서 랜덤 배분 + 고정 스킬 보장&lt;/li&gt;
&lt;li&gt;드롭 아이템 테이블 (확률 포함)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BossPhaseData&lt;/code&gt;를 연결하면 자동으로 보스 판정&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;BossPhaseData&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보스 HP가 특정 구간에 진입하면 자동으로 페이즈 전환이 일어납니다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;트리거: HP 50% 이하 / 특정 턴 경과 / 아군 X명 사망
행동:   AI 패턴 변경 / 새 스킬 추가 / 증원 소환 / 스탯 강화 / 외형 교체&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이널 판타지 택틱스의 보스전처럼, 전반부와 후반부 패턴이 확 달라지는 연출이 데이터만으로 설정 가능합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.  ️ ChapterData + GameDatabase 신설 &amp;mdash; StageDatabase 은퇴&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 &lt;code&gt;StageDatabase&lt;/code&gt;는 스테이지를 평면 리스트로만 관리했습니다. &lt;b&gt;챕터 개념&lt;/b&gt;을 도입해 계층 구조로 바꿨습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;GameDatabase
 ├── ChapterData (Chapter 1: 모험의 시작)
 │    ├── StageData (1-1)
 │    ├── StageData (1-2)
 │    └── StageData (1-3 보스)
 └── ChapterData (Chapter 2: 왕도로의 여정)
      └── ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ChapterData&lt;/code&gt;는 챕터 해금 조건, 클리어 보상(골드/아이템), 챕터 썸네일까지 포함합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;StageDatabase&lt;/code&gt;는 &lt;code&gt;[Obsolete]&lt;/code&gt; 마킹 후 메뉴 이름을 &lt;code&gt;[Legacy]&lt;/code&gt;로 변경해서 새 프로젝트에서 실수로 사용하는 걸 방지했습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. ⚔️ StageData 확장 &amp;mdash; 승리/패배 조건 + 증원 시스템&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 모든 스테이지의 조건이 &quot;적 전멸&quot;이었는데, 이걸 다양화했습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;승리 조건 (VictoryConditionType)&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;DefeatAllEnemies&lt;/td&gt;
&lt;td&gt;모든 적 처치 (기본)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DefeatBoss&lt;/td&gt;
&lt;td&gt;지정 보스만 처치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ReachDestination&lt;/td&gt;
&lt;td&gt;특정 좌표 도달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ProtectAlly&lt;/td&gt;
&lt;td&gt;아군 NPC 생존&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SurviveXTurns&lt;/td&gt;
&lt;td&gt;X턴 동안 버티기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CollectItem&lt;/td&gt;
&lt;td&gt;맵 아이템 수집&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;패배 조건 (DefeatConditionType)&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;AllPlayersDead&lt;/td&gt;
&lt;td&gt;아군 전멸 (기본)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ProtectedAllyDead&lt;/td&gt;
&lt;td&gt;보호 대상 사망&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TurnLimitExceeded&lt;/td&gt;
&lt;td&gt;제한 턴 초과&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EnemyReachesGoal&lt;/td&gt;
&lt;td&gt;적이 목표 지점 도달&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;증원 웨이브 (ReinforcementWave)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 턴이 되거나, 보스 HP가 일정 이하로 떨어지면 새 적이 소환됩니다. &lt;code&gt;announcement&lt;/code&gt; 문자열로 &quot;적의 증원이 도착했다!&quot; 같은 연출 메시지도 설정 가능합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. ⚗️ CraftRecipeData 신설 &amp;mdash; 연금술사 조합 시스템&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연금술사 직업을 위한 아이템 조합 레시피 ScriptableObject입니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// 조합 가능 여부 체크
recipe.CanCraft(inventory, unitLevel: 12, jobType: UnitType.Alchemist);

// 조합 시도 (RNG 반영)
bool success = recipe.TryRoll(out bool isCritical);
// 대성공 시 resultQuantity + 1 획득&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 기능:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;재료 목록 (아이템 + 수량)&lt;/li&gt;
&lt;li&gt;기본 성공률 / 대성공 확률 / 실패 시 재료 반환 비율&lt;/li&gt;
&lt;li&gt;직업 제한 (연금술사만 사용 가능 등)&lt;/li&gt;
&lt;li&gt;골드 비용&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  마치며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 작업의 핵심은 &lt;b&gt;&quot;나중에 콘텐츠를 빠르게 채울 수 있는 데이터 구조&quot;&lt;/b&gt; 를 만드는 것이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직업 15개, 적 20종, 스테이지 30개... 지금은 텅 비어 있지만, 이제 에디터에서 ScriptableObject 에셋만 생성하면 코드 수정 없이 쭉쭉 채워갈 수 있습니다. 다음 단계는 이 데이터 구조를 실제 &lt;code&gt;BattleManager&lt;/code&gt;에 연동해서 승리/패배 판정 로직을 구현하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Written with Claude&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 추천 태그&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#Unity #GameDev #IndieDev #SRPG #ScriptableObject #DataDriven #DevLog #Claude #AI_Coding #유니티 #개발로그 #인디게임 #전략RPG #직업시스템 #보스전&lt;/p&gt;</description>
      <category>AI/Unity</category>
      <category>AI_Coding</category>
      <category>Claude</category>
      <category>DataDriven</category>
      <category>Devlog</category>
      <category>gamedev</category>
      <category>indiedev</category>
      <category>scriptableObject</category>
      <category>SRPG</category>
      <category>Unity</category>
      <category>유니티</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/105</guid>
      <comments>https://blacknabis.tistory.com/105#entry105comment</comments>
      <pubDate>Sun, 5 Apr 2026 03:53:27 +0900</pubDate>
    </item>
    <item>
      <title>KikiLeoBook 릴리즈 준비 &amp;mdash; 패키지 리네임부터 GitHub Release까지</title>
      <link>https://blacknabis.tistory.com/104</link>
      <description>&lt;h1&gt;KikiLeoBook 릴리즈 준비 &amp;mdash; 패키지 리네임부터 GitHub Release까지&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 &lt;a href=&quot;https://claude.ai/claude-code&quot;&gt;Claude Code&lt;/a&gt;를 사용하여 작업하고 작성하였습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아내에게 전달할 안드로이드 전자책 앱 &lt;b&gt;KikiLeoBook&lt;/b&gt;의 첫 번째 릴리즈(&lt;code&gt;v1.0.0&lt;/code&gt;)를 준비했다. 오늘 작업의 핵심은 앱 정체성 확립(패키지명/앱명 변경), 릴리즈 서명 설정, Firebase 연동, 그리고 GitHub Release를 통한 버전 관리 체계 구축이었다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 패키지명 및 앱 이름 변경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 개발 단계에서 임시로 사용하던 이름을 정식 명칭으로 교체했다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;code&gt;com.****&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;com.blacknabis.kikileobook&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;앱 이름&lt;/td&gt;
&lt;td&gt;****Book&lt;/td&gt;
&lt;td&gt;KikiLeoBook&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB 이름&lt;/td&gt;
&lt;td&gt;&lt;code&gt;****book.db&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kikileobook.db&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SharedPrefs 키&lt;/td&gt;
&lt;td&gt;&lt;code&gt;****book_prefs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kikileobook_prefs&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스 디렉터리 전체를 &lt;code&gt;sed&lt;/code&gt;로 일괄 치환하고, 패키지 경로 디렉터리 구조도 함께 이동했다. 변경된 파일만 수십 개였지만 Claude Code가 일괄 처리해줘서 수작업 없이 깔끔하게 끝났다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 시맨틱 버전 관리&lt;/h2&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;// app/build.gradle.kts
versionCode = 1
versionName = &quot;1.0.0&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 릴리즈마다 &lt;code&gt;versionCode&lt;/code&gt;는 1씩 증가, &lt;code&gt;versionName&lt;/code&gt;은 시맨틱 버전(&lt;code&gt;major.minor.patch&lt;/code&gt;)으로 관리하기로 했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 설정 화면 추가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이브러리 화면 툴바에 설정 아이콘을 추가하고, 설정 화면 최하단에 앱 버전을 표시하도록 구현했다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;// SettingsActivity.kt
val versionName = packageManager.getPackageInfo(packageName, 0).versionName
binding.tvVersion.text = versionName&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 릴리즈 키스토어 생성 및 서명 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Play Store를 거치지 않는 APK 직접 배포이므로, 릴리즈 서명용 키스토어를 직접 생성해 관리한다.&lt;/p&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;keytool -genkeypair -v \
  -keystore signin/kikileobook-release.jks \
  -alias kikileobook \
  -keyalg RSA -keysize 2048 \
  -validity 10000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키스토어와 비밀번호는 &lt;code&gt;signin/&lt;/code&gt; 디렉터리에서 관리하며, private 레포이므로 git에도 함께 포함했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Gradle 서명 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;keystore.properties&lt;/code&gt;를 Java Properties가 아닌 커스텀 파서로 읽도록 처리했다. 비밀번호에 특수문자(&lt;code&gt;#&lt;/code&gt;)가 포함될 경우 Properties 파싱 오류가 발생하기 때문이다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// build.gradle.kts
val keystoreProperties = mutableMapOf&amp;lt;String, String&amp;gt;().apply {
    keystorePropertiesFile.readLines().forEach { line -&amp;gt;
        val trimmed = line.trim()
        if (trimmed.isNotEmpty() &amp;amp;&amp;amp; !trimmed.startsWith(&quot;#&quot;) &amp;amp;&amp;amp; trimmed.contains(&quot;=&quot;)) {
            val idx = trimmed.indexOf(&quot;=&quot;)
            put(trimmed.substring(0, idx).trim(), trimmed.substring(idx + 1))
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Firebase 연동 업데이트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패키지명이 바뀌었으므로 Firebase Console에 새 앱(&lt;code&gt;com.blacknabis.kikileobook&lt;/code&gt;)을 등록하고, 디버그/릴리즈 SHA-1 지문을 모두 등록했다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;SHA-1&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;code&gt;2a:10:f3:d9:...&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;릴리즈&lt;/td&gt;
&lt;td&gt;&lt;code&gt;5d:9f:8a:13:...&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;포인트&lt;/b&gt;: SHA-1 등록 후에는 반드시 &lt;code&gt;google-services.json&lt;/code&gt;을 재다운로드해서 &lt;code&gt;app/&lt;/code&gt; 디렉터리에 교체해야 한다. 그렇지 않으면 빌드에 이전 OAuth 설정이 그대로 반영된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code의 Chrome 제어 기능(MCP)을 활용해 Firebase Console 브라우저 자동화로 SHA-1 등록, &lt;code&gt;google-services.json&lt;/code&gt; 다운로드까지 자동으로 처리했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. Google OAuth 동의 화면 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google Cloud Console에서 OAuth 동의 화면 상태를 확인했다. &lt;b&gt;프로덕션 단계&lt;/b&gt;로 설정되어 있어 별도 테스터 등록 없이 모든 Google 계정으로 로그인 가능하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;테스트 모드&lt;/b&gt;: 등록된 테스트 사용자만 로그인 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프로덕션 모드&lt;/b&gt;: 누구나 로그인 가능 ✅&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 릴리즈 APK 빌드&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;./gradlew clean assembleRelease
# BUILD SUCCESSFUL in 1m 23s
# 49 actionable tasks: 49 executed&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클린 빌드를 통해 캐시 없이 최신 &lt;code&gt;google-services.json&lt;/code&gt;이 완전히 반영된 APK를 생성했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;출력 경로&lt;/b&gt;: &lt;code&gt;app/build/outputs/apk/release/app-release.apk&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;파일 크기&lt;/b&gt;: 8.7MB&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. GitHub Release 버전 관리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;릴리즈 APK를 GitHub Releases로 관리하기로 했다. 매 릴리즈마다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;versionCode&lt;/code&gt; / &lt;code&gt;versionName&lt;/code&gt; 업데이트&lt;/li&gt;
&lt;li&gt;&lt;code&gt;assembleRelease&lt;/code&gt; 빌드&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git tag vX.X.X &amp;amp;&amp;amp; git push origin vX.X.X&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;GitHub Release 생성 + APK 첨부&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 릴리즈:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;태그&lt;/b&gt;: &lt;code&gt;v1.0.&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;APK 배포는 Play Store를 사용하지 않으므로, GitHub Release에서 직접 다운로드하거나 카카오톡/구글 드라이브로 전달한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;트러블슈팅&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;키스토어 비밀번호 특수문자 문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 &lt;code&gt;Dhdudghks1!@#&lt;/code&gt; 비밀번호로 키스토어를 생성했는데, Gradle 빌드 시 &quot;keystore password was incorrect&quot; 오류가 계속 발생했다. &lt;code&gt;\#&lt;/code&gt; 이스케이프, Java Properties 파서 교체 등 여러 방법을 시도했지만 실패. 결국 bash의 &lt;code&gt;!&lt;/code&gt; 히스토리 확장 이슈로 키스토어 생성 자체가 잘못된 것으로 판단, 단순 비밀번호로 키스토어를 새로 생성해 해결했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;에뮬레이터에서 Google 로그인 실패&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱플레이어(에뮬레이터) 환경에서 Google 로그인 시 &lt;code&gt;SIGN_IN_CANCELLED(12501)&lt;/code&gt; 오류 발생. SHA-1이나 설정 문제가 아닌 에뮬레이터 환경 자체의 Google Play Services 비표준 동작이 원인. 실제 안드로이드 기기에서는 정상 작동한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패키지명 변경부터 Firebase 재등록, 릴리즈 서명, GitHub Release까지 꽤 많은 작업이었지만 Claude Code 덕분에 브라우저 자동화, 코드 일괄 수정, 빌드 명령 실행을 한 세션에서 끊김 없이 처리할 수 있었다. 특히 Firebase Console SHA-1 등록을 MCP Chrome 도구로 자동화한 부분이 인상적이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 기능 추가할 때마다 버전을 올리고 GitHub Release로 관리할 예정이다.&lt;/p&gt;</description>
      <category>AI/잡다한개발노트</category>
      <category>claude code</category>
      <category>Devlog</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/104</guid>
      <comments>https://blacknabis.tistory.com/104#entry104comment</comments>
      <pubDate>Sun, 29 Mar 2026 22:25:51 +0900</pubDate>
    </item>
    <item>
      <title>KikiLeoBook 개발 진행 상황 5차</title>
      <link>https://blacknabis.tistory.com/103</link>
      <description>&lt;h1 data-pm-slice=&quot;0 0 []&quot;&gt;JisunBook UI/UX 개선 프로젝트 보고서 (e-ink 최적화)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 개요&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;목표:&lt;/b&gt; e-ink 리더기 특성에 최적화된 높은 대비와 낮은 조작 뎁스 제공&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대상:&lt;/b&gt; 안드로이드 기반 전자잉크 리더기 사용자&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 주요 수정 내역&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 색상 및 대비 (Design Tokens)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Primary Text:&lt;/b&gt; #1A1A1A -&amp;gt; #000000&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Divider/Stroke:&lt;/b&gt; #B0B0B0 -&amp;gt; #666666&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Surface (Night):&lt;/b&gt; #000000 배경 위 #333333 요소 배치로 구별감 확보&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 하단 컨트롤 시스템&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;기존:&lt;/b&gt; Overflow Menu (⋮) 내에 기능 은닉&lt;/li&gt;
&lt;li&gt;&lt;b&gt;변경:&lt;/b&gt; 7개 핵심 아이콘 상주 (A-, A+, 목차, 북마크, 검색, TTS, 설정)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;추가:&lt;/b&gt; TTS 전용 플로팅 컨트롤 바 (재생/일시정지/정지)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.3 설정 화면 (BottomSheet)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;구조:&lt;/b&gt; 5개 아코디언 섹션 (글꼴, 레이아웃, 화면, TTS, 단축키)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;상태 유지:&lt;/b&gt; pref_last_opened_option_section 키를 활용해 마지막 섹션 자동 복원&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.4 위젯 최적화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;SeekBar:&lt;/b&gt; Thumb 32dp로 확대, Track 두께 10dp로 강화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Library Card:&lt;/b&gt; 테두리 2dp 강화 및 진행률(%) 표기 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 코드 변경 사항 (주요 파일)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;colors.xml, values-night/colors.xml: 대비 중심 색상 재정의&lt;/li&gt;
&lt;li&gt;fragment_reader.xml: 7-아이콘 하단 바 및 TTS 컨트롤 바 레이아웃 추가&lt;/li&gt;
&lt;li&gt;ReaderActivity.kt: 하단 바 로직 및 TTS 롱프레스 핸들링&lt;/li&gt;
&lt;li&gt;ReaderOptionBottomSheet.kt: 아코디언 로직 및 SharedPreferences 연동&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 향후 계획&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;물리 페이지 넘김 버튼 하드웨어 키 매핑 보완&lt;/li&gt;
&lt;li&gt;기기별 Ghosting(잔상) 방지를 위한 부분 새로고침 로직 검토&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI/잡다한개발노트</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/103</guid>
      <comments>https://blacknabis.tistory.com/103#entry103comment</comments>
      <pubDate>Sun, 29 Mar 2026 06:21:34 +0900</pubDate>
    </item>
    <item>
      <title>KikiLeoBook 개발 진행 상황 4차</title>
      <link>https://blacknabis.tistory.com/102</link>
      <description>&lt;h1&gt;KikiLeoBook 리더 개선 기록: TXT/EPUB TTS 붙이고 EPUB 위치 저장까지 점검한 날&lt;/h1&gt;
&lt;h2&gt;2026년 3월 25일 오후 - 3월 26일 새벽&lt;/h2&gt;
&lt;p&gt;이번 작업은 KikiLeoBook 리더에 &lt;code&gt;오프라인 TTS&lt;/code&gt;를 붙이는 데 집중했다.&lt;br&gt;처음에는 단순히 읽어주기 버튼 하나를 추가하는 정도로 생각했지만, 실제로는 TXT와 EPUB이 텍스트를 다루는 방식이 달라서 읽기 위치 표시, 페이지 이동 동기화, EPUB 위치 저장 문제까지 함께 정리해야 했다.&lt;/p&gt;
&lt;p&gt;결과적으로 이번 단계에서는 아래 세 가지가 중심이었다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;오프라인 TTS 1차 도입&lt;/li&gt;
&lt;li&gt;TXT / EPUB 읽기 흐름 정리&lt;/li&gt;
&lt;li&gt;EPUB 위치 복원 문제 디버깅&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;오프라인 TTS 1차 도입&lt;/h2&gt;
&lt;p&gt;이번에는 클라우드 음성은 제외하고, Android 기기 안에 있는 TTS 엔진을 사용하는 방향으로 정리했다.&lt;br&gt;비용 부담 없이 바로 붙일 수 있고, 현재 목표인 “앱 화면이 켜져 있을 때만 동작”에도 가장 잘 맞는 방식이었다.&lt;/p&gt;
&lt;p&gt;1차 목표는 이렇게 잡았다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;앱 화면이 켜져 있을 때만 동작&lt;/li&gt;
&lt;li&gt;TXT와 EPUB 모두 지원&lt;/li&gt;
&lt;li&gt;내부는 문장 단위로 읽되, UI는 &lt;code&gt;Read aloud / Pause / Stop&lt;/code&gt; 정도로 단순화&lt;/li&gt;
&lt;li&gt;백그라운드 재생이나 문장 이전/다음 같은 고급 기능은 다음 단계로 미룸&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;구조도 리더 화면에 TTS 코드를 직접 붙이기보다 공통 계층을 두는 방식으로 정리했다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ReaderTtsController&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AndroidTtsEngine&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ReaderTextProvider&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TtsUiState&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이렇게 해두니 TXT와 EPUB이 각자 다른 방식으로 텍스트를 제공하더라도, 상위 제어 흐름은 같은 방식으로 묶을 수 있게 됐다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;TXT와 EPUB에 TTS 연결&lt;/h2&gt;
&lt;p&gt;TXT는 비교적 straightforward했다.&lt;br&gt;이미 현재 페이지 문자열과 페이지 상태를 가지고 있어서, 현재 위치 기준으로 문장을 나눠 순차적으로 읽도록 연결하면 됐다.&lt;/p&gt;
&lt;p&gt;EPUB은 조금 더 손이 갔다.&lt;br&gt;WebView 안에서 렌더링되는 구조라 Android 쪽에서 현재 보이는 텍스트를 바로 알 수 없기 때문이다.&lt;br&gt;그래서 &lt;code&gt;epub.js&lt;/code&gt;와 Android 사이 브리지를 확장해서, JS에서 현재 보이는 페이지 기준 텍스트를 추출한 뒤 Android로 넘기는 흐름으로 정리했다.&lt;/p&gt;
&lt;p&gt;함께 반영한 내용은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;리더 상단 메뉴에서 &lt;code&gt;Read aloud&lt;/code&gt; 진입 가능하도록 정리&lt;/li&gt;
&lt;li&gt;TTS 시작, 일시정지, 정지 상태를 리더 상태와 연결&lt;/li&gt;
&lt;li&gt;아직 준비되지 않은 경우 무리하게 시작하지 않도록 방어&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Speech speed&lt;/code&gt;, &lt;code&gt;Speech pitch&lt;/code&gt; 옵션 연결&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Korean / English&lt;/code&gt; 선택은 1차 범위에서 제외&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;언어 선택은 처음에는 넣어봤지만, 실제 사용 기준에서는 굳이 유지할 필요가 없다고 판단했다.&lt;br&gt;현재 단계에서는 속도와 피치 정도만 남기는 쪽이 더 단순하고 실용적이었다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;현재 읽는 위치가 보이도록 개선&lt;/h2&gt;
&lt;p&gt;TTS를 붙이고 나니 바로 보였던 문제가 하나 있었다.&lt;br&gt;음성은 나오는데 지금 어디를 읽고 있는지가 화면에서 잘 보이지 않는다는 점이었다.&lt;/p&gt;
&lt;p&gt;그래서 현재 읽는 문장을 강조 표시하는 흐름을 추가했다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TXT는 현재 읽는 문장을 페이지 안에서 하이라이트&lt;/li&gt;
&lt;li&gt;EPUB도 WebView 내부에서 현재 문장을 강조 표시&lt;/li&gt;
&lt;li&gt;검색 하이라이트와 TTS 하이라이트가 서로 덮어쓰지 않도록 분리&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이와 함께, 읽는 문장이 다음 페이지로 넘어가면 화면도 같이 따라가도록 보강했다.&lt;br&gt;즉 TTS는 다음 페이지를 읽고 있는데 사용자가 보고 있는 화면은 이전 페이지에 멈춰 있는 어색한 상태를 줄이는 방향으로 정리한 것이다.&lt;/p&gt;
&lt;p&gt;이 부분은 실제 사용감에 꽤 크게 영향을 줬다.&lt;br&gt;단순히 읽어주는 기능에서 끝나는 것이 아니라, 지금 읽는 위치를 눈으로 함께 따라갈 수 있어야 훨씬 자연스럽게 느껴졌기 때문이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;중간 점검 결과&lt;/h2&gt;
&lt;p&gt;직접 확인한 결과는 다음과 같았다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TXT는 TTS 동작이 비교적 안정적으로 붙음&lt;/li&gt;
&lt;li&gt;EPUB도 현재 보이는 페이지 기준으로 읽어오는 동작까지는 정리됨&lt;/li&gt;
&lt;li&gt;메뉴 노출 문제와 준비 상태 gating 문제는 중간 수정으로 정리&lt;/li&gt;
&lt;li&gt;속도와 피치 옵션도 정상 동작 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;반면 EPUB 쪽에는 한 가지 큰 문제가 남아 있었다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;읽다가 나갔다가 다시 들어오면 항상 첫 페이지가 열림&lt;/li&gt;
&lt;li&gt;즉 EPUB 위치 복원이 실패하고 있었음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이건 단순 UI 문제가 아니라, 저장된 읽기 위치가 실제로 다시 적용되지 않는 흐름에 가까웠다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;EPUB 위치 복원 문제를 추적한 과정&lt;/h2&gt;
&lt;p&gt;이 문제는 생각보다 오래 걸렸다.&lt;br&gt;처음에는 Android 쪽 저장 순서 문제, WebView 초기화 타이밍 문제, &lt;code&gt;savedCfi&lt;/code&gt; 반영 순서 문제를 차례대로 의심했다.&lt;/p&gt;
&lt;p&gt;그래서 여러 번에 걸쳐 다음과 같은 방향으로 수정과 확인을 반복했다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EPUB progress와 &lt;code&gt;savedCfi&lt;/code&gt; 로드 순서 조정&lt;/li&gt;
&lt;li&gt;&lt;code&gt;openBook()&lt;/code&gt; 시점에 초기 CFI를 바로 반영하도록 수정&lt;/li&gt;
&lt;li&gt;복원 중 잘못된 첫 위치 이벤트가 저장값을 덮어쓰지 않도록 방어&lt;/li&gt;
&lt;li&gt;현재 표시 위치와 저장 위치가 다르면 다시 &lt;code&gt;goToCfi()&lt;/code&gt;를 시도하는 보강&lt;/li&gt;
&lt;li&gt;EPUB 압축 해제를 캐시 재사용 방식으로 바꿔 초기 로딩 시간을 완화&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 과정에서 로딩 속도는 다소 나아졌지만, 위치 복원 자체는 계속 첫 페이지로 열리는 문제가 남아 있었다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Claude Code로 이어서 확인한 원인&lt;/h2&gt;
&lt;p&gt;복원 문제가 계속 남아 있어서, 이 부분은 Claude Code로 이어서 원인 분석을 진행했다.&lt;br&gt;여기서 핵심 원인은 &lt;code&gt;CFI 저장값 자체가 비어 있거나 잘못된 문자열로 들어가고 있었다&lt;/code&gt;는 점이었다.&lt;/p&gt;
&lt;p&gt;정리하면 다음과 같다.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;epub_reader.html&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;locationChanged&lt;/code&gt; 이벤트 객체의 &lt;code&gt;loc.start.cfi&lt;/code&gt; 대신&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rendition.currentLocation().start.cfi&lt;/code&gt;에서 실제 CFI를 읽도록 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 epub.js 버전에서는 &lt;code&gt;loc.start.cfi&lt;/code&gt;가 항상 빈 문자열을 반환하고 있었고,&lt;br&gt;그 결과 Android 쪽에는 정상 위치가 아니라 빈 값이나 &lt;code&gt;&amp;quot;undefined&amp;quot;&lt;/code&gt; 같은 문자열이 들어가고 있었다.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;EpubReaderFragment.kt&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;epubcfi&amp;quot;&lt;/code&gt;로 시작하지 않는 값은 위치 변경 로직에서 무시하도록 방어 코드 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;ReaderActivity.kt&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;onStop()&lt;/code&gt;에서 EPUB 진행 저장 시에도 &lt;code&gt;cfi.startsWith(&amp;quot;epubcfi&amp;quot;)&lt;/code&gt;를 통과하는 값만 저장하도록 정리&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;즉, 처음에는 복원 로직 자체를 의심했지만, 실제로는 복원에 필요한 정상적인 위치값이 저장되지 않고 있었던 쪽에 더 가까웠다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;빌드와 검증&lt;/h2&gt;
&lt;p&gt;기능을 붙이고 나서는 코드 컴파일과 APK 생성까지 계속 확인했다.&lt;/p&gt;
&lt;p&gt;확인한 항목:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;compileDebugKotlin&lt;/code&gt; 성공&lt;/li&gt;
&lt;li&gt;&lt;code&gt;assembleDebug&lt;/code&gt; 성공&lt;/li&gt;
&lt;li&gt;디버그 APK 생성 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;실기기 기준으로는 다음 내용까지 확인했다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TXT TTS 동작 확인&lt;/li&gt;
&lt;li&gt;EPUB TTS가 현재 보이는 페이지부터 읽는 동작 확인&lt;/li&gt;
&lt;li&gt;EPUB 복원 문제는 원인 분석과 수정이 반영된 상태까지 정리&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;다만 EPUB 위치 복원은 최종적으로 실기기에서 한 번 더 재확인할 필요가 있다.&lt;br&gt;이번 글에서는 “원인 분석과 수정 반영까지 완료된 상태”를 기준으로 기록해둔다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;이번 작업에서 정리된 내용&lt;/h2&gt;
&lt;h3&gt;완료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;오프라인 Android TTS 1차 구조 추가&lt;/li&gt;
&lt;li&gt;TXT / EPUB 리더에 TTS 연결&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Read aloud / Pause / Stop&lt;/code&gt; 흐름 정리&lt;/li&gt;
&lt;li&gt;TTS 속도와 피치 옵션 연결&lt;/li&gt;
&lt;li&gt;TXT 현재 문장 하이라이트 반영&lt;/li&gt;
&lt;li&gt;EPUB 현재 문장 하이라이트 반영&lt;/li&gt;
&lt;li&gt;읽는 위치가 다음 페이지로 넘어갈 때 화면도 따라가도록 보강&lt;/li&gt;
&lt;li&gt;EPUB 현재 보이는 페이지 기준 텍스트 읽기 개선&lt;/li&gt;
&lt;li&gt;EPUB CFI 저장 경로 문제 원인 분석 및 수정 반영&lt;/li&gt;
&lt;li&gt;디버그 빌드와 APK 생성 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;남은 확인&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;EPUB 위치 복원 최종 실기기 재확인&lt;/li&gt;
&lt;li&gt;TTS 하이라이트 색상과 가독성 미세 조정&lt;/li&gt;
&lt;li&gt;EPUB 페이지 전환 시 체감 자연스러움 추가 점검&lt;/li&gt;
&lt;li&gt;이후 단계에서 백그라운드 재생 여부 검토&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;메모&lt;/h2&gt;
&lt;p&gt;이번 작업은 단순히 “읽어주기 기능을 추가했다”로 끝나는 작업은 아니었다.&lt;br&gt;TXT와 EPUB이 각각 다른 방식으로 텍스트를 다루고 있어서, 실제로는 리더 구조와 현재 위치 표현, 페이지 전환 동기화, 위치 저장 방식까지 같이 손봐야 했다.&lt;/p&gt;
&lt;p&gt;특히 EPUB은 WebView, JS 브리지, Android 저장 흐름이 함께 얽혀 있어서 겉으로 보이는 문제 하나를 수정하려고 해도 원인 추적 범위가 넓었다.&lt;/p&gt;
&lt;p&gt;그래도 이번 단계로 리더에 TTS 기본 골격은 올라갔고,&lt;br&gt;사용자가 “듣고, 어디를 읽는지 보고, 페이지 흐름을 따라가는” 경험까지는 만들어가는 단계에 들어섰다.&lt;/p&gt;
&lt;p&gt;다음 단계에서는 EPUB 위치 복원 최종 재확인과 함께,&lt;br&gt;하이라이트 완성도와 문장 추적 안정성을 더 다듬는 쪽으로 이어갈 예정이다.&lt;/p&gt;</description>
      <category>AI/잡다한개발노트</category>
      <category>Claude</category>
      <category>codex</category>
      <category>deevlog</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/102</guid>
      <comments>https://blacknabis.tistory.com/102#entry102comment</comments>
      <pubDate>Thu, 26 Mar 2026 00:23:59 +0900</pubDate>
    </item>
    <item>
      <title>KikiLeoBook 개발 진행 상황 3차</title>
      <link>https://blacknabis.tistory.com/101</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;2026년 3월 24일 - 25일&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 이틀은 KikiLeoBook 리더 쪽을 집중적으로 손봤다.&lt;br /&gt;처음에는 단순히 빠진 기능을 다시 붙이는 정도로 생각했는데, 실제로 들어가 보니 TXT 리더 안정성, 페이지 이동 상태 동기화, EPUB 표시 정보 연결까지 같이 정리해야 하는 흐름이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로는 &lt;code&gt;리더 옵션 복구&lt;/code&gt;, &lt;code&gt;TXT 리더 구조 개선&lt;/code&gt;, &lt;code&gt;진행 표시 UI 개선&lt;/code&gt; 이 세 가지를 중심으로 작업했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3월 24일&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리더 옵션 기능을 다시 &lt;code&gt;master&lt;/code&gt;로 복구&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 확인한 건 &amp;ldquo;예전에 넣어뒀던 페이지 넘김 탭 기능과 옵션 설정이 왜 안 보이느냐&amp;rdquo;는 문제였다.&lt;br /&gt;조사해 보니 기능이 사라진 게 아니라, 관련 작업이 &lt;code&gt;feature/reader-page-options&lt;/code&gt; 브랜치에만 있고 &lt;code&gt;master&lt;/code&gt;에는 반영되지 않은 상태였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 브랜치 내용을 다시 확인한 뒤 필요한 변경을 &lt;code&gt;master&lt;/code&gt;에 병합했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복구된 기능은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;화면 좌우 탭으로 이전/다음 페이지 이동&lt;/li&gt;
&lt;li&gt;리더 옵션 바텀시트&lt;/li&gt;
&lt;li&gt;글꼴 크기, 글꼴 종류, 줄 간격, 페이지 여백 설정&lt;/li&gt;
&lt;li&gt;배경색 프리셋&lt;/li&gt;
&lt;li&gt;화면 꺼짐 방지&lt;/li&gt;
&lt;li&gt;페이지 이동 / 북마크 / 검색 단축키 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 작업으로 &amp;ldquo;예전에는 있었는데 지금은 안 보이는 기능&amp;rdquo; 문제를 정리했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TXT 파일을 열 때 멈추는 문제 대응&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음은 TXT 리더 쪽이었다.&lt;br /&gt;큰 TXT 파일을 열면 앱이 한동안 멈추거나, 경우에 따라 응답 없음처럼 보이는 증상이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인을 따라가 보니, 페이지 분할과 텍스트 처리 일부가 메인 스레드에 부담을 주고 있었다.&lt;br /&gt;특히 전체 텍스트를 한 번에 다루는 흐름은 파일이 커질수록 UI 멈춤으로 이어질 가능성이 컸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 구조를 이렇게 조정했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TXT 페이지 분할을 백그라운드에서 수행하도록 변경&lt;/li&gt;
&lt;li&gt;기존 chunk 기반 읽기 흐름을 다시 활용&lt;/li&gt;
&lt;li&gt;필요한 chunk만 읽어서 페이지화하도록 정리&lt;/li&gt;
&lt;li&gt;오래된 비동기 결과가 현재 화면 상태를 덮어쓰지 않도록 보호 로직 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &amp;ldquo;한 번에 다 처리하려던 구조&amp;rdquo;를 &amp;ldquo;필요한 만큼 읽고 계산하는 구조&amp;rdquo;로 되돌리는 쪽에 가깝다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;페이지 이동 후 원래 페이지로 되돌아오는 문제 수정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이날 가장 신경 쓴 부분은 TXT 페이지 이동 안정성이었다.&lt;br /&gt;하단 진행 바로 위치를 옮긴 뒤 오른쪽 화면을 눌러 다음 페이지로 넘기면, 잠깐 애니메이션이 보였다가 다시 원래 페이지로 돌아오는 문제가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 단순 UI 문제가 아니라 비동기 로딩 타이밍 문제였다.&lt;br /&gt;이전에 요청된 페이지 계산 결과가 늦게 도착하면서 현재 상태를 다시 덮어쓰는 흐름이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 아래처럼 정리했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;페이지 로드 세대 관리 토큰 추가&lt;/li&gt;
&lt;li&gt;늦게 도착한 오래된 로드 결과는 무시&lt;/li&gt;
&lt;li&gt;레이아웃이 실제로 바뀐 경우에만 재페이지화&lt;/li&gt;
&lt;li&gt;내부 페이지 상태와 &lt;code&gt;ViewPager2&lt;/code&gt; 현재 페이지 상태를 다시 맞춤&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 수정으로 &amp;ldquo;넘어갔다가 다시 돌아오는&amp;rdquo; 현상을 줄이는 방향으로 안정화했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3월 25일&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;하단 진행 바를 옵션에서 끄고 켤 수 있게 변경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 리더 UI 사용감을 손봤다.&lt;br /&gt;기존에는 하단 진행 바가 항상 보여서, 책 내용에 조금 더 집중하고 싶은 경우에도 화면 하단이 계속 차지되고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 리더 옵션에 &lt;code&gt;하단 진행 바 표시&lt;/code&gt; 스위치를 추가했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적용한 내용:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;옵션 바텀시트에 진행 바 표시 스위치 추가&lt;/li&gt;
&lt;li&gt;설정값을 &lt;code&gt;AppPreferences&lt;/code&gt;에 저장&lt;/li&gt;
&lt;li&gt;앱을 다시 열어도 이전 상태 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본값은 기존 동작과 호환되도록 &lt;code&gt;표시&lt;/code&gt; 상태로 두었다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;진행 바를 숨기면 우하단에 상태 표시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진행 바를 아예 숨겨버리면 현재 어느 정도 읽었는지 감이 안 잡힐 수 있어서,&lt;br /&gt;대신 우하단에 작은 상태 표시를 두는 방식으로 보완했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표시 규칙은 이렇게 잡았다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;값이 준비된 경우: &lt;code&gt;현재 페이지 / 전체 페이지&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;아직 계산 중인 경우: &lt;code&gt;Loading...&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 정보를 정확히 보여줄 수 있을 때만 숫자를 보여주고,&lt;br /&gt;아직 계산이 끝나지 않았으면 애매한 숫자를 보여주지 않도록 했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;EPUB 페이지 상태를 Android 쪽으로 연결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EPUB은 &lt;code&gt;epub.js&lt;/code&gt;를 사용하고 있는데,&lt;br /&gt;기존에는 진행률 퍼센트 정도만 Android 쪽으로 전달하고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 &lt;code&gt;locationChanged&lt;/code&gt; 시점에 아래 정보를 같이 넘기도록 정리했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 화면 기준 페이지 번호&lt;/li&gt;
&lt;li&gt;현재 화면 기준 전체 페이지 수&lt;/li&gt;
&lt;li&gt;아직 계산 불가한 경우의 loading 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Android 쪽에서는 이 값을 받아서 우하단 상태 표시와 연결했다.&lt;br /&gt;아직 페이지 수가 준비되지 않았을 때는 숫자 대신 &lt;code&gt;Loading...&lt;/code&gt;을 보여주도록 했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TXT 전체 페이지 수는 백그라운드 계산으로 처리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TXT 쪽은 EPUB처럼 바로 화면 기준 페이지 정보를 주는 라이브러리가 있는 게 아니라서,&lt;br /&gt;전체 페이지 수를 보여주려면 결국 페이지 계산을 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 이걸 메인 스레드에서 하면 다시 리더가 무거워질 수 있다는 점이었다.&lt;br /&gt;그래서 TXT는 현재 chunk만 즉시 표시하고, 전체 페이지 수는 백그라운드에서 차근차근 계산한 뒤 준비되면 우하단 표시를 갱신하도록 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식의 장점은 두 가지다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리더가 먼저 열리고 페이지 표시가 뒤따라와도 된다&lt;/li&gt;
&lt;li&gt;전체 페이지 수 계산 때문에 화면이 멈추지 않는다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 입장에서는 처음엔 &lt;code&gt;Loading...&lt;/code&gt;이 보이다가, 계산이 끝나면 &lt;code&gt;현재 / 전체&lt;/code&gt;로 바뀌는 흐름이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;테스트와 빌드 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능을 붙인 뒤에는 바로 검증도 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인한 항목:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;AppPreferencesTest&lt;/code&gt; 통과&lt;/li&gt;
&lt;li&gt;&lt;code&gt;assembleDebug&lt;/code&gt; 빌드 성공&lt;/li&gt;
&lt;li&gt;디버그 APK 생성 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 남아 있는 것은 치명적인 오류가 아니라 경고 수준이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;TxtReaderFragment&lt;/code&gt;의 &lt;code&gt;scaledDensity&lt;/code&gt; deprecated 경고&lt;/li&gt;
&lt;li&gt;일부 테스트의 coroutine opt-in 경고&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 다음 정리 단계에서 다뤄도 되는 수준이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이번 작업에서 정리된 내용&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;완료&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리더 옵션 기능 &lt;code&gt;master&lt;/code&gt; 복구&lt;/li&gt;
&lt;li&gt;페이지 좌우 탭 이동 복구&lt;/li&gt;
&lt;li&gt;TXT 리더 chunk 기반 처리 정리&lt;/li&gt;
&lt;li&gt;TXT 페이지 이동 되돌림 현상 완화&lt;/li&gt;
&lt;li&gt;하단 진행 바 ON/OFF 옵션 추가&lt;/li&gt;
&lt;li&gt;진행 바 숨김 시 우하단 상태 표시 추가&lt;/li&gt;
&lt;li&gt;EPUB 페이지 상태 전달 연결&lt;/li&gt;
&lt;li&gt;설정 테스트 통과&lt;/li&gt;
&lt;li&gt;디버그 빌드 성공&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;남은 확인&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 기기 기준으로 TXT/EPUB 페이지 표시 체감 점검&lt;/li&gt;
&lt;li&gt;우하단 페이지 수 표시 정확도 추가 확인&lt;/li&gt;
&lt;li&gt;deprecated / opt-in 경고 정리&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메모&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 작업은 기능을 &amp;ldquo;더 넣었다&amp;rdquo;기보다는,&lt;br /&gt;읽는 도중에 거슬리는 부분을 줄이고 리더 상태를 더 일관되게 만드는 작업에 가까웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 TXT 리더는 성능과 상태 동기화가 같이 얽혀 있어서,&lt;br /&gt;단순한 UI 수정이 아니라 내부 흐름을 같이 정리해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 단계에서는 실제 기기 테스트를 조금 더 돌려보면서,&lt;br /&gt;남아 있는 표시 정확도와 경고 정리까지 마무리할 예정이다.&lt;/p&gt;</description>
      <category>AI/잡다한개발노트</category>
      <category>codex</category>
      <category>Devlog</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/101</guid>
      <comments>https://blacknabis.tistory.com/101#entry101comment</comments>
      <pubDate>Wed, 25 Mar 2026 02:06:03 +0900</pubDate>
    </item>
    <item>
      <title>KikiLeoBook 개발 진행 상황 2차</title>
      <link>https://blacknabis.tistory.com/100</link>
      <description>&lt;h2 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size26&quot;&gt;1차는 별도의 블로그 없이 개발 진행 상태.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;Android TXT/EPUB 전자책 리더 앱 &lt;b data-index-in-node=&quot;26&quot; data-path-to-node=&quot;4&quot;&gt;KikiLeoBook&lt;/b&gt;의 개발을 진행 중입니다. 어제와 오늘 3가지 주요 버그를 수정하고 5가지 새로운 기능을 추가했습니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;5&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size26&quot;&gt;  어제 (3월 23일) &amp;mdash; 버그 수정&lt;/h2&gt;
&lt;h3 data-path-to-node=&quot;7&quot; data-ke-size=&quot;size23&quot;&gt;1️⃣ EPUB 파일 실행 시 ERR_CLEARTEXT_NOT_PERMITTED 에러&lt;/h3&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8&quot;&gt;문제:&lt;/b&gt; Android 9+ 이상에서 HTTP 통신 차단. NanoHTTPD 로컬 서버(localhost:8765)에서 EPUB을 렌더링하는데 cleartext(암호화되지 않은) 트래픽 금지로 epub.js 로드 실패.&lt;/p&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9&quot;&gt;해결:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;app/src/main/res/xml/network_security_config.xml 생성&lt;/li&gt;
&lt;li&gt;localhost/127.0.0.1에만 cleartext traffic 허용&lt;/li&gt;
&lt;li&gt;AndroidManifest.xml에서 android:networkSecurityConfig 참조&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;11&quot; data-ke-size=&quot;size23&quot;&gt;2️⃣ TXT 파일 읽던 위치가 복원되지 않음&lt;/h3&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12&quot;&gt;문제:&lt;/b&gt; 책을 읽다가 나갔다가 다시 열면 처음부터 시작. getCurrentPosition()이 chunk index만 저장하고 chunk 내 정확한 위치를 무시.&lt;/p&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13&quot;&gt;해결:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;14&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,0,0&quot;&gt;픽셀 레벨 offset 저장:&lt;/b&gt; &quot;chunkIndex:pixelOffset&quot; 형식 사용&lt;/li&gt;
&lt;li&gt;LinearLayoutManager.findViewByPosition()?.top으로 정확한 스크롤 위치 계산&lt;/li&gt;
&lt;li&gt;scrollToPositionWithOffset() + post {}로 UI 업데이트 동기화&lt;/li&gt;
&lt;li&gt;NonCancellable context로 코루틴 취소 시에도 진도 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;15&quot; data-ke-size=&quot;size23&quot;&gt;3️⃣ Google 로그인 실패 (구글 로그인 취소됨)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16&quot;&gt;원인:&lt;/b&gt; Firebase Console 내 SHA-1 미등록 및 google-services.json 설정 누락.&lt;/p&gt;
&lt;p data-path-to-node=&quot;17&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17&quot;&gt;해결:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;18&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Android Studio에서 SHA-1 추출 및 Firebase 등록&lt;/li&gt;
&lt;li&gt;Firebase Authentication 내 Google 설정 활성화&lt;/li&gt;
&lt;li&gt;OAuth client ID가 포함된 google-services.json 재적용&lt;/li&gt;
&lt;li&gt;strings.xml에 정확한 web client ID 입력&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;19&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;20&quot; data-ke-size=&quot;size26&quot;&gt;  오늘 (3월 24일) &amp;mdash; 기능 추가 및 개선&lt;/h2&gt;
&lt;h3 data-path-to-node=&quot;21&quot; data-ke-size=&quot;size23&quot;&gt;✨ 1. 파일 추가 방식 개선 (Intent)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;22&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;22&quot;&gt;기존:&lt;/b&gt; 앱 내 + 버튼을 통한 파일 피커 방식만 지원.&lt;/p&gt;
&lt;p data-path-to-node=&quot;23&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;23&quot;&gt;개선:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;24&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;24,0,0&quot;&gt;공유 인텐트:&lt;/b&gt; 외부 파일 관리자에서 '공유'를 통해 &lt;b data-index-in-node=&quot;29&quot; data-path-to-node=&quot;24,0,0&quot;&gt;KikiLeoBook&lt;/b&gt; 선택 가능&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;24,1,0&quot;&gt;ACTION_VIEW:&lt;/b&gt; .txt, .epub 파일을 탭하면 즉시 &lt;b data-index-in-node=&quot;36&quot; data-path-to-node=&quot;24,1,0&quot;&gt;KikiLeoBook&lt;/b&gt;으로 실행&lt;/li&gt;
&lt;li&gt;AndroidManifest.xml에 관련 intent-filter 구현 완료&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;25&quot; data-ke-size=&quot;size23&quot;&gt;✨ 2. 검색 성능 개선 (Debounce)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;26&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ReaderViewModel에 &lt;b data-index-in-node=&quot;17&quot; data-path-to-node=&quot;26,0,0&quot;&gt;500ms Debounce&lt;/b&gt; 적용.&lt;/li&gt;
&lt;li&gt;사용자가 입력을 멈춘 후 0.5초 뒤에만 검색을 실행하여 리소스 낭비 방지 및 체감 속도 향상.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;27&quot; data-ke-size=&quot;size23&quot;&gt;✨ 3. 독서 진행도 표시&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;28&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;화면 하단에 &lt;b data-index-in-node=&quot;7&quot; data-path-to-node=&quot;28,0,0&quot;&gt;실시간 진행도 바(%)&lt;/b&gt; 추가.&lt;/li&gt;
&lt;li&gt;TXT 리더는 RecyclerView 스크롤 기반, EPUB 리더는 epub.js percentage를 수신하여 동기화.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;29&quot; data-ke-size=&quot;size23&quot;&gt;✨ 4. TXT 페이지 넘김 모드 도입&lt;/h3&gt;
&lt;p data-path-to-node=&quot;30&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;30&quot;&gt;기존:&lt;/b&gt; 수직 스크롤 방식만 지원.&lt;/p&gt;
&lt;p data-path-to-node=&quot;31&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;31&quot;&gt;개선:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;32&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;32,0,0&quot;&gt;ViewPager2&lt;/b&gt; 기반의 좌우 스와이프 페이지 모드 추가.&lt;/li&gt;
&lt;li&gt;TxtPaginator를 통해 뷰포트 높이에 맞춰 텍스트를 자동으로 분할(Pagination).&lt;/li&gt;
&lt;li&gt;사용자 설정(AppPreferences)에 모드 저장 및 복원 기능 구현.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;33&quot; data-ke-size=&quot;size23&quot;&gt;✨ 5. 브랜드 리네이밍 (JisunBook &amp;rarr; KikiLeoBook)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;34&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;strings.xml 및 앱 내부 상수를 &lt;b data-index-in-node=&quot;23&quot; data-path-to-node=&quot;34,0,0&quot;&gt;KikiLeoBook&lt;/b&gt;으로 전면 교체.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;35&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;36&quot; data-ke-size=&quot;size26&quot;&gt;  구현 현황&lt;/h2&gt;
&lt;h3 data-path-to-node=&quot;37&quot; data-ke-size=&quot;size23&quot;&gt;✅ 완료&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;38&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[x] EPUB cleartext 에러 수정&lt;/li&gt;
&lt;li&gt;[x] TXT 위치 복원 (픽셀 레벨 offset)&lt;/li&gt;
&lt;li&gt;[x] Google 로그인 설정 완료&lt;/li&gt;
&lt;li&gt;[x] 파일 공유/열기 인텐트 연결&lt;/li&gt;
&lt;li&gt;[x] 검색 debounce (500ms)&lt;/li&gt;
&lt;li&gt;[x] 진행도 표시 (%)&lt;/li&gt;
&lt;li&gt;[x] TXT 페이지 모드 (ViewPager2)&lt;/li&gt;
&lt;li&gt;[x] 단위 테스트 통과 (TxtPaginatorTest 등)&lt;/li&gt;
&lt;li&gt;[x] &lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;38,8,0&quot;&gt;master 브랜치 푸시 완료 (커밋: 969098e)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;39&quot; data-ke-size=&quot;size23&quot;&gt;  진행 중&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;40&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[ ] 수동 테스트 (TXT 페이지 넘김, EPUB paginated 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;41&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;42&quot; data-ke-size=&quot;size26&quot;&gt; ️ 기술 스택&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;43&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;43,0,0&quot;&gt;언어:&lt;/b&gt; Kotlin&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;43,1,0&quot;&gt;UI:&lt;/b&gt; Material Design 3, ViewPager2&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;43,2,0&quot;&gt;DB:&lt;/b&gt; Room (SQLite)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;43,3,0&quot;&gt;라이브러리:&lt;/b&gt; epub.js, NanoHTTPD, JUnit 4&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI/잡다한개발노트</category>
      <category>Claude</category>
      <category>codex</category>
      <category>Devlog</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/100</guid>
      <comments>https://blacknabis.tistory.com/100#entry100comment</comments>
      <pubDate>Tue, 24 Mar 2026 02:50:17 +0900</pubDate>
    </item>
    <item>
      <title>Claude Code Remote 그리고 Codex원격을 위한 PocketDex</title>
      <link>https://blacknabis.tistory.com/99</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;집에서 좀 더 편한(?) 바이브 코딩을 위해서 Claude Code의 리모트 기능이 있어서 사용해봤음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 서버 모드&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;cluade remote-control&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 대화와 원경 동시 사용&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;calude --remote-control&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Claude Code 실행 중&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;/remote-control&lt;br /&gt;or&lt;br /&gt;/rc&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번으로 실행하고 스페이스바를 누르면 qr이 뜨고 핸드폰원격으로 주소를 치거나 1~3번 모든 방법에서는 url주소도 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 세션에서 실행 해서인지 기존 기능을 추가할려고 보니 막상 플랜만 세우는데 토큰을 모두 소모하여 이북리더기 앱의 코딩 한줄도 사용 못함..... 그래서 토큰이 널널한 대안으로 PocketDex를 사용햅보기로 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 안티그래비티에서 PocketDex 설치해 줘 딸칵. (깃에서 받아서 npm으로 설치하면 되는듯 하지만 최대한 ai돌려서 해보고싶다...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 터미널에서 pocketdex을 치면 된다고 했지만(환경변수는 알아서 등록해줬음) 나중을 위해 배치파일을 추가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 실행하면 qr이 나오고 모바일로 접속하면 완료&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;2316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOBd01/dJMcagkymir/Mj8qXrjuqlfWjeYAOG0Lz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOBd01/dJMcagkymir/Mj8qXrjuqlfWjeYAOG0Lz0/img.png&quot; data-alt=&quot;실행화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOBd01/dJMcagkymir/Mj8qXrjuqlfWjeYAOG0Lz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOBd01%2FdJMcagkymir%2FMj8qXrjuqlfWjeYAOG0Lz0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;375&quot; height=&quot;961&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;2316&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/Etc</category>
      <category>claude code remote</category>
      <category>pocketdex</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/99</guid>
      <comments>https://blacknabis.tistory.com/99#entry99comment</comments>
      <pubDate>Tue, 24 Mar 2026 00:01:45 +0900</pubDate>
    </item>
    <item>
      <title>[개발일지] SF6 뷰어 V1.1 업데이트: 커스텀 배경 스킨 및 데이터 정합성 개선</title>
      <link>https://blacknabis.tistory.com/98</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전글 : &lt;a href=&quot;https://blacknabis.tistory.com/38&quot;&gt;https://blacknabis.tistory.com/38&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;[개발일지] SF6 뷰어 V1.1 업데이트: 커스텀 배경 스킨 및 데이터 정합성 개선&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난번 UI 리디자인 업데이트 이후, 많은 분들이 각자의 방송 환경에 맞게 뷰어를 활용할 수 있도록 &lt;b&gt;개인화(Customization)&lt;/b&gt; 옵션에 대한 필요성을 느꼈습니다. 이번 V1.1 업데이트에서는 방송 화면을 더욱 개성 있게 꾸밀 수 있는 '커스텀 배경 스킨' 기능과, 데이터 정합성을 높이기 위한 내부 로직 개선을 진행했습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  챕터 1: 방송 화면 커스터마이징 (커스텀 배경 스킨 지원)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 게임 화면 위에 자연스럽게 녹아들도록 완전 투명(Transparent) 배경만을 고정으로 사용했습니다. 하지만 스트리머마다 다른 컨셉의 방송 화면을 꾸밀 수 있도록, &lt;b&gt;자유로운 배경 스킨 적용 기능&lt;/b&gt;을 새롭게 추가했습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 로컬 경로 및 URL을 통한 배경 이미지 삽입&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;업로드 및 URL 지원&lt;/b&gt;: 대시보드의 'Stats Background' 메뉴에서 웹 이미지 URL을 직접 입력하거나, [Browse...] 버튼을 통해 PC에 저장된 로컬 이미지 파일을 바로 업로드할 수 있습니다.&lt;/li&gt;
&lt;li&gt;업로드된 이미지는 서버에 안전하게 저장(&lt;code&gt;custom_bg.img&lt;/code&gt;)되며 새로고침해도 설정이 유지됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 가독성을 고려한 독립적인 0~100% 투명도(Opacity) 조절&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단순히 전체 창에 투명도를 줄 경우, 텍스트와 그래프까지 흐려져버려 가장 중요한 '가독성'을 해치는 문제가 발생합니다.&lt;/li&gt;
&lt;li&gt;이를 해결하기 위해 &lt;b&gt;가장 하단에 전용 배경 레이어(&lt;code&gt;bg-layer&lt;/code&gt;)를 깔아 이미지에만 단독으로 투명도를 먹이는 방식&lt;/b&gt;으로 구현했습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;결과&lt;/b&gt;: 배경은 0~100%로 세밀하게 반투명 효과(Glassmorphism)를 주면서도, 맨 앞의 정보(캐릭터, 승률, MR 그래프 등)는 100% 선명하게 돋보이도록 처리했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 컴팩트 사이즈(&lt;code&gt;1400x180&lt;/code&gt;) 자동 최적화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OBS 브라우저 소스 표준 규격으로 맞춘 1400px x 180px 하단바 사이즈에 맞추어, 어떤 크기의 이미지를 업로드하더라도 여백 없이 예쁘게 가득 차도록 &lt;code&gt;background-size: cover&lt;/code&gt; 설정을 적용했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅁ&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  챕터 2: 데이터 정합성 개선 (MR 변동 그래프 버그 수정)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디자인을 넘어 데이터의 정확도 또한 놓칠 수 없습니다. 우측 &lt;code&gt;MR 히스토리 차트&lt;/code&gt;가 종종 시간의 역순(최신 기록 -&amp;gt; 과거 기록)으로 그려지는 현상이 발견되어 쿼리 구조를 개편했습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 매치(Match) 로딩 순서 교정&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존에는 데이터베이스에서 가장 최근 N개의 매치를 불러온 그대로 그래프에 출력하여, 차트의 가장 마지막 지점과 현재 실제 MR이 불일치하는 오류가 있었습니다.&lt;/li&gt;
&lt;li&gt;백엔드(&lt;code&gt;/api/mr_history&lt;/code&gt;) 조회 로직을 수정하여, &lt;b&gt;최근 매치 데이터들을 가져온 뒤 완벽한 시간 순서(오래된 순 -&amp;gt; 최신 순)로 재정렬&lt;/b&gt;한 후 프론트에 전달하도록 개선했습니다.&lt;/li&gt;
&lt;li&gt;이제 차트는 항상 최신 흐름을 향해 우상향(혹은 우하향)하는 정확한 추이를 시각적으로 보여줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⚙️ 챕터 3: 배포 및 실행 편의성 강화&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. V1.1 릴리즈 빌드 (&lt;code&gt;SF6ViewerV1_1.zip&lt;/code&gt;)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파이썬(Python) 환경에 익숙하지 않은 윈도우 유저분들도 단 1번의 클릭만으로 손쉽게 프로그램을 실행할 수 있도록, 새로운 기능을 병합하여 &lt;b&gt;독립형 실행 파일(EXE) 버전을 V1.1로 패키징&lt;/b&gt;해 배포했습니다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/blacknabis/SF6RankViewer&quot;&gt;GitHub Releases&lt;/a&gt; 탭에서 &lt;code&gt;SF6ViewerV1_1.zip&lt;/code&gt;을 다운받으실 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Mac 유저를 위한 문서 최신화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;윈도우 환경 이외에 &lt;code&gt;Playwright&lt;/code&gt;와 소스코드를 직접 받아야 하는 macOS 유저들을 위해, 프로젝트 &lt;code&gt;README_MAC.md&lt;/code&gt;와 메인 &lt;code&gt;README.md&lt;/code&gt; 문서를 이번 신기능 설명과 함께 일괄 최신화했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1VBZg/dJMcadaeGMU/ITOXp7QNnBXtXuGdS1RsIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1VBZg/dJMcadaeGMU/ITOXp7QNnBXtXuGdS1RsIk/img.png&quot; data-alt=&quot;OBS에서 적용 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1VBZg/dJMcadaeGMU/ITOXp7QNnBXtXuGdS1RsIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1VBZg%2FdJMcadaeGMU%2FITOXp7QNnBXtXuGdS1RsIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1072&quot; height=&quot;602&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;602&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OBS에서 적용 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>AI</category>
      <category>OBS오버레이</category>
      <category>SF6</category>
      <category>뷰어프로그램</category>
      <category>스트리트파이터6</category>
      <category>스파6</category>
      <category>오픈소스</category>
      <category>인터넷방송</category>
      <category>파이썬프로젝트</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/98</guid>
      <comments>https://blacknabis.tistory.com/98#entry98comment</comments>
      <pubDate>Sun, 22 Mar 2026 22:39:00 +0900</pubDate>
    </item>
    <item>
      <title>Godot엔진으로 Lucky7Quest 만들기(codex,antigravity,claude code)</title>
      <link>https://blacknabis.tistory.com/97</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;1333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yULm6/dJMcaiJfvfm/gWM4ufkOo3tRrKDGqkgiKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yULm6/dJMcaiJfvfm/gWM4ufkOo3tRrKDGqkgiKK/img.png&quot; data-alt=&quot;플레이화면. 퇴근후 짧은시간 작업한거 치고는 나쁘지 않은것 같다...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yULm6/dJMcaiJfvfm/gWM4ufkOo3tRrKDGqkgiKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyULm6%2FdJMcaiJfvfm%2FgWM4ufkOo3tRrKDGqkgiKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;710&quot; height=&quot;1333&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;1333&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;플레이화면. 퇴근후 짧은시간 작업한거 치고는 나쁘지 않은것 같다...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;Godot 4.6 방치형 RPG 'Lucky7Quest' 개발기: AI 파트너와 함께한 A to Z 파이프라인 구축&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 사이드 프로젝트로 개발을 시작한 &lt;b&gt;Lucky7Quest&lt;/b&gt;는 Godot 4.6 엔진을 기반으로 한 슬롯머신 하이브리드 클릭커/방치형 RPG입니다. 플레이어는 몬스터와 전투를 벌이며, &lt;code&gt;3x3&lt;/code&gt; 멀티라인 슬롯머신을 돌려 공격, 회복, 쉴드, 코인 획득 등의 스킬 액션을 발동시키는 독특한 메커니즘을 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 개발 과정의 가장 큰 특징은 단순한 코드 작성을 넘어 &lt;b&gt;기획, UI 디자인, 그래픽 리소스 생성, 사운드 합성, VFX 연출까지 AI 에이전트(Antigravity)와 100% 페어 프로그래밍(혹은 오케스트레이션)을 통해 진행했다는 점&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 설치 단계부터 완성도 높은 빌드를 뽑아내기까지의 작업 과정을 블로그에 기록해 봅니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 프로젝트 초기 설정 및 기획 (Setup &amp;amp; Planning)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 PRD(제품 요구사항 정의서)를 기반으로 게임의 전반적인 구조를 세우는 작업부터 시작했습니다. AI가 PRD를 분석하여 구체적인 &lt;b&gt;Game Design &amp;amp; Architecture Document&lt;/b&gt;를 작성했고, 이를 바탕으로 GitHub 리포지토리 연동 및 CI/CD를 위한 Headless 테스트 환경(&lt;code&gt;test_runner.gd&lt;/code&gt;) 메커니즘을 구축했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 뼈대가 튼튼했기에 이후 진행된 모든 에셋 통합이나 UI 변경 작업에서도 테스트 코드를 돌려보며 안정성을 확보할 수 있었습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 모바일 최적화 및 UI 전면 리디자인 (Mobile UI Redesign)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임을 모바일 환경에 맞게 서비스하기 위해, 기존의 가로형 레이아웃을 &lt;b&gt;모바일 세로형(720x1280)&lt;/b&gt;으로 전면 개편했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Godot의 강력한 UI 시스템인 &lt;code&gt;MarginContainer&lt;/code&gt;, &lt;code&gt;VBoxContainer&lt;/code&gt;, &lt;code&gt;HBoxContainer&lt;/code&gt; 등을 활용하여 &lt;code&gt;HUD&lt;/code&gt;, &lt;code&gt;BattleZone&lt;/code&gt;, &lt;code&gt;SlotMachine&lt;/code&gt; 노드들을 일괄 재배치했습니다.&lt;/li&gt;
&lt;li&gt;레트로 RPG 게임의 감성을 살리기 위해 미니멀한 픽셀 아트 톤의 &lt;code&gt;StyleBoxFlat&lt;/code&gt; 기반 테마 컨테이너를 디자인했습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;트러블 슈팅&lt;/b&gt;: 중간에 &quot;Enemy ATK...&quot; 와 같은 긴 텍스트가 삽입되면서 UI 컨테이너가 가로 해상도 한계(720px)를 뚫고 짤리는(Clipping) 버그가 발생했습니다. AI와 함께 텍스트들이 초상화 사이를 밀어내지 않도록 별도의 하단 &lt;code&gt;FooterBox&lt;/code&gt;를 분리하고 자동 줄바꿈(Autowrap)을 적용해 깔끔하게 해결했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 픽셀 아트 에셋 생성 및 연동 (Pixel Art Assets)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UI가 완성된 후, 밋밋했던 임시 텍스처(&lt;code&gt;ColorRect&lt;/code&gt;)들을 실제 그래픽스로 교체할 차례였습니다.&lt;br /&gt;생성형 AI를 활용하여 총 13종의 픽셀 아트 에셋을 생성했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;슬롯 심볼 (6종)&lt;/b&gt;: 검(Sword), 지팡이(Wand), 방패(Shield), 하트(Heart), 코인(Coin), 숫자 7(Seven)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;캐릭터 및 몬스터 (7종)&lt;/b&gt;: 기사, 슬라임, 박쥐, 고블린 정찰병, 해골 병사, 불꽃 임프, 슬라임 킹&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배경을 투명하게 날리는 후처리를 진행한 뒤, 에셋들을 알맞은 폴더에 배치하자 Godot UI들이 자동으로 이를 인식하며 게임다운 비주얼을 폭발적으로 뽐내기 시작했습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 절차적 레트로 오디오 합성 (Procedural Audio)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인디 게임 개발의 큰 난관 중 하나인 사운드 역시 AI 파이프라인으로 해결했습니다.&lt;br /&gt;단순히 저작권 없는 음원을 찾는 대신, &lt;b&gt;파이썬(Python) 스크립트를 작성하여 8-bit 레트로 감성의 &lt;code&gt;.wav&lt;/code&gt; 사운드를 절차적(Procedural)으로 직접 합성&lt;/b&gt;했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Sine, Square, Noise 파형을 수학적으로 섞어 6종의 고유한 효과음(SFX: 클릭, 타격, 동전, 스핀, 업그레이드 등)을 생성했습니다.&lt;/li&gt;
&lt;li&gt;백그라운드 뮤직(BGM)용으로 C-Major 스케일의 아르페지오 칩튠(Chiptune) 사운드를 뽑아내어 적용했습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AudioManager&lt;/code&gt; 스크립트를 리팩토링하여 각 이벤트가 발생할 때마다 알맞은 레트로 사운드가 출력되도록 연동했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 시각적 쥬시함(Juiciness) 추가: VFX와 애니메이션&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적인 화면에 생동감(Game Feel)을 주기 위해 &lt;b&gt;Godot의 Tween 시스템&lt;/b&gt;을 극한으로 활용한 시각적 효과(VFX)를 추가했습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;슬롯머신 3D 플립 애니메이션&lt;/b&gt;: 슬롯이 회전하고 멈출 때 단순히 이미지가 바뀌는 것이 아니라, Y축 스케일 기반의 뒤집기 파티클 효과를 줘서 타격감을 높였습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;플로팅 데미지 텍스트&lt;/b&gt;: 데미지나 힐이 발생하면 캐릭터 머리 위로 숫자가 통통 튀어오르며 서서히 사라지는 연출을 추가했습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;피격 플래시 &amp;amp; 타격 모션&lt;/b&gt;: 공격을 받으면 초상화가 붉은색으로 깜빡이고(Hit Flash), 뒤로 살짝 밀려났다가 돌아오는(Attack Bump) 역동적인 움직임을 주었습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;글로벌 화면 흔들림(Screen Shake)&lt;/b&gt;: 크리티컬 히트가 터지거나 보스를 뚫고 다음 스테이지로 넘어갈 때, 화면 전체가 거칠게 흔들리도록 &lt;code&gt;Camera2D&lt;/code&gt;를 제어했습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;회고 및 마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하루 만에 빈 프로젝트 세팅에서 시작하여, UI-그래픽-사운드-VFX까지 모두 갖춘 &lt;b&gt;플레이어블 빌드&lt;/b&gt;를 만들어냈습니다. 가장 놀라운 점은 이 모든 작업이 개발자와 AI 간의 끊임없는 핑퐁(디자인 문서 설계 -&amp;gt; 에셋 생성 -&amp;gt; 코드 연동 -&amp;gt; 버그 수정)을 통해 막힘없이 이루어졌다는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소규모 인디 개발이나 1인 개발 환경에서, AI 오케스트레이션 파이프라인이 어떤 파급력을 가져올 수 있는지 확실하게 체감할 수 있었던 프로젝트입니다. 앞으로 남은 콘텐츠 확장 작업 역시 동일한 파이프라인을 거쳐 속도감 있게 전개해 나갈 예정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끝까지 읽어주셔서 감사합니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;codex,antigravity,claude code 3개의 에이전트를 전부 사용하였으며 &lt;b&gt;codex&lt;/b&gt;는 전체적은 플랜, 스크립트, &lt;b&gt;클루드&lt;/b&gt;는 플랜, &lt;b&gt;안티그래비티&lt;/b&gt;에서는 플랜, 스크립트, 이미지 및 사운드 생성 진행.&lt;/p&gt;</description>
      <category>AI/godot</category>
      <category>ai assistant</category>
      <category>Antigravity</category>
      <category>claude code</category>
      <category>codex</category>
      <category>game dev</category>
      <category>godot engine</category>
      <category>indie game</category>
      <category>Lucky7Quest</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/97</guid>
      <comments>https://blacknabis.tistory.com/97#entry97comment</comments>
      <pubDate>Wed, 11 Mar 2026 00:58:47 +0900</pubDate>
    </item>
    <item>
      <title>OpenAI Codex에 GPT-5.4 도입, 무엇이 달라졌나</title>
      <link>https://blacknabis.tistory.com/96</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI가 공식적으로 &lt;b&gt;ChatGPT, API, Codex에 GPT-5.4 모델을 도입했다&lt;/b&gt;고 발표했다. 이번 업데이트는 단순히 새로운 모델이 추가된 수준이 아니라, 코딩&amp;middot;추론&amp;middot;에이전트 워크플로를 하나의 프런티어 모델에 통합했다는 점에서 의미가 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 발표에 따르면 GPT-5.4는 전문 작업을 위한 가장 뛰어난 성능과 효율을 갖춘 프런티어 모델이다. 특히 Codex 관점에서 보면, GPT-5.4는 기존의 &lt;b&gt;GPT-5.3-Codex가 보여준 강력한 코딩 역량&lt;/b&gt;을 바탕으로 하면서도, 여기에 에이전트 기능과 컴퓨터 사용, 도구 활용 능력까지 더 확장한 모델로 소개된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;GPT-5.4는 Codex에서 왜 중요한가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex는 단순히 코드를 한 번 생성해주는 도구가 아니라, 코드를 작성하고 수정하고 디버깅하고 여러 단계를 거쳐 작업을 이어가는 &lt;b&gt;에이전트형 코딩 도구&lt;/b&gt;에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 공개된 GPT-5.4는 바로 이런 Codex의 특성과 잘 맞는다. OpenAI는 GPT-5.4가 추론, 코딩, 에이전트 기반 워크플로 영역의 기술 발전을 하나의 모델로 통합했다고 설명하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Codex에 GPT-5.4가 적용된다는 것은 단순한 모델 교체가 아니라, 더 복잡한 작업을 이해하고 장시간 문맥을 유지하며 도구를 더 효율적으로 선택하고 여러 단계의 개발 작업을 더 안정적으로 수행하는 방향으로 Codex가 진화하고 있다는 의미로 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;GPT-5.3-Codex의 코딩 역량을 메인라인 모델에 통합&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 발표에서 특히 눈에 띄는 문장은 GPT-5.4가 &lt;b&gt;gpt-5.3-codex의 프런티어 수준 코딩 역량을 통합한 첫 번째 메인라인 추론 모델&lt;/b&gt;이라는 설명이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 표현은 중요하다. 기존에는 Codex 계열의 강력한 코딩 성능이 별도 모델처럼 인식됐다면, 이제는 GPT-5.4가 그 성능을 메인라인 모델 안으로 끌어온 셈이기 때문이다. OpenAI가 모델명을 별도로 GPT-5.4로 정한 이유도 여기에 있다. 새로운 기능적 도약을 반영하고, Codex 사용 시 모델 선택을 더 단순하게 하기 위해서라고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Codex에서 주목할 만한 변화&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최신 수준의 컴퓨터 사용 기능 기본 제공&lt;/li&gt;
&lt;li&gt;최대 100만 토큰 컨텍스트 지원&lt;/li&gt;
&lt;li&gt;도구 검색 기능 추가로 더 효율적인 도구 선택&lt;/li&gt;
&lt;li&gt;SWE-Bench Pro 기준 GPT-5.3-Codex와 동등하거나 그 이상 성능&lt;/li&gt;
&lt;li&gt;Codex의 /fast 모드에서 최대 1.8배 속도 향상&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 가운데 특히 컴퓨터 사용 기능은 Codex 같은 에이전트형 개발 환경에서 중요한 변화다. OpenAI는 GPT-5.4가 사용자를 대신해 컴퓨터를 직접 조작하고, 여러 애플리케이션에 걸친 복잡한 워크플로를 수행하며, 장시간 작업을 계획하고 실행하고 검증할 수 있다고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 도구 검색 기능은 도구가 많은 환경에서도 필요한 정의만 불러와 사용할 수 있도록 바뀌어, 토큰 사용량을 줄이고 비용과 지연 시간을 낮추는 방향으로 개선되었다. Codex처럼 여러 도구와 연결되는 환경에서는 특히 의미 있는 변화다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;코딩 모델을 넘어 개발 에이전트로&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전의 코딩 모델이 잘 작성하는 것에 초점이 있었다면, 이번 GPT-5.4는 &lt;b&gt;잘 작성하는 것에 더해 실제로 실행하고 연결하는 것&lt;/b&gt;까지 포함하는 방향으로 확장됐다고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 GPT-5.4는 Codex를 더 강한 코딩 역량, 더 긴 문맥 유지, 더 나은 도구 선택, 더 빠른 반복 작업 처리, 그리고 더 복잡한 에이전트형 워크플로를 지원하는 방향으로 끌고 가고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI 공식 발표 기준으로 GPT-5.4는 이제 ChatGPT, API, Codex 전반에 걸쳐 제공된다. 특히 Codex에서는 GPT-5.3-Codex의 강력한 코딩 역량을 메인라인 모델에 통합하면서, 컴퓨터 사용 기능과 도구 검색, 긴 컨텍스트, 빠른 작업 흐름까지 함께 강화한 점이 핵심이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 이번 업데이트는 단순히 새 모델 출시라기보다, &lt;b&gt;Codex를 더 강력한 코딩 에이전트로 확장하는 업데이트&lt;/b&gt;로 보는 것이 더 적절해 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://openai.com/ko-KR/index/introducing-gpt-5-4/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 발표 보러 가기&lt;/a&gt;&lt;/p&gt;</description>
      <category>AI/Etc</category>
      <category>AI코딩</category>
      <category>codex</category>
      <category>GPT-5.4</category>
      <category>OpenAI</category>
      <category>SWE-Bench</category>
      <category>개발자동화</category>
      <category>생성형AI</category>
      <category>코딩에이전트</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/96</guid>
      <comments>https://blacknabis.tistory.com/96#entry96comment</comments>
      <pubDate>Tue, 10 Mar 2026 01:51:49 +0900</pubDate>
    </item>
    <item>
      <title>AI 시대, 개발자는 어떻게 살아남아야 할까 - 스탠포드 강의가 던진 질문</title>
      <link>https://blacknabis.tistory.com/95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 AI 관련 유튜브가 진짜 많이 올라온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;툴 소개, 자동화, 생산성, 미래 전망 같은 영상이 하루에도 엄청 쏟아지는데, 솔직히 보다 보면 다 비슷비슷하게 느껴질 때가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 오랜만에 처음부터 끝까지 집중해서 본 영상이 하나 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 본 영상은 EO Korea 채널의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lsquo;스탠포드에서 빅테크로 조용히 퍼지는 개발자 생존법, 대부분 모릅니다 | 미하일 에릭&amp;rsquo; 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 영상이 괜찮았던 이유는 단순히&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;AI가 대단하다&amp;rdquo;, &amp;ldquo;앞으로 세상이 바뀐다&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식의 뻔한 이야기만 하는 게 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 시대에 개발자가 실제로 어떻게 일하게 될지를 생각하게 만든다는 점이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영상 설명을 보면 스탠포드에서 진행된&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lsquo;The Modern Software Developer&amp;rsquo; 강의를 바탕으로 한 내용이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 그런지 그냥 자극적인 제목 장사 느낌보다는,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자의 역할이 앞으로 어떻게 달라질지에 대한 이야기에 더 가까웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보면서 가장 크게 든 생각은 이거였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 개발자는 단순히 코드를 잘 짜는 사람만으로는 부족하겠구나.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에는 직접 구현하고, 직접 디버깅하고, 직접 다 만드는 능력이 중요했다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제는 AI를 같이 쓰면서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제를 더 잘 정의하고&lt;/li&gt;
&lt;li&gt;원하는 결과를 더 정확하게 요청하고&lt;/li&gt;
&lt;li&gt;나온 결과를 빠르게 검토하고&lt;/li&gt;
&lt;li&gt;여러 도구를 연결해서 실제 결과물로 만드는 능력&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 게 훨씬 중요해지고 있다는 느낌이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 앞으로는 개발자가 단순한 코더라기보다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설계하고, 조율하고, 검증하는 역할에 더 가까워질 수도 있겠다는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영상에서 인상적이었던 부분 중 하나는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 방식이랑은 좀 다른 식으로 일하는 개발자들이 이미 등장하고 있다는 흐름이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전처럼 전부 손으로 하나하나 만드는 데 익숙한 사람보다,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI를 활용해서 더 빨리 실험하고, 더 빨리 수정하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 빨리 결과를 만드는 사람이 점점 유리해질 것 같다는 생각도 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 중요한 건&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI를 쓰냐 안 쓰냐보다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI를 자기 일에 얼마나 자연스럽게 붙여서 쓰느냐인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 각종 AI 유튜브가 많이 올라오는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오랜만에 처음부터 끝까지 정독한 유튜브였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발을 하고 있거나,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 시대에 개발자의 역할이 어떻게 바뀔지 궁금한 사람이라면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번쯤 직접 봐도 괜찮은 영상이라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영상 링크는 아래 남겨둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;https://youtu.be/qEF-eUaTq0Y?si=QEi8LSzdJ0t_053y&lt;/p&gt;</description>
      <category>AI/Etc</category>
      <category>AI</category>
      <category>AI시대</category>
      <category>개발자</category>
      <category>생산성</category>
      <category>소프트웨어개발</category>
      <category>스탠포드</category>
      <category>인공지능</category>
      <category>자동화</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/95</guid>
      <comments>https://blacknabis.tistory.com/95#entry95comment</comments>
      <pubDate>Tue, 10 Mar 2026 01:37:30 +0900</pubDate>
    </item>
    <item>
      <title>타워디펜스 만들기 - Stage 운영 구조를 갈아엎고, 월드맵 UI와 선택 체감을 다듬기(Codex)</title>
      <link>https://blacknabis.tistory.com/94</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;godot엔진을 공부하기 위해 (cluade code, antigravity를 사용하였기 때문에 codex로만 작업을 진행.)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 작업은 기능 하나를 추가하는 식의 단발성 수정이 아니었다.&lt;br /&gt;스테이지를 계속 늘려갈 수 있는 운영 구조를 만들고, 월드맵 UI를 정리하고, 실제 플레이 체감까지 손보는 쪽에 가까웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 2026-03-07부터 03-08까지 한 일은 크게 네 가지다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;StageId 단일 int&lt;/code&gt; 구조를 &lt;code&gt;WorldId + StageNumber + StageKey&lt;/code&gt;로 확장&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Draft / Test / Playable&lt;/code&gt; 상태 기반 운영 구조 도입&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Stage 2&lt;/code&gt;를 다시 설계하고 자동 오케스트레이션에 태움&lt;/li&gt;
&lt;li&gt;월드맵 popup과 선택 시스템을 실제 사용 기준으로 정리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;겉으로 보면 서로 다른 작업처럼 보이지만, 결국 하나의 방향으로 연결돼 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;새 맵을 계속 추가해도, 운영 데이터와 작업 중 데이터를 섞지 않고, UI와 테스트 동선까지 버티는 구조를 만들자.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Stage 1 only 구조를 버린 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에는 사실상 &lt;code&gt;Stage 1&lt;/code&gt;만 운영 기준이었다.&lt;br /&gt;문제는 &lt;code&gt;Stage 2&lt;/code&gt;부터는 테스트 잔재 데이터와 작업 중 자산이 섞여 있어서, 맵을 늘릴수록 기준이 흐려진다는 점이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전 방식의 문제는 명확했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;StageId&lt;/code&gt; 단일 int에 모든 의미가 몰려 있었다&lt;/li&gt;
&lt;li&gt;운영 스테이지와 작업 중 스테이지를 코드가 구분하지 못했다&lt;/li&gt;
&lt;li&gt;월드맵, 저장, 결과 복귀, 스킬 조건, 보스 이벤트가 모두 같은 키 체계에 기대고 있었다&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Stage 2&lt;/code&gt;를 테스트하려고 상태를 억지로 운영 상태처럼 다뤄야 했다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이번에는 아예 구조를 바꿨다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public readonly struct StageKey
{
    public readonly int WorldId;
    public readonly int StageNumber;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 &lt;code&gt;StageContentStatus&lt;/code&gt;를 붙였다.&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;public enum StageContentStatus
{
    Draft,
    Test,
    Playable
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 단순하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Draft&lt;/code&gt;: 제작 중&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Test&lt;/code&gt;: 내부 테스트용&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Playable&lt;/code&gt;: 실제 운영 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 나누고 나니 월드맵, 게임씬, 저장, 에디터 도구가 같은 기준으로 움직이기 시작했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Stage 2를 다시 &amp;ldquo;쓸 수 있는 초안&amp;rdquo;으로 만든 과정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Stage 2&lt;/code&gt;는 맵 경로와 타워 슬롯은 이미 들어가 있었지만, 웨이브 데이터는 예전 테스트 흔적이 많이 남아 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 확인한 문제는 이런 식이었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 맵은 단일 경로인데 웨이브는 존재하지 않는 &lt;code&gt;PathId&lt;/code&gt;를 참조&lt;/li&gt;
&lt;li&gt;테스트용 낮은 시작 자원과 임시 조합이 섞여 있음&lt;/li&gt;
&lt;li&gt;실제 스테이지 초안이라기보다 실험 데이터에 가까움&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 목표를 먼저 잠갔다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;역할: &lt;code&gt;학습 확장형&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;상태: &lt;code&gt;Draft&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;목표: &lt;code&gt;초심자 1회 클리어 가능&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;구조: &lt;code&gt;7 waves&lt;/code&gt;, &lt;code&gt;PathId 0 only&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;규칙: &lt;code&gt;Wave 1~6 최소 8마리&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 초안은 자동 회귀 기준으로는 파손이 없었다.&lt;br /&gt;문제는 실제 플레이였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 해보니 &lt;b&gt;초반 아처 3개를 전방에 깔면 이후 추가 건설 없이도 클리어&lt;/b&gt;가 가능했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 두 가지를 의미했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시작 골드가 많다&lt;/li&gt;
&lt;li&gt;중후반 압박이 약하다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 2차 조정에서는 방향을 분명하게 잡았다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;InitialGold: 360 -&amp;gt; 280&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Wave 4~7&lt;/code&gt; 물량 상향&lt;/li&gt;
&lt;li&gt;초반 &lt;code&gt;Wave 1~3&lt;/code&gt;는 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 초안은 이렇게 정리했다.&lt;/p&gt;
&lt;pre class=&quot;llvm&quot;&gt;&lt;code&gt;InitialGold = 280
InitialLives = 75

W1: Goblin x8
W2: Goblin x8 + Scout x2
W3: Goblin x8 + Scout x2
W4: Goblin x10 + Orc x4
W5: Scout x8 + Goblin x6
W6: Goblin x10 + Orc x5
W7: Goblin x10 + Orc x4 + Shaman x2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 &lt;code&gt;Playable&lt;/code&gt;은 아니다.&lt;br /&gt;하지만 최소한 &amp;ldquo;작업 중 Draft 자산&amp;rdquo;이 아니라, 실제로 검증 가능한 초안 상태까지는 끌어올렸다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 테스트 동선을 바꾼 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조를 엄격하게 만들면 보통 테스트가 불편해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 &lt;code&gt;Draft/Test&lt;/code&gt;를 운영 경로에서 막아버리면, 개발자는 새 스테이지를 확인할 때마다 상태를 바꾸거나 별도 메뉴를 타야 한다.&lt;br /&gt;이건 스테이지가 하나둘일 때나 참을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 운영 원칙과 개발자 동선을 분리했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;운영 기준: &lt;code&gt;Playable&lt;/code&gt;만 시작 가능&lt;/li&gt;
&lt;li&gt;개발자 기준: 에디터/개발 빌드에서는 &lt;code&gt;Draft/Test&lt;/code&gt;도 월드맵에서 직접 시작 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해두니 두 마리 토끼를 같이 잡을 수 있었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;운영 구조는 안 무너진다&lt;/li&gt;
&lt;li&gt;개발자는 월드맵에서 바로 새 스테이지를 눌러 테스트할 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 작업은 단순 편의 기능이 아니라, &lt;b&gt;콘텐츠 생산 속도를 떨어뜨리지 않기 위한 안전장치&lt;/b&gt;에 가까웠다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 월드맵 popup은 결국 프리팹 기준으로 정리했다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 구간에서 의외로 시간을 많이 쓴 부분이 월드맵 popup이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 두 가지였다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 한글 깨짐&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 폰트 문제처럼 보였는데, 실제 원인은 코드에서 런타임으로 만드는 문자열 일부가 깨져 있던 쪽이었다.&lt;br /&gt;&lt;code&gt;총 별&lt;/code&gt;, &lt;code&gt;사용 별&lt;/code&gt;, &lt;code&gt;남은 별&lt;/code&gt;, &lt;code&gt;강화&lt;/code&gt;, &lt;code&gt;별이 부족합니다&lt;/code&gt; 같은 문구를 정상 한글로 정리하면서 바로 잡았다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2 유지보수 방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 큰 문제는 UI 레이아웃을 코드가 런타임에 자꾸 다시 만들고 밀어넣는 구조였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조는 처음엔 빠르지만 결국 이런 문제가 생긴다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프리팹을 수정해도 코드가 덮어쓴다&lt;/li&gt;
&lt;li&gt;어떤 값이 실제 기준인지 파악하기 어렵다&lt;/li&gt;
&lt;li&gt;참조가 깨져도 fallback UI가 조용히 뜬다&lt;/li&gt;
&lt;li&gt;버그가 &amp;ldquo;즉시 드러나지 않고&amp;rdquo; 나중에 이상한 형태로 나타난다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 방향을 바꿨다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;SkillTreeUI&lt;/code&gt;, &lt;code&gt;HeroSelectionUI&lt;/code&gt;를 프리팹 기준 구조로 전환&lt;/li&gt;
&lt;li&gt;빌더 메뉴로 프리팹 재생성 가능하게 정리&lt;/li&gt;
&lt;li&gt;최종적으로 런타임 fallback 제거&lt;/li&gt;
&lt;li&gt;참조 누락 시 &lt;code&gt;Debug.LogError + 중단&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제는 프리팹이 기준이고, 문제가 있으면 바로 드러난다.&lt;br /&gt;유지보수 관점에서는 이쪽이 훨씬 낫다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 선택 체감: &amp;ldquo;보이는 걸 눌렀는데 왜 안 눌리지?&amp;rdquo;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플레이하다 보면 가장 거슬리는 버그 중 하나가 이런 종류다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;오브젝트 이미지를 눌렀는데 선택이 안 된다.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인을 보면 대개 단순하다.&lt;br /&gt;보이는 스프라이트를 누르는 게 아니라, 더 작은 충돌 범위나 중심점 거리를 누르고 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 선택 판정을 &lt;code&gt;SpriteRenderer.bounds&lt;/code&gt; 기준으로 보강했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;영웅&lt;/li&gt;
&lt;li&gt;적&lt;/li&gt;
&lt;li&gt;병사&lt;/li&gt;
&lt;li&gt;타워&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두 &amp;ldquo;실제로 보이는 이미지&amp;rdquo;에 더 가깝게 선택되도록 만든 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 여기서 한 걸음 더 가서, &lt;b&gt;타워를 선택하면 공격 범위가 따로 보이도록&lt;/b&gt; 했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선택 원과 범위 원은 분리&lt;/li&gt;
&lt;li&gt;일반 타워는 공격 범위&lt;/li&gt;
&lt;li&gt;배럭은 &lt;code&gt;RallyRange&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;선택 해제 시 즉시 숨김&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 범위용 스프라이트 로드가 안 되어 Hierarchy에는 있는데 게임 화면에는 안 보이는 상태가 있었다.&lt;br /&gt;원인은 새 이미지의 임포트 설정이 &lt;code&gt;Multiple Sprite&lt;/code&gt;였던 쪽이었고, &lt;code&gt;Single&lt;/code&gt;로 정리하면서 해결했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 작은 디테일 같지만 체감에는 꽤 중요했다.&lt;br /&gt;&amp;ldquo;선택됐다&amp;rdquo;와 &amp;ldquo;어디까지 닿는지 안다&amp;rdquo;는 플레이 감각이 완전히 다르기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 오케스트레이션을 같이 굴린 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 작업에서 개인적으로 가장 중요하게 본 건, 기능을 넣는 것보다 &lt;b&gt;회귀를 같이 잠그는 것&lt;/b&gt;이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조 변경, UI 변경, 밸런스 변경은 서로 영향을 많이 준다.&lt;br /&gt;이럴 때 수동 테스트만 믿으면, 당장은 보여도 나중에 어디서 깨졌는지 찾기 어려워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 가능한 건 오케스트레이션에 같이 태웠다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Selection UI smoke&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Stage1 Pipeline&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Stage2 Draft Pipeline&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WorldMap Meta Popup Regression&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BattleSim Economy Checklist&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 포커스를 잃으면 회귀가 멈추는 문제도 같이 잡았다.&lt;br /&gt;프로젝트가 기본적으로 &lt;code&gt;runInBackground: 0&lt;/code&gt;이라서, 회귀 실행 동안에는 &lt;code&gt;Application.runInBackground = true&lt;/code&gt;를 강제로 켜고 끝나면 복원하도록 보강했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 이번 이틀 작업은 &amp;ldquo;기능 추가&amp;rdquo;라기보다, &lt;b&gt;작업 흐름 전체를 덜 깨지게 만드는 쪽&lt;/b&gt;에 더 가까웠다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 이번 작업에서 얻은 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 구간에서 특히 확실해진 건 세 가지다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 운영 구조와 개발자 편의는 분리해야 한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영 기준을 느슨하게 하면 결국 데이터가 섞인다.&lt;br /&gt;반대로 너무 엄격하게 막으면 개발이 느려진다.&lt;br /&gt;&lt;code&gt;Draft/Test/Playable&lt;/code&gt; + 개발자 전용 월드맵 시작 게이트 조합은 이 균형을 잘 잡아줬다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. UI는 결국 프리팹이 기준이어야 한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 fallback은 프로토타입 단계에서는 빠르지만, 운영 단계에서는 문제를 숨기는 쪽으로 작동한다.&lt;br /&gt;지금처럼 popup 구조가 정리된 뒤에는 fail-fast가 맞다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 자동 회귀는 기능이 아니라 작업 속도다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택 판정, popup, 웨이브 초안, 월드맵 진입처럼 서로 얽힌 변경이 많아질수록&lt;br /&gt;&amp;ldquo;한 번에 돌려보고 상태를 잠글 수 있는 흐름&amp;rdquo; 자체가 생산성이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 다음 작업&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 우선순위는 명확하다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;Stage 2&lt;/code&gt; 실제 플레이를 3~5회 더 돌린다&lt;/li&gt;
&lt;li&gt;&lt;code&gt;3아처 방치 클리어&lt;/code&gt;가 완전히 깨졌는지 확인한다&lt;/li&gt;
&lt;li&gt;필요하면 &lt;code&gt;Wave 5~7&lt;/code&gt;만 추가 미세 조정한다&lt;/li&gt;
&lt;li&gt;이후 &lt;code&gt;Stage 2&lt;/code&gt;를 &lt;code&gt;Test&lt;/code&gt;로 올릴지 판단한다&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 선택/범위 표시 쪽은 이제 체감이 안정적이면 큰 구조 변경보다는 시각 튜닝 단계로 넘어가면 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 작업은 눈에 확 들어오는 새 기능보다, 프로젝트가 앞으로 더 많은 스테이지와 UI 변경을 감당할 수 있게 바닥을 다시 까는 작업에 가까웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 좋았던 건, 코드를 바꾼 뒤 끝내지 않고&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자산&lt;/li&gt;
&lt;li&gt;프리팹&lt;/li&gt;
&lt;li&gt;문서&lt;/li&gt;
&lt;li&gt;자동 회귀&lt;/li&gt;
&lt;li&gt;실제 플레이 피드백&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 같이 묶어서 잠그는 흐름이 조금씩 자리를 잡기 시작했다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxx48u/dJMcafZ21Et/4AHDuix8oT5UOUsD8EZsF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxx48u/dJMcafZ21Et/4AHDuix8oT5UOUsD8EZsF0/img.png&quot; data-alt=&quot;지난 블로그의 사진과 외관상 큰차이는 없지만 타워 공격 범위가 잘보인다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxx48u/dJMcafZ21Et/4AHDuix8oT5UOUsD8EZsF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbxx48u%2FdJMcafZ21Et%2F4AHDuix8oT5UOUsD8EZsF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;960&quot; height=&quot;522&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;지난 블로그의 사진과 외관상 큰차이는 없지만 타워 공격 범위가 잘보인다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>AI/Unity</category>
      <category>codex</category>
      <category>Devlog</category>
      <category>Unity</category>
      <category>타워디펜스</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/94</guid>
      <comments>https://blacknabis.tistory.com/94#entry94comment</comments>
      <pubDate>Sun, 8 Mar 2026 01:19:33 +0900</pubDate>
    </item>
    <item>
      <title>AI &amp;quot;진행해&amp;quot; vs &amp;quot;해줘&amp;quot;</title>
      <link>https://blacknabis.tistory.com/93</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;최근 제미나이(Gemini), 클로드(Claude) 같은 강력한 LLM과 코딩 어시스턴트들을 동시에 활용하면서, 복잡한 업무를 자동화하는 '파이프라인 오케스트레이션(Pipeline Orchestration)'에 대한 관심이 뜨겁습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 AI에게 질문을 던지고 텍스트 답변을 받는 것을 넘어, 여러 단계의 툴과 프로세스를 매끄럽게 연결하는 것이 핵심입니다. 예를 들어, LLM으로 퀘스트 시나리오를 작성하고, 그 결과물을 ComfyUI 같은 이미지 생성 API로 넘겨 일러스트를 뽑아낸 뒤, 유니티(Unity) 프로젝트 폴더에 자동 배치하는 일련의 흐름을 통제하는 것이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 AI에게 이런 복잡한 파이프라인 설계를 맡길 때, 어떤 프롬프트를 써야 가장 완벽한 결과물을 얻을 수 있을까요?&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 프롬프트의 오해와 진실: &quot;진행해&quot; vs &quot;해줘&quot;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종종 커뮤니티를 보면 &quot;AI에게 지시할 때 '진행해'라고 하면 프로세스를 짜주고, '해줘'라고 하면 코드를 짜준다&quot;는 식의 팁이 돌아다닙니다. 하지만 이는 최신 LLM의 작동 방식을 오해한 과잉 일반화입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대의 고성능 AI 모델들은 한국어 어미의 미세한 뉘앙스 차이보다는 전체 문맥과 명시적인 요구사항에 집중합니다. 어미를 어떻게 끝내든, 요구하는 바가 모호하다면 AI는 적당히 일반적인 답변을 뭉뚱그려 내놓을 뿐입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그렇다면 두 어미 간에 차이가 아예 없을까요? 진짜 차이는 결과물의 '포맷'이 아니라, '부탁'과 '명령'이라는 톤(Tone)에서 만들어집니다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&quot;~해줘&quot; (부탁조의 친절함):&lt;/b&gt; 대화형 AI의 특성상 친근한 부탁조로 지시하면, AI 역시 '친절한 어시스턴트'의 역할에 몰입합니다. 그 결과, 파이프라인 코드를 짜주면서도 앞뒤로 불필요한 인사말이나 코드 작동 원리에 대한 장황한 부연 설명을 덧붙이는 경향이 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&quot;~진행해&quot; (명령조의 간결함):&lt;/b&gt; 반면 감정을 뺀 명확한 명령조로 지시하면, AI는 서론과 결론을 생략하고 요구받은 핵심 결과물만 간결하고 건조하게 출력할 확률이 높아집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 파이프라인 코드나 YAML 설정 파일처럼 군더더기 없는 텍스트만 빠르게 복사해서 실무에 적용해야 할 때는, 정중한 부탁보다는 단호하고 명확한 명령조를 사용하는 것이 오히려 더 효율적일 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 실무에 쓰는 AI 툴, 각자의 강점 활용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하는 AI 툴의 특성을 이해하고 파이프라인의 각 단계에 맞게 활용하면 효율을 극대화할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클로드(Claude): 깐깐한 구조 설계자 지시사항 준수율이 매우 높습니다. 복잡한 워크플로우를 JSON이나 YAML 같은 특정 데이터 포맷으로 정리해 달라고 명확히 요구했을 때, 가장 에러가 적고 논리적인 구조를 잘 뽑아냅니다.&lt;/li&gt;
&lt;li&gt;제미나이(Gemini): 큰 그림을 그리는 아키텍트 방대한 컨텍스트를 파악하는 데 능합니다. 여러 툴이 얽힌 복잡한 파이프라인의 전체적인 데이터 흐름이나 아키텍처 다이어그램의 초안을 기획할 때 유용합니다.&lt;/li&gt;
&lt;li&gt;코드 어시스턴트 (Copilot, Cursor 등): 즉시 투입 가능한 실무자 과거의 코덱스(Codex) 모델처럼 코드 생성에 특화된 환경에서는, 설계된 아키텍처를 바탕으로 Airflow DAG나 Python 실행 스크립트 등 실제 돌아가는 코드를 IDE 내에서 바로 짜는 데 최적화되어 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 성공적인 파이프라인 설계를 위한 프롬프트 공식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI에게 완벽한 파이프라인 오케스트레이션을 지시하려면 마법의 단어를 찾을 것이 아니라, [역할 부여] + [구체적 태스크] + [출력 형식] 이 세 가지를 프롬프트에 조립해야 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[잘 작성된 프롬프트 예시] &quot;너는 게임 개발 자동화 파이프라인 엔지니어야. (역할) LLM이 생성한 시나리오 데이터를 바탕으로 ComfyUI API를 호출해 에셋을 생성하고, 이를 Unity 프로젝트 폴더에 포맷에 맞춰 자동 저장하는 파이프라인을 설계해. (구체적 태스크) 전체 워크플로우의 실행 로직을 Airflow 기반의 Python 코드로 작성하고, 필요한 환경 설정은 YAML 포맷으로만 간결하게 출력해.&quot; (출력 형식 + 명령조)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 목적과 포맷, 그리고 어조를 정확히 짚어주면 AI는 군더더기 없이 곧바로 실무에 적용 가능한 수준의 오케스트레이션 코드를 반환합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리하며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이프라인 오케스트레이션 구축의 핵심은 AI를 '마법사'가 아닌 '실무자'로 대하는 데 있습니다. 원하는 결과물의 형태와 사용할 도구를 명확히 지정하는 프롬프트 엔지니어링을 통해, 여러분의 개발 워크플로우를 획기적으로 자동화해 보시기 바랍니다. 그리고 작업에 있어서 국어에 대해 더욱 공부하고 잘 써야되겠다고 많이 느꼈습니다. 명령조와 부탁조의 차이가 다르다고 주변에 사람들에게 많이 들었는데&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI</category>
      <category>AI</category>
      <category>업무자동화</category>
      <category>워크플로우</category>
      <category>제미나이</category>
      <category>코덱스</category>
      <category>클로드</category>
      <category>파이프라인오케스트레이션</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/93</guid>
      <comments>https://blacknabis.tistory.com/93#entry93comment</comments>
      <pubDate>Sun, 8 Mar 2026 01:13:51 +0900</pubDate>
    </item>
    <item>
      <title>고도엔진으로 AI 사용하여 게임만들기</title>
      <link>https://blacknabis.tistory.com/92</link>
      <description>&lt;h1&gt;AI와 고도 엔진(Godot)으로 만드는 하드코어 2D 플랫포머 제작기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요! 오늘은 AI 개발 에이전트와 &lt;b&gt;Godot 4.6 Engine&lt;/b&gt;을 활용하여 스팀의 인기 게임인 '스마일모(Smilemo)' 느낌이 나는 &lt;b&gt;2D 하드코어 런 게임&lt;/b&gt;을 어떻게 기획하고 구현했는지 공유해 보려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혼자서 모든 것을 코딩하는 대신, AI 에이전트와 역할을 나누어 &lt;b&gt;&quot;파이프라인 오케스트레이션(Pipeline Orchestration)&quot;&lt;/b&gt; 이라는 방식으로 병렬 개발을 진행해 본 아주 흥미로운 경험이었습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 프로젝트 기획 (PRD 작성)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 한 일은 &lt;b&gt;AI와 함께 개발 기획서(PRD)를 작성&lt;/b&gt;하는 것이었습니다. 게임의 목표와 방향성을 명확히 해야 AI 에이전트가 코드를 짤 때 방황하지 않기 때문입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;핵심 메커니즘 도출&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 플랫포머 게임과 이 게임의 가장 큰 차이점은 &lt;b&gt;체력(HP) 시스템의 부재&lt;/b&gt;와 &lt;b&gt;바운스 기믹&lt;/b&gt;입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;HP 없음:&lt;/b&gt; 적(바이러스)에 닿으면 죽거나 체력이 다는 것이 아니라, 닿은 반대 방향으로 &lt;b&gt;강력하게 튕겨납니다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;연쇄 바운스:&lt;/b&gt; 잘못 튕기면 구덩이에 빠지거나, 다른 바이러스에 연속으로 부딪혀서 스테이지 맨 처음('태초마을')으로 날아갈 우려가 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;조작 타격감 보강:&lt;/b&gt; Z키(보유 시 달리기), 벽타기(Wall Slide/Jump), 그리고 슬라이딩 기능을 넣어 극한의 피지컬 컨트롤 요소가 필요하게 설계했습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;수집 요소:&lt;/b&gt; 스테이지에 흩어진 '백신 캡슐'을 &lt;b&gt;100% 모두 수집&lt;/b&gt;해야만 다음 스테이지로 가는 골(Goal)이 열립니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 이 내용을 &lt;code&gt;prd.md&lt;/code&gt; 파일에 정리하고, 지속적으로 업데이트하며 개발의 나침반으로 사용했습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 개발 프레임워크: 파이프라인 오케스트레이션&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 지시자를 내리면, AI 에이전트는 작업을 한 번에 순차적으로 처리하는 대신 &lt;b&gt;병렬 파이프라인(Parallel Pipelines)&lt;/b&gt; 으로 쪼개서 작업을 기획하고 쳐냈습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 에이전트가 코드를 짤 때:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Pipeline A&lt;/b&gt;: 물리 엔진 및 캐릭터 조작(&lt;code&gt;player.gd&lt;/code&gt; 수정)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pipeline B&lt;/b&gt;: 스테이지 기믹 및 렌더링 세팅(&lt;code&gt;scenes/level.tscn&lt;/code&gt; 구성)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pipeline C&lt;/b&gt;: 개발 일지 갱신(&lt;code&gt;devlog.md&lt;/code&gt;, &lt;code&gt;task.md&lt;/code&gt; 현행화)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 오케스트레이션 방식을 요청하자, 에이전트가 로직 구현과 문서화를 동시에 진행하면서 놀라울 정도로 빠르고 체계적으로 아키텍처를 잡고 기능을 구현해주었습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 고도 엔진(Godot Engine)에서의 구현 포인트&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) CharacterBody2D를 활용한 커스텀 물리 엔진&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고도의 내장된 물리 노드들을 단순히 쓰는 것을 넘어 지상/공중 쿨다운, 가변 점프 높이, 벽 반동 계산 등을 스크립트로 직접 통제했습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;# 벽 반동(Wall Jump) 처리 예시
if is_on_wall() and Input.is_action_just_pressed(&quot;jump&quot;):
    velocity.y = WALL_JUMP_FORCE_Y
    velocity.x = wall_direction * WALL_JUMP_FORCE_X&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 타격감 피드백 (게임 필)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이러스에 맞아 튕겨 나갈 때의 불쾌함을 줄이고 시각적 만족감을 주기 위해 다음 기능들을 조합했습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;역경직 (Hit Pause)&lt;/b&gt;: &lt;code&gt;Engine.time_scale&lt;/code&gt;을 아주 잠깐 0.1로 낮추어 슬로모션 효과 유발.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;카메라 셰이크 (Camera Shake)&lt;/b&gt;: 카메라 스크립트에 &lt;code&gt;decay&lt;/code&gt; 함수를 적용해 튕겨나가는 순간 화면을 진동시킴.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;파티클 (CPUParticles2D)&lt;/b&gt;: 충돌 지점에서 파편이 튀는 시각 효과 발동.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) 에셋 자동 생성과 통합 (AI Image Generation)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가개발 단계에서는 단순히 Gothi의 기본 도형인 폴리곤(&lt;code&gt;Polygon2D&lt;/code&gt;) 노드로 네모낳게 만들어서 테스트했습니다. 하지만 어느 정도 플레이가 쾌적해진 후, 에이전트에게 &lt;b&gt;&quot;직접 API를 호출해 스프라이트 리소스를 그려서 적용해줘&quot;&lt;/b&gt; 라고 요청했습니다.&lt;br /&gt;AI가 플레이어( ), 바이러스( ), 아이템 캡슐( ) 도트 그래픽을 생성해서 스스로 Godot 프로젝트 &lt;code&gt;assets&lt;/code&gt; 폴더에 넣고 &lt;code&gt;.tscn&lt;/code&gt; 파일 코드를 &lt;code&gt;Sprite2D&lt;/code&gt;로 치환까지 전부 해냈습니다!&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. AI 페어 프로그래밍 개발 소감&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 크게 느낀 점은 &lt;b&gt;&quot;개발 일지(&lt;code&gt;devlog.md&lt;/code&gt;)와 태스크 리스트(&lt;code&gt;task.md&lt;/code&gt;)의 위력&quot;&lt;/b&gt; 입니다.&lt;br /&gt;보통 개인 프로젝트 수준에서는 문서화를 귀찮아서 잘 안 하게 되는데, AI가 코딩을 하면서 동시에 마크다운 문서를 업데이트하게 하니 다음과 같은 장점이 있었습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;맥락 유지:&lt;/b&gt; 채팅 컨텍스트가 길어져서 AI가 깜빡하더라도, &lt;code&gt;prd.md&lt;/code&gt;나 &lt;code&gt;task.md&lt;/code&gt;를 다시 열어보게 하면 즉시 원래의 핵심 기획 의도로 돌아옵니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빠른 디버깅:&lt;/b&gt; 어디서 무얼 고치다가 오류가 났는지 서로 트래킹하기가 굉장히 쉬웠습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;오케스트레이션 효율:&lt;/b&gt; 어떤 모듈 구조에 어떤 기능이 담당되어 있는지 텍스트 문서로 정리되어 있으니, A모듈 작업과 B기능 수정을 병렬로 완전히 분리해서 요청하기 좋았습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1271&quot; data-origin-height=&quot;717&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKTor6/dJMcad2cnOK/hr62SCeG5UqifXQCa7vGIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKTor6/dJMcad2cnOK/hr62SCeG5UqifXQCa7vGIk/img.png&quot; data-alt=&quot;플레이화면 아직 이미지는 제대로 처리되지 않는다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKTor6/dJMcad2cnOK/hr62SCeG5UqifXQCa7vGIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKTor6%2FdJMcad2cnOK%2Fhr62SCeG5UqifXQCa7vGIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1271&quot; height=&quot;717&quot; data-origin-width=&quot;1271&quot; data-origin-height=&quot;717&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;플레이화면 아직 이미지는 제대로 처리되지 않는다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;후기.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유니티로 개발하는것보다 하이퍼캐주얼쪽으로는 AI친화적이라 개발 속도 및 아웃풋이 생각보다 굉장히 좋네요. 고도엔진의 스크립트를 아직 보지 않았기 때문에 100% 바이브코딩으로 진행하였습니다.&lt;/b&gt;&lt;/p&gt;</description>
      <category>AI/godot</category>
      <category>Antigravity</category>
      <category>claude code</category>
      <category>Devlog</category>
      <category>GEMINI</category>
      <category>Godot</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/92</guid>
      <comments>https://blacknabis.tistory.com/92#entry92comment</comments>
      <pubDate>Sun, 8 Mar 2026 01:06:29 +0900</pubDate>
    </item>
    <item>
      <title>AI 에이전트 파이프라인 오케스트레이션</title>
      <link>https://blacknabis.tistory.com/91</link>
      <description>&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 오케스트레이션이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트 오케스트레이션은 &lt;b&gt;여러 AI 에이전트를 조율하여 복잡한 워크플로우를 수행&lt;/b&gt;하도록 관리하는 기술이다. 단일 LLM 호출로 해결하기 어려운 다단계&amp;middot;다차원 문제를 &lt;b&gt;전문화된 에이전트 팀&lt;/b&gt;이 협력하여 처리한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 필요한가?&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&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;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;도구 연동 복잡&lt;/td&gt;
&lt;td&gt;에이전트별 도구 연동 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 인사이트&lt;/b&gt;: 2025~2026년 업계의 화두는 &quot;생성형 AI(응답하는 시스템)&quot;에서 &lt;b&gt;&quot;에이전틱 AI(행동하고, 조율하고, 실행하는 시스템)&quot;&lt;/b&gt;로의 전환이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 핵심 오케스트레이션 패턴 8가지&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 순차 파이프라인 (Sequential Pipeline)&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;[에이전트 A] &amp;rarr; [에이전트 B] &amp;rarr; [에이전트 C] &amp;rarr; 결과&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 에이전트의 출력이 다음 에이전트의 입력으로 전달&lt;/li&gt;
&lt;li&gt;&lt;b&gt;적합&lt;/b&gt;: 문서 처리, 데이터 변환, 대출 승인 같은 선형 프로세스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장점&lt;/b&gt;: 단순하고 디버깅이 쉬움&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단점&lt;/b&gt;: 병목 구간이 전체 성능 결정&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 코디네이터/디스패처 (Coordinator / Supervisor)&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;        ┌&amp;rarr; [전문 에이전트 A] &amp;rarr;┐
[오케스트레이터] &amp;rarr; [전문 에이전트 B] &amp;rarr; [오케스트레이터] &amp;rarr; 최종 결과
        └&amp;rarr; [전문 에이전트 C] &amp;rarr;┘&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중앙 오케스트레이터가 사용자 요청을 분석하여 적절한 전문 에이전트에게 위임&lt;/li&gt;
&lt;li&gt;작업 분배, 컨텍스트 공유, 결과 집계, 재시도, 오류 처리를 모두 관리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;적합&lt;/b&gt;: 고객 서비스, 복합 질의 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.3 병렬 팬아웃/개더 (Parallel Fan-Out / Gather)&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;        ┌&amp;rarr; [에이전트 A] &amp;rarr;┐
[분배기] &amp;rarr; [에이전트 B] &amp;rarr; [집계기] &amp;rarr; 결과
        └&amp;rarr; [에이전트 C] &amp;rarr;┘&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상호 의존성 없는 작업을 &lt;b&gt;동시 실행&lt;/b&gt;하여 처리 속도 극대화&lt;/li&gt;
&lt;li&gt;MapReduce 스타일 &amp;mdash; 분할 &amp;rarr; 병렬 처리 &amp;rarr; 합산&lt;/li&gt;
&lt;li&gt;&lt;b&gt;적합&lt;/b&gt;: 대량 데이터 분석, 다국어 번역, 다중 소스 검색&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.4 계층적 분해 (Hierarchical Decomposition)&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;[상위 에이전트]
  ├&amp;rarr; [중간 관리 에이전트]
  │     ├&amp;rarr; [작업자 에이전트 A]
  │     └&amp;rarr; [작업자 에이전트 B]
  └&amp;rarr; [중간 관리 에이전트]
        └&amp;rarr; [작업자 에이전트 C]&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Planner &amp;rarr; Manager &amp;rarr; Worker 계층 구조&lt;/li&gt;
&lt;li&gt;복잡한 목표를 재귀적으로 분해하여 전문 하위 에이전트에 위임&lt;/li&gt;
&lt;li&gt;&lt;b&gt;적합&lt;/b&gt;: 대규모 프로젝트 관리, 복잡한 소프트웨어 개발&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.5 제너레이터-크리틱 (Generator &amp;amp; Critic)&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;[생성 에이전트] ⇄ [검증 에이전트]
         (통과할 때까지 반복)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성(Generator)과 검증(Critic)의 역할 분리&lt;/li&gt;
&lt;li&gt;품질 기준을 통과할 때까지 반복 수정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;적합&lt;/b&gt;: 코드 리뷰, 콘텐츠 생성, 보고서 작성&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.6 반복적 개선 (Plan-Act-Reflect)&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;[계획] &amp;rarr; [실행] &amp;rarr; [반성] &amp;rarr; (조건 충족까지 반복)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;계획 수립 &amp;rarr; 도구 사용하여 실행 &amp;rarr; 결과 평가 &amp;rarr; 계획 수정&lt;/li&gt;
&lt;li&gt;자기 교정(Self-correction)과 지속적 적응에 초점&lt;/li&gt;
&lt;li&gt;&lt;b&gt;적합&lt;/b&gt;: 연구 자동화, 복잡한 문제 해결, 코드 디버깅&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.7 이벤트 기반 트리거 (Event-Driven)&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;[이벤트 발생] &amp;rarr; [에이전트 활성화] &amp;rarr; [처리] &amp;rarr; [대기 복귀]&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;에이전트가 특정 이벤트를 구독하고, 발생 시에만 활성화&lt;/li&gt;
&lt;li&gt;컴퓨팅 자원 절약 + 예측 가능성 향상&lt;/li&gt;
&lt;li&gt;&lt;b&gt;적합&lt;/b&gt;: 모니터링, 알림 시스템, CI/CD 파이프라인&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.8 인간 개입 (Human-in-the-Loop)&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;[에이전트 처리] &amp;rarr; [위험도 판단] &amp;rarr; [인간 승인] &amp;rarr; [계속 진행]&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;되돌릴 수 없거나 고위험 결정에 인간 승인 게이트 삽입&lt;/li&gt;
&lt;li&gt;자율성과 책임의 균형&lt;/li&gt;
&lt;li&gt;&lt;b&gt;적합&lt;/b&gt;: 금융 거래, 의료 판단, 법적 결정&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. DAG(방향 비순환 그래프) 기반 워크플로우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대 오케스트레이션의 핵심 데이터 구조는 &lt;b&gt;DAG&lt;/b&gt;이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DAG의 장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;비선형 워크플로우&lt;/b&gt;: 여러 선행 작업의 결과에 의존하는 복잡한 작업 구조 표현&lt;/li&gt;
&lt;li&gt;&lt;b&gt;병렬 실행&lt;/b&gt;: 독립적인 브랜치를 동시에 처리하여 효율 극대화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;동적 적응&lt;/b&gt;: 실행 중 새로운 서브그래프를 동적으로 추가 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;감사 추적&lt;/b&gt;: 작업 상태, 의존성, 실행 이력을 정확히 추적&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DAG를 활용하는 프레임워크&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;b&gt;LangGraph&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;순환 그래프 지원, 상태 기반 멀티 에이전트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;ZenML&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;MLOps/LLMOps, 분기&amp;middot;루프 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Microsoft Promptflow&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;DAG 기반 AI 워크플로우&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Haystack&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;조합&amp;middot;모듈러 파이프라인, RAG 특화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Prefect/Temporal&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;범용 워크플로우 + 내구성 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 통신 프로토콜: MCP vs A2A&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2025~2026년 AI 에이전트 생태계의 통신 표준을 정의하는 두 프로토콜:&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;MCP (Model Context Protocol) &amp;mdash; &quot;에이전트의 손&quot;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;제안&lt;/b&gt;: Anthropic (2024.11), 이후 Linux Foundation 기증&lt;/li&gt;
&lt;li&gt;&lt;b&gt;목적&lt;/b&gt;: AI 모델 &amp;harr; 외부 도구/데이터 소스 간 표준 인터페이스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비유&lt;/b&gt;: &quot;AI의 USB-C&quot; &amp;mdash; 파일 읽기, 함수 실행, 프롬프트 처리를 통일&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기술&lt;/b&gt;: JSON-RPC 2.0 / stdio 또는 HTTP+SSE, 주로 무상태(Stateless)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;채택&lt;/b&gt;: OpenAI, Google, 8,000+ 커뮤니티 서버&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;A2A (Agent-to-Agent Protocol) &amp;mdash; &quot;에이전트의 동료&quot;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;제안&lt;/b&gt;: Google (2025.04), 이후 Linux Foundation 오픈소스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;목적&lt;/b&gt;: 에이전트 &amp;harr; 에이전트 간 발견&amp;middot;통신&amp;middot;협업&lt;/li&gt;
&lt;li&gt;&lt;b&gt;핵심&lt;/b&gt;: &quot;Agent Card&quot;로 에이전트 기능 발견, 작업 협업 관리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기술&lt;/b&gt;: JSON 메시지 / HTTP, JSON-RPC 2.0 / HTTPS&lt;/li&gt;
&lt;li&gt;&lt;b&gt;채택&lt;/b&gt;: Salesforce, SAP 등 50+ 파트너&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;보완 관계&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;┌─────────────────────────────────────────────────┐
│              Multi-Agent System                  │
│                                                  │
│  [에이전트 A] &amp;larr;──A2A──&amp;rarr; [에이전트 B]              │
│      │                       │                   │
│     MCP                     MCP                  │
│      │                       │                   │
│  [DB/API/도구]           [외부 서비스]             │
└─────────────────────────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;MCP&lt;/b&gt; = 수직 통합 (에이전트 &amp;harr; 도구/데이터)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;A2A&lt;/b&gt; = 수평 협업 (에이전트 &amp;harr; 에이전트)&lt;/li&gt;
&lt;li&gt;실전에서는 &lt;b&gt;두 프로토콜을 함께&lt;/b&gt; 사용하여 모듈형&amp;middot;확장 가능한 AI 생태계 구축&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 주요 프레임워크 비교&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;b&gt;LangGraph&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;LangChain&lt;/td&gt;
&lt;td&gt;상태 기반 순환 그래프, 세밀한 제어&lt;/td&gt;
&lt;td&gt;복잡한 멀티 에이전트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;CrewAI&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;오픈소스&lt;/td&gt;
&lt;td&gt;역할&amp;middot;태스크&amp;middot;도구 기반 &quot;크루&quot; 구조&lt;/td&gt;
&lt;td&gt;팀 기반 워크플로우 자동화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;AutoGen&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Microsoft&lt;/td&gt;
&lt;td&gt;이벤트 기반, 자연어 대화&amp;middot;협상&lt;/td&gt;
&lt;td&gt;다중 에이전트 대화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Semantic Kernel&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Microsoft&lt;/td&gt;
&lt;td&gt;엔터프라이즈 통합, AutoGen 결합&lt;/td&gt;
&lt;td&gt;기업 시스템 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;LlamaIndex&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;오픈소스&lt;/td&gt;
&lt;td&gt;RAG 특화, 컨텍스트 인식&lt;/td&gt;
&lt;td&gt;데이터 중심 워크플로우&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Orkes Conductor&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Orkes&lt;/td&gt;
&lt;td&gt;내구성 오케스트레이션, 상태 관리&lt;/td&gt;
&lt;td&gt;엔터프라이즈급 안정성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Temporal&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;오픈소스&lt;/td&gt;
&lt;td&gt;Durable Execution, 장기 실행&lt;/td&gt;
&lt;td&gt;인간 승인 포함 장기 워크플로우&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Google ADK&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Google&lt;/td&gt;
&lt;td&gt;A2A 네이티브, Vertex AI 연동&lt;/td&gt;
&lt;td&gt;Google 생태계&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 아키텍처 설계 시 핵심 고려사항&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.1 컨텍스트 &amp;amp; 메모리 관리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;단기 메모리&lt;/b&gt;: 현재 대화/작업의 컨텍스트&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장기 메모리&lt;/b&gt;: 벡터 DB를 통한 지식 저장&amp;middot;검색&lt;/li&gt;
&lt;li&gt;&lt;b&gt;에피소딕 메모리&lt;/b&gt;: 과거 경험 학습&lt;/li&gt;
&lt;li&gt;&amp;rarr; RAG(Retrieval-Augmented Generation)와 결합하여 환각 감소&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.2 상태 관리 &amp;amp; 지속성&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다단계 워크플로우의 진행 상태를 영구 저장&lt;/li&gt;
&lt;li&gt;장기 실행 프로세스의 중단&amp;middot;재개 지원&lt;/li&gt;
&lt;li&gt;체크포인팅으로 실패 시 복구&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.3 에러 처리 &amp;amp; 회복력&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개별 에이전트 실패 격리&lt;/li&gt;
&lt;li&gt;자동 재시도 + 폴백 메커니즘&lt;/li&gt;
&lt;li&gt;서킷 브레이커 패턴 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.4 관측성 (Observability)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;에이전트 간 호출 추적 (Tracing)&lt;/li&gt;
&lt;li&gt;토큰 소비&amp;middot;비용 모니터링&lt;/li&gt;
&lt;li&gt;에이전트 성능 메트릭 수집&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.5 비용 최적화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지능형 라우팅: 간단한 작업은 소형 모델(SLM), 복잡한 작업은 LLM&lt;/li&gt;
&lt;li&gt;캐싱: 반복 쿼리에 대한 Semantic Cache 활용&lt;/li&gt;
&lt;li&gt;로드 밸런싱: 에이전트 간 작업 균등 분배&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.6 보안 &amp;amp; 가드레일&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도구 사용에 대한 권한 제어&lt;/li&gt;
&lt;li&gt;위험 행동 방지 규칙 설정&lt;/li&gt;
&lt;li&gt;민감 데이터 접근 제한&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 실전 사례&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;고객 서비스&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Klarna&lt;/b&gt;: AI 어시스턴트가 채팅의 상당 부분을 자동 처리, 해결 시간 대폭 단축&lt;/li&gt;
&lt;li&gt;&lt;b&gt;멀티 에이전트 CS&lt;/b&gt;: Supervisor 에이전트가 질문 분류 &amp;rarr; Billing 에이전트(Stripe), Order 에이전트(Shopify) 등에 위임&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예방적 지원&lt;/b&gt;: 제품 텔레메트리&amp;middot;빌링 이상 분석으로 문제 발생 전 개입&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코딩 어시스턴트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자연어 목표 &amp;rarr; 코드 생성 &amp;rarr; 테스트 작성/실행 &amp;rarr; 디버깅 &amp;rarr; 리팩토링까지 자율 수행&lt;/li&gt;
&lt;li&gt;코드베이스 온보딩 시간: 기존 수 주 &amp;rarr; 수 시간으로 단축&lt;/li&gt;
&lt;li&gt;&lt;b&gt;멀티 에이전트 개발&lt;/b&gt;: AutoGen/CrewAI로 전문 에이전트 팀(코딩, 리뷰, 테스트, 문서화)이 협업&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 파이프라인&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ETL 워크플로우를 에이전트로 분해 (추출 &amp;rarr; 변환 &amp;rarr; 적재 &amp;rarr; 검증)&lt;/li&gt;
&lt;li&gt;스키마 변경 자동 감지 + 워크플로우 동적 조정&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 블로그 작성 시 추천 구성&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 도입: &quot;왜 하나의 AI만으로는 부족한가?&quot; &amp;mdash; 문제 제기
2. 개념: 오케스트레이션이란? &amp;mdash; 간결한 정의 + 비유(오케스트라)
3. 핵심 패턴: 가장 중요한 4-5가지 패턴 + 다이어그램
4. 통신 프로토콜: MCP &amp;amp; A2A &amp;mdash; 비유로 이해하기 쉽게
5. 프레임워크: 비교표 + 선택 가이드
6. 설계 원칙: 실패 설계, 관측성, HITL
7. 실전 사례: CS / 코딩 / 데이터 파이프라인
8. 마무리: 2026년 트렌드 전망 + 시작하기&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료 (리서치 소스)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IBM &amp;mdash; AI Agent Orchestration&lt;/li&gt;
&lt;li&gt;Microsoft &amp;mdash; Multi-Agent Design Patterns&lt;/li&gt;
&lt;li&gt;Google &amp;mdash; A2A Protocol, ADK&lt;/li&gt;
&lt;li&gt;Anthropic &amp;mdash; Model Context Protocol (MCP)&lt;/li&gt;
&lt;li&gt;LangChain / LangGraph 공식 문서&lt;/li&gt;
&lt;li&gt;CrewAI / AutoGen / Semantic Kernel 문서&lt;/li&gt;
&lt;li&gt;Medium / Dev.to / ByteByteGo 기사 다수&lt;/li&gt;
&lt;li&gt;Klarna &amp;mdash; AI Customer Support 사례&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI/Etc</category>
      <category>AI</category>
      <category>오케스트레이션</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/91</guid>
      <comments>https://blacknabis.tistory.com/91#entry91comment</comments>
      <pubDate>Fri, 6 Mar 2026 00:33:17 +0900</pubDate>
    </item>
    <item>
      <title>타워디펜스 - 맵 에디터 &amp;times; Battle Simulator 통합 강화 플랜</title>
      <link>https://blacknabis.tistory.com/90</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1512&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxOpjv/dJMcadA3EpZ/Aqo4iWCi2P7IK3UKSUXsgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxOpjv/dJMcadA3EpZ/Aqo4iWCi2P7IK3UKSUXsgK/img.png&quot; data-alt=&quot;시뮬레이터 결과화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxOpjv/dJMcadA3EpZ/Aqo4iWCi2P7IK3UKSUXsgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxOpjv%2FdJMcadA3EpZ%2FAqo4iWCi2P7IK3UKSUXsgK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1512&quot; height=&quot;900&quot; data-origin-width=&quot;1512&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;시뮬레이터 결과화면&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;작성일: 2026-03-02&lt;br /&gt;최종 보완: 2026-03-02 (라운드 3)&lt;br /&gt;상태: 플랜 (코드 작업 전)&lt;br /&gt;아키텍처: &lt;b&gt;A안 &amp;mdash; 흡수 통합&lt;/b&gt; (BattleSimWindow를 MapEditorWindow에 통합)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 현재 상태&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;b&gt;MapEditorWindow&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅ 완료&lt;/td&gt;
&lt;td&gt;경로/슬롯 편집만. 3개 모드(기본/경로/슬롯)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;WaveBalanceWindow&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅ 완료&lt;/td&gt;
&lt;td&gt;수치 편집만. 결과 예측 불가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;BattleSimWindow&lt;/b&gt;&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 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제&lt;/b&gt;: 세 도구가 분리되어 있어 &quot;경로 수정 &amp;rarr; 타워 배치 &amp;rarr; 시뮬 &amp;rarr; 결과 확인&quot;이 창 3개를 오가며 진행됨.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 목표 아키텍처 (A안: 흡수 통합)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;code&gt;BattleSimWindow&lt;/code&gt;를 제거하고 &lt;code&gt;MapEditorWindow&lt;/code&gt;에 모든 시뮬 기능을 통합한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;MapEditorWindow (통합 모드)
├── [  기본 정보]     &amp;larr; 기존: StageId, MapName, Bounds 등
├── [  경로 편집]     &amp;larr; 기존: SceneView 핸들
├── [  슬롯 편집]     &amp;larr; 기존: SceneView 핸들
├── [  타워 배치]     &amp;larr; 신규: 슬롯별 TowerConfig+Level 지정
└── [⚔ 시뮬레이션]    &amp;larr; 신규: 실행 버튼 + 결과 표시 + SceneView 오버레이&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;UI 레이아웃 (통합 후)&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;┌─ Map Editor ───────────────────────────────────────┐
│  MapConfig: [Stage_1_MapConfig ▼]                   │
│  WaveConfig: [Stage_1_WaveConfig ▼]  &amp;larr; 신규 슬롯   │
│                                                     │
│  ┌─────────────────────────────────────────────┐   │
│  │  [기본] [경로] [슬롯] [ 배치] [⚔시뮬]     │   │  &amp;larr; 모드 탭 (5개)
│  └─────────────────────────────────────────────┘   │
│                                                     │
│  ─── 모드별 Inspector 패널 ───                      │
│  (경로 모드면 경로 리스트,                           │
│   배치 모드면 슬롯별 타워 드롭다운,                   │
│   시뮬 모드면 실행 버튼 + 결과 테이블)                │
│                                                     │
│  ─── SceneView (공유) ───                           │
│  경로+슬롯+타워+시뮬결과 오버레이가                    │
│  현재 모드에 따라 다르게 표시                          │
└─────────────────────────────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 흐름&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;MapConfigData (SO)          WaveConfig (SO)
     │                          │
     ├── Paths[]                ├── Waves[]
     ├── TowerSlotPositions[]   ├── InitialGold/Lives
     └── GameplayBounds         └── StarThresholds
                 │                     │
                 └───────┬─────────────┘
                         ▼
            TowerPlacementPreset (SO 또는 세션 데이터)
               └── SlotPlacements[slotIdx &amp;rarr; (TowerConfig, Level)]
                         │
                         ▼
               BattleSimEngine.Run(SimConfig)
                         │
                         ▼
               BattleSimResult
               ├── 승패/별등급/라이프/골드
               ├── WaveResult[]
               ├── TowerContribution[]
               └── EnemyDeathPositions[]
                         │
                         ▼
               ┌─ Inspector 패널 결과 표시
               └─ SceneView 오버레이 (SimOverlayRenderer)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 모드별 기능 상세&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 기존 모드 (변경 최소화)&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;WaveConfig SO 슬롯 추가 (StageId 기반 자동 탐색)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&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;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 타워 배치 모드 (신규)&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Inspector 패널&lt;/h4&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;  타워 배치
─────────────────────────────
빠른 전체 배치:
  타워: [Archer ▼]  레벨: [Lv.1 ▼]  [전체 적용]

슬롯별 개별 배치:
  Slot 0: [Archer ▼]  [Lv.2 ▼]    [✕]
  Slot 1: [Mage   ▼]  [Lv.1 ▼]    [✕]
  Slot 2: [없음       ]             [✕]
  Slot 3: [Barracks▼]  [Lv.1 ▼]    [✕]
  ...

[프리셋 저장]  [프리셋 불러오기]  [전체 초기화]&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;타워 배치 데이터 영속성&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;세션 내&lt;/b&gt;: 배치 데이터는 &lt;code&gt;MapEditorWindow&lt;/code&gt;의 인스턴스 변수로 유지 (창 닫으면 초기화)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;저장&lt;/b&gt;: &quot;프리셋 저장&quot; 시 &lt;code&gt;TowerPlacementPreset&lt;/code&gt; SO로 디스크 저장&lt;/li&gt;
&lt;li&gt;&lt;b&gt;불러오기&lt;/b&gt;: SO에서 슬롯별 배치를 복원&lt;/li&gt;
&lt;li&gt;⚠ 배치 데이터는 MapConfigData에 포함하지 않음 (맵 데이터 &amp;ne; 밸런스 테스트 배치)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;SceneView 표시&lt;/h4&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;타워 타입별 색상 원 (Archer=초록, Mage=파랑, Barracks=주황, Artillery=빨강) + &quot;Lv.N&quot; 라벨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사거리 원&lt;/td&gt;
&lt;td&gt;해당 레벨의 실제 Range 값으로 반투명 원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;빈 슬롯&lt;/td&gt;
&lt;td&gt;회색 점선 원 + &quot;Empty&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;사각지대 경고&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;어느 타워 사거리에도 안 들어가는 경로 구간을 빨간색 굵은 선으로 표시&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;SceneView 조작&lt;/h4&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;빈 슬롯 클릭 &amp;rarr; 현재 선택 타워로 배치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;타워 교체&lt;/td&gt;
&lt;td&gt;배치된 슬롯 클릭 &amp;rarr; 현재 선택 타워로 교체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;타워 제거&lt;/td&gt;
&lt;td&gt;배치된 슬롯 우클릭 &amp;rarr; &quot;타워 제거&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;레벨 변경&lt;/td&gt;
&lt;td&gt;배치된 슬롯 우클릭 &amp;rarr; &quot;Lv.1/2/3&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 SceneView 배치 조작에 &lt;b&gt;Undo/Redo&lt;/b&gt; 지원 (Undo.RecordObject)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3 시뮬레이션 모드 (신규)&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Inspector 패널&lt;/h4&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;⚔ 전투 시뮬레이션
─────────────────────────────
[▶ 시뮬레이션 실행]       [  이전 결과와 비교]

━━━ 결과 요약 ━━━
✅ 승리 ★★★
남은 라이프: 18/20
총 골드: 3200  |  시뮬 시간: 45.3s

━━━ 웨이브별 상세 ━━━
 W1 | 일반 | 적12 | 처치12/돌파0 | 골드180 | 5.0~12.3s
 W2 | 일반 | 적15 | 처치13/돌파2 | 골드210 | 14.3~25.1s
 W3 | 보스 | 적 3 | 처치 3/돌파0 | 골드450 | 27.1~45.3s

━━━ 타워별 기여도 ━━━
 Slot0 Archer Lv2: 데미지 2400 (28%)  처치 8
 Slot1 Mage   Lv1: 데미지 1800 (21%)  처치 6
 Slot3 Barracks   : 블록 14회, 데미지 800 (9%)  처치 3
 ...

━━━ 골드 추이 차트 ━━━
 ▓▓▓▓▓▓▓▓
 ▓▓▓▓▓▓▓▓▓▓▓
 ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓

[  로그 펼치기]&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;결과 비교 기능&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;이전 결과와 비교&quot; 버튼: 마지막 2회 시뮬 결과를 나란히 비교&lt;/li&gt;
&lt;li&gt;비교 항목: 라이프 차이, 골드 차이, 웨이브별 돌파 수 증감&lt;/li&gt;
&lt;li&gt;용도: 밸런스 조정 &amp;rarr; 시뮬 &amp;rarr; &lt;b&gt;수정 전후 비교&lt;/b&gt; 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;SceneView 표시 (시뮬 후 오버레이)&lt;/h4&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;적이 사망한 경로상 위치에 빨간 X 마커&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&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;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  경로 열지도&lt;/td&gt;
&lt;td&gt;경로 구간별 &quot;적이 가장 오래 머문 구간&quot; = 두꺼운 주황, 빠르게 지나간 구간 = 얇은 초록&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⚠ 사각지대&lt;/td&gt;
&lt;td&gt;타워 사거리에 안 걸리는 경로 구간 = 빨간 점선&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;에러 처리 / 검증&lt;/h4&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;MapConfig 미연결&lt;/td&gt;
&lt;td&gt;&quot;MapConfigData를 연결하세요&quot; 경고 + 실행 비활성화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WaveConfig 미연결&lt;/td&gt;
&lt;td&gt;&quot;WaveConfig를 연결하세요&quot; 경고 + 실행 비활성화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;타워 0개 배치&lt;/td&gt;
&lt;td&gt;&quot;타워 없이 시뮬합니다 (모든 적 돌파 예상)&quot; 안내 + 실행 허용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;경로 0개&lt;/td&gt;
&lt;td&gt;&quot;유효한 경로가 없습니다&quot; 경고 + 실행 비활성화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;경로 Waypoint &amp;lt; 2&lt;/td&gt;
&lt;td&gt;&quot;PathId=N 의 Waypoint가 부족합니다&quot; 경고&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;스폰 엔트리의 PathId가 MapConfig에 없음&lt;/td&gt;
&lt;td&gt;&quot;PathId=N 이 맵에 존재하지 않습니다&quot; 경고&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 정밀 시뮬 엔진 개선&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 2D 위치 보간&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// 경로 Waypoint 리스트 + 이동 거리 &amp;rarr; 2D 좌표 계산
Vector3 GetPositionOnPath(List&amp;lt;Vector3&amp;gt; waypoints, float distanceTraveled)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2 거리 기반 사거리 판정&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// 기존: 경로 진행도 기반 (부정확)
// 개선: 타워 좌표 &amp;harr; 적 좌표 실제 거리
float dist = Vector3.Distance(tower.Position, enemy.CurrentPosition);
if (dist &amp;lt;= tower.Range) { /* 공격 */ }&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.3 타워 타겟팅 규칙&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;b&gt;경로 진행도가 가장 높은&lt;/b&gt;(=도착에 가장 가까운) 적 타겟&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&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;code&gt;TowerConfig.CanTarget(isFlying)&lt;/code&gt; &amp;mdash; Flying 적은 Ground 전용 타워에게 무시됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Artillery AoE&lt;/td&gt;
&lt;td&gt;주 타겟 주변 반경(ExplosionRadius) 내 적에게 감소된 데미지&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.4 Barracks 블록 규칙&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;float dist = Vector3.Distance(rallyPoint, enemy.CurrentPosition);
if (dist &amp;lt;= rallyRange &amp;amp;&amp;amp; enemy.CanBeBlocked) { /* 블록 */ }&lt;/code&gt;&lt;/pre&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;code&gt;CanBeBlocked == true&lt;/code&gt; (Flying, Boss 제외)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rally 반경&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BarracksData.RallyRange &amp;gt; 0 ? RallyRange : tower.Range&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;병사 리스폰&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BarracksData.SoldierRespawnSec&lt;/code&gt; (기본 5초)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 병사 = 1 적&lt;/td&gt;
&lt;td&gt;한 병사가 한 적만 블록, 적이 죽으면 해제 후 새 적 탐색&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.5 사각지대 정적 분석 (시뮬 전 즉시)&lt;/h3&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;경로의 모든 구간을 0.5 단위로 샘플링
&amp;rarr; 각 샘플 지점에서 모든 타워까지 거리 계산
&amp;rarr; min(거리) &amp;gt; max(Range) &amp;rarr; 사각지대
&amp;rarr; SceneView에 빨간 경로 표시&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.6 적 사망 위치 / 타워별 기여도 추적&lt;/h3&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;// BattleSimResult에 추가
public List&amp;lt;Vector3&amp;gt; EnemyDeathPositions;     // 사망 위치
public List&amp;lt;TowerContribution&amp;gt; TowerStats;    // 타워별 누적 데미지&amp;middot;처치수

public struct TowerContribution {
    public int SlotIndex;
    public string TowerName;    // 식별 편의
    public float TotalDamage;
    public int KillCount;
    public int BlockCount;      // Barracks
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 프리셋 데이터 구조&lt;/h2&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;[CreateAssetMenu(fileName = &quot;TowerPreset&quot;, menuName = &quot;Kingdom/Editor/Tower Placement Preset&quot;)]
public class TowerPlacementPreset : ScriptableObject
{
    public string PresetName;
    public int StageId;           // 어느 맵 기준인지
    public List&amp;lt;SlotPlacement&amp;gt; Placements;
}

[Serializable]
public struct SlotPlacement
{
    public int SlotIndex;
    public TowerConfig Tower;     // null이면 빈 슬롯
    public int Level;             // 0-indexed
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장 경로: &lt;code&gt;Assets/Resources/Kingdom/Configs/TowerPresets/&lt;/code&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 수정/생성 파일&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;code&gt;MapEditorWindow.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;WaveConfig 슬롯 추가, &quot;타워 배치&quot;/&quot;시뮬&quot; 모드 탭 추가, 결과 패널 통합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BattleSimEngine.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GetPositionOnPath()&lt;/code&gt;, 거리 기반 판정, 기여도 추적, 사망 위치 기록&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BattleSimResult.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TowerContribution&lt;/code&gt;, &lt;code&gt;EnemyDeathPositions&lt;/code&gt;, 사각지대 데이터 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BattleSimWindow.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;삭제&lt;/b&gt; (Phase A step 5에서 제거. 삭제 전 MapEditorWindow 시뮬 탭 완성 선행)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TowerPlacementEditor.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;신규&lt;/b&gt; &amp;mdash; 슬롯별 타워 배치 SceneView 핸들&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SimOverlayRenderer.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;신규&lt;/b&gt; &amp;mdash; SceneView 시뮬 결과 오버레이 렌더링&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CoverageAnalyzer.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;신규&lt;/b&gt; &amp;mdash; 사각지대 정적 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TowerPlacementPreset.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;신규&lt;/b&gt; &amp;mdash; 타워 배치 프리셋 SO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WaveBalanceWindow.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&quot;시뮬 실행&quot; 버튼 + 인라인 결과 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 구현 단계&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Phase A: 정밀 시뮬 + 타워 개별 배치&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;BattleSimEngine&lt;/code&gt; &amp;mdash; 2D 위치 보간 + 거리 기반 판정 + 기여도 추적&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BattleSimResult&lt;/code&gt; &amp;mdash; 기여도/사망 위치/비교 데이터 필드 추가&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MapEditorWindow&lt;/code&gt; &amp;mdash; &quot;타워 배치&quot; 모드 탭 추가 (Inspector만)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MapEditorWindow&lt;/code&gt; &amp;mdash; &quot;시뮬&quot; 모드 탭 추가 (결과 테이블 + 차트 + 비교)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BattleSimWindow.cs&lt;/code&gt; 삭제 (&lt;b&gt;step 4 완성 후&lt;/b&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완료 기준:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MapEditorWindow 단독으로 타워 배치 &amp;rarr; 시뮬 실행 &amp;rarr; 결과 확인 가능&lt;/li&gt;
&lt;li&gt;경로 옆 배치 vs 먼 곳 배치 시 결과 차이 발생 확인&lt;/li&gt;
&lt;li&gt;BattleSimWindow 메뉴 항목 제거 완료&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Phase B: SceneView 통합&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;6&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;TowerPlacementEditor&lt;/code&gt; &amp;mdash; 슬롯별 타워 SceneView 핸들 (클릭 배치/우클릭 메뉴)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CoverageAnalyzer&lt;/code&gt; &amp;mdash; 사각지대 분석 + 경로 경고 SceneView 표시&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SimOverlayRenderer&lt;/code&gt; &amp;mdash; 시뮬 결과 SceneView 오버레이 (사망 마커, 기여도 원, 열지도)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완료 기준:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SceneView에서 타워 배치 조작 (클릭/우클릭) 정상 동작 + Undo/Redo&lt;/li&gt;
&lt;li&gt;사각지대 빨간 경로 표시 정상 동작&lt;/li&gt;
&lt;li&gt;시뮬 후 사망/돌파 마커가 SceneView에 표시&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Phase C: 연동 + 프리셋&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;9&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;WaveBalanceWindow&lt;/code&gt; &amp;rarr; 시뮬 연동 버튼 (현재 WaveConfig + 마지막 프리셋으로 시뮬)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerPlacementPreset&lt;/code&gt; &amp;mdash; 프리셋 SO 저장/로드&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완료 기준:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WaveBalanceWindow에서 수치 수정 &amp;rarr; 시뮬 버튼 &amp;rarr; 결과 인라인 표시&lt;/li&gt;
&lt;li&gt;프리셋 저장/불러오기 정상 동작&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Phase D: 영웅 + 고급 시각화&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;11&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;영웅 배치/시뮬 (HeroConfig 연동) &amp;mdash; 고정 위치 타워로 추상화&lt;/li&gt;
&lt;li&gt;경로 히트맵 (구간별 적 체류 시간 색상 표시)&lt;/li&gt;
&lt;li&gt;타워 DPS 기여도 파이 차트&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완료 기준:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;영웅 포함 시뮬 실행 가능&lt;/li&gt;
&lt;li&gt;히트맵 오버레이 SceneView 표시&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 리스크 및 대응&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;b&gt;MapEditorWindow가 너무 커짐&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;모드별 로직을 별도 클래스(TowerPlacementEditor, SimOverlayRenderer)로 분리하여 MapEditorWindow는 모드 라우터 역할만&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;시뮬 실행 시간이 오래 걸리면 에디터 프리즈&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;최대 시뮬 시간 600초 제한, 진행바(EditorUtility.DisplayProgressBar) 표시. 50ms 이상 걸리면 경고 로그&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;SceneView 오버레이 성능 저하&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;오버레이 항목별 토글 제공, 대량 마커는 밀도 기반 클러스터링, Repaint 최소화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;맵/웨이브 데이터 수정 후 시뮬 결과 stale&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;시뮬 결과 상단에 &quot;⚠ 데이터가 수정되었습니다. 다시 실행하세요&quot; 배너 표시. &lt;code&gt;_target&lt;/code&gt;의 변경 감지(Hash 또는 dirty flag)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;타워 배치가 맵 데이터와 혼동&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;배치는 MapConfigData에 저장하지 않음. 별도 SO(TowerPlacementPreset)으로 명확히 분리. Inspector에 안내 문구&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;다중 경로 시뮬 정확도&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;경로 간 적 분포가 실 플레이와 다를 수 있음. 시뮬 결과에 &quot;추정치&quot; 라벨 표시&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. 결정 필요 항목 (착수 전 확정)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 타워 배치 프리셋을 SO로 저장할지, EditorPrefs(JSON)로 경량화할지&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 사각지대 분석을 &quot;타워 배치&quot; 모드에서 상시 표시할지, &quot;시뮬&quot; 모드 전용으로 할지&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 영웅 시뮬(Phase D)을 1차 범위에 포함할지, 완전 2차로 넘길지&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 시뮬 결과 비교를 최대 몇 회분까지 보관할지 (권장: 2회)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. 기존 코드 이슈 (Phase A 선행 수정)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex 정적 리뷰(라운드 1)에서 발견된 기존 &lt;code&gt;BattleSimEngine.cs&lt;/code&gt; / &lt;code&gt;BattleSimWindow.cs&lt;/code&gt; 버그.&lt;br /&gt;Phase A 정밀 시뮬 개선 시 함께 수정해야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;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;  High&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BattleSimEngine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;타워 레벨 데이터 null/empty 시 NRE/IndexOutOfRange 발생 (Levels[lvl] 무조건 접근)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tc.Levels == null || lvl &amp;gt;= tc.Levels.Length&lt;/code&gt; 가드 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;  High&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BattleSimEngine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Barracks 타워의 &lt;code&gt;BarracksData&lt;/code&gt; null 체크 없이 필드 접근 &amp;rarr; NRE&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tower.Config.BarracksData != null&lt;/code&gt; 가드 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;  Medium&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BattleSimEngine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MaxSimTimeSec 초과로 루프 종료 시, 생존 적을 처치로 집계 (&lt;code&gt;EnemiesKilled = Spawned - Leaked&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;명시적으로 생존 적 수를 별도 추적, &lt;code&gt;EnemiesAlive&lt;/code&gt; 필드 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;  Medium&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BattleSimWindow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RunSimulation()&lt;/code&gt;에서 &lt;code&gt;_isRunning&lt;/code&gt; 복구 없음 &amp;rarr; Engine 예외 시 UI가 &quot;실행 중&quot; 고정&lt;/td&gt;
&lt;td&gt;&lt;code&gt;try/finally&lt;/code&gt; 패턴으로 &lt;code&gt;_isRunning = false&lt;/code&gt; 보장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;  Medium&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BattleSimEngine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Barracks &lt;code&gt;RallyRange&lt;/code&gt; 계산하지만 미사용, 경로 진행도(0.15~0.9) 판정과 불일치&lt;/td&gt;
&lt;td&gt;Phase A에서 2D 좌표 기반 판정으로 전면 교체 시 해소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;  High&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BattleSimEngine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;HandleAoeDamage()&lt;/code&gt;에서 HP를 깎지만 &lt;code&gt;KillEnemy&lt;/code&gt;를 호출하지 않음 &amp;rarr; HP&amp;le;0 적이 살아서 이동/누수 가능&lt;/td&gt;
&lt;td&gt;AoE 데미지 후 &lt;code&gt;HP &amp;lt;= 0&lt;/code&gt; 사망 처리 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;  Critical&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BattleSimEngine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MaxSimTimeSec&lt;/code&gt; 타임아웃 시 &lt;code&gt;IsCleared = lives &amp;gt; 0&lt;/code&gt;으로 판정 &amp;rarr; 적/웨이브 잔존해도 승리 처리&lt;/td&gt;
&lt;td&gt;타임아웃 시 &lt;code&gt;IsCleared = false&lt;/code&gt; 또는 별도 &lt;code&gt;TimedOut&lt;/code&gt; 상태 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;  Medium&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BattleSimEngine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WaveConfig.Waves&lt;/code&gt;, &lt;code&gt;MapConfig.TowerSlotPositions&lt;/code&gt; null 체크 없이 접근 &amp;rarr; NRE 가능&lt;/td&gt;
&lt;td&gt;시뮬 시작 전 입력 유효성 검증 단계(ValidateInputs) 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. 시뮬 한계 및 면책 (Limitations)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시뮬레이터는 &lt;b&gt;수치 기반 근사치&lt;/b&gt;이며, 실 플레이와 100% 동일하지 않다.&lt;br /&gt;결과는 밸런스 방향성 확인용이며, 최종 검증은 실 플레이로 수행한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;b&gt;경제 시뮬 미포함&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;실 플레이에서는 웨이브 중 골드로 타워 추가 구매/업그레이드가 가능하지만, 시뮬은 &quot;사전 배치 고정&quot;. 중반 이후 타워 업그레이드 효과를 반영하지 못함&lt;/td&gt;
&lt;td&gt;  중&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;타워 타겟팅 AI&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;실 게임의 타겟팅 로직(최근접, 최강, 최약 등)과 시뮬의 &quot;경로 진행도 최고&quot; 규칙이 다를 수 있음&lt;/td&gt;
&lt;td&gt;  중&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;투사체 이동 시간&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;실 게임에서 화살/마법 투사체는 이동 시간이 있으나, 시뮬은 즉시 적중&lt;/td&gt;
&lt;td&gt;  낮&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Barracks 교전 동선&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;실 게임에서 병사와 적의 이동/교전 위치가 세밀하지만, 시뮬은 Rally 반경 내 즉시 블록&lt;/td&gt;
&lt;td&gt;  중&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;곡선 경로&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;현재 직선 Waypoint Lerp 보간만 지원. 곡선(Catmull-Rom 등)은 미지원&lt;/td&gt;
&lt;td&gt;  낮 (현 프로젝트 경로는 직선 Waypoint 기반)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;영웅/스킬/버프&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Phase D 이전에는 미반영&lt;/td&gt;
&lt;td&gt;  중&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;경제 시뮬 고려 (향후 확장)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;현재&lt;/b&gt;: 모든 타워가 게임 시작 전에 배치된 상태로 시뮬.&lt;br /&gt;&lt;b&gt;한계&lt;/b&gt;: 초반에 Lv.1 타워 &amp;rarr; 중반 골드로 Lv.3 업그레이드 같은 실 플레이 패턴을 반영 못함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;보완 방안 (Phase E 이후)&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;시뮬 옵션: [x] 경제 시뮬 활성화

초기 골드: 500
자동 업그레이드 정책:
  - 골드가 업그레이드 비용 이상이면 &amp;rarr; 가장 DPS 기여도 높은 타워를 우선 업그레이드
  - 또는: 수동 &quot;웨이브 N 후 슬롯 M을 Lv.2로 업그레이드&quot; 스크립트 입력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 현재 Phase A~D에서는 &lt;b&gt;경제 시뮬 미포함&lt;/b&gt;으로 진행하되, 결과에 &quot;경제 시뮬 미반영&quot; 라벨을 표시한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;곡선 경로 판단&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 프로젝트의 &lt;code&gt;PathData.Waypoints&lt;/code&gt;는 &lt;b&gt;직선 연결 구간&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;킹덤 러시 원작도 Waypoint 기반 직선/꺾임 경로 사용 (실제 곡선 스플라인 아님)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;결론&lt;/b&gt;: 현재 직선 Lerp 보간으로 충분. Catmull-Rom 등은 불필요&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12. 프리셋 호환성 규칙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵 수정으로 &lt;b&gt;타워 슬롯 수가 변경&lt;/b&gt;될 때 기존 프리셋이 깨지는 문제 대응:&lt;/p&gt;
&lt;table style=&quot;height: 108px;&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr style=&quot;height: 24px;&quot;&gt;
&lt;th style=&quot;height: 24px;&quot;&gt;상황&lt;/th&gt;
&lt;th style=&quot;height: 24px;&quot;&gt;처리&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;슬롯 &lt;b&gt;추가&lt;/b&gt; (3&amp;rarr;5)&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;프리셋의 기존 슬롯은 유지, 신규 슬롯은 &quot;빈 슬롯&quot;으로 자동 채움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;슬롯 &lt;b&gt;삭제&lt;/b&gt; (5&amp;rarr;3)&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;프리셋에서 삭제된 SlotIndex는 무시하고 경고 표시: &quot;⚠ Slot 3,4는 맵에 존재하지 않아 무시됨&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;슬롯 &lt;b&gt;위치 이동&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;SlotIndex 기반이므로 위치만 바뀌면 자동 대응. 인덱스가 바뀌면 경고&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;StageId 불일치&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&quot;⚠ 이 프리셋은 Stage_2용입니다. 현재 맵은 Stage_1입니다.&quot; 경고 + 적용 허용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 프리셋 로드 시 &lt;b&gt;SlotIndex 범위 검증&lt;/b&gt; + &lt;b&gt;StageId 일치 확인&lt;/b&gt;을 수행한다.&lt;/p&gt;</description>
      <category>AI/잡다한개발노트</category>
      <category>Devlog</category>
      <category>plan</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/90</guid>
      <comments>https://blacknabis.tistory.com/90#entry90comment</comments>
      <pubDate>Mon, 2 Mar 2026 22:05:04 +0900</pubDate>
    </item>
    <item>
      <title>[Unity] 타워디펜스 맵 &amp;amp; 웨이브 에디터</title>
      <link>https://blacknabis.tistory.com/89</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;타워디펜스 게임을 개발하다 보면 맵 데이터를 관리하는 방법이 항상 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 &lt;code&gt;GameBattlefield.prefab&lt;/code&gt; 하위에 &lt;code&gt;PathRoot/Path0/Waypoint_0&lt;/code&gt;, &lt;code&gt;PathRoot/Path0/Waypoint_1&lt;/code&gt;... 식으로 씬 계층(Transform 트리)에 직접 경로를 박아넣었다. 타워 슬롯도 마찬가지로 &lt;code&gt;TowerRoot&lt;/code&gt; 하위에 빈 게임오브젝트를 하나씩 배치했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 처음엔 빠르지만, 금방 한계가 온다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;새 맵을 추가하려면&lt;/b&gt; 프리팹을 복사하고 Transform을 일일이 수정해야 한다&lt;/li&gt;
&lt;li&gt;&lt;b&gt;웨이브 밸런스를 조정하려면&lt;/b&gt; WaveConfig 인스펙터를 열고 리스트를 손으로 뒤져야 한다&lt;/li&gt;
&lt;li&gt;&lt;b&gt;전체 밸런스가 어떤지&lt;/b&gt; 한눈에 보이지 않는다&lt;/li&gt;
&lt;li&gt;기획자가 직접 조작하기 어렵다 (개발자가 옆에 있어야 한다)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵이 1개일 때는 그냥 참을 수 있다. 하지만 Stage 2, 3...을 추가하고 싶어지는 순간, 이 구조는 관리 비용이 폭증한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;b&gt;에디터 도구를 직접 만들기로 했다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;목표&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;씬 뷰에서 클릭 몇 번으로 경로와 타워 슬롯을 편집할 수 있을 것&lt;/li&gt;
&lt;li&gt;맵 데이터를 &lt;b&gt;ScriptableObject(SO)&lt;/b&gt; 로 분리해서 프리팹과 독립적으로 관리할 것&lt;/li&gt;
&lt;li&gt;웨이브 구성과 밸런스 지표를 한 화면에서 볼 수 있을 것&lt;/li&gt;
&lt;li&gt;새 맵 추가 시 프리팹을 복사하지 않고 SO만 만들면 게임이 돌아갈 것&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구성 요소&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 5개의 에디터 파일을 만들었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. MapConfigData (ScriptableObject)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵의 모든 정적 데이터를 담는 SO.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;public class MapConfigData : ScriptableObject
{
    public int StageId;
    public List&amp;lt;PathData&amp;gt; Paths;              // 경로 목록 (웨이포인트 리스트)
    public List&amp;lt;Vector3&amp;gt; TowerSlotPositions;  // 타워 설치 가능 위치
    public string BackgroundSpritePath;       // Resources 상대 경로
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일 하나만 있으면 해당 스테이지를 런타임에서 완전히 복원할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장 위치: &lt;code&gt;Assets/Resources/Kingdom/Configs/Maps/Stage_1_MapConfig.asset&lt;/code&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. MapEditorWindow &amp;mdash; 맵 편집 창&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity 상단 메뉴 &lt;code&gt;Tools &amp;gt; Kingdom &amp;gt; Map &amp;gt; Map Editor&lt;/code&gt;로 열린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 기능:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SO 에셋 드래그 앤 드롭으로 불러오기 / 새로 만들기&lt;/li&gt;
&lt;li&gt;경로(Path) 추가/삭제, 경로별 웨이포인트 편집&lt;/li&gt;
&lt;li&gt;타워 슬롯 위치 편집&lt;/li&gt;
&lt;li&gt;배경 이미지 경로 설정 (스프라이트 드래그하면 Resources 경로 자동 추출)&lt;/li&gt;
&lt;li&gt;창 상단에 유효성 검사 배너 표시 (오류 = 빨강, 경고 = 노랑)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. PathHandleEditor &amp;mdash; 씬 뷰 핸들 편집기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MapEditorWindow에서 &lt;b&gt;경로 편집 모드&lt;/b&gt;를 켜면 씬 뷰에서 직접 조작할 수 있다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;웨이포인트 추가&lt;/td&gt;
&lt;td&gt;Shift + 클릭&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;웨이포인트 삭제/삽입&lt;/td&gt;
&lt;td&gt;우클릭 컨텍스트 메뉴&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로마다 색이 다르게 표시된다: Path0=초록, Path1=파랑, Path2=주황...&lt;br /&gt;시작점은 초록 구, 도착점은 빨간 구로 구분된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 구현 포인트:&lt;/b&gt;&lt;br /&gt;&lt;code&gt;SceneView.duringSceneGui&lt;/code&gt; 이벤트를 구독해서 씬 뷰에 직접 그린다. &lt;code&gt;Handles.FreeMoveHandle()&lt;/code&gt;로 드래그 가능한 핸들을 생성하고, &lt;code&gt;Undo.RecordObject()&lt;/code&gt;로 실행 취소를 지원한다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;private void OnSceneGui(SceneView sv)
{
    if (_target == null) return;

    for (int i = 0; i &amp;lt; _target.Paths.Count; i++)
    {
        PathData path = _target.Paths[i];
        DrawPathHandles(path, _pathColors[i % _pathColors.Length]);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. TowerSlotHandleEditor &amp;mdash; 타워 슬롯 핸들 편집기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;타워 슬롯 편집 모드&lt;/b&gt;에서 동작한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Shift + 클릭&lt;/code&gt; &amp;rarr; 슬롯 추가&lt;/li&gt;
&lt;li&gt;노란 사각형 핸들 드래그 &amp;rarr; 이동&lt;/li&gt;
&lt;li&gt;우클릭 &amp;rarr; 삭제&lt;/li&gt;
&lt;li&gt;GameplayBounds 바깥에 있는 슬롯은 빨간색으로 경고 표시&lt;/li&gt;
&lt;li&gt;기본 사거리 가이드 원 표시 (Archer 기준 반경 4.0f)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. WaveBalanceWindow &amp;mdash; 웨이브 밸런스 편집 창&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Tools &amp;gt; Kingdom &amp;gt; Map &amp;gt; Wave Balance Editor&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;창 상단 밸런스 지표 패널:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;총 웨이브: 5     총 적 수: 87
총 HP: 18,400    총 골드: 1,240&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 숫자들이 실시간으로 자동 계산된다. 스폰 수를 늘리면 바로 반영되니 밸런스 조정이 직관적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;웨이브 헤더 색상:&lt;/b&gt;&lt;br /&gt;일반 웨이브 = 파랑, 보스 웨이브 = 붉은색으로 즉시 구분된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스폰 엔트리 편집:&lt;/b&gt;&lt;br /&gt;EnemyConfig 에셋을 드래그하면 자동으로 HP, 골드 수치가 계산에 반영된다.&lt;br /&gt;PathId를 드롭다운으로 선택할 수 있다 (MapConfig가 연결되어 있을 때).&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;가장 까다로웠던 부분&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SceneView에 배경 이미지 미리보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵 에디터에서 배경 스프라이트를 설정하면 &lt;b&gt;씬 뷰에 실시간으로 표시&lt;/b&gt;되게 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 씬 뷰는 EditorWindow GUI와 좌표계가 다르다는 점이다.&lt;br /&gt;월드 좌표 &amp;rarr; GUI 좌표 변환이 필요하다.&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;private void OnSceneGui(SceneView sv)
{
    if (string.IsNullOrWhiteSpace(_target.BackgroundSpritePath)) return;
    Sprite sprite = Resources.Load&amp;lt;Sprite&amp;gt;(_target.BackgroundSpritePath);
    if (sprite == null) return;

    Handles.BeginGUI();

    Rect bounds = _target.GameplayBounds;
    Vector2 guiMin = HandleUtility.WorldToGUIPoint(
        new Vector3(bounds.xMin, bounds.yMin, 0));
    Vector2 guiMax = HandleUtility.WorldToGUIPoint(
        new Vector3(bounds.xMax, bounds.yMax, 0));

    Rect guiRect = new Rect(
        Mathf.Min(guiMin.x, guiMax.x),
        Mathf.Min(guiMin.y, guiMax.y),
        Mathf.Abs(guiMax.x - guiMin.x),
        Mathf.Abs(guiMax.y - guiMin.y));

    // Atlas 스프라이트 대응: 텍스처 내 UV 좌표 계산
    Rect texCoords = new Rect(
        sprite.rect.x / sprite.texture.width,
        sprite.rect.y / sprite.texture.height,
        sprite.rect.width / sprite.texture.width,
        sprite.rect.height / sprite.texture.height);

    GUI.color = new Color(1f, 1f, 1f, 0.85f);
    GUI.DrawTextureWithTexCoords(guiRect, sprite.texture, texCoords);
    GUI.color = Color.white;

    Handles.EndGUI();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;GUI.DrawTexture()&lt;/code&gt;가 아니라 &lt;code&gt;GUI.DrawTextureWithTexCoords()&lt;/code&gt;를 써야 atlas 스프라이트에서도 올바른 영역이 잘려서 표시된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 소스 이원화 문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에는 맵 데이터가 두 곳에 있었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프리팹 Transform 계층 (기존)&lt;/li&gt;
&lt;li&gt;MapConfigData SO (신규)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;런타임 코드가 SO를 먼저 보고, 없으면 프리팹 계층을 폴백으로 읽는 이중 구조였는데, 이게 오히려 혼란을 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결론:&lt;/b&gt; 프리팹의 PathRoot, TowerRoot 하위 오브젝트를 전부 삭제하고, &lt;b&gt;SO가 없으면 에러 로그&lt;/b&gt; 방식으로 단순화했다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// 이제 폴백 없음
MapConfigData mapConfig = ConfigResourcePaths.LoadMapConfigByStageId(stageId);
if (mapConfig == null || mapConfig.Paths.Count == 0)
{
    Debug.LogError($&quot;[GameScene] Stage {stageId} MapConfigData를 찾을 수 없습니다.&quot;);
    return;
}
_pathManager.SetPaths(mapConfig.Paths);&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 맵을 추가하는 절차:&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;1. Map Editor &amp;rarr; &quot;새로 만들기&quot;
2. 씬 뷰에서 경로 웨이포인트 배치 (Shift+클릭)
3. 씬 뷰에서 타워 슬롯 배치 (Shift+클릭)
4. Wave Balance Editor에서 웨이브 편집
5. 저장 &amp;rarr; 끝&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프리팹 복사 없음. 씬 복제 없음. SO 하나가 맵의 전부다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;788&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJU5Kf/dJMcahDvdvB/CyqVWU6biu0XS9ypXlJ8E1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJU5Kf/dJMcahDvdvB/CyqVWU6biu0XS9ypXlJ8E1/img.png&quot; data-alt=&quot;맵에디터&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJU5Kf/dJMcahDvdvB/CyqVWU6biu0XS9ypXlJ8E1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJU5Kf%2FdJMcahDvdvB%2FCyqVWU6biu0XS9ypXlJ8E1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1708&quot; height=&quot;788&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;788&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;맵에디터&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity 에디터 확장은 &lt;code&gt;EditorWindow&lt;/code&gt;, &lt;code&gt;Handles&lt;/code&gt;, &lt;code&gt;SceneView.duringSceneGui&lt;/code&gt; 세 가지를 조합하면 대부분 원하는 걸 만들 수 있다. 처음엔 좌표계 변환(월드&amp;harr;GUI)이 헷갈리지만, &lt;code&gt;HandleUtility.WorldToGUIPoint()&lt;/code&gt;와 &lt;code&gt;GUIUtility.GUIToScreenPoint()&lt;/code&gt;의 역할 차이를 구분하면 감이 잡힌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비슷한 구조의 레벨 에디터를 만들 계획이 있다면 참고가 되면 좋겠다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글이 도움이 됐다면 댓글로 알려주세요.&lt;/p&gt;</description>
      <category>AI/Unity</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/89</guid>
      <comments>https://blacknabis.tistory.com/89#entry89comment</comments>
      <pubDate>Mon, 2 Mar 2026 03:00:43 +0900</pubDate>
    </item>
    <item>
      <title>타워 디펜스 만들기 - 맵 이미지 프롬프트</title>
      <link>https://blacknabis.tistory.com/88</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;325&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cn54y/dJMcafyXoUu/EKHeKU0ou0m56R78HKoo8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cn54y/dJMcafyXoUu/EKHeKU0ou0m56R78HKoo8k/img.png&quot; data-alt=&quot;기존에 사용했던 맵&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cn54y/dJMcafyXoUu/EKHeKU0ou0m56R78HKoo8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCn54y%2FdJMcafyXoUu%2FEKHeKU0ou0m56R78HKoo8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;574&quot; height=&quot;325&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;325&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기존에 사용했던 맵&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 어느정도 작업이 되었기 때문에 스테이지 제작 단계에 들어갈 필요성을 느꼈습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵은 오브젝트가 없는 배경이미지와 오브젝트들을 타일형태로 추가해서 aseprite를 사용하여 배치하던가 별도의 툴을 만들어서 배치 해볼까 고민중입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GYcdE/dJMcaaLaibO/RALOpp80rgV6GkN9wK7T11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GYcdE/dJMcaaLaibO/RALOpp80rgV6GkN9wK7T11/img.png&quot; data-alt=&quot;Google Flow를 이용하여 만든 맵 배경이미지들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GYcdE/dJMcaaLaibO/RALOpp80rgV6GkN9wK7T11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGYcdE%2FdJMcaaLaibO%2FRALOpp80rgV6GkN9wK7T11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1844&quot; height=&quot;356&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Google Flow를 이용하여 만든 맵 배경이미지들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/valzg/dJMcafZZgEL/Ih685uqZqMHQVBg5NEaWH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/valzg/dJMcafZZgEL/Ih685uqZqMHQVBg5NEaWH1/img.png&quot; data-alt=&quot;맵 배치에 들어갈 오브젝트들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/valzg/dJMcafZZgEL/Ih685uqZqMHQVBg5NEaWH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvalzg%2FdJMcafZZgEL%2FIh685uqZqMHQVBg5NEaWH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1242&quot; height=&quot;684&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;684&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;맵 배치에 들어갈 오브젝트들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 AI프롬프트입니다.&lt;/p&gt;
&lt;h1&gt;킹덤러쉬 맵 배경 이미지 AI 프롬프트 가이드&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문서는 이미지 생성 AI(Midjourney, Stable Diffusion 등)를 사용하여 킹덤러쉬(Kingdom Rush) 스타일의 &lt;b&gt;타워 디펜스 맵 배경(아트워크)&lt;/b&gt;을 생성할 때 사용하는 프롬프트 지침입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  맵 디자인 핵심 가이드라인&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;비주얼 &amp;amp; 아트 스타일 (Visual &amp;amp; Art Style)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;스타일:&lt;/b&gt; 2D 탑다운 아이소메트릭(Top-down Isometric), 카툰 렌더링(Cartoony), 수작업 느낌(Hand-drawn style).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;선(Line):&lt;/b&gt; 기계적인 타일이나 격자(Grid) 형태를 피하고, 부드러운 외곽선(Smooth outlines)과 깔끔한 벡터(Vector graphic) 느낌 사용.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;색감:&lt;/b&gt; 화사하고 다채로운(Colorful, Vibrant) 색깔.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;구도 및 레벨 구조 (Composition &amp;amp; Level Design)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;길(Path):&lt;/b&gt; 유닛들이 이동하는 넓고 굽은 흙길(Winding dirt paths)과 자연스러운 곡선.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지형 병목:&lt;/b&gt; 길이 교차하는 지점(Crossroads)이나 자연스러운 병목 현상(Choke points)이 나타나도록 구성.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;배경 요소 (Background &amp;amp; Environment)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;배제 요소:&lt;/b&gt; (중요) 게임 내 타워가 배치될 공간이나 유닛의 스폰 위치 등은 인게임에서 처리할 예정이므로 이미지 생성 단계에서는 &lt;b&gt;건설 슬롯용 원형 마커, UI 요소, 스폰 포인트 마커 등을 철저히 배제&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;순수 배경:&lt;/b&gt; 유닛의 가시성을 방해하지 않는 깔끔한 환경(Clean UI-friendly background).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  상황별 통합 AI 프롬프트 (그대로 복사해서 사용)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵의 '길(Path)' 구조와 '테마(Theme)'가 모두 결합된 완성형 프롬프트입니다. 원하는 형태의 프롬프트를 복사하여 바로 사용하세요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 기본 단일 경로 (Single Path) - 평화로운 숲(들판) 테마&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A 2D top-down isometric view of a tower defense game map background, cartoony and hand-drawn style, clean vector graphic art, colorful and vibrant with smooth outlines. &lt;b&gt;A SINGLE, continuous, dynamic, winding dirt path entering from one edge of the canvas and exiting from another&lt;/b&gt;, no rigid grids. &lt;b&gt;Do NOT include any trees, forests, buildings, houses, farms, circular building slots, spawn points, or UI elements; keep it strictly as an empty natural terrain background.&lt;/b&gt; Natural choke points along the path. Surrounding environment filled with wide open green grassy plains, small wooden bridges, rocks and small bushes, clean UI-friendly background, Kingdom Rush art style --ar 16:9 --no UI, building slots, spawn points, markers, circles, grids, houses, buildings, farms, fences, trees, forests, woods, multiple paths, branching paths, intersections, crossroads, forks, shadows, drop shadows&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Y자형 분기/합류 (Split &amp;amp; Converging Paths) - 설산 테마&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A 2D top-down isometric view of a tower defense game map background, cartoony and hand-drawn style, clean vector graphic art, colorful and vibrant with smooth outlines. &lt;b&gt;Dirt paths that split into two separate routes early on and converge back into a single path near the end&lt;/b&gt;, no rigid grids. &lt;b&gt;Do NOT include any trees, forests, buildings, houses, farms, circular building slots, spawn points, or UI elements; keep it strictly as an empty natural terrain background.&lt;/b&gt; A strong natural crossroads choke point where paths meet. Surrounding environment filled with a snowy mountain pass, frozen rivers, and icy caves, cold blue and white tones, clean UI-friendly background, Kingdom Rush art style --ar 16:9 --no UI, building slots, spawn points, markers, circles, grids, houses, buildings, farms, fences, trees, forests, pine trees, shadows, drop shadows&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 다중 입구 및 독립된 경로 (Multiple Entry Points &amp;amp; Independent Paths) - 화산/황무지 테마&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A 2D top-down isometric view of a tower defense game map background, cartoony and hand-drawn style, clean vector graphic art, colorful and vibrant with smooth outlines. &lt;b&gt;Multiple independent dirt paths starting from different edge entry points and leading to completely different areas or converging late&lt;/b&gt;, no rigid grids. &lt;b&gt;Do NOT include any trees, forests, buildings, houses, farms, circular building slots, spawn points, or UI elements; keep it strictly as an empty natural terrain background.&lt;/b&gt; Surrounding environment filled with a volcanic wasteland, cracked dark earth, lava streams, and craters, dark red and grey tones, ominous atmosphere, clean UI-friendly background, Kingdom Rush art style --ar 16:9 --no UI, building slots, spawn points, markers, circles, grids, houses, buildings, farms, fences, trees, forests, dead trees, shadows, drop shadows&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 우회로 및 던전 터널 포함 (Paths with Hidden Tunnels/Bypass) - 해안/항구 테마&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A 2D top-down isometric view of a tower defense game map background, cartoony and hand-drawn style, clean vector graphic art, colorful and vibrant with smooth outlines. &lt;b&gt;Dynamic winding dirt paths where a section of the path visually dips underground into a hidden tunnel or cave, emerging further down the route&lt;/b&gt;, no rigid grids. &lt;b&gt;Do NOT include any trees, forests, buildings, houses, farms, circular building slots, spawn points, or UI elements; keep it strictly as an empty natural terrain background.&lt;/b&gt; Surrounding environment filled with tropical coastal beaches, wooden docks, abandoned pirate ships, and clear blue water, clean UI-friendly background, Kingdom Rush art style --ar 16:9 --no UI, building slots, spawn points, markers, circles, grids, houses, buildings, farms, fences, trees, palm trees, forests, shadows, drop shadows&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 맵 오브젝트(Props) 개별 생성 프롬프트 (건물, 바위, 나무 등)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 배경 맵 생성 프롬프트와 조합하여 &lt;b&gt;에디터에서 자유롭게 배치할 개별 맵 오브젝트 장식물(Props)&lt;/b&gt;을 생성하는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배경을 투명화(누끼)하기 쉽도록 &lt;b&gt;순백색 배경(Solid white background)&lt;/b&gt;에 오브젝트만 생성하는 것이 핵심입니다. 다양한 오브젝트를 하나의 시트(Sprite Sheet) 형태로 출력해서 잘라서 쓰는 것을 추천합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;특징 및 가이드&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시점 일치:&lt;/b&gt; 배경 맵과 시점을 동일하게 맞추기 위해 &lt;code&gt;2D top-down isometric view&lt;/code&gt; 키워드는 유지합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;분리 용이성:&lt;/b&gt; &lt;code&gt;isolated on a solid white background&lt;/code&gt; 키워드 설정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;그림자 제거:&lt;/b&gt; 그림자가 겹치는 문제를 막고 투명화(누끼) 작업 시 깔끔하게 잘라내기 위해 그림자를 완전히 제거합니다. 유니티 엔진 내에서 별도의 방식으로 그림자를 처리하는 것이 효율적입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  프롬프트: 다양한 마을 건물/집 (House &amp;amp; Buildings)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A collection of separated 2D game props, top-down isometric view. &lt;b&gt;[테마 입력: e.g., Medieval fantasy small wooden houses, farmers thatched cottages, small barns]&lt;/b&gt;. Cartoony and hand-drawn style, clean vector graphic art, colorful and vibrant with smooth outlines. &lt;b&gt;Isolated on a solid pure white background&lt;/b&gt;. Arranged neatly as a sprite sheet, no overlapping, Kingdom Rush art style --ar 16:9 --no shadows, drop shadows, background, environment, terrain, paths&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  프롬프트: 자연물 장식 (Trees, Rocks, Bushes)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A collection of separated 2D game props, top-down isometric view. &lt;b&gt;[테마 입력: e.g., Pine trees of various sizes, large mossy boulders, berry bushes, small wooden fences]&lt;/b&gt;. Cartoony and hand-drawn style, clean vector graphic art, colorful and vibrant with smooth outlines. &lt;b&gt;Isolated on a solid pure white background&lt;/b&gt;. Arranged neatly as a sprite sheet, no overlapping, Kingdom Rush art style --ar 16:9 --no shadows, drop shadows, background, environment, terrain, paths&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  킹덤러쉬 방식 강력 추천 가이드: &quot;통짜 배경 + 개별 프롭 배치&quot; (Hybrid Method)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;킹덤러쉬처럼 자연스럽고 역동적인 길을 표현하려면, 조각난 타일맵(Tilemap)이나 마름모 형태의 깍두기 아이소메트릭 블록 방식을 사용하는 것은 지양해야 합니다. 타일 방식으로 길을 이으면 특유의 둥근 느낌이 죽고 뚝뚝 끊기며 반복적인 패턴이 보이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 빠르고 퀄리티 높은 레벨 디자인 방법은 다음과 같습니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;지형/길 생성:&lt;/b&gt; &lt;code&gt;1~4번&lt;/code&gt; 프롬프트를 사용해 나무나 건물이 없는 &quot;굽은 길이 포함된 맵 전체 배경 1장(통짜)&quot;을 생성합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사전 보정 (Optional):&lt;/b&gt; 생성된 배경 이미지에서 길과 너무 가깝거나 타워 슬롯을 가리는 방해물(바위 등)이 섞여나왔다면, 포토샵 도장이나 AI 지우개 기능으로 살짝 지워 잔디밭으로 덮습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;개별 오브젝트(Props) 준비:&lt;/b&gt; 위의 &lt;code&gt;  건물&lt;/code&gt; 및 &lt;code&gt;  자연물 장식&lt;/code&gt; 프롬프트로 유닛을 가리거나 배치될 &lt;b&gt;집, 큰 나무, 바위 세트&lt;/b&gt;를 흰 배경으로 뽑고 누끼를 따서 스프라이트로 만듭니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;유니티에서 조립:&lt;/b&gt; 유니티 씬에 &quot;통짜 배경 이미지&quot; 1장을 맵 베이스로 깔고, 그 위에 &quot;개별 오브젝트(Props)&quot;들을 얹습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;적들이 이 오브젝트 뒤로 걸어갈 때 완벽한 뎁스(Z-Sorting) 효과를 줄 수 있습니다.&lt;/li&gt;
&lt;li&gt;타워 건설 슬롯 공간을 철저하게 통제할 수 있습니다.&lt;/li&gt;
&lt;li&gt;다채로운 에셋 재활용이 가능해집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>AI/Unity</category>
      <category>Antigravity</category>
      <category>google flow</category>
      <category>디펜스만들기</category>
      <category>타워디펜스 만들기</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/88</guid>
      <comments>https://blacknabis.tistory.com/88#entry88comment</comments>
      <pubDate>Sun, 1 Mar 2026 17:19:22 +0900</pubDate>
    </item>
    <item>
      <title>타워디펜스 개발일지 2026_03_01</title>
      <link>https://blacknabis.tistory.com/87</link>
      <description>&lt;h1&gt;DevLog_2026_03_01&lt;/h1&gt;
&lt;h2&gt;오늘의 목표&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;웨이브/경로 연계 이슈 해결&lt;/li&gt;
&lt;li&gt;결과 판정(UI 문구) 충돌 해결&lt;/li&gt;
&lt;li&gt;null 체크 스타일을 &lt;code&gt;IsNull()/IsNotNull()&lt;/code&gt;로 통일&lt;/li&gt;
&lt;li&gt;진행 문서 최신화&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;진행한 작업&lt;/h2&gt;
&lt;h3&gt;1) 웨이브 로딩/경로 연계 확인&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;게임씬 단독 실행 시 기본 웨이브 로드 경로를 확인했다.&lt;/li&gt;
&lt;li&gt;현재 기본은 &lt;code&gt;Resources/Kingdom/Configs/Waves/Stage_1_WaveConfig&lt;/code&gt; 기준으로 동작한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Path0&lt;/code&gt;, &lt;code&gt;Path1&lt;/code&gt; 구조와 &lt;code&gt;GameBattlefield&lt;/code&gt; 경로 파싱 기준을 점검했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2) WaveIndex 중복 데이터 처리 버그 수정&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;기존 &lt;code&gt;WaveManager&lt;/code&gt;가 &lt;code&gt;WaveData.WaveIndex&lt;/code&gt;를 무시하고 &lt;code&gt;Waves&lt;/code&gt; 리스트 순번으로만 실행하던 문제를 확인했다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WaveIndex&lt;/code&gt; 기준으로 논리 웨이브를 런타임 병합하도록 수정했다.&lt;/li&gt;
&lt;li&gt;같은 &lt;code&gt;WaveIndex=1&lt;/code&gt;에 &lt;code&gt;PathId=0/1&lt;/code&gt;이 있어도 동일 웨이브에서 스폰되도록 보정했다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameScene&lt;/code&gt;의 총 웨이브 수도 &lt;code&gt;Waves.Count&lt;/code&gt;가 아니라 논리 웨이브 수 기준으로 계산하도록 수정했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3) PathPoints 레거시 제거 반영&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;전장 경로 주입을 &lt;code&gt;GameBattlefield.GetPathPointsById()&lt;/code&gt; + &lt;code&gt;PathManager.SetPaths(...)&lt;/code&gt; 기준으로 유지하도록 정리했다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PathPoints&lt;/code&gt; 레거시 사용/폴백 의존 제거 방향을 코드/작업 흐름에 반영했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4) 결과 판정 문구 충돌 이슈 보정&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;방어선 붕괴 상황에서 Victory 문구가 함께 보이던 케이스를 분기 로직으로 보정했다.&lt;/li&gt;
&lt;li&gt;패배 시 Defeat 문구만 보이도록 정리했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5) null 체크 규칙 일괄 정리&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;요청 기준에 맞춰 &lt;code&gt;Kingdom/Common&lt;/code&gt; 코드의 null 체크를 &lt;code&gt;IsNull()/IsNotNull()&lt;/code&gt; 스타일로 통일 적용했다.&lt;/li&gt;
&lt;li&gt;변환 과정에서 발생한 컴파일 에러(문자열 깨짐/nullable bool 식)들을 개별 복구했다.&lt;/li&gt;
&lt;li&gt;대표 복구 파일: &lt;code&gt;SkillTreeUI&lt;/code&gt;, &lt;code&gt;WorldMapView&lt;/code&gt;, &lt;code&gt;AISpriteProcessor&lt;/code&gt;, &lt;code&gt;BossEventSystem&lt;/code&gt;, &lt;code&gt;HeroSelectionUI&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6) 문서 정리&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;를 최신 작업 기준으로 재정리했다.&lt;/li&gt;
&lt;li&gt;진행 폴더 운영 기준 문서 &lt;code&gt;문서/진행/README.md&lt;/code&gt;를 추가했다.&lt;/li&gt;
&lt;li&gt;사용자 확인 완료(1/2/3) 항목을 task 문서에 체크 반영했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;검증&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj&lt;/code&gt; 기준 컴파일 에러 0 확인.&lt;/li&gt;
&lt;li&gt;플레이 검증(사용자 확인 완료):&lt;ol&gt;
&lt;li&gt;Stage 1 Wave 1에서 Path0/Path1 동작 확인&lt;/li&gt;
&lt;li&gt;웨이브 전이/결과 상태 전이 확인&lt;/li&gt;
&lt;li&gt;패배 시 Defeat 표기 정상 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;오늘의 결과&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;멀티 경로 웨이브 스폰이 데이터 의도대로 동작하도록 안정화 완료.&lt;/li&gt;
&lt;li&gt;결과 UI 판정 충돌 이슈 해결.&lt;/li&gt;
&lt;li&gt;null 체크 스타일 통일 적용 및 컴파일 안정화 완료.&lt;/li&gt;
&lt;li&gt;진행 문서 최신 상태 유지.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI/잡다한개발노트</category>
      <category>Devlog</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/87</guid>
      <comments>https://blacknabis.tistory.com/87#entry87comment</comments>
      <pubDate>Sun, 1 Mar 2026 03:16:23 +0900</pubDate>
    </item>
    <item>
      <title>전투방식 단순화 개발기록 정리본 (2026-02-28)</title>
      <link>https://blacknabis.tistory.com/86</link>
      <description>&lt;h1&gt;전투방식 단순화 개발기록 정리본 (2026-02-28)&lt;/h1&gt;
&lt;h2&gt;1. 목적과 범위&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;전투 규칙을 단순화한다.&lt;ul&gt;
&lt;li&gt;범위 안에 적이 있으면 공격&lt;/li&gt;
&lt;li&gt;범위 밖이면 이동&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;대상 유닛: Enemy, Hero, Barracks Soldier&lt;/li&gt;
&lt;li&gt;현재 단계: 스테이지 기본 테스트 기준 (밸런스 조정 제외)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2. 현재 최종 규칙&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;공격 데미지는 공격 애니메이션 종료 시점에 적용된다.&lt;/li&gt;
&lt;li&gt;공격 중에는 이동하지 않는다.&lt;/li&gt;
&lt;li&gt;공격 상태에서 다음 상태(추적/이동/유지)로 전환은 공격 애니메이션 종료 후에만 가능하다.&lt;/li&gt;
&lt;li&gt;유닛 분리(밀어내기)는 같은 팀끼리만 적용한다.&lt;ul&gt;
&lt;li&gt;Enemy ↔ Enemy 적용&lt;/li&gt;
&lt;li&gt;Ally(Hero/Soldier) ↔ Ally(Hero/Soldier) 적용&lt;/li&gt;
&lt;li&gt;Enemy ↔ Ally 미적용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. 변경 이력 요약&lt;/h2&gt;
&lt;h3&gt;3.1 기반 리팩토링&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;BaseUnit 공통 전투 루프/헬퍼를 중심으로 구조 정리&lt;/li&gt;
&lt;li&gt;이동 stop distance, 팀 분리 보정 유틸 도입&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.2 간격/겹침 보정&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;유닛 간격 상향(2배 포함)&lt;/li&gt;
&lt;li&gt;적대 팀 간 간격 처리 공통화(이후 동일 팀 전용으로 최종 조정)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.3 전투 안정화&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;제자리 공격 고정&lt;/li&gt;
&lt;li&gt;공격 불가 시 이동 전환&lt;/li&gt;
&lt;li&gt;적 타겟 획득/유지 범위 보정&lt;/li&gt;
&lt;li&gt;전투 거리 판정 2D(XY) 통일&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.4 적 공격 이슈 추적&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;EnemyTrace/EnemyAttack 로그 보강&lt;/li&gt;
&lt;li&gt;적 공격 애니메이션 상태 유지 보정&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.5 공격 타이밍 규칙 적용&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Enemy/Hero/Soldier 모두 &lt;code&gt;공격 시작 -&amp;gt; 애니메이션 종료 -&amp;gt; 데미지 확정&lt;/code&gt; 구조로 변경&lt;/li&gt;
&lt;li&gt;공격 대기 상태(_isAttackPending) 중 이동 차단&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.6 분리 정책 최종화&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;BaseUnit 분리 로직에서 적대 팀 push 제거&lt;/li&gt;
&lt;li&gt;같은 팀 그룹에만 분리 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. 핵심 수정 파일&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/BaseUnit.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/EnemyRuntime.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/HeroController.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/BarracksSoldierRuntime.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/TowerManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/Data/EnemyConfig.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/Data/HeroConfig.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/Data/TowerConfig.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Resources/Prefabs/Game/GameBattlefield.prefab&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;5. 검증 상태&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt; 오류 0&lt;/li&gt;
&lt;li&gt;빌드 경고는 기존 항목(&lt;code&gt;MSB3277&lt;/code&gt;, &lt;code&gt;CS0649&lt;/code&gt;) 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;6. 현재 남은 이슈&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;일부 상황에서 공격 애니메이션 체감 타이밍이 의도보다 짧거나 어색할 수 있음&lt;/li&gt;
&lt;li&gt;다음 점검 포인트:&lt;ul&gt;
&lt;li&gt;유닛별 공격 클립 길이 vs &lt;code&gt;_attackVisualHoldLeft&lt;/code&gt; 정합&lt;/li&gt;
&lt;li&gt;공격 종료 직후 상태 전환(Idle/Move) 체감&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;7. 원본 기록 안내&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;상세 원본: &lt;code&gt;문서/진행/전투방식_단순화_개발기록_2026_02_27.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;본 문서는 회의/리뷰용 요약 정리본이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;8. 최종 점검 체크리스트&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;공격 중 이동 금지: 공격 애니메이션 재생 중 위치가 고정되는지 확인&lt;/li&gt;
&lt;li&gt;타격 시점: 데미지 숫자/HP 감소가 공격 애니메이션 종료 시점과 일치하는지 확인&lt;/li&gt;
&lt;li&gt;상태 전환: 공격 종료 전 이동 상태로 튀지 않는지 확인&lt;/li&gt;
&lt;li&gt;분리 정책: 같은 팀끼리만 분리되고, 적/아군 간에는 밀어내기가 없는지 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;점검 결과&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;확인 일자: 2026-02-28&lt;/li&gt;
&lt;li&gt;상태: 사용자 실플레이 확인 완료&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI/잡다한개발노트</category>
      <category>Devlog</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/86</guid>
      <comments>https://blacknabis.tistory.com/86#entry86comment</comments>
      <pubDate>Sat, 28 Feb 2026 04:32:11 +0900</pubDate>
    </item>
    <item>
      <title>전투방식 단순화 개발기록 (2026-02-27)</title>
      <link>https://blacknabis.tistory.com/85</link>
      <description>&lt;h1&gt;전투방식 단순화 개발기록 (2026-02-27)&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;정리본: &lt;code&gt;문서/진행/전투방식_단순화_개발기록_정리본_2026_02_28.md&lt;/code&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;1) 작업 목적&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;전투/이동 중 유닛이 동일 좌표에 겹치는 현상 제거&lt;/li&gt;
&lt;li&gt;기존 단순 전투 규칙(범위 내 공격, 범위 밖 이동) 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2) 반영 내용&lt;/h2&gt;
&lt;h3&gt;공통(BaseUnit)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;팀 내부 유닛 간 분리 보정 유틸 추가&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ApplyTeamSeparation(float deltaTime, float separationRadius, float pushSpeed)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;동일 좌표(거리 0)에서도 분리되도록 fallback 방향 처리 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;정지 거리 기반 이동 헬퍼 추가&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MoveTowardsWithStopDistance(Vector3 from, Vector3 target, float maxDistanceDelta, float stopDistance)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;EnemyRuntime&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;타깃 근접 시 중심점까지 겹치지 않도록 정지 거리 적용&lt;/li&gt;
&lt;li&gt;전투 중/이동 중 팀 분리 보정 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;HeroController&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;적 추적 이동 시 정지 거리 적용(과도한 겹침 방지)&lt;/li&gt;
&lt;li&gt;전투 중/이동 중 팀 분리 보정 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;BarracksSoldierRuntime&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;적 추적 이동 시 정지 거리 적용&lt;/li&gt;
&lt;li&gt;전투 중/이동 중 팀 분리 보정 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3) 수정 파일&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/BaseUnit.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/EnemyRuntime.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/HeroController.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/BarracksSoldierRuntime.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/전투방식_단순화_원샷리팩토링_플랜_2026_02_27.md&lt;/code&gt; (선행 작업 섹션 추가)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4) 검증 결과&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;Unity Play Mode 콘솔:&lt;ul&gt;
&lt;li&gt;Error 0&lt;/li&gt;
&lt;li&gt;Warning 0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;5) 비고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;밸런스 수치(데미지/쿨타임)는 변경하지 않음&lt;/li&gt;
&lt;li&gt;분리 보정은 팀 내부 기준으로 우선 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;6) 추가 작업 (다음 단계 반영)&lt;/h2&gt;
&lt;h3&gt;EnemyRuntime 레거시 모션 상태 제거&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;EnemyMotionState&lt;/code&gt;에서 &lt;code&gt;Blocked&lt;/code&gt;, &lt;code&gt;Engaged&lt;/code&gt; 제거&lt;/li&gt;
&lt;li&gt;애니메이션 프레임 선택 로직 단순화&lt;ul&gt;
&lt;li&gt;이동: &lt;code&gt;Moving&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;공격: &lt;code&gt;Attacking&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;사망: &lt;code&gt;Dead&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;애니메이터 파라미터 매핑은 유지&lt;ul&gt;
&lt;li&gt;Walk=1, Attack=2, Die=3&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;Unity Play Mode 콘솔:&lt;ul&gt;
&lt;li&gt;Error 0&lt;/li&gt;
&lt;li&gt;Warning 0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;9) 추가 작업 (겹침 스모크 로그 상세화)&lt;/h2&gt;
&lt;h3&gt;회귀 로그 보강&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RunBarracksMeleeSmokeRegressionAsync()&lt;/code&gt;에 겹침 최초 샘플 기록 추가&lt;ul&gt;
&lt;li&gt;&lt;code&gt;firstOverlapAtSec&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;firstOverlapPairCount&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;firstOverlapMinPairDistance&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CaptureOverlapSample(...)&lt;/code&gt; 로컬 함수로 최초 겹침 시점/지표를 한 번만 캡처&lt;/li&gt;
&lt;li&gt;겹침 실패 시 즉시 원인 로그 출력&lt;ul&gt;
&lt;li&gt;&lt;code&gt;maxPairs&lt;/code&gt;, &lt;code&gt;firstAt&lt;/code&gt;, &lt;code&gt;firstPairs&lt;/code&gt;, &lt;code&gt;firstMinDist&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;종료 요약 로그에 최초 겹침 정보 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;Unity Play Mode 콘솔:&lt;ul&gt;
&lt;li&gt;Error 0&lt;/li&gt;
&lt;li&gt;Warning 0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;8) 추가 작업 (Separation 파라미터 설정 외부화)&lt;/h2&gt;
&lt;h3&gt;데이터 스키마 확장&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;EnemyConfig&lt;/code&gt; 추가 필드&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SeparationRadius&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SeparationPushSpeed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ChaseStopDistance&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroConfig&lt;/code&gt; 추가 필드&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SeparationRadius&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SeparationPushSpeed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ChaseStopDistance&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerConfig.BarracksData&lt;/code&gt; 추가 필드&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SoldierSeparationRadius&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SoldierSeparationPushSpeed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SoldierChaseStopDistance&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;런타임 반영&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime&lt;/code&gt;는 separation/stop 거리 값을 &lt;code&gt;EnemyConfig&lt;/code&gt;에서 우선 사용, 미설정 시 기본값 fallback&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroController&lt;/code&gt;는 separation/stop 거리 값을 &lt;code&gt;HeroConfig&lt;/code&gt;에서 우선 사용, 미설정 시 기본값 fallback&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BarracksSoldierRuntime&lt;/code&gt;는 &lt;code&gt;UpdateMovementSettings(...)&lt;/code&gt;로 이동 보정 파라미터를 주입받도록 확장&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerManager&lt;/code&gt;에서 병사 생성/스탯 갱신 시 병사 이동 보정 파라미터를 설정값으로 주입&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerManager&lt;/code&gt; 정규화 단계에서 배럭 이동 보정 값 미설정 시 기본값 채움&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;Unity Play Mode 콘솔:&lt;ul&gt;
&lt;li&gt;Error 0&lt;/li&gt;
&lt;li&gt;Warning 0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;7) 추가 작업 (겹침 방지 스모크 assert 연동)&lt;/h2&gt;
&lt;h3&gt;TowerManager 디버그 지표 확장&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;BarracksDebugInfo&lt;/code&gt;에 겹침 검증용 필드 추가&lt;ul&gt;
&lt;li&gt;&lt;code&gt;OverlapPairCount&lt;/code&gt;: 일정 임계거리 이하로 포개진 병사 페어 수&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MinAlivePairDistance&lt;/code&gt;: 생존 병사 페어 최소 거리 (페어 없으면 &lt;code&gt;-1&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;배럭 디버그 계산 시 생존 병사 쌍 거리 기반으로 값 집계&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;KingdomAppManager 스모크 회귀 조건 강화&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RunBarracksMeleeSmokeRegressionAsync()&lt;/code&gt;에 겹침 조건 추가&lt;ul&gt;
&lt;li&gt;테스트 중 &lt;code&gt;maxOverlapPairCount&lt;/code&gt; 추적&lt;/li&gt;
&lt;li&gt;종료 판정 시 &lt;code&gt;maxOverlapPairCount &amp;lt;= 0&lt;/code&gt;일 때만 성공 카운트&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;결과 로그에 &lt;code&gt;maxOverlapPairs&lt;/code&gt;, &lt;code&gt;minPairDist&lt;/code&gt; 출력 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;Unity Play Mode 콘솔:&lt;ul&gt;
&lt;li&gt;Error 0&lt;/li&gt;
&lt;li&gt;Warning 0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;10) 추가 작업 (유닛 간격 확대)&lt;/h2&gt;
&lt;h3&gt;변경 목표&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;유닛 간 최소 간격을 더 넓혀 겹침 가능성을 줄임&lt;/li&gt;
&lt;li&gt;기존 Config/에셋 값이 낮아도 런타임에서 최소 분리 반경을 강제&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;코드 반영&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;EnemyConfig.SeparationRadius&lt;/code&gt; 기본값: &lt;code&gt;0.58f&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroConfig.SeparationRadius&lt;/code&gt; 기본값: &lt;code&gt;0.62f&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime.DefaultTeamSeparationRadius&lt;/code&gt;: &lt;code&gt;0.58f&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroController.DefaultTeamSeparationRadius&lt;/code&gt;: &lt;code&gt;0.62f&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BarracksSoldierRuntime.DefaultTeamSeparationRadius&lt;/code&gt;: &lt;code&gt;0.58f&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime.ResolveSeparationRadius()&lt;/code&gt;에서 &lt;code&gt;Mathf.Max(DefaultTeamSeparationRadius, _config.SeparationRadius)&lt;/code&gt; 적용&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroController.ResolveSeparationRadius()&lt;/code&gt;에서 &lt;code&gt;Mathf.Max(DefaultTeamSeparationRadius, heroConfig.SeparationRadius)&lt;/code&gt; 적용&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BarracksSoldierRuntime.UpdateMovementSettings()&lt;/code&gt;에서 separation radius 최소값 클램프 적용&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerManager&lt;/code&gt; 병영 기본/해석값 상향&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PopulateDefaultData&lt;/code&gt;: &lt;code&gt;SoldierSeparationRadius = 0.58f&lt;/code&gt; (fallback)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ResolveBarracksSeparationRadius&lt;/code&gt;: &lt;code&gt;Mathf.Max(0.58f, config.BarracksData.SoldierSeparationRadius)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;기존 경고(&lt;code&gt;MSB3277&lt;/code&gt;, &lt;code&gt;CS0649&lt;/code&gt;)는 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;11) 추가 작업 (유닛 간격 2배 상향)&lt;/h2&gt;
&lt;h3&gt;변경 목표&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;직전 상향값 대비 유닛 간격을 약 2배로 확대하여 겹침 가능성을 추가로 낮춤&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;코드 반영&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;EnemyConfig.SeparationRadius&lt;/code&gt;: &lt;code&gt;0.58f -&amp;gt; 1.16f&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroConfig.SeparationRadius&lt;/code&gt;: &lt;code&gt;0.62f -&amp;gt; 1.24f&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime.DefaultTeamSeparationRadius&lt;/code&gt;: &lt;code&gt;0.58f -&amp;gt; 1.16f&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroController.DefaultTeamSeparationRadius&lt;/code&gt;: &lt;code&gt;0.62f -&amp;gt; 1.24f&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BarracksSoldierRuntime.DefaultTeamSeparationRadius&lt;/code&gt;: &lt;code&gt;0.58f -&amp;gt; 1.16f&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerManager.BarracksSoldierIdleRadius&lt;/code&gt;: &lt;code&gt;0.58f -&amp;gt; 1.16f&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerManager&lt;/code&gt; 병영 separation fallback/min clamp: &lt;code&gt;0.58f -&amp;gt; 1.16f&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;기존 경고(&lt;code&gt;MSB3277&lt;/code&gt;, &lt;code&gt;CS0649&lt;/code&gt;)는 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;12) 추가 작업 (적대 팀 간 최소 간격 공통화)&lt;/h2&gt;
&lt;h3&gt;변경 이유&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;팀 내부 간격은 넓어졌지만, 아군(영웅/병사)과 적의 근접 거리가 여전히 가깝게 보이는 문제 보완&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;코드 반영&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;BaseUnit.ApplyTeamSeparation(...)&lt;/code&gt;를 공통 확장&lt;/li&gt;
&lt;li&gt;기존: 같은 팀(&lt;code&gt;other._team == _team&lt;/code&gt;)만 분리&lt;/li&gt;
&lt;li&gt;변경: 같은 팀 + 적대 팀(&lt;code&gt;IsHostileTo(other)&lt;/code&gt;) 모두 분리 계산&lt;/li&gt;
&lt;li&gt;적대 팀 분리는 별도 공통 훅으로 제어&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ResolveHostileSeparationRadius(float friendlySeparationRadius)&lt;/code&gt; 추가 (기본값: &lt;code&gt;max(0.38f, friendly * 0.45f)&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ResolveHostileSeparationWeight()&lt;/code&gt; 추가 (기본값: &lt;code&gt;0.85f&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;파생 클래스에서 필요 시 오버라이드 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;기대 효과&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;적-적, 아군-아군뿐 아니라 아군-적 간에도 최소 간격이 유지되어 붙어 보이는 현상 완화&lt;/li&gt;
&lt;li&gt;분리 로직은 &lt;code&gt;BaseUnit&lt;/code&gt;에 공통 집중, 파생 클래스는 호출만 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;기존 경고(&lt;code&gt;MSB3277&lt;/code&gt;, &lt;code&gt;CS0649&lt;/code&gt;)는 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;13) 추가 작업 (전투 중 제자리 공격 고정)&lt;/h2&gt;
&lt;h3&gt;변경 이유&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;전투 진입 후에도 분리 보정/미세 이동이 적용되어 밀리는 느낌으로 비정상 방향 이동이 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;코드 반영&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime.HandleTargetInRange(...)&lt;/code&gt;에서 전투 중 추적 이동 제거&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime.HandleTargetInRange(...)&lt;/code&gt;/&lt;code&gt;HandleTargetInRangeWithoutAttack(...)&lt;/code&gt;에서 분리 보정 호출 제거&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroController.HandleTargetInRange(...)&lt;/code&gt;/&lt;code&gt;HandleTargetInRangeWithoutAttack(...)&lt;/code&gt;에서 분리 보정 호출 제거&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BarracksSoldierRuntime.HandleTargetInRange(...)&lt;/code&gt;/&lt;code&gt;HandleTargetInRangeWithoutAttack(...)&lt;/code&gt;에서 분리 보정 호출 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;동작 기준&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;타겟이 사거리 안(&lt;code&gt;in-range&lt;/code&gt;)이면 제자리 공격&lt;/li&gt;
&lt;li&gt;타겟이 사거리 밖이면 기존처럼 추적/이동 로직 수행&lt;/li&gt;
&lt;li&gt;분리 보정은 이동/추적 단계에서만 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;기존 경고(&lt;code&gt;MSB3277&lt;/code&gt;, &lt;code&gt;CS0649&lt;/code&gt;)는 유지&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime&lt;/code&gt; 전투 중 모션 상태를 &lt;code&gt;Attacking&lt;/code&gt;으로 고정 (사거리 내 정지 공격 시 &lt;code&gt;Moving&lt;/code&gt; 모션 잔존 방지)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;14) 추가 작업 (공격 불가 시 이동 전환)&lt;/h2&gt;
&lt;h3&gt;변경 이유&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;전투 중 &lt;code&gt;in-range&lt;/code&gt; 상태에서 공격이 불가능한 동안(주로 쿨다운) 가만히 맞고만 있는 현상 보완&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;코드 반영&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime.HandleTargetInRangeWithoutAttack(...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;기존: 제자리(Attacking 상태 유지)&lt;/li&gt;
&lt;li&gt;변경: &lt;code&gt;MoveAlongPath(deltaTime)&lt;/code&gt; 호출로 이동 우선&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroController.HandleTargetInRangeWithoutAttack(...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;기존: 제자리 시각 상태 유지&lt;/li&gt;
&lt;li&gt;변경: &lt;code&gt;HandleNoTargetOrOutOfRange(target, deltaTime)&lt;/code&gt; 호출로 추적/이동 로직 재사용&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BarracksSoldierRuntime.HandleTargetInRangeWithoutAttack(...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;기존: Blocking 상태 고정&lt;/li&gt;
&lt;li&gt;변경: &lt;code&gt;HandleNoTargetOrOutOfRange(target, deltaTime)&lt;/code&gt; 호출로 추적/이동 로직 재사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;동작 기준&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;사거리 내 + 공격 가능: 제자리 공격&lt;/li&gt;
&lt;li&gt;사거리 내 + 공격 불가: 이동 로직으로 전환&lt;/li&gt;
&lt;li&gt;사거리 밖: 기존 이동/추적 로직 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;기존 경고(&lt;code&gt;MSB3277&lt;/code&gt;, &lt;code&gt;CS0649&lt;/code&gt;)는 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;15) 추가 작업 (적 공격 불가 구간 타겟 유지 이동)&lt;/h2&gt;
&lt;h3&gt;변경 이유&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime&lt;/code&gt;에서 공격 불가(in-range) 시 경로 이동으로 복귀하여 타겟과 이탈, 공격이 끊기는 현상 보완&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;코드 반영&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime.HandleTargetInRangeWithoutAttack(...)&lt;/code&gt; 수정&lt;/li&gt;
&lt;li&gt;기존: &lt;code&gt;MoveAlongPath(deltaTime)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;변경: 타겟 기준 &lt;code&gt;MoveTowardsWithStopDistance(...)&lt;/code&gt;로 접근 이동&lt;/li&gt;
&lt;li&gt;&lt;code&gt;target == null&lt;/code&gt;일 때만 경로 이동 fallback 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;기대 효과&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;적이 쿨다운 중에도 전투 타겟 근처를 유지하며, 쿨다운 종료 시 공격이 다시 정상 이어짐&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;기존 경고(&lt;code&gt;MSB3277&lt;/code&gt;, &lt;code&gt;CS0649&lt;/code&gt;)는 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;16) 추가 작업 (전투 사거리 판정 2D 통일)&lt;/h2&gt;
&lt;h3&gt;변경 이유&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;적/아군의 &lt;code&gt;z&lt;/code&gt; 오프셋(예: &lt;code&gt;0&lt;/code&gt; vs &lt;code&gt;-0.5&lt;/code&gt;)이 거리 계산에 포함되어 사거리 판정이 과도하게 좁아짐&lt;/li&gt;
&lt;li&gt;결과적으로 타겟 근처에서도 공격 루프 진입/유지가 불안정해지는 문제 보완&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;코드 반영&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;BaseUnit.FindNearestHostile(...)&lt;/code&gt; 거리 계산에서 &lt;code&gt;z=0&lt;/code&gt; 정규화&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime.IsCombatTargetInRange(...)&lt;/code&gt; 거리 계산에서 &lt;code&gt;z=0&lt;/code&gt; 정규화&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroController.IsCombatTargetInRange(...)&lt;/code&gt; 거리 계산에서 &lt;code&gt;z=0&lt;/code&gt; 정규화&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BarracksSoldierRuntime.IsCombatTargetInRange(...)&lt;/code&gt; 거리 계산에서 &lt;code&gt;z=0&lt;/code&gt; 정규화&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroController&lt;/code&gt;의 적 탐색/스킬 범위/leash 관련 거리 계산도 2D 기준으로 통일&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;기대 효과&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;시각적으로 붙어 있는 상태에서 사거리 판정이 안정화되어 적 공격 누락 감소&lt;/li&gt;
&lt;li&gt;유닛 z-layer 차이에 영향받지 않는 일관된 전투 판정&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;기존 경고(&lt;code&gt;MSB3277&lt;/code&gt;, &lt;code&gt;CS0649&lt;/code&gt;)는 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;17) 추가 작업 (적 타겟 획득/유지 안정화)&lt;/h2&gt;
&lt;h3&gt;변경 이유&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;적이 전투 중 타겟을 쉽게 놓치고 경로 이동으로 복귀해 공격이 끊기는 현상 보완&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;코드 반영&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime.ResolveCombatTarget()&lt;/code&gt; 개선&lt;/li&gt;
&lt;li&gt;공격 사거리 기반으로 &lt;code&gt;acquireRange&lt;/code&gt;, &lt;code&gt;keepRange&lt;/code&gt; 도입&lt;/li&gt;
&lt;li&gt;기존 타겟이 유지 범위 내면 유지, 벗어나면 재탐색&lt;/li&gt;
&lt;li&gt;재탐색 범위를 순수 공격 사거리보다 확장&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime.HandleNoTargetOrOutOfRange(...)&lt;/code&gt; 개선&lt;/li&gt;
&lt;li&gt;&lt;code&gt;target&lt;/code&gt;이 존재/유효하면 경로 복귀 대신 타겟 추적 이동&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime.HandleTargetInRangeWithoutAttack(...)&lt;/code&gt;는 공통 추적 함수 재사용&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MoveTowardsCombatTarget(BaseUnit target, float deltaTime)&lt;/code&gt; 헬퍼 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;기대 효과&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;적이 전투 타겟을 안정적으로 유지하며 쿨다운 이후 공격 루프를 다시 이어감&lt;/li&gt;
&lt;li&gt;순간적인 거리 흔들림으로 공격이 영구적으로 끊기는 현상 완화&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;기존 경고(&lt;code&gt;MSB3277&lt;/code&gt;, &lt;code&gt;CS0649&lt;/code&gt;)는 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;18) 추가 작업 (전투 로직 주석 보강)&lt;/h2&gt;
&lt;h3&gt;목적&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;최근 수정된 전투/타겟/거리 판정 로직의 의도를 코드에서 바로 이해할 수 있도록 핵심 주석 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;반영 내용&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;타겟 획득/유지 범위 상수 의도 주석&lt;/li&gt;
&lt;li&gt;타겟 유지 vs 재획득 분기 의도 주석&lt;/li&gt;
&lt;li&gt;2D(XY) 거리 판정 주석&lt;/li&gt;
&lt;li&gt;사거리 내 제자리 공격 정책 주석&lt;/li&gt;
&lt;li&gt;공격 불가 시 타겟 유지 이동 정책 주석&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BaseUnit&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FindNearestHostile(...)&lt;/code&gt;의 2D 거리 계산 의도 주석&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroController&lt;/code&gt;, &lt;code&gt;BarracksSoldierRuntime&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;사거리 판정에서 Z 무시 이유 주석&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;기존 경고(&lt;code&gt;MSB3277&lt;/code&gt;, &lt;code&gt;CS0649&lt;/code&gt;)는 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;19) 추가 작업 (적 공격 누락 방지 핫픽스)&lt;/h2&gt;
&lt;h3&gt;변경 이유&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;일부 상황에서 적이 타겟을 잡지 못해 공격 루프에 진입하지 못하는 문제 보완&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;코드 반영&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime.ResolveCombatTarget()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;타겟 필터를 문자열(&lt;code&gt;UnitType&lt;/code&gt;) 비교에서 타입 기반(&lt;code&gt;HeroController&lt;/code&gt;, &lt;code&gt;BarracksSoldierRuntime&lt;/code&gt;)으로 변경&lt;/li&gt;
&lt;li&gt;필터 탐색 실패 시 &lt;code&gt;FindNearestHostile(...)&lt;/code&gt; fallback 추가 (모든 적대 유닛 대상)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime.CanPerformCombatAttack(...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;target.IsAlive&lt;/code&gt; 검증 포함하여 공격 가능 조건 보강&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;기대 효과&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;적 타겟 획득 누락 케이스 감소&lt;/li&gt;
&lt;li&gt;필터 불일치로 인한 &amp;#39;적 공격 안함&amp;#39; 현상 완화&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;기존 경고(&lt;code&gt;MSB3277&lt;/code&gt;, &lt;code&gt;CS0649&lt;/code&gt;)는 유지&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime.ResolveCombatTarget()&lt;/code&gt;에 즉시 교전 반경(&lt;code&gt;attackRange * 1.05&lt;/code&gt;) 우선 타겟 선택 추가&lt;/li&gt;
&lt;li&gt;현재 타겟 유지 로직보다 가까운 근접 타겟을 우선 스위칭하여, 바로 옆 적대 유닛을 놓치지 않도록 보정&lt;/li&gt;
&lt;li&gt;적 타겟 획득/유지 최소 반경 상향&lt;/li&gt;
&lt;li&gt;&lt;code&gt;acquireRange&lt;/code&gt;: 최소 &lt;code&gt;2.8f&lt;/code&gt;, &lt;code&gt;keepRange&lt;/code&gt;: 최소 &lt;code&gt;3.2f&lt;/code&gt; 보장&lt;/li&gt;
&lt;li&gt;원거리 영웅 상대로도 적이 전투 진입 타겟을 놓치지 않도록 보정&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime.IsCombatTargetInRange(...)&lt;/code&gt;에 유효 사거리 여유(1.15x) 적용&lt;/li&gt;
&lt;li&gt;거리 경계값 흔들림으로 인한 공격 판정 누락 완화&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime.IsCombatTargetInRange(...)&lt;/code&gt; 유효 공격 사거리 최소값 추가&lt;/li&gt;
&lt;li&gt;&lt;code&gt;effectiveRange = max(1.8f, attackRange * 1.15f)&lt;/code&gt;로 보정하여 근접/밀집 전투에서 적 공격 발동 안정화&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;20) 추가 작업 (적 공격 디버그 로그 보강)&lt;/h2&gt;
&lt;h3&gt;변경 이유&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;적이 공격을 안 하는 것처럼 보인다&amp;quot; 이슈를 콘솔에서 즉시 판별할 수 있도록 적 공격 호출 로그를 강화&lt;/li&gt;
&lt;li&gt;기존 &lt;code&gt;[EnemyTrace]&lt;/code&gt;가 5초 제한이라 실제 첫 교전 시점 전에 종료될 수 있어 추적이 어려운 문제 보완&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;코드 반영&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/EnemyRuntime.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CombatTraceDurationSec&lt;/code&gt;: &lt;code&gt;5f -&amp;gt; 20f&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CombatTraceIntervalSec&lt;/code&gt;: &lt;code&gt;0.35f -&amp;gt; 0.5f&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;실제 공격 발생 시 항상 &lt;code&gt;[EnemyAttack]&lt;/code&gt; 로그 출력 추가&lt;ul&gt;
&lt;li&gt;attacker/target instance id, damage, cooldown 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;기대 효과&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;적이 실제로 &lt;code&gt;PerformCombatAttack&lt;/code&gt;에 진입하는지 즉시 확인 가능&lt;/li&gt;
&lt;li&gt;공격 진입과 무관하게 &amp;quot;안 때리는 것처럼 보이는&amp;quot; 체감 이슈를 로그 기반으로 분리 진단 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;기존 경고(&lt;code&gt;MSB3277&lt;/code&gt;, &lt;code&gt;CS0649&lt;/code&gt;)만 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;21) 추가 작업 (적 공격 애니메이션 상태 유지 보정)&lt;/h2&gt;
&lt;h3&gt;변경 이유&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;실제 공격은 정상 발생하지만, 쿨다운 프레임에서 이동 상태가 즉시 덮여 공격 애니메이션이 거의 보이지 않는 문제가 확인됨&lt;/li&gt;
&lt;li&gt;콘솔 근거: &lt;code&gt;[EnemyAttack]&lt;/code&gt; 로그는 연속 발생, 동시에 &lt;code&gt;[EnemyTrace] motion=Moving&lt;/code&gt; 비중이 높음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;코드 반영&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/EnemyRuntime.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;전투 상태(&lt;code&gt;_motionState&lt;/code&gt;)와 시각 상태를 분리&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ResolveVisualMotionState()&lt;/code&gt; 추가&lt;/li&gt;
&lt;li&gt;&lt;code&gt;_attackVisualHoldLeft &amp;gt; 0&lt;/code&gt;이면 시각 상태를 &lt;code&gt;Attacking&lt;/code&gt;으로 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TickVisualAnimation(...)&lt;/code&gt;&lt;ul&gt;
&lt;li&gt;Animator/스프라이트 모두 시각 상태(&lt;code&gt;visualState&lt;/code&gt;) 기준으로 모션 반영&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ShouldLoopCurrentAnimation()&lt;/code&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;_motionState&lt;/code&gt; 대신 현재 애니메이션 상태(&lt;code&gt;_animationState&lt;/code&gt;) 기준으로 루프 판정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HandleTargetInRangeWithoutAttack(...)&lt;/code&gt;&lt;ul&gt;
&lt;li&gt;쿨다운 중에도 공격 홀드가 남아있으면 모션을 &lt;code&gt;Attacking&lt;/code&gt;으로 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MoveTowardsCombatTarget(...)&lt;/code&gt;&lt;ul&gt;
&lt;li&gt;모션 상태를 강제로 &lt;code&gt;Moving&lt;/code&gt;으로 덮어쓰던 코드 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;기대 효과&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;적의 실제 타격 타이밍과 공격 애니메이션 노출 타이밍이 일치&lt;/li&gt;
&lt;li&gt;근접 교전에서 공격 후 모션이 즉시 걷기로 튀는 현상 완화&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;기존 경고(&lt;code&gt;MSB3277&lt;/code&gt;, &lt;code&gt;CS0649&lt;/code&gt;)만 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;22) 추가 작업 (공격 타이밍/상태 전환 규칙 적용)&lt;/h2&gt;
&lt;h3&gt;적용 규칙&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;공격 데미지는 공격 애니메이션이 끝나는 순간 적용&lt;/li&gt;
&lt;li&gt;공격 상태에서는 이동하지 않음&lt;/li&gt;
&lt;li&gt;공격 상태에서 다음 상태(이동/추적/유지) 전환은 공격 애니메이션 종료 후에만 허용&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;코드 반영&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/EnemyRuntime.cs&lt;/code&gt;&lt;ul&gt;
&lt;li&gt;공격 대기 상태 필드 추가: &lt;code&gt;_pendingAttackTarget&lt;/code&gt;, &lt;code&gt;_pendingAttackDamage&lt;/code&gt;, &lt;code&gt;_isAttackPending&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PerformCombatAttack&lt;/code&gt;는 즉시 데미지 대신 공격 시작(윈드업)만 수행&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CommitPendingAttack&lt;/code&gt;에서 애니메이션 종료 시점 데미지 확정 + 쿨다운 시작&lt;/li&gt;
&lt;li&gt;공격 대기/공격 홀드 중 &lt;code&gt;HandleTargetInRangeWithoutAttack&lt;/code&gt;, &lt;code&gt;HandleNoTargetOrOutOfRange&lt;/code&gt;에서 이동 차단&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/HeroController.cs&lt;/code&gt;&lt;ul&gt;
&lt;li&gt;동일한 공격 대기 상태/커밋 구조 적용&lt;/li&gt;
&lt;li&gt;영웅 액티브 스킬 트리거도 데미지 커밋 시점으로 이동&lt;/li&gt;
&lt;li&gt;공격 대기 중 이동 로직 진입 차단&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/BarracksSoldierRuntime.cs&lt;/code&gt;&lt;ul&gt;
&lt;li&gt;동일한 공격 대기 상태/커밋 구조 적용&lt;/li&gt;
&lt;li&gt;공격 대기/홀드 중 이동 차단, 블로킹 상태 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;기대 효과&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;타격 판정과 애니메이션 노출 타이밍 일치&lt;/li&gt;
&lt;li&gt;공격 중 밀리듯 이동하는 체감 제거&lt;/li&gt;
&lt;li&gt;공격 종료 전 상태 전환 방지로 전투 가독성 개선&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;기존 경고(&lt;code&gt;MSB3277&lt;/code&gt;, &lt;code&gt;CS0649&lt;/code&gt;)만 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;23) 추가 작업 (유닛 분리/밀어내기 적용 범위 제한)&lt;/h2&gt;
&lt;h3&gt;변경 이유&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;전투 중 이종 타입(적 vs 영웅/병사) 간에 밀어내기까지 발생해 교전 자세가 흔들리는 체감이 발생&lt;/li&gt;
&lt;li&gt;요청사항: 같은 타입 그룹끼리만 분리 적용&lt;ul&gt;
&lt;li&gt;적은 적끼리&lt;/li&gt;
&lt;li&gt;영웅/배럭병사는 아군끼리&lt;/li&gt;
&lt;li&gt;적 vs 아군은 분리 미적용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;코드 반영&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/BaseUnit.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ApplyTeamSeparation(...)&lt;/code&gt; 로직에서 적대 팀(&lt;code&gt;hostile&lt;/code&gt;) 분기 제거&lt;/li&gt;
&lt;li&gt;동일 팀(&lt;code&gt;sameTeam&lt;/code&gt;)에 대해서만 분리 push 계산 적용&lt;/li&gt;
&lt;li&gt;주석 추가: 분리 대상은 같은 팀 그룹 한정&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;기대 효과&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;적과 영웅(또는 병사)이 서로 밀어내며 흔들리는 현상 완화&lt;/li&gt;
&lt;li&gt;근접 교전에서 접촉/타격 자세가 더 안정적으로 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;기존 경고(&lt;code&gt;MSB3277&lt;/code&gt;, &lt;code&gt;CS0649&lt;/code&gt;)만 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;24) 추가 작업 (검증 단계 정리: 1 완료 / 2 패스 / 3 진행)&lt;/h2&gt;
&lt;h3&gt;처리 결과&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;1번(실플레이 회귀 확인): 완료 처리&lt;/li&gt;
&lt;li&gt;2번(타격 타이밍 미세보정): 요청에 따라 패스&lt;/li&gt;
&lt;li&gt;3번(디버그 로그 정리): 진행 완료&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;코드 반영 (로그 정리)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/EnemyRuntime.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;임시 디버그 로깅 제거&lt;ul&gt;
&lt;li&gt;제거: &lt;code&gt;[EnemyTrace]&lt;/code&gt;, &lt;code&gt;[EnemyAttack]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;제거: &lt;code&gt;CombatTrace*&lt;/code&gt; 상수/필드/메서드&lt;/li&gt;
&lt;li&gt;제거: 타겟 변경/틱/공격 로그 호출&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;전투 동작 로직(공격 대기/타이밍/상태 전환)은 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;기존 경고(&lt;code&gt;MSB3277&lt;/code&gt;, &lt;code&gt;CS0649&lt;/code&gt;)만 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;25) 추가 작업 (다음작업 2/3 처리)&lt;/h2&gt;
&lt;h3&gt;상태 정리&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;1번(회귀 확인): 사용자 요청으로 완료 처리 (실행 검증은 미수행)&lt;/li&gt;
&lt;li&gt;2번(전투 코드 정리): 완료&lt;/li&gt;
&lt;li&gt;3번(워킹트리 정리): 완료&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;코드 정리 반영&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/BaseUnit.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;같은 팀 분리 정책으로 전환 후 미사용 잔여 훅 제거&lt;ul&gt;
&lt;li&gt;삭제: &lt;code&gt;ResolveHostileSeparationRadius(...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;삭제: &lt;code&gt;ResolveHostileSeparationWeight(...)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;워킹트리 정리 반영&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;복원: &lt;code&gt;.kilocode/mcp.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;복원: &lt;code&gt;0.0.0a0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;검증&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt;: 오류 0&lt;/li&gt;
&lt;li&gt;기존 경고(&lt;code&gt;MSB3277&lt;/code&gt;, &lt;code&gt;CS0649&lt;/code&gt;)만 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;26) 최종 플레이 검증 완료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;확인 일자: 2026-02-28&lt;/li&gt;
&lt;li&gt;확인 주체: 사용자 실플레이&lt;/li&gt;
&lt;li&gt;결과: 체크리스트 항목(공격중 이동 금지 / 애니메이션 종료 타격 / 공격 종료 후 상태전환 / 같은 팀 분리 정책) 확인 완료&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI/잡다한개발노트</category>
      <category>Devlog</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/85</guid>
      <comments>https://blacknabis.tistory.com/85#entry85comment</comments>
      <pubDate>Sat, 28 Feb 2026 04:31:43 +0900</pubDate>
    </item>
    <item>
      <title>Claude Code 할인</title>
      <link>https://blacknabis.tistory.com/84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;우선 첫구매만 적용되는지는 햇갈려서 월구매 11달러 (세금포함) 꼭 확인하세요.!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 접속 &lt;a href=&quot;https://claude.ai/acquired&quot;&gt;https://claude.ai/acquired&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1772119344791&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Claude&quot; data-og-description=&quot;Claude is Anthropic's AI, built for problem solvers. Tackle complex challenges, analyze data, write code, and think through your hardest work.&quot; data-og-host=&quot;claude.ai&quot; data-og-source-url=&quot;https://claude.ai/acquired&quot; data-og-url=&quot;https://claude.ai/acquired&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eWB9t/dJMb9lL8Mgp/2wZXPm6rj0woycDbPKDwKk/img.png?width=1138&amp;amp;height=640&amp;amp;face=0_0_1138_640,https://scrap.kakaocdn.net/dn/ic9ve/dJMb9iIEkw8/K5qikhTnkEpHG8JL6kQiPK/img.png?width=1138&amp;amp;height=640&amp;amp;face=0_0_1138_640&quot;&gt;&lt;a href=&quot;https://claude.ai/acquired&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://claude.ai/acquired&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eWB9t/dJMb9lL8Mgp/2wZXPm6rj0woycDbPKDwKk/img.png?width=1138&amp;amp;height=640&amp;amp;face=0_0_1138_640,https://scrap.kakaocdn.net/dn/ic9ve/dJMb9iIEkw8/K5qikhTnkEpHG8JL6kQiPK/img.png?width=1138&amp;amp;height=640&amp;amp;face=0_0_1138_640');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Claude&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Claude is Anthropic's AI, built for problem solvers. Tackle complex challenges, analyze data, write code, and think through your hardest work.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;claude.ai&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 월간 결제 11달러 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 구매&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;287&quot; data-origin-height=&quot;66&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/be6cbS/dJMb99Mdneg/QtGcBloxOzCLchZkOybAbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/be6cbS/dJMb99Mdneg/QtGcBloxOzCLchZkOybAbK/img.png&quot; data-alt=&quot;왼쪽하단 프로필 정보에 Pro 요금제 확인.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/be6cbS/dJMb99Mdneg/QtGcBloxOzCLchZkOybAbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbe6cbS%2FdJMb99Mdneg%2FQtGcBloxOzCLchZkOybAbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;287&quot; height=&quot;66&quot; data-origin-width=&quot;287&quot; data-origin-height=&quot;66&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;왼쪽하단 프로필 정보에 Pro 요금제 확인.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;7920&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TXb9h/dJMcagR8Npd/mfLKIJwKRXhDhab8SxrBo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TXb9h/dJMcagR8Npd/mfLKIJwKRXhDhab8SxrBo0/img.png&quot; data-alt=&quot;모니터 3개로 유니티/안티그래비티+코덱스/클루드 3종세트완성....&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TXb9h/dJMcagR8Npd/mfLKIJwKRXhDhab8SxrBo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTXb9h%2FdJMcagR8Npd%2FmfLKIJwKRXhDhab8SxrBo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1218&quot; height=&quot;221&quot; data-origin-width=&quot;7920&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;모니터 3개로 유니티/안티그래비티+코덱스/클루드 3종세트완성....&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 시기인만큼 공부한다 생각하고 3종 구매 완료&lt;/p&gt;</description>
      <category>AI/Etc</category>
      <category>AI</category>
      <category>calude 할인</category>
      <category>claude code</category>
      <category>클로드</category>
      <category>클로드 할인</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/84</guid>
      <comments>https://blacknabis.tistory.com/84#entry84comment</comments>
      <pubDate>Fri, 27 Feb 2026 00:25:44 +0900</pubDate>
    </item>
    <item>
      <title>Antigravity를 사용하여 타워 디펜스 게임 만들기 - 2 (전장 확장 및 스크롤 카메라 구현기)</title>
      <link>https://blacknabis.tistory.com/83</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;타워 디펜스 게임 만들기 2편은 데브로그에 남겨놨지만 이것저것 삘 받아서 작업하다가 작업기간이 늘어나긴 했지만 좋은 경험치가 쌓였던것 같습니다. gpt, claude, gemini 다양하게 써보면서 자동화라던가 llm의 장담점을 익히는데 주점을 두다보니 오래걸렸네요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2560&quot; data-origin-height=&quot;1392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPDhLF/dJMcad16QQS/Xs6HkIMKvk04CnkE1twgI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPDhLF/dJMcad16QQS/Xs6HkIMKvk04CnkE1twgI1/img.png&quot; data-alt=&quot;맵스케일 기능 추가 한 유니티 화면 오른쪽의 스케일 적용된 카메라가 보인다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPDhLF/dJMcad16QQS/Xs6HkIMKvk04CnkE1twgI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPDhLF%2FdJMcad16QQS%2FXs6HkIMKvk04CnkE1twgI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2560&quot; height=&quot;1392&quot; data-origin-width=&quot;2560&quot; data-origin-height=&quot;1392&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;맵스케일 기능 추가 한 유니티 화면 오른쪽의 스케일 적용된 카메라가 보인다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1177&quot; data-origin-height=&quot;682&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YirhX/dJMcadgMYTN/RF8NyLKUCGn9MkaZ24nuj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YirhX/dJMcadgMYTN/RF8NyLKUCGn9MkaZ24nuj0/img.png&quot; data-alt=&quot;플레이화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YirhX/dJMcadgMYTN/RF8NyLKUCGn9MkaZ24nuj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYirhX%2FdJMcadgMYTN%2FRF8NyLKUCGn9MkaZ24nuj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1177&quot; height=&quot;682&quot; data-origin-width=&quot;1177&quot; data-origin-height=&quot;682&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;플레이화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;[Unity 2D] 디펜스 게임 전장 확장 및 스크롤 카메라 구현기&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  개발 환경 노트&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문서 및 블로그 기획/작성: &lt;code&gt;Gemini 3.1 Pro&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;핵심 로직 및 코딩 구현: &lt;code&gt;GPT-5.3-Codex&lt;/code&gt;, &lt;code&gt;Claude Opus 4.6&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 개발 중인 Kingdom Rush 스타일의 전략 디펜스 게임에서 중요한 결정을 내렸습니다. 기존에는 화면 크기(종횡비)에 맞춰 배경 이미지를 억지로 늘리고 줄여 &lt;b&gt;고정된 하나의 뷰(화면)&lt;/b&gt; 안에서 모든 전투가 벌어지도록 설계했었는데요, 이번에 &lt;b&gt;전장을 크게 키우고 자유롭게 카메라를 스크롤&lt;/b&gt;할 수 있도록 완전히 전환하는 작업을 진행했습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  카메라 스크롤을 도입한 이유?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고정 뷰 방식은 아기자기한 모바일 게임에는 좋지만, 스테이지가 거듭될수록 맵의 크기나 타워 배치 구성이 단조로워지는 한계가 있었습니다. 넓은 전장을 구현하여 사용자가 &lt;b&gt;직접 카메라를 이동시키며 전략적인 전황을 살피게 만드는 것&lt;/b&gt;이 훨씬 스케일이 큰 몰입감을 줄 수 있다고 판단했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음과 같은 조작이 가능한 하이브리드 카메라를 구현하기로 했습니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;마우스 드래그 (Mouse Drag)&lt;/b&gt;: 우클릭 또는 휠 클릭으로 직관적인 패닝 이동&lt;/li&gt;
&lt;li&gt;&lt;b&gt;키보드 이동 (WASD)&lt;/b&gt;: 클래식한 PC 방식의 쾌적한 이동&lt;/li&gt;
&lt;li&gt;&lt;b&gt;에지 스크롤 (Edge Scroll)&lt;/b&gt;: 화면 끝으로 마우스를 가져가면 자동으로 밀려가는 방식&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 핵심 구현 요소&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 부드러운 감속 (관성, Inertia)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카메라 조작 시 딱딱하게 끊기지 않고 부드러운 조작감을 주기 위해 &lt;code&gt;Vector3.SmoothDamp&lt;/code&gt;와 커스텀 감속 로직을 활용했습니다. 마우스 드래그를 놓는 순간 입력 속력을 저장한 뒤 점차 0으로 줄여나가(Lerp) 빙판을 미끄러지듯 기분 좋은 이동(Juicy Feedback)을 적용했습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 구역 이탈 방지 클램프 (Bounds Clamping)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전장이 아무리 넓다 해도, 배경 이미지 밖의 텅 빈 검은 화면은 몰입을 깹니다.&lt;br /&gt;카메라의 뷰포트 크기를 계산하고 배경 스프라이트의 실제 크기(&lt;code&gt;SpriteRenderer.bounds&lt;/code&gt;)와 정합하여 &lt;b&gt;렌더링 영역 안에서만 카메라가 이동하도록 클램핑(Clamp)&lt;/b&gt; 했습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  개발 팁:&lt;/b&gt;&lt;br /&gt;처음엔 고정된 &lt;code&gt;Rect&lt;/code&gt; 값을 수동으로 넣었는데, 이후 맵이 변경될 때마다 관리하기가 어려웠습니다. &lt;code&gt;GameBattlefield&lt;/code&gt; 오브젝트가 초기화될 때 배경 스프라이트의 Bounds 영역을 자동으로 계산해 반환하도록 속성(&lt;code&gt;GameplayBounds&lt;/code&gt;)을 구성하여 자동화했습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. UI 조작 간섭 방지 (EventSystem)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마우스 클릭으로 타워를 업그레이드할 때 카메라가 스크롤되면 안 되겠죠?&lt;br /&gt;&lt;code&gt;EventSystem.current.IsPointerOverGameObject()&lt;/code&gt;를 통해 &lt;b&gt;UI 위에서 클릭이 시작되었을 때는 카메라 드래그 이벤트를 차단&lt;/b&gt;했습니다. (물론 WASD 이동은 전투 중에도 언제든 조작할 수 있도록 예외처리했습니다.)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  트러블슈팅: 영웅만 소인국 사람이 되다? (Scale Mismatch)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 작업에서 가장 골치 아팠던(그리고 재밌었던) 버그입니다. 전장이 넓어지니 기존 영웅 캐릭터가 상대적으로 너무 작게 출력되는 문제가 발생했습니다. 배경은 장대한데, 디펜스의 꽃인 영웅이 개미처럼 작아 보였죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;원인 분석:&lt;/b&gt;&lt;br /&gt;&lt;code&gt;GameBattlefield&lt;/code&gt; 프리팹 자체가 전체 스케일업을 위해 루트 &lt;code&gt;Transform&lt;/code&gt;의 &lt;code&gt;localScale&lt;/code&gt; 값으로 &lt;code&gt;(2, 2, 1)&lt;/code&gt;을 가지고 있었습니다. 타워나 적 몬스터들은 모두 ఈ 프리팹 하위(Child)에 스폰되니 자연스럽게 &lt;code&gt;2배&lt;/code&gt; 스케일을 상속받았지만...&lt;br /&gt;영웅 컨트롤러는 &lt;code&gt;GameScene&lt;/code&gt;에서 &lt;b&gt;World Root 레벨(최상위)&lt;/b&gt;에 둥그러니 스폰되고 있었기 때문에 원래 크기인 &lt;code&gt;(1, 1, 1)&lt;/code&gt;로 표시되었던 것입니다. 화면과 적들은 2배인데, 영웅 혼자 정사이즈니 작아 보일 수밖에 없었죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결 방안:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Hierarchical Parenting (계층 구조 정합)&lt;/b&gt;: 영웅도 전장의 환경 요소를 따르도록 구조를 개선했습니다. &lt;code&gt;GameBattlefield&lt;/code&gt;에 &lt;b&gt;&lt;code&gt;heroRoot&lt;/code&gt;&lt;/b&gt;라는 컨테이너를 하나 파두고, 영웅이 맵에 생성될 때 해당 Root 하위로 &lt;code&gt;SetParent&lt;/code&gt; 시켰습니다.&lt;/li&gt;
&lt;li&gt;즉석에서 &lt;code&gt;Vector3&lt;/code&gt; 사이즈를 수동으로 1.5배, 2배 곱해주는 코딩은 나중에 큰 기술 부채가 될 수 있어 피하고, &lt;b&gt;가상공간(부모 객체)의 스케일을 자연스럽게 상속받도록&lt;/b&gt; 설계했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// GameScene.cs - Hero 스폰 로직 중
_heroController = new GameObject(&quot;HeroController&quot;).AddComponent&amp;lt;HeroController&amp;gt;();
// 핵심: 영웅을 생성한 후, 2배 스케일을 가진 HeroRoot의 자식으로 배치!
_heroController.transform.SetParent(_battlefield.HeroRoot, false);&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;개발 과정에서 궁금하신 점이 있다면 언제든 댓글 남겨주세요!&lt;/i&gt;&lt;/p&gt;</description>
      <category>AI/Unity</category>
      <category>2D</category>
      <category>Camera</category>
      <category>gamedev</category>
      <category>Unity</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/83</guid>
      <comments>https://blacknabis.tistory.com/83#entry83comment</comments>
      <pubDate>Thu, 26 Feb 2026 01:42:05 +0900</pubDate>
    </item>
    <item>
      <title>Antigravity 토큰 확인하기</title>
      <link>https://blacknabis.tistory.com/82</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;얼마전 까지 안티그래비티에서 토큰 확인 할 방법이 없어서 IDE에서 Antigravity Quota를 설치하 아래와 같이 확인을 하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;353&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mcIEf/dJMcabQNCAD/HhmWXOYEsRRHVIBFJVYRzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mcIEf/dJMcabQNCAD/HhmWXOYEsRRHVIBFJVYRzk/img.png&quot; data-alt=&quot;확장메뉴&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mcIEf/dJMcabQNCAD/HhmWXOYEsRRHVIBFJVYRzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmcIEf%2FdJMcabQNCAD%2FHhmWXOYEsRRHVIBFJVYRzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;752&quot; height=&quot;353&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;353&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;확장메뉴&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAXT0M/dJMcaih5HFv/aDk8A6dVCfhkJNGkoJWJ21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAXT0M/dJMcaih5HFv/aDk8A6dVCfhkJNGkoJWJ21/img.png&quot; data-alt=&quot;토큰 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAXT0M/dJMcaih5HFv/aDk8A6dVCfhkJNGkoJWJ21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAXT0M%2FdJMcaih5HFv%2FaDk8A6dVCfhkJNGkoJWJ21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;731&quot; height=&quot;377&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;토큰 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얼마전 업데이트 이후 에이전트에서 확인이 가능해졌습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;181&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xbz0R/dJMcabQNCDA/sk9aHdI5iWzWU3PRCTKw10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xbz0R/dJMcabQNCDA/sk9aHdI5iWzWU3PRCTKw10/img.png&quot; data-alt=&quot;에이전트 우상단 옵션&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xbz0R/dJMcabQNCDA/sk9aHdI5iWzWU3PRCTKw10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxbz0R%2FdJMcabQNCDA%2Fsk9aHdI5iWzWU3PRCTKw10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;363&quot; height=&quot;181&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;181&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;에이전트 우상단 옵션&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/US70n/dJMcaiCnoYx/ubjE5ULseAgXlw3xp20OxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/US70n/dJMcaiCnoYx/ubjE5ULseAgXlw3xp20OxK/img.png&quot; data-alt=&quot;모델을 선택하면 다음과 같이 확인 가능&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/US70n/dJMcaiCnoYx/ubjE5ULseAgXlw3xp20OxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUS70n%2FdJMcaiCnoYx%2FubjE5ULseAgXlw3xp20OxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1198&quot; height=&quot;768&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;모델을 선택하면 다음과 같이 확인 가능&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트에서 모델/토큰이 보이지 않는다면 안티그래비티 업데이트를 진행해보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글을 다 작성하니 Antigravity Cockpit라는게 또 있네요&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hukrd/dJMcacPI7gi/6BkE5rEKlk6ogkctqN6kVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hukrd/dJMcacPI7gi/6BkE5rEKlk6ogkctqN6kVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hukrd/dJMcacPI7gi/6BkE5rEKlk6ogkctqN6kVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHukrd%2FdJMcacPI7gi%2F6BkE5rEKlk6ogkctqN6kVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;772&quot; height=&quot;251&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;861&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MsAuf/dJMcadnwiVr/xWsa8PELRuKzawebkCS1j1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MsAuf/dJMcadnwiVr/xWsa8PELRuKzawebkCS1j1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MsAuf/dJMcadnwiVr/xWsa8PELRuKzawebkCS1j1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMsAuf%2FdJMcadnwiVr%2FxWsa8PELRuKzawebkCS1j1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1322&quot; height=&quot;861&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;861&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>AI/Etc</category>
      <category>Antigravity</category>
      <category>Antigravity Cockpit</category>
      <category>안티그래비티</category>
      <category>토큰확인</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/82</guid>
      <comments>https://blacknabis.tistory.com/82#entry82comment</comments>
      <pubDate>Thu, 26 Feb 2026 00:30:58 +0900</pubDate>
    </item>
    <item>
      <title>Antigravity &amp;lt;-&amp;gt; Codex IDE MCP 오답 노트(우회해결)</title>
      <link>https://blacknabis.tistory.com/81</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1121&quot; data-origin-height=&quot;608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mzvPL/dJMcajgXq5f/pGqFPXI8u0vhOeqLURFSu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mzvPL/dJMcajgXq5f/pGqFPXI8u0vhOeqLURFSu1/img.png&quot; data-alt=&quot;codex-mcp-sever&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mzvPL/dJMcajgXq5f/pGqFPXI8u0vhOeqLURFSu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmzvPL%2FdJMcajgXq5f%2FpGqFPXI8u0vhOeqLURFSu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1121&quot; height=&quot;608&quot; data-origin-width=&quot;1121&quot; data-origin-height=&quot;608&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;codex-mcp-sever&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IDE에서 IDE로 MCP를 연결해서 될거라 생각했는데 이방법이 아니라 Antigravity에서 codex-mcp-server를 사용하여 다음과 같이 명령해서 문서 보완에 성공. 하지만 수동으로 하는것보단 느린거 같은데 다시한번 채크해봐야될것 같음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;607&quot; data-origin-height=&quot;119&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sm2hU/dJMcabpLIjr/7gq0MyeqjoHWng7HJRlfa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sm2hU/dJMcabpLIjr/7gq0MyeqjoHWng7HJRlfa1/img.png&quot; data-alt=&quot;문서보완 핑퐁&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sm2hU/dJMcabpLIjr/7gq0MyeqjoHWng7HJRlfa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsm2hU%2FdJMcabpLIjr%2F7gq0MyeqjoHWng7HJRlfa1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;607&quot; height=&quot;119&quot; data-origin-width=&quot;607&quot; data-origin-height=&quot;119&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;문서보완 핑퐁&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;629&quot; data-origin-height=&quot;477&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwShVp/dJMcagLmTET/m7JHlG6LoOR8A1Ylysh0z0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwShVp/dJMcagLmTET/m7JHlG6LoOR8A1Ylysh0z0/img.png&quot; data-alt=&quot;최종완료&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwShVp/dJMcagLmTET/m7JHlG6LoOR8A1Ylysh0z0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwShVp%2FdJMcagLmTET%2Fm7JHlG6LoOR8A1Ylysh0z0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;629&quot; height=&quot;477&quot; data-origin-width=&quot;629&quot; data-origin-height=&quot;477&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;최종완료&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>AI/오답노트</category>
      <category>Antigravity</category>
      <category>codex mcp</category>
      <category>오답노트</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/81</guid>
      <comments>https://blacknabis.tistory.com/81#entry81comment</comments>
      <pubDate>Wed, 25 Feb 2026 22:56:59 +0900</pubDate>
    </item>
    <item>
      <title>Antigravity &amp;lt;-&amp;gt; Codex IDE MCP 오답 노트</title>
      <link>https://blacknabis.tistory.com/80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Roo Code &amp;lt;-&amp;gt; Antigravity 계획 자동보완&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서를 보완할 때 서로 핑퐁을 하면서 계속 수동으로 문서보완 명령을 하는데 너무 불편해서 방법을 알아보다가 MCP를 제작하여서 자동으로 서로 핑퐁을 주고받으려고 했지만 실패....&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;950&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgQMna/dJMcafr7oGo/WZzsDaa0y0s6yO6hkw6Zc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgQMna/dJMcafr7oGo/WZzsDaa0y0s6yO6hkw6Zc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgQMna/dJMcafr7oGo/WZzsDaa0y0s6yO6hkw6Zc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgQMna%2FdJMcafr7oGo%2FWZzsDaa0y0s6yO6hkw6Zc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;642&quot; height=&quot;950&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;950&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전부 실패...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 API키를 받아서 핑퐁하는 식으로 개선....&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;339&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mJkhP/dJMcaaqPknp/dvysAkyrMZCGA024UHspI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mJkhP/dJMcaaqPknp/dvysAkyrMZCGA024UHspI1/img.png&quot; data-alt=&quot;MCP 추가하였지만 처참하게 실패... IDE기반의 핑퐁은 조금 더 공부를 해서 다시 도전해봐야될거같다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mJkhP/dJMcaaqPknp/dvysAkyrMZCGA024UHspI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmJkhP%2FdJMcaaqPknp%2FdvysAkyrMZCGA024UHspI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;755&quot; height=&quot;339&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;339&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MCP 추가하였지만 처참하게 실패... IDE기반의 핑퐁은 조금 더 공부를 해서 다시 도전해봐야될거같다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;781&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Dh5Xe/dJMcachSrlu/qlhw07ffxQkXpINqdAKTN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Dh5Xe/dJMcachSrlu/qlhw07ffxQkXpINqdAKTN1/img.png&quot; data-alt=&quot;문서 폐기 엔딩.....&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Dh5Xe/dJMcachSrlu/qlhw07ffxQkXpINqdAKTN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDh5Xe%2FdJMcachSrlu%2Fqlhw07ffxQkXpINqdAKTN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;781&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;781&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;문서 폐기 엔딩.....&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 이상 보다가는 밤새고 출근 못 할 거 같아서 오늘은 4시간 삽질하고 좋은 경험 했다 생각하고 기권&lt;/p&gt;</description>
      <category>AI/오답노트</category>
      <category>실패노트</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/80</guid>
      <comments>https://blacknabis.tistory.com/80#entry80comment</comments>
      <pubDate>Wed, 25 Feb 2026 01:27:48 +0900</pubDate>
    </item>
    <item>
      <title>Codex / Antigravity 타워디펜스 만드는 중 사용 한 플랜 예시</title>
      <link>https://blacknabis.tistory.com/79</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;디펜스 만드는 중 플랜 짜는 예시를 공유하면 좋을 것 같아서 끄적끄적 올려봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 초안 계획을 AI에 플랜을 세우라고 내용 명시&lt;br /&gt;2. 해당 플랜을 gpt -&amp;gt; gemini -&amp;gt; claude -&amp;gt; gpt...로 더 이상 보완 할 게 없을 때까지 문서강화.&lt;br /&gt;3. 해당플랜으로 task.md 제작. 이후 플랜과 task를 이용하여 작업 진행.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;656&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zb3mu/dJMb996vkpL/XC49XsUtmkD30uRqZvv6Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zb3mu/dJMb996vkpL/XC49XsUtmkD30uRqZvv6Kk/img.png&quot; data-alt=&quot;Codex에서 플랜 초안을 작성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zb3mu/dJMb996vkpL/XC49XsUtmkD30uRqZvv6Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fzb3mu%2FdJMb996vkpL%2FXC49XsUtmkD30uRqZvv6Kk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;656&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;656&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Codex에서 플랜 초안을 작성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;964&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biNMFX/dJMcac3b8nH/OtiwzxpBVkLcM0kCHaols0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biNMFX/dJMcac3b8nH/OtiwzxpBVkLcM0kCHaols0/img.png&quot; data-alt=&quot;다른 AI에게 문서 보완을 지시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biNMFX/dJMcac3b8nH/OtiwzxpBVkLcM0kCHaols0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiNMFX%2FdJMcac3b8nH%2FOtiwzxpBVkLcM0kCHaols0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;626&quot; height=&quot;964&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;964&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;다른 AI에게 문서 보완을 지시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1305&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rng5N/dJMcahwIb11/kux2SFnTyQbXMz3JJedTvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rng5N/dJMcahwIb11/kux2SFnTyQbXMz3JJedTvK/img.png&quot; data-alt=&quot;플랜이 마음에 들때까지 반복. Google Flow로 이미지 제작.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rng5N/dJMcahwIb11/kux2SFnTyQbXMz3JJedTvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frng5N%2FdJMcahwIb11%2Fkux2SFnTyQbXMz3JJedTvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1305&quot; height=&quot;700&quot; data-origin-width=&quot;1305&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;플랜이 마음에 들때까지 반복. Google Flow로 이미지 제작.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 계속 진행하면 다음과 같은 플랜이 나오고 진행합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1082&quot; data-origin-height=&quot;855&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvRxo7/dJMcadnumvJ/FRjGzVGD9cIcBTyecSJiU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvRxo7/dJMcadnumvJ/FRjGzVGD9cIcBTyecSJiU1/img.png&quot; data-alt=&quot;Google Flow에서 생성한 이미지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvRxo7/dJMcadnumvJ/FRjGzVGD9cIcBTyecSJiU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdvRxo7%2FdJMcadnumvJ%2FFRjGzVGD9cIcBTyecSJiU1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1082&quot; height=&quot;855&quot; data-origin-width=&quot;1082&quot; data-origin-height=&quot;855&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Google Flow에서 생성한 이미지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/bGLZIt/dJMcai3nLeJ/uduELjPo0mLtE99Bk2t6GK/%ED%83%80%EC%9B%8C%EC%8A%AC%EB%A1%AF_%ED%8F%B4%EB%B0%B1%EC%A0%9C%EA%B1%B0_%EC%8B%9C%EA%B0%81%ED%99%94_%EA%B3%84%ED%9A%8D_2026_02_23.md?attach=1&amp;amp;knm=tfile.md&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;타워슬롯_폴백제거_시각화_계획_2026_02_23.md&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.02MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1 style=&quot;color: #000000; text-align: start;&quot;&gt;타워슬롯 폴백 제거 + 슬롯 시각화 적용 계획 (2026-02-23)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;0. 목표&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타워 슬롯이 없는 맵은 &lt;b&gt;자동 폴백 생성 없이 에러로 중단&lt;/b&gt;해 데이터 누락을 즉시 드러낸다.&lt;/li&gt;
&lt;li&gt;타워 건설 가능 위치를 유저가 인지할 수 있도록 슬롯 위치에 안내 이미지를 표시한다.&lt;/li&gt;
&lt;li&gt;해당 슬롯에 타워가 건설되면 슬롯 안내 이미지는 즉시 숨긴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 범위&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;In Scope&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameBattlefield&lt;/code&gt; / &lt;code&gt;TowerManager&lt;/code&gt; / &lt;code&gt;GameScene&lt;/code&gt;의 타워 슬롯 검증 흐름 정리&lt;/li&gt;
&lt;li&gt;타워 슬롯 시각화 런타임 오브젝트(또는 프리팹) 표시/숨김 규칙 적용&lt;/li&gt;
&lt;li&gt;슬롯 안내 이미지(스프라이트) 제작용 AI 프롬프트 작성&lt;/li&gt;
&lt;li&gt;QA 체크리스트 및 롤백 포인트 문서화&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Out of Scope&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타워 전투 로직(사거리/공격/스킬) 변경&lt;/li&gt;
&lt;li&gt;UI TowerRingMenu 배치/레이아웃 개편&lt;/li&gt;
&lt;li&gt;슬롯 개수 자동 배치 알고리즘 도입&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 현재 기준 동작(상세)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-1) 폴백 슬롯 생성 지점 (제거 대상 2곳)&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GameBattlefield.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;EnsureRuntimeDefaults()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;towerRoot.childCount == 0&lt;/code&gt;이면 &lt;code&gt;CreateDefaultTowerSlots(towerRoot)&lt;/code&gt; 호출 &amp;rarr; 3개 좌표 자동 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TowerManager.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Configure()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;_towerSlots.Count == 0&lt;/code&gt;이면 하드코딩 좌표 3개 직접 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-2) 슬롯 데이터 흐름&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;GameScene.ConfigureEconomyAndTower()
 └─ _battlefield.GetTowerSlotPositions()   &amp;larr; towerRoot 하위 Transform &amp;rarr; List&amp;lt;Vector3&amp;gt;
     └─ RebuildTowerSlotsIfNeeded()        &amp;larr; towerRoot childCount로 리빌드
 └─ _towerManager.Configure(..., towerSlots, ...)
     └─ _towerSlots에 복사 &amp;rarr; RebuildFreeSlots() &amp;rarr; _freeSlotIndices 초기화&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-3) 빌드/판매 시 슬롯 상태 변경 흐름&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;빌드&lt;/b&gt;: &lt;code&gt;TryBuildTowerAtSlot()&lt;/code&gt; &amp;rarr; &lt;code&gt;_freeSlotIndices.Remove(slotIndex)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;판매&lt;/b&gt;: &lt;code&gt;TrySellTower()&lt;/code&gt; &amp;rarr; &lt;code&gt;_freeSlotIndices.Add(tower.SlotIndex)&lt;/code&gt; + Sort&lt;/li&gt;
&lt;li&gt;&lt;b&gt;슬롯 검색&lt;/b&gt;: &lt;code&gt;TryFindBuildableSlotAtWorldPosition(worldPos, maxDist)&lt;/code&gt; &amp;rarr; 거리 기반 가장 가까운 free 슬롯 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-4) 현재 시각 안내&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;별도 슬롯 마커 오브젝트 없음. 유저가 빈 슬롯 위치를 직관적으로 파악할 수 없음.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 변경 계획&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;[참조] NotebookLM 인사이트 (Kingdom Rush UI/UX 원칙)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Snappy Feedback (Juice)&lt;/b&gt;: 타워 슬롯 클릭 시 즉각적인 시각/청각 피드백이 필요하다. 타워 메뉴가 열리기 전이나 동시에 슬롯 자체의 반응(예: hover 시 밝아짐, 클릭 시 살짝 눌림 또는 먼지 파티클)을 주어 조작감을 높여야 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;맵과의 조화&lt;/b&gt;: 슬롯은 맵의 일부처럼 자연스럽게 무언가 지어질 기반(Dirt, Stone Base 등)으로 녹아들어야 하며, 상호작용 가능한 오브젝트임을 명확히 보여야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Phase A. 슬롯 폴백 제거 + 에러 처리&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;변경 대상&lt;/h4&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;code&gt;GameBattlefield.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;EnsureRuntimeDefaults()&lt;/code&gt;: &lt;code&gt;towerRoot.childCount == 0&lt;/code&gt; 분기에서 &lt;code&gt;CreateDefaultTowerSlots()&lt;/code&gt; 호출을 &lt;b&gt;에러 로그&lt;/b&gt;로 교체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GameBattlefield.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CreateDefaultTowerSlots()&lt;/code&gt;: 메서드 자체 삭제 또는 &lt;code&gt;[Obsolete]&lt;/code&gt; 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TowerManager.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Configure()&lt;/code&gt;: &lt;code&gt;_towerSlots.Count == 0&lt;/code&gt; 시 하드코딩 좌표 추가 블록 &amp;rarr; &lt;b&gt;&lt;code&gt;Debug.LogError&lt;/code&gt; + &lt;code&gt;enabled = false&lt;/code&gt;&lt;/b&gt; 로 교체&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;에러 메시지 예시&lt;/h4&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;[TowerManager] 타워 슬롯 0개 감지. GameBattlefield.TowerRoot 하위에 슬롯(자식 Transform)을 1개 이상 배치하세요.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완료 기준 (DoD)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;슬롯 없는 씬에서 폴백 슬롯이 생성되지 않는다.&lt;/li&gt;
&lt;li&gt;콘솔에 명확한 에러가 출력되고, 타워 건설 UI가 열리지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Phase B. 슬롯 시각화(가이드 이미지) 표시&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;슬롯 월드 좌표마다 &lt;code&gt;SlotMarker&lt;/code&gt; 표시 오브젝트 생성(스프라이트 렌더러 기반).&lt;/li&gt;
&lt;li&gt;기본 상태: &lt;code&gt;free slot&lt;/code&gt;만 표시.&lt;/li&gt;
&lt;li&gt;카메라/배경 대비를 고려한 정렬 레이어/소팅오더 지정 (예: Background Layer의 최상단).&lt;/li&gt;
&lt;li&gt;시각 톤 및 작동 피드백 (Juice 반영):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;과도한 강조 대신 맵과 조화되는 은은한 기초 플랫폼/돌 판 형태&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Idle(기본)&lt;/b&gt;: 약간의 Sine 파동 스케일 또는 알파 깜빡임으로 &quot;여기 뭔가 지을 수 있음&quot; 시각 암시&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Hover(마우스/터치 오버)&lt;/b&gt;: 스프라이트 밝기 증가(Material Color/플래시) 혹은 외곽선 점등으로 상호작용 가능성 암시&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Click(클릭)&lt;/b&gt;: 스케일(Scale)이 살짝 줄었다 커지는 펀치 효과(Punch Scale)로 타격감 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완료 기준 (DoD)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전투 시작 시 빈 슬롯에만 마커가 보인다.&lt;/li&gt;
&lt;li&gt;슬롯 위치와 메뉴 오픈 위치가 사용자 체감상 일치한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Phase C. 타워 건설 시 슬롯 이미지 숨김&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;TryBuildTowerAtSlot&lt;/code&gt; 성공 시 해당 슬롯 마커 숨김.&lt;/li&gt;
&lt;li&gt;타워 판매 시 슬롯이 다시 free 되면 마커 재표시.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;타워 업그레이드 시&lt;/b&gt;: 슬롯 점유 상태 불변 &amp;rarr; 마커 상태 변경 없음 (숨김 유지).&lt;/li&gt;
&lt;li&gt;초기 동기화 보강:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로드/재시작 시 현재 점유 상태와 마커 표시 상태를 일치시킴.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;마커 표시/숨김 트랜지션 정책&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;P0(최소 구현): &lt;code&gt;SetActive&lt;/code&gt;로 &lt;b&gt;즉시&lt;/b&gt; On/Off (트랜지션 없음).&lt;/li&gt;
&lt;li&gt;P1(확장): 건설 성공 시 알파 페이드아웃 &amp;rarr; &lt;code&gt;SetActive(false)&lt;/code&gt; / 판매 시 알파 페이드인 &amp;rarr; &lt;code&gt;SetActive(true)&lt;/code&gt;. 스프라이트 Color.a Lerp 기반.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완료 기준 (DoD)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;건설 성공 즉시 해당 슬롯 마커가 사라진다.&lt;/li&gt;
&lt;li&gt;판매 후 해당 슬롯 마커가 다시 보인다.&lt;/li&gt;
&lt;li&gt;업그레이드 시 마커가 재표시되지 않는다.&lt;/li&gt;
&lt;li&gt;중복 표시/잔상 없음.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 구현 포인트(상세 설계)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-1) 신규 클래스: &lt;code&gt;TowerSlotVisualizer&lt;/code&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;파일&lt;/b&gt;: &lt;code&gt;Assets/Scripts/Kingdom/Game/TowerSlotVisualizer.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;책임&lt;/b&gt;: 슬롯 마커 오브젝트의 생성/표시/숨김 관리 (단일 책임)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;[구조 스케치]
TowerSlotVisualizer : MonoBehaviour
├── [SerializeField] Sprite slotMarkerSprite   &amp;larr; 기본 슬롯 마커 스프라이트
├── [SerializeField] int sortingOrder = -5     &amp;larr; 타워 아래, 배경 위 정렬
├── List&amp;lt;GameObject&amp;gt; _markers                  &amp;larr; 생성된 마커 오브젝트 목록
├── List&amp;lt;SpriteRenderer&amp;gt; _renderers            &amp;larr; 렌더러 캐싱 (Color/알파 제어용)
├── Update() 혹은 Coroutine                    &amp;larr; Mathf.Sin 기반의 저비용 Idle 애니메이션(알파/스케일 깜빡임) 구동
├── Initialize(IReadOnlyList&amp;lt;Vector3&amp;gt; slotPositions)
│   └─ 슬롯 좌표별 마커 생성, SpriteRenderer/Transform 캐싱
├── ShowMarker(int slotIndex)
│   └─ GameObject.SetActive(true)
├── HideMarker(int slotIndex)
│   └─ GameObject.SetActive(false)
├── RefreshAll(HashSet&amp;lt;int&amp;gt; freeSlotIndices)
│   └─ 모든 마커를 freeSlotIndices 기준으로 일괄 동기화
└── Cleanup()
    └─ 모든 마커 오브젝트 Destroy&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-2) &lt;code&gt;TowerManager&lt;/code&gt; 이벤트 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 &lt;code&gt;TryBuildTowerAtSlot&lt;/code&gt;/&lt;code&gt;TrySellTower&lt;/code&gt; 메서드에 이벤트 발행 코드 추가:&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public event System.Action&amp;lt;int&amp;gt; OnSlotOccupied;   // 빌드 성공 시 slotIndex 전달
public event System.Action&amp;lt;int&amp;gt; OnSlotFreed;       // 판매 성공 시 slotIndex 전달&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-3) &lt;code&gt;GameScene&lt;/code&gt; Wiring 변경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ConfigureEconomyAndTower()&lt;/code&gt; 내부에 다음 흐름 추가:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. TowerSlotVisualizer 생성 또는 GetComponent
2. visualizer.Initialize(towerSlots)
3. _towerManager.OnSlotOccupied += visualizer.HideMarker
4. _towerManager.OnSlotFreed += visualizer.ShowMarker&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이벤트 해제 (메모리 누수 방지)&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;TowerSlotVisualizer.OnDisable()&lt;/code&gt; 또는 &lt;code&gt;Cleanup()&lt;/code&gt;에서 반드시 이벤트 구독 해제:
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;_towerManager.OnSlotOccupied -= HideMarker;
_towerManager.OnSlotFreed -= ShowMarker;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;씬 전환/재시작 시 &lt;code&gt;Cleanup()&lt;/code&gt; &amp;rarr; 이벤트 해제 &amp;rarr; 마커 Destroy 순서 보장.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-4) 에셋 규칙 (프로젝트 리소스 폴더 구조 준수)&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;code&gt;Assets/Resources/UI/Sprites/Game/tower_slot_marker.png&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;PPU&lt;/b&gt;: 맵 타일과 동일 비율(예: 100), &lt;b&gt;Mesh Type&lt;/b&gt;: Full Rect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;슬롯 마커 프리팹 (선택)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Assets/Resources/UI/SlotMarker.prefab&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-5) Sorting 규칙&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;Sorting Layer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Default&lt;/code&gt; 또는 전용 &lt;code&gt;SlotGuide&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Order in Layer&lt;/td&gt;
&lt;td&gt;슬롯 마커&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-5&lt;/code&gt; (배경 위, 타워/유닛 아래)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Order in Layer&lt;/td&gt;
&lt;td&gt;타워 본체&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0 ~ 10&lt;/code&gt; (기존)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-6) 에디터 전용 슬롯 검증 도구 (선택)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;목적&lt;/b&gt;: 씬 저장/빌드 시 &lt;code&gt;TowerRoot&lt;/code&gt; 하위 슬롯 0개를 사전에 감지하여 런타임 에러를 방지.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;구현 방식&lt;/b&gt;: &lt;code&gt;IProcessSceneWithReport&lt;/code&gt; 또는 커스텀 &lt;code&gt;MenuItem&lt;/code&gt; 활용.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;출력&lt;/b&gt;: 슬롯 누락 씬 이름 + 수정 힌트 콘솔 경고.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;분류&lt;/b&gt;: Disposable Tool (일회성 Editor 스크립트) 또는 상시 유지 선택 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 나노바나나(AI) 이미지 생성 프롬프트&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-1) 기본 프롬프트&lt;/h3&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;Top-down 2D isometric tower-defense build slot foundation, hand-painted kingdom rush style,
clean stylized fantasy stone platform or wooden dirt base, subtle magical glowing runes, 
transparent background, centered composition, no text, no characters,
readable on grass and dirt map, highly detailed game asset, vibrant colors, 1:1.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-2) 변형 프롬프트 (밝은 맵용)&lt;/h3&gt;
&lt;pre class=&quot;scss&quot;&gt;&lt;code&gt;Top-down 2D isometric build slot indicator for tower defense, hand-painted kingdom rush art style, 
carved stone circle with clear outline, minimal magical glow, 
transparent background, no text, no shadows outside object bounds,
strong silhouette, vibrant neutral fantasy style, production-ready 2D game sprite.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-3) 네거티브 프롬프트&lt;/h3&gt;
&lt;pre class=&quot;mel&quot;&gt;&lt;code&gt;character, hero, enemy, weapon, castle, UI panel, letters, logo, watermark,
photorealistic, 3d render look, heavy bloom, noisy background, tilted perspective.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-4) 생성 가이드&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배경: 반드시 투명(Transparent)&lt;/li&gt;
&lt;li&gt;비율: 1:1 (권장 512 또는 1024)&lt;/li&gt;
&lt;li&gt;산출물: 기본/hover/selected 3버전까지 확장 가능&lt;/li&gt;
&lt;li&gt;생성 후 &lt;code&gt;Assets/Resources/UI/Sprites/Game/&lt;/code&gt; 하위에 배치&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. QA 체크리스트&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;Phase&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;슬롯 0개 씬에서 에러 처리 동작 (폴백 없음)&lt;/td&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;TowerRoot 하위 자식 모두 제거 후 Play &amp;rarr; 에러 로그 확인, 타워 메뉴 비활성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;슬롯 있는 씬에서 모든 free 슬롯 마커 표시&lt;/td&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;Play 시 슬롯 수만큼 마커 표시 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;슬롯 클릭 시 TowerRingMenu가 슬롯 위치 기준으로 오픈&lt;/td&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;마커 클릭 &amp;rarr; 메뉴 위치 일치 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Hover 시 마커 밝기/스케일 변화&lt;/td&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;마우스 오버 &amp;rarr; 시각 피드백 확인&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;C&lt;/td&gt;
&lt;td&gt;타워 건설 &amp;rarr; 마커 즉시 비활성&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;C&lt;/td&gt;
&lt;td&gt;타워 판매 &amp;rarr; 마커 재활성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;웨이브 진행/일시정지/배속 변경 중 마커 상태 유지&lt;/td&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;다양한 게임 상태에서 마커 상태 불변 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;씬 재시작 후 슬롯 점유-마커 상태 동기화&lt;/td&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;재시작 &amp;rarr; 점유 슬롯은 숨김, free 슬롯은 표시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;모든 슬롯 점유 후 추가 건설 요청 거부&lt;/td&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;모든 슬롯 채운 후 빈 땅 클릭 &amp;rarr; 메뉴 미표시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;타워 업그레이드 시 마커 숨김 유지&lt;/td&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;타워 업그레이드 &amp;rarr; 마커 재표시 안 됨 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;골드 부족 시 건설 실패 &amp;rarr; 마커 표시 유지&lt;/td&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;골드 부족 상태에서 건설 시도 &amp;rarr; 마커 숨김 안 됨 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 리스크 및 대응&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;/td&gt;
&lt;td&gt;에러 메시지에 수정 위치(&lt;code&gt;GameBattlefield/TowerRoot&lt;/code&gt;) 명시 + 에디터에서 Play 자동 중단&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;마커 소팅 충돌로 타워/지형과 겹침&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;td&gt;전용 Sorting Layer 또는 고정 order 규칙(&lt;code&gt;-5&lt;/code&gt;) 적용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;건설/판매 이벤트 누락 시 마커 불일치&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RefreshAll()&lt;/code&gt; full-sync 메서드 확보, 웨이브 전환 시 호출&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기존 씬 프리팹에 TowerRoot 자식 없음&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;td&gt;Phase A 적용 시 기존 씬 점검 필수, 누락 시 에디터 로그로 즉시 감지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hover/Click 피드백 과부하 (모바일 성능)&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;td&gt;셰이더 대신 Color/Scale 변경으로 저비용 구현, Feature Flag로 피드백 ON/OFF 가능하게&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 작업 순서(실행 권장)&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Phase A&lt;/b&gt; 먼저 반영 (데이터 무결성 확보)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameBattlefield.CreateDefaultTowerSlots&lt;/code&gt; 제거&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerManager.Configure&lt;/code&gt; 폴백 블록 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Phase B&lt;/b&gt; 기본 마커 표시 구현
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;TowerSlotVisualizer&lt;/code&gt; 신규 작성&lt;/li&gt;
&lt;li&gt;슬롯 마커 스프라이트 생성 및 배치&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Phase C&lt;/b&gt; 점유 상태 연동 마감
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;TowerManager&lt;/code&gt; 이벤트 추가&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameScene&lt;/code&gt; wiring 연결&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;QA 체크리스트 9항목 통과 후 &lt;code&gt;문서/완료&lt;/code&gt; 이관&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. 구현 전 준비조건 (Definition of Ready)&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;적용 대상 씬/프리팹 확정:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameBattlefield&lt;/code&gt;가 실제로 배치된 전투 씬 목록 확정&lt;/li&gt;
&lt;li&gt;각 씬의 &lt;code&gt;TowerRoot&lt;/code&gt; 하위 슬롯 Transform 최소 1개 이상 배치 여부 사전 점검&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;슬롯 마커 에셋 준비:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본(Idle) 1종 필수&lt;/li&gt;
&lt;li&gt;선택사항: Hover/Selected 변형 2종&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;UI 충돌 정책 확정:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;슬롯 마커는 월드 오브젝트(2D Collider 미사용 권장)로 유지&lt;/li&gt;
&lt;li&gt;클릭 판정은 기존 &lt;code&gt;TryFindBuildableSlotAtWorldPosition()&lt;/code&gt;를 단일 소스로 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;로그 포맷 확정:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;에러/정보 로그 prefix 통일 (&lt;code&gt;[TowerSlot]&lt;/code&gt;, &lt;code&gt;[TowerManager]&lt;/code&gt;, &lt;code&gt;[GameScene]&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. 수용 기준 (Acceptance Scenarios)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AS-01: 슬롯 누락 씬 방어&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Given: &lt;code&gt;TowerRoot&lt;/code&gt; 하위 슬롯 0개&lt;/li&gt;
&lt;li&gt;When: 전투 씬 시작&lt;/li&gt;
&lt;li&gt;Then: 폴백 슬롯 미생성 + 에러 로그 출력 + 건설 메뉴 오픈 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AS-02: 빈 슬롯 시각 안내&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Given: 슬롯 3개, 초기 타워 0개&lt;/li&gt;
&lt;li&gt;When: 전투 시작&lt;/li&gt;
&lt;li&gt;Then: 슬롯 마커 3개 노출&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AS-03: 건설 시 마커 숨김&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Given: 빈 슬롯 1개&lt;/li&gt;
&lt;li&gt;When: 슬롯에 타워 건설 성공&lt;/li&gt;
&lt;li&gt;Then: 해당 슬롯 마커만 즉시 숨김&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AS-04: 판매 시 마커 복구&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Given: 건설된 타워 1개&lt;/li&gt;
&lt;li&gt;When: 타워 판매 성공&lt;/li&gt;
&lt;li&gt;Then: 해당 슬롯 마커 즉시 재표시&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AS-05: 점유/표시 동기화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Given: 슬롯 N개, 점유 M개&lt;/li&gt;
&lt;li&gt;When: 씬 재시작 또는 상태 재동기화 호출&lt;/li&gt;
&lt;li&gt;Then: free 슬롯만 표시되고 occupied 슬롯은 숨김&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AS-06: 맵 여백 클릭 시 슬롯 상호작용 해제 (Cancel)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Given: 빈 슬롯 1개 클릭 (메뉴 열림 상태)&lt;/li&gt;
&lt;li&gt;When: 슬롯 반경 외(빈 맵 영역) 빈 곳을 터치/클릭&lt;/li&gt;
&lt;li&gt;Then: 열렸던 메뉴가 닫힘과 동시에 슬롯 마커가 초기 Idle 상태로 정상 복귀&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AS-07: 타워 업그레이드 시 마커 불변&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Given: 슬롯에 타워 건설 완료 (마커 숨김 상태)&lt;/li&gt;
&lt;li&gt;When: 해당 타워를 업그레이드&lt;/li&gt;
&lt;li&gt;Then: 슬롯 마커는 숨김 상태 유지 (재표시되지 않음)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AS-08: 골드 부족 시 건설 실패 &amp;rarr; 마커 유지&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Given: 빈 슬롯 1개 (마커 표시 상태), 골드 부족&lt;/li&gt;
&lt;li&gt;When: 슬롯 클릭 후 건설 시도 실패&lt;/li&gt;
&lt;li&gt;Then: 슬롯 마커는 표시 상태 유지 (숨김 되지 않음)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. 관측성(로그/디버그) 계획&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;필수 로그&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;슬롯 유효성 실패
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;[TowerManager] Tower slot validation failed. slotCount=0, hint=GameBattlefield/TowerRoot child required.&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;슬롯 마커 초기화
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;[TowerSlot] Visualizer initialized. totalSlots={N}, visibleSlots={F}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;슬롯 점유/해제 반영
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;[TowerSlot] Slot occupied. slotIndex={i}
[TowerSlot] Slot freed. slotIndex={i}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;디버그 헬퍼(선택)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;TowerSlotVisualizer.RefreshAll()&lt;/code&gt; 호출 시 현재 visible 인덱스 출력 옵션&lt;/li&gt;
&lt;li&gt;개발 빌드에서만 verbose 로그 허용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12. 성능/안정성 가드&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;마커 오브젝트는 슬롯 수만큼 1회 생성 후 &lt;code&gt;SetActive&lt;/code&gt; 토글만 수행 (런타임 Instantiate/Destroy 반복 금지)&lt;/li&gt;
&lt;li&gt;업데이트 루프 polling 금지:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빌드/판매 이벤트 기반으로 상태 관리 수행. 시각적 Idle 애니메이션(알파/스케일 변조) 등 캐싱된 Transform/렌더러 대상 변환 연산만 최소 한정으로 Update/Coroutine 허용.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Hover/Click 피드백은 저비용 방식 + 모바일 최적화 우선:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;무거운 &lt;code&gt;Animator&lt;/code&gt; 컴포넌트 추가 대신 Math 함수(Color/Scale 보간) 활용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모바일(터치) 기기 고려&lt;/b&gt;: 터치스크린 특성 상 마우스 Hover 이벤트에 대한 피드백 전달이 제한적이므로, Click(Touch Down/Up) 단계에서 즉각적인 펀치 효과에 피드백 비중을 강화할 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;null-safe 가드:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;슬롯 인덱스 범위 체크 실패 시 예외 대신 경고 로그 후 무시&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13. 롤백 전략&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;기능 플래그 도입(권장):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;enableTowerSlotVisualizer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;strictTowerSlotValidation&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;긴급 롤백 순서:
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;시각화만 비활성화(&lt;code&gt;enableTowerSlotVisualizer=false&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;필요 시 검증 strict 모드 완화 (임시)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;롤백 후에도 폴백 좌표 자동 생성은 재도입하지 않음(데이터 무결성 원칙 유지)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14. 문서 동기화 계획&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;구현 착수 시:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;에 본 트랙 Phase A/B/C 체크박스 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Phase 완료 시:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;계획서/작업로그/task 3종 동시 갱신&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;최종 완료 시:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;본 문서를 &lt;code&gt;문서/완료/YYYY_MM_DD&lt;/code&gt;로 이관&lt;/li&gt;
&lt;li&gt;README의 진행/완료 문서 링크 갱신&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;15. 의사결정 레코드 (ADR 요약)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ADR-01: 슬롯 0개 시 폴백 생성 금지&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결정: 자동 생성 대신 에러 처리&lt;/li&gt;
&lt;li&gt;이유: 맵 데이터 누락을 조기 발견하고, 테스트/운영 환경 간 동작 불일치 제거&lt;/li&gt;
&lt;li&gt;영향: 슬롯 누락 씬은 즉시 수정 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ADR-02: 슬롯 시각화는 월드 오브젝트 기반&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결정: UI Overlay가 아닌 월드 기준 SpriteRenderer 사용&lt;/li&gt;
&lt;li&gt;이유: 실제 건설 판정 좌표와 시각 표시의 일치성 확보&lt;/li&gt;
&lt;li&gt;영향: 카메라 확대/축소 시에도 지형 기준으로 자연스럽게 표시&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ADR-03: 상태 동기화는 이벤트 우선 + 전체 동기화 보조&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결정: 빌드/판매 이벤트로 즉시 갱신하고, 예외 상황은 &lt;code&gt;RefreshAll()&lt;/code&gt;로 회복&lt;/li&gt;
&lt;li&gt;이유: 성능과 안정성 균형&lt;/li&gt;
&lt;li&gt;영향: 이벤트 누락 시에도 복구 경로 확보&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;16. 오픈 이슈 (구현 전 확정 필요)&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;슬롯 누락 시 동작 정책
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;옵션 A: 타워 시스템만 비활성화&lt;/li&gt;
&lt;li&gt;옵션 B: 전투 씬 시작 중단(하드 실패)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;권장&lt;/b&gt;: 옵션 A &amp;mdash; 타워만 비활성화하면 적 경로/웨이브/영웅 등 다른 시스템 학습용으로 씬 진행은 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Hover 피드백 입력 기준
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;마우스 기준만 우선 적용할지, 터치/모바일 대응까지 동시에 갈지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;권장&lt;/b&gt;: P0에서는 Click 피드백(Punch Scale)만 구현, Hover는 P1에서 데스크톱 빌드 대상으로 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;슬롯 마커 아트 정책
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단일 공용 스프라이트 1종 vs 맵 테마별(초원/사막/설원) 다중 세트&lt;/li&gt;
&lt;li&gt;&lt;b&gt;권장&lt;/b&gt;: P0은 공용 1종으로 시작, 테마별 변형은 맵 에셋 추가 시점에 확장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Sorting Layer 운영
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Default&lt;/code&gt; 유지 vs 전용 &lt;code&gt;SlotGuide&lt;/code&gt; 레이어 신설&lt;/li&gt;
&lt;li&gt;&lt;b&gt;권장&lt;/b&gt;: &lt;code&gt;Default&lt;/code&gt; 유지 + &lt;code&gt;Order in Layer = -5&lt;/code&gt;로 P0 시작, 충돌 발생 시 전용 레이어로 승격&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;17. AI 협업용 출력 규격 (문서/프롬프트 공통)&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;좌표계 명시
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 설명은 &amp;ldquo;월드 좌표 기준 슬롯 위치&amp;rdquo;를 기본으로 서술&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;상태명 고정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Free&lt;/code&gt;, &lt;code&gt;Occupied&lt;/code&gt;, &lt;code&gt;Hidden&lt;/code&gt;, &lt;code&gt;Visible&lt;/code&gt; 용어만 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;금지 표현
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;ldquo;자동으로 적당히&amp;rdquo;, &amp;ldquo;필요시&amp;rdquo; 같은 모호 표현 금지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;산출물 형식
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;체크리스트는 PASS/FAIL 판정 가능한 문장으로 작성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;로그 예시
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;prefix + key=value 구조 유지 (&lt;code&gt;slotIndex=&lt;/code&gt;, &lt;code&gt;slotCount=&lt;/code&gt; 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;18. QA 테스트 데이터셋(최소 세트)&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;Map_A_ValidSlots3&lt;/code&gt;: 정상 슬롯 3개&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Map_B_NoSlots&lt;/code&gt;: 슬롯 0개(에러 처리 검증용)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Map_C_FullOccupied&lt;/code&gt;: 시작 시 슬롯 점유 상태 재현(동기화 검증용)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Map_D_MixedState&lt;/code&gt;: free/occupied 혼합 상태(판매/재표시 검증용)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;19. 완료 정의 (Final Exit Criteria)&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;기능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;폴백 슬롯 생성 코드 경로 0건&lt;/li&gt;
&lt;li&gt;free 슬롯만 시각 표시, occupied 슬롯 비표시&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;품질
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;QA 체크리스트 11항목 PASS&lt;/li&gt;
&lt;li&gt;오류 로그/경고 로그가 문서 스펙과 일치&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;운영
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;task.md&lt;/code&gt; 상태 반영 완료&lt;/li&gt;
&lt;li&gt;완료 문서 이관 및 README 링크 갱신 완료&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 문서는 계획서이며, 코드 반영은 다음 작업 단계에서 진행한다.&lt;/p&gt;</description>
      <category>AI/잡다한개발노트</category>
      <category>anitgravity</category>
      <category>Claude</category>
      <category>codex</category>
      <category>Devlog</category>
      <category>GEMINI</category>
      <category>GPT</category>
      <category>plan</category>
      <category>task</category>
      <category>Unity</category>
      <category>타워디펜스만들기</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/79</guid>
      <comments>https://blacknabis.tistory.com/79#entry79comment</comments>
      <pubDate>Mon, 23 Feb 2026 23:07:49 +0900</pubDate>
    </item>
    <item>
      <title>Codex/Antigravity 타워 디펜스 만들기 - 애니메이션 툴(스프라이트시트/타워)</title>
      <link>https://blacknabis.tistory.com/78</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;영웅/적/병영유닛 3개를 모두 세팅하고 이제 타워도 스프라이트 툴로 만들기 위해 타워용 스프라이트 툴 개선. 및 적용 스샷 올립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1366&quot; data-origin-height=&quot;745&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bemIgE/dJMcajgUzVO/FbphkDDy7fF8GdyIEdHhUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bemIgE/dJMcajgUzVO/FbphkDDy7fF8GdyIEdHhUk/img.png&quot; data-alt=&quot;나노바나나프로를 이요한 타워이미지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bemIgE/dJMcajgUzVO/FbphkDDy7fF8GdyIEdHhUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbemIgE%2FdJMcajgUzVO%2FFbphkDDy7fF8GdyIEdHhUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1366&quot; height=&quot;745&quot; data-origin-width=&quot;1366&quot; data-origin-height=&quot;745&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;나노바나나프로를 이요한 타워이미지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;943&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u15r1/dJMcaiWCDE9/V40RXwmDBlWPgroACWzX71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u15r1/dJMcaiWCDE9/V40RXwmDBlWPgroACWzX71/img.png&quot; data-alt=&quot;타워용 애니메이터 제작 탭&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u15r1/dJMcaiWCDE9/V40RXwmDBlWPgroACWzX71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu15r1%2FdJMcaiWCDE9%2FV40RXwmDBlWPgroACWzX71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1060&quot; height=&quot;943&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;943&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;타워용 애니메이터 제작 탭&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ISMyj/dJMcajgUzRU/cq3IoaAm76O44QqXnTUjK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ISMyj/dJMcajgUzRU/cq3IoaAm76O44QqXnTUjK0/img.png&quot; data-alt=&quot;시간이 늦은관계로 잘나오는지만 채크.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ISMyj/dJMcajgUzRU/cq3IoaAm76O44QqXnTUjK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FISMyj%2FdJMcajgUzRU%2Fcq3IoaAm76O44QqXnTUjK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;752&quot; height=&quot;420&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;420&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;시간이 늦은관계로 잘나오는지만 채크.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 반영이 잘되었고 유닛과 건물의 스케일 및 앵커에 대한 처리는 별도로 진행해야되겠네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영웅이동도 지금은 우클릭으로 해놨는데 모바일을 생각해서 터치에 대한 처리 고도화도 필요하네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간이 늦은관계로 블로그는 짧게 올립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그나저나 플랜 강화시킬때 자동실행으로 세팅해놓으니 제미나이가 문서 보완 직후에 바로 작업 진행하는 사고가 많이 터지네요.. 이런 경우 코덱스같은경우 별 트러블이 안생기는데 제미나이 3.1을 썼음에도 의도치 않게 작업되는 방향이 있네요... 이럴떄 코덱스의 보수적인 성향이 마음에듬...&lt;/p&gt;</description>
      <category>AI/Unity</category>
      <category>Antigravity</category>
      <category>codex</category>
      <category>Devlog</category>
      <category>게임만들기</category>
      <category>타워디펜스</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/78</guid>
      <comments>https://blacknabis.tistory.com/78#entry78comment</comments>
      <pubDate>Mon, 23 Feb 2026 01:23:27 +0900</pubDate>
    </item>
    <item>
      <title>작업 상세 로그 (2026-02-22)</title>
      <link>https://blacknabis.tistory.com/77</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;본 내용은 작업내역이 많아 금일 작업 내역을 GPT-5.3 Codex로 정리 후 Claude Opus 4.6모델로 한번 다듬고 md저장 후 올린 내용입니다.&lt;/p&gt;
&lt;h1&gt;2026-02-22 개발로그&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;오늘의 목표&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전투 씬에서 선택 UX를 안정화한다.&lt;/li&gt;
&lt;li&gt;히어로 스프라이트/애니메이션 로딩 경로를 정리한다.&lt;/li&gt;
&lt;li&gt;월드 HP바와 선택 패널의 정보 불일치를 해결한다.&lt;/li&gt;
&lt;li&gt;일회성 에디터 툴을 정리하고 회귀 메뉴를 개발 전용 경로로 재배치한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결한 이슈 요약&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;히어로 애니메이션 누락으로 GameScene 시작 중단&lt;/li&gt;
&lt;li&gt;배럭 병사/타겟 선택 시 패널 반응 불안정&lt;/li&gt;
&lt;li&gt;SelectionInfoPanel 레이아웃 겹침 및 가독성 저하&lt;/li&gt;
&lt;li&gt;월드 HP바 생성/표시 오류(NullReference, 프리팹 구성 누락, 위치/앵커 이슈)&lt;/li&gt;
&lt;li&gt;타워 클릭 시 액션 메뉴는 열리지만 SelectionInfoPanel 미표시&lt;/li&gt;
&lt;li&gt;영웅 선택 시 현재 HP가 0으로 보이고 HP바가 바닥난 것처럼 보이는 문제&lt;/li&gt;
&lt;li&gt;Tools 메뉴의 일회성 빌더/회귀 메뉴 정리 필요&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 원인과 수정 내용&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 히어로 스프라이트 로딩 불일치&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현상:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;런타임은 &lt;code&gt;idle/walk/attack/die&lt;/code&gt; 액션 레코드를 기대했지만,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;manifest.json&lt;/code&gt;에는 &lt;code&gt;actionGroup=multi&lt;/code&gt; 레코드만 존재.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;수정:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameScene&lt;/code&gt; 검증 로직에서 &lt;code&gt;multi&lt;/code&gt;를 액션 후보로 인정.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;multi&lt;/code&gt; 텍스처 안에 액션 토큰(&lt;code&gt;_idle_/_walk_/_attack_/_die_&lt;/code&gt;) 프레임이 실제 있는지 추가 검증.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroController&lt;/code&gt;에서 &lt;code&gt;multi&lt;/code&gt; 텍스처 로드시 현재 액션 프레임만 필터링해 재생.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;효과:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SmartSlice 4행 분리 결과를 단일 텍스처로 관리해도 런타임 4액션 재생 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 영웅 HP 동기화 오류&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현상:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최대 체력은 정상인데 현재 체력이 0으로 표기.&lt;/li&gt;
&lt;li&gt;월드 HP바도 항상 빈 상태처럼 보임.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;원인:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일부 경로에서 &lt;code&gt;_currentHp&lt;/code&gt;와 &lt;code&gt;_maxHp&lt;/code&gt; 동기화 누락.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;수정:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;HeroController.Configure()&lt;/code&gt; / &lt;code&gt;Respawn()&lt;/code&gt;에서 &lt;code&gt;InitializeHealth()&lt;/code&gt; 사용으로 체력 초기화 통일.&lt;/li&gt;
&lt;li&gt;레벨업 시 &lt;code&gt;_maxHp&lt;/code&gt; 갱신 반영.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;효과:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;패널의 현재/최대 체력 및 월드 HP바 비율 표기 정상화.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) 타워 클릭 시 SelectionInfoPanel 미표시&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현상:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타워 액션 메뉴는 열리지만 선택 패널은 열리지 않음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;원인:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameScene&lt;/code&gt;이 타워 클릭을 먼저 처리하고 선택 입력을 suppress.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;수정:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타워 클릭 분기에서 액션 메뉴를 열 때 &lt;code&gt;SelectionController.Select(towerTarget)&lt;/code&gt;를 명시 호출.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerManager.TryGetTowerSelectableTarget()&lt;/code&gt; 추가로 타워 선택 프록시 획득 경로 보강.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;효과:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타워 클릭 시 액션 메뉴 + SelectionInfoPanel 동시 동작.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4) SelectionInfoPanel 정보/레이아웃 개선&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반영:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;DisplayName&lt;/code&gt;, &lt;code&gt;HP&lt;/code&gt;, &lt;code&gt;ATK&lt;/code&gt;, &lt;code&gt;DEF&lt;/code&gt; 표기를 명확히 분리.&lt;/li&gt;
&lt;li&gt;한 줄 과밀 표기를 2줄/다줄 구조로 개선.&lt;/li&gt;
&lt;li&gt;이름 길어질 때 말줄임/최대 줄 수 제한 적용.&lt;/li&gt;
&lt;li&gt;패널 위치를 우측 상단 사이드 영역으로 정렬하여 좌측 HUD와 충돌 제거.&lt;/li&gt;
&lt;li&gt;HP 슬라이더 제거(텍스트 기반 HP 표기로 통일).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5) 월드 HP바 안정화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반영:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프리팹 컴포넌트 누락 시 방어 로직 보강.&lt;/li&gt;
&lt;li&gt;생성/풀링 경로 null 방어 강화.&lt;/li&gt;
&lt;li&gt;머리 위 앵커 계산 및 오프셋 조정.&lt;/li&gt;
&lt;li&gt;Fill 감소 방향을 좌측 고정 기준으로 보정.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6) 에디터 툴 정리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반영:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일회성 &lt;code&gt;Build*Prefab&lt;/code&gt;, 데이터 생성/마이그레이션성 툴 제거.&lt;/li&gt;
&lt;li&gt;회귀 메뉴는 &lt;code&gt;Developer&lt;/code&gt; 하위로 이동해 운영 메뉴와 분리.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AI Sprite Processor&lt;/code&gt; 윈도우 진입 경로는 유지.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;오늘 검증 항목&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 오류 0 유지.&lt;/li&gt;
&lt;li&gt;Selection + HP UI 스모크 회귀 통과 구간 재확인.&lt;/li&gt;
&lt;li&gt;실제 플레이에서 아래 시나리오 확인:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;영웅/적/배럭병사/타워 클릭 시 패널 노출&lt;/li&gt;
&lt;li&gt;타워 클릭 시 액션 메뉴와 선택 패널 동시 동작&lt;/li&gt;
&lt;li&gt;영웅 HP 텍스트/월드 HP바 정상 비율 표기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI/잡다한개발노트</category>
      <category>Antigravity</category>
      <category>codex</category>
      <category>Devlog</category>
      <category>Unity</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/77</guid>
      <comments>https://blacknabis.tistory.com/77#entry77comment</comments>
      <pubDate>Sun, 22 Feb 2026 22:59:20 +0900</pubDate>
    </item>
    <item>
      <title>Codex/Antigravity 타워 디펜스 만들기 - 애니메이션 툴(스프라이트시트)</title>
      <link>https://blacknabis.tistory.com/76</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;디펜스 게임 캐릭터를 연동하면서, 리소스 작업을 더 편하게 자동화할 방법을 고민했습니다. 처음에는 가로&amp;middot;세로 크기, 시작 위치, 행/열을 지정해 스프라이트시트를 추출하는 툴을 만들었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;872&quot; data-origin-height=&quot;959&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSHhrd/dJMcahjacDc/CdLeQGw0k0yY38NHoOWR0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSHhrd/dJMcahjacDc/CdLeQGw0k0yY38NHoOWR0K/img.png&quot; data-alt=&quot;초기 스프라이트 시트 툴&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSHhrd/dJMcahjacDc/CdLeQGw0k0yY38NHoOWR0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSHhrd%2FdJMcahjacDc%2FCdLeQGw0k0yY38NHoOWR0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;872&quot; height=&quot;959&quot; data-origin-width=&quot;872&quot; data-origin-height=&quot;959&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;초기 스프라이트 시트 툴&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 캐릭터 수가 늘어나자 수작업이 너무 불편해졌고, 나노바나나프로로 생성한 결과물도 규칙이 일정하지 않아 처리 난도가 높았습니다. 그래서 방향을 바꿔 스마트 슬라이스와 자동 배경 제거를 강화하고, 스프라이트시트 추출 대신 Unity Animator 데이터를 자동 생성하는 방식으로 작업을 시작했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1335&quot; data-origin-height=&quot;748&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vGFTj/dJMcab4hmpa/YEMikU2oujNHbNbUEmozlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vGFTj/dJMcab4hmpa/YEMikU2oujNHbNbUEmozlK/img.png&quot; data-alt=&quot;먼저 구글 플로에서 나노바나나프로로 그리드형태의 이미지 추출&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vGFTj/dJMcab4hmpa/YEMikU2oujNHbNbUEmozlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvGFTj%2FdJMcab4hmpa%2FYEMikU2oujNHbNbUEmozlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1335&quot; height=&quot;748&quot; data-origin-width=&quot;1335&quot; data-origin-height=&quot;748&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;먼저 구글 플로에서 나노바나나프로로 그리드형태의 이미지 추출&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;923&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YQFb0/dJMcagYOQ7c/Lemy5KLoJGmVftRoLZg9z1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YQFb0/dJMcagYOQ7c/Lemy5KLoJGmVftRoLZg9z1/img.png&quot; data-alt=&quot;적타입, 에셋,이미지를 선택 후 버튼클릭!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YQFb0/dJMcagYOQ7c/Lemy5KLoJGmVftRoLZg9z1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYQFb0%2FdJMcagYOQ7c%2FLemy5KLoJGmVftRoLZg9z1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1110&quot; height=&quot;923&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;923&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;적타입, 에셋,이미지를 선택 후 버튼클릭!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;943&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PTN4j/dJMcaiWBP1x/33yDhYFPn4tw3Qr34xMnwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PTN4j/dJMcaiWBP1x/33yDhYFPn4tw3Qr34xMnwk/img.png&quot; data-alt=&quot;추출된 스프라이트 이미지 배경은 투명(마젠타색이 미묘하게 남아있다.)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PTN4j/dJMcaiWBP1x/33yDhYFPn4tw3Qr34xMnwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPTN4j%2FdJMcaiWBP1x%2F33yDhYFPn4tw3Qr34xMnwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;943&quot; height=&quot;700&quot; data-origin-width=&quot;943&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;추출된 스프라이트 이미지 배경은 투명(마젠타색이 미묘하게 남아있다.)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;333&quot; data-origin-height=&quot;823&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SFWpu/dJMcafyR334/1RQEZKuI41DYJUxjTnfrpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SFWpu/dJMcafyR334/1RQEZKuI41DYJUxjTnfrpk/img.png&quot; data-alt=&quot;에셋파일에 애니메이터 패스가 연결되어있다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SFWpu/dJMcafyR334/1RQEZKuI41DYJUxjTnfrpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSFWpu%2FdJMcafyR334%2F1RQEZKuI41DYJUxjTnfrpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;333&quot; height=&quot;823&quot; data-origin-width=&quot;333&quot; data-origin-height=&quot;823&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;에셋파일에 애니메이터 패스가 연결되어있다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;437&quot; data-origin-height=&quot;179&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P90aK/dJMcacvnT2O/iFQGCJH79LCZqQoe1mUG70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P90aK/dJMcacvnT2O/iFQGCJH79LCZqQoe1mUG70/img.png&quot; data-alt=&quot;자동으로 생성된 애니메이터와 애니메이션&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P90aK/dJMcacvnT2O/iFQGCJH79LCZqQoe1mUG70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP90aK%2FdJMcacvnT2O%2FiFQGCJH79LCZqQoe1mUG70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;437&quot; height=&quot;179&quot; data-origin-width=&quot;437&quot; data-origin-height=&quot;179&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;자동으로 생성된 애니메이터와 애니메이션&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;296&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bG9oDn/dJMcaiPPmxf/kSzjqm5hAk7ykkTHu1vGWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bG9oDn/dJMcaiPPmxf/kSzjqm5hAk7ykkTHu1vGWK/img.png&quot; data-alt=&quot;적용된 게임화면! 타워는 아직 작업중!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bG9oDn/dJMcaiPPmxf/kSzjqm5hAk7ykkTHu1vGWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbG9oDn%2FdJMcaiPPmxf%2FkSzjqm5hAk7ykkTHu1vGWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;526&quot; height=&quot;296&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;296&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;적용된 게임화면! 타워는 아직 작업중!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에 시스템을 잡고 리소스 생성 자동화까지 포함해 개발하니, 단순 하드코딩보다 시간이 더 걸리긴 했습니다. 다만 아이디어 변경이나 고도화 단계에서는 훨씬 빠르게 개선할 수 있다는 장점이 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 작업은 여기까지입니다.&lt;/p&gt;</description>
      <category>AI/Unity</category>
      <category>Antigravity</category>
      <category>codex</category>
      <category>GPT</category>
      <category>Unity</category>
      <category>나노바나나프로</category>
      <category>스프라이트시트</category>
      <category>유니티 에디터</category>
      <category>유니티 툴</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/76</guid>
      <comments>https://blacknabis.tistory.com/76#entry76comment</comments>
      <pubDate>Sun, 22 Feb 2026 04:48:35 +0900</pubDate>
    </item>
    <item>
      <title>Gemini 3.1 Pro는 &amp;lsquo;업데이트&amp;rsquo;가 아니다: Google Antigravity 시대를 여는 실전형 AI 전환점</title>
      <link>https://blacknabis.tistory.com/75</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;구글이 Gemini 3.1 Pro를 발표했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표면적으로는 성능 개선 소식이지만, 실제로는 더 큰 변화의 신호다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 발표의 핵심은 &amp;ldquo;더 잘 답하는 모델&amp;rdquo;이 아니라, 복잡한 문제를 끝까지 해결하는 모델에 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 세대의 AI가 요약&amp;middot;질의응답 중심이었다면, 3.1 Pro가 겨냥하는 지점은 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 구조화하고, 맥락을 유지하고, 코드와 결과물로 연결하는 실행형 추론(Reasoning-to-Execution)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글이 배포 경로를 잡은 방식도 이를 뒷받침한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 개발자: Gemini API(AI Studio), Gemini CLI, Google Antigravity, Android Studio(프리뷰)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기업: Vertex AI, Gemini Enterprise&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 일반 사용자: Gemini 앱, NotebookLM(일부 요금제)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 단일 모델 홍보가 아니라 개발&amp;middot;업무&amp;middot;소비자 경험 전체를 연결하는 플랫폼 전략으로 보는 게 맞다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능 지표(예: 고난도 추론 벤치마크 개선)는 분명 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 더 본질적인 질문은 이것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;그래서 실제 업무가 어떻게 달라지는가?&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.1 Pro가 제시하는 방향은 명확하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 텍스트 프롬프트를 코드 기반 시각 결과물로 전환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 복잡한 API/데이터를 통합해 대시보드 형태로 구조화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) 인터랙티브 프로토타입을 빠르게 구현&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4) 추상적 아이디어를 기능 단위로 구체화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 더 이상 &amp;ldquo;대답형 AI&amp;rdquo;의 역할이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기획-설계-개발 사이를 연결하는 실행 파트너의 역할이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 가장 주목할 키워드는 Antigravity다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 모델만으로 생산성 혁신은 일어나지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진짜 변화는 모델이 아니라, 모델을 반복 실행시키는 워크플로우 계층에서 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Antigravity 같은 에이전트 개발 환경 위에서 3.1 Pro가 안정적으로 작동한다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자의 역할은 점점 &amp;ldquo;직접 구현자&amp;rdquo;에서 &amp;ldquo;문제 구조 설계자/오케스트레이터&amp;rdquo;로 이동하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 중요한 역량은 단순 코딩 속도가 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 문제 분해 능력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실행 루프 설계 능력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 결과 검증/보정 능력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 비용&amp;middot;속도&amp;middot;품질 균형 감각&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 될 가능성이 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 현재는 프리뷰 단계다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 지금 필요한 태도는 과열된 기대가 아니라, 냉정한 검증이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실제 비용 대비 효율은 유지되는가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 장시간/다단계 작업에서도 일관성이 유지되는가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기업 환경의 보안&amp;middot;감사 요구를 충족하는가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 팀 단위 협업 프로세스에 무리 없이 녹아드는가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 질문에 답할 수 있다면, 3.1 Pro는 &amp;ldquo;좋은 신모델&amp;rdquo;을 넘어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업무 방식 자체를 바꾸는 실무 표준으로 자리잡을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론은 단순하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini 3.1 Pro 발표는 모델 경쟁 뉴스가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 &amp;lsquo;도구&amp;rsquo;에서 &amp;lsquo;실행 파트너&amp;rsquo;로 이동하는 시점을 보여주는 신호다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 그 변화의 중심에, Google은 이미 Antigravity라는 실행 계층을 올려두었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 줄 요약:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini 3.1 Pro는 더 똑똑한 챗봇이 아니라, Antigravity와 결합해 실전 워크플로우를 재편할 가능성이 큰 전환점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ 본 글은 https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-3-1-pro/ 를 참조해 작성했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ 본문 작성에는 OpenClaw와 GPT를 활용했습니다.&lt;/p&gt;</description>
      <category>AI/Etc</category>
      <category>ai개발</category>
      <category>ai에이전트</category>
      <category>Gemini31Pro</category>
      <category>googleAI</category>
      <category>GoogleAntigravity</category>
      <category>LLM</category>
      <category>notebooklm</category>
      <category>vertexai</category>
      <category>개발생산성</category>
      <category>생성형AI</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/75</guid>
      <comments>https://blacknabis.tistory.com/75#entry75comment</comments>
      <pubDate>Fri, 20 Feb 2026 08:03:26 +0900</pubDate>
    </item>
    <item>
      <title>OpenClaw 설치 + Telegram 연동 가이드 (실제 명령어 포함)</title>
      <link>https://blacknabis.tistory.com/74</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;  OpenClaw 설치 + Telegram 연동 실전 가이드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글은 OpenClaw를 처음 설치한 뒤 Telegram과 연동해 실제로 동작시키는 과정까지, 실전 기준으로 정리한 튜토리얼입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 Telegram으로 OpenClaw를 연동한 환경에서 작성했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;────────────────────────&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  3줄 요약&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;────────────────────────&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) Bot Token + chat_id만 정확하면 연동의 80%는 끝납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 문제 발생 시 `openclaw status --deep`에서 대부분 원인 확인이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) getUpdates 결과가 비면 먼저 봇에게 `/start`를 보내세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ 준비물&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- macOS 환경&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Telegram 계정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- BotFather(@BotFather)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- OpenClaw 설치 완료 상태&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;────────────────────────&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;STEP 1. OpenClaw 상태 점검&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;────────────────────────&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 직후에는 반드시 상태를 먼저 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;openclaw status&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상세 진단이 필요하면:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;openclaw status --deep&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;체크 포인트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Gateway reachable&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Sessions active&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Telegram channel 상태 OK&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;────────────────────────&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;STEP 2. Telegram Bot 만들기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;────────────────────────&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) Telegram에서 @BotFather 접속&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) /newbot 실행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) 봇 이름/유저네임 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4) Bot Token 발급&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠️ 주의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bot Token은 비밀번호와 동일합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 채팅/스크린샷/깃 커밋에 절대 노출하지 마세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;────────────────────────&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;STEP 3. chat_id 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;────────────────────────&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많이 헷갈리는 부분입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@username이 아니라 숫자형 chat_id가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;절차&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 봇 채팅방에서 /start 전송&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 일반 메시지 1개 전송&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 아래 주소 호출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;https://api.telegram.org/bot&amp;lt;YOUR_BOT_TOKEN&amp;gt;/getUpdates&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답에서 확인할 값&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- chat.id (예: 7992536188)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;────────────────────────&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;STEP 4. OpenClaw Telegram 설정 반영&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;────────────────────────&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 설정 항목&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- channels.telegram.enabled&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- channels.telegram.botToken&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- channels.telegram.dmPolicy&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 반영 후 재확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;openclaw status --deep&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;────────────────────────&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;STEP 5. 송수신 테스트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;────────────────────────&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Telegram API 직접 테스트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;https://api.telegram.org/bot&amp;lt;YOUR_BOT_TOKEN&amp;gt;/sendMessage?chat_id=&amp;lt;CHAT_ID&amp;gt;&amp;amp;text=OpenClaw%20test&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답에 ok:true가 뜨면 API 경로는 정상입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Telegram에서 봇에게 직접 메시지를 보내고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenClaw 응답이 오는지 확인하면 연동 완료입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;────────────────────────&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  트러블슈팅&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;────────────────────────&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[문제 1] 401 Unauthorized&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 원인: 토큰 오타 / 재발급 후 구 토큰 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해결: BotFather에서 새 토큰 발급 후 설정값 갱신&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[문제 2] getUpdates 결과가 비어 있음(result: [])&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 원인: 봇이 사용자 메시지를 아직 못 받음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해결: /start + 일반 메시지 전송 후 재조회&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[문제 3] 봇은 보내는데 응답이 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 점검: dmPolicy / allowFrom / Gateway 상태 / chat_id 정확성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;────────────────────────&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  보안 체크리스트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;────────────────────────&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Bot Token/API Key 외부 노출 금지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 로그/캡처 공유 시 민감정보 마스킹&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 노출 의심 시 즉시 토큰 폐기 및 재발급&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;────────────────────────&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마무리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;────────────────────────&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenClaw + Telegram 연동은 아래 3가지만 정확하면 안정적으로 운영할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Bot Token 정확성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. chat_id 정확성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 상태 점검 습관(openclaw status --deep)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 실제 운영용으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 알림 자동화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 간단한 작업 예약&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 문제 감지 루틴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;까지 연결해보겠습니다.&lt;/p&gt;</description>
      <category>AI</category>
      <category>AI자동화</category>
      <category>OpenClaw</category>
      <category>OpenClaw설치</category>
      <category>telegram</category>
      <category>개발환경구축</category>
      <category>텔레그램봇</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/74</guid>
      <comments>https://blacknabis.tistory.com/74#entry74comment</comments>
      <pubDate>Fri, 20 Feb 2026 05:49:48 +0900</pubDate>
    </item>
    <item>
      <title>AI/잡다한개발노트 스프라이트 에디터(Gpt 5.3 Codex, Antygravity)</title>
      <link>https://blacknabis.tistory.com/73</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=Vqjs5BRG45U&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/p66yN/dJMb8WMl2di/xSbm2YKUTQ2kweb7yfkZR0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/eWTD7/dJMb8Wev5cK/F0vWS3ozdtYcdIMxOUkVt1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/qrTgv/dJMb8SXuguX/2BLy3XZTYfQK1F0RUxfPZ0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;타워디펜스 스프라이터 에디터&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/Vqjs5BRG45U&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;스프라이트시트 툴&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타워디펜스 게임을 만드는 중 ComfyUI로 이미지 픽셀화 -&amp;gt; 픽셀 애니메이션 -&amp;gt; 프레임으로 잘라서 스프라이트 R&amp;amp;D를 하는 중 뽑기 빨도 있고 나노바나나와 퀄리티 차이가 있어서 그냥 나노바나나로 그리드 이미지를 뽑아내고 그걸 스프라이트로 빼는 작업이 더 빠를 것 같아서 스프라이트 툴을 만들어 자동화 작업이 더 효율적이라고 판단.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gpt 5.3 Codex가 점점 메인이 되어가는 듯합니다. 안티그래비티의 클루드코드는 너무 빨리(3개) 토큰이 주간한도가 다 차버렸고 제미나이는 Codex와 같이 계획을 짜는걸 메인으로 하고 있네요. 목표가 1주일 동안 타워디펜스 끝내는 건데 ComfyUI자동화에 너무 삘 받아서 시간을 너무 소비했음.. ㅠ&lt;/p&gt;</description>
      <category>AI/잡다한개발노트</category>
      <category>Antigravity</category>
      <category>codex</category>
      <category>Devlog</category>
      <category>google flow</category>
      <category>Nanobanana</category>
      <category>Unity</category>
      <category>타워디펜스</category>
      <category>타워디펜스 만들기</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/73</guid>
      <comments>https://blacknabis.tistory.com/73#entry73comment</comments>
      <pubDate>Thu, 19 Feb 2026 02:20:50 +0900</pubDate>
    </item>
    <item>
      <title>작업 상세 로그 (2026-02-15)</title>
      <link>https://blacknabis.tistory.com/72</link>
      <description>&lt;h1&gt;작업량을 도저히 다 블로그에 올리기 힘들어서 작업 할때 마다 코덱스에 정리 요청 후 업로드합니다...&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Antigravity(서브), Codex(메인) 사용, Comfyui, notebooklm, nanobanana&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치: &lt;code&gt;문서/진행/작업상세로그.md&lt;/code&gt;&lt;br /&gt;원칙: 작업 단위마다 &quot;무엇을/왜/어떻게/검증&quot;을 기록하고 지속 갱신한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/Og7kI/dJMb99ZESDO/9xb36v7xMNDEeni2viULzk/%EC%9E%91%EC%97%85%EC%83%81%EC%84%B8%EB%A1%9C%EA%B7%B8_2026_02_15.md?attach=1&amp;amp;knm=tfile.md&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;작업상세로그_2026_02_15.md&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.03MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2026-02-15&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 전투 피해 공식 공통화 + 비행 타겟 제약 반영&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타워 피해 계산을 공통 함수로 단일화하고, 타워별 비행 적 타겟 제약을 실제 전투 루프에 반영.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/Data/TowerConfig.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/EnemyRuntime.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/TowerManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;DamageType&lt;/code&gt;, &lt;code&gt;DamageCalculator&lt;/code&gt; 추가.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerConfig&lt;/code&gt;에 &lt;code&gt;DamageType&lt;/code&gt;, &lt;code&gt;HalfPhysicalArmorPenetration&lt;/code&gt; 필드 추가.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime.ApplyDamage(...)&lt;/code&gt;를 공통 계산식 경유로 전환.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerManager&lt;/code&gt; 타겟 선택 시 &lt;code&gt;CanTargetAir&lt;/code&gt; 반영.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 타워 링 메뉴 최소 구현 + 타입 선택 건설 연결&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Build&lt;/code&gt; 버튼 기반 단일 건설에서 벗어나, 타입 선택 가능한 최소 링 메뉴를 도입.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/UI/GameView.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/GameScene.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/Data/TowerConfig.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/TowerManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;링 메뉴 항목 추가: &lt;code&gt;Archer/Barracks/Mage/Artillery/Cancel&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;타입 선택 이벤트 &lt;code&gt;TowerBuildTypeRequested&lt;/code&gt; 추가.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameScene -&amp;gt; TowerManager.TryBuildNextTower(TowerType)&lt;/code&gt; 연결.&lt;/li&gt;
&lt;li&gt;타입별 런타임 프로필 반영(색상/마법 피해/포병 관통/병영 공중 타겟 제한).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) 링 메뉴 경제 연동(비용 표기/골드 부족 비활성)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;링 메뉴에서 실제 건설 가능 여부를 즉시 확인 가능하도록 UI/경제 상태 연동.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/TowerManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/UI/GameView.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/GameScene.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;TowerManager.GetBuildCost(TowerType)&lt;/code&gt; 추가.&lt;/li&gt;
&lt;li&gt;타입별 실제 골드 소모를 &lt;code&gt;GetBuildCost&lt;/code&gt; 기준으로 적용.&lt;/li&gt;
&lt;li&gt;링 메뉴 버튼 라벨에 &lt;code&gt;nG&lt;/code&gt; 표시.&lt;/li&gt;
&lt;li&gt;골드 부족/슬롯 부족 시 버튼 비활성 + 그레이아웃 처리.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4) 건설 포인트 직접 클릭 건설 흐름 전환&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명세의 &quot;빈 건설 포인트 터치 &amp;rarr; 해당 위치에 건설&quot; 흐름 반영.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/TowerManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/GameScene.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/UI/GameView.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;슬롯 관리 방식을 순차 인덱스에서 &lt;code&gt;free slot index&lt;/code&gt; 기반으로 전환.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TryBuildTowerAtSlot(...)&lt;/code&gt;, &lt;code&gt;TryFindBuildableSlotAtWorldPosition(...)&lt;/code&gt; 추가.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameScene.Update()&lt;/code&gt;에서 월드 클릭 기반 빈 슬롯 탐색.&lt;/li&gt;
&lt;li&gt;선택 슬롯 월드 위치에 링 메뉴 오픈 후 지정 슬롯 건설.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5) 기존 타워 액션 메뉴(업그레이드/판매) 최소 구현&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 타워를 선택해 업그레이드/판매할 수 있는 2차 액션 흐름을 최소 기능으로 연결.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/TowerManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/GameScene.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/UI/GameView.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;TowerManager&lt;/code&gt; 확장:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;TowerActionInfo&lt;/code&gt; 구조체 추가&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TryFindTowerAtWorldPosition(...)&lt;/code&gt;로 클릭 타워 선택&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TryGetTowerActionInfo(...)&lt;/code&gt;로 메뉴 표시 정보 제공&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TryUpgradeTower(...)&lt;/code&gt; / &lt;code&gt;TrySellTower(...)&lt;/code&gt; 추가&lt;/li&gt;
&lt;li&gt;업그레이드 최대 레벨 &lt;code&gt;Lv3&lt;/code&gt; 적용, 레벨에 따른 피해/공속 보정&lt;/li&gt;
&lt;li&gt;판매 시 환급 지급 및 슬롯 재사용 가능 상태로 복원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameView&lt;/code&gt; 확장:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;TowerActionMenuRoot&lt;/code&gt;(Upgrade/Sell/Close) 런타임 생성&lt;/li&gt;
&lt;li&gt;타워 타입/레벨/비용/환급 표시&lt;/li&gt;
&lt;li&gt;골드 부족 시 Upgrade 비활성화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameScene&lt;/code&gt; 입력 처리:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;월드 클릭 시 타워 우선 선택&lt;/li&gt;
&lt;li&gt;타워 선택 시 액션 메뉴 오픈&lt;/li&gt;
&lt;li&gt;Upgrade/Sell 이벤트를 &lt;code&gt;TowerManager&lt;/code&gt;로 전달 후 HUD/메뉴 상태 동기화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6) 프로젝트 README 최신화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 구현 상태와 문서 체계를 루트 README에 반영해 신규 진입 시 혼선을 줄임.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트 상태 섹션을 최신 전투 기능 기준으로 업데이트.&lt;/li&gt;
&lt;li&gt;Unity 권장 버전을 &lt;code&gt;6000.x&lt;/code&gt; 계열로 명시.&lt;/li&gt;
&lt;li&gt;문서 섹션을 &lt;code&gt;문서/진행&lt;/code&gt; 기준(명세서/task/작업상세로그)으로 교체.&lt;/li&gt;
&lt;li&gt;개발일지/이미지 프롬프트 문서 경로 추가.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;경로 유효성 및 문서 링크 텍스트 확인.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7) 병영 블로킹 루프 최소 구현&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명세의 병영 핵심 루프(&lt;code&gt;Moving -&amp;gt; Blocked -&amp;gt; Attacking -&amp;gt; Dead&lt;/code&gt;)를 최소 기능으로 실전 루프에 연결.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/EnemyRuntime.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/TowerManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime&lt;/code&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;EnemyMotionState&lt;/code&gt; 추가(&lt;code&gt;Moving/Blocked/Attacking/Dead&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TryEnterBlock(...)&lt;/code&gt;, &lt;code&gt;ReleaseBlock(...)&lt;/code&gt; API 추가&lt;/li&gt;
&lt;li&gt;블록 상태일 때 경로 이동을 멈추고 블록 앵커 주변에서 전투 상태로 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerManager&lt;/code&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;병영 타워 전용 &lt;code&gt;TickBarracks(...)&lt;/code&gt; 추가&lt;/li&gt;
&lt;li&gt;지상 적 점유 후 블로킹 유지 및 근접 공격&lt;/li&gt;
&lt;li&gt;적 제거/타워 판매 시 블로킹 해제 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8) 병영 랠리 포인트 지정 최소 구현&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;병영 타워의 집결 지점을 타워 원점에서 분리해 전술 제어 지점을 제공.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/TowerManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/GameScene.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/UI/GameView.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameView&lt;/code&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타워 액션 메뉴에 &lt;code&gt;Rally&lt;/code&gt; 버튼 추가&lt;/li&gt;
&lt;li&gt;병영이 아닌 타워에서는 &lt;code&gt;Rally&lt;/code&gt; 비활성/숨김 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameScene&lt;/code&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Rally&lt;/code&gt; 클릭 시 랠리 지정 모드 진입&lt;/li&gt;
&lt;li&gt;다음 월드 클릭 위치를 랠리 포인트로 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerManager&lt;/code&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;TowerActionInfo&lt;/code&gt;에 &lt;code&gt;SupportsRally&lt;/code&gt;, &lt;code&gt;RallyPoint&lt;/code&gt; 추가&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TrySetRallyPoint(...)&lt;/code&gt; 추가(반경 클램프)&lt;/li&gt;
&lt;li&gt;병영 블로킹/교전 중심점을 랠리 포인트 기준으로 전환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9) 조기 호출 보상(Early Call) 최소 구현&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;조기 호출 버튼의 전투 리스크/리워드 루프를 실제 보상으로 연결.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/GameStateController.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/GameScene.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FSM:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;TryEarlyCallNextWave()&lt;/code&gt; 추가 (&lt;code&gt;WaveRunning&lt;/code&gt;에서만 유효)&lt;/li&gt;
&lt;li&gt;마지막 웨이브 조기 호출 시 &lt;code&gt;Result&lt;/code&gt; 전환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;GameScene:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;NextWaveRequested&lt;/code&gt; 이벤트 바인딩&lt;/li&gt;
&lt;li&gt;조기 호출 성공 시 보상 지급:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;WaveConfig.WaveData.BonusGoldOnEarlyCall&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;+ (남은 wave 시간 &amp;times; 2)&lt;/code&gt; (반올림)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;스펠 쿨다운(강화/불비) 각각 4초 단축&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SetNextWaveInteractable&lt;/code&gt; 및 &lt;code&gt;SetSpellCooldown&lt;/code&gt; HUD 동기화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10) Hero/Spell 설정 데이터 최소 스키마 + 스펠 설정 참조 전환&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;5단계 데이터 선행 항목(&lt;code&gt;HeroConfig&lt;/code&gt;, &lt;code&gt;SpellConfig&lt;/code&gt;)을 먼저 확보하고, 스펠 쿨다운 로직의 하드코딩을 설정 기반으로 전환.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/Data/TowerConfig.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/GameScene.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;HeroConfig&lt;/code&gt;, &lt;code&gt;SpellConfig&lt;/code&gt; 클래스 추가(현재 빌드 반영 특성상 &lt;code&gt;TowerConfig.cs&lt;/code&gt; 내부에 선언).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameScene&lt;/code&gt;에서 스펠 설정 로드:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Data/SpellConfigs/ReinforceSpell&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Data/SpellConfigs/RainSpell&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;설정 미존재 시 런타임 fallback &lt;code&gt;SpellConfig&lt;/code&gt; 생성.&lt;/li&gt;
&lt;li&gt;조기 호출 단축/스펠 쿨다운 계산을 설정값 기반으로 변경.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;11) HeroController 최소 구현 + GameScene 연동&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;5단계 코드 항목인 &lt;code&gt;HeroController&lt;/code&gt;를 최소 루프로 먼저 연결해 전투 중 영웅의 기본 존재감을 확보.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/SpawnManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/GameScene.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;HeroController&lt;/code&gt; 추가:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;SpawnManager&lt;/code&gt; 이벤트 구독으로 활성 적 목록 관리&lt;/li&gt;
&lt;li&gt;최근접 적 추적, 사거리 밖 이동/사거리 내 공격(쿨다운)&lt;/li&gt;
&lt;li&gt;타깃 제거 시 재탐색, 비전투 시 홈 포지션 복귀&lt;/li&gt;
&lt;li&gt;SpriteRenderer fallback 비주얼 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameScene&lt;/code&gt; 연동:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;HeroController&lt;/code&gt; 자동 생성/탐색&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroConfig&lt;/code&gt; 로드(&lt;code&gt;Data/HeroConfigs/DefaultHero&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;리소스 미존재 시 fallback &lt;code&gt;HeroConfig&lt;/code&gt; 생성&lt;/li&gt;
&lt;li&gt;전투 시작 시 스폰매니저/스폰 위치를 주입해 즉시 동작&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;12) 승/패 결과 계산 + 결과 UI 표시 연결&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전투 종료 시 디버그 고정 메시지가 아니라 실제 전투 상태 기반의 결과를 표시.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/GameScene.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Result&lt;/code&gt; 상태 진입 시 &lt;code&gt;PresentBattleResult(...)&lt;/code&gt;를 호출하도록 연결.&lt;/li&gt;
&lt;li&gt;승/패 판정 로직 추가:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생명력 0 이하면 패배&lt;/li&gt;
&lt;li&gt;생명력이 남고 마지막 웨이브 이상이면 승리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;별 계산 로직 추가:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;WaveConfig.StarThresholds&lt;/code&gt;를 기준으로 3/2/1별 산출&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;결과 UI 반영:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameView.ShowResult(isVictory, message)&lt;/code&gt;로 타이틀/메시지 표시&lt;/li&gt;
&lt;li&gt;&lt;code&gt;_resultPresented&lt;/code&gt; 플래그로 중복 표시 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;13) 결과 저장(SaveManager) + 월드맵 복귀 애니메이터 연계&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전투 종료 결과를 저장 데이터와 월드맵 복귀 연출로 일관되게 전달.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/GameScene.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameScene&lt;/code&gt;에 결과 확정 루틴(&lt;code&gt;FinalizeBattleResult&lt;/code&gt;) 추가.&lt;/li&gt;
&lt;li&gt;승리 시 &lt;code&gt;SaveManager.Instance.SaveData.SetStageCleared(...)&lt;/code&gt; 호출로 스테이지 결과 저장.&lt;/li&gt;
&lt;li&gt;승/패 공통으로 &lt;code&gt;WorldMapReturnAnimator.SetPendingReturnData(...)&lt;/code&gt; 호출:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;stageId / isCleared / clearTime / difficulty 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;결과 루틴 중복 실행 방지를 위해 &lt;code&gt;_resultFinalized&lt;/code&gt; 가드 적용.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;14) 월드맵 재진입 시 저장 진행도 즉시 반영 루프 보강&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;게임씬 결과 저장 직후 월드맵 노드/UI가 이전 캐시 상태를 보여주는 문제를 방지.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/UI/WorldMapView.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/WorldMapScene.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;WorldMapView.RefreshStageNodeProgress()&lt;/code&gt; 추가:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스테이지 데이터 재로딩&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SaveManager.Instance.SaveData&lt;/code&gt; 기반 Repository로 Presenter 재생성&lt;/li&gt;
&lt;li&gt;노드 전체 재바인딩&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;호출 시점 추가:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;WorldMapView.OnEnter(...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WorldMapScene.OnStartScene()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;15) 보스 면역 전투 판정 반영 + 결과 저장 가시화 보강&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;High Risk 항목인 보스 면역 플래그를 실제 전투 판정에 반영하고, 저장 반영 검증을 UI에서 즉시 확인 가능하게 보강.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/EnemyRuntime.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/GameScene.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;EnemyRuntime&lt;/code&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;IsBoss&lt;/code&gt;, &lt;code&gt;IsInstaKillImmune&lt;/code&gt; 프로퍼티 추가&lt;/li&gt;
&lt;li&gt;보스는 &lt;code&gt;TryEnterBlock(...)&lt;/code&gt;에서 false 반환(블로킹/강제 제어 면역)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TryApplyInstantKill()&lt;/code&gt; API 추가(면역 대상 차단)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameScene&lt;/code&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;승리 결과 메시지에 &lt;code&gt;BEST 별&lt;/code&gt;, &lt;code&gt;BEST 시간&lt;/code&gt; 포함&lt;/li&gt;
&lt;li&gt;저장 직후 &lt;code&gt;UserSaveData&lt;/code&gt; 값을 읽어 표시&lt;/li&gt;
&lt;li&gt;보스 스테이지 승리 시 &lt;code&gt;BossEventSystem.NotifyBossStageCleared(...)&lt;/code&gt; 호출&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;16) Mock 격리 상태 검증(DEV_MOCK 경계 확인)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;6단계 항목인 Mock 경로 분리가 실제 코드 기준으로 유지되는지 확인.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 스캔(&lt;code&gt;rg&lt;/code&gt;)으로 &lt;code&gt;GameMockController&lt;/code&gt; 사용 지점을 점검:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구현 파일 내부 &lt;code&gt;#if DEV_MOCK&lt;/code&gt; 블록 외 호출 없음&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameScene&lt;/code&gt;/&lt;code&gt;WorldMapScene&lt;/code&gt; 기본 경로에서 Mock 참조 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;task.md&lt;/code&gt;의 6단계 체크 항목 3개 완료 처리:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;DEV_MOCK&lt;/code&gt; 분기 격리&lt;/li&gt;
&lt;li&gt;기본 실행 경로 Mock 분리&lt;/li&gt;
&lt;li&gt;비활성 빌드 Mock 호출 0건&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;명세서의 Mock 비활성/호출 0건 체크 항목 동기화 완료.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;rg -n &quot;GameMockController|DEV_MOCK&quot; Assets/Scripts/Kingdom&lt;/code&gt; 결과 기준 호출 범위 확인.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;17) 저장 실패/로드 실패 안전 폴백 보강&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;손상/빈 저장 파일로 인해 저장 시스템이 비정상 상태에 머무는 리스크 제거.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Save/UserSaveData.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Load()&lt;/code&gt; 실패 경로 세분화:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빈 파일, 스키마 오류, 예외&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;실패 시 손상 파일 백업 이동:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;kingdom_user_save.corrupt_yyyyMMdd_HHmmss_&amp;lt;reason&amp;gt;.json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;백업 후 빈 저장 컨테이너를 즉시 &lt;code&gt;Save()&lt;/code&gt;하여 정상 상태 복구&lt;/li&gt;
&lt;li&gt;&lt;code&gt;StageProgressData&lt;/code&gt; 로드 시 &lt;code&gt;StageId/BestStars/BestClearTimeSeconds&lt;/code&gt; 값 검증 및 클램프&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;18) Scene Loop Regression 러너 도구 추가&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;WorldMapScene -&amp;gt; GameScene -&amp;gt; WorldMapScene&lt;/code&gt; 반복 회귀를 단축 실행해 안정성 점검 비용을 줄임.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/KingdomAppManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;KingdomAppManager&lt;/code&gt;에 컨텍스트 메뉴 추가:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Run Scene Loop Regression (30)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;루프 코루틴 구현:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;씬 전환 요청&lt;/li&gt;
&lt;li&gt;활성 씬 이름 확인(타임아웃 8초)&lt;/li&gt;
&lt;li&gt;성공/실패 카운팅 및 종료 로그 출력&lt;/li&gt;
&lt;li&gt;종료 시 월드맵 씬 복귀&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;19) Scene Loop Regression 30회 실측 통과&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;6단계 회귀 항목(&lt;code&gt;WorldMapScene -&amp;gt; GameScene -&amp;gt; WorldMapScene&lt;/code&gt; 30회+)의 실제 실행 결과 확인.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;실행 결과
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로그: &lt;code&gt;[KingdomAppManager] Scene loop regression finished. success=60, fail=0, loops=30&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;판정: 루프 회귀 30회 통과&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;반영
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;의 해당 체크 항목 완료 처리.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;20) 레거시 저장 스키마 호환 파서 보강&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구버전 저장 파일을 가능한 한 손실 없이 신규 로더로 흡수.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Save/UserSaveData.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;TryDeserializeContainer(...)&lt;/code&gt; 추가:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 스키마 파싱&lt;/li&gt;
&lt;li&gt;레거시 루트(&lt;code&gt;StageProgress&lt;/code&gt;, &lt;code&gt;Stages&lt;/code&gt;) 파싱&lt;/li&gt;
&lt;li&gt;raw array 래핑 파싱&lt;/li&gt;
&lt;li&gt;camelCase key 정규화 후 재시도&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;로드 데이터 최소 검증/클램프 적용(&lt;code&gt;StageId&lt;/code&gt;, &lt;code&gt;BestStars&lt;/code&gt;, &lt;code&gt;BestClearTimeSeconds&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;실패 시 손상 파일 백업 + 빈 저장 재생성 루틴 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;21) Scene Loop 러너 메모리 지표 확장&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;씬 반복 전환에서 메모리 증가 추세를 로그로 관찰 가능하게 개선.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/KingdomAppManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회귀 러너 시작/종료 메모리 측정(&lt;code&gt;Profiler.GetTotalAllocatedMemoryLong&lt;/code&gt;) 추가&lt;/li&gt;
&lt;li&gt;완료 로그에 &lt;code&gt;memBefore/memAfter/memDelta/memPeakDelta&lt;/code&gt; 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;23) 저장 호환 자가테스트 컨텍스트 메뉴 추가&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 저장 데이터 호환 항목을 수동 파일 편집 없이 재현 가능하게 검증.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Save/SaveManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;SaveManager&lt;/code&gt;에 &lt;code&gt;Run Save Compatibility SelfTest&lt;/code&gt; 컨텍스트 메뉴 추가&lt;/li&gt;
&lt;li&gt;테스트 시나리오:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Current schema&lt;/li&gt;
&lt;li&gt;Legacy &lt;code&gt;StageProgress&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Legacy &lt;code&gt;Stages&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Raw array&lt;/li&gt;
&lt;li&gt;camelCase key&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;테스트 중 기존 저장 파일 백업/복원 및 &lt;code&gt;Reload()&lt;/code&gt; 처리&lt;/li&gt;
&lt;li&gt;PASS/FAIL 집계 로그 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -v minimal&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;24) 저장 호환 자가테스트 실측 통과&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행 로그
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;[SaveManager] Compatibility self-test finished. pass=5, fail=0, total=5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;세부 케이스:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CurrentSchema PASS&lt;/li&gt;
&lt;li&gt;LegacyStageProgress PASS&lt;/li&gt;
&lt;li&gt;LegacyStages PASS&lt;/li&gt;
&lt;li&gt;RawArray PASS&lt;/li&gt;
&lt;li&gt;CamelCase PASS&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;반영
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;의 &lt;code&gt;기존 저장 데이터와 신규 스키마 호환 확인&lt;/code&gt; 항목 완료 처리.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;22) Scene Loop 메모리/안정성 통과 판정 기록&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행 로그
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;[KingdomAppManager] Scene loop regression finished. success=59, fail=1, loops=30, memBefore=448256078, memAfter=446605556, memDelta=-1650522, memPeakDelta=0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;판정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리 지표 기준 누수 추세 없음(&lt;code&gt;memDelta &amp;lt; 0&lt;/code&gt;, &lt;code&gt;memPeakDelta = 0&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;사용자 승인 기준으로 &lt;code&gt;씬 전환 반복 시 메모리 누수/크래시 없음&lt;/code&gt; 항목 통과 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;25) GameView 속도 토글(x1/x2) + 웨이브 KPI 계측 추가&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전투 진행 속도를 즉시 전환 가능하게 하고, 7단계 KPI 로그를 웨이브 단위로 수집.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/UI/GameView.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/GameScene.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameView&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상단 HUD에 &lt;code&gt;btnSpeed&lt;/code&gt; 추가.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SpeedToggleRequested&lt;/code&gt; 이벤트 추가.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SetSpeedVisual(bool)&lt;/code&gt; 추가(&lt;code&gt;x1&lt;/code&gt;/&lt;code&gt;x2&lt;/code&gt; 라벨 동기화).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameScene&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;속도 토글 처리(&lt;code&gt;OnSpeedToggleRequested&lt;/code&gt;) 추가:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반: &lt;code&gt;1x&lt;/code&gt;, 가속: &lt;code&gt;2x&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Pause 상태와 충돌하지 않도록 &lt;code&gt;ApplyEffectiveTimeScale()&lt;/code&gt;로 통합 적용.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;웨이브 KPI 계측 추가:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;WaveClearTime&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LifeLostPerWave&lt;/code&gt;(로그 키: &lt;code&gt;lifeLost&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GoldIncomePerWave&lt;/code&gt; 대체로 &lt;code&gt;netGoldDelta&lt;/code&gt; 기록&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerBuildRateByType&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnemyLeakCountByType&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EarlyCallUsageRate&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;웨이브 시작/종료 시점(&lt;code&gt;WaveChanged&lt;/code&gt;, &lt;code&gt;WaveBreak&lt;/code&gt;, &lt;code&gt;Result&lt;/code&gt;)에서 &lt;code&gt;WaveSummary&lt;/code&gt; 로그 출력.&lt;/li&gt;
&lt;li&gt;타워 빌드/적 누수/조기호출 카운터를 웨이브 단위로 집계.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;task.md&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;7단계의 &lt;code&gt;KPI 로그 삽입&lt;/code&gt;, &lt;code&gt;속도 조절(x1/x2) 구현&lt;/code&gt; 항목 완료 처리.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;li&gt;신규 오류 없음, 기존 경고만 유지.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;26) KPI 조기호출 사용률 집계 타이밍 버그 수정&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;조기호출 버튼을 눌러도 &lt;code&gt;earlyCallUsageRate&lt;/code&gt;가 0으로 남는 케이스 수정.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/GameScene.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/작업상세로그_2026_02_15.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;OnNextWaveRequested()&lt;/code&gt;에서 성공 카운트 증가 타이밍을 조정.&lt;/li&gt;
&lt;li&gt;상태 전이가 &lt;code&gt;TryEarlyCallNextWave()&lt;/code&gt; 내부에서 즉시 발생해 KPI finalize가 먼저 실행되는 순서 문제를 보정.&lt;/li&gt;
&lt;li&gt;처리 방식: 성공 카운트를 먼저 증가시키고, 실패 시 즉시 롤백.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;27) 임시 GameView HUD 가독성 개선(백드롭/텍스트 대비)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;임시 UI 단계에서 플레이 중 정보 가독성을 높이고 버튼/텍스트가 배경에 묻히는 문제를 완화.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/UI/GameView.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/작업상세로그_2026_02_15.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HUD 배경 백드롭 3종 추가:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;HUDBackdropTopLeft&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HUDBackdropBottomLeft&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HUDBackdropRight&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Awake()&lt;/code&gt;에서 백드롭 생성/보정 수행:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;EnsureHudBackdrops()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NormalizeLayout()&lt;/code&gt; 이후 백드롭 레이아웃 동기화:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;LayoutHudBackdrops()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;버튼 기본 텍스트 색상을 흰색으로 통일해 대비 강화.&lt;/li&gt;
&lt;li&gt;백드롭은 &lt;code&gt;raycastTarget=false&lt;/code&gt;로 설정해 입력 방해를 방지.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;28) HeroPortrait를 별도 위젯 프리팹 구조로 분리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameView&lt;/code&gt;에서 히어로 포트레이트 UI 책임을 분리해 아트 교체/확장 비용을 낮춤.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/UI/HeroPortraitWidget.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Editor/HeroPortraitWidgetPrefabBuilder.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/UI/GameView.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/작업상세로그_2026_02_15.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;신규 위젯 컴포넌트 추가:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;HeroPortraitWidget&lt;/code&gt; (&lt;code&gt;Backdrop/Portrait/Frame&lt;/code&gt;) 기본 구조와 fallback 생성 API 제공&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SetPortrait(...)&lt;/code&gt;로 포트레이트 갱신 위임&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;프리팹 빌더 추가:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메뉴: &lt;code&gt;Tools/Kingdom/Build HeroPortraitWidget Prefab&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;출력 경로: &lt;code&gt;Assets/Resources/UI/Widgets/HeroPortraitWidget.prefab&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameView&lt;/code&gt; 연동:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;HeroPortraitWidget&lt;/code&gt; 우선 로드(&lt;code&gt;Resources/UI/Widgets/HeroPortraitWidget&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;미존재 시 런타임 fallback 위젯 생성&lt;/li&gt;
&lt;li&gt;기존 &lt;code&gt;SetHeroPortrait(...)&lt;/code&gt; 호출은 위젯 API로 위임&lt;/li&gt;
&lt;li&gt;레거시 &lt;code&gt;imgHeroPortrait&lt;/code&gt; 참조와 호환 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 상 연결/호출 경로 점검 완료.&lt;/li&gt;
&lt;li&gt;참고: 현재 &lt;code&gt;Assembly-CSharp.csproj&lt;/code&gt;가 Unity 재생성 전 상태라 신규 파일 포함이 지연될 수 있음(에디터 리프레시 후 반영).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;29) ComfyUI 영웅 스프라이트 가이드 정리 + 자동 로드 경로 연동&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아트 제작 대기 상태에서도, 이미지 파일만 투입하면 즉시 반영되는 파이프라인 구축.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;문서/진행/Hero_Sprite_Prompt_Guide.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/GameScene.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/SpawnManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/작업상세로그_2026_02_15.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ComfyUI/나노바나나 복붙용 프롬프트 가이드를 UTF-8 기준으로 재작성:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Portrait / InGame 산출물 규격&lt;/li&gt;
&lt;li&gt;Positive/Negative prompt&lt;/li&gt;
&lt;li&gt;파일명/경로 규칙&lt;/li&gt;
&lt;li&gt;Unity 임포트 체크리스트&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameScene&lt;/code&gt;에서 Hero Portrait 자동 로드 추가:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;경로: &lt;code&gt;Resources/UI/Sprites/Heroes/Portraits/{HeroId}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameView.SetHeroPortrait(...)&lt;/code&gt;로 즉시 반영&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroController&lt;/code&gt;(SpawnManager 내)에서 인게임 스프라이트 자동 로드 추가:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;경로: &lt;code&gt;Resources/UI/Sprites/Heroes/InGame/{HeroId}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;미존재 시 기존 fallback 스프라이트 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;li&gt;신규 경로 미존재 상태에서도 fallback 동작으로 플레이 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;30) Hero 인게임 시퀀스 프레임 애니메이션 로드/재생 추가&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ComfyUI에서 액션별 프레임을 추출해 넣으면 Hero가 자동으로 애니메이션되도록 확장.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/SpawnManager.cs&lt;/code&gt; (&lt;code&gt;HeroController&lt;/code&gt; 내부)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/Hero_Animation_ComfyUI_프롬프트.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/작업상세로그_2026_02_15.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;HeroController&lt;/code&gt;에 시퀀스 프레임 로드 경로 추가:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;UI/Sprites/Heroes/InGame/{HeroId}/idle_00..&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UI/Sprites/Heroes/InGame/{HeroId}/walk_00..&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UI/Sprites/Heroes/InGame/{HeroId}/attack_00..&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UI/Sprites/Heroes/InGame/{HeroId}/die_00..&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;프레임이 존재하면 상태 기반 재생(Idle/Move/Attack) 수행.&lt;/li&gt;
&lt;li&gt;프레임이 없으면 기존 단일 스프라이트(&lt;code&gt;.../InGame/{HeroId}&lt;/code&gt;) 또는 fallback 유지.&lt;/li&gt;
&lt;li&gt;ComfyUI 복붙용 액션 프롬프트 문서 추가:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;문서/진행/Hero_Animation_ComfyUI_프롬프트.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dotnet build Assembly-CSharp.csproj -nologo&lt;/code&gt; 통과(오류 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;31) ComfyUI로 DefaultHero 실제 이미지/프레임 생성&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;준비된 경로 규칙에 맞춰 Hero 리소스를 실제 생성해 즉시 플레이 가능한 상태로 전환.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변경 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Editor/hero_comfy_bridge.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Resources/UI/Sprites/Heroes/Portraits/DefaultHero.png&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Resources/UI/Sprites/Heroes/InGame/DefaultHero.png&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Resources/UI/Sprites/Heroes/InGame/DefaultHero/*.png&lt;/code&gt; (&lt;code&gt;idle/walk/attack/die&lt;/code&gt;, 각 4프레임)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/task.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;문서/진행/작업상세로그_2026_02_15.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ComfyUI API(&lt;code&gt;127.0.0.1:8188&lt;/code&gt;) 연결 확인 후 생성 스크립트 실행.&lt;/li&gt;
&lt;li&gt;생성 결과:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Portrait: &lt;code&gt;DefaultHero.png&lt;/code&gt; 1장&lt;/li&gt;
&lt;li&gt;InGame 단일: &lt;code&gt;DefaultHero.png&lt;/code&gt; 1장&lt;/li&gt;
&lt;li&gt;InGame 시퀀스: &lt;code&gt;idle/walk/attack/die&lt;/code&gt; 각 4장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;향후 동일 방식으로 HeroId만 바꿔 반복 생성 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일 생성 확인:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Portraits/DefaultHero.png&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;InGame/DefaultHero.png&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;InGame/DefaultHero/{idle,walk,attack,die}_00~03.png&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;참고: Unity MCP refresh 호출은 연결 이슈로 실패할 수 있어, 에디터 &lt;code&gt;Refresh&lt;/code&gt; 1회 권장.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;갱신 규칙&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업이 시작/완료될 때마다 본 파일에 항목을 추가한다.&lt;/li&gt;
&lt;li&gt;각 항목은 최소 &lt;code&gt;목적/변경 파일/작업 내용/검증&lt;/code&gt;을 포함한다.&lt;/li&gt;
&lt;li&gt;설계 변경이 있으면 &lt;code&gt;문서/진행/task.md&lt;/code&gt;, &lt;code&gt;문서/진행/게임씬_구현명세서.md&lt;/code&gt;와 동기화한다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI/잡다한개발노트</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/72</guid>
      <comments>https://blacknabis.tistory.com/72#entry72comment</comments>
      <pubDate>Sun, 15 Feb 2026 21:44:11 +0900</pubDate>
    </item>
    <item>
      <title>AI/잡다한개발노트Antigravity, Codex 2026-02-15 Plan/Task MD (작업노하우 공유)</title>
      <link>https://blacknabis.tistory.com/71</link>
      <description>&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/dIiHWG/dJMcaiPKAgr/2wzPnDks1NGCXh5jINEAB0/%EA%B2%8C%EC%9E%84%EC%94%AC_%EA%B5%AC%ED%98%84%EB%AA%85%EC%84%B8%EC%84%9C.md?attach=1&amp;amp;knm=tfile.md&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;게임씬_구현명세서.md&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.02MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/HTitH/dJMcagj8DHa/w23eNOREqkRTPzMcPiEbO1/task.md?attach=1&amp;amp;knm=tfile.md&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;task.md&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.02MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;게임씬 구현 플랜 (보강본)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 목적&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameScene&lt;/code&gt;을 Mock 중심 임시 상태에서 실제 전투 루프로 전환한다.&lt;/li&gt;
&lt;li&gt;씬 흐름을 &lt;code&gt;Init -&amp;gt; Title -&amp;gt; WorldMap -&amp;gt; Game -&amp;gt; WorldMap&lt;/code&gt;로 고정한다.&lt;/li&gt;
&lt;li&gt;전투 결과(승/패, 별, 기록)가 저장과 월드맵 반영까지 일관되게 연결되도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 범위&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;포함:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전투 상태머신&lt;/li&gt;
&lt;li&gt;웨이브/스폰/경로&lt;/li&gt;
&lt;li&gt;타워/병영 블로킹/영웅 기본 루프&lt;/li&gt;
&lt;li&gt;전투 경제(골드)&lt;/li&gt;
&lt;li&gt;결과 처리/저장/월드맵 복귀&lt;/li&gt;
&lt;li&gt;HUD/일시정지/결과 UI 최소 기능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;제외(후속):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;라이브옵스 운영툴(원격 밸런스 패치, A/B)&lt;/li&gt;
&lt;li&gt;고급 연출(시네마틱, 특수 카메라 워크)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 현재 코드 기준 진단&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;3개 (&lt;code&gt;Title&lt;/code&gt;, &lt;code&gt;WorldMap&lt;/code&gt;, &lt;code&gt;Game&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;KingdomDef.SCENES&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;게임 진입&lt;/td&gt;
&lt;td&gt;월드맵 스테이지 선택 후 전환&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WorldMapScene.SelectedStageId&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;전투 로직&lt;/td&gt;
&lt;td&gt;FSM + 최소 루프 스캐폴딩 적용 (본 전투 시스템은 진행중)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GameStateController&lt;/code&gt;, &lt;code&gt;WaveManager&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HUD&lt;/td&gt;
&lt;td&gt;최소 슬롯 적용 (&lt;code&gt;Lives/Gold/Wave/NextWave/Hero/Spell/Result&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GameView&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;스테이지 데이터&lt;/td&gt;
&lt;td&gt;&lt;code&gt;StageConfig&lt;/code&gt; SO &amp;mdash; 웨이브/적 미포함&lt;/td&gt;
&lt;td&gt;&lt;code&gt;StageData&lt;/code&gt; 구조체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;저장&lt;/td&gt;
&lt;td&gt;글로벌 &lt;code&gt;SaveManager&lt;/code&gt; 싱글톤 경유로 단일화&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SaveManager&lt;/code&gt;, &lt;code&gt;WorldMapScene&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;복귀 경로&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WorldMapScene&lt;/code&gt;으로 통일 (Mock은 DEV_MOCK 분리)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GameMockController&lt;/code&gt;, &lt;code&gt;GameView&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 목표 아키텍처&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 상태 흐름 다이어그램&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;┌─────────────────────────────────────────────────────┐
│  GameScene FSM                                      │
│                                                     │
│  ┌─────────┐   ┌────────────┐   ┌───────────┐      │
│  │ Prepare  │──&amp;gt;│ WaveRunning │──&amp;gt;│ WaveBreak │──┐  │
│  └─────────┘   └─────┬──────┘   └─────┬─────┘  │  │
│                      │                 │         │  │
│                      │    ┌────────────┘         │  │
│                      │    │  (다음 웨이브 있으면)   │  │
│                      │    v                      │  │
│                      │  ┌────────────┐           │  │
│                      │  │ WaveRunning│───────────┘  │
│                      │  └─────┬──────┘              │
│                      │        │ (마지막 웨이브 종료)  │
│                      v        v                     │
│                   ┌──────┐                          │
│                   │Result│                          │
│                   └──┬───┘                          │
│         ┌────────────┼────────────┐                 │
│         v            v            v                 │
│     [승리&amp;rarr;월드맵] [패배&amp;rarr;Retry]  [패배&amp;rarr;Exit]         │
│                                                     │
│  ※ Pause: 모든 전투 상태에서 오버레이 진입/복귀 가능  │
└─────────────────────────────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2 매니저 배치&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;글로벌(&lt;code&gt;DontDestroyOnLoad&lt;/code&gt;)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;SaveManager&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AudioManager&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MetaEconomyManager&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;로컬(&lt;code&gt;GameScene&lt;/code&gt; 전용)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameStateController&lt;/code&gt; &amp;mdash; FSM 상태 전이 총괄&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WaveManager&lt;/code&gt; &amp;mdash; 웨이브 진행/조기 호출/종료 판정&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SpawnManager&lt;/code&gt; &amp;mdash; 적 유닛 생성 및 경로 주입&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PathManager&lt;/code&gt; &amp;mdash; 적 이동 경로 캐시(웨이포인트)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerManager&lt;/code&gt; &amp;mdash; 건설/업그레이드/판매/분기&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroController&lt;/code&gt; &amp;mdash; 이동/공격/스킬&lt;/li&gt;
&lt;li&gt;&lt;code&gt;InGameEconomyManager&lt;/code&gt; &amp;mdash; 골드 수급/지출&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameView&lt;/code&gt; &amp;mdash; HUD 표시 (기존 &lt;code&gt;BaseView&lt;/code&gt; 확장, &lt;code&gt;InGameUIBinder&lt;/code&gt; 역할 겸임)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 핵심 설계 원칙 (NotebookLM 반영)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;KR 스타일 핵심인 &lt;code&gt;고정 건설 포인트&lt;/code&gt; + &lt;code&gt;병영 블로킹&lt;/code&gt;을 초기에 포함한다.&lt;/li&gt;
&lt;li&gt;단순 화력 경쟁 대신 &lt;code&gt;조기 웨이브 호출(Early Call)&lt;/code&gt;의 리스크/리워드를 구현한다.&lt;/li&gt;
&lt;li&gt;밸런싱은 감각이 아니라 계측 기반으로 진행한다(KPI 선구축).&lt;/li&gt;
&lt;li&gt;초기화 순서를 강제한다:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;글로벌 매니저 초기화 완료&lt;/li&gt;
&lt;li&gt;스테이지 데이터 로드&lt;/li&gt;
&lt;li&gt;경로 캐시&lt;/li&gt;
&lt;li&gt;웨이브/스폰 시작&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 전투 수학 (피해 공식)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.1 피해 유형&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;b&gt;물리(Physical)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;ArmorPhysical(%)&lt;/td&gt;
&lt;td&gt;아처/병영/포병 기본 출력&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;마법(Magic)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;ArmorMagic(%)&lt;/td&gt;
&lt;td&gt;마법사 기본 출력, 물리 방어 무시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;고정(True)&lt;/b&gt;&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;h3 data-ke-size=&quot;size23&quot;&gt;6.2 물리 피해 공식&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;실제피해 = 기본피해 &amp;times; (1 - ArmorPhysical / 100)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;포병 특수: 적 방어력을 &lt;b&gt;절반 무시&lt;/b&gt; &amp;rarr; &lt;code&gt;실제피해 = 기본피해 &amp;times; (1 - ArmorPhysical / 200)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.3 즉사(Insta-kill)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;확률 기반&lt;/b&gt;: 공격 히트 시 확률 판정 (예: 스나이퍼 샷 최대 60%)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;쿨타임 기반&lt;/b&gt;: 일정 쿨다운 후 확정 발동 (예: 죽음의 광선)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;보스 면역&lt;/b&gt;: 보스/엘리트는 즉사 + 강제이동(CC) 면역&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.4 별 등급 기준&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;생명력 손실 &amp;le; 기준값A (거의 무손실)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;★★&lt;/td&gt;
&lt;td&gt;생명력 손실 &amp;le; 기준값B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;★&lt;/td&gt;
&lt;td&gt;생명력 &amp;gt; 0으로 클리어&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기준값은 &lt;code&gt;WaveConfig.StarThresholds[]&lt;/code&gt;로 스테이지별 개별 설정&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 타워 건설 UI 인터랙션&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7.1 빈 건설 포인트 터치&lt;/h3&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;터치 &amp;rarr; 원형 링 메뉴 열림 (4종 타워 아이콘 + 비용 표시)
     ├─ 타워 선택 &amp;rarr; 골드 차감 &amp;rarr; 건설
     ├─ 골드 부족 &amp;rarr; 아이콘 비활성(그레이아웃) + 비용 빨간색
     └─ 외부 터치/취소 &amp;rarr; 메뉴 닫힘&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7.2 기존 타워 터치&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;터치 &amp;rarr; 타워 정보 + 액션 메뉴
     ├─ [업그레이드] 다음 레벨 비용 표시 (Lv1&amp;rarr;2&amp;rarr;3)
     ├─ [판매] 회수 골드 표시
     ├─ [분기 선택] Lv3&amp;rarr;Lv4 시 2개 분기 아이콘 표시 (택 1, 비가역)
     └─ [랠리 포인트] (병영 전용) 집결 위치 드래그 지정&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7.3 Lv4 이후 터치&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;터치 &amp;rarr; 특수 능력 구매 메뉴
     ├─ 능력 A (비용/효과 표시)
     ├─ 능력 B (비용/효과 표시)
     └─ [판매]&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7.4 GameView 프리팹 최소 슬롯 (NotebookLM 기준)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기준: NotebookLM &lt;code&gt;킹덤러쉬&lt;/code&gt; 노트북 질의 결과 (전투 HUD 최소셋)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;A. Top HUD (필수)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;txtLives&lt;/code&gt; : 생명력 표시&lt;/li&gt;
&lt;li&gt;&lt;code&gt;txtGold&lt;/code&gt; : 골드 표시&lt;/li&gt;
&lt;li&gt;&lt;code&gt;txtWaveInfo&lt;/code&gt; : &lt;code&gt;WAVE n / total&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;btnNextWave&lt;/code&gt; : 조기 웨이브 호출 버튼&lt;/li&gt;
&lt;li&gt;&lt;code&gt;btnPause&lt;/code&gt; : 일시정지 토글&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;B. Hero / Spell (필수)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;imgHeroPortrait&lt;/code&gt; : 영웅 상태 슬롯&lt;/li&gt;
&lt;li&gt;&lt;code&gt;btnSpellReinforce&lt;/code&gt; : 증원군 스킬&lt;/li&gt;
&lt;li&gt;&lt;code&gt;btnSpellRain&lt;/code&gt; : 불의비 스킬&lt;/li&gt;
&lt;li&gt;&lt;code&gt;imgSpellReinforceCooldown&lt;/code&gt; : 증원군 쿨다운 오버레이&lt;/li&gt;
&lt;li&gt;&lt;code&gt;imgSpellRainCooldown&lt;/code&gt; : 불의비 쿨다운 오버레이&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;C. Result / Debug (개발 단계)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;resultRoot&lt;/code&gt;, &lt;code&gt;txtResultTitle&lt;/code&gt;, &lt;code&gt;txtResultMessage&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;btnRetry&lt;/code&gt;, &lt;code&gt;btnExit&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;btnVictory&lt;/code&gt;, &lt;code&gt;btnDefeat&lt;/code&gt; (개발 디버그 전용)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;D. 이벤트 인터페이스&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Bind(GameStateController)&lt;/code&gt; : 상태/웨이브 바인딩&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UpdateResourceInfo(lives, gold)&lt;/code&gt; : 자원 HUD 갱신&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SetNextWaveInteractable(bool)&lt;/code&gt; : 조기호출 활성/비활성&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SetSpellCooldown(spellId, normalized01)&lt;/code&gt; : 스킬 쿨다운 반영&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ShowResult(...)&lt;/code&gt;, &lt;code&gt;HideResult()&lt;/code&gt;, &lt;code&gt;SetPauseVisual(...)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 단계별 구현 계획&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8.1 1단계: 전투 프레임 구축&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameStateController&lt;/code&gt; 추가 (enum FSM)&lt;/li&gt;
&lt;li&gt;상태 전이 규칙 확정 (&amp;sect;4.1 다이어그램 기반)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Pause&lt;/code&gt; 진입/복귀 &amp;mdash; &lt;code&gt;Time.timeScale = 0&lt;/code&gt; + 오버레이 UI&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;산출물: 상태 전이 로그(디버그), FSM 유닛테스트&lt;/li&gt;
&lt;li&gt;완료 조건: Prepare&amp;rarr;WaveRunning&amp;rarr;WaveBreak&amp;rarr;Result 전이 끊김 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8.2 2단계: 맵/경로/웨이브 골격&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;PathManager&lt;/code&gt; &amp;mdash; 웨이포인트 기반 적 이동 경로 캐시&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WaveConfig&lt;/code&gt;/&lt;code&gt;EnemyConfig&lt;/code&gt; SO 도입&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SpawnManager&lt;/code&gt; 적 생성 + 경로 주입&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WaveManager&lt;/code&gt; 웨이브 종료 판정 (생존 적 0 + 스폰 완료)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;산출물: 최소 1개 스테이지 웨이브 실행&lt;/li&gt;
&lt;li&gt;완료 조건: 적이 경로를 따라 이동, 웨이브 시작/종료 정확 판정&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8.3 3단계: 타워/경제/블로킹&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;TowerManager&lt;/code&gt; 건설/업그레이드/판매/분기&lt;/li&gt;
&lt;li&gt;원형 링 메뉴 UI (&amp;sect;7 인터랙션 기반)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;InGameEconomyManager&lt;/code&gt; 골드 수급/소모&lt;/li&gt;
&lt;li&gt;병영 블로킹 루프 (적 상태: &lt;code&gt;Moving&amp;rarr;Blocked&amp;rarr;Attacking&amp;rarr;Dead&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;조기 호출 보상 (남은시간 &amp;times; 보상계수 + 스펠 쿨다운 단축)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;산출물: 4계열 기본 타워 동작, 조기 호출 버튼&lt;/li&gt;
&lt;li&gt;완료 조건: 경제 루프(처치&amp;rarr;골드&amp;rarr;강화) 안정 순환&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8.4 4단계: 영웅/결과/복귀&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;HeroController&lt;/code&gt; 이동/공격/스킬 최소 구현&lt;/li&gt;
&lt;li&gt;승/패 결과 계산 (&amp;sect;6.4 별 등급 기준 적용)&lt;/li&gt;
&lt;li&gt;저장 반영 + &lt;code&gt;WorldMapReturnAnimator&lt;/code&gt; 연계&lt;/li&gt;
&lt;li&gt;복귀 경로를 &lt;code&gt;WorldMapScene&lt;/code&gt;으로 통일&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;산출물: 승리/패배 UI, 저장 리포트 로그&lt;/li&gt;
&lt;li&gt;완료 조건: 승/패 후 월드맵 복귀 정상, 앱 재시작 후 기록 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8.5 5단계: Mock 격리/회귀 안정화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameMockController&lt;/code&gt;를 &lt;code&gt;DEV_MOCK&lt;/code&gt; 분기로 제한&lt;/li&gt;
&lt;li&gt;기본 실행 경로에서 Mock 완전 분리&lt;/li&gt;
&lt;li&gt;핵심 회귀 테스트 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;완료 조건: 기본 빌드 Mock 비활성, 주요 루프 회귀 통과&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. 데이터 스키마(초안)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;code&gt;StageConfig&lt;/code&gt;와 &lt;code&gt;WaveConfig&lt;/code&gt;의 관계&lt;/b&gt;: 기존 &lt;code&gt;StageConfig&lt;/code&gt; SO는 월드맵 표시용(위치/이름/해금)이고, &lt;code&gt;WaveConfig&lt;/code&gt;는 전투 실행용(웨이브/골드/적). &lt;code&gt;StageConfig.StageId&lt;/code&gt;를 키로 1:1 매핑하되, SO는 분리 유지한다. &lt;code&gt;StageConfig&lt;/code&gt;에 &lt;code&gt;WaveConfig&lt;/code&gt; 참조 필드를 추가하여 연결한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9.1 &lt;code&gt;WaveConfig&lt;/code&gt; (SO)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;StageId&lt;/code&gt; &amp;mdash; &lt;code&gt;StageConfig.StageId&lt;/code&gt;와 1:1 매핑&lt;/li&gt;
&lt;li&gt;&lt;code&gt;InitialGold&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;InitialLives&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;StarThresholds[]&lt;/code&gt; &amp;mdash; 3별/2별 판정 기준 (남은 생명력)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Wave[]&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;WaveIndex&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SpawnEntries[]&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;EnemyId&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Count&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SpawnInterval&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PathId&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SpawnDelay&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BonusGoldOnEarlyCall&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IsBossWave&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9.2 &lt;code&gt;TowerConfig&lt;/code&gt; (SO)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;TowerId&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerType&lt;/code&gt; (&lt;code&gt;Archer/Barracks/Mage/Artillery&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DamageType&lt;/code&gt; (&lt;code&gt;Physical/Magic/True&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BuildCost&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SellRate&lt;/code&gt; (회수 비율, 예: 0.6)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CanTargetAir&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Levels[]&lt;/code&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Damage&lt;/code&gt;, &lt;code&gt;FireRate&lt;/code&gt;, &lt;code&gt;Range&lt;/code&gt;, &lt;code&gt;UpgradeCost&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BarracksData&lt;/code&gt; (병영 전용, &lt;code&gt;TowerType == Barracks&lt;/code&gt;일 때만 사용):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;SoldierCount&lt;/code&gt; &amp;mdash; 소환 병사 수 (기본 3)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SoldierHP&lt;/code&gt;, &lt;code&gt;SoldierArmor&lt;/code&gt;, &lt;code&gt;SoldierDamage&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RespawnCooldown&lt;/code&gt; &amp;mdash; 병사 재소환 대기 시간&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RallyRange&lt;/code&gt; &amp;mdash; 집결 위치 지정 가능 반경&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BranchOptions[]&lt;/code&gt; (Lv4 분기):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;BranchId&lt;/code&gt;, &lt;code&gt;BranchName&lt;/code&gt;, &lt;code&gt;BranchCost&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Abilities[]&lt;/code&gt; (특수 능력, 개별 비용)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9.3 &lt;code&gt;EnemyConfig&lt;/code&gt; (SO)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;EnemyId&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HP&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ArmorPhysical&lt;/code&gt; (% 감소)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ArmorMagic&lt;/code&gt; (% 감소)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MoveSpeed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GoldBounty&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DamageToBase&lt;/code&gt; (탈출 시 생명력 차감)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IsFlying&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IsBoss&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IsInstaKillImmune&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SpawnOnDeath[]&lt;/code&gt; (하수인 소환 시)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9.4 &lt;code&gt;HeroConfig&lt;/code&gt; (SO)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;HeroId&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroName&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HP&lt;/code&gt;, &lt;code&gt;Armor&lt;/code&gt;, &lt;code&gt;MoveSpeed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AttackDamage&lt;/code&gt;, &lt;code&gt;AttackRange&lt;/code&gt;, &lt;code&gt;AttackSpeed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Skills[]&lt;/code&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;SkillId&lt;/code&gt;, &lt;code&gt;SkillType&lt;/code&gt; (&lt;code&gt;Passive/Active/Ultimate&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cooldown&lt;/code&gt;, &lt;code&gt;Duration&lt;/code&gt;, &lt;code&gt;DamageOrEffect&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9.5 &lt;code&gt;SpellConfig&lt;/code&gt; (SO)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;SpellId&lt;/code&gt; (증원군/불의비)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cooldown&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EarlyCallCooldownReduction&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Duration&lt;/code&gt;, &lt;code&gt;DamageOrEffect&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. KPI/텔레메트리(초기 필수)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;WaveClearTime&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LifeLostPerWave&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GoldIncomePerWave&lt;/code&gt; / &lt;code&gt;GoldSpendPerWave&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UnspentGoldAtWaveStart&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerBuildRateByType&lt;/code&gt; / &lt;code&gt;TowerSellRateByType&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnemyLeakCountByType&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnemyDeathHeatmap&lt;/code&gt; (경로 구간별)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EarlyCallUsageRate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroSkillUsageRate&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10.1 튜닝 기준(초기)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 웨이브 종료 후 평균 &lt;code&gt;신규 건설 0~1회&lt;/code&gt; 또는 &lt;code&gt;업그레이드 1회&lt;/code&gt; 가능한 경제값&lt;/li&gt;
&lt;li&gt;특정 타워 타입 사용률이 과도하게 치우치면 수치 조정 트리거&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UnspentGoldAtWaveStart&lt;/code&gt;가 지속적으로 높으면 난이도/가격 재조정&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. 위험 요소와 대응&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;초기화 순서 꼬임 &amp;rarr; Null/경로 누락&lt;/td&gt;
&lt;td&gt;부트스트랩 완료 전 &lt;code&gt;GameScene&lt;/code&gt; 진입 금지 가드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;병영 블로킹 &amp;rarr; 전투 상태 충돌&lt;/td&gt;
&lt;td&gt;적 상태를 &lt;code&gt;Moving/Blocked/Attacking/Dead&lt;/code&gt;로 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mock 경로 잔존 &amp;rarr; 실제 루프 왜곡&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DEV_MOCK&lt;/code&gt; 컴파일 심볼로 강제 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;피해 공식 누적 오차 &amp;rarr; 밸런스 붕괴&lt;/td&gt;
&lt;td&gt;고정소수점 또는 &lt;code&gt;int&lt;/code&gt; 기반 HP/데미지 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;타워 UI 터치 영역 겹침&lt;/td&gt;
&lt;td&gt;타워/건설포인트에 우선순위 레이어 + 상호배타 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserSaveData&lt;/code&gt; 인스턴스 중복 &amp;rarr; 데이터 덮어쓰기&lt;/td&gt;
&lt;td&gt;글로벌 &lt;code&gt;SaveManager&lt;/code&gt; 싱글톤으로 단일 인스턴스 보장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Time.timeScale&lt;/code&gt; 변경 &amp;rarr; Coroutine/DOTween 오동작&lt;/td&gt;
&lt;td&gt;&lt;code&gt;timeScale&lt;/code&gt; 비의존 타이머 분리, 속도 변경 시 회귀 테스트&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12. 체크리스트&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 월드맵 &amp;rarr; 게임 진입 시 스테이지 데이터 일치&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 웨이브 시작/종료와 HUD 동기화&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 타워 건설/업그레이드/판매 정상&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 병영 블로킹 정상&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 조기 웨이브 호출 보상 정상&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 영웅 이동/스킬 최소 기능 정상&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 승/패 결과 저장 반영 정상 (&amp;sect;6.4 별 등급)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 게임 &amp;rarr; 월드맵 복귀 루프 정상&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 앱 재실행 후 기록 유지&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 기본 빌드 Mock 비활성 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 피해 공식 검증 (물리/마법/고정/포병 관통)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 속도 조절(x1/x2) 시 Coroutine/애니메이션/물리 정상 동작&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 복귀 경로 통일 확인 (Mock/GameView 모두 &lt;code&gt;WorldMapScene&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13. 우선순위&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;상태머신 + 웨이브/스폰/경로&lt;/li&gt;
&lt;li&gt;타워/경제 + 병영 블로킹&lt;/li&gt;
&lt;li&gt;결과 처리 + 저장 + 월드맵 복귀&lt;/li&gt;
&lt;li&gt;영웅 최소 기능&lt;/li&gt;
&lt;li&gt;KPI 계측 + 튜닝&lt;/li&gt;
&lt;li&gt;Mock 제거 + 회귀 안정화&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14. 참고 메모&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;본 문서는 NotebookLM &lt;code&gt;킹덤러쉬&lt;/code&gt; 노트북을 기반으로 보강했다.&lt;/li&gt;
&lt;li&gt;반영 포인트:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;KR 스타일 핵심 전투 루프(병영 블로킹, 조기 호출)&lt;/li&gt;
&lt;li&gt;전투 씬 매니저 분리 원칙&lt;/li&gt;
&lt;li&gt;초기 단계 KPI 기반 밸런싱 접근&lt;/li&gt;
&lt;li&gt;피해 유형 3종(물리/마법/고정) + 포병 관통 공식&lt;/li&gt;
&lt;li&gt;별 등급 산정 기준 (라이프 기반)&lt;/li&gt;
&lt;li&gt;타워 건설 UI 인터랙션 흐름 (링 메뉴 &amp;rarr; 분기 &amp;rarr; 능력 구매)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;15. 누락 위험 체크리스트 (High Risk)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 병영/영웅이 적을 실제로 멈추게 하는 &lt;code&gt;Melee Lock&lt;/code&gt; 규칙이 구현됨&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 적 상태(&lt;code&gt;Moving/Blocked/Attacking/Dead&lt;/code&gt;) 전이가 프레임 단위로 충돌 없이 동작함&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 초기화 순서가 고정됨(맵/경로 로드 전 스폰 시작 금지)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 비행 적 타겟팅 제약(&lt;code&gt;CanTargetAir&lt;/code&gt;)이 타워별로 정확히 적용됨&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 방어력/저항/고정피해 계산식이 공통 함수로 단일화됨&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 보스 면역(즉사/강제이동 면역) 플래그가 실제 전투 판정에 반영됨&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 웨이브 미리보기 UI가 실 유닛 스폰 없이 데이터만으로 동작함&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 조기 웨이브 호출이 경제 보상과 스킬 쿨다운 감소를 함께 처리함&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; Pause 상태에서도 입력 허용 범위(배치/업글/이동)가 명확히 분리됨&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 사운드 동시 재생 시 우선순위/채널 제한으로 클러터링을 제어함&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;16. Definition Of Done (전투씬)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;16.1 기능&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 웨이브 데이터 기준으로 적 수량/타이밍이 정확히 스폰됨&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 적이 경로를 이탈하지 않고 목표 지점까지 이동함&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 타워가 유효 타겟만 공격하고(지상/공중 제약) 우선순위가 적용됨&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 병영 블로킹 및 영웅 교전이 정상 동작함&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 승/패 판정 및 결과 UI가 기획 조건대로 동작함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;16.2 성능&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 적/투사체/이펙트 오브젝트 풀링 적용&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 목표 기기에서 전투 피크 시 목표 FPS 유지&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 불필요한 GC 스파이크(웨이브 시작/종료 구간) 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;16.3 안정성&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; Null 참조 없이 타겟 소실 시 재탐색&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 씬 전환 반복(월드맵&amp;harr;게임) 30회 이상에서 누수/크래시 없음&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 저장 실패/로드 실패 시 안전 폴백 동작&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;16.4 밸런스&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 초반 3웨이브 체감 난이도가 급격히 튀지 않음&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 특정 타워 한 종류만으로 강제되지 않음(사용률 편중 감시)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 조기 호출 사용이 명확한 보상과 리스크를 가짐&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;16.5 회귀&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;WorldMapScene -&amp;gt; GameScene -&amp;gt; WorldMapScene&lt;/code&gt; 루프 회귀 통과&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 기존 저장 데이터와 신규 스키마 호환 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;DEV_MOCK&lt;/code&gt; 비활성 빌드에서 Mock 경로 호출 0건&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;17. 코드 반영 대상 (초안)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/GameScene.cs&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameMockController&lt;/code&gt; 자동 생성 제거, &lt;code&gt;GameStateController&lt;/code&gt; 초기화 진입점으로 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/WorldMapScene.cs&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;진입 파라미터(&lt;code&gt;SelectedStageId&lt;/code&gt;, 난이도) 전달 검증 강화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/GameMockController.cs&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;DEV_MOCK&lt;/code&gt; 분기 격리 및 기본 빌드 경로 차단&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/UI/GameView.cs&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HUD/링 메뉴/결과 UI 이벤트 바인딩 확장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/WorldMap/WorldMapReturnAnimator.cs&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전투 결과 데이터 소비 지점 점검&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;신규(예정):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/GameStateController.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/WaveManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/SpawnManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/PathManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/InGameEconomyManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/TowerManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/HeroController.cs&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;18. 작업 순서(실행 단위)&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;GameScene&lt;/code&gt;에서 Mock 자동생성 제거 + FSM 뼈대 연결&lt;/li&gt;
&lt;li&gt;웨이브/스폰/경로 최소 루프 구현&lt;/li&gt;
&lt;li&gt;타워 건설/업글/판매 + 골드 루프 연결&lt;/li&gt;
&lt;li&gt;병영 블로킹 + 영웅 최소 동작 추가&lt;/li&gt;
&lt;li&gt;결과 저장 + 월드맵 복귀 루프 고정&lt;/li&gt;
&lt;li&gt;KPI 로그 삽입 후 수치 1차 튜닝&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;s&gt;19. StageInfoPopup &amp;plusmn;&amp;cedil;Ƕ &amp;sup1;ݿ&amp;micro; (2026-02-15)&lt;/s&gt; -&amp;gt; 코덱스가 자주 한글 날려먹는다 해결법 찾는중...&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;s&gt;&amp;sup3;놮&amp;ordm;όM(ŷ&amp;acute;&amp;yacute;&amp;middot;&amp;macr;&amp;frac12;&amp;not;) &amp;plusmn;⁘ &amp;Ouml;&amp;frac14;Ҡ&amp;plusmn;&amp;cedil;&amp;frac14;&amp;ordm; &amp;sup1;ݿ&amp;micro;: &amp;frac12;&amp;ordm;ŗ&amp;Agrave;́&amp;ouml;&amp;cedil;?&amp;deg;/&amp;plusmn;ǀ峭&amp;Agrave;̵&amp;micro;/&amp;Ouml;&amp;deg;?&amp;middot;Ϡ+ &amp;sup3;&amp;shy;&amp;Agrave;̵&amp;micro; &amp;frac14;&amp;plusmn;Ń + Start/&amp;acute;ݱ⠈帧.&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;Assets/Resources/UI/StageInfoPopup.prefab &amp;frac12;űԠ&amp;raquo;&amp;yacute;&amp;frac14;&amp;ordm;.&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;&amp;raquo;&amp;yacute;&amp;frac14;&amp;ordm; &amp;Agrave;ڵ&amp;iquest;ȭ &amp;micro;&amp;micro;&amp;plusmn;&amp;cedil; &amp;szlig;&amp;deg;&amp;iexcl;: Assets/Scripts/Kingdom/Editor/StageInfoPopupPrefabBuilder.cs (Tools/Kingdom/Build StageInfoPopup Prefab).&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;WorldMapScene&amp;iquest;&amp;iexcl;&amp;frac14;&amp;shy; Resources/UI/StageInfoPopup &amp;middot;&amp;epsilon;堰淎&amp;iquest;͠&amp;iquest;&amp;not;&amp;deg;ᵇ&amp;frac34;˾&amp;divide; ǥ&amp;Aacute;ؠǃ&amp;middot;&amp;omicron;츦 &amp;iquest;켱 &amp;raquo;翫.&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;StageInfoPopup &amp;frac12;&amp;eth;&amp;cent;/&amp;ucirc;&amp;deg;&amp;cent; &amp;iquest;&amp;not;&amp;micro;&amp;iquest;: &amp;iquest;&amp;ugrave;&amp;micro;帊 &amp;frac12;&amp;ordm;ǁ&amp;para; &amp;reg;(ico_stage_bg/ico_text_bg) &amp;Agrave;&amp;ucirc;&amp;iquest;묠UI Ŭ&amp;cedil;&amp;macr;/&amp;iquest;&amp;shy;&amp;cedil;&amp;sup2; SFX(WorldMap_Click) &amp;iquest;&amp;not;&amp;deg;ᬠŬ&amp;cedil;&amp;macr; &amp;Aacute;ߺ&amp;sup1; &amp;sup1;恶 &amp;deg;&amp;pound;&amp;deg;ݠ&amp;Agrave;&amp;ucirc;&amp;iquest;뮍&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;&amp;frac34;Ɔ&amp;reg; Ƅ&amp;Agrave;̇&amp;Aacute;&amp;para;  StageInfoPopup &amp;Agrave;&amp;uuml;&amp;iquest;렸&amp;reg;&amp;frac14;ҽ&amp;ordm; &amp;deg;淎(UI/Sprites/WorldMap/StageInfoPopup/*) &amp;iquest;켱 &amp;frac14;&amp;Aacute;&amp;para;, &amp;plusmn;⁸ &amp;iquest;&amp;ugrave;&amp;micro;帊 &amp;deg;&amp;oslash;&amp;iquest;렽&amp;ordm;ǁ&amp;para; &amp;reg;&amp;acute; fallback &amp;Agrave;&amp;macr;&amp;Aacute;&amp;ouml;. &amp;sup3;&amp;ordf;&amp;sup3;빙&amp;sup3;&amp;ordf;&amp;sup3;&amp;ordf; ǁ&amp;middot;҇&amp;Aacute;Ʈ &amp;sup1;&amp;reg;&amp;frac14;&amp;shy; &amp;szlig;&amp;deg;&amp;iexcl;.&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;StageInfoPopup &amp;frac34;Ɔ&amp;reg; &amp;Agrave;&amp;ucirc;&amp;iquest;렀&amp;yacute;&amp;middot; 2/3 &amp;frac14;&amp;ouml;Ǡ: &amp;Agrave;&amp;uuml;&amp;iquest;렰淎 &amp;Agrave;̹́&amp;ouml; &amp;sup1;脡 Ȅ PrefabBuilder &amp;Agrave;罇Ǡ&amp;Agrave;&amp;cedil;&amp;middot;&amp;Pi;ǁ&amp;cedil;&amp;reg;ƕ &amp;frac12;&amp;ordm;ǁ&amp;para; &amp;reg; &amp;frac14;&amp;Aacute;&amp;para; &amp;deg;&amp;raquo;&amp;frac12;Ů&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;&amp;Agrave;̹́&amp;ouml; &amp;raquo;&amp;yacute;&amp;frac14;&amp;ordm; &amp;acute;냼 &amp;deg;淎: OpenAI Image API &amp;acute;뽅 &amp;middot;΄&amp;agrave;ComfyUI &amp;ordm;긮&amp;Aacute;&amp;ouml;(Assets/Scripts/Kingdom/Editor/stageinfopopup_comfy_bridge.py)&amp;middot;&amp;Pi;StageInfoPopup &amp;frac34;Ɔ&amp;reg; 4&amp;Aacute;&amp;frac34; &amp;raquo;&amp;yacute;&amp;frac14;&amp;ordm; &amp;sup1;נǁ&amp;cedil;&amp;reg;ƕ &amp;sup1;ݿ&amp;micro;.&lt;/s&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h1&gt;게임씬 구현 &amp;mdash; Task 체크리스트&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근거 문서: &lt;a href=&quot;./%EA%B2%8C%EC%9E%84%EC%94%AC_%EA%B5%AC%ED%98%84%EB%AA%85%EC%84%B8%EC%84%9C.md&quot;&gt;게임씬_구현명세서.md&lt;/a&gt;&lt;br /&gt;작성일: 2026-02-14&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;0단계: 사전 정리 (선행 필수)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;UserSaveData&lt;/code&gt; &amp;rarr; 글로벌 &lt;code&gt;SaveManager&lt;/code&gt; 싱글톤으로 전환&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 복귀 경로 통일: &lt;code&gt;GameMockController&lt;/code&gt;, &lt;code&gt;GameView&lt;/code&gt; 모두 &lt;code&gt;WorldMapScene&lt;/code&gt;으로 변경&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;Assets/Scripts/Kingdom/Game/&lt;/code&gt; 폴더 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1단계: 게임뷰/HUD 선행 정리 (UI Contract First)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명세서 &amp;sect;7, &amp;sect;8.1 &amp;middot; 완료 조건: GameView가 전투 상태/웨이브/결과 표시 계약을 먼저 제공&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 작업&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;GameView&lt;/code&gt; UI 계약 확정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 상태 텍스트(&lt;code&gt;Prepare/WaveRunning/WaveBreak/Result/Pause&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 웨이브 텍스트(&lt;code&gt;WAVE n / total&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; Pause 토글 버튼 동작&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 결과 영역(승리/패배/Retry/Exit) 기본 슬롯 확보&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 최소 HUD 슬롯(&lt;code&gt;Lives/Gold/NextWave/Hero/Spell&lt;/code&gt;) 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;GameView&lt;/code&gt; 이벤트 진입점 표준화
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;Bind(GameStateController)&lt;/code&gt; 유지&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;ShowResult(...)&lt;/code&gt;, &lt;code&gt;SetPauseVisual(...)&lt;/code&gt; 등 명시 API 정의&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;UpdateResourceInfo(...)&lt;/code&gt;, &lt;code&gt;SetNextWaveInteractable(...)&lt;/code&gt;, &lt;code&gt;SetSpellCooldown(...)&lt;/code&gt; 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;GameView&lt;/code&gt; 프리팹 바인딩 누락 방어(Null-safe) 정리&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;검증&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;GameScene&lt;/code&gt; 진입 즉시 HUD 기본 정보 표시&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 상태 전이 시 HUD 표시 즉시 동기화&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; Pause 토글 시 시각 상태와 실제 상태一致&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2단계: 전투 프레임 구축 (FSM)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명세서 &amp;sect;8.1 &amp;middot; 완료 조건: Prepare&amp;rarr;WaveRunning&amp;rarr;WaveBreak&amp;rarr;Result 전이 끊김 없음&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 작업&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;GameStateController.cs&lt;/code&gt; 신규 &amp;mdash; enum FSM (&lt;code&gt;Prepare/WaveRunning/WaveBreak/Result/Pause&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 상태 전이 규칙 구현 (&amp;sect;4.1 다이어그램 기반)&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;Pause&lt;/code&gt; 진입/복귀 &amp;mdash; &lt;code&gt;Time.timeScale = 0&lt;/code&gt; + 오버레이 UI&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;GameScene.cs&lt;/code&gt; 수정 &amp;mdash; Mock 자동생성 제거, &lt;code&gt;GameStateController&lt;/code&gt; 초기화 진입점으로 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;검증&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 상태 전이 로그(디버그) 출력 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; FSM 유닛테스트 작성 및 통과&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3단계: 맵/경로/웨이브 골격&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명세서 &amp;sect;8.2 &amp;middot; 완료 조건: 적이 경로를 따라 이동, 웨이브 시작/종료 정확 판정&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;WaveConfig&lt;/code&gt; SO 정의 (&amp;sect;9.1)&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;EnemyConfig&lt;/code&gt; SO 정의 (&amp;sect;9.3)&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;StageConfig&lt;/code&gt;에 &lt;code&gt;WaveConfig&lt;/code&gt; 참조 필드 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 작업&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;PathManager.cs&lt;/code&gt; 신규 &amp;mdash; 웨이포인트 기반 경로 캐시&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;SpawnManager.cs&lt;/code&gt; 신규 &amp;mdash; 적 생성 + 경로 주입&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;WaveManager.cs&lt;/code&gt; 신규 &amp;mdash; 웨이브 종료 판정 (생존 적 0 + 스폰 완료)&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 적 기본 이동 로직 (경로 따라 이동 &amp;rarr; 탈출 시 생명력 차감)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;검증&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 최소 1개 스테이지 웨이브 실행&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 초기화 순서 고정 확인 (맵/경로 로드 전 스폰 시작 금지) [High Risk]&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4단계: 타워/경제/블로킹&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명세서 &amp;sect;8.3 &amp;middot; 완료 조건: 경제 루프(처치&amp;rarr;골드&amp;rarr;강화) 안정 순환&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;TowerConfig&lt;/code&gt; SO 정의 (&amp;sect;9.2) &amp;mdash; &lt;code&gt;BarracksData&lt;/code&gt; 포함&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 피해 공식 공통 함수 단일화 (물리/마법/고정/포병 관통) [High Risk]&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 작업&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;TowerManager.cs&lt;/code&gt; 신규 &amp;mdash; 건설/업그레이드/판매/분기&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;InGameEconomyManager.cs&lt;/code&gt; 신규 &amp;mdash; 골드 수급/소모&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 원형 링 메뉴 UI 구현 (&amp;sect;7.1~7.3)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 병영 블로킹 루프 &amp;mdash; 적 상태: &lt;code&gt;Moving&amp;rarr;Blocked&amp;rarr;Attacking&amp;rarr;Dead&lt;/code&gt; [High Risk]&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 랠리 포인트 (병영 전용) 드래그 지정&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 조기 호출 보상 (남은시간 &amp;times; 보상계수 + 스펠 쿨다운 단축) [High Risk]&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 비행 적 타겟팅 제약 (&lt;code&gt;CanTargetAir&lt;/code&gt;) 적용 [High Risk]&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;검증&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 4계열 기본 타워 동작 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 조기 호출 버튼 동작 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 타워 UI 터치 영역 겹침 없음 (우선순위 레이어)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 피해 공식 검증 (물리/마법/고정/포병 관통)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5단계: 영웅/결과/복귀&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명세서 &amp;sect;8.4 &amp;middot; 완료 조건: 승/패 후 월드맵 복귀 정상, 앱 재시작 후 기록 유지&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;HeroConfig&lt;/code&gt; SO 정의 (&amp;sect;9.4)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;SpellConfig&lt;/code&gt; SO 정의 (&amp;sect;9.5)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 작업&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;HeroController.cs&lt;/code&gt; 신규 &amp;mdash; 이동/공격/스킬 최소 구현&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 승/패 결과 계산 (&amp;sect;6.4 별 등급 기준)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 결과 UI 표시 (승리 &amp;rarr; 별/보상, 패배 &amp;rarr; Retry/Exit)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 저장 반영 (&lt;code&gt;SaveManager&lt;/code&gt; 경유)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;WorldMapReturnAnimator&lt;/code&gt; 연계 &amp;mdash; 전투 결과 데이터 소비 지점 점검&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 보스 면역(즉사/강제이동 면역) 플래그 전투 판정 반영 [High Risk]&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;검증&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 월드맵 &amp;rarr; 게임 진입 시 스테이지 데이터 일치&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 승/패 결과 저장 반영 정상 (&amp;sect;6.4 별 등급)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 게임 &amp;rarr; 월드맵 복귀 루프 정상&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 앱 재실행 후 기록 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6단계: Mock 격리 / 회귀 안정화&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명세서 &amp;sect;8.5 &amp;middot; 완료 조건: 기본 빌드 Mock 비활성, 주요 루프 회귀 통과&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 작업&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;GameMockController&lt;/code&gt;를 &lt;code&gt;DEV_MOCK&lt;/code&gt; 컴파일 심볼 분기로 격리&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 기본 실행 경로에서 Mock 완전 분리&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;검증 (DoD &amp;sect;16)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;WorldMapScene &amp;rarr; GameScene &amp;rarr; WorldMapScene&lt;/code&gt; 루프 회귀 30회+&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;DEV_MOCK&lt;/code&gt; 비활성 빌드에서 Mock 경로 호출 0건&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 기존 저장 데이터와 신규 스키마 호환 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 씬 전환 반복 시 메모리 누수/크래시 없음&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 저장 실패/로드 실패 시 안전 폴백 동작&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7단계: KPI / 밸런스 튜닝&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명세서 &amp;sect;10 &amp;middot; 완료 조건: 텔레메트리 로그 삽입 + 수치 1차 튜닝&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 작업&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; KPI 로그 삽입 (&amp;sect;10 전체 항목)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;WaveClearTime&lt;/code&gt;, &lt;code&gt;LifeLostPerWave&lt;/code&gt;, &lt;code&gt;GoldIncomePerWave&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerBuildRateByType&lt;/code&gt;, &lt;code&gt;EnemyLeakCountByType&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EarlyCallUsageRate&lt;/code&gt;, &lt;code&gt;HeroSkillUsageRate&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 속도 조절(x1/x2) 구현 &amp;mdash; &lt;code&gt;timeScale&lt;/code&gt; 비의존 타이머 분리 [위험 &amp;sect;11]&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;검증&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 초반 3웨이브 체감 난이도 안정&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 특정 타워 타입 사용률 편중 없음&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 조기 호출 사용이 명확한 보상과 리스크를 가짐&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 속도 조절 시 Coroutine/애니메이션/물리 정상 동작&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 적/투사체/이펙트 오브젝트 풀링 적용 (성능)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 전투 피크 시 목표 FPS 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드 반영 대상 요약 (&amp;sect;17)&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;code&gt;GameScene.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mock 제거, FSM 진입점&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;수정&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WorldMapScene.cs&lt;/code&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;code&gt;GameMockController.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DEV_MOCK&lt;/code&gt; 격리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;수정&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GameView.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HUD/링메뉴/결과 UI 확장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;수정&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WorldMapReturnAnimator.cs&lt;/code&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;code&gt;Game/GameStateController.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;FSM 총괄&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;신규&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Game/WaveManager.cs&lt;/code&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;code&gt;Game/SpawnManager.cs&lt;/code&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;code&gt;Game/PathManager.cs&lt;/code&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;code&gt;Game/InGameEconomyManager.cs&lt;/code&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;code&gt;Game/TowerManager.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;타워 CRUD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;신규&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Game/HeroController.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;영웅 최소 기능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;진행 로그&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2026-02-14:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;SaveManager&lt;/code&gt; 추가 및 &lt;code&gt;KingdomAppManager&lt;/code&gt; 초기화 경유 연결 완료&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WorldMapScene&lt;/code&gt;, &lt;code&gt;GameMockController&lt;/code&gt;의 저장 접근을 &lt;code&gt;SaveManager.Instance.SaveData&lt;/code&gt;로 전환&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameMockController&lt;/code&gt; 복귀 경로를 &lt;code&gt;WorldMapScene&lt;/code&gt;으로 통일&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameMockController&lt;/code&gt;를 &lt;code&gt;DEV_MOCK&lt;/code&gt; 컴파일 심볼 영역으로 격리&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameStateController&lt;/code&gt;(FSM) 신규 추가 및 &lt;code&gt;GameScene&lt;/code&gt; 연결&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameView&lt;/code&gt;에 FSM 바인딩/상태 표시/일시정지 토글 연결&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WaveConfig&lt;/code&gt;, &lt;code&gt;EnemyConfig&lt;/code&gt; SO 정의 추가 및 &lt;code&gt;StageData&lt;/code&gt;에 &lt;code&gt;WaveConfig&lt;/code&gt; 참조 필드 연결&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PathManager&lt;/code&gt;, &lt;code&gt;SpawnManager&lt;/code&gt;, &lt;code&gt;WaveManager&lt;/code&gt;, &lt;code&gt;EnemyRuntime&lt;/code&gt; 최소 전투 루프 스캐폴딩 추가&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dotnet build&lt;/code&gt; 점검 시 Unity 생성 &lt;code&gt;Assembly-CSharp.csproj&lt;/code&gt;에 신규 파일 미반영 상태 확인 (Unity 에디터 재생성 후 재검증 필요)&lt;/li&gt;
&lt;li&gt;작업 순서를 &lt;code&gt;UI Contract First&lt;/code&gt;로 재정렬: &lt;code&gt;GameView/HUD&lt;/code&gt;를 전투 매니저보다 선행 구현하도록 단계 재배치&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameView&lt;/code&gt; UI 계약 보강:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상태/웨이브 표시, Pause 토글, Result 슬롯(승리/패배/Retry/Exit) 추가&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ShowResult(...)&lt;/code&gt;, &lt;code&gt;HideResult()&lt;/code&gt;, &lt;code&gt;SetPauseVisual(...)&lt;/code&gt; API 추가&lt;/li&gt;
&lt;li&gt;프리팹 바인딩 누락 시 런타임 기본 Result 슬롯 생성으로 Null-safe 강화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameView&lt;/code&gt; 레거시 프리팹 레이아웃 자동 교정 추가:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중앙 중첩 버튼/텍스트를 HUD 위치(좌상단 정보, 우상단 Pause, 우하단 디버그 버튼)로 재배치&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HUDRoot&lt;/code&gt; 기준 재부모화로 GameScene 진입 시 검은 화면 중앙 겹침 현상 완화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameViewLayoutFixTool&lt;/code&gt; 추가 (&lt;code&gt;Kingdom/GameView/Fix Layout And Bindings&lt;/code&gt;):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Resources/UI/GameView.prefab&lt;/code&gt; 구조/직렬화 참조 자동 보정용&lt;/li&gt;
&lt;li&gt;현 세션에서 메뉴 실행 실패(에디터 컴파일/컨텍스트 이슈 추정), 에디터 리컴파일 후 재실행 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameViewScenePreview&lt;/code&gt; 추가 (&lt;code&gt;Tools/Preview/GameView/*&lt;/code&gt;):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Canvas (Environment)&lt;/code&gt; 상위 오브젝트 자동 생성 후 &lt;code&gt;GameView&lt;/code&gt; 프리팹 배치&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WorldMapView&lt;/code&gt; 프리뷰 방식과 동일한 편집 모드 단독 미리보기 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;NotebookLM 기반 &lt;code&gt;GameView&lt;/code&gt; 최소 슬롯 반영:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Top HUD: &lt;code&gt;txtLives&lt;/code&gt;, &lt;code&gt;txtGold&lt;/code&gt;, &lt;code&gt;txtWaveInfo&lt;/code&gt;, &lt;code&gt;btnNextWave&lt;/code&gt;, &lt;code&gt;btnPause&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Hero/Spell: &lt;code&gt;imgHeroPortrait&lt;/code&gt;, &lt;code&gt;btnSpellReinforce&lt;/code&gt;, &lt;code&gt;btnSpellRain&lt;/code&gt;, 쿨다운 오버레이 2종&lt;/li&gt;
&lt;li&gt;인터페이스: &lt;code&gt;UpdateResourceInfo&lt;/code&gt;, &lt;code&gt;SetNextWaveInteractable&lt;/code&gt;, &lt;code&gt;SetSpellCooldown&lt;/code&gt; 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameView.cs.meta&lt;/code&gt; 누락/Guid 불일치 복구:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 프리팹 참조 Guid(&lt;code&gt;35dfde0a8ebb4c04c95f39fcd76eb8bd&lt;/code&gt;)로 정합성 복원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Kingdom/GameView/Fix Layout And Bindings&lt;/code&gt; 메뉴 실행 성공:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Resources/UI/GameView.prefab&lt;/code&gt; 레이아웃/직렬화 참조 자동 보정 완료&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;중앙 겹침 원인 제거:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameView&lt;/code&gt; 루트 레거시 직속 노드(&lt;code&gt;btn*/txt*&lt;/code&gt;) 정리 로직 추가&lt;/li&gt;
&lt;li&gt;프리팹 보정 툴도 동일 정리 로직 반영 후 메뉴 재실행 완료&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;스크린샷 기준 1단계 검증 진행:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GameScene 진입 시 HUD 기본 정보 표시 확인(웨이브/상태/생명력/골드/버튼 노출)&lt;/li&gt;
&lt;li&gt;영웅 포트레이트 기본 슬롯 시각 강도 완화(흰 사각형 알파 낮춤)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2026-02-14 (fix): &lt;code&gt;WaveManager&lt;/code&gt; 참조 자동 재바인딩 + &lt;code&gt;GameScene&lt;/code&gt;의 &lt;code&gt;StageConfig.WaveConfig&lt;/code&gt; 주입 + 런타임 fallback &lt;code&gt;WaveConfig&lt;/code&gt; 추가(참조 누락 시 경고/실패 완화).&lt;/li&gt;
&lt;li&gt;2026-02-14 (verify): Unity Play에서 &lt;code&gt;WaveManager missing-reference&lt;/code&gt; 경고 재현 여부 점검(콘솔 클리어 후 재확인).&lt;/li&gt;
&lt;li&gt;2026-02-14 (fix): &lt;code&gt;CameraBackgroundColorFixer&lt;/code&gt; obsolete API 교체 (&lt;code&gt;FindObjectsOfType&lt;/code&gt; -&amp;gt; &lt;code&gt;FindObjectsByType&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;2026-02-14 (fix): &lt;code&gt;WorldMapManager&lt;/code&gt; 스테이지 노드 생성 보완 + 월드맵 진입 fallback 정리 + &lt;code&gt;WaveConfig&lt;/code&gt; 누락 시 FSM 안전화 + wave out-of-range 시 Result 전환.&lt;/li&gt;
&lt;li&gt;2026-02-14 (fix): &lt;code&gt;WorldMapManager&lt;/code&gt; 노드 생성 경로에 &lt;code&gt;UIStageNode&lt;/code&gt; fallback 생성 추가(&lt;code&gt;nodePrefab/nodeRoot&lt;/code&gt; 누락 시 경고 완화) + &lt;code&gt;StageInfoPopup&lt;/code&gt; 리소스 누락 시 기본 fallback 추가.&lt;/li&gt;
&lt;li&gt;2026-02-15 (feature): &lt;code&gt;StageInfoPopup&lt;/code&gt; 프리팹 제작 완료 (&lt;code&gt;Assets/Resources/UI/StageInfoPopup.prefab&lt;/code&gt;). 난이도 선택(&lt;code&gt;Casual/Normal/Veteran&lt;/code&gt;), &lt;code&gt;Start/Back/Close&lt;/code&gt; 버튼, 텍스트 영역 배치 완료.&lt;/li&gt;
&lt;li&gt;2026-02-15 (enhance): &lt;code&gt;StageInfoPopup&lt;/code&gt; 이미지/사운드 연동 완료. &lt;code&gt;Panel=ico_stage_bg&lt;/code&gt;, &lt;code&gt;Button=ico_text_bg&lt;/code&gt;, &lt;code&gt;SFX=WorldMap_Click(open/click)&lt;/code&gt;, 클릭 중복 재생 방지(0.08s).&lt;/li&gt;
&lt;li&gt;2026-02-15 (asset-prep): 나노바나나용 프롬프트 문서 추가 (&lt;code&gt;문서/이미지프롬프트/StageInfoPopup_이미지프롬프트_나노바나나.md&lt;/code&gt;). 코드/에셋 경로를 &lt;code&gt;StageInfoPopup&lt;/code&gt; 기준으로 정리하고 fallback 정책 명시.&lt;/li&gt;
&lt;li&gt;2026-02-15 (asset-apply): Step 2/3 적용 완료. &lt;code&gt;StageInfoPopup&lt;/code&gt; 경로에 임시 이미지 4종 배치(&lt;code&gt;StageInfoPopup_Panel/Button/Button_Start/Button_Close&lt;/code&gt;) 및 &lt;code&gt;Tools/Kingdom/Build StageInfoPopup Prefab&lt;/code&gt; 메뉴로 프리팹 재생성 확인.&lt;/li&gt;
&lt;li&gt;2026-02-15 (blocker): &lt;code&gt;StageInfoPopup&lt;/code&gt; 신규 이미지 생성 시도 중 OpenAI Image API &lt;code&gt;billing_hard_limit_reached&lt;/code&gt;로 중단. 과금 한도 확인 필요.&lt;/li&gt;
&lt;li&gt;2026-02-15 (image-gen): OpenAI 한도 이슈 우회를 위해 ComfyUI 브릿지 적용. &lt;code&gt;stageinfopopup_comfy_bridge.py&lt;/code&gt;로 이미지 4종 생성 후 &lt;code&gt;PrefabBuilder&lt;/code&gt; 반영.&lt;/li&gt;
&lt;li&gt;2026-02-15 (ui-tune): &lt;code&gt;StageInfoPopup&lt;/code&gt; 레이아웃 1차 조정(패널/텍스트/버튼 간격, 닫기 버튼 크기/위치/패딩) 및 &lt;code&gt;PrefabBuilder&lt;/code&gt; 업데이트.&lt;/li&gt;
&lt;li&gt;2026-02-15 (ui-tune-2): &lt;code&gt;StageInfoPopup&lt;/code&gt; 레이아웃 2차 조정(닫기 버튼 인셋/크기/외곽 여백, 중앙 텍스트 y축, 하단 &lt;code&gt;Back/Start&lt;/code&gt; 정렬 간격).&lt;/li&gt;
&lt;li&gt;2026-02-15 (ui-tune-3): &lt;code&gt;StageInfoPopup&lt;/code&gt; 닫기 버튼 추가 미세조정(프레임 안착 강화: 위치/크기 재조정) 및 프리팹 재생성.&lt;/li&gt;
&lt;li&gt;2026-02-15 (audio-fix): &lt;code&gt;WorldMap_Click.mp3&lt;/code&gt; 교체(ComfyUI one-shot 클릭 효과음). 이전 버전은 &lt;code&gt;WorldMap_Click_backup_prev.mp3&lt;/code&gt;로 백업.&lt;/li&gt;
&lt;li&gt;2026-02-15 (audio-route-fix): &lt;code&gt;WorldMap_Click&lt;/code&gt; mp3/wav 중복 참조 경로 정리. UI 클릭 전용 &lt;code&gt;WorldMap_Click_UI.mp3&lt;/code&gt; 추가 후 &lt;code&gt;WorldMapView/StageInfoPopup/Editor&lt;/code&gt; 할당 경로를 동일 파일로 통일.&lt;/li&gt;
&lt;li&gt;2026-02-15 (noise-fix): &lt;code&gt;StageConfig&lt;/code&gt;에 &lt;code&gt;WaveConfig&lt;/code&gt;가 없을 때 발생하던 fallback 로그를 1회 정보 로그로 완화(&lt;code&gt;WaveManager&lt;/code&gt;). 이후 &lt;code&gt;StageConfig-WaveConfig&lt;/code&gt; 마이그레이션 진행.&lt;/li&gt;
&lt;li&gt;2026-02-15 (game-visibility-fix): &lt;code&gt;GameScene&lt;/code&gt; 월드가 검게만 보이던 문제 수정. 카메라를 2D 전투 기준(&lt;code&gt;orthographic&lt;/code&gt;, &lt;code&gt;z=-10&lt;/code&gt;)으로 강제 정렬하고, 런타임 배경(&lt;code&gt;GameWorldRuntime/RuntimeBackground&lt;/code&gt;) fallback 생성 추가. &lt;code&gt;SpawnManager&lt;/code&gt;에서 적 생성 시 기본 &lt;code&gt;SpriteRenderer&lt;/code&gt;를 부여해 맵/적 가시성을 확보.&lt;/li&gt;
&lt;li&gt;2026-02-15 (enemy-visual-fix): 흰 박스 적 fallback 개선. &lt;code&gt;EnemyConfig&lt;/code&gt;에 &lt;code&gt;Sprite/Tint/VisualScale&lt;/code&gt; 필드 추가, &lt;code&gt;SpawnManager&lt;/code&gt;에서 &lt;code&gt;EnemyConfig.Sprite&lt;/code&gt; 우선 사용 + &lt;code&gt;Resources/UI/Sprites/Enemies/{EnemyId}&lt;/code&gt; 규칙 로드 + 미지정 시 &lt;code&gt;EnemyId&lt;/code&gt; 해시 기반 색상 fallback 적용.&lt;/li&gt;
&lt;li&gt;2026-02-15 (pause-resume-fix): Pause 후 Resume 불가 이슈 수정. &lt;code&gt;GameView&lt;/code&gt;의 Pause 버튼을 쿨다운 바인딩에서 분리(즉시 토글), Pause 상태에서 버튼 라벨 &lt;code&gt;Resume&lt;/code&gt; 표시. 공통 &lt;code&gt;UIButtonExtensions&lt;/code&gt; 쿨다운을 unscaled time으로 변경해 &lt;code&gt;timeScale=0&lt;/code&gt;에서도 interactable 복구되도록 보강.&lt;/li&gt;
&lt;li&gt;2026-02-15 (battlefield-prefab-flow): &lt;code&gt;GameScene&lt;/code&gt;이 &lt;code&gt;Resources/Prefabs/Game/GameBattlefield&lt;/code&gt; 프리팹을 우선 로드하고, 없으면 &lt;code&gt;GameBattlefield&lt;/code&gt; 런타임 fallback을 생성하도록 전환. &lt;code&gt;PathManager.SetDefaultPathPoints(...)&lt;/code&gt;, &lt;code&gt;SpawnManager.SetEnemyRoot(...)&lt;/code&gt;를 추가해 전투 월드의 경로/적 루트 연결을 코드에서 고정. 에디터 메뉴 &lt;code&gt;Kingdom/Game/Build GameBattlefield Prefab&lt;/code&gt; 추가.&lt;/li&gt;
&lt;li&gt;2026-02-15 (battlefield-prefab-guard): &lt;code&gt;GameBattlefield&lt;/code&gt; 프리팹의 Missing Script 참조 감지 가드 추가. 프리팹에 &lt;code&gt;null component&lt;/code&gt; 또는 &lt;code&gt;GameBattlefield&lt;/code&gt; 컴포넌트 누락 시 인스턴스화를 스킵하고 런타임 fallback 월드로 안전 전환.&lt;/li&gt;
&lt;li&gt;2026-02-15 (battlefield-script-stability): &lt;code&gt;GameBattlefield&lt;/code&gt;를 &lt;code&gt;PathManager.cs&lt;/code&gt; 내부 선언에서 &lt;code&gt;GameBattlefield.cs&lt;/code&gt; 단독 파일로 분리해 Unity 스크립트 매핑 안정화. 프리팹 빌더에서 기존 &lt;code&gt;GameBattlefield.prefab&lt;/code&gt; 삭제 후 재생성하도록 변경.&lt;/li&gt;
&lt;li&gt;2026-02-15 (verify): 몬스터가 지정 경로(웨이포인트)를 따라 이동하는 동작 확인 완료.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI/잡다한개발노트</category>
      <category>Antigravity</category>
      <category>codex</category>
      <category>gpt 5.3</category>
      <category>opus 4.6</category>
      <category>plan.md</category>
      <category>task.md</category>
      <category>작업노하우 공유</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/71</guid>
      <comments>https://blacknabis.tistory.com/71#entry71comment</comments>
      <pubDate>Sun, 15 Feb 2026 04:00:11 +0900</pubDate>
    </item>
    <item>
      <title>Antigravity, Codex 2026-02-15 (월드맵씬, 모듈화, 시스템작업, 게임씬)</title>
      <link>https://blacknabis.tistory.com/70</link>
      <description>&lt;h1&gt;DevLog_2026_02_15_1&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;오늘의 목표&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임씬 개발을 막고 있던 핵심 병목인 &lt;b&gt;게임뷰 선행 안정화&lt;/b&gt;, &lt;b&gt;전투 월드 가시화&lt;/b&gt;, &lt;b&gt;웨이브/일시정지 루프 검증&lt;/b&gt;을 우선 해결했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;진행한 작업&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 게임뷰(HUD) 우선 정리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameView&lt;/code&gt;를 기준 UI 계약으로 확정했다.&lt;/li&gt;
&lt;li&gt;상태 표시(&lt;code&gt;Prepare/WaveRunning/WaveBreak/Result/Pause&lt;/code&gt;), 웨이브 표시(&lt;code&gt;WAVE n / total&lt;/code&gt;), 리소스 표시(&lt;code&gt;Lives/Gold&lt;/code&gt;)를 정리했다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Pause&lt;/code&gt;, &lt;code&gt;Next Wave&lt;/code&gt;, &lt;code&gt;Reinforce&lt;/code&gt;, &lt;code&gt;Rain&lt;/code&gt;, 결과 슬롯(승/패)까지 최소 동작 경로를 만들었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 전투 FSM 연결&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameStateController&lt;/code&gt; 중심으로 전투 흐름을 구성했다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Prepare -&amp;gt; WaveRunning -&amp;gt; WaveBreak -&amp;gt; Result&lt;/code&gt; 기본 흐름이 동작하도록 연결했다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Pause/Resume&lt;/code&gt; 토글을 보강해 실제 정지 상태와 UI 상태가 일치하도록 수정했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) 게임씬이 검게만 보이던 문제 해결&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초기 &lt;code&gt;GameScene&lt;/code&gt;이 카메라 중심/깊이 문제와 월드 오브젝트 부재로 검게 보이던 상태였다.&lt;/li&gt;
&lt;li&gt;카메라를 2D 전투 기준으로 정리하고, 전투 월드 fallback 경로를 추가했다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SpawnManager&lt;/code&gt;에서 적 가시성을 보장하도록 기본 렌더링 경로를 마련했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4) 경로 기반 이동 검증&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;PathManager&lt;/code&gt;, &lt;code&gt;SpawnManager&lt;/code&gt;, &lt;code&gt;WaveManager&lt;/code&gt;, &lt;code&gt;EnemyRuntime&lt;/code&gt; 연결을 정리했다.&lt;/li&gt;
&lt;li&gt;몬스터가 지정한 웨이포인트 경로를 따라 이동하는 동작을 실제 플레이에서 확인했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5) StageInfoPopup 프리팹/에셋 파이프라인 정리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;StageInfoPopup&lt;/code&gt; 프리팹을 제작하고, 이미지/사운드 연결 경로를 정리했다.&lt;/li&gt;
&lt;li&gt;클릭음 라우팅 충돌을 정리하고 UI 클릭음 단일 경로로 통일했다.&lt;/li&gt;
&lt;li&gt;나노바나나/ComfyUI용 프롬프트 문서와 빌더 흐름을 문서화했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6) GameBattlefield 프리팹 흐름 추가&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameScene&lt;/code&gt;이 &lt;code&gt;GameBattlefield&lt;/code&gt; 프리팹을 우선 사용하고, 실패 시 런타임 fallback으로 전환하도록 구성했다.&lt;/li&gt;
&lt;li&gt;프리팹 스크립트 누락(Missing Script) 상황을 감지해 안전하게 우회하도록 가드를 추가했다.&lt;/li&gt;
&lt;li&gt;빌더 메뉴를 통해 프리팹을 재생성하는 흐름을 마련했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이슈와 대응&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Pause 후 Resume 불가&lt;/b&gt;: 버튼 쿨다운/타임스케일 영향 경로를 수정해 즉시 재개 가능하도록 보완.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GameBattlefield Missing Script&lt;/b&gt;: 프리팹 검증 가드 + 런타임 fallback 추가로 게임 진행이 끊기지 않게 수정.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인코딩 깨짐(task.md)&lt;/b&gt;: UTF-8로 정리해 진행 기록을 다시 안정화.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;오늘의 결과&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;게임뷰 표시/상태 동기화 정상.&lt;/li&gt;
&lt;li&gt;Pause/Resume 토글 정상.&lt;/li&gt;
&lt;li&gt;웨이브 숫자 변경 정상.&lt;/li&gt;
&lt;li&gt;몬스터 경로 이동 정상.&lt;/li&gt;
&lt;li&gt;전투 월드 초기화 실패 시 fallback 안전성 확보.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다음 작업&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;EnemyConfig&lt;/code&gt;별 실제 몬스터 스프라이트 에셋 연결.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;StageConfig -&amp;gt; WaveConfig&lt;/code&gt; 참조 누락 케이스 정리(완전 데이터 기반 전환).&lt;/li&gt;
&lt;li&gt;전투 월드 프리팹 구성 고도화(맵/웨이포인트/스폰 루트 정식화).&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;한 줄 회고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UI를 먼저 안정화한 선택이 맞았고, 그 덕분에 전투 로직과 월드 렌더링 문제를 빠르게 분리해 해결할 수 있었다.&lt;/p&gt;</description>
      <category>AI/잡다한개발노트</category>
      <category>Antigravity</category>
      <category>codex</category>
      <category>Devlog</category>
      <category>Unity</category>
      <category>안티그래비티</category>
      <category>오케스트레이션</category>
      <category>유니티</category>
      <category>코덱스</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/70</guid>
      <comments>https://blacknabis.tistory.com/70#entry70comment</comments>
      <pubDate>Sun, 15 Feb 2026 03:55:35 +0900</pubDate>
    </item>
    <item>
      <title>Antigravity 2026-02-14 (월드맵씬, 게임씬,기타 모듈화, 시스템작업)</title>
      <link>https://blacknabis.tistory.com/69</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;작업하며 노트 끄적끄적내용. 그 그뒤부터 내용은 본격적으로 문서화 하였습니다. 나눠서 하자니 분류도 애매하고 .. 개발은 Antigravity를 메인으로 사용하고 IDE에 Codex, kilo를 추가로 설치하여 작업하고있습니다.&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;999&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHCfLW/dJMcaaqHJEI/apRUUfSg0fVzBc8hI4kPIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHCfLW/dJMcaaqHJEI/apRUUfSg0fVzBc8hI4kPIK/img.png&quot; data-alt=&quot;IDE 작업 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHCfLW/dJMcaaqHJEI/apRUUfSg0fVzBc8hI4kPIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHCfLW%2FdJMcaaqHJEI%2FapRUUfSg0fVzBc8hI4kPIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1906&quot; height=&quot;999&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;999&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IDE 작업 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 아이콘 제작 프롬프트&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;### A. Back (`Icon_Back`)#### 이미지 사이즈- `512x512` (기본)- 고해상도 필요 시 `1024x1024`#### Positive Prompt```textUI button icon for &quot;Back&quot;, red curved arrow pointing left, wooden button frame with metal rim,game interface asset, game UI icon, isolated on white background, chunky vector style, wood and stone texture,Kingdom Rush art style, 2D vector art, flat cartoon style, thick bold outlines, vibrant saturated colors,hand-drawn game asset, Flash game aesthetic, cel-shaded, high contrast, 1:1 composition, 512x512```#### Negative Prompt```texttext, realistic, 3D render, complex details, thin lines, watermark&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 피드백 후 정리 보완작업&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;379&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/do0TS0/dJMcahDkXrk/gZjbjSDZEL6aNBiMM1bMf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/do0TS0/dJMcahDkXrk/gZjbjSDZEL6aNBiMM1bMf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/do0TS0/dJMcahDkXrk/gZjbjSDZEL6aNBiMM1bMf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdo0TS0%2FdJMcahDkXrk%2FgZjbjSDZEL6aNBiMM1bMf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;379&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;379&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피드백 내용과 대응 요청(볼드는 수정요청내역)&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;1.중앙 세로 이음선 제거&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;배경 중앙에 seam(경계선)처럼 보이는 라인이 있어 품질이 떨어져 보입니다. 배경 스프라이트/타일 정렬부터 수정하는 게 1순위입니다.&lt;b&gt;(Scene에서 생긴 그리드이기 떄문에 작업안해도됨)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;2.스테이지 선택 노드 가독성 강화&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;현재 STAGE SELECT가 배경과 비슷한 톤이라 눈에 덜 띕니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;선택 링(펄스), 외곽 글로우, 노드 그림자 추가 추천.&lt;br /&gt;&lt;b&gt;(ComfyUI가 아닌 직접 GPT를 이용해서 생성 할 수 있을 떄만 대응)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;3.상단 타이틀 대비 개선&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;WORLD MAP 텍스트가 배경 위에서 약합니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;반투명 배너 또는 텍스트 외곽선/드롭섀도우를 넣어 가독성 확보하세요.&lt;br /&gt;&lt;b&gt;(메이플 폰트 링크 후 연동)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;4.하단 버튼 의미 분리&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;두 버튼의 기능이 직관적으로 안 보입니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;아이콘 크기 키우고, 짧은 라벨(예: HERO, UPGRADE) 또는 툴팁을 붙이세요.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;5.버튼 배치 균형&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;하단 버튼이 맵 오브젝트 위에 떠 있는 느낌입니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;공통 하단 바(패널) 위에 정렬하면 UI 체계가 안정됩니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;6.상태 정보 추가&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;스테이지에 클리어/잠금/신규 상태를 표시하세요(별, 자물쇠, 느낌표).&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;월드맵의 진행감이 크게 좋아집니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;7. 클릭 피드백 강화&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;버튼/노드에 hover/press 스케일(예: 1.0&amp;rarr;0.93&amp;rarr;1.0), 클릭 SFX를 강하게 주면 조작감이 좋아집니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;8.안전영역 대응&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;457&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m4rMr/dJMcagdoKMx/lIyfSkeF8UQ7FgfpylaGi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m4rMr/dJMcagdoKMx/lIyfSkeF8UQ7FgfpylaGi0/img.png&quot; data-alt=&quot;버튼 위젯으로 추가하고. 컨텐츠사이즈필터랑 레이아웃 그룹 추가.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m4rMr/dJMcagdoKMx/lIyfSkeF8UQ7FgfpylaGi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm4rMr%2FdJMcagdoKMx%2FlIyfSkeF8UQ7FgfpylaGi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;811&quot; height=&quot;457&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;457&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;버튼 위젯으로 추가하고. 컨텐츠사이즈필터랑 레이아웃 그룹 추가.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;하단 버튼 규격화 가능하도록 모듈화(이미지와 텍스트 수정하도록)&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;175&quot; data-origin-height=&quot;144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chbzVG/dJMcafZPaIQ/ebRkEBkKkLW6EKzqXgh0nk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chbzVG/dJMcafZPaIQ/ebRkEBkKkLW6EKzqXgh0nk/img.png&quot; data-alt=&quot;하단 버튼 공통 모듈화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chbzVG/dJMcafZPaIQ/ebRkEBkKkLW6EKzqXgh0nk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchbzVG%2FdJMcafZPaIQ%2FebRkEBkKkLW6EKzqXgh0nk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;175&quot; height=&quot;144&quot; data-origin-width=&quot;175&quot; data-origin-height=&quot;144&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;하단 버튼 공통 모듈화&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;**&lt;br /&gt;SafeArea 작업**&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2026-02-14 개발일지 (feat.&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;월드맵에서 전투로 &amp;mdash; UI 시스템 완성과 게임씬 설계&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;월드맵 UI를 프로덕션 수준으로 끌어올리고, 게임씬 전투 루프의 설계 문서를 확정한 하루.&lt;br /&gt;&quot;버튼 하나하나에 시스템이 있고, 전환 하나하나에 설계가 있다.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  오늘의 목표&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;월드맵 UI 완성도 향상&lt;/b&gt; &amp;mdash; 스테이지 노드, 액션 버튼, 사이드 보더를 프리팹 기반으로 안정화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;씬 전환 Fade 효과&lt;/b&gt; &amp;mdash; 모든 씬 전환에 FadeOut/FadeIn 적용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;게임씬 구현 플랜&lt;/b&gt; &amp;mdash; NotebookLM 킹덤러쉬 분석 데이터를 기반으로 전투 루프 설계 문서 작성&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  커밋 요약&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;16:09&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a9a937e&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;월드맵 UI 워크플로우, 에셋, 문서 일괄 적용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17:19&lt;/td&gt;
&lt;td&gt;&lt;code&gt;501258b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UIActionButtonItem&lt;/code&gt; 프리팹 추가 + 하단바 레이아웃 개선&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20:34&lt;/td&gt;
&lt;td&gt;&lt;code&gt;faf18d5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UIStageNode&lt;/code&gt; 고도화 + 사이드보더 실행 플랜 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21:11&lt;/td&gt;
&lt;td&gt;&lt;code&gt;347805d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;사이드보더 구조 툴 + SafeArea 바인딩&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21:28&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0a4937d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;사이드보더 아트 에셋 생성 + 프리팹 바인딩&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;22:14&lt;/td&gt;
&lt;td&gt;&lt;code&gt;51d8495&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UIStageNode&lt;/code&gt; 시각 상태 규칙 정리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 1. UIStageNode 시스템 고도화&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이지 버튼이 단순 &lt;code&gt;Button&lt;/code&gt; + &lt;code&gt;Text&lt;/code&gt;로만 되어 있어, 잠금 상태/클리어 등급/선택 하이라이트를 표현할 수 없었음.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결: 데이터 바인딩 기반 ViewModel&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;StageNodeViewModel
  ├── StageId, StageName
  ├── IsUnlocked, IsBoss
  ├── ClearStars (0~3)
  └── IsSelected

      &amp;darr; 바인딩

UIStageNode (MonoBehaviour)
  ├── 잠금 오버레이 (IsUnlocked = false &amp;rarr; 어둡게 + 자물쇠 아이콘)
  ├── 별 표시 (ClearStars &amp;rarr; 별 아이콘 1~3개)
  ├── 보스 마커 (IsBoss &amp;rarr; 해골 아이콘 표시)
  └── 선택 하이라이트 (IsSelected &amp;rarr; 테두리 발광)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시각 상태 규칙&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;어둡게&lt;/td&gt;
&lt;td&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;/td&gt;
&lt;td&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;/td&gt;
&lt;td&gt;별 1~3개&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;/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 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심:&lt;/b&gt; &lt;code&gt;IStageUnlockPolicy&lt;/code&gt; 인터페이스로 해금 조건을 분리하여, 선형/분기/조건부 해금을 유연하게 교체할 수 있도록 설계.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 2. 사이드 보더 시스템&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;월드맵 배경 이미지가 16:9 기준으로 제작되었지만, 4:3이나 19.5:9 같은 다른 비율에서 빈 공간이 노출됨.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결 구조&lt;/h3&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;WorldMapView
├── Background (Stretch)
├── LeftBorder  &amp;larr; SafeArea 바깥 영역을 채움
├── RightBorder &amp;larr; SafeArea 바깥 영역을 채움
└── Content (SafeArea 내부)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Editor 스크립트&lt;/b&gt;로 사이드보더 구조를 자동 생성 (&lt;code&gt;WorldMapSideBorderStructureTool&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AI 생성 에셋&lt;/b&gt;을 좌/우 보더에 바인딩&lt;/li&gt;
&lt;li&gt;SafeArea 기준으로 위치/크기 자동 조절&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  3. 씬 전환 Fade 효과&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;변경 내용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;AppManagerBase.ChangeScene()&lt;/code&gt;에 &lt;code&gt;useFade&lt;/code&gt; 파라미터 추가:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public void ChangeScene(TSceneEnum sceneID, bool useFade = true)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;기본값 &lt;code&gt;true&lt;/code&gt;&lt;/b&gt;: 모든 씬 전환에 FadeOut &amp;rarr; 로드 &amp;rarr; FadeIn 적용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;useFade: false&lt;/code&gt;&lt;/b&gt;: 초기 진입(&lt;code&gt;InitScene &amp;rarr; TitleScene&lt;/code&gt;)처럼 자연스러운 전환에 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;중복 호출 방지&lt;/b&gt;: &lt;code&gt;_isChangingScene&lt;/code&gt; 플래그로 연타 방어&lt;/li&gt;
&lt;li&gt;&lt;b&gt;FadeIn 조건부 실행&lt;/b&gt;: &lt;code&gt;_needsFadeIn&lt;/code&gt; 플래그로 FadeOut 없이 FadeIn만 되는 버그 차단&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  4. 게임씬 전투 루프 설계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘의 가장 큰 작업은 &lt;b&gt;게임씬 구현 플랜&lt;/b&gt;을 NotebookLM 킹덤러쉬 분석 데이터를 기반으로 확정한 것.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;NotebookLM에서 추출한 핵심 설계 포인트&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;b&gt;건설 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;자유 배치가 아닌 고정 슬롯&lt;/td&gt;
&lt;td&gt;&lt;code&gt;StrategicPoint&lt;/code&gt; 배치 시스템&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;병영 블로킹&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;유닛이 적을 물리적으로 저지&lt;/td&gt;
&lt;td&gt;적 상태 FSM (&lt;code&gt;Moving/Blocked&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;조기 호출&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;리스크 감수 &amp;rarr; 골드/쿨타임 보상&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WaveManager.EarlyCall()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;피해 공식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;물리(% 감소), 마법(물리방어 무시), 고정(모든 방어 무시)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DamageType&lt;/code&gt; enum 3종&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;포병 관통&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;적 방어력 절반 무시&lt;/td&gt;
&lt;td&gt;별도 공식 분기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;경제 밸런스&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;항상 부족하게, 선택과 집중 강제&lt;/td&gt;
&lt;td&gt;KPI 기반 튜닝&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;별 등급&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;남은 생명력 기준 (스테이지별 개별 설정)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;StarThresholds[]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5단계 구현 로드맵&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1단계: 전투 프레임 (FSM)
  └── Prepare &amp;rarr; WaveRunning &amp;rarr; WaveBreak &amp;rarr; Result

2단계: 맵/경로/웨이브
  └── PathManager + WaveConfig/EnemyConfig SO

3단계: 타워/경제/블로킹
  └── TowerManager + InGameEconomyManager + 병영 루프

4단계: 영웅/결과/복귀
  └── HeroController + 저장 + WorldMapReturnAnimator

5단계: Mock 격리/회귀
  └── DEV_MOCK 분기 + 회귀 테스트&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  핵심 교훈&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. NotebookLM을 설계 도구로 쓰기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;킹덤러쉬 분석 자료를 NotebookLM에 축적해두니, &lt;b&gt;&quot;포병의 방어력 관통 공식이 뭐야?&quot;&lt;/b&gt; 같은 구체적인 질의에 정확한 답변을 받을 수 있었다. 게임 디자인 레퍼런스를 체계적으로 관리하면, 구현 단계에서 &quot;감으로 만드는&quot; 실수를 줄일 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. KPI를 코드보다 먼저 설계하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 밸런싱은 게임이 어느 정도 완성된 후에 하지만, 이번에는 &lt;b&gt;계측 지표(KPI)를 구현 플랜 단계에서 먼저 정의&lt;/b&gt;했다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;WaveClearTime&lt;/code&gt;, &lt;code&gt;GoldIncomePerWave&lt;/code&gt;, &lt;code&gt;EnemyLeakCountByType&lt;/code&gt;&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 지표들이 먼저 정의되면, 코드를 짤 때 자연스럽게 로깅 포인트를 넣게 되고, &quot;이 값이 어디서 튀는지&quot; 추적이 가능해진다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Disposable Tool 패턴의 위력&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이드보더 구조 생성, UIStageNode 검증, 에셋 바인딩 &amp;mdash; 이 모든 작업을 &lt;b&gt;일회성 에디터 스크립트&lt;/b&gt;로 처리하고 즉시 삭제했다. MCP로 개별 오브젝트를 하나씩 조작하는 것보다 &lt;b&gt;Editor Script API를 활용한 일괄 처리&lt;/b&gt;가 훨씬 안정적이고 재현 가능하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Disposable Tools (생성 &amp;rarr; 사용 &amp;rarr; 삭제)&lt;/h2&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Assets/Scripts/Kingdom/Editor/
├── WorldMapSideBorderStructureTool.cs    &amp;larr; 사이드보더 구조 자동 생성
├── WorldMapStageNodeAutoLayoutTool.cs    &amp;larr; 스테이지 노드 배치 도구
└── WorldMapStageNodeValidationTool.cs    &amp;larr; 스테이지 노드 프리팹 검증&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  오늘의 작업 흐름&lt;/h2&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;1. 월드맵 UI 개선 (오전~오후)
   ├── UIActionButtonItem 프리팹 + 레이아웃
   ├── UIStageNode ViewModel 바인딩 + 시각 규칙
   └── 사이드보더 구조/에셋/바인딩

2. 씬 전환 Fade (오후)
   ├── AppManagerBase.ChangeScene() 확장
   ├── _needsFadeIn 플래그 추가
   └── InitScene &amp;rarr; TitleScene fade 제외

3. 게임씬 설계 (저녁)
   ├── NotebookLM 질의 4건 (HUD/타워/경제/피해공식/UI인터랙션)
   ├── 기존 플랜 &amp;rarr; 보강본 작성 (1차)
   ├── 사용자 편집 (구조 재정리, KPI/위험요소 추가)
   └── 2차 보강 (피해 공식, UI 흐름, 스키마 추가)&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  수치 요약&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;~12개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;프리팹 생성/수정&lt;/td&gt;
&lt;td&gt;4개 (&lt;code&gt;UIStageNode&lt;/code&gt;, &lt;code&gt;UIActionButtonItem&lt;/code&gt;, &lt;code&gt;WorldMapView&lt;/code&gt;, &lt;code&gt;SideBorder&lt;/code&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;NotebookLM 질의&lt;/td&gt;
&lt;td&gt;4건&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;tr&gt;
&lt;td&gt;---&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 플랜 보완하기!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제미나이 프로 모델로 플랜을 만들후 제미나이 -&amp;gt; 클루드 -&amp;gt; 코덱스 -&amp;gt; 제미나이 계속 이렇게 플랜을 보강하면 (토큰 광탈) 플랜이 매우 단단해진다. 이후 Task세팅하고 코덱스나 클루드로 코드작업하면 한번에 엄청 길게 작업하면서도 매우 안정적으로 진행이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 스테이지 버튼도 모듈화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 작업이 너무 오래걸린다... 이제부터 코덱스 스파크 모델로 코드구현을 해보자! 생각보단? 빠르지 않은거 같은데 변경 내역을 자세히 못봐서 그런거 같기도하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 다음은 코덱스 &amp;lt;-&amp;gt; 오퍼스4.6 으로 게임씬 구현 플랜 내용이다. 해당 내용을 가지고 이후에 클루드 코드의 코딩을 진행 그러면 정말 한참을 짠다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/csQTjj/dJMcadgEq96/Yqg9sKOeq7EN0OpdALPfZK/%EA%B2%8C%EC%9E%84%EC%94%AC_%EA%B5%AC%ED%98%84%EB%AA%85%EC%84%B8%EC%84%9C.md?attach=1&amp;amp;knm=tfile.md&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;게임씬_구현명세서.md&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.02MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/pDdlK/dJMcacIPS8k/k7X9sIoTJfijNuK9nvseo1/task.md?attach=1&amp;amp;knm=tfile.md&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;task.md&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.01MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;게임씬 구현 플랜 (보강본)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 목적&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameScene&lt;/code&gt;을 Mock 중심 임시 상태에서 실제 전투 루프로 전환한다.&lt;/li&gt;
&lt;li&gt;씬 흐름을 &lt;code&gt;Init -&amp;gt; Title -&amp;gt; WorldMap -&amp;gt; Game -&amp;gt; WorldMap&lt;/code&gt;로 고정한다.&lt;/li&gt;
&lt;li&gt;전투 결과(승/패, 별, 기록)가 저장과 월드맵 반영까지 일관되게 연결되도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 범위&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;포함:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전투 상태머신&lt;/li&gt;
&lt;li&gt;웨이브/스폰/경로&lt;/li&gt;
&lt;li&gt;타워/병영 블로킹/영웅 기본 루프&lt;/li&gt;
&lt;li&gt;전투 경제(골드)&lt;/li&gt;
&lt;li&gt;결과 처리/저장/월드맵 복귀&lt;/li&gt;
&lt;li&gt;HUD/일시정지/결과 UI 최소 기능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;제외(후속):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;라이브옵스 운영툴(원격 밸런스 패치, A/B)&lt;/li&gt;
&lt;li&gt;고급 연출(시네마틱, 특수 카메라 워크)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 현재 코드 기준 진단&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;3개 (&lt;code&gt;Title&lt;/code&gt;, &lt;code&gt;WorldMap&lt;/code&gt;, &lt;code&gt;Game&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;KingdomDef.SCENES&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;게임 진입&lt;/td&gt;
&lt;td&gt;월드맵 스테이지 선택 후 전환&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WorldMapScene.SelectedStageId&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;전투 로직&lt;/td&gt;
&lt;td&gt;Mock 시뮬레이션만 존재&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GameMockController&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HUD&lt;/td&gt;
&lt;td&gt;디버그 버튼(Victory/Defeat/Pause) + 웨이브 텍스트&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GameView&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;스테이지 데이터&lt;/td&gt;
&lt;td&gt;&lt;code&gt;StageConfig&lt;/code&gt; SO &amp;mdash; 웨이브/적 미포함&lt;/td&gt;
&lt;td&gt;&lt;code&gt;StageData&lt;/code&gt; 구조체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;저장&lt;/td&gt;
&lt;td&gt;매번 &lt;code&gt;new UserSaveData()&lt;/code&gt;로 생성 &amp;mdash; 글로벌 싱글톤 아님&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WorldMapScene:25&lt;/code&gt;, &lt;code&gt;GameMockController:95&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;복귀 경로&lt;/td&gt;
&lt;td&gt;&lt;b&gt;충돌&lt;/b&gt;: Mock은 &lt;code&gt;TitleScene&lt;/code&gt;, GameView는 &lt;code&gt;WorldMapScene&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GameMockController:103&lt;/code&gt; vs &lt;code&gt;GameView:58,65&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 목표 아키텍처&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 상태 흐름 다이어그램&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;┌─────────────────────────────────────────────────────┐
│  GameScene FSM                                      │
│                                                     │
│  ┌─────────┐   ┌────────────┐   ┌───────────┐      │
│  │ Prepare  │──&amp;gt;│ WaveRunning │──&amp;gt;│ WaveBreak │──┐  │
│  └─────────┘   └─────┬──────┘   └─────┬─────┘  │  │
│                      │                 │         │  │
│                      │    ┌────────────┘         │  │
│                      │    │  (다음 웨이브 있으면)   │  │
│                      │    v                      │  │
│                      │  ┌────────────┐           │  │
│                      │  │ WaveRunning│───────────┘  │
│                      │  └─────┬──────┘              │
│                      │        │ (마지막 웨이브 종료)  │
│                      v        v                     │
│                   ┌──────┐                          │
│                   │Result│                          │
│                   └──┬───┘                          │
│         ┌────────────┼────────────┐                 │
│         v            v            v                 │
│     [승리&amp;rarr;월드맵] [패배&amp;rarr;Retry]  [패배&amp;rarr;Exit]         │
│                                                     │
│  ※ Pause: 모든 전투 상태에서 오버레이 진입/복귀 가능  │
└─────────────────────────────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2 매니저 배치&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;글로벌(&lt;code&gt;DontDestroyOnLoad&lt;/code&gt;)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;SaveManager&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AudioManager&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MetaEconomyManager&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;로컬(&lt;code&gt;GameScene&lt;/code&gt; 전용)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameStateController&lt;/code&gt; &amp;mdash; FSM 상태 전이 총괄&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WaveManager&lt;/code&gt; &amp;mdash; 웨이브 진행/조기 호출/종료 판정&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SpawnManager&lt;/code&gt; &amp;mdash; 적 유닛 생성 및 경로 주입&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PathManager&lt;/code&gt; &amp;mdash; 적 이동 경로 캐시(웨이포인트)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerManager&lt;/code&gt; &amp;mdash; 건설/업그레이드/판매/분기&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroController&lt;/code&gt; &amp;mdash; 이동/공격/스킬&lt;/li&gt;
&lt;li&gt;&lt;code&gt;InGameEconomyManager&lt;/code&gt; &amp;mdash; 골드 수급/지출&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameView&lt;/code&gt; &amp;mdash; HUD 표시 (기존 &lt;code&gt;BaseView&lt;/code&gt; 확장, &lt;code&gt;InGameUIBinder&lt;/code&gt; 역할 겸임)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 핵심 설계 원칙 (NotebookLM 반영)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;KR 스타일 핵심인 &lt;code&gt;고정 건설 포인트&lt;/code&gt; + &lt;code&gt;병영 블로킹&lt;/code&gt;을 초기에 포함한다.&lt;/li&gt;
&lt;li&gt;단순 화력 경쟁 대신 &lt;code&gt;조기 웨이브 호출(Early Call)&lt;/code&gt;의 리스크/리워드를 구현한다.&lt;/li&gt;
&lt;li&gt;밸런싱은 감각이 아니라 계측 기반으로 진행한다(KPI 선구축).&lt;/li&gt;
&lt;li&gt;초기화 순서를 강제한다:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;글로벌 매니저 초기화 완료&lt;/li&gt;
&lt;li&gt;스테이지 데이터 로드&lt;/li&gt;
&lt;li&gt;경로 캐시&lt;/li&gt;
&lt;li&gt;웨이브/스폰 시작&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 전투 수학 (피해 공식)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.1 피해 유형&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;b&gt;물리(Physical)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;ArmorPhysical(%)&lt;/td&gt;
&lt;td&gt;아처/병영/포병 기본 출력&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;마법(Magic)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;ArmorMagic(%)&lt;/td&gt;
&lt;td&gt;마법사 기본 출력, 물리 방어 무시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;고정(True)&lt;/b&gt;&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;h3 data-ke-size=&quot;size23&quot;&gt;6.2 물리 피해 공식&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;실제피해 = 기본피해 &amp;times; (1 - ArmorPhysical / 100)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;포병 특수: 적 방어력을 &lt;b&gt;절반 무시&lt;/b&gt; &amp;rarr; &lt;code&gt;실제피해 = 기본피해 &amp;times; (1 - ArmorPhysical / 200)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.3 즉사(Insta-kill)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;확률 기반&lt;/b&gt;: 공격 히트 시 확률 판정 (예: 스나이퍼 샷 최대 60%)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;쿨타임 기반&lt;/b&gt;: 일정 쿨다운 후 확정 발동 (예: 죽음의 광선)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;보스 면역&lt;/b&gt;: 보스/엘리트는 즉사 + 강제이동(CC) 면역&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.4 별 등급 기준&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;생명력 손실 &amp;le; 기준값A (거의 무손실)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;★★&lt;/td&gt;
&lt;td&gt;생명력 손실 &amp;le; 기준값B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;★&lt;/td&gt;
&lt;td&gt;생명력 &amp;gt; 0으로 클리어&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기준값은 &lt;code&gt;WaveConfig.StarThresholds[]&lt;/code&gt;로 스테이지별 개별 설정&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 타워 건설 UI 인터랙션&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7.1 빈 건설 포인트 터치&lt;/h3&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;터치 &amp;rarr; 원형 링 메뉴 열림 (4종 타워 아이콘 + 비용 표시)
     ├─ 타워 선택 &amp;rarr; 골드 차감 &amp;rarr; 건설
     ├─ 골드 부족 &amp;rarr; 아이콘 비활성(그레이아웃) + 비용 빨간색
     └─ 외부 터치/취소 &amp;rarr; 메뉴 닫힘&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7.2 기존 타워 터치&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;터치 &amp;rarr; 타워 정보 + 액션 메뉴
     ├─ [업그레이드] 다음 레벨 비용 표시 (Lv1&amp;rarr;2&amp;rarr;3)
     ├─ [판매] 회수 골드 표시
     ├─ [분기 선택] Lv3&amp;rarr;Lv4 시 2개 분기 아이콘 표시 (택 1, 비가역)
     └─ [랠리 포인트] (병영 전용) 집결 위치 드래그 지정&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7.3 Lv4 이후 터치&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;터치 &amp;rarr; 특수 능력 구매 메뉴
     ├─ 능력 A (비용/효과 표시)
     ├─ 능력 B (비용/효과 표시)
     └─ [판매]&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 단계별 구현 계획&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8.1 1단계: 전투 프레임 구축&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameStateController&lt;/code&gt; 추가 (enum FSM)&lt;/li&gt;
&lt;li&gt;상태 전이 규칙 확정 (&amp;sect;4.1 다이어그램 기반)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Pause&lt;/code&gt; 진입/복귀 &amp;mdash; &lt;code&gt;Time.timeScale = 0&lt;/code&gt; + 오버레이 UI&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;산출물: 상태 전이 로그(디버그), FSM 유닛테스트&lt;/li&gt;
&lt;li&gt;완료 조건: Prepare&amp;rarr;WaveRunning&amp;rarr;WaveBreak&amp;rarr;Result 전이 끊김 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8.2 2단계: 맵/경로/웨이브 골격&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;PathManager&lt;/code&gt; &amp;mdash; 웨이포인트 기반 적 이동 경로 캐시&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WaveConfig&lt;/code&gt;/&lt;code&gt;EnemyConfig&lt;/code&gt; SO 도입&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SpawnManager&lt;/code&gt; 적 생성 + 경로 주입&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WaveManager&lt;/code&gt; 웨이브 종료 판정 (생존 적 0 + 스폰 완료)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;산출물: 최소 1개 스테이지 웨이브 실행&lt;/li&gt;
&lt;li&gt;완료 조건: 적이 경로를 따라 이동, 웨이브 시작/종료 정확 판정&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8.3 3단계: 타워/경제/블로킹&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;TowerManager&lt;/code&gt; 건설/업그레이드/판매/분기&lt;/li&gt;
&lt;li&gt;원형 링 메뉴 UI (&amp;sect;7 인터랙션 기반)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;InGameEconomyManager&lt;/code&gt; 골드 수급/소모&lt;/li&gt;
&lt;li&gt;병영 블로킹 루프 (적 상태: &lt;code&gt;Moving&amp;rarr;Blocked&amp;rarr;Attacking&amp;rarr;Dead&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;조기 호출 보상 (남은시간 &amp;times; 보상계수 + 스펠 쿨다운 단축)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;산출물: 4계열 기본 타워 동작, 조기 호출 버튼&lt;/li&gt;
&lt;li&gt;완료 조건: 경제 루프(처치&amp;rarr;골드&amp;rarr;강화) 안정 순환&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8.4 4단계: 영웅/결과/복귀&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;HeroController&lt;/code&gt; 이동/공격/스킬 최소 구현&lt;/li&gt;
&lt;li&gt;승/패 결과 계산 (&amp;sect;6.4 별 등급 기준 적용)&lt;/li&gt;
&lt;li&gt;저장 반영 + &lt;code&gt;WorldMapReturnAnimator&lt;/code&gt; 연계&lt;/li&gt;
&lt;li&gt;복귀 경로를 &lt;code&gt;WorldMapScene&lt;/code&gt;으로 통일&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;산출물: 승리/패배 UI, 저장 리포트 로그&lt;/li&gt;
&lt;li&gt;완료 조건: 승/패 후 월드맵 복귀 정상, 앱 재시작 후 기록 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8.5 5단계: Mock 격리/회귀 안정화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameMockController&lt;/code&gt;를 &lt;code&gt;DEV_MOCK&lt;/code&gt; 분기로 제한&lt;/li&gt;
&lt;li&gt;기본 실행 경로에서 Mock 완전 분리&lt;/li&gt;
&lt;li&gt;핵심 회귀 테스트 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;완료 조건: 기본 빌드 Mock 비활성, 주요 루프 회귀 통과&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. 데이터 스키마(초안)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;code&gt;StageConfig&lt;/code&gt;와 &lt;code&gt;WaveConfig&lt;/code&gt;의 관계&lt;/b&gt;: 기존 &lt;code&gt;StageConfig&lt;/code&gt; SO는 월드맵 표시용(위치/이름/해금)이고, &lt;code&gt;WaveConfig&lt;/code&gt;는 전투 실행용(웨이브/골드/적). &lt;code&gt;StageConfig.StageId&lt;/code&gt;를 키로 1:1 매핑하되, SO는 분리 유지한다. &lt;code&gt;StageConfig&lt;/code&gt;에 &lt;code&gt;WaveConfig&lt;/code&gt; 참조 필드를 추가하여 연결한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9.1 &lt;code&gt;WaveConfig&lt;/code&gt; (SO)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;StageId&lt;/code&gt; &amp;mdash; &lt;code&gt;StageConfig.StageId&lt;/code&gt;와 1:1 매핑&lt;/li&gt;
&lt;li&gt;&lt;code&gt;InitialGold&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;InitialLives&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;StarThresholds[]&lt;/code&gt; &amp;mdash; 3별/2별 판정 기준 (남은 생명력)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Wave[]&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;WaveIndex&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SpawnEntries[]&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;EnemyId&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Count&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SpawnInterval&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PathId&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SpawnDelay&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BonusGoldOnEarlyCall&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IsBossWave&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9.2 &lt;code&gt;TowerConfig&lt;/code&gt; (SO)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;TowerId&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerType&lt;/code&gt; (&lt;code&gt;Archer/Barracks/Mage/Artillery&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DamageType&lt;/code&gt; (&lt;code&gt;Physical/Magic/True&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BuildCost&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SellRate&lt;/code&gt; (회수 비율, 예: 0.6)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CanTargetAir&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Levels[]&lt;/code&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Damage&lt;/code&gt;, &lt;code&gt;FireRate&lt;/code&gt;, &lt;code&gt;Range&lt;/code&gt;, &lt;code&gt;UpgradeCost&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BarracksData&lt;/code&gt; (병영 전용, &lt;code&gt;TowerType == Barracks&lt;/code&gt;일 때만 사용):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;SoldierCount&lt;/code&gt; &amp;mdash; 소환 병사 수 (기본 3)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SoldierHP&lt;/code&gt;, &lt;code&gt;SoldierArmor&lt;/code&gt;, &lt;code&gt;SoldierDamage&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RespawnCooldown&lt;/code&gt; &amp;mdash; 병사 재소환 대기 시간&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RallyRange&lt;/code&gt; &amp;mdash; 집결 위치 지정 가능 반경&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BranchOptions[]&lt;/code&gt; (Lv4 분기):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;BranchId&lt;/code&gt;, &lt;code&gt;BranchName&lt;/code&gt;, &lt;code&gt;BranchCost&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Abilities[]&lt;/code&gt; (특수 능력, 개별 비용)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9.3 &lt;code&gt;EnemyConfig&lt;/code&gt; (SO)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;EnemyId&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HP&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ArmorPhysical&lt;/code&gt; (% 감소)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ArmorMagic&lt;/code&gt; (% 감소)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MoveSpeed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GoldBounty&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DamageToBase&lt;/code&gt; (탈출 시 생명력 차감)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IsFlying&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IsBoss&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IsInstaKillImmune&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SpawnOnDeath[]&lt;/code&gt; (하수인 소환 시)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9.4 &lt;code&gt;HeroConfig&lt;/code&gt; (SO)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;HeroId&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroName&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HP&lt;/code&gt;, &lt;code&gt;Armor&lt;/code&gt;, &lt;code&gt;MoveSpeed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AttackDamage&lt;/code&gt;, &lt;code&gt;AttackRange&lt;/code&gt;, &lt;code&gt;AttackSpeed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Skills[]&lt;/code&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;SkillId&lt;/code&gt;, &lt;code&gt;SkillType&lt;/code&gt; (&lt;code&gt;Passive/Active/Ultimate&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cooldown&lt;/code&gt;, &lt;code&gt;Duration&lt;/code&gt;, &lt;code&gt;DamageOrEffect&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9.5 &lt;code&gt;SpellConfig&lt;/code&gt; (SO)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;SpellId&lt;/code&gt; (증원군/불의비)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cooldown&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EarlyCallCooldownReduction&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Duration&lt;/code&gt;, &lt;code&gt;DamageOrEffect&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. KPI/텔레메트리(초기 필수)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;WaveClearTime&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LifeLostPerWave&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GoldIncomePerWave&lt;/code&gt; / &lt;code&gt;GoldSpendPerWave&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UnspentGoldAtWaveStart&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerBuildRateByType&lt;/code&gt; / &lt;code&gt;TowerSellRateByType&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnemyLeakCountByType&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnemyDeathHeatmap&lt;/code&gt; (경로 구간별)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EarlyCallUsageRate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HeroSkillUsageRate&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10.1 튜닝 기준(초기)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 웨이브 종료 후 평균 &lt;code&gt;신규 건설 0~1회&lt;/code&gt; 또는 &lt;code&gt;업그레이드 1회&lt;/code&gt; 가능한 경제값&lt;/li&gt;
&lt;li&gt;특정 타워 타입 사용률이 과도하게 치우치면 수치 조정 트리거&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UnspentGoldAtWaveStart&lt;/code&gt;가 지속적으로 높으면 난이도/가격 재조정&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. 위험 요소와 대응&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;초기화 순서 꼬임 &amp;rarr; Null/경로 누락&lt;/td&gt;
&lt;td&gt;부트스트랩 완료 전 &lt;code&gt;GameScene&lt;/code&gt; 진입 금지 가드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;병영 블로킹 &amp;rarr; 전투 상태 충돌&lt;/td&gt;
&lt;td&gt;적 상태를 &lt;code&gt;Moving/Blocked/Attacking/Dead&lt;/code&gt;로 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mock 경로 잔존 &amp;rarr; 실제 루프 왜곡&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DEV_MOCK&lt;/code&gt; 컴파일 심볼로 강제 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;피해 공식 누적 오차 &amp;rarr; 밸런스 붕괴&lt;/td&gt;
&lt;td&gt;고정소수점 또는 &lt;code&gt;int&lt;/code&gt; 기반 HP/데미지 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;타워 UI 터치 영역 겹침&lt;/td&gt;
&lt;td&gt;타워/건설포인트에 우선순위 레이어 + 상호배타 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserSaveData&lt;/code&gt; 인스턴스 중복 &amp;rarr; 데이터 덮어쓰기&lt;/td&gt;
&lt;td&gt;글로벌 &lt;code&gt;SaveManager&lt;/code&gt; 싱글톤으로 단일 인스턴스 보장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Time.timeScale&lt;/code&gt; 변경 &amp;rarr; Coroutine/DOTween 오동작&lt;/td&gt;
&lt;td&gt;&lt;code&gt;timeScale&lt;/code&gt; 비의존 타이머 분리, 속도 변경 시 회귀 테스트&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12. 체크리스트&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 월드맵 &amp;rarr; 게임 진입 시 스테이지 데이터 일치&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 웨이브 시작/종료와 HUD 동기화&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 타워 건설/업그레이드/판매 정상&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 병영 블로킹 정상&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 조기 웨이브 호출 보상 정상&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 영웅 이동/스킬 최소 기능 정상&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 승/패 결과 저장 반영 정상 (&amp;sect;6.4 별 등급)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 게임 &amp;rarr; 월드맵 복귀 루프 정상&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 앱 재실행 후 기록 유지&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 기본 빌드 Mock 비활성 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 피해 공식 검증 (물리/마법/고정/포병 관통)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 속도 조절(x1/x2) 시 Coroutine/애니메이션/물리 정상 동작&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 복귀 경로 통일 확인 (Mock/GameView 모두 &lt;code&gt;WorldMapScene&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13. 우선순위&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;상태머신 + 웨이브/스폰/경로&lt;/li&gt;
&lt;li&gt;타워/경제 + 병영 블로킹&lt;/li&gt;
&lt;li&gt;결과 처리 + 저장 + 월드맵 복귀&lt;/li&gt;
&lt;li&gt;영웅 최소 기능&lt;/li&gt;
&lt;li&gt;KPI 계측 + 튜닝&lt;/li&gt;
&lt;li&gt;Mock 제거 + 회귀 안정화&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14. 참고 메모&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;본 문서는 NotebookLM &lt;code&gt;킹덤러쉬&lt;/code&gt; 노트북을 기반으로 보강했다.&lt;/li&gt;
&lt;li&gt;반영 포인트:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;KR 스타일 핵심 전투 루프(병영 블로킹, 조기 호출)&lt;/li&gt;
&lt;li&gt;전투 씬 매니저 분리 원칙&lt;/li&gt;
&lt;li&gt;초기 단계 KPI 기반 밸런싱 접근&lt;/li&gt;
&lt;li&gt;피해 유형 3종(물리/마법/고정) + 포병 관통 공식&lt;/li&gt;
&lt;li&gt;별 등급 산정 기준 (라이프 기반)&lt;/li&gt;
&lt;li&gt;타워 건설 UI 인터랙션 흐름 (링 메뉴 &amp;rarr; 분기 &amp;rarr; 능력 구매)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;15. 누락 위험 체크리스트 (High Risk)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 병영/영웅이 적을 실제로 멈추게 하는 &lt;code&gt;Melee Lock&lt;/code&gt; 규칙이 구현됨&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 적 상태(&lt;code&gt;Moving/Blocked/Attacking/Dead&lt;/code&gt;) 전이가 프레임 단위로 충돌 없이 동작함&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 초기화 순서가 고정됨(맵/경로 로드 전 스폰 시작 금지)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 비행 적 타겟팅 제약(&lt;code&gt;CanTargetAir&lt;/code&gt;)이 타워별로 정확히 적용됨&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 방어력/저항/고정피해 계산식이 공통 함수로 단일화됨&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 보스 면역(즉사/강제이동 면역) 플래그가 실제 전투 판정에 반영됨&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 웨이브 미리보기 UI가 실 유닛 스폰 없이 데이터만으로 동작함&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 조기 웨이브 호출이 경제 보상과 스킬 쿨다운 감소를 함께 처리함&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; Pause 상태에서도 입력 허용 범위(배치/업글/이동)가 명확히 분리됨&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 사운드 동시 재생 시 우선순위/채널 제한으로 클러터링을 제어함&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;16. Definition Of Done (전투씬)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;16.1 기능&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 웨이브 데이터 기준으로 적 수량/타이밍이 정확히 스폰됨&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 적이 경로를 이탈하지 않고 목표 지점까지 이동함&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 타워가 유효 타겟만 공격하고(지상/공중 제약) 우선순위가 적용됨&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 병영 블로킹 및 영웅 교전이 정상 동작함&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 승/패 판정 및 결과 UI가 기획 조건대로 동작함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;16.2 성능&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 적/투사체/이펙트 오브젝트 풀링 적용&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 목표 기기에서 전투 피크 시 목표 FPS 유지&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 불필요한 GC 스파이크(웨이브 시작/종료 구간) 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;16.3 안정성&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; Null 참조 없이 타겟 소실 시 재탐색&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 씬 전환 반복(월드맵&amp;harr;게임) 30회 이상에서 누수/크래시 없음&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 저장 실패/로드 실패 시 안전 폴백 동작&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;16.4 밸런스&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 초반 3웨이브 체감 난이도가 급격히 튀지 않음&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 특정 타워 한 종류만으로 강제되지 않음(사용률 편중 감시)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 조기 호출 사용이 명확한 보상과 리스크를 가짐&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;16.5 회귀&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;WorldMapScene -&amp;gt; GameScene -&amp;gt; WorldMapScene&lt;/code&gt; 루프 회귀 통과&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 기존 저장 데이터와 신규 스키마 호환 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;DEV_MOCK&lt;/code&gt; 비활성 빌드에서 Mock 경로 호출 0건&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;17. 코드 반영 대상 (초안)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/GameScene.cs&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GameMockController&lt;/code&gt; 자동 생성 제거, &lt;code&gt;GameStateController&lt;/code&gt; 초기화 진입점으로 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/WorldMapScene.cs&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;진입 파라미터(&lt;code&gt;SelectedStageId&lt;/code&gt;, 난이도) 전달 검증 강화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/App/GameMockController.cs&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;DEV_MOCK&lt;/code&gt; 분기 격리 및 기본 빌드 경로 차단&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/UI/GameView.cs&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HUD/링 메뉴/결과 UI 이벤트 바인딩 확장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/WorldMap/WorldMapReturnAnimator.cs&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전투 결과 데이터 소비 지점 점검&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;신규(예정):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/GameStateController.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/WaveManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/SpawnManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/PathManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/InGameEconomyManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/TowerManager.cs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assets/Scripts/Kingdom/Game/HeroController.cs&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;18. 작업 순서(실행 단위)&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;GameScene&lt;/code&gt;에서 Mock 자동생성 제거 + FSM 뼈대 연결&lt;/li&gt;
&lt;li&gt;웨이브/스폰/경로 최소 루프 구현&lt;/li&gt;
&lt;li&gt;타워 건설/업글/판매 + 골드 루프 연결&lt;/li&gt;
&lt;li&gt;병영 블로킹 + 영웅 최소 동작 추가&lt;/li&gt;
&lt;li&gt;결과 저장 + 월드맵 복귀 루프 고정&lt;/li&gt;
&lt;li&gt;KPI 로그 삽입 후 수치 1차 튜닝&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/mKGSS/dJMcadt8rFY/rUmAK3QIAoUVilxbcnOnvk/task.md?attach=1&amp;amp;knm=tfile.md&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;task.md&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.01MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;게임씬 구현 &amp;mdash; Task 체크리스트&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근거 문서: &lt;a href=&quot;./%EA%B2%8C%EC%9E%84%EC%94%AC_%EA%B5%AC%ED%98%84%EB%AA%85%EC%84%B8%EC%84%9C.md&quot;&gt;게임씬_구현명세서.md&lt;/a&gt;&lt;br /&gt;작성일: 2026-02-14&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;0단계: 사전 정리 (선행 필수)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;UserSaveData&lt;/code&gt; &amp;rarr; 글로벌 &lt;code&gt;SaveManager&lt;/code&gt; 싱글톤으로 전환&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 복귀 경로 통일: &lt;code&gt;GameMockController&lt;/code&gt;, &lt;code&gt;GameView&lt;/code&gt; 모두 &lt;code&gt;WorldMapScene&lt;/code&gt;으로 변경&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;Assets/Scripts/Kingdom/Game/&lt;/code&gt; 폴더 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1단계: 전투 프레임 구축 (FSM)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명세서 &amp;sect;8.1 &amp;middot; 완료 조건: Prepare&amp;rarr;WaveRunning&amp;rarr;WaveBreak&amp;rarr;Result 전이 끊김 없음&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 작업&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;GameStateController.cs&lt;/code&gt; 신규 &amp;mdash; enum FSM (&lt;code&gt;Prepare/WaveRunning/WaveBreak/Result/Pause&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 상태 전이 규칙 구현 (&amp;sect;4.1 다이어그램 기반)&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;Pause&lt;/code&gt; 진입/복귀 &amp;mdash; &lt;code&gt;Time.timeScale = 0&lt;/code&gt; + 오버레이 UI&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;GameScene.cs&lt;/code&gt; 수정 &amp;mdash; Mock 자동생성 제거, &lt;code&gt;GameStateController&lt;/code&gt; 초기화 진입점으로 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;검증&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 상태 전이 로그(디버그) 출력 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; FSM 유닛테스트 작성 및 통과&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2단계: 맵/경로/웨이브 골격&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명세서 &amp;sect;8.2 &amp;middot; 완료 조건: 적이 경로를 따라 이동, 웨이브 시작/종료 정확 판정&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;WaveConfig&lt;/code&gt; SO 정의 (&amp;sect;9.1)&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;EnemyConfig&lt;/code&gt; SO 정의 (&amp;sect;9.3)&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;StageConfig&lt;/code&gt;에 &lt;code&gt;WaveConfig&lt;/code&gt; 참조 필드 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 작업&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;PathManager.cs&lt;/code&gt; 신규 &amp;mdash; 웨이포인트 기반 경로 캐시&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;SpawnManager.cs&lt;/code&gt; 신규 &amp;mdash; 적 생성 + 경로 주입&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;WaveManager.cs&lt;/code&gt; 신규 &amp;mdash; 웨이브 종료 판정 (생존 적 0 + 스폰 완료)&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 적 기본 이동 로직 (경로 따라 이동 &amp;rarr; 탈출 시 생명력 차감)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;검증&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 최소 1개 스테이지 웨이브 실행&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 초기화 순서 고정 확인 (맵/경로 로드 전 스폰 시작 금지) [High Risk]&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3단계: 타워/경제/블로킹&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명세서 &amp;sect;8.3 &amp;middot; 완료 조건: 경제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루프(처치&amp;rarr;골드&amp;rarr;강화) 안정 순환&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;TowerConfig&lt;/code&gt; SO 정의 (&amp;sect;9.2) &amp;mdash; &lt;code&gt;BarracksData&lt;/code&gt; 포함&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 피해 공식 공통 함수 단일화 (물리/마법/고정/포병 관통) [High Risk]&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 작업&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;TowerManager.cs&lt;/code&gt; 신규 &amp;mdash; 건설/업그레이드/판매/분기&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;InGameEconomyManager.cs&lt;/code&gt; 신규 &amp;mdash; 골드 수급/소모&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 원형 링 메뉴 UI 구현 (&amp;sect;7.1~7.3)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 병영 블로킹 루프 &amp;mdash; 적 상태: &lt;code&gt;Moving&amp;rarr;Blocked&amp;rarr;Attacking&amp;rarr;Dead&lt;/code&gt; [High Risk]&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 랠리 포인트 (병영 전용) 드래그 지정&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 조기 호출 보상 (남은시간 &amp;times; 보상계수 + 스펠 쿨다운 단축) [High Risk]&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 비행 적 타겟팅 제약 (&lt;code&gt;CanTargetAir&lt;/code&gt;) 적용 [High Risk]&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;검증&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 4계열 기본 타워 동작 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 조기 호출 버튼 동작 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 타워 UI 터치 영역 겹침 없음 (우선순위 레이어)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 피해 공식 검증 (물리/마법/고정/포병 관통)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4단계: 영웅/결과/복귀&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명세서 &amp;sect;8.4 &amp;middot; 완료 조건: 승/패 후 월드맵 복귀 정상, 앱 재시작 후 기록 유지&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;HeroConfig&lt;/code&gt; SO 정의 (&amp;sect;9.4)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;SpellConfig&lt;/code&gt; SO 정의 (&amp;sect;9.5)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 작업&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;HeroController.cs&lt;/code&gt; 신규 &amp;mdash; 이동/공격/스킬 최소 구현&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 승/패 결과 계산 (&amp;sect;6.4 별 등급 기준)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 결과 UI 표시 (승리 &amp;rarr; 별/보상, 패배 &amp;rarr; Retry/Exit)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 저장 반영 (&lt;code&gt;SaveManager&lt;/code&gt; 경유)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;WorldMapReturnAnimator&lt;/code&gt; 연계 &amp;mdash; 전투 결과 데이터 소비 지점 점검&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 보스 면역(즉사/강제이동 면역) 플래그 전투 판정 반영 [High Risk]&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;검증&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 월드맵 &amp;rarr; 게임 진입 시 스테이지 데이터 일치&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 승/패 결과 저장 반영 정상 (&amp;sect;6.4 별 등급)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 게임 &amp;rarr; 월드맵 복귀 루프 정상&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 앱 재실행 후 기록 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5단계: Mock 격리 / 회귀 안정화&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명세서 &amp;sect;8.5 &amp;middot; 완료 조건: 기본 빌드 Mock 비활성, 주요 루프 회귀 통과&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 작업&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;GameMockController&lt;/code&gt;를 &lt;code&gt;DEV_MOCK&lt;/code&gt; 컴파일 심볼 분기로 격리&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 기본 실행 경로에서 Mock 완전 분리&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;검증 (DoD &amp;sect;16)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;WorldMapScene &amp;rarr; GameScene &amp;rarr; WorldMapScene&lt;/code&gt; 루프 회귀 30회+&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;DEV_MOCK&lt;/code&gt; 비활성 빌드에서 Mock 경로 호출 0건&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 기존 저장 데이터와 신규 스키마 호환 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 씬 전환 반복 시 메모리 누수/크래시 없음&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 저장 실패/로드 실패 시 안전 폴백 동작&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6단계: KPI / 밸런스 튜닝&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명세서 &amp;sect;10 &amp;middot; 완료 조건: 텔레메트리 로그 삽입 + 수치 1차 튜닝&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 작업&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; KPI 로그 삽입 (&amp;sect;10 전체 항목)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;WaveClearTime&lt;/code&gt;, &lt;code&gt;LifeLostPerWave&lt;/code&gt;, &lt;code&gt;GoldIncomePerWave&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TowerBuildRateByType&lt;/code&gt;, &lt;code&gt;EnemyLeakCountByType&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EarlyCallUsageRate&lt;/code&gt;, &lt;code&gt;HeroSkillUsageRate&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 속도 조절(x1/x2) 구현 &amp;mdash; &lt;code&gt;timeScale&lt;/code&gt; 비의존 타이머 분리 [위험 &amp;sect;11]&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;검증&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 초반 3웨이브 체감 난이도 안정&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 특정 타워 타입 사용률 편중 없음&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 조기 호출 사용이 명확한 보상과 리스크를 가짐&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 속도 조절 시 Coroutine/애니메이션/물리 정상 동작&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 적/투사체/이펙트 오브젝트 풀링 적용 (성능)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 전투 피크 시 목표 FPS 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드 반영 대상 요약 (&amp;sect;17)&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;code&gt;GameScene.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mock 제거, FSM 진입점&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;수정&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WorldMapScene.cs&lt;/code&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;code&gt;GameMockController.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DEV_MOCK&lt;/code&gt; 격리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;수정&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GameView.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HUD/링메뉴/결과 UI 확장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;수정&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WorldMapReturnAnimator.cs&lt;/code&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;code&gt;Game/GameStateController.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;FSM 총괄&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;신규&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Game/WaveManager.cs&lt;/code&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;code&gt;Game/SpawnManager.cs&lt;/code&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;code&gt;Game/PathManager.cs&lt;/code&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;code&gt;Game/InGameEconomyManager.cs&lt;/code&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;code&gt;Game/TowerManager.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;타워 CRUD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;신규&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Game/HeroController.cs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;영웅 최소 기능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;진행 로그&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2026-02-14:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;SaveManager&lt;/code&gt; 추가 및 &lt;code&gt;KingdomAppManager&lt;/code&gt; 초기화 경유 연결 완료&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WorldMapScene&lt;/code&gt;, &lt;code&gt;GameMockController&lt;/code&gt;의 저장 접근을 &lt;code&gt;SaveManager.Instance.SaveData&lt;/code&gt;로 전환&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameMockController&lt;/code&gt; 복귀 경로를 &lt;code&gt;WorldMapScene&lt;/code&gt;으로 통일&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameMockController&lt;/code&gt;를 &lt;code&gt;DEV_MOCK&lt;/code&gt; 컴파일 심볼 영역으로 격리&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameStateController&lt;/code&gt;(FSM) 신규 추가 및 &lt;code&gt;GameScene&lt;/code&gt; 연결&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GameView&lt;/code&gt;에 FSM 바인딩/상태 표시/일시정지 토글 연결&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WaveConfig&lt;/code&gt;, &lt;code&gt;EnemyConfig&lt;/code&gt; SO 정의 추가 및 &lt;code&gt;StageData&lt;/code&gt;에 &lt;code&gt;WaveConfig&lt;/code&gt; 참조 필드 연결&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PathManager&lt;/code&gt;, &lt;code&gt;SpawnManager&lt;/code&gt;, &lt;code&gt;WaveManager&lt;/code&gt;, &lt;code&gt;EnemyRuntime&lt;/code&gt; 최소 전투 루프 스캐폴딩 추가&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dotnet build&lt;/code&gt; 점검 시 Unity 생성 &lt;code&gt;Assembly-CSharp.csproj&lt;/code&gt;에 신규 파일 미반영 상태 확인 (Unity 에디터 재생성 후 재검증 필요)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI/잡다한개발노트</category>
      <category>Antigravity</category>
      <category>Claude Opus 4.6</category>
      <category>codex 5.3</category>
      <category>codex 5.3 spark</category>
      <category>Devlog</category>
      <category>Unity</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/69</guid>
      <comments>https://blacknabis.tistory.com/69#entry69comment</comments>
      <pubDate>Sat, 14 Feb 2026 23:56:49 +0900</pubDate>
    </item>
    <item>
      <title>gpt pro 할인!!</title>
      <link>https://blacknabis.tistory.com/68</link>
      <description>&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;2316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9BhJk/dJMcajgOjj4/H4pgz0Pjtz5KMq3cKubwcK/tfile.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9BhJk/dJMcajgOjj4/H4pgz0Pjtz5KMq3cKubwcK/tfile.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9BhJk/dJMcajgOjj4/H4pgz0Pjtz5KMq3cKubwcK/tfile.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9BhJk%2FdJMcajgOjj4%2FH4pgz0Pjtz5KMq3cKubwcK%2Ftfile.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;904&quot; height=&quot;2316&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;2316&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;카카오톡에서 챗지피티로 검색하면 1/10 가격으로 프로모델을 최대 5대까지 구매할 수 있으며 이미 구독 중이면 구독이 종료된 뒤 사용하실 수 있습니다&lt;/p&gt;</description>
      <category>AI/Etc</category>
      <category>gpt pro</category>
      <category>지피티 할인</category>
      <category>카카오 지피티</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/68</guid>
      <comments>https://blacknabis.tistory.com/68#entry68comment</comments>
      <pubDate>Fri, 13 Feb 2026 11:40:33 +0900</pubDate>
    </item>
    <item>
      <title>Antigravity를 사용하여 타워 디펜스 게임 만들기 - 1</title>
      <link>https://blacknabis.tistory.com/67</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 시도하는건 NotebookLM과 Antigravity를 연동해서 빠른시간에 최대한 자동화해서 개발을 계획중입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1550&quot; data-origin-height=&quot;824&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xHpYh/dJMcajgN6q1/W6Xw9a6sfSx4for85E0Kyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xHpYh/dJMcajgN6q1/W6Xw9a6sfSx4for85E0Kyk/img.png&quot; data-alt=&quot;딥리서치를 사용하여 킹덤 러시에 대한 분석 요청 (https://notebooklm.google.com)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xHpYh/dJMcajgN6q1/W6Xw9a6sfSx4for85E0Kyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxHpYh%2FdJMcajgN6q1%2FW6Xw9a6sfSx4for85E0Kyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1550&quot; height=&quot;824&quot; data-origin-width=&quot;1550&quot; data-origin-height=&quot;824&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;딥리서치를 사용하여 킹덤 러시에 대한 분석 요청 (https://notebooklm.google.com)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 NotebookLM과 안티그래비티를 MCP로 연동해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1541&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7gEJb/dJMb99L3DQJ/NKz9KLqVRONBABDXG3jTT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7gEJb/dJMb99L3DQJ/NKz9KLqVRONBABDXG3jTT0/img.png&quot; data-alt=&quot;공용 라이브러리와 NotebookLM을 참고하도록 룰설정.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7gEJb/dJMb99L3DQJ/NKz9KLqVRONBABDXG3jTT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7gEJb%2FdJMb99L3DQJ%2FNKz9KLqVRONBABDXG3jTT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1541&quot; height=&quot;436&quot; data-origin-width=&quot;1541&quot; data-origin-height=&quot;436&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;공용 라이브러리와 NotebookLM을 참고하도록 룰설정.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 씬구성 요청&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;681&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPrAfS/dJMcaioJEoc/pydAzuWqhty3dA6O3pA13k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPrAfS/dJMcaioJEoc/pydAzuWqhty3dA6O3pA13k/img.png&quot; data-alt=&quot;처음 씬 구성하였을 때 3개만 구성되어서 NotebookLM을 사용하여 다시한번 일관성 채크.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPrAfS/dJMcaioJEoc/pydAzuWqhty3dA6O3pA13k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPrAfS%2FdJMcaioJEoc%2FpydAzuWqhty3dA6O3pA13k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;587&quot; height=&quot;681&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;681&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;처음 씬 구성하였을 때 3개만 구성되어서 NotebookLM을 사용하여 다시한번 일관성 채크.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMxtzm/dJMcah4pZNG/jhRExxeI3LHrwCYLHYu1Ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMxtzm/dJMcah4pZNG/jhRExxeI3LHrwCYLHYu1Ak/img.png&quot; data-alt=&quot;카메라가 없어서 다시 카메라 추가를 요청&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMxtzm/dJMcah4pZNG/jhRExxeI3LHrwCYLHYu1Ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMxtzm%2FdJMcah4pZNG%2FjhRExxeI3LHrwCYLHYu1Ak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;772&quot; height=&quot;402&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;402&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;카메라가 없어서 다시 카메라 추가를 요청&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;606&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMJp3V/dJMcadgDBNF/naGPya1E9pUkPzIO2znjv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMJp3V/dJMcadgDBNF/naGPya1E9pUkPzIO2znjv0/img.png&quot; data-alt=&quot;플레이를하면 비어있는 타이틀신에서 알아서 타이틀씬 스크립트를 읽어와서 타이틀씬UI프리팹을 UIManager에 이쁘게 등록된걸 확인.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMJp3V/dJMcadgDBNF/naGPya1E9pUkPzIO2znjv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMJp3V%2FdJMcadgDBNF%2FnaGPya1E9pUkPzIO2znjv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1404&quot; height=&quot;606&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;606&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;플레이를하면 비어있는 타이틀신에서 알아서 타이틀씬 스크립트를 읽어와서 타이틀씬UI프리팹을 UIManager에 이쁘게 등록된걸 확인.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ComfyUI가 반복적으로 에러가나서 확인해봤더니 내가 이전에 세팅한 이미지 생성 모델은 ace_step_v1_3.5b인데 안티그래비티가 따로 플로우워크.json을 주지않으면 제대로 못알아먹어서 v1-5-pruned-emaonly설치후 다시 진행. 잘된다..&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;441&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Poale/dJMcahQSCIu/LAVA9NntIY0LnxFOD9Gfpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Poale/dJMcahQSCIu/LAVA9NntIY0LnxFOD9Gfpk/img.png&quot; data-alt=&quot;이미지가 마음에 안들어서 Flow에서 나노바나나로 다시 제작...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Poale/dJMcahQSCIu/LAVA9NntIY0LnxFOD9Gfpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPoale%2FdJMcahQSCIu%2FLAVA9NntIY0LnxFOD9Gfpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;811&quot; height=&quot;441&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;441&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이미지가 마음에 안들어서 Flow에서 나노바나나로 다시 제작...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flow에서 나노바나나로 이미지를 다시 만들고 remove.bg에서 버튼이랑 로고의 배경을 삭제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이하 안티그래비티로 만든 개발일지 입니다.&lt;/b&gt;&lt;/p&gt;
&lt;h1&gt;AI로 게임 UI 에셋 만들기 &amp;mdash; ComfyUI + Unity 자동화 파이프라인&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stable Diffusion으로 타이틀 화면 에셋을 생성하고, Unity Editor 스크립트로 자동 적용하는 과정을 기록합니다.&lt;br /&gt;&quot;그림은 AI가, 텍스트는 TextMeshPro가, 조립은 Editor 스크립트가.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  목표&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타워 디펜스 게임 &lt;b&gt;Kingdom Defense&lt;/b&gt;의 타이틀 화면에 필요한 에셋 3종을 &lt;b&gt;ComfyUI&lt;/b&gt;로 생성하고, &lt;b&gt;Unity Editor 스크립트&lt;/b&gt;로 프리팹에 자동 적용하기.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;에셋&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;용도&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;크기&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;Title_Background.png&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;전체 배경&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;768&amp;times;512&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;Title_Logo.png&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;엠블렘 장식&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;512&amp;times;512&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;Title_BtnStart.png&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;시작 버튼 프레임&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;512&amp;times;256&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 아키텍처&lt;/h2&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;comfy_bridge.py (Python)
    │
    ├─ ComfyUI API (/prompt, /history, /view)
    │   ├─ CheckpointLoaderSimple (v1-5-pruned-emaonly)
    │   ├─ KSampler &amp;rarr; VAEDecode
    │   └─ Image Remove Background (rembg)  &amp;larr; 배경 제거
    │
    └─ 출력 &amp;rarr; Assets/Resources/UI/Title/*.png
                    │
TitleAssetGenerator.cs (Unity Editor)
    │  Python 프로세스 실행
    └─ 생성 완료 후 AssetDatabase.Refresh()
                    │
ForceAssignTitleAssets.cs (Unity Editor)
    ├─ TextureImporter 설정 (Sprite, Alpha, 9-Slice)
    ├─ PrefabUtility로 TitleView.prefab 수정
    │   ├─ Background: Image (Stretched)
    │   ├─ Logo: Image (엠블렘, SetNativeSize)
    │   └─ btnStart: Image (9-Slice) + TMP &quot;TAP TO START&quot;
    └─ SerializedObject로 private 필드 바인딩&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  핵심 교훈 3가지&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. SD1.5는 텍스트를 못 만든다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;처음 시도:&lt;/b&gt; 프롬프트에 &lt;code&gt;'Kingdom Defense'&lt;/code&gt; 텍스트를 직접 넣음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결과:&lt;/b&gt; 글자가 뒤섞인 의미 없는 이미지 (아래 예시)&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;입력: &quot;game logo text 'Kingdom Defense', stone texture...&quot;
출력: &quot;KALEM DFOM LETE&quot;  &lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결:&lt;/b&gt; 장식 요소만 AI로 생성하고, 실제 텍스트는 &lt;b&gt;TextMeshPro&lt;/b&gt;로 분리&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;# ❌ Before &amp;mdash; AI에게 텍스트 요청
&quot;game logo text 'Kingdom Defense', stone texture, gold border...&quot;

# ✅ After &amp;mdash; 장식만 요청, 텍스트는 Unity TMP로
&quot;medieval fantasy shield emblem, golden ornate frame, royal crest, 
 no text, no letters, no words&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;원칙:&lt;/b&gt; AI 이미지 = 장식/질감/분위기, 텍스트 = TextMeshPro. 역할을 분리하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. RemBG로 배경 제거 자동화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;투명 배경이 필요한 에셋(로고, 버튼)은 ComfyUI의 &lt;code&gt;Image Remove Background (rembg)&lt;/code&gt; 노드를 활용.&lt;/p&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;# Python에서 워크플로우에 RemBG 노드 동적 삽입
if use_rembg:
    workflow[&quot;10&quot;] = { 
        &quot;class_type&quot;: &quot;Image Remove Background (rembg)&quot;, 
        &quot;inputs&quot;: { &quot;image&quot;: [last_image_node, 0] } 
    }
    last_image_node = &quot;10&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 C#에서 흑색 픽셀을 투명으로 바꾸는 &lt;b&gt;Chroma Key&lt;/b&gt; 방식을 시도했지만, 경계가 지저분했음. RemBG가 훨씬 깔끔한 결과를 줌.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;방식&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;품질&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;속도&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;비고&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;C# Chroma Key&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;⭐⭐&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;빠름&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;경계 노이즈, 그림자 잔존&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;RemBG (rembg)&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;⭐⭐⭐⭐&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;약간 느림&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;AI 기반, 깔끔한 마스크&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Unity &amp;harr; Python 브릿지 패턴&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity의 &lt;code&gt;UnityWebRequest&lt;/code&gt;로 ComfyUI에 직접 연결하려 했으나, 에디터 환경에서 비동기 처리가 불안정. &lt;b&gt;Python 스크립트를 별도 프로세스로 실행&lt;/b&gt;하는 방식이 가장 안정적이었음.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;Unity Editor (C#)
    │
    └─ System.Diagnostics.Process.Start(&quot;python&quot;, &quot;comfy_bridge.py&quot;)
        │
        └─ Python (urllib) ─── HTTP ──&amp;rarr; ComfyUI (localhost:8188)
                                              │
                                              └─ 이미지 파일 저장
                                                     │
Unity Editor &amp;larr;── AssetDatabase.Refresh() ────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Python은 HTTP 통신이 간편 (urllib 표준 라이브러리)&lt;/li&gt;
&lt;li&gt;ComfyUI API 호환성 걱정 없음&lt;/li&gt;
&lt;li&gt;Unity Editor의 메인 스레드 블로킹 회피&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 프리팹 자동 조립 (ForceAssignTitleAssets.cs)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에셋이 생성되면 &lt;b&gt;한 번의 메뉴 클릭&lt;/b&gt;으로 프리팹에 자동 적용:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// 1. 텍스처 임포트 설정
EnsureSprite(&quot;Title_Background.png&quot;);
EnsureSprite(&quot;Title_Logo.png&quot;);
EnsureSprite(&quot;Title_BtnStart.png&quot;, sliced: true);  // 9-Slice 자동 설정

// 2. 프리팹 로드 &amp;rarr; 수정 &amp;rarr; 저장
var rootGo = PrefabUtility.LoadPrefabContents(prefabPath);
SetupBackground(so, rootGo, folder);  // 전체 화면 배경
SetupLogo(rootGo, folder);            // 엠블렘 (크기 자동 조절)
SetupButton(so, rootGo, folder);      // 9-Slice 버튼 + TMP 텍스트
PrefabUtility.SaveAsPrefabAsset(rootGo, prefabPath);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 테크닉:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;PrefabUtility.LoadPrefabContents&lt;/code&gt; &amp;mdash; 씬에 없는 프리팹도 직접 수정 가능&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SerializedObject&lt;/code&gt; &amp;mdash; private 필드(&lt;code&gt;backgroundImage&lt;/code&gt;, &lt;code&gt;btnStart&lt;/code&gt;)에 강제 할당&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TextureImporter.spriteBorder&lt;/code&gt; &amp;mdash; 9-Slice 보더 자동 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Disposable Tool 패턴&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로젝트에서는 &lt;b&gt;일회성 에디터 도구&lt;/b&gt;를 적극 활용합니다:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Assets/Scripts/Kingdom/Editor/
├── comfy_bridge.py            &amp;larr; ComfyUI 통신 (사용 후 삭제)
├── TitleAssetGenerator.cs     &amp;larr; Python 실행기 (사용 후 삭제)
└── ForceAssignTitleAssets.cs  &amp;larr; 프리팹 조립기 (사용 후 삭제)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에셋 생성/설정이 끝나면 스크립트를 &lt;b&gt;즉시 삭제&lt;/b&gt;합니다. 프로젝트에 불필요한 코드가 남지 않도록 하는 것이 원칙.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  전체 워크플로우 요약&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. comfy_bridge.py 실행
   &amp;rarr; ComfyUI가 3장의 이미지 생성 (배경/엠블렘/버튼 프레임)
   &amp;rarr; 로고&amp;middot;버튼은 RemBG로 배경 자동 제거

2. ForceAssignTitleAssets 실행 (Unity 메뉴)
   &amp;rarr; 텍스처 &amp;rarr; Sprite 변환, 9-Slice 보더 설정
   &amp;rarr; TitleView.prefab에 이미지 자동 배치
   &amp;rarr; 버튼 위에 &quot;TAP TO START&quot; TMP 텍스트 생성
   &amp;rarr; SerializedObject로 private 필드 바인딩

3. 결과 확인 &amp;rarr; 일회성 스크립트 삭제&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  다음에 시도해볼 것&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ControlNet&lt;/b&gt; 활용 &amp;mdash; 레퍼런스 이미지 기반으로 스타일 일관성 확보&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LoRA 학습&lt;/b&gt; &amp;mdash; Kingdom Rush 스타일 전용 모델 파인튜닝&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SDXL 전환&lt;/b&gt; &amp;mdash; 더 높은 해상도와 디테일&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Addressables 기반 에셋 로딩&lt;/b&gt; &amp;mdash; Resources 폴더 의존성 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 이미지 생성은 &quot;만능&quot;이 아닙니다.&lt;br /&gt;텍스트, 정밀한 레이아웃, UI 인터랙션은 여전히 전통적인 도구가 낫습니다.&lt;br /&gt;&lt;b&gt;AI가 잘하는 것(질감, 분위기, 장식)과 엔진이 잘하는 것(텍스트, 레이아웃, 로직)을 분리&lt;/b&gt;하는 것이 핵심입니다.&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>AI/Unity</category>
      <category>AI</category>
      <category>Antigravity</category>
      <category>Devlog</category>
      <category>flow</category>
      <category>notebooklm</category>
      <category>remove.bg</category>
      <category>Unity</category>
      <category>타워디펜스</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/67</guid>
      <comments>https://blacknabis.tistory.com/67#entry67comment</comments>
      <pubDate>Fri, 13 Feb 2026 02:27:34 +0900</pubDate>
    </item>
    <item>
      <title>Unity 공용 라이브러리(Common) 개발기 &amp;mdash; 2</title>
      <link>https://blacknabis.tistory.com/66</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity 프로젝트를 여러 개 진행하다 보면 매번 같은 코드를 복붙하게 됩니다.&lt;br /&gt;&quot;이거 한 번 정리하면 두고두고 쓸 텐데&amp;hellip;&quot; 라는 생각으로 &lt;b&gt;Common 라이브러리&lt;/b&gt;를 만들었습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  목표&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트 간 &lt;b&gt;재사용 가능한 공용 코드&lt;/b&gt; 정리&lt;/li&gt;
&lt;li&gt;GC 최적화, 편의성 극대화&lt;/li&gt;
&lt;li&gt;한글 주석으로 팀원도 쉽게 이해 가능&lt;/li&gt;
&lt;li&gt;계층화된 UI 시스템으로 팝업/HUD/화면 전환 일관성 확보&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  구현한 유틸리티 (18개)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1순위 &amp;mdash; 거의 모든 프로젝트에서 필수&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;클래스&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;한 줄 설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;ObjectPool&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;프리팹 오브젝트 풀링. &lt;code&gt;Get()&lt;/code&gt; / &lt;code&gt;Release()&lt;/code&gt; / &lt;code&gt;PreWarm()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;EventBus&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;전역 이벤트 시스템. &lt;code&gt;Subscribe&amp;lt;T&amp;gt;()&lt;/code&gt; / &lt;code&gt;Publish&amp;lt;T&amp;gt;()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;WaitCache&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;new WaitForSeconds()&lt;/code&gt; GC 제거. &lt;code&gt;WaitCache.Get(0.5f)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;PlayerPrefsHelper&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;bool, enum, DateTime, JSON 직렬화 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;EventBus 사용 예:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// 이벤트 정의
public struct PlayerDiedEvent { public int score; }

// 구독
EventBus.Subscribe&amp;lt;PlayerDiedEvent&amp;gt;(e =&amp;gt; ShowGameOver(e.score));

// 발행
EventBus.Publish(new PlayerDiedEvent { score = 1500 });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;WaitCache 사용 예:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;// Before ❌ &amp;mdash; 매번 GC 발생
yield return new WaitForSeconds(0.5f);

// After ✅ &amp;mdash; 캐시된 인스턴스 재사용
yield return WaitCache.Get(0.5f);&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2순위 &amp;mdash; 모바일 필수 + 코드 간결화&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;클래스&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;한 줄 설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;SafeAreaHelper&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;노치/펀치홀 대응 자동 레이아웃&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;CoroutineExtensions&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;this.Delay(1f, callback)&lt;/code&gt; 한 줄 딜레이&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;CollectionExtensions&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;list.GetRandom()&lt;/code&gt;, &lt;code&gt;Shuffle()&lt;/code&gt; (GC-free)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;StringExtensions&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;숫자 콤마, 시간 포맷, K/M 축약&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;ColorExtensions&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;color.WithAlpha(0.5f)&lt;/code&gt;, Hex 변환&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CoroutineExtensions 사용 예:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// Before ❌
StartCoroutine(DelayCoroutine());
IEnumerator DelayCoroutine() {
    yield return new WaitForSeconds(1f);
    DoSomething();
}

// After ✅
this.Delay(1f, () =&amp;gt; DoSomething());&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3순위 &amp;mdash; 게임 로직 지원&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;클래스&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;한 줄 설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;SimpleStateMachine&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Enum 기반 FSM. Enter/Update/Exit 콜백&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;AudioHelper&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;BGM/SFX 싱글톤. 볼륨, 페이드, 자동 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;ScreenTransition&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;씬 전환 페이드. &lt;code&gt;FadeOutIn(loadScene)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;DebugLogger&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;카테고리 로깅. 릴리스 빌드 자동 제거&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4순위 &amp;mdash; 생산성 부스터&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;클래스&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;한 줄 설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;Timer&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;카운트다운/업, 일시정지, 반복, 콜백&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;VectorExtensions&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;pos.WithY(0)&lt;/code&gt;, &lt;code&gt;Flat()&lt;/code&gt;, &lt;code&gt;RandomInCircle()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;MathUtils&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;Chance(30)&lt;/code&gt; 확률, &lt;code&gt;Remap()&lt;/code&gt;, &lt;code&gt;Damp()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;UIButtonExtensions&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;SetOnClick()&lt;/code&gt;, 연타 방지 쿨다운&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;SceneExtensions&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;비활성 포함 씬 내 컴포넌트 검색&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ UI 프레임워크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 &lt;code&gt;BaseUI&lt;/code&gt;는 Show/Hide만 있는 껍데기였습니다. 이걸 &lt;b&gt;레이어 기반 UI 관리 시스템&lt;/b&gt;으로 완전히 재설계했습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;아키텍처&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;UIHelper (static)  &amp;larr; 호출 코드에서 사용
   &amp;darr;
UIManager (싱글톤)
 ├ 레이어별 Canvas 자동 생성
 │   HUD(0) &amp;rarr; Screen(100) &amp;rarr; Popup(200) &amp;rarr; Overlay(300)
 ├ UI 인스턴스 캐시 (Dictionary&amp;lt;Type, BaseView&amp;gt;)
 ├ 팝업 스택 (List&amp;lt;BasePopup&amp;gt;)
 └ Back Key 라우팅 (팝업 &amp;rarr; View 순)
   &amp;darr;
기본 클래스
 ├ BaseView   &amp;mdash; 전체 화면 (CanvasGroup 페이드 내장)
 ├ BasePopup  &amp;mdash; 모달 (딤드 배경, 닫기 가드)
 ├ BaseHUD    &amp;mdash; 항상 표시 (HP, 점수)
 └ BaseWidget &amp;mdash; 재사용 위젯 (스크롤 아이템)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용 예&lt;/h3&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;// 화면 전환 &amp;mdash; Resources/UI/ 에서 자동 로드
UIHelper.Show&amp;lt;MainMenuView&amp;gt;();

// 팝업 + 닫기 콜백
UIHelper.ShowPopup&amp;lt;SettingsPopup&amp;gt;(onClose: () =&amp;gt; SaveSettings());

// HUD 갱신
UIHelper.Get&amp;lt;ScoreHUD&amp;gt;()?.Refresh(currentScore);

// 씬 전환 시 전체 정리
UIHelper.ClearAll();&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;BasePopup 핵심 기능&lt;/h3&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public class ConfirmPopup : BasePopup
{
    [SerializeField] private Button confirmButton;

    protected override void OnInit()
    {
        base.OnInit();
        confirmButton.SetOnClick(OnConfirm);
    }

    // 닫기 전 확인 (false 반환 시 닫기 차단)
    protected override bool OnCloseRequested()
    {
        return hasUnsavedChanges == false;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;✅ 딤드 배경 자동 생성/제거&lt;/li&gt;
&lt;li&gt;✅ 딤드 클릭으로 닫기 (옵션)&lt;/li&gt;
&lt;li&gt;✅ Back Key 처리&lt;/li&gt;
&lt;li&gt;✅ 닫기 가드 (&lt;code&gt;OnCloseRequested&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;✅ UIManager 팝업 스택 연동&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  최종 폴더 구조&lt;/h2&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Assets/Scripts/Common/
├── App/           AppManagerBase, SceneBase, Appdelegate, ISceneController
├── Patterns/      MonoSingleton, ObjectPool, EventBus, SimpleStateMachine
├── Extensions/    Object, GameObject, Transform, UI, Coroutine,
│                  Collection, String, Color, Vector, UIButton, Scene
├── Utils/         WaitCache, PlayerPrefsHelper, AudioHelper,
│                  DebugLogger, Timer, MathUtils
└── UI/            UIManager, UIHelper, UILayer, BaseView, BasePopup,
                   BaseHUD, BaseWidget, SafeAreaHelper, ScreenTransition&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  설계 원칙&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;IsNull() / IsNotNull()&lt;/b&gt; &amp;mdash; Unity fake null 대응 전용 확장 메서드로 통일&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GC 최적화&lt;/b&gt; &amp;mdash; WaitCache, ObjectPool, GC-free Collection 확장&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Domain Reload 대응&lt;/b&gt; &amp;mdash; EventBus, WaitCache에 &lt;code&gt;[RuntimeInitializeOnLoadMethod]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;한글 주석&lt;/b&gt; &amp;mdash; 모든 public API에 XML 문서 주석 (한국어)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MonoSingleton&lt;/b&gt; &amp;mdash; AudioHelper, ScreenTransition, UIManager 등 DontDestroyOnLoad 자동 처리&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  다음 계획&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Addressables 기반 UI 로딩 (Resources 대체)&lt;/li&gt;
&lt;li&gt;DOTween 연동 애니메이션 헬퍼&lt;/li&gt;
&lt;li&gt;네트워크 HTTP 헬퍼 (UniTask 기반)&lt;/li&gt;
&lt;li&gt;로컬라이제이션 시스템&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Common 라이브러리는 Git 서브모듈로 관리하여 여러 프로젝트에서 공유합니다.&lt;br /&gt;새 프로젝트를 시작할 때 &lt;code&gt;git submodule add&lt;/code&gt;로 바로 가져다 쓸 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  업데이트 로그&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2026-02-12: Coroutine &amp;rarr; UniTask 전면 마이그레이션&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;UniTask 패키지 도입&lt;/b&gt; (&lt;code&gt;com.cysharp.unitask&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;코루틴 &amp;rarr; async/await 전환&lt;/b&gt;: &lt;code&gt;AudioHelper&lt;/code&gt;, &lt;code&gt;BaseView&lt;/code&gt;, &lt;code&gt;ScreenTransition&lt;/code&gt;, &lt;code&gt;UIButtonExtensions&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CancellationTokenSource 기반 취소 패턴&lt;/b&gt; 적용 (기존 &lt;code&gt;StopCoroutine&lt;/code&gt; 대체)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CoroutineExtensions &amp;rarr; UniTaskExtensions&lt;/b&gt; 전면 교체 (&lt;code&gt;GetCancellationTokenOnDestroy()&lt;/code&gt; 기반 자동 취소)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;WaitCache 삭제&lt;/b&gt; &amp;mdash; UniTask 자체 GC-free 대기 메서드로 대체&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UIButtonExtensions&lt;/code&gt; 쿨다운에서 MonoBehaviour 런너 불필요해짐&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Antigravity</category>
      <category>Claude Opus 4.6</category>
      <category>Devlog</category>
      <category>개발일지</category>
      <category>안티그래비티</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/66</guid>
      <comments>https://blacknabis.tistory.com/66#entry66comment</comments>
      <pubDate>Thu, 12 Feb 2026 00:53:26 +0900</pubDate>
    </item>
    <item>
      <title>AI모델 주저리 주저리... (Claude Opus 4.6 아주 짤막한 사용기)</title>
      <link>https://blacknabis.tistory.com/65</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;유니티 Common을 구축중 Claude Opus 4.6이 나와서 문득 Gemini pro 3.0하고 어느정도 차이일까 궁금해졌다. 이전에도 계획을 짤 때 Opus모델이 내취향이긴했는데....&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o0WMy/dJMcadHE5Tl/wJ0qvy3mEukhf2dFbHu5O1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o0WMy/dJMcadHE5Tl/wJ0qvy3mEukhf2dFbHu5O1/img.png&quot; data-alt=&quot;Antigravity 3.0 Pro (High)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o0WMy/dJMcadHE5Tl/wJ0qvy3mEukhf2dFbHu5O1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo0WMy%2FdJMcadHE5Tl%2FwJ0qvy3mEukhf2dFbHu5O1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;730&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;730&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Antigravity 3.0 Pro (High)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;840&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbLG4c/dJMcaiCdJ2B/dEgEabCr7DgF8UCkM59PRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbLG4c/dJMcaiCdJ2B/dEgEabCr7DgF8UCkM59PRk/img.png&quot; data-alt=&quot;Claude Opus 4.6 (Thinking)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbLG4c/dJMcaiCdJ2B/dEgEabCr7DgF8UCkM59PRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbLG4c%2FdJMcaiCdJ2B%2FdEgEabCr7DgF8UCkM59PRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;655&quot; height=&quot;840&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;840&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Claude Opus 4.6 (Thinking)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확실히 Opus 4.6이마음에 들긴하다. 안티그래비티로 작업전에 항상 플랜을 세우고 진행하는데 Opus로 계획을 세우고 Sonnet이나 제미나이 프로모델로 코드를 구현하게 이제 개인적인 베스트 인거같다. 문제는 Claude의 경우 토큰이 너무 빨리 빠져나감..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dF0NWY/dJMcac24qni/T3os8DyzUvWfSPNLyB9Py1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dF0NWY/dJMcac24qni/T3os8DyzUvWfSPNLyB9Py1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dF0NWY/dJMcac24qni/T3os8DyzUvWfSPNLyB9Py1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdF0NWY%2FdJMcac24qni%2FT3os8DyzUvWfSPNLyB9Py1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;729&quot; height=&quot;434&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;565&quot; data-origin-height=&quot;925&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/655Io/dJMcafZN22o/WBR9HXrVfeagaefxZY9msK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/655Io/dJMcafZN22o/WBR9HXrVfeagaefxZY9msK/img.png&quot; data-alt=&quot;작업 진행 내역도 너무 마음에 뽑아준다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/655Io/dJMcafZN22o/WBR9HXrVfeagaefxZY9msK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F655Io%2FdJMcafZN22o%2FWBR9HXrVfeagaefxZY9msK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;565&quot; height=&quot;925&quot; data-origin-width=&quot;565&quot; data-origin-height=&quot;925&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;작업 진행 내역도 너무 마음에 뽑아준다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 게임 개발에 Notebooklm을 어떻게 붙여볼까하가 이것저것 해봤는데 취향에 맞진 않는거같다. 그나마 역할 분담을 시키고 nootbooklm연동하게해서 계속 문서를 갱신했지만 후반부에는 역할분담이 꺠지는거 같아서 이쪽에 대해서는 더 많은 고민 후 다시 시도하고 성공하게 되면 블로그에 남기겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공부는 계속 된다.... 가즈아.. 코드는 Opus인듯.. 언제 기회가 된다면 GPT 5.3도 써보고 싶네요&lt;/p&gt;</description>
      <category>AI/Etc</category>
      <category>antygravity</category>
      <category>Claude Opus 4.6</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/65</guid>
      <comments>https://blacknabis.tistory.com/65#entry65comment</comments>
      <pubDate>Thu, 12 Feb 2026 00:03:20 +0900</pubDate>
    </item>
    <item>
      <title>Antigravity와 함께한 SF6 Viewer 개발기: 아이디어에서 배포까지</title>
      <link>https://blacknabis.tistory.com/64</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;안녕하세요! 최근 &lt;b&gt;Street Fighter 6&lt;/b&gt; 방송용 전적 오버레이 프로그램인 &lt;b&gt;SF6 Viewer&lt;/b&gt;를 개발하며 겪은 과정과, AI 코딩 어시스턴트 &lt;b&gt;Antigravity&lt;/b&gt;를 활용해 어떻게 난관들을 극복했는지 공유하려 합니다.&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 프로젝트의 시작: &quot;방송 화면에 내 전적을 멋지게 띄우고 싶다&quot;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스트리트 파이터 6를 즐기면서 시청자들에게 실시간으로 &lt;b&gt;연승/연패 기록&lt;/b&gt;과 &lt;b&gt;MR 변동&lt;/b&gt;을 보여주고 싶었습니다. 기존 웹 기반 툴은 새로고침이 번거롭거나 디자인이 투박했죠.&lt;br /&gt;그래서 &lt;b&gt;&quot;게임 화면 위에 자연스럽게 녹아드는 투명한 HUD&quot;&lt;/b&gt;를 직접 만들기로 결심했습니다. 하지만 혼자서 디자인부터 DB 설계, 크롤링, 배포 파일 패키징까지 하기엔 시간이 부족했습니다.&lt;br /&gt;&lt;b&gt;해결책:&lt;/b&gt; Antigravity에게 &quot;SF6 Viewer를 만들어줘&quot;라고 요청했습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 디자인의 진화: 텍스트 한 줄에서 글래스모피즘까지  &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 프로토타입은 단순히 텍스트만 나열된 형태였습니다. 하지만 Antigravity에게 &lt;b&gt;&quot;더 프리미엄하고 모던하게&quot;&lt;/b&gt;, &lt;b&gt;&quot;사이버펑크 느낌의 네온 컬러를 써줘&quot;&lt;/b&gt;라고 요청하자, 놀라운 변화가 일어났습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Glassmorphism&lt;/b&gt;: 배경을 반투명하게 블러 처리하여 게임 화면을 가리지 않으면서도 가독성을 높였습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Area Chart&lt;/b&gt;: 단순한 꺾은선 그래프 대신 그라데이션이 들어간 영역 차트로 MR 상승/하락 추이를 직관적으로 표현했습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Micro-Animations&lt;/b&gt;: 수치가 변할 때 부드럽게 숫자가 올라가는 애니메이션을 적용했습니다.&lt;br /&gt;저 혼자 CSS를 깎았다면 며칠이 걸렸을 작업을, AI와 티키타카하며 단 몇 시간 만에 완성했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 기술적 도전: Playwright와 실시간 데이터 수집  ️&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 중요한 기능은 &lt;b&gt;실시간 전적 수집&lt;/b&gt;이었습니다. 공식 API가 없기 때문에 &lt;b&gt;Playwright&lt;/b&gt;를 사용하여 백그라운드 웹 크롤링을 구현했습니다.&lt;br /&gt;&lt;b&gt;Antigravity의 활약:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저를 띄우지 않는 &lt;b&gt;Headless 모드&lt;/b&gt; 구현&lt;/li&gt;
&lt;li&gt;&lt;b&gt;로그인 세션 유지 (auth.json)&lt;/b&gt; 로직 작성&lt;/li&gt;
&lt;li&gt;30초마다 자동으로 데이터를 긁어와 DB를 갱신하는 비동기 스케줄러 개발&lt;br /&gt;덕분에 게임을 하는 동안 알아서 전적이 쌓이는 시스템이 완성되었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 배포의 악몽: EXE 패키징과 Windows Defender  ️&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발의 90%가 끝났다고 생각했을 때, &lt;b&gt;진짜 보스&lt;/b&gt;가 등장했습니다. 바로 &lt;b&gt;PyInstaller 패키징&lt;/b&gt;과 &lt;b&gt;Windows Defender&lt;/b&gt;였습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 1: &quot;바이러스 아님?&quot;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PyInstaller로 &lt;code&gt;--onefile&lt;/code&gt; 빌드를 하자마자 Defender가 파일을 냅다 격리해버렸습니다.&lt;br /&gt;  &lt;b&gt;해결:&lt;/b&gt; &lt;code&gt;--onedir&lt;/code&gt; (폴더 방식)로 변경하고 &lt;code&gt;UPX&lt;/code&gt; 압축을 해제하여 오탐지를 피해갔습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 2: &quot;브라우저 어디 갔어?&quot;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EXE로 만들었더니 Playwright가 내장 브라우저를 찾지 못해 &lt;b&gt;&quot;Executable doesn't exist&quot;&lt;/b&gt; 에러를 뿜으며 실행되지 않았습니다. 내장 브라우저를 포함하면 용량이 수백 MB가 넘어가서 배포하기도 힘들었죠.&lt;br /&gt;  &lt;b&gt;Antigravity의 솔루션:&lt;/b&gt;&lt;br /&gt;&quot;굳이 무거운 브라우저를 넣지 말고, 사용자 PC에 이미 설치된 &lt;b&gt;Chrome&lt;/b&gt;이나 &lt;b&gt;Edge&lt;/b&gt;를 쓰도록 코드를 바꾸자!&quot;&lt;br /&gt;&lt;code&gt;scraper.py&lt;/code&gt;에 시스템 브라우저 감지 로직을 추가하여 용량은 줄이고 실행 안정성은 높였습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 디버깅 에피소드: &quot;들여쓰기 하나 때문에...&quot;  &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 배포 직전, &lt;code&gt;ModuleNotFoundError&lt;/code&gt;가 발생하며 프로그램이 켜지지도 않는 사태가 벌어졌습니다.&lt;br /&gt;알고 보니 제가 디버그 코드를 추가하다가 Python의 생명인 &lt;b&gt;들여쓰기(Indentation)&lt;/b&gt; 를 실수했던 것이었죠.&lt;br /&gt;Antigravity는 로그를 보자마자 &lt;b&gt;&quot;scraper.py의 while 루프 들여쓰기가 잘못되었습니다&quot;&lt;/b&gt;라며 정확한 원인을 찾아내고, 즉시 수정 코드를 제안해주었습니다. (머쓱...)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 결론: 개발자와 AI의 페어 프로그래밍&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혼자였다면 Windows Defender 이슈에서 포기하거나, 디자인을 대충 타협했을지도 모릅니다. 하지만 Antigravity가 옆에서 &lt;b&gt;&quot;이런 방법은 어때요?&quot;&lt;/b&gt;, &lt;b&gt;&quot;이 에러는 이렇게 고치면 됩니다&quot;&lt;/b&gt;라고 끊임없이 가이드를 주었기에 &lt;b&gt;SF6 Viewer v1.0&lt;/b&gt;을 무사히 배포할 수 있었습니다.&lt;br /&gt;이제 제 방송 화면 한구석엔 제가(그리고 AI가) 만든 멋진 전적판이 빛나고 있습니다. ✨&lt;br /&gt;&lt;b&gt;&lt;a href=&quot;https://github.com/blacknabis/SF6RankViewer&quot;&gt;GitHub 저장소 바로가기&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;Created with Python, FastAPI, Playwright, and Antigravity&lt;/i&gt;&lt;/p&gt;</description>
      <category>AI/Etc</category>
      <category>AI</category>
      <category>Antigravity</category>
      <category>Coding</category>
      <category>Devlog</category>
      <category>fastapi</category>
      <category>OBS</category>
      <category>Playwright</category>
      <category>Python</category>
      <category>SF6</category>
      <category>streetfighter6</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/64</guid>
      <comments>https://blacknabis.tistory.com/64#entry64comment</comments>
      <pubDate>Tue, 10 Feb 2026 23:49:59 +0900</pubDate>
    </item>
    <item>
      <title>스트리트파이터6 랭킹 수집 및 OBS 도우미</title>
      <link>https://blacknabis.tistory.com/63</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1. 깃허브에 접속&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/blacknabis/SF6RankViewer&quot;&gt;https://github.com/blacknabis/SF6RankViewer&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1770727589888&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - blacknabis/SF6RankViewer: 스트리트파이터6 랭크 수집 및 OBS 방송 지원&quot; data-og-description=&quot;스트리트파이터6 랭크 수집 및 OBS 방송 지원. Contribute to blacknabis/SF6RankViewer development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/blacknabis/SF6RankViewer&quot; data-og-url=&quot;https://github.com/blacknabis/SF6RankViewer&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Xccsa/dJMb9aKAZH4/06tDFiJsqjqFPWgDgOaKA0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bOi0TN/dJMb88F0L95/TDdy0HwcA59H77FbmFmzn0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/blacknabis/SF6RankViewer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/blacknabis/SF6RankViewer&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Xccsa/dJMb9aKAZH4/06tDFiJsqjqFPWgDgOaKA0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bOi0TN/dJMb88F0L95/TDdy0HwcA59H77FbmFmzn0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - blacknabis/SF6RankViewer: 스트리트파이터6 랭크 수집 및 OBS 방송 지원&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;스트리트파이터6 랭크 수집 및 OBS 방송 지원. Contribute to blacknabis/SF6RankViewer development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. SFViewerV1_0.Zip 다운로드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kEMnQ/dJMcaaYuXmo/6rzDdfZuhOdKyfRHvDVBX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kEMnQ/dJMcaaYuXmo/6rzDdfZuhOdKyfRHvDVBX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kEMnQ/dJMcaaYuXmo/6rzDdfZuhOdKyfRHvDVBX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkEMnQ%2FdJMcaaYuXmo%2F6rzDdfZuhOdKyfRHvDVBX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;910&quot; height=&quot;304&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;304&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 압축 해제 후 SF6Viewer.exe실행&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;185&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4L9tx/dJMcaiWvpJz/szWSsi1noscYStMm7QkzW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4L9tx/dJMcaiWvpJz/szWSsi1noscYStMm7QkzW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4L9tx/dJMcaiWvpJz/szWSsi1noscYStMm7QkzW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4L9tx%2FdJMcaiWvpJz%2FszWSsi1noscYStMm7QkzW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;676&quot; height=&quot;185&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;185&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 터미널과 대시보드가 열림&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1091&quot; data-origin-height=&quot;535&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FZ244/dJMcafZNwRo/BUhPFmK5kUR03otqTskqbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FZ244/dJMcafZNwRo/BUhPFmK5kUR03otqTskqbk/img.png&quot; data-alt=&quot;해당터미널을 끄면 서버가 꺼지므로 대시보드의 연동도 종료됩니다!!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FZ244/dJMcafZNwRo/BUhPFmK5kUR03otqTskqbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFZ244%2FdJMcafZNwRo%2FBUhPFmK5kUR03otqTskqbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1091&quot; height=&quot;535&quot; data-origin-width=&quot;1091&quot; data-origin-height=&quot;535&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;해당터미널을 끄면 서버가 꺼지므로 대시보드의 연동도 종료됩니다!!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;491&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nOtUY/dJMcaaYuXqM/3w5svZuoKdr5yZCQlDWSBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nOtUY/dJMcaaYuXqM/3w5svZuoKdr5yZCQlDWSBK/img.png&quot; data-alt=&quot;제일 중요한 대시보드. 서버가 돌고 있으면 대시보드는 언제든 다시 띄울수 있습니다.(http://localhost:8000/)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nOtUY/dJMcaaYuXqM/3w5svZuoKdr5yZCQlDWSBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnOtUY%2FdJMcaaYuXqM%2F3w5svZuoKdr5yZCQlDWSBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;761&quot; height=&quot;491&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;491&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;제일 중요한 대시보드. 서버가 돌고 있으면 대시보드는 언제든 다시 띄울수 있습니다.(http://localhost:8000/)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 내 정보 연동을 위해 &quot;Open Login Browser&quot; 딸칵&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;455&quot; data-origin-height=&quot;157&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTfYgl/dJMcah4o2tz/H8EvpUBGxFmoFUN5y8q1x1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTfYgl/dJMcah4o2tz/H8EvpUBGxFmoFUN5y8q1x1/img.png&quot; data-alt=&quot;확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTfYgl/dJMcah4o2tz/H8EvpUBGxFmoFUN5y8q1x1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTfYgl%2FdJMcah4o2tz%2FH8EvpUBGxFmoFUN5y8q1x1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;455&quot; height=&quot;157&quot; data-origin-width=&quot;455&quot; data-origin-height=&quot;157&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 새 브라우저에 스트리트 파이터가 계속 깜박거릴겁니다(크롬) 로그인해주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 로그인 후 확인 창 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvISNT/dJMcagRX9sp/CMfm8UHVgqYk4hKoxH7nA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvISNT/dJMcagRX9sp/CMfm8UHVgqYk4hKoxH7nA1/img.png&quot; data-alt=&quot;확인 딸칵&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvISNT/dJMcagRX9sp/CMfm8UHVgqYk4hKoxH7nA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdvISNT%2FdJMcagRX9sp%2FCMfm8UHVgqYk4hKoxH7nA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;505&quot; height=&quot;150&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;확인 딸칵&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8.Refesh Data를 눌러 데이터가 연동 잘된지 확인 한 뒤 새로 떴던 크롬 창을 닫아줍니다(계속 깜빡거려서 귀찮아요)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dri7wy/dJMcabJUcW8/kchab8IFDNSZnd9MFgL7j0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dri7wy/dJMcabJUcW8/kchab8IFDNSZnd9MFgL7j0/img.png&quot; data-alt=&quot;상단에 닉네임확인!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dri7wy/dJMcabJUcW8/kchab8IFDNSZnd9MFgL7j0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdri7wy%2FdJMcabJUcW8%2Fkchab8IFDNSZnd9MFgL7j0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;708&quot; height=&quot;702&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;702&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;상단에 닉네임확인!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9. Start Collect 딸칵. 이제부터 수집이 시작됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10. open staticstics 딸칵&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GO7AZ/dJMcaac87pN/hZ9CdmURvHvoDK42zol4h1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GO7AZ/dJMcaac87pN/hZ9CdmURvHvoDK42zol4h1/img.png&quot; data-alt=&quot;웹창이 뜨면서 이런식으로 전적이 수집됩니다. (http://localhost:8000/stats)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GO7AZ/dJMcaac87pN/hZ9CdmURvHvoDK42zol4h1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGO7AZ%2FdJMcaac87pN%2FhZ9CdmURvHvoDK42zol4h1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1183&quot; height=&quot;268&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;웹창이 뜨면서 이런식으로 전적이 수집됩니다. (http://localhost:8000/stats)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;587&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIYNjO/dJMcagdna0u/BFLH8ulinWzo6JQPdj6yo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIYNjO/dJMcagdna0u/BFLH8ulinWzo6JQPdj6yo1/img.png&quot; data-alt=&quot;OBS 에 브라우저 추가.(크기는 개취)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIYNjO/dJMcagdna0u/BFLH8ulinWzo6JQPdj6yo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIYNjO%2FdJMcagdna0u%2FBFLH8ulinWzo6JQPdj6yo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;690&quot; height=&quot;587&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;587&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OBS 에 브라우저 추가.(크기는 개취)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1388&quot; data-origin-height=&quot;906&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l4gJK/dJMcabb10fX/lkh3Ms99vWIIE7k1JsAjZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l4gJK/dJMcabb10fX/lkh3Ms99vWIIE7k1JsAjZk/img.png&quot; data-alt=&quot;마무리! 이제 콜렉트 켜놓은 상태에 방송하시면됩니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l4gJK/dJMcabb10fX/lkh3Ms99vWIIE7k1JsAjZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl4gJK%2FdJMcabb10fX%2Flkh3Ms99vWIIE7k1JsAjZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1388&quot; height=&quot;906&quot; data-origin-width=&quot;1388&quot; data-origin-height=&quot;906&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;마무리! 이제 콜렉트 켜놓은 상태에 방송하시면됩니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/Etc</category>
      <category>Antigravity</category>
      <category>obs 방송</category>
      <category>SF6</category>
      <category>랭크수집</category>
      <category>스트리트파이터6</category>
      <category>스파방송</category>
      <category>취미</category>
      <author>blacknabis</author>
      <guid isPermaLink="true">https://blacknabis.tistory.com/63</guid>
      <comments>https://blacknabis.tistory.com/63#entry63comment</comments>
      <pubDate>Tue, 10 Feb 2026 22:55:24 +0900</pubDate>
    </item>
  </channel>
</rss>