Content Directories 는 아직 로컬 전용이다
Esc

    Content Directories 는 아직 로컬 전용이다

    –Unity메모리

    라이브 서비스 게임은 콘텐츠를 앱 업데이트 없이 서버에서 내려받습니다. Addressables 의 원격 그룹과 카탈로그가 그 일을 해 왔고, Content Directories 로 옮기려면 그 길이 있는지부터 봐야 합니다.

    Unity 6.6 의 Content Directories 는 Player 와 함께 넣는 로컬 콘텐츠만 다룹니다. 원격 콘텐츠는 AssetBundle 로 남겨 섞어 써야 하고, 섞으면 공유하는 에셋이 양쪽에 따로 빌드됩니다. Addressables 4.x 에서 그룹을 바꾸는 길은 있지만 빌드 스크립트 하나에 따라 결과가 갈립니다.

    6.6 에는 원격 배포가 없다

    매뉴얼은 6.6 의 Content Directories 에 원격 다운로드, CDN, 카탈로그 갱신, 패치가 없다고 적습니다. Unity 포럼의 답변도 같습니다. 6.6 은 Player 와 함께 배포하는 고정된 디렉터리를 대상으로 하고, 동적인 콘텐츠는 이후 릴리스에서 다룬다는 것입니다.

    파일 이름이 내용 해시라 바뀐 파일만 골라내기는 쉽습니다. 하지만 받아 오고, 저장하고, 옛 파일을 치우는 일을 전부 직접 만들어야 합니다. Unity 는 이것을 Unity 7 세대에서 넓히겠다고 예고했습니다. 기기에 없는 파일만 골라 받고, 바뀐 파일 단위로 갱신하는 방식이며 2027년에 더 공개한다고 했습니다. 구체적인 시점과 API 는 아직 없습니다.

    그래서 6.6 에서 원격 콘텐츠가 필요하면 그 부분은 AssetBundle(Addressables 원격 그룹)로 남깁니다. 매뉴얼도 “원격은 AssetBundle, 로컬은 Content Directories” 로 섞어 쓰는 것을 허용합니다. 대신 경고가 하나 붙습니다.

    섞으면 공유 에셋이 양쪽에 따로 빌드된다

    Addressables 4.1.1 에서 Cube 는 Content Directory 그룹에, 같은 Lit.mat 을 쓰는 CubeB 는 AssetBundle 그룹에 넣고 한 번에 빌드했습니다. 같은 출력 폴더에 콘텐츠 디렉터리 아카이브와 AssetBundle 하나가 나왔고, 공유하는 에셋이 어디에 들어갔는지 양쪽 리포트를 봤습니다.

    공유 에셋 콘텐츠 디렉터리 AssetBundle
    Lit.mat 들어감 들어감
    Lit.shader 들어감 들어감
    FallbackError.shader, FallbackShader.shader 들어감 들어감

    매뉴얼의 경고대로 공유 에셋이 두 번 빌드됐습니다. 셰이더처럼 큰 공용 에셋일수록 빌드 크기가 그만큼 늘어납니다.

    더 조심할 것은 이 중복이 리포트에 안 잡힌다는 점입니다. Addressables 빌드 레이아웃의 DuplicatedAssetCount 는 0 이었습니다. 콘텐츠 디렉터리와 AssetBundle 사이의 중복은 이 숫자로 알 수 없습니다.

    콘텐츠 디렉터리끼리도 같습니다. 같은 빌드 안에서만 중복이 없고, 디렉터리를 따로 빌드하면 각자 담습니다. Unity 는 포럼 답변에서 같은 에셋이 두 디렉터리에 있으면 메모리에도 두 번 올라온다고 하고, 디렉터리를 작게 여럿 두기보다 크게 적게 두라고 권했습니다.

    Addressables 4.x 는 코드 그대로 옮길 수 있다

    Addressables 4.x 의 Groups 창에는 그룹을 Content Directory 로 바꾸는 메뉴(Convert schema(s) to Content Directory)가 있습니다. 바꾼 그룹을 로드 코드를 한 글자도 안 고치고 쓸 수 있는지 Player 에서 확인했습니다. 쓴 코드는 평범한 Addressables 코드입니다.

    var handle = Addressables.LoadAssetAsync<GameObject>("Cube");
    yield return handle;
    var instance = Instantiate(handle.Result);
    yield return null;
    Destroy(instance);
    Addressables.Release(handle);
    
    var instHandle = Addressables.InstantiateAsync("Cube");
    yield return instHandle;
    Addressables.ReleaseInstance(instHandle.Result);

    AssetBundle 그룹일 때와 바꾼 뒤의 결과가 같았습니다. 컴포넌트, 머티리얼, 올리고 내릴 때의 콘텐츠 머티리얼 수(2 → 3 → 2)가 전부 같고, 달라진 것은 Addressables 안에서 위치를 푸는 공급자 하나였습니다. 바꾼 뒤에는 NativeContentAssetEntryProvider 가 의존 번들 없이 에셋을 찾습니다. Root Asset 도 Addressables 가 빌드할 때 알아서 만들어서 직접 만들 필요가 없었습니다.

    다만 이것은 활성 빌드 스크립트가 맞을 때만 그렇습니다. 같은 변환 그룹을 빌드 스크립트만 바꿔 빌드했습니다.

    AssetBundle 전용 빌더는 Content Directory 그룹을 모르니 그냥 건너뜁니다. 빌드가 성공으로 끝나서 런타임에서야 알게 됩니다. 4.1.1 에서 새로 만든 설정은 BuildScriptSchemaDriven 이 활성인데, 2.x 에서 올린 기존 프로젝트의 활성 빌더가 무엇이 되는지는 확인하지 못했습니다.

    직접 확인하는 테스트 코드

    혼합 빌드에서 공유 에셋이 양쪽에 들어갔는지 보려면 두 리포트를 같이 봅니다. 콘텐츠 디렉터리 쪽은 Library/BuildHistory/<빌드ID>/ContentLayout.json 의 SourceAssets, AssetBundle 쪽은 Addressables 빌드 레이아웃입니다. 활성 빌더와 결과는 에디터 스크립트로 찍을 수 있습니다.

    [MenuItem("Tools/Addressables/Build And Report")]
    static void BuildAndReport()
    {
        var settings = AddressableAssetSettingsDefaultObject.Settings;
        var builder = settings.ActivePlayerDataBuilder;
        Debug.Log($"활성 빌더: {builder.GetType().Name} ({builder.Name})");
    
        foreach (var group in settings.groups)
            Debug.Log($"{group.Name}: [{string.Join(", ", group.Schemas.Select(s => s.GetType().Name))}]");
    
        AddressableAssetSettings.BuildPlayerContent(out AddressablesPlayerBuildResult result);
        Debug.Log($"에러: '{result.Error}', 번들 {result.AssetBundleBuildResults.Count}개");
    }

    변환한 그룹이 있는데 활성 빌더가 BuildScriptPackedMode 이고 번들도 콘텐츠 디렉터리도 안 나오면 빈 빌드입니다.

    주의할 점

    1. 원격 콘텐츠와 로컬 콘텐츠가 같이 쓰는 에셋을 확인한다

    안 지키면 생기는 문제. 공용 셰이더나 머티리얼이 콘텐츠 디렉터리와 AssetBundle 양쪽에 따로 들어갑니다. Addressables 리포트의 중복 수에는 안 잡혀서 빌드 크기가 왜 늘었는지 알기 어렵습니다.

    이렇게 쓴다. 원격 그룹과 로컬 그룹이 같이 쓰는 에셋은 한쪽에만 두도록 그룹을 나누고, 빌드 뒤 ContentLayout.json 과 빌드 레이아웃을 같이 확인합니다.

    2. 그룹을 바꿨으면 활성 빌더를 확인한다

    안 지키면 생기는 문제. 활성 빌더가 AssetBundle 전용이면 변환한 그룹은 에러 없이 빌드에서 빠지고, Player 에서 InvalidKeyException 이 납니다.

    이렇게 쓴다. 변환한 뒤 Build > New Build 메뉴의 Default Build Script(BuildScriptSchemaDriven)로 빌드하고, Play Mode Script 를 Use Existing Build 로 둔 채 한 번 돌려 봅니다.

    3. 디렉터리를 잘게 나누지 않는다

    안 지키면 생기는 문제. 디렉터리끼리는 중복을 못 지우니, 잘게 나눌수록 같은 에셋이 여러 디렉터리에 들어가고 메모리에도 여러 번 올라옵니다.

    이렇게 쓴다. 디렉터리는 DLC 나 언어 팩처럼 설치 단위로 붙였다 떼야 할 때만 나누고, 나머지는 한 디렉터리 안에서 Loadable 로 끊습니다.

    정리

    Unity 6.6 의 Content Directories 는 Player 에 넣는 로컬 콘텐츠만 다룹니다.

    • 원격 배포는 없고 Unity 7 세대에 예고만 됐습니다. 원격 콘텐츠는 AssetBundle 로 남겨 섞어 씁니다.
    • 섞으면 공유 에셋이 양쪽에 따로 빌드되고, Addressables 의 중복 수에는 안 잡힙니다. 디렉터리끼리도 중복을 못 지웁니다.
    • Addressables 4.x 는 그룹만 바꾸면 로드 코드 그대로 옮겨지지만, 활성 빌더가 AssetBundle 전용이면 경고 없이 빈 빌드가 됩니다.