Unity 커스텀 인스펙터 GUI 레이아웃 코드 자동 생성: 로컬 AI 익스텐션 만들기
Unity EditorWindow에서 반복되는 CustomEditor와 EditorGUILayout 코드를 로컬 AI로 자동 생성하는 방법을 소개합니다. JSON 레이아웃 스키마, 로컬 HTTP 연동, C# 검증과 안전한 파일 생성까지 정리했습니다.

핵심 요약
Unity 커스텀 인스펙터 GUI 레이아웃 코드 자동 생성은 로컬 AI가 C# 소스 전체를 즉흥적으로 작성하게 하는 방식보다 InspectorLayout JSON을 먼저 생성한 뒤 검증된 C# 템플릿으로 변환하는 방식이 안정적입니다. Unity EditorWindow가 로컬 HTTP 엔드포인트에 필드 메타데이터와 요구사항을 보내고 AI가 반환한 레이아웃을 SerializedObject 기반 코드로 렌더링하면 반복적인 CustomEditor 작업을 줄일 수 있습니다.
- 입력: 대상 컴포넌트의 직렬화 필드, 라벨, 그룹, 표시 조건
- AI 출력: 코드가 아니라
InspectorLayoutJSON - 최종 결과:
SerializedProperty,EditorGUILayout.PropertyField,ApplyModifiedProperties를 사용하는 생성 C# 파일
왜 Unity 커스텀 인스펙터 GUI 코드 자동 생성이 필요한가?
Unity 기본 인스펙터는 빠르게 동작하지만 필드 그룹, 도움말, 조건부 표시, 편집 순서 같은 팀 전용 UI 규칙을 표현하기에는 한계가 있습니다. 그래서 다음과 같은 CustomEditor 코드를 반복해서 작성하게 됩니다.
SerializedObject.Update()와ApplyModifiedProperties()호출serializedObject.FindProperty()로 필드 찾기EditorGUILayout기반 컨트롤 배치BeginVertical()또는BeginFoldoutHeaderGroup()을 이용한 그룹 구성- 누락된 프로퍼티 경로와 컴파일 오류 확인
로컬 AI 익스텐션은 이 반복 작업을 줄이는 도구입니다. 소스 코드나 프로젝트 에셋을 외부 API로 보내지 않고 개발 PC의 로컬 모델에 전달할 수 있다는 점도 게임 프로젝트에서 중요합니다. 다만 로컬 모델을 사용한다고 해서 자동으로 안전해지는 것은 아닙니다. 엔드포인트를 기본적으로 127.0.0.1에 고정하고 전송하는 메타데이터를 최소화해야 합니다.
| 방식 | 장점 | 주의점 |
|---|---|---|
수동 CustomEditor 작성 | 결과를 세밀하게 제어할 수 있음 | 반복 코드와 프로퍼티 경로 실수가 많음 |
| AI가 C# 전체 생성 | 초기 결과가 빠름 | 잘못된 API와 컴파일 실패 가능 |
| JSON 레이아웃 + C# 템플릿 | 출력 검증과 재현성이 높음 | 스키마와 렌더러를 먼저 설계해야 함 |
로컬 AI 익스텐션은 어떤 구조로 동작할까?
핵심은 AI를 최종 코드 컴파일러가 아니라 레이아웃 의사결정기로 사용하는 것입니다. Unity 쪽은 필드 목록과 허용된 컨트롤 종류를 전달하고 AI는 정해진 JSON 스키마만 반환합니다. 이후 익스텐션이 JSON을 역직렬화하고 허용 목록을 검증한 다음 C# 템플릿으로 파일을 생성합니다.
flowchart TD
A[Unity EditorWindow] --> B[필드 메타데이터와 요구사항]
B --> C[로컬 AI HTTP 엔드포인트]
C --> D[InspectorLayout JSON]
D --> E[스키마와 프로퍼티 경로 검증]
E --> F[C# CustomEditor 템플릿 렌더링]
F --> G[Assets Editor Generated에 저장]
G --> H[Unity 컴파일과 인스펙터 확인]
이 분리를 적용하면 모델을 바꾸거나 프롬프트를 수정해도 최종 코드의 기본 규칙은 유지됩니다. 생성 파일은 사람이 직접 수정하는 파일과 분리하고 다음 요청에서 다시 생성할 수 있게 만드는 편이 좋습니다.

어떤 JSON 스키마로 인스펙터 레이아웃을 표현할까?
최소 스키마는 행의 순서와 컨트롤 종류, SerializedProperty에 대응하는 경로를 표현하면 됩니다. 모델이 실제 클래스의 필드명을 임의로 만들지 않도록 propertyPath는 입력으로 제공한 목록 안에서만 선택하게 합니다.
{
"targetType": "CharacterStats",
"rows": [
{ "kind": "header", "label": "Combat" },
{ "kind": "property", "propertyPath": "health", "label": "Health", "includeChildren": false },
{ "kind": "property", "propertyPath": "damage", "label": "Damage", "includeChildren": false },
{ "kind": "helpBox", "label": "Health는 0보다 작을 수 없습니다.", "messageType": "Info" }
]
}
kind는 header, property, space, helpBox처럼 익스텐션이 지원하는 값만 허용합니다. propertyPath가 없는 행은 필드 컨트롤로 렌더링하지 않으며 targetType도 프로젝트에서 선택한 스크립트의 타입명으로 덮어쓰는 것이 안전합니다.
C# DTO는 다음처럼 단순하게 유지할 수 있습니다.
[Serializable]
public sealed class InspectorLayout
{
public string targetType;
public List<InspectorRow> rows = new();
}
[Serializable]
public sealed class InspectorRow
{
public string kind;
public string label;
public string propertyPath;
public bool includeChildren;
public string messageType;
}
AI 프롬프트에는 무엇을 명시해야 할까?
프롬프트는 자연어 설명만 보내지 말고 입력 필드와 출력 제약을 함께 포함해야 합니다.
역할: Unity CustomEditor 레이아웃 설계기
대상 타입: CharacterStats
직렬화 가능 프로퍼티: health, damage, criticalChance
허용 kind: header, property, space, helpBox
규칙:
1. propertyPath는 직렬화 가능 프로퍼티 목록에서만 선택한다.
2. 설명이나 Markdown 없이 JSON 객체 하나만 반환한다.
3. 알 수 없는 필드는 생성하지 않는다.
4. 모든 property 행에는 label과 propertyPath를 포함한다.
모델이 JSON 앞뒤에 설명을 붙이는 경우를 대비해 응답을 파싱하기 전에 코드 펜스 제거, 최대 길이 제한, 필수 키 확인을 수행합니다. 더 엄격하게 운영하려면 로컬 브리지에서 JSON Schema 검증을 거친 뒤 Unity로 전달합니다.
Unity EditorWindow에서 로컬 모델을 호출하는 방법은?
로컬 모델 제공자는 서로 다른 요청과 응답 형식을 사용할 수 있으므로 익스텐션 내부에 특정 모델의 세부 형식을 흩어 놓지 않는 것이 좋습니다. 아래 예시는 익스텐션이 다음과 같은 어댑터 계약을 사용한다고 가정합니다.
POST http://127.0.0.1:8787/generate-layout- 요청:
{ "prompt": "..." } - 응답:
{ "layoutJson": "{...}" }
어댑터가 Ollama, llama.cpp 서버 또는 다른 로컬 런타임의 형식을 Unity용 계약으로 정규화합니다.
using System.Text;
using UnityEditor;
using UnityEngine;
using UnityEngine.Networking;
private void GenerateLayout(string prompt)
{
var body = JsonUtility.ToJson(new RequestBody { prompt = prompt });
var request = UnityWebRequest.Post(
"http://127.0.0.1:8787/generate-layout",
body,
"application/json");
var operation = request.SendWebRequest();
EditorApplication.update += Poll;
void Poll()
{
if (!operation.isDone)
return;
EditorApplication.update -= Poll;
if (request.result == UnityWebRequest.Result.Success)
{
var response = JsonUtility.FromJson<ResponseBody>(
request.downloadHandler.text);
PreviewJson(response.layoutJson);
}
else
{
ShowError(request.error);
}
request.Dispose();
}
}
실제 프로젝트에서는 BuildPrompt에 선택한 MonoScript의 타입명과 직렬화 필드 목록을 추가합니다. 필드 목록은 리플렉션으로 추측하기보다 SerializedObject 순회 결과와 [SerializeField] 규칙을 기준으로 만들면 Unity 인스펙터와 모델 입력의 차이를 줄일 수 있습니다.

생성된 JSON을 C# CustomEditor로 바꾸는 방법은?
렌더러는 고정된 템플릿을 사용하고 행 종류만 분기합니다. 모든 프로퍼티는 SerializedProperty로 찾고 serializedObject.Update()와 ApplyModifiedProperties() 사이에서 그립니다.
public static string Render(string targetType, InspectorLayout layout)
{
var builder = new StringBuilder();
builder.AppendLine("using UnityEditor;");
builder.AppendLine("using UnityEngine;");
builder.AppendLine($"[CustomEditor(typeof({targetType}))]");
builder.AppendLine($"public sealed class {targetType}Editor : Editor");
builder.AppendLine("{");
builder.AppendLine(" public override void OnInspectorGUI()");
builder.AppendLine(" {");
builder.AppendLine(" serializedObject.Update();");
foreach (var row in layout.rows)
{
if (row.kind == "header")
builder.AppendLine($" EditorGUILayout.LabelField(\"{row.label}\", EditorStyles.boldLabel);");
else if (row.kind == "space")
builder.AppendLine(" EditorGUILayout.Space(6f);");
else if (row.kind == "property")
builder.AppendLine($" EditorGUILayout.PropertyField(serializedObject.FindProperty(\"{row.propertyPath}\"), new GUIContent(\"{row.label}\"), {row.includeChildren.ToString().ToLowerInvariant()});");
}
builder.AppendLine(" serializedObject.ApplyModifiedProperties();");
builder.AppendLine(" }");
builder.AppendLine("}");
return builder.ToString();
}
위 예제는 설명을 위해 단순화한 렌더러입니다. label과 propertyPath에 따옴표가 들어갈 수 있으므로 실제 구현에서는 C# 문자열 이스케이프 함수를 적용해야 합니다. targetType은 C# 식별자 검증을 통과한 타입명만 허용하고 파일명에도 같은 검증을 적용합니다.
구현 절차를 어떻게 나누면 안전할까?
1단계: 입력 메타데이터를 제한한다
선택한 컴포넌트의 타입명, 필드의 propertyPath, Unity 직렬화 타입, 현재 표시 순서만 보냅니다. 프로젝트 소스 전체나 플레이어 데이터는 기본 입력에서 제외합니다.
2단계: AI 응답을 스키마로 검증한다
rows가 배열인지 확인하고 각 kind를 허용 목록과 비교합니다. propertyPath는 입력 필드 목록과 대조합니다. 검증에 실패하면 파일을 생성하지 말고 JSON 미리보기에서 오류를 보여줍니다.
3단계: 템플릿으로 생성하고 컴파일한다
생성 위치를 Assets/Editor/Generated처럼 고정하고 기존 파일을 바로 덮어쓰지 말고 미리보기와 확인 단계를 둡니다. 생성 뒤에는 Unity 컴파일 완료 여부를 확인하고 대상 컴포넌트의 인스펙터를 열어 다음 항목을 점검합니다.
- 필드가 의도한 순서로 표시되는가?
- 배열과 중첩 직렬화 클래스가 예상대로 펼쳐지는가?
- 프로퍼티 경로가 변경되었을 때 오류가 조용히 누락되지 않는가?
- 다중 오브젝트 편집에서 값이 정상적으로 표시되는가?
ApplyModifiedProperties()가 호출되어 Undo와 변경 저장이 동작하는가?
자동 생성 품질을 높이는 설계 포인트
첫째, 모델에게 “예쁜 인스펙터를 만들어라”라고만 지시하지 않습니다. 사용할 수 있는 kind, 필드 목록, 금지된 동작, JSON 예시를 함께 제공합니다.
둘째, 생성 결과의 책임을 분리합니다. 모델은 배치 순서를 제안하고 C# 렌더러는 Unity API 호출과 문자열 이스케이프를 책임집니다. 이 구조에서는 모델이 존재하지 않는 EditorGUILayout 오버로드를 만들어도 최종 출력에 바로 반영되지 않습니다.
셋째, 캐시 키를 입력의 해시로 구성합니다. 다음 값이 같으면 동일한 레이아웃을 재사용할 수 있습니다.
스키마 버전을 키에 포함하면 렌더러 규칙을 바꾼 뒤 오래된 생성 결과가 재사용되는 문제를 피할 수 있습니다.
넷째, 로컬 모델의 응답 지연을 고려해 생성 요청을 비동기로 처리합니다. EditorApplication.update에서 폴링하는 방식이나 프로젝트에 이미 도입한 에디터 코루틴 패키지를 사용할 수 있습니다. 요청 중에는 버튼을 잠그고 취소 기능을 제공하면 Unity 에디터가 멈춘 것처럼 보이는 상황을 줄일 수 있습니다.
로컬 AI 익스텐션에서 자주 발생하는 문제는?
| 문제 | 원인 | 대응 |
|---|---|---|
FindProperty가 null을 반환함 | 필드명 변경 또는 잘못된 중첩 경로 | 입력 메타데이터와 경로를 생성 전 대조 |
| 생성 코드가 컴파일되지 않음 | 모델이 존재하지 않는 API나 타입을 사용함 | 자유 코드 생성을 줄이고 고정 템플릿 사용 |
| 한글 라벨이 깨짐 | 인코딩을 기본값에 의존함 | HTTP 요청과 파일 저장을 UTF-8로 고정 |
| 생성 결과가 매번 달라짐 | 모델 샘플링이 불안정함 | JSON 스키마, 낮은 temperature, 캐시 적용 |
| 프로젝트 보안 문제가 생김 | 로컬 엔드포인트가 외부 주소로 변경됨 | 루프백 기본값, 주소 허용 목록, 전송 로그 제공 |
자주 묻는 질문 (FAQ)
Unity 커스텀 인스펙터 코드를 AI가 전부 작성하게 해도 될까?
가능하지만 권장하지 않습니다. SerializedObject 수명, Undo, 다중 오브젝트 편집, Unity API 버전 차이를 모델이 항상 정확하게 처리한다고 보장할 수 없습니다. 레이아웃은 AI가 제안하고 핵심 C# 코드는 검증된 템플릿이 생성하도록 분리하는 편이 안전합니다.
로컬 AI를 사용하면 인터넷 전송이 완전히 차단될까?
아닙니다. Unity 익스텐션이 호출하는 주소와 로컬 브리지의 모델 설정을 확인해야 합니다. 기본값을 127.0.0.1로 두고 외부 주소를 입력할 때 경고를 표시하며 소스 코드와 민감한 에셋 경로를 전송하지 않는 정책을 함께 적용해야 합니다.
SerializedProperty 경로 검증은 왜 필요한가?
FindProperty는 문자열 경로를 사용하므로 필드 이름을 바꾸거나 중첩 구조를 변경하면 런타임에 null이 될 수 있습니다. 생성 전에 허용된 경로 목록과 비교하고 생성 후에는 컴파일 및 인스펙터 검사를 수행해야 누락된 UI를 빠르게 찾을 수 있습니다.
정리
Unity 커스텀 인스펙터 GUI 레이아웃 코드 자동 생성의 핵심은 로컬 AI의 자유도를 높이는 데 있지 않습니다. EditorWindow에서 제한된 메타데이터를 보내고 InspectorLayout JSON을 받은 뒤 스키마 검증과 고정 C# 템플릿을 거치는 파이프라인을 만드는 데 있습니다. 이 구조를 사용하면 프로젝트 코드의 외부 노출을 줄이면서도 CustomEditor와 EditorGUILayout 반복 작업을 빠르게 자동화할 수 있습니다.



