유니티(Unity) 게임 엔진으로 대규모 프로젝트를 개발하다 보면 필연적으로 '에셋 관리(Asset Management)'라는 거대한 장벽에 부딪히게 됩니다. 초기 프로토타입 단계에서는 Resources 폴더에 에셋을 넣고 Resour
Unity Addressables Asset System으로 로딩 시간 300% 단축하기
1. 서론: 레거시 에셋 관리 시스템의 한계
유니티(Unity) 게임 엔진으로 대규모 프로젝트를 개발하다 보면 필연적으로 '에셋 관리(Asset Management)'라는 거대한 장벽에 부딪히게 됩니다. 초기 프로토타입 단계에서는 Resources 폴더에 에셋을 넣고 Resources.Load()를 호출하는 방식이 매우 직관적이고 편리합니다. 하지만 프로젝트 규모가 커지고 모바일 기기에서의 메모리 관리가 중요해지면 Resources 폴더의 치명적인 단점들이 드러나기 시작합니다. 앱의 초기 시작 시간이 기하급수적으로 길어지며, 사용하지 않는 에셋까지 메모리에 상주하게 되어 Out Of Memory(OOM) 크래시의 주범이 됩니다.
이를 해결하기 위해 과거에는 AssetBundle 시스템을 직접 구축하여 사용했습니다. 하지만 에셋 번들은 의존성 관리(Dependency Management)가 극도로 까다롭고, 중복된 에셋이 여러 번들에 포함되는 등 유지보수 측면에서 많은 개발자들의 밤잠을 설치게 했습니다. 이러한 문제를 근본적으로 해결하기 위해 등장한 것이 바로 Addressables Asset System (어드레서블 시스템)입니다.
2. Addressables의 핵심 철학: 분리(Separation)와 주소 지정(Addressing)
Addressables의 가장 큰 특징은 에셋의 '빌드 위치'와 '로드 방식'을 코드로부터 완전히 분리했다는 점입니다. 개발자는 에셋에 고유한 '주소(Address)' 문자열만 부여하면 됩니다. 이 에셋이 로컬에 있는지, 원격 서버(CDN)에 있는지, 어떤 번들에 포함되어 있는지는 어드레서블 시스템이 프로필(Profile) 설정에 따라 알아서 처리합니다.
- Address: 에셋을 호출하기 위한 식별자.
- Label: 여러 에셋을 그룹화하여 한 번에 로드하거나 관리하기 위한 태그.
- Group: 에셋들이 실제로 빌드될 때 어떤 에셋 번들로 묶일지 결정하는 논리적 단위.
3. 실무 적용: 로딩 시간 300% 단축의 비밀
저희 팀이 최근 런칭한 모바일 RPG 프로젝트에서는 Addressables를 도입하여 씬 전환 및 리소스 로딩 시간을 기존 대비 300% 이상 단축했습니다. 그 구체적인 실무 노하우를 공유합니다.
3.1. 에셋 그룹(Group)의 전략적 분할
가장 흔히 하는 실수는 모든 에셋을 하나의 거대한 그룹으로 묶거나, 반대로 너무 잘게 쪼개는 것입니다. 우리는 다음과 같은 기준으로 그룹을 설계했습니다.
- 동시 로드 빈도: 특정 UI 팝업창을 열 때 항상 같이 쓰이는 텍스처, 프리팹, 사운드는 하나의 번들에 묶어 I/O 오버헤드를 최소화합니다.
- 업데이트 빈도 (Static vs Dynamic): 게임 출시 후 절대 바뀌지 않을 핵심 리소스(Core Data)와, 향후 라이브 서비스 중 패치로 변경될 가능성이 있는 리소스(Live Data)를 분리합니다. 이를 통해 패치 용량을 획기적으로 줄일 수 있습니다.
3.2. 의존성 지옥(Dependency Hell) 탈출하기
Addressables Analyze 툴은 프로젝트 최적화의 핵심입니다. Check Duplicate Bundle Dependencies 룰을 실행하면 여러 번들에 중복으로 포함된 에셋들을 찾아냅니다. 예를 들어, 서로 다른 캐릭터 프리팹이 동일한 공용 머티리얼을 참조하고 있다면, 이 머티리얼은 두 캐릭터의 에셋 번들에 각각 중복으로 들어가 빌드 용량과 메모리를 낭비하게 됩니다. 우리는 이러한 공용 에셋들을 별도의 'Shared_Group'으로 분리하여 의존성을 깔끔하게 해결했습니다.
3.3. 비동기 로딩(Async Loading)과 메모리 해제(Release)
Addressables는 기본적으로 비동기(Asynchronous)로 동작합니다. Addressables.LoadAssetAsync<T>()를 호출하여 반환된 AsyncOperationHandle을 철저하게 관리해야 합니다.
AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>("HeroPrefab");
await handle.Task;
// ... 인스턴스화 작업 ...
가장 중요한 것은 메모리 해제입니다. Addressables는 레퍼런스 카운팅(Reference Counting) 기반으로 작동합니다. 로드한 횟수만큼 정확히 Addressables.Release() 또는 Addressables.ReleaseInstance()를 호출해야 메모리 누수(Leak)를 막을 수 있습니다. 우리는 자체적인 AssetManager 클래스를 래핑(Wrapping)하여 핸들 객체의 생명주기를 씬(Scene) 생명주기와 동기화하는 자동화 시스템을 구축했습니다.
4. 카탈로그(Catalog)와 CDN 연동
Addressables의 진가는 원격 콘텐츠 전송 네트워크(CDN)와 결합할 때 발휘됩니다. 로컬 카탈로그(Local Catalog)와 원격 카탈로그(Remote Catalog)를 비교하여 변경된 에셋 번들만 다운로드하는 OTA(Over-The-Air) 패치 시스템을 단 몇 줄의 코드로 구현할 수 있었습니다. CRC 체크를 통한 무결성 검증과, GetDownloadSizeAsync를 활용한 다운로드 진행률 UI 표시도 매우 쉽게 통합됩니다.
5. 결론 및 최적화 팁
Unity Addressables는 단순한 기능이 아니라 프로젝트의 에셋 관리 패러다임 자체를 바꾸는 강력한 도구입니다. 초기 세팅 러닝 커브가 존재하지만, 이를 극복하고 나면 로딩 속도 향상, 메모리 사용량 감소, 라이브 패치 용이성이라는 엄청난 보상을 얻을 수 있습니다. 프로파일러(Addressables Profiler)를 항상 띄워두고 레퍼런스 카운트가 정상적으로 0으로 떨어지는지 확인하는 습관을 들이는 것이 성공적인 Addressables 도입의 지름길입니다.