Unity Addressable Assets
Addressable Assets System은 Resources 폴더와 AssetBundle의 단점을 해소하기 위해 도입된 Unity의 공식 에셋 관리 시스템입니다. 에셋에 주소(address) 문자열을 부여하고 비동기로 로드하며, 로컬·원격 배포를 동일한 API로 처리합니다.
Resources·AssetBundle 대비 장점
Section titled “Resources·AssetBundle 대비 장점”| 비교 항목 | Resources | AssetBundle | Addressables |
|---|---|---|---|
| 빌드 포함 여부 | 항상 포함 | 수동 관리 | 그룹 설정으로 제어 |
| 원격 배포 | 불가 | 가능 (복잡) | 기본 지원 |
| 의존성 관리 | 수동 | 수동 | 자동 |
| 메모리 해제 | Resources.UnloadAsset | Bundle.Unload | Handle.Release |
| 주소 참조 | 경로 문자열 | 수동 매핑 | 주소 / 라벨 |
- Address — 에셋을 식별하는 임의의 문자열 키 (
"Characters/Hero") - Label — 여러 에셋을 묶는 태그 (
"preload","ui") - Group — 빌드 전략(로컬/원격)을 공유하는 에셋 묶음
- Profile — 로컬·원격 경로를 환경(개발/스테이징/프로덕션)별로 설정
비동기 로드 API
Section titled “비동기 로드 API”단일 에셋 로드
Section titled “단일 에셋 로드”using UnityEngine;using UnityEngine.AddressableAssets;using UnityEngine.ResourceManagement.AsyncOperations;
public class HeroLoader : MonoBehaviour{ AsyncOperationHandle<GameObject> _handle;
async void Start() { // 주소로 로드 _handle = Addressables.LoadAssetAsync<GameObject>("Characters/Hero"); GameObject prefab = await _handle.Task;
if (_handle.Status == AsyncOperationStatus.Succeeded) Instantiate(prefab); }
void OnDestroy() { // 반드시 Release — 내부 참조 카운트를 감소시켜 메모리 해제 if (_handle.IsValid()) Addressables.Release(_handle); }}라벨로 다수 에셋 로드
Section titled “라벨로 다수 에셋 로드”async void LoadUIAssets(){ var handle = Addressables.LoadAssetsAsync<Sprite>("ui", sprite => { // 각 에셋이 로드될 때마다 콜백 호출 Debug.Log($"로드됨: {sprite.name}"); });
await handle.Task;
if (handle.Status == AsyncOperationStatus.Succeeded) { IList<Sprite> sprites = handle.Result; // 사용 완료 후 해제 Addressables.Release(handle); }}async void LoadGameScene(){ var handle = Addressables.LoadSceneAsync("Scenes/GameLevel", UnityEngine.SceneManagement.LoadSceneMode.Additive);
await handle.Task; // 씬 언로드 시 await Addressables.UnloadSceneAsync(handle).Task;}메모리 해제 패턴
Section titled “메모리 해제 패턴”Addressables는 참조 카운팅 방식으로 메모리를 관리합니다. LoadAssetAsync 한 번 → Release 한 번으로 균형을 맞춰야 합니다.
// ❌ 잘못된 패턴 — Release 없이 재로드 반복 → 메모리 누수for (int i = 0; i < 100; i++) Addressables.LoadAssetAsync<Texture2D>("icon");
// ✅ 올바른 패턴 — 핸들 보관 후 사용 완료 시 Releasepublic class AssetManager : MonoBehaviour{ readonly Dictionary<string, AsyncOperationHandle> _handles = new();
public async Task<T> LoadAsync<T>(string address) { if (_handles.TryGetValue(address, out var cached)) return (T)cached.Result;
var handle = Addressables.LoadAssetAsync<T>(address); await handle.Task; _handles[address] = handle; return handle.Result; }
public void Unload(string address) { if (_handles.TryGetValue(address, out var handle)) { Addressables.Release(handle); _handles.Remove(address); } }
void OnDestroy() { foreach (var handle in _handles.Values) Addressables.Release(handle); _handles.Clear(); }}| 그룹 용도 | 빌드 설정 | 예시 |
|---|---|---|
| 로컬 필수 에셋 | Local / Build Path | 기본 UI, 공통 셰이더 |
| 원격 레벨 에셋 | Remote / CDN Path | 스테이지별 씬, 캐릭터 |
| DLC | Remote / 별도 카탈로그 | 추가 콘텐츠 |
원격 배포 (CDN) 전략
Section titled “원격 배포 (CDN) 전략”- Profile 에서 Remote Build Path / Remote Load Path 설정
Addressables > Build > New Build > Default Build Script실행ServerData/폴더의 번들 파일을 CDN에 업로드- 런타임에
Addressables.InitializeAsync()→ 카탈로그 업데이트 체크
async void CheckForUpdates(){ await Addressables.InitializeAsync().Task;
var checkHandle = Addressables.CheckForCatalogUpdates(false); await checkHandle.Task;
if (checkHandle.Result.Count > 0) { var updateHandle = Addressables.UpdateCatalogs(checkHandle.Result); await updateHandle.Task; Addressables.Release(updateHandle); }
Addressables.Release(checkHandle);}