7. 프레임워크를 내 프로젝트에 맞추기

일곱 개의 어셈블리, 선택적 패키지 제거, 서비스를 자체 구현으로 교체하기, 서브시스템 끄기, 그리고 파일이 디스크 어디에 저장되는지.

프레임워크는 다시 빚어 쓸 수 있도록 만들어졌습니다. 모든 서비스는 자체 구현으로 교체하거나 완전히 끌 수 있고, 선택적 부분은 원하지 않을 때 스스로 깔끔하게 빠집니다. 이 장은 그 레버들 하나하나와, 프레임워크가 디스크에 파일을 어디에 두는지 보여줍니다.

7.1 일곱 개의 어셈블리, 그리고 무엇을 참조할지

런타임 코드는 일곱 개의 어셈블리로 나뉘어 있습니다. 이 분할은 선택적 Unity 패키지가 선택으로 남게 하기 위한 것입니다: 각 애드온 어셈블리는 해당 패키지가 설치되어 있을 때만 컴파일됩니다. 모두가 하나의 네임스페이스 CommonGameSystem.Core를 공유하므로 여러분의 using 줄은 결코 바뀌지 않습니다.

스크립트가 기본 Assembly-CSharp에 있다면(어셈블리 정의 파일을 만든 적이 없다면) 이 절은 건너뛰세요 — Unity가 자동으로 모든 것을 참조해 줍니다.

코드가 자체 어셈블리 정의(.asmdef)를 쓴다면 사용하는 것에 따라 참조를 추가하세요:

어셈블리들어 있는 것참조가 필요한 경우...
CommonGameSystem.Core23개 중 18개 서비스: 로깅, 이벤트, 시간, 저장, 설정, 오브젝트 풀링, 오디오, 씬 플로우, 업적, 타이머, 트윈, 트윈 시퀀스, 난수, 상태 기계, 상태 스택, 지연 이벤트 큐, 커맨드 레지스트리, 그리고 UI 패널 스택 계약.항상. 이것이 심장이며, Unity 자체 외에는 어떤 패키지도 필요로 하지 않습니다.
CommonGameSystem.Input입력 서비스: 액션 맵 컨텍스트와 리바인딩.코드가 IInputService를 사용할 때. Input System 패키지 필요.
CommonGameSystem.UI패널 스택의 uGUI 구현.코드가 PanelStack을 생성하거나 uGUI 헬퍼를 사용할 때. IPanelStack을 해석만 한다면 Core만으로 충분합니다.
CommonGameSystem.Localization로컬라이제이션 서비스와 텍스트 바인딩 컴포넌트.코드가 ILocalizationService를 사용할 때. uGUI 필요.
CommonGameSystem.Assets애셋 프로바이더(주소로 로드, 참조 카운트).코드가 IAssetProvider를 사용할 때. Addressables 필요.
CommonGameSystem.AddressableSceneAddressables 기반 애디티브 씬 로딩.코드가 IAddressableSceneService를 사용할 때. Addressables 필요.
CommonGameSystem.Bootstrap자동 시작 시퀀스.참조할 일 없음. 여러분이 호출할 것이 아무것도 없습니다.

자체 어셈블리 정의에 프레임워크 참조를 추가하려면:

  1. Project 창에서 여러분의 .asmdef 파일을 선택합니다.
  2. Inspector에서 Assembly Definition References를 찾아 **+**를 클릭합니다.
  3. CommonGameSystem.Core와, 사용하는 애드온 어셈블리(예: CommonGameSystem.Input)를 선택합니다.
  4. Inspector 하단의 Apply를 클릭합니다.

7.2 선택적 Unity 패키지 제거하기

세 가지 Unity 패키지는 선택 사항입니다. 하나를 제거해도 프레임워크는 결코 깨지지 않습니다: 그 패키지가 필요했던 어셈블리가 스스로 컴파일에서 빠지고, 나머지는 모두 계속 동작합니다. 시작도 여전히 정상적으로 완료됩니다.

