[Addressable]
● Unity의 리소스 관리 시스템
● 프로젝트에 포함된 에셋(텍스처, 프리팹, 오디오 등)을 주소 기반으로 관리하며 비동기 로딩, 메모리 최적화, 빌드 분리 등의 장점을 제공한다.
■ 핵심 개념
● 주소만 있으면 어디에 있어도 사용 가능
○ 에셋을 Addressable로 등록하고 '주소 문자열' 하나만 알고 있으면 로드 가능
○ 에셋이 Resources 폴더, StreamAssets, 원격 서버, CDN 어디에 있든 상관없이 사용 가능
Addressables.LoadAssetAsync<GameObject>("MyCharacter");
● 비동기 로드 지원
○ Addressables.LoadAssetAsync는 비동기 방식으로 작동해 게임의 로딩 지연을 줄임
○ yield return 혹은 async/awit와 조합하여 자연스러운 흐름 구현 가능
// Coroutine 예시
IEnumerator Start()
{
var handle = Addressables.LoadAssetAsync<GameObject>("MyEffect");
yield return handle;
GameObject obj = handle.Result;
Instantiate(obj);
}
● 메모리 자동 관리
○ 사용하지 않는 에셋은 Unload 처리 가능
○ Addressable.Release(handle)을 호출하면 참조를 제거하고 필요 시 메모리에서 해제
○ 더 이상 사용하지 않는 리소스를 명시적으로 해제해서 메모리 누수 방지
Addressables.Release(handle); // 메모리에서 언로드
● 그룹으로 에셋 관리
○ 에셋은 Addressable Group으로 분류 가능
○ 예시: UI, Enemy, Stage1Resources 등
○ 그룹별로 빌드 설정, 로딩 방식, 배포 경로를 달리할 수 있음
○ Local, Remote, Streaming 등
● 빌드 시 에셋을 번들로 분리
○ Addressables 빌드 시 자동으로 에셋을 AssetBundle 형태로 압축
○ 필요할 때만 다운로드 → 최초 용량 최소화 + 업데이트 용이
○ Patch, Hotfix 구현 시 유용
● Preload / Lazy Load 등 전략 가능
○ 특정 에셋 그룹에 Preload 라벨을 붙이면 게임 시작 시 미리 로딩
○ 리소스가 많은 게임에서 초기 로딩 시간과 성능의 균형 조절 가능
■ Addressables vs 기존 Resources의 차이점
| 항목 | Addressables | Resources |
| 로딩 방식 | 비동기 | 동기 |
| 메모리 관리 | 자동 관리 가능 | 직접 관리 어려움 |
| 빌드 전략 | 개별 번들로 분리 가능 | 모든 에셋 포함 |
| 원격 다운로드 | 가능 | 불가능 |
| 유연성 | 매우 높음 | 낮음 |
| 런타임 로드 | 주소 기반 | 경로 기반 (Resources.Load) |
■ 언제 Addressable을 써야 할까?
● 리소스가 많고 최적화가 중요한 프로젝트
● 런타임에 리소스를 비동기로 로드해야 할 경우
● DLC, 패치, 원격 업데이트, 콘텐츠 스트리밍이 필요한 경우
[Addressable Profiles]
● 빌드 시 에셋의 저장 위치와 로드 위치를 설정할 수 있는 템플릿
● BuildPath: 에셋 번들이 생성되어 저장되는 물리적 위치
● LoadPath: 런타임에 게임이 해당 에셋을 로드하려고 접근하는 URL/경로
■ Profile 종류
| 이름 | 설명 |
| Build-in | 번들이 앱 내에 포함됨 → 무조건 로컬에서 로딩 |
| Remote | 번들이 원격 서버에 저장됨 → HTTP/HTTPS로 다운로드 필요 |
| Editor Hosted | Unity 에디터가 간이 HTTP 서버를 띄워서 에셋을 제공 → 로컬 테스트에 유용 |
| Cloud Content Delivery (CCD) | Unity의 클라우드 서버를 활용하여 배포 → 유니티 CDN 주소 자동 연결 |
| Custom | 직접 주소를 작성하여 사용자 정의 서버에 연결 → http://yourserver.com/... 처럼 직접 입력 가능 |
■ BuildTarget
● StandaloneWindows64, Android, iOS, WebGL 등 Unity의 빌드 플랫폼 이름이 그대로 사용됨
● [BuildTarget] 변수는 자동으로 현재 플랫폼에 맞는 폴더 이름으로 치환됨
■ 중요한 설정
● HTTP 접근 허용
● LoadPath가 HTTP라면 반드시 Player Settings에서 Allow HTTP를 활성화해야 함
● Project Settings > Player > Other Settings > Configuration > Allow downloads over HTTP

[Addressable 꿀팁]
● 이름 간단하게 변경
○ 해당 에셋 우클릭 > Simplify Addressable Names

[Addressable 원격 테스트 설정]
■ 씬 또는 에셋을 Addressable로 등록
● 씬을 Build Settings에 추가한 뒤 Project 창에서 해당 씬의 Inspector > Addressable 체크
● 원하는 그룹으로 이동
■ Group 설정: Content Packing & Loading 스키마 추가
● Addressable Groups > 해당 group 선택
● 상단 Add Schema > Content Packing & Loading 추가

■ Build & Load Paths를 Remote로 설정
● Content Packing & Loading 섹션에서
○ Build Path: RemoteBuildPath
○ Load Path: RemoteLoadPath
● 기본 Remote 경로
○ Build Path: ServerData/[BuildTarget]
○ LoadPath: http://localhost:yourport/[BuildTarget]

■ Addressable Asset Settings에서 카탈로그도 Remote로 설정
● Inspector > AddresableAssetSettings.asset 선택
● Build Remote Catalog 체크
● BuildPath & LoadPath Remote로 설정

■ Project SEttings > HTTP 허용 설정
● Edit > Project Settings > Player > Other Settings
● Allow downloads over HTTP를 Always allowed로 설정
● 개발할 때만 사용하기

■ Addressables Hostring Service 설정
● Window > Asset Management > Addressables > Hosting Services
● Port 재설정
● Enable 체크하여 로컬 HTTP 서버 켜기

■ Play Mode Script 설정
● Addressable Groups 창 > 상단의 Play Mode Script
● Use Existing Build (Widnows) 선택 → 실제 번들을 사용하고 싶을 때 선택

■ Adddressables Build 실행
● Addressables Groups > 상단 Build > New Build > Defualt Build Script 선택
● 실제 RemoteBuildPath에 번들이 생성됨
● catalog.json이 포함됨

■ 번들 확인
● 프로젝트 루트: ServerData/StandaloneWindows64 폴더 내 확인
● .bundle, .json 파일 등이 포함되어 있어야 함
● 에디터 테스트 시 이 경로가 웹서버 루트가 됨

[Addressable 예시]
■ 에셋 로드
● Addressable

● LoadAssetManager
public class LoadAssetManager : MonoBehaviour
{
//어드레서블 주소
[SerializeField] private string _assetName = "Warrior Red";
private GameObject _warriorInstance;
//비동기 로딩 시 사용할 핸들
private AsyncOperationHandle<GameObject> _loadHandle;
//비동기 로드 메소드
public void LoadWarriorAsync()
{
//비동기 함수이기 때문에 끝났을 때 끝났을 때 호출 함수를 콜백으로 받아오기
Addressables.LoadAssetAsync<GameObject>(_assetName).Completed +=
(AsyncOperationHandle<GameObject> handle) =>
{
_loadHandle = handle;
//로드된 에셋의 인스턴스를 생성
OnWarriorLoad();
};
}
private void OnWarriorLoad()
{
if (_loadHandle.Status == AsyncOperationStatus.Succeeded)
{
var warrior = _loadHandle.Result;
_warriorInstance = Instantiate(warrior, Vector3.zero, Quaternion.identity);
Debug.Log("전사 생성");
}
}
//해제
public void ReleaseWarrior()
{
//어드레서블 메모리 해제
Addressables.Release(_loadHandle);
//생성한 인스턴스 삭제
Destroy(_warriorInstance);
}
}
● LoadAssetManagerEditor
[CustomEditor(typeof(LoadAssetManager))]
public class LoadAssetManagerEditor : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
LoadAssetManager manager = (LoadAssetManager)target;
if (GUILayout.Button("전사 로드"))
{
manager.LoadWarriorAsync();
}
if (GUILayout.Button("전사 해제"))
{
manager.ReleaseWarrior();
}
}
}

■ 씬 로드
● LoadSceneManager
public class LoadSceneManager : MonoBehaviour
{
[SerializeField] private string sceneName = "Level01";
public async void DownloadAndLoadAsync()
{
await CheckDownloadSize();
await Task.Delay(2000);
await LoadSceneAsync();
}
//캐시 클리어
public void ClearCache()
{
Addressables.ClearDependencyCacheAsync(sceneName);
Debug.Log("캐시 삭제");
}
//다운로드 사이즈 체크
public async Task CheckDownloadSize()
{
//파일 사이즈 체크
AsyncOperationHandle<long> handle = Addressables.GetDownloadSizeAsync(sceneName);
//Task 작업이 끝날 때까지 기다리는 것 (다른 작업들을 lock 거는 것이 아니라 비동기 방식으로 진행)
await handle.Task;
if (handle.Status == AsyncOperationStatus.Succeeded)
{
long size = handle.Result / (1024 * 1024); //메가바이트로 변환
Debug.Log($"다운로드 사이즈 {size}MB");
}
//핸들 해제
Addressables.Release(handle);
}
//씬 로딩
public async Task LoadSceneAsync()
{
try
{
var loadHandle = Addressables.LoadSceneAsync(sceneName);
while (!loadHandle.IsDone)
{
Debug.Log($"Progress: {loadHandle.PercentComplete * 100}%");
await Task.Yield();
}
Debug.Log($"Status: {loadHandle.Status}");
}
catch (System.Exception e)
{
Debug.Log(e.Message);
}
}
}
● LoadSceneManagerEditor
[CustomEditor(typeof(LoadSceneManager))]
public class LoadSceneManagerEditor : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
var manager = (LoadSceneManager)target;
if (GUILayout.Button("씬 로드"))
{
manager.DownloadAndLoadAsync();
}
if (GUILayout.Button("캐시 삭제"))
{
manager.ClearCache();
}
}
}

'내일 배움 캠프 > 뭐하지' 카테고리의 다른 글
| 저작권 (3) | 2025.06.22 |
|---|---|
| UniTask, UniRx (0) | 2025.06.22 |
| 직렬화 (0) | 2025.06.11 |
| 코드 최적화 & 프로파일러 (0) | 2025.06.10 |
| 게임 빌드 프로세스 (0) | 2025.06.05 |