Unity 가상공간 로딩 최적화: Addressables와 Additive Scene 설계
Unity 가상공간은 첫 화면에 필요한 UI와 사용자가 입장할 공간의 에셋이 다릅니다. 모든 Scene과 아바타, 텍스처를 시작 시점에 함께 로드하면 진입 시간이 길어지고 사용하지 않는 공간까지 메모리를 차지합니다.
시작에 필요한 최소 환경을 먼저 열고, 선택한 공간을 비동기로 추가하는 구조가 필요합니다. Unity의 Additive Scene과 Addressables는 이 경계를 만드는 도구지만, 로드 호출만 비동기로 바꾼다고 전환이 자동으로 안정되지는 않습니다.
앱을 유지하는 Scene과 교체되는 공간을 나눕니다
Bootstrap Scene에는 앱 생명 주기 동안 유지할 객체를 둡니다. 인증 상태, 네트워크 연결, 공통 UI, 오디오와 공간 전환 관리자가 여기에 해당합니다. 실제 가상공간은 별도의 Content Scene으로 분리합니다.
Bootstrap Scene
App State
Network Session
Common UI
Scene Coordinator
Content Scene
Environment
Lighting
Spawn Points
Room ObjectsContent Scene을 Additive 방식으로 로드하면 Bootstrap을 유지한 채 공간만 교체할 수 있습니다. 공간을 나갈 때는 해당 Scene과 그 공간이 소유한 리소스를 정리합니다.
DontDestroyOnLoad에 많은 객체를 보내는 방식은 소유 관계를 흐리기 쉽습니다. 유지 객체는 Bootstrap에 모으고, 공간 객체는 Content Scene에 남겨 Scene 해제와 함께 수명 주기가 끝나도록 구성하는 편이 관리하기 쉽습니다.
Addressables의 주소는 소유권까지 설명하지 않습니다
Addressables를 사용하면 로컬 또는 원격 위치의 에셋을 주소로 비동기 로드할 수 있습니다. 같은 Prefab을 여러 공간에서 참조하면 의존 Bundle이 공유될 수도 있습니다.
문제는 누가 로드했고 언제 해제해야 하는지입니다. 전역 UI가 사용하는 텍스처와 한 공간에서만 사용하는 오브젝트가 같은 방식으로 관리되면 공간 퇴장 시 너무 일찍 해제하거나 계속 메모리에 남길 수 있습니다.
에셋의 소유 범위를 명시합니다.
- 앱 범위: 종료할 때까지 유지하는 공통 에셋
- 공간 범위: Content Scene과 함께 해제하는 환경 에셋
- 화면 범위: 특정 패널을 닫을 때 해제하는 미리보기 에셋
- 인스턴스 범위: 생성한 오브젝트가 사라질 때 반환하는 Prefab
주소 문자열을 직접 흩어 놓기보다 공간 Manifest가 필요한 Scene, Prefab과 Label을 설명하도록 만들면 로드와 해제 대상을 함께 추적하기 좋습니다.
로드 작업은 상태 기계로 관리합니다
사용자가 공간 A를 선택한 직후 공간 B를 선택하거나 로딩 중 앱이 백그라운드로 갈 수 있습니다. 먼저 시작한 비동기 콜백이 나중 선택한 공간을 덮어쓰면 잘못된 Scene이 활성화됩니다.
Idle
↓
Resolving Content
↓
Downloading
↓
Loading Scene
↓
Preparing Objects
↓
Ready각 전환에는 요청 ID나 세대를 부여합니다. 완료 콜백은 자신이 현재 전환에 속하는지 확인한 뒤 결과를 적용합니다. 취소된 전환의 Handle이 완료되더라도 화면을 바꾸지 않고 안전하게 해제해야 합니다.
이미 열린 공간을 먼저 내린 뒤 새 공간 로드가 실패하면 빈 화면이 됩니다. 메모리가 허용한다면 새 공간의 필수 에셋을 준비한 뒤 이전 공간을 해제하는 순서를 사용할 수 있습니다. 동시에 두 공간을 유지하기 어렵다면 실패 시 돌아갈 경량 대기 Scene을 별도로 둡니다.
다운로드와 로딩 진행률을 구분합니다
원격 Bundle 다운로드, 압축 해제, 에셋 생성과 Scene 활성화는 서로 다른 작업입니다. 하나의 비율로 단순 합치면 진행률이 오랫동안 멈추거나 거의 완료된 값에서 대기하는 화면이 생깁니다.
사용자에게는 세부 수치보다 현재 단계를 정확히 알려 주는 편이 낫습니다.
공간 정보 확인 중
필요한 콘텐츠 다운로드 중
공간 구성 중
입장 준비 중재방문으로 다운로드가 필요 없는 경우와 네트워크에서 새 Bundle을 받아야 하는 경우도 다르게 처리합니다. 다운로드 크기를 확인할 수 있다면 네트워크 단계에만 바이트 기준 진행률을 사용하고, Scene 로드에는 별도 상태를 표시합니다.
Handle의 수명과 에셋의 수명을 맞춥니다
Addressables는 로드 작업과 리소스 참조를 Handle로 추적합니다. 로드를 호출한 만큼 대응하는 해제가 필요합니다. Handle을 잃어버리면 필요 없는 에셋이 남고, 사용 중인 Handle을 너무 일찍 해제하면 다른 오브젝트가 참조하는 리소스에 문제가 생길 수 있습니다.
공간 전환 관리자는 자신이 획득한 Handle 목록을 보관하고 역순으로 정리할 수 있습니다. 전역 캐시가 Handle을 공유한다면 참조 횟수와 소유자를 별도로 기록해야 합니다.
해제를 호출했다고 메모리가 같은 프레임에 바로 줄어드는 것은 아닙니다. 의존 에셋이 다른 곳에서 사용 중일 수 있고 Unity의 정리 시점도 다릅니다. 메모리 사용량은 코드 추측보다 Addressables Event Viewer와 Memory Profiler 같은 도구로 확인해야 합니다.
의존 Bundle 중복을 빌드 단계에서 확인합니다
같은 재질이나 텍스처가 여러 그룹에 중복 포함되면 다운로드 크기와 메모리 사용량이 늘어날 수 있습니다. 공통 의존성을 별도 그룹으로 분리할 수 있지만, 너무 작은 Bundle이 많아지면 요청과 관리 비용이 증가합니다.
그룹 기준은 폴더 구조보다 배포와 수명 주기에 맞추는 편이 좋습니다. 함께 다운로드되고 함께 해제되는 에셋을 같은 경계에 둡니다. 자주 바뀌는 아바타 아이템과 거의 변하지 않는 공간 배경을 분리하면 작은 업데이트 때문에 큰 Bundle을 다시 받는 일을 줄일 수 있습니다.
원격 Catalog와 앱 코드의 호환성도 확인해야 합니다. 새 에셋 구조가 이전 앱 코드에서 해석되지 않는다면 콘텐츠만 교체해 해결할 수 없습니다. Manifest에 콘텐츠 스키마 버전과 최소 앱 버전을 두고 입장 전에 검사할 수 있습니다.
로드 완료와 공간 입장 완료는 다릅니다
Scene이 메모리에 올라왔다고 사용자가 바로 움직일 수 있는 것은 아닙니다. Spawn Point, 조명, NavMesh, 네트워크 Entity와 필수 UI가 준비돼야 합니다.
Content Scene은 준비 단계를 완료한 뒤 Ready 신호를 전환 관리자에게 전달합니다. 그전에는 로딩 화면을 유지하고 사용자 입력과 아바타 표시를 보류합니다.
네트워크 룸에는 이미 다른 사용자의 입장과 이동 이벤트가 들어올 수 있습니다. Scene이 준비되기 전 이벤트는 버전과 순서를 유지해 대기시키고, 전체 룸 Snapshot을 적용한 뒤 최신 이벤트를 반영합니다. 네트워크 연결과 Unity Scene 로드는 서로 다른 상태이므로 하나의 완료 Boolean으로 합치지 않는 편이 좋습니다.
보이는 오브젝트 수를 로딩 이후에도 줄입니다
에셋을 나눠 불러와도 공간 안의 모든 오브젝트를 항상 최고 품질로 렌더링하면 프레임과 메모리 문제가 남습니다. 거리에 따른 LOD, 가려진 오브젝트를 줄이는 Occlusion Culling과 반복 오브젝트의 Pooling을 함께 적용할 수 있습니다.
아바타나 상호작용 오브젝트처럼 동적으로 움직이는 대상은 정적 환경과 같은 방식으로 처리하기 어렵습니다. 멀리 있는 아바타의 메시와 애니메이션 갱신 빈도를 낮추더라도 소셜 상태와 상호작용 가능 여부는 별도 모델로 유지해야 합니다.
품질 변경은 한 프레임에 많은 에셋을 교체하지 않도록 분산합니다. 카메라 이동으로 여러 LOD가 동시에 바뀌거나 새 공간 진입 직후 Shader 준비가 몰리면 순간적인 프레임 저하가 생길 수 있습니다.
공간 전환은 로드와 해제의 한 쌍입니다
가상공간 로딩 최적화는 시작 시간을 줄이는 작업과 메모리를 회수하는 작업이 함께 있어야 완성됩니다. Bootstrap과 Content Scene의 책임을 나누고, Addressables Handle을 소유 범위별로 관리하며, 전환 요청의 세대를 확인해야 합니다.
비동기 로딩은 기다림을 없애지 않습니다. 다운로드, Unity 로딩과 네트워크 입장 단계를 분리해 사용자가 현재 무엇을 기다리는지 보여 주고, 실패하거나 취소됐을 때 이전 상태로 돌아갈 수 있게 만드는 것이 핵심입니다.
함께 읽기
- 실시간 소셜 룸 상태 관리: 입장·이탈·재접속 처리실시간 소셜 룸은 접속자 목록에 사용자를 추가하고 제거하는 기능만으로 유지되지 않습니다. 모바일 네트워크가 잠시 끊기거나 앱이 백그라운드로 가면 연결 종료 이벤트가 늦게 도착할 수 있고, 재접속한 사용자가 기존 아바타와 별도 참가자로 생성될 수도 있습니다.
- Unity 아바타 동기화: 위치·회전 보간과 네트워크 지터 처리Unity에서 원격 아바타의 위치를 네트워크로 받아 Transform에 바로 적용하면 움직임이 쉽게 끊깁니다. 화면은 매 프레임 렌더링되지만 위치 패킷은 더 낮은 빈도로 도착하고, 각 패킷의 간격도 일정하지 않기 때문입니다.
- 차량 상태 동기화: Push, WebSocket과 Polling을 함께 쓰는 이유커넥티드 카 앱의 차량 상태는 한 번 조회하고 끝나는 데이터가 아닙니다. 도어, 공조, 충전과 차량 온라인 상태는 앱 밖에서도 계속 바뀝니다. 사용자가 원격 명령을 실행하는 동안에도 이전 상태 이벤트와 새로운 조회 응답이 다른 순서로 도착할 수 있습니다.
- 커넥티드 카 원격 명령: 요청부터 차량 응답까지의 상태 관리커넥티드 카 앱에서 도어 잠금이나 공조 시작 버튼을 누르면 요청은 여러 시스템을 통과합니다. 앱이 서버 요청에 성공했다고 차량 기능까지 실행된 것은 아닙니다. 명령이 차량에 도착하고 제어 장치가 결과를 반환해야 비로소 실행 여부를 판단할 수 있습니다.
- BLE 차량 제어: GATT 연결과 저전력 통신을 안정화하는 방법BLE는 차량과 휴대폰이 가까이 있을 때 작은 데이터를 주고받는 데 적합합니다. 디지털 키, 근거리 도어 제어와 차량 식별처럼 연결 시간이 짧고 배터리 소비를 줄여야 하는 기능에서 자주 사용됩니다.