AI 보조 게임 개발

Claude Code로 Unity 보일러플레이트 5종 자동 생성하기 (2026 모던 C# 12)

kr-gamedev 2026. 5. 22. 21:29

Unity 개발자가 매번 반복해서 작성하는 Singleton·Object Pool·Save System·Event Bus·Input Handler 5종 보일러플레이트를 Claude Code로 15분 안에 완성하는 실전 가이드. 80% 시간 절약 검증.


왜 보일러플레이트 자동화인가

게임 개발자라면 누구나 새 프로젝트 시작할 때 매번 같은 코드를 작성한다. Singleton 패턴, Object Pool, Save/Load 시스템, Event Bus, Input Handler — 이 5종은 사실상 모든 Unity 프로젝트의 기본 인프라다. 그런데 매번 처음부터 작성하면 검색·복사·붙여넣기·테스트·디버깅 사이클로 2~3시간이 사라진다.

2026년 한국 게임 개발자 사이에서 빠르게 확산되는 도구가 있다. Anthropic의 Claude Code다. CLI 환경에서 동작하는 AI 페어 프로그래밍 도구로, Cursor·GitHub Copilot과 다르게 터미널에서 직접 코드를 생성·수정·실행한다. 이 글에서는 Claude Code로 5종 보일러플레이트를 자동 생성하고, 실제 시간 절약 효과를 검증한다.


1. Claude Code 셋업 (5분)

설치는 npm 한 줄이다.

npm install -g @anthropic-ai/claude-code

Unity 프로젝트 루트 디렉터리로 이동한 후 claude 명령으로 실행한다. 초기 컨텍스트는 CLAUDE.md 파일에 박는 것이 핵심이다. 프로젝트 메타데이터를 한 번만 박으면 모든 prompt가 자동으로 이 컨텍스트를 참조한다.

# Project Context

- Unity 6 (URP)
- C# 12
- Target Platform: Mobile (iOS/Android)
- Project Type: 3D 액션
- Conventions:
  - SerializeField 우선 (public 필드 X)
  - using 정렬: System → UnityEngine → 프로젝트
  - 클래스마다 한 파일 (partial 제외)
  - nullable 활성화

2. 효과적인 prompt 3가지 패턴

코드 생성 품질은 prompt 품질이 결정한다. 다음 3가지 패턴을 익히면 80% 케이스에서 한 번에 사용 가능한 코드가 나온다.

패턴 A · 타입을 명시한다

❌ "주문 처리 함수 만들어줘"
✅ "Order entity 받아서 ProcessingResult 반환하는 메서드. 의존성: OrderRepository, InventoryService"

패턴 B · 책임과 이벤트를 명시한다

✅ "MonoBehaviour 상속 PlayerHealth.
- SerializeField max HP, current HP
- 메서드: TakeDamage(int), Heal(int), Die()
- UnityEvent: OnHealthChanged(int), OnDeath()
- 초기 HP = max HP"

패턴 C · C# 버전과 사용할 API를 명시한다

✅ "C# 12 primary constructor 사용. nullable 활성화. UnityEngine.Pool API 활용."

3. 보일러플레이트 5종 실전

3-A · Singleton 패턴

prompt:

Unity MonoBehaviour 상속 generic Singleton 만들어줘. DontDestroyOnLoad. Lazy 초기화. Inspector 노출 X. C# 12.

Claude Code 응답 (30초 안):

using UnityEngine;

public abstract class Singleton<T> : MonoBehaviour where T : Component
{
    private static T _instance;

    public static T Instance
    {
        get
        {
            if (_instance != null) return _instance;

            _instance = FindFirstObjectByType<T>();
            if (_instance != null) return _instance;

            var obj = new GameObject(typeof(T).Name);
            _instance = obj.AddComponent<T>();
            DontDestroyOnLoad(obj);
            return _instance;
        }
    }

    protected virtual void Awake()
    {
        if (_instance == null)
        {
            _instance = this as T;
            DontDestroyOnLoad(gameObject);
        }
        else if (_instance != this)
        {
            Destroy(gameObject);
        }
    }
}

사용:

public class GameManager : Singleton<GameManager>
{
    public int Score { get; private set; }
}
// GameManager.Instance.Score 로 접근

3-B · Object Pool (UnityEngine.Pool 활용)

prompt:

UnityEngine.Pool 의 ObjectPool 래핑한 PooledSpawner 만들어줘. defaultCapacity·maxSize 설정. Reset 콜백.

응답 (요약):

using UnityEngine;
using UnityEngine.Pool;

public class PooledSpawner<T> where T : MonoBehaviour
{
    private readonly ObjectPool<T> _pool;
    private readonly T _prefab;

    public PooledSpawner(T prefab, int defaultCapacity = 10, int maxSize = 100)
    {
        _prefab = prefab;
        _pool = new ObjectPool<T>(
            createFunc: () => Object.Instantiate(_prefab),
            actionOnGet: obj => obj.gameObject.SetActive(true),
            actionOnRelease: obj => obj.gameObject.SetActive(false),
            actionOnDestroy: obj => Object.Destroy(obj.gameObject),
            collectionCheck: true,
            defaultCapacity: defaultCapacity,
            maxSize: maxSize);
    }

    public T Get() => _pool.Get();
    public void Release(T obj) => _pool.Release(obj);
    public void Clear() => _pool.Clear();
}

3-C · Save/Load System (JSON)

prompt:

SaveSystem 만들어줘. JSON 직렬화. Application.persistentDataPath 사용. async 지원. 간단 암호화 (base64).

응답:

using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

public static class SaveSystem
{
    private static string GetPath(string key) =>
        Path.Combine(Application.persistentDataPath, $"{key}.save");

    public static async Task SaveAsync<T>(string key, T data)
    {
        var json = JsonUtility.ToJson(data);
        var encoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(json));
        await File.WriteAllTextAsync(GetPath(key), encoded);
    }

    public static async Task<T> LoadAsync<T>(string key) where T : new()
    {
        var path = GetPath(key);
        if (!File.Exists(path)) return new T();

        var encoded = await File.ReadAllTextAsync(path);
        var json = Encoding.UTF8.GetString(Convert.FromBase64String(encoded));
        return JsonUtility.FromJson<T>(json);
    }
}

사용:

[Serializable]
public class PlayerData { public int Level; public int Gold; }

await SaveSystem.SaveAsync("player", new PlayerData { Level = 5, Gold = 1000 });
var data = await SaveSystem.LoadAsync<PlayerData>("player");

3-D · Type-safe Event Bus

prompt:

Type-safe EventBus 만들어줘. Publish(T) / Subscribe(Action) / Unsubscribe. Dictionary 기반. Reflection X.

응답 (요약):

using System;
using System.Collections.Generic;

public static class EventBus
{
    private static readonly Dictionary<Type, Delegate> _handlers = new();

    public static void Subscribe<T>(Action<T> handler)
    {
        var type = typeof(T);
        _handlers[type] = _handlers.TryGetValue(type, out var existing)
            ? Delegate.Combine(existing, handler)
            : handler;
    }

    public static void Unsubscribe<T>(Action<T> handler)
    {
        var type = typeof(T);
        if (_handlers.TryGetValue(type, out var existing))
        {
            var updated = Delegate.Remove(existing, handler);
            if (updated == null) _handlers.Remove(type);
            else _handlers[type] = updated;
        }
    }

    public static void Publish<T>(T evt)
    {
        if (_handlers.TryGetValue(typeof(T), out var handler))
            ((Action<T>)handler)?.Invoke(evt);
    }
}

3-E · Input Handler (Unity Input System)

prompt:

Unity Input System PlayerInput wrapper 만들어줘. Move·Jump·Attack 액션. OnEnable·OnDisable 자동 등록. UnityEvent 발사.

응답: (생략 — 위 패턴과 동일)


4. 시간 절약 측정

작업 manual Claude Code 절약
Singleton 15분 1분 93%
Object Pool 30분 2분 93%
Save System 40분 3분 92%
Event Bus 25분 2분 92%
Input Handler 30분 3분 90%
합계 140분 11분 약 92%

검증 환경: Unity 6 URP 빈 프로젝트, 단순 검토·테스트 포함 (디버깅 X).


5. 효과적으로 쓰는 룰 5개

  1. CLAUDE.md에 컨벤션 박기 — 한 번 박으면 모든 prompt가 자동 적용
  2. 한 번에 한 클래스 — 멀티 클래스는 컨텍스트 오염, 품질 저하
  3. C# 버전·Unity 버전 명시 — 모던 문법 활용 (primary constructor·collection expression 등)
  4. 의존성 의존 inject — Singleton 패턴 외엔 의존성 명시 후 생성자 주입
  5. 응답 review 후 commit — 자동 신뢰 금지. 5종 모두 한 번 읽고 테스트

마무리

보일러플레이트 자동화는 게임 개발자가 Claude Code를 도입했을 때 가장 빠르게 효과를 체감하는 영역이다. 첫 번째 글에서 5종을 다뤘는데, 다음 글에서는 더 깊은 영역 — Unity Editor 자동화, Scriptable Object 생성, Asset 일괄 처리 — 를 다룬다.

이 5종 중 한 줄이라도 자신의 프로젝트에 박았다면 댓글로 어떤 게 가장 도움됐는지 알려달라. 다음 글에서 어떤 보일러플레이트를 우선으로 자동화할지 결정하는 데 참고하겠다.