제거하는 패키지꺼지는 것계속 동작하는 것
Input System<br>com.unity.inputsystem입력 서비스가 사라집니다(타입이 컴파일되지 않음). 포커스 내비게이션에 입력이 필요하므로 UI 패널 스택은 무동작 버전으로 폴백합니다.그 외 전부. 남은 서비스는 모두 정상 동작하며, 패널 스택 호출은 그저 아무 일도 하지 않습니다.
uGUI<br>com.unity.uguiuGUI 패널 스택이 무동작 버전으로 폴백합니다. 로컬라이제이션 서비스가 사라집니다. 데모 씬의 스크립트도 스스로 빠집니다.입력을 포함한 그 외 전부.
Addressables<br>com.unity.addressables애셋 프로바이더와 어드레서블 씬 서비스가 사라집니다.일반 씬 서비스를 포함한 그 외 전부.

두 가지 참고 사항. 첫째, 제거된 서비스를 언급하는 여러분의 스크립트는 컴파일이 멈춥니다 — 그 참조들도 제거하거나, ServiceLocator.TryResolve<T>(...)로 가용성을 확인하고 해당 코드를 자체 스크립팅 디파인 뒤에 두세요. 둘째, 패키지를 다시 설치하면 추가 설정 없이 모든 것이 돌아옵니다.

7.3 서비스를 자체 구현으로 교체하기

모든 서비스는 오직 인터페이스를 통해서만 사용되므로, 자체 버전으로 바꿔 넣어도 어떤 호출부도 눈치채지 못합니다. 패턴은 언제나 같은 세 줄입니다: 이전 인스턴스를 붙잡고, Replace로 여러분의 것을 등록하고, 이전 것을 dispose하세요.

완전한 실전 예제가 여기 있습니다. 세이브 서비스는 교체 가능한 직렬화기 — 데이터를 텍스트로 바꾸는 부품 — 를 받습니다. 이 커스텀 직렬화기는 세이브를 읽기 쉬운 JSON 대신 Base64로 저장해, 플레이어가 아무렇게나 수정하지 못하게 만듭니다:

using System;
using CommonGameSystem.Core;
using UnityEngine;

// The framework's serializer seam has just two methods.
public sealed class Base64SaveSerializer : ISaveSerializer
{
    public string Serialize(object o)
    {
        string json = JsonUtility.ToJson(o);
        byte[] bytes = System.Text.Encoding.UTF8.GetBytes(json);
        return Convert.ToBase64String(bytes);
    }

    public object Deserialize(Type t, string s)
    {
        byte[] bytes = Convert.FromBase64String(s);
        string json = System.Text.Encoding.UTF8.GetString(bytes);
        return JsonUtility.FromJson(json, t);
    }
}

그리고 교체는, 첫 씬에 배치한 스크립트에서:

using CommonGameSystem.Core;
using UnityEngine;

[DefaultExecutionOrder(-100)] // swap before other scripts cache the save service
public sealed class SaveSetup : MonoBehaviour
{
    private void Awake()
    {
        var previous = ServiceLocator.Resolve<ISaveService>() as System.IDisposable;
        ServiceLocator.Replace<ISaveService>(
            new SaveService(new Base64SaveSerializer()));
        previous?.Dispose();
    }
}

같은 패턴이 어떤 서비스에도 통합니다: 인터페이스를 구현하는 클래스를 작성한 뒤(또는 6.1장과 6.2장처럼 프레임워크의 클래스를 다른 옵션으로 생성한 뒤) Replace하세요. 세 가지 규칙이 교체를 깔끔하게 만듭니다:

  • 일찍 교체하세요. 스크립트는 한 번 해석한 참조를 계속 쓰므로, 다른 것이 이전 인스턴스를 캐시하기 전에 첫 씬에서, 실행 순서 -100에서 서비스를 교체하세요. 일부 프레임워크 서비스는 시작 시 서로 연결되기도 합니다 — 예를 들어 설정 영속화와 업적은 시작할 때 받은 세이브 서비스를 계속 들고 있습니다 — 그래서 늦은 교체는 그 이후에 해석하는 코드에만 영향을 줍니다.
  • 밀어낸 것은 dispose하세요. Replace는 이전 인스턴스를 파괴하지 않습니다. 일곱 개 서비스(시간, 오브젝트 풀, 오디오, UI 패널 스택, 스케줄러, 트윈, 트윈 시퀀스)는 각각 하이어라키에 "[CGS] ..."라는 이름의 숨은 헬퍼 오브젝트를 소유합니다. 이전 인스턴스를 dispose하면 그 오브젝트가 제거되고, dispose를 건너뛰면 세션 내내 계속 돌아갑니다.
  • dispose는 교체 후에, 절대 먼저 하지 마세요. 그래야 그 사이에 어떤 스크립트도 이미 죽은 인스턴스를 해석할 수 없습니다.

7.4 서브시스템 끄기

기능을 완전히 끄려면 내장 무동작 버전으로 교체하세요. 모든 호출부는 계속 동작하며, 호출은 그저 아무 일도 하지 않습니다. 읽기는 안전한 기본값(0, false, 비어 있음, 또는 "없음")을 반환합니다.

ServiceLocator.Replace<IAudioService>(new NullAudio()); // total silence, no code changes

모든 서비스에 하나씩 있습니다:

서비스(인터페이스)무동작 클래스
ILoggerNullLogger
IObjectPoolServiceNullObjectPool
ITimeServiceNullTimeService
IEventBusNullEventBus
ISaveServiceNullSaveService
IConfigurationNullConfiguration
IInputServiceNullInputService
IAudioServiceNullAudio
IPanelStackNullPanelStack
ISceneServiceNullSceneService
ILocalizationServiceNullLocalization
ISchedulerNullScheduler
ITweenServiceNullTweenService
IAssetProviderNullAssetProvider
IRandomServiceNullRandomService
IStateMachineServiceNullStateMachineService
IPushdownStackServiceNullPushdownStackService
ITweenSequenceServiceNullTweenSequenceService
IAddressableSceneServiceNullAddressableSceneService
IDeferredBusNullDeferredBus
ICommandRegistryNullCommandRegistry
IAchievementServiceNullAchievementService

7.3의 dispose 규칙이 여기에도 적용됩니다: "[CGS] ..." 헬퍼 오브젝트를 소유한 일곱 서비스 중 하나를 끌 때는 밀어낸 인스턴스를 dispose하세요.

7.5 파일이 디스크 어디에 저장되나

프레임워크가 쓰는 모든 것은 Unity의 표준 게임별 데이터 폴더인 Application.persistentDataPath 아래로 갑니다:

  • Windows: C:\Users\<you>\AppData\LocalLow\<Company>\<Product>
  • macOS: ~/Library/Application Support/<Company>/<Product>
  • Linux: ~/.config/unity3d/<Company>/<Product>

그 안에는:

  • 세이브saves/<slot>.json, 슬롯당 파일 하나와 이전 버전의 .bak 백업. 쓰기 중에는 서비스가 먼저 임시 파일을 쓰고 원자적으로 교체하므로, 크래시나 정전이 기존 세이브를 손상시키는 일은 결코 없습니다.
  • 설정 — 같은 saves/ 폴더 안 config_로 시작하는 파일들(예: config_AudioSettings.json). 세이브 서비스를 끄면 설정은 자동으로 Unity의 PlayerPrefs로 폴백합니다.
  • 업적 — 같은 폴더에 세이브 서비스를 통해 저장됩니다.
  • 입력 리바인드 — Unity의 PlayerPrefs에 저장됩니다(Windows에서는 레지스트리, macOS에서는 plist 파일).

회사·제품 이름은 Edit > Project Settings > Player에서 가져옵니다. 파일을 동기화하는 클라우드 세이브 시스템(예: Steam Auto-Cloud)은 그냥 saves/ 폴더를 가리키게 하면 됩니다.


다음: 8. 문제 해결과 FAQ