Unity·Unreal 실전

Unity 에디터 도구 설정이 재컴파일마다 초기화될 때, ScriptableSingleton 저장 위치부터 확인하세요

kr-gamedev 2026. 7. 16. 09:06

Unity·Unreal 실전 개념도 (AI 생성)

서론

빌드 경로, 개발 빌드 여부, 마지막으로 선택한 플랫폼처럼 에디터 도구가 기억해야 할 값이 있다. 이를 EditorWindow 필드나 static 변수에만 넣으면 스크립트 재컴파일과 에디터 재시작 뒤에 값이 사라진다.

EditorPrefs로 옮기면 저장은 되지만 프로젝트별 설정과 사용자 개인 설정이 섞이기 쉽다. Unity의 ScriptableSingleton<T>을 사용하면 에디터 전용 상태를 직렬화하고, 저장 위치까지 명시적으로 결정할 수 있다.

1. static 필드가 해결책이 아닌 이유

static은 접근하기 편하지만 영속 저장소가 아니다. Domain Reload가 발생하면 초기화되며, Reload Domain을 꺼도 에디터 재시작까지 버티지는 못한다.

Unity 공식 문서에 따르면 ScriptableSingleton<T>의 직렬화 가능한 데이터는 Assembly Reload 이후에도 유지되며, FilePathAttribute를 함께 사용하면 에디터 세션 사이에도 보존된다.
출처: Unity ScriptableSingleton API

2. 설정 저장소 만들기

다음 코드는 팀이 함께 사용하는 빌드 도구 설정을 ProjectSettings 아래에 텍스트로 저장한다.

using UnityEditor;
using UnityEngine;

[FilePath(
    "ProjectSettings/BuildDashboardSettings.asset",
    FilePathAttribute.Location.ProjectFolder)]
public sealed class BuildDashboardSettings
    : ScriptableSingleton<BuildDashboardSettings>
{
    [SerializeField] private string outputRoot = "Builds";
    [SerializeField] private bool developmentBuild = true;

    public string OutputRoot => outputRoot;
    public bool DevelopmentBuild => developmentBuild;

    public void Apply(string newRoot, bool newDevelopmentBuild)
    {
        if (outputRoot == newRoot &&
            developmentBuild == newDevelopmentBuild)
            return;

        outputRoot = newRoot;
        developmentBuild = newDevelopmentBuild;
        Save(true);
    }
}

instance에 처음 접근하면 Unity가 싱글턴을 생성하거나 기존 파일을 읽는다. Save(true)true는 텍스트 저장을 뜻하므로 버전 관리에서 변경 내용을 검토하기도 쉽다.

3. EditorWindow와 연결하기

창의 필드를 직접 저장하지 말고, 화면은 입력만 받고 실제 상태는 싱글턴이 소유하게 만든다.

using UnityEditor;
using UnityEngine;

public sealed class BuildDashboardWindow : EditorWindow
{
    private string outputRoot;
    private bool developmentBuild;

    [MenuItem("Tools/Build Dashboard")]
    private static void Open()
        => GetWindow<BuildDashboardWindow>("Build Dashboard");

    private void OnEnable()
    {
        var settings = BuildDashboardSettings.instance;
        outputRoot = settings.OutputRoot;
        developmentBuild = settings.DevelopmentBuild;
    }

    private void OnGUI()
    {
        EditorGUI.BeginChangeCheck();

        outputRoot = EditorGUILayout.TextField(
            "Output Root", outputRoot);
        developmentBuild = EditorGUILayout.Toggle(
            "Development Build", developmentBuild);

        if (EditorGUI.EndChangeCheck())
        {
            BuildDashboardSettings.instance.Apply(
                outputRoot, developmentBuild);
        }
    }
}

값이 실제로 바뀌었을 때만 저장하므로 OnGUI가 반복 호출되어도 불필요한 디스크 쓰기가 발생하지 않는다.

4. 저장 위치를 목적에 맞게 고르기

ProjectFolder는 현재 프로젝트 기준 경로다. 팀 공통 빌드 규칙처럼 저장소에 포함할 값에 적합하다.

PreferencesFolder는 Unity 환경설정 폴더 기준이다. 창 크기, 개인 출력 경로처럼 다른 팀원에게 공유하면 안 되는 값에 사용한다. 프로젝트별 값과 개인별 값을 하나의 싱글턴에 섞지 않는 것이 핵심이다.

5. 재컴파일과 재시작을 따로 검증하기

구현 후에는 다음 순서로 확인한다.

  1. 값을 변경하고 스크립트를 재컴파일한다.
  2. 창을 다시 열어 값이 유지되는지 확인한다.
  3. Unity를 완전히 종료한 뒤 프로젝트를 다시 연다.
  4. 설정 파일을 삭제했을 때 기본값으로 복구되는지 확인한다.

재컴파일만 통과하고 재시작에서 실패한다면 FilePathAttribute 또는 Save 호출이 빠졌을 가능성이 크다.

6. Save를 OnValidate에서 호출하지 않기

Unity는 SaveOnValidate에서 호출하지 말라고 안내한다. 파일을 읽는 도중 다시 저장을 시도해 오류가 발생할 수 있기 때문이다. 저장은 버튼, 입력 완료, 명시적인 Apply 메서드처럼 변경 시점이 분명한 경로에서 실행한다.
출처: Unity ScriptableSingleton Save API

또한 이 코드는 UnityEditor API를 사용하므로 반드시 Editor 전용 어셈블리나 Editor 폴더에 둬야 한다.

결론

저장 방식 재컴파일 유지 에디터 재시작 유지 적합한 용도
static 필드 조건부 불가 임시 캐시
EditorPrefs 가능 가능 사용자 개인 설정
ScriptableSingleton + ProjectFolder 가능 가능 프로젝트 공통 설정
ScriptableSingleton + PreferencesFolder 가능 가능 에디터 개인 설정

에디터 도구의 상태가 자꾸 초기화된다면 창 자체에 값을 붙잡아 두지 말자. 상태 소유권을 ScriptableSingleton으로 분리하고, 공유 범위에 맞는 저장 위치와 명시적인 저장 시점을 선택하면 재컴파일과 재시작 모두에서 예측 가능한 도구를 만들 수 있다.