Addressables 의 그룹 설정이 번들 모양을 정한다
Addressables 그룹 창에 에셋을 정리해 넣으면 할 일이 끝난 것 같습니다. 그런데 런타임에 올라오고 내려가는 것은 그룹이 아니라 번들이고, 같은 그룹이라도 설정 하나로 번들이 하나가 되기도 하고 스무 개가 되기도 합니다.
Unity 의 Addressables Q&A 는 이렇게 정리합니다. 그룹은 에디터 전용 개념이고, 런타임에는 번들 단위로 필요한 것만 올라온다. 그래서 그룹 설정을 정하는 일이 곧 번들 모양을 정하는 일입니다.
묶음 방식이 번들 수를 정한다
그룹의 Bundle Mode 는 세 가지입니다.
- Pack Together. 그룹 전체를 번들 하나로
- Pack Separately. 엔트리마다 번들 하나씩
- Pack Together By Label. 같은 라벨끼리 번들 하나로
512×512 텍스처 20개(라벨 넷에 다섯 개씩)를 넣은 그룹을 세 방식으로 빌드하고, 텍스처 하나만 로드했습니다.
세 방식 모두 메모리에 올라온 텍스처는 요청한 하나뿐이었습니다. 번들 안에서 에셋 하나만 꺼내는 것은 같고, 다른 것은 번들 크기와 수입니다. 번들이 클수록 하나를 꺼내는 시간이 늘었고, 번들을 쪼갤수록 카탈로그가 커져 20개일 때는 Pack Together 의 약 두 배였습니다. 로드 시간이 번들 크기에 따라 늘어난 원인은 확인하지 못했습니다.
내릴 때도 번들 단위라는 점이 더 중요합니다. 같은 번들의 에셋은 다 놓아야 번들이 내려가니, Q&A 의 권고대로 같이 내려갈 것끼리 같은 번들로 묶는 것이 기준이 됩니다. 한 화면에서 같이 쓰고 같이 버리는 것은 Pack Together 나 라벨로 묶고, 따로따로 쓰는 큰 에셋은 따로 뺍니다.
그룹끼리 공유하는 에셋은 그룹마다 복사된다
밑이 AssetBundle 이라서 공용 에셋 문제도 그대로 따라옵니다. 두 그룹의 프리팹이 같은 머티리얼을 쓰는데 그 머티리얼이 어느 그룹에도 없으면, 그룹마다의 번들에 복사됩니다. 이것을 찾는 것이 Analyze 창의 Check Duplicate Bundle Dependencies 규칙입니다.
AD3_A, AD3_B 그룹에 같은 머티리얼을 쓰는 프리팹을 하나씩 넣고 Analyze 를 돌렸습니다.
| Analyze | 프리팹 번들 | 머티리얼 · native | |
|---|---|---|---|
| 고치기 전 | 중복 11건 | 264.5 KiB 씩 | 2개 · +7.18 MiB |
| Fix 후 | No issues found | 3.6 KiB 씩, 격리 번들 262.9 KiB | 1개 · +5.82 MiB |
11건은 머티리얼 하나와 셰이더 셋(Lit, FallbackError, FallbackShader)이 두 그룹에 각각 잡힌 것, 그리고 다른 그룹의 셰이더 중복 셋입니다. Fix 를 누르면 Duplicate Asset Isolation 이라는 그룹을 새로 만들고 중복 에셋을 그리로 옮깁니다. 공용 에셋을 공용 번들에 적는 것을 자동으로 해 주는 셈입니다. 크기 대부분은 머티리얼이 아니라 URP 패키지의 셰이더였습니다.
Fix 결과를 그대로 두면 공용 에셋이 전부 한 번들에 모입니다. 그 번들은 공용 에셋을 쓰는 그룹 중 하나라도 살아 있는 동안 내려가지 않으니, 크면 그룹을 나눠 다시 정리합니다.
직접 확인하는 테스트 코드
그룹의 묶음 방식을 코드로 바꿔 빌드하고, 번들 수와 카탈로그 크기를 봅니다. 에디터 스크립트입니다.
[MenuItem("Tools/Addressables/Build With Mode")]
static void BuildWithMode()
{
var settings = AddressableAssetSettingsDefaultObject.Settings;
var group = settings.FindGroup("AD2");
var schema = group.GetSchema<BundledAssetGroupSchema>();
foreach (var mode in new[]
{
BundledAssetGroupSchema.BundlePackingMode.PackTogether,
BundledAssetGroupSchema.BundlePackingMode.PackSeparately,
BundledAssetGroupSchema.BundlePackingMode.PackTogetherByLabel,
})
{
schema.BundleMode = mode;
AddressableAssetSettings.BuildPlayerContent(out AddressablesPlayerBuildResult result);
long bytes = result.AssetBundleBuildResults
.Where(b => b.SourceAssetGroup == group)
.Sum(b => new FileInfo(b.FilePath).Length);
int count = result.AssetBundleBuildResults.Count(b => b.SourceAssetGroup == group);
Debug.Log($"{mode}: 번들 {count}개, {bytes / 1024.0:F1} KiB");
}
}
Player 에서는 텍스처 하나를 로드한 뒤 같은 이름 규칙의 텍스처가 몇 장 올라왔는지 셉니다. 세 방식 모두 1이 나오고 번들 수만 1, 20, 4 로 달라지면 같은 결과입니다.
var handle = Addressables.LoadAssetAsync<Texture2D>("AD2/Tex03");
yield return handle;
int alive = Resources.FindObjectsOfTypeAll<Texture2D>().Count(t => t.name.StartsWith("AD2_Tex"));
Debug.Log($"올라온 텍스처 {alive}장");
Addressables.Release(handle);
주의할 점
1. 같이 내려갈 것끼리 같은 번들로 묶는다
안 지키면 생기는 문제. 수명이 다른 에셋이 한 번들에 있으면, 하나라도 쓰는 동안 번들 전체가 열려 있고 이미 놓은 에셋도 같이 남습니다.
이렇게 쓴다. 화면이나 스테이지처럼 같이 올라오고 같이 버리는 단위로 그룹을 나눕니다. 그 안에서 따로 쓰는 큰 에셋은 Pack Separately 그룹으로 뺍니다.
2. 번들을 무작정 잘게 쪼개지 않는다
안 지키면 생기는 문제. 에셋마다 번들을 만들면 카탈로그가 커지고, Q&A 에 따르면 번들마다 붙는 메타데이터도 늘어납니다. 이 측정에서 20개로 쪼개자 카탈로그가 약 두 배가 됐습니다.
이렇게 쓴다. 하나씩 따로 쓰는 큰 에셋만 Pack Separately 로 두고, 작은 에셋은 같이 쓰는 단위로 묶습니다.
3. 빌드 전에 Analyze 로 중복을 확인한다
안 지키면 생기는 문제. 그룹끼리 공유하는 머티리얼과 셰이더가 그룹마다 복사돼 빌드 크기와 메모리가 늘어납니다. 그룹 창에는 안 보입니다.
이렇게 쓴다. 그룹을 바꿀 때마다 Check Duplicate Bundle Dependencies 를 돌리고, Fix 로 만든 격리 그룹이 너무 커지면 공용 에셋을 성격별로 나눠 다시 정리합니다.
정리
Addressables 에서 올라오고 내려가는 단위는 그룹이 아니라 그룹 설정이 만든 번들입니다.
- 같은 텍스처 20개가 Pack Together, Pack Separately, By Label 에 따라 번들 1개, 20개, 4개가 됐고, 어느 쪽이든 메모리에 올라온 것은 요청한 하나였습니다.
- 번들이 클수록 하나를 꺼내는 시간이 늘고, 잘게 쪼갤수록 카탈로그가 커집니다. 같이 내려갈 것끼리 묶습니다.
- 그룹끼리 공유하는 에셋은 그룹마다 복사되고, Analyze 의 Fix 가 격리 그룹으로 빼서 한 벌로 줄입니다.