Unity

Unity Addressable Asset (4)

CCS_Cheese 2025. 1. 2. 20:22

오늘은 최종적으로 Addressable Asset을 Remote 서버에 Build 해서 업로드하고, 해당 Build 된 Addressable Asset을 Load 하여 Runtime에 Prefab을 생성하는 것을 알아보려고 합니다.

 

먼저 Addressable Asset Group을 Build Load 모드를 변경합니다.

Addressable Groups Settings 에서 Build & Load Paths 변경

내가 선택한 Group의 Build or Load 방식을 Remote, Local을각각 지정할 수 있고 이 옵션은 Addressable Group 별로 지정이 가능하다.

 

Load Path와 Remote Path를 지정하는 것에 대해 다시한번 설명하자면, Addressable Profile에서 추가 설정을 하면 됩니다.

Path 설정 화면

Remote Build, Load Path를 지정해주고, Addressable Build를 실행시키면 해당 Remote Path로 빌드됩니다.

Path

Firebase에 직접적으로 Remote 빌드하는 방법은 아직 확인하지 못하였고, Remote Path에 위치를 지정하여 빌드한 뒤 빌드된 파일을 서버에 업로드하는 방식으로 자동화를 할 수 있다.

 

여기서 Build 파일을 직접 업로드 or Script에서 Build 파일을 업로드할 수 있는데 이를 같이 적용해보려고 합니다.

 

Addressable Build 후 직접 업로드

Unity에서 기본 Addressable Build 방식에는 Default Build Script 방식이 있습니다. 이를 사용하면 기본적인 Addressable build 방식으로 assetbundle이 생성되는 것을 볼 수 있습니다. 

이때 Remote Build Path에 bundle 파일이 아래와 같이 생성됩니다.

생성된 bundle 파일들

해당 파일들을 remote LoadPath에 지정하였던 경로에 넣어주게 되면 Runtime에 Load시에 해당 LoadPath를 보고 Load 하는 것을 볼 수 있습니다.

 

Addressable Build와 함께 Firebase에 자동 업로드하는 Editor Code

using UnityEditor;
using UnityEditor.AddressableAssets.Settings;
using UnityEditor.AddressableAssets.Build;
using UnityEngine;
using Firebase.Storage;
using System.IO;
using System.Threading.Tasks;

public class AddressableCloud
{
    [MenuItem("Addressables/Build and Upload to Cloud")]
    public static void BuildAndUpload()
    {
        // Addressables 빌드 실행
        AddressableAssetSettings.BuildPlayerContent();

        // 빌드된 파일 경로
        string buildPath = Path.Combine(Application.persistentDataPath, "ServerData");
        string platformFolder = GetPlatformFolder();

        string fullPath = Path.Combine(buildPath, platformFolder);

        Debug.Log(fullPath);
        if (Directory.Exists(fullPath))
        {
            Debug.Log($"Build Completed. Uploading files from {fullPath} to Firebase...");
            UploadFolderToFirebase(fullPath, platformFolder).ConfigureAwait(false);
        }
        else
        {
            Debug.LogError("Build folder not found. Please ensure Addressables are built correctly.");
        }
    }
    public static string GetPlatformFolder()
    {
        // 플랫폼에 따른 빌드 폴더 결정
        switch (EditorUserBuildSettings.activeBuildTarget)
        {
            case BuildTarget.Android: return "Android";
            case BuildTarget.iOS: return "iOS";
            case BuildTarget.StandaloneWindows64: return "StandaloneWindows64";
            case BuildTarget.StandaloneOSX: return "Mac";
            default: return "Unknown";
        }
    }
    private static async Task UploadFolderToFirebase(string localFolderPath, string remoteFolderPath)
    {
        FirebaseStorage storage = FirebaseStorage.DefaultInstance;

        // 로컬 폴더의 모든 파일 가져오기
        string[] files = Directory.GetFiles(localFolderPath, "*.*", SearchOption.AllDirectories);

        foreach (string file in files)
        {
            string relativePath = file.Substring(localFolderPath.Length + 1).Replace("\\", "/");
            string firebasePath = Path.Combine(remoteFolderPath, relativePath).Replace("\\", "/");

            Debug.Log($"Uploading {file} to Firebase: {firebasePath}");

            try
            {
                var storageRef = storage.GetReference(firebasePath);
                await storageRef.PutFileAsync(file);
                Debug.Log($"Uploaded {file} to {firebasePath}");
            }
            catch (System.Exception ex)
            {
                Debug.LogError($"Failed to upload {file}: {ex}");
            }
        }
    }
}
Unity에서 Addressable Build를 Script에서 할 수 있는 API인 AddressableAssetSettings.BuildPlayerContent();를 제공합니다. Editor Code에서 Addressable build를 진행 한 뒤 생성된 Remote build Path에 파일을 그대로 firebase에 업로드하는 방식입니다.

firebase에 Platform별로 bundle파일을 생성할 수 있고, 생성된 파일을 접근하는 방식으로 runtime Load를 지원합니다.

 

여기까지 진행을 하게 되면 파일이 firebase Storage Server에 올라가는 것을 볼 수 있고, Remote Path에서 runtime에 Load 하면 됩니다.

Remote Load Path를 지정하면서 발생한 이슈에 대한 정리

Remote Load를 진행하면서 발생한 이슈에 대한 정리를 하려고 합니다. 처음 위 방법대로, build Path를 지정하고, remote Path를 지정한 뒤 정상적으로 Load가 될 것이라고 생각하였습니다. 

 

아래와 같은 문제가 발생을 하는 것을 알 수 있었습니다. 

1. firebase 인코딩 오류

 firebase에 접근할 때 파일의 직접 경로를 접근하면 '/' 문자 인코딩이 달라 인터넷 브라우저에서도 접근할 때 아래 이미지와 같은 error가 발생하는 것을 볼 수 있었습니다. 많이 검색해 본 결과, 인코딩 방식이 달라 %2F == '/'로 접근해야 된다 라는 내용을 알 수 있었습니다.
HTTP method error

 

2. firebase not found file 

인코딩 오류를 수정하기 위해 '/' == %2F로 변경

'/'를 %2F로 변경해 보니 이번에는 file을 찾을 수없다는 error를 리턴하였고, 이로 인해 최초에 설정하였던 Remote Path가 잘못된 것을 알 수 있었습니다. 그렇게 시행착오를 겪은 뒤 확인할 수 있었던 retmote Path로는 위 설명에서 작성하였던 path를 확인할 수 있었습니다. 

 

Addressable Asset을 빌드하고, bundle 파일을 업로드한 뒤 runtime에 Load 해보면서 다양한 이슈 및 오류를 경험할 수 있었고, 그 과정에서 다양한 레퍼런스를 확인하며 이슈를 찾는 과정이 즐거우면서도 힘들었습니다. 다른 사용자들은 같은 이슈를 만나게 되었을 때 위와 같은 가이드가 도움이 되었으면 하여 이런 글을 남기게 되었습니다.

 

 

마치며

Addressable Asset의 초기 설정 및 Load 방식 적용에 대한 순서 정리를 하자면 아래와 같습니다. (서버는 firebase)

1. firebase 프로젝트를 생성합니다

2. 생성된 프로젝트를 Unity Project에 연결합니다.

3. 생성된 firebase 프로젝트를 확인하고, Addressable Remote build, Remote Load Path를 지정합니다.

4. resource (prefab, texture, audioclip)등을 Addressable (주소 지정)을 한 뒤 Addressable을 Group을 지정합니다.

5. 지정된 Group의 Asset이 Local or Remote Load 될 것인지를 지정하고, build 및 bundle 파일을 업로드합니다.

6. Runtime에 Addressables.InstantiateAsync("Addressable Name", Transform transform);을 통해 로드합니다.

7. 위 Instantiate 말고도 다양한 방식으로 Load가 가능하고, Addressable Asset을 사용하지 않으면 Release 해주면 메모리관리에 효과적입니다.

 

적용을 해보며 느낀 점은 하나의 applicaiton에 다양한 콘텐츠가 올라가게 되는데 이때 실제로 사용자가 사용하는 콘텐츠는 한 번에 하나라고 판단이 될 때 10~100개가 넘는 콘텐츠를 application에 한 번에 Load(메모리)에 올리는 것이 아닌, 사용하고 있는 콘텐츠만 Load 및 Release 하는 방식으로 메모리 관리를 하게 되면 불필요한 메모리 리소스에 대한 부하가 줄어들 것 같습니다. 개인프로젝트나, 실제 서비스하는 application에서 시도해보고자 합니다. 

'Unity' 카테고리의 다른 글

Unity Runtime Memory 관리 - 1  (1) 2025.01.12
Unity Localization (2)  (1) 2025.01.04
Unity Addressable Asset (3)  (4) 2024.12.29
Unity Addressable Asset (2)  (1) 2024.12.27
Unity Addressable Asset (1)  (1) 2024.12.15