Claude API 기반 Unity C# 에디터 익스텐션: 인스펙터에서 에셋 자동 세팅하기

Unity 커스텀 인스펙터에서 Claude API가 추천한 에셋 설정을 안전하게 적용하는 방법을 다룹니다. 서버 프록시, JSON 스키마, Undo, 검증, AssetDatabase 자동 연결까지 실전 코드로 정리합니다.

✓ 복사 완료!
챕터에디터 확장 & 파이프라인 도구
난이도중급
상태● 검토 완료
대상 엔진
Unity
최종 업데이트
Claude API 기반 Unity C# 에디터 익스텐션: 인스펙터에서 에셋 자동 세팅하기

핵심 요약

Unity 인스펙터에서 Claude API를 호출해 에셋을 자동 세팅하려면 API 키는 Unity 클라이언트에 넣지 않고 서버 프록시에 보관해야 한다. Claude에는 자연어 요구사항을 전달하되 실제 변경은 AssetDatabase.FindAssets, SerializedObject, Undo.RecordObject를 사용하는 C# 에디터 코드가 검증된 JSON 응답만 적용하도록 설계한다.

이 글의 구현은 Unity 2022.3 LTS 기준이며 예시는 ScriptableObject 설정 에셋의 아이콘과 프리팹을 자동 연결하는 흐름이다. 핵심은 AI가 에셋을 직접 수정하게 두는 것이 아니라 에셋 후보 선택안을 반환하게 하고 Unity가 허용 목록 안에서 적용하는 것이다.

인스펙터 내 에셋 자동 세팅은 왜 필요한가?

게임 클라이언트 프로젝트에는 반복적인 설정 작업이 많다. 예를 들어 아이템 정의 에셋을 만들 때마다 아이콘, 월드 프리팹, 카테고리, 표시 이름을 연결해야 한다. 파일 이름과 폴더 규칙이 정해져 있어도 사람이 직접 드래그 앤 드롭하면 누락과 오연결이 생긴다.

Claude API 기반 에디터 익스텐션은 다음 작업을 보조하는 데 적합하다.

작업입력 예시자동화 결과
아이콘 연결HealthPotionAssets/Art/Icons/health_potion.png 후보 선택
프리팹 연결IceSwordAssets/Prefabs/Weapons/IceSword.prefab 후보 선택
문자열 정규화hp potionHP Potion 제안
필수 필드 점검아이템 정의 에셋비어 있는 참조와 누락 경고 반환

단, AI가 프로젝트 전체 파일을 임의로 탐색하거나 수정하도록 만들면 재현성과 보안이 급격히 나빠진다. 에디터 확장 기능은 Claude에게 필요한 메타데이터만 제공하고 Unity 측에서 결과를 검증한 뒤 적용해야 한다.

Unity 커스텀 인스펙터에서 아이콘과 프리팹 자동 연결 후보를 검토하는 화면

Claude API와 Unity 에디터는 어떻게 분리해야 할까?

API 키를 Unity 프로젝트에 넣으면 안 되는 이유

Unity Editor 코드에 Anthropic API 키를 하드코딩하거나 EditorPrefs에 저장하면 빌드 산출물, 저장소 기록, 로그, 팀원의 로컬 환경을 통해 키가 노출될 수 있다. 에디터 전용 어셈블리라도 보안 경계가 아니다.

권장 구조는 다음과 같다.

flowchart LR
    A[Unity Custom Inspector] -->|에셋 메타데이터와 요구사항| B[사내 API 프록시]
    B -->|Messages API 요청| C[Claude API]
    C -->|구조화된 JSON 제안| B
    B -->|검증 가능한 JSON| A
    A -->|Undo와 SerializedObject| D[Unity AssetDatabase]

프록시는 API 키를 환경 변수 또는 시크릿 저장소에 보관한다. Unity는 팀 내부 API의 짧은 수명 토큰 또는 개발용 인증 정보를 사용하고 프록시는 요청 가능한 프로젝트·경로·모델을 제한한다.

AI 응답은 설명문이 아니라 명시적 스키마로 받는다

자연어 응답을 파싱해 파일 경로를 추출하면 모델 출력 변화에 쉽게 깨진다. 응답 형식을 아래처럼 고정한다.

{
  "displayName": "Health Potion",
  "iconGuid": "a1b2c3d4e5f6",
  "prefabGuid": "f6e5d4c3b2a1",
  "reason": "이름과 에셋 태그가 모두 일치합니다.",
  "confidence": 0.92
}

GUID를 사용하면 경로 이동이나 파일명 변경에 상대적으로 강하다. 단, Claude가 실제 GUID를 추측하게 해서는 안 된다. Unity가 검색한 후보 목록의 GUID와 이름, 타입, 경로만 프롬프트에 포함하고 그중 하나를 고르게 해야 한다.

Step 1. 자동 세팅 대상 ScriptableObject를 만든다

먼저 런타임에서 사용할 데이터와 에디터 자동화 대상 필드를 정의한다.

using UnityEngine;

[CreateAssetMenu(menuName = "Game/Item Definition")]
public sealed class ItemDefinition : ScriptableObject
{
    [SerializeField] private string itemId;
    [SerializeField] private string displayName;
    [SerializeField] private Sprite icon;
    [SerializeField] private GameObject worldPrefab;

    public string ItemId => itemId;
    public string DisplayName => displayName;
    public Sprite Icon => icon;
    public GameObject WorldPrefab => worldPrefab;
}

에디터 자동 설정 로직은 Editor 폴더 또는 Editor 전용 Assembly Definition에 둔다. 그러면 플레이어 빌드에 UnityEditor 코드와 API 호출 코드가 포함되지 않는다.

Step 2. AssetDatabase로 제한된 후보 목록을 만든다

에셋 검색 결과 전체를 모델에 보내지 말고 규칙 기반으로 먼저 후보를 줄인다. 예를 들어 아이콘은 Assets/Art/Icons, 프리팹은 Assets/Prefabs 아래에서만 찾는다.

#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;

public readonly struct AssetCandidate
{
    public readonly string Guid;
    public readonly string Path;
    public readonly string Name;
    public readonly string Type;

    public AssetCandidate(string guid, string path, string type)
    {
        Guid = guid;
        Path = path;
        Name = System.IO.Path.GetFileNameWithoutExtension(path);
        Type = type;
    }
}

public static class AssetCandidateFinder
{
    public static List<AssetCandidate> Find(string filter, string folder)
    {
        return AssetDatabase.FindAssets(filter, new[] { folder })
            .Select(guid => new AssetCandidate(
                guid,
                AssetDatabase.GUIDToAssetPath(guid),
                filter))
            .Take(30)
            .ToList();
    }
}
#endif

후보 상한은 프롬프트 크기와 응답 품질을 함께 고려해 정한다. 일반적으로 이름 기반 후보 10~30개면 충분하다. 후보가 너무 많으면 Claude가 의미적으로 비슷한 에셋을 고르는 문제보다 애초에 에셋 네이밍 규칙과 폴더 구조를 개선해야 하는 문제일 가능성이 크다.

Step 3. Claude용 요청과 응답 DTO를 정의한다

Unity와 프록시 사이의 계약을 C# DTO로 고정한다. JsonUtility는 제한 사항이 있으므로 배열, 사전, 복잡한 다형성 구조가 많다면 프로젝트에서 사용하는 JSON 라이브러리에 맞춰 구현한다. 아래 예시는 단순한 응답을 위한 JsonUtility 형태다.

using System;
using UnityEngine;

[Serializable]
public sealed class AutoSetupRequest
{
    public string itemId;
    public string currentDisplayName;
    public AssetCandidateDto[] iconCandidates;
    public AssetCandidateDto[] prefabCandidates;
}

[Serializable]
public sealed class AssetCandidateDto
{
    public string guid;
    public string path;
    public string name;
}

[Serializable]
public sealed class AutoSetupResponse
{
    public string displayName;
    public string iconGuid;
    public string prefabGuid;
    public string reason;
    public float confidence;
}

프록시에서 Claude에게 전달할 지시는 다음 원칙을 포함해야 한다.

  1. 제공된 후보의 GUID 외 다른 GUID나 경로를 만들지 않는다.
  2. 확신이 낮거나 일치 후보가 없으면 빈 문자열을 반환한다.
  3. 응답은 정의된 JSON 스키마만 반환한다.
  4. 파일명, 경로, 태그처럼 제공된 정보만 근거로 선택한다.

커스텀 인스펙터에서 안전하게 변경 사항을 적용하는 방법

비동기 네트워크 요청이 끝난 뒤 대상 에셋이 삭제되었거나 인스펙터 선택이 바뀔 수 있다. 따라서 응답 수신 시점에 다시 null을 확인하고 GUID가 최초 후보 목록에 있었는지 검증해야 한다.

#if UNITY_EDITOR
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(ItemDefinition))]
public sealed class ItemDefinitionEditor : Editor
{
    private AutoSetupResponse _result;
    private string _error;

    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();
        EditorGUILayout.Space();

        using (new EditorGUI.DisabledScope(Application.isPlaying))
        {
            if (GUILayout.Button("Claude로 에셋 추천 받기"))
            {
                RequestSuggestion((ItemDefinition)target);
            }
        }

        if (!string.IsNullOrEmpty(_error))
        {
            EditorGUILayout.HelpBox(_error, MessageType.Error);
        }

        if (_result != null)
        {
            EditorGUILayout.HelpBox(_result.reason, MessageType.Info);
            if (GUILayout.Button("추천 결과 적용"))
            {
                ApplySuggestion((ItemDefinition)target, _result);
            }
        }
    }

    private void RequestSuggestion(ItemDefinition item)
    {
        _error = null;
        _result = null;

        // 프로젝트의 프록시 클라이언트가 요청을 전송한 뒤
        // 완료 콜백에서 _result 또는 _error를 설정한다.
        ClaudeAutoSetupClient.Request(item, OnRequestCompleted);
    }

    private void OnRequestCompleted(AutoSetupResponse result, string error)
    {
        _result = result;
        _error = error;
        Repaint();
    }

    private static void ApplySuggestion(ItemDefinition item, AutoSetupResponse result)
    {
        Undo.RecordObject(item, "Apply Claude Asset Setup");

        var serializedObject = new SerializedObject(item);
        serializedObject.FindProperty("displayName").stringValue = result.displayName;

        var icon = LoadAllowedAsset<Sprite>(result.iconGuid, "Assets/Art/Icons");
        var prefab = LoadAllowedAsset<GameObject>(result.prefabGuid, "Assets/Prefabs");

        serializedObject.FindProperty("icon").objectReferenceValue = icon;
        serializedObject.FindProperty("worldPrefab").objectReferenceValue = prefab;
        serializedObject.ApplyModifiedProperties();

        EditorUtility.SetDirty(item);
        AssetDatabase.SaveAssets();
    }

    private static T LoadAllowedAsset<T>(string guid, string requiredFolder) where T : Object
    {
        if (string.IsNullOrWhiteSpace(guid)) return null;

        var path = AssetDatabase.GUIDToAssetPath(guid);
        string folderPrefix = requiredFolder.TrimEnd('/') + "/";
        if (string.IsNullOrEmpty(path) ||
            !path.StartsWith(folderPrefix, StringComparison.Ordinal)) return null;

        return AssetDatabase.LoadAssetAtPath<T>(path);
    }
}
#endif

Undo.RecordObject가 없으면 작업자가 추천 결과를 되돌릴 수 없다. 또한 SerializedObject와 SerializedProperty를 쓰면 Unity 직렬화 흐름을 따르며 프리팹 오버라이드와 인스펙터 갱신 문제를 줄일 수 있다.

응답 검증에서 반드시 확인할 항목

AI 출력은 신뢰 경계 밖의 입력으로 취급한다. confidence가 높아도 검증을 생략하면 안 된다.

검증 항목확인 방법실패 시 처리
JSON 형식역직렬화 예외 및 필수 필드 검사적용 버튼을 숨기고 오류 표시
GUID 존재 여부AssetDatabase.GUIDToAssetPath해당 필드는 변경하지 않음
허용 폴더path.StartsWith() 또는 더 엄격한 경로 검사거부 및 로그 기록
예상 타입LoadAssetAtPath<Sprite> 등null 처리와 경고 표시
후보 목록 포함 여부요청 시 전송한 GUID HashSet 비교응답 전체 거부
신뢰도 임계값예: confidence >= 0.70f사용자 수동 확인만 허용

특히 경로 검증은 단순 문자열 비교만으로 끝내지 않는 편이 좋다. 운영 도구에서는 요청 직전에 생성한 후보 GUID HashSet<string>을 보관하고 응답 GUID가 그 집합에 포함될 때만 적용하는 방식이 가장 명확하다.

성능과 팀 작업 흐름은 어떻게 관리할까?

인스펙터의 OnInspectorGUI()는 자주 호출된다. 네트워크 요청이나 AssetDatabase.FindAssets 전체 검색을 매 프레임 실행하면 에디터가 느려진다. 버튼 클릭 시에만 검색·요청하고 결과는 에셋 변경 또는 도메인 리로드 전까지 캐시한다.

또한 다음 정책을 두면 팀 작업에서 문제가 줄어든다.

  • 자동 적용보다 추천 후 적용을 기본값으로 둔다.
  • 적용 전후의 GUID, 사용자, 시간, 모델 응답을 로컬 로그 또는 팀 도구에 남긴다.
  • 대량 설정은 인스펙터 버튼이 아니라 별도 EditorWindow에서 미리보기와 일괄 적용을 제공한다.
  • .meta 파일을 반드시 버전 관리해 GUID 안정성을 유지한다.
  • 프록시에서 요청 크기, 호출 빈도, 모델 이름을 제한해 비용을 통제한다.

자주 묻는 질문 (FAQ)

Claude API를 Unity Editor에서 직접 호출해도 되나요?

기술적으로는 가능하지만 API 키 노출 위험 때문에 권장하지 않는다. Unity는 인증된 사내 프록시만 호출하고 프록시가 Claude API를 호출하는 구조가 안전하다.

AI가 없는 에셋을 지어내면 어떻게 되나요?

응답 GUID가 요청 후보 목록에 포함되는지 검사하면 된다. 목록에 없는 GUID, 허용 폴더 밖의 경로, 타입이 맞지 않는 에셋은 적용하지 않는다.

한 번에 수백 개 에셋을 설정해도 되나요?

가능하지만 인스펙터 단건 처리보다 전용 배치 도구가 적합하다. 먼저 드라이런 결과를 표로 보여 주고 선택된 항목만 Undo 그룹 안에서 적용해야 검토와 롤백이 쉽다.

정리

Claude API 기반 Unity 에디터 익스텐션의 가치는 AI가 에셋을 마음대로 수정하는 데 있지 않다. 파일명과 메타데이터를 바탕으로 한 후보 선택을 AI에 맡기고 실제 프로젝트 변경은 Unity의 타입 검증, 허용 경로, Undo, SerializedObject를 통과한 결과만 적용하는 데 있다.

이 경계를 지키면 아이템 정의, UI 프리팹 바인딩, 캐릭터 설정, 로컬라이제이션 키 점검처럼 반복적이지만 실수 비용이 큰 클라이언트 제작 업무를 안전하게 줄일 수 있다.