애셋번들 사용패턴 (AssetBundle usage patterns) 2- 번역

애셋번들 사용패턴 (AssetBundle usage patterns) – 번역

원문 링크

 

이전글 – 로드한 애셋번들 관리하기

4.2 배포 (Distribution)

기본적으로 프로젝트에서 클라이언트에게 애셋번들을 배포하는 두 가지 방법이 있습니다: 프로젝트가 설치되는동안 애셋번들을 같이 설치하는 방법과 프로젝트를 설치한 후에 배포하는 방법이 있습니다.

애셋번들을 프로그램(게임)과 함께 제공하거나 설치 후에 애셋번들을 제공하는 것은, 해당 프로젝트가 실행되는 플랫폼의 사양 및 제한사항에 의해서 결정됩니다.
일반적으로 모바일 프로젝트의 경우, 초기 설치 크기를 줄이고 무선 다운로드 크기의 제한 때문에 설치 후 다운로드 방식을 선택합니다.
콘솔 및 PC 프로젝트에서는 일반적으로, 애셋번들을 설치 프로그램과 함께 제공합니다.

애셋번들이 처음에 어떻게 전달되는지 여부에 관계없이, 프로젝트에 애셋번들을 새로 패치(patch)하거나 프로젝트가 설치된 후에 애셋번들을 통해서 변경된 콘텐츠를 적용하는 것이 가능하도록 아키텍쳐를 적절하게 설계하는 것이 중요합니다.
이에 대한 자세한 내용은 이 장의 애셋번들로 패치하기 섹션을 참고하시기 바랍니다.

 

4.2.1 프로젝트와 함께 배포하기 (Ship with project)

애셋번들을 프로젝트와 함께 배포하는 방법은 가장 간단한 배포 방법이라고 할 수 있습니다.
추가적으로 애셋번들을 다운로드 시키는 코드의 관리가 필요없기 때문입니다.
프로젝트에 애셋번들을 포함시키는 두 가지 주요 이유는 다음과 같습니다:

  • 첫째는 프로젝트의 빌드시간과 개발과정의 반복을 단순화시키기 위해서입니다. 애셋번들이 프로그램(게임)에 별도로 업데이트가 필요하지 않다면, Streaming Assets 폴더에 애셋번들을 저장해서 프로그램을 배포할 수 있습니다. Streaming Assets에 대한 내용은 아래 섹션을 참고하시기 바랍니다.
  • 둘째는 업데이트 가능한 콘텐츠의 초기 버전을 제공하기 위해서입니다. 이는 일반적으로 초기 설치 후 최종-사용자(end-user)의 시간을 절약하거나 나중에 있을 패치(patch)를 위한 기본 콘텐츠를 제공하기 위해서 사용됩니다. 이경우, Streaming Assets는 적합하지 않습니다. 하지만, 커스텀 다운로드 시스템이나 캐싱(caching) 시스템을 제작할 수 없는 경우라면, 업데이트 가능한 콘텐츠의 초기 버전을 Streaming Assets로부터 유니티에 로드해서 캐시에 저장할 수 있습니다.

 

4.2.1.1 Streaming Assets

유니티 프로그램(게임)의 설치 시 콘텐츠를 포함시키는 가장 쉬운 방법은, 유니티 프로젝트를 빌드하기 전에 /Assets/StreamingAssets/ 폴더에 해당 콘텐츠를 빌드하는 것입니다.
프로젝트를 빌드할때 StreamingAssets 폴더에 포함된 리소스는 모두 최종 프로그램(게임)에 복사됩니다.
이 폴더는 애셋번들 뿐만 아니라, 최종 프로그램에 모든 종류의 콘텐츠를 저장하는 목적으로도 사용할 수 있습니다.

로컬 저장소의 StreamingAssets 폴더의 전체 경로 값은, 런타임에 Application.streamingAssetsPath 속성을 통해서 접근할 수 있습니다.
그런 다음, 대부분의 플랫폼에서 애셋번들은 AssetBundle.LoadFromFile API를 통해서 로드할 수 있습니다.

안드로이드 개발자: 안드로이드에서 Application.streamingAssetsPath 값은 애셋번들이 압축되어 있는 경우에도, 압축된 .jar 파일 경로를 가리킵니다.
따라서, WWW.LoadFromCacheOrDownload을 사용해서 각 애셋번들을 로드해야 합니다.
물론, .jar 파일의 압축을 해제하고 읽기가능한 로컬 저장소에 애셋번들을 추출하는 커스텀 코드를 작성하는 것 역시 가능합니다.

노트: Streaming Assets 경로는 몇몇 플랫폼에서 쓰기가 불가능한 경로입니다. 어떤 프로젝트의 애셋번들이 설치 후에 업데이트가 필요한 경우, WWW.LoadFromCacheOrDownload를 사용하거나 커스텀 다운로더를 제작해야합니다. 이에 대한 더 자세한 내용은 커스텀 다운로더 섹션을 참고하시기 바랍니다.

 

4.2.2 설치 후에 다운로드하기 (Downloaded post-install)

모바일 장치에 애셋번들을 전달할때 많이 사용되는 방법은 앱 설치 후에 번들을 다운로드하는 것입니다.
또한 이 방법은, 사용자에게 프로그램 전체를 다시 다운로드 하도록 강제하지 않고, 설치 후 특정 콘텐츠를 업데이트 하거나 수정할 수 있는 방법이기도 합니다.
모바일 플랫폼에서, 어플리케이션 바이너리는, 비싸고 시간이 오래걸리는 재-인증 과정을 거쳐야 합니다.
따라서 설치 후에 다운로드할 수 있도록, 좋은 시스템을 개발하는 것이 중요합니다.

애셋번들을 전달하는 가장 간단한 방법은 웹 서버에 애셋번들을 올려두고, WWW.LoadFromCacheOrDownload 또는 UnityWebRequest를 통해서 전달하는 방법입니다.
해당 애셋번들이 캐시에 저장되어 있다면, 유니티는 자동으로 로컬 저장소의 캐시에 저장되어 있는 애셋번들을 로드합니다.
다운로드 받은 애셋번들이 LZMA 방식으로 압축되어 있는 경우, 나중에 로딩을 할 때 더 빠르게 로드를 하기위해서, 압축을 푼 상태로 캐시에 저장됩니다.
다운로드 받은 애셋번들이 LZ4 방식으로 압축된 경우, 애셋번들이 압축된 상태로 캐시에 저장됩니다.

캐시 저장소가 가득 찬 경우, 최근의 사용빈도가 가장 적은(LRU 방식, the least recently used) 애셋번들을 캐시에서 제거합니다.
이에 대한 더 자세한 내용은 내장 캐싱(Built-in Caching) 섹션을 참고하시기 바랍니다.

WWW.LoadFromCacheOrDownload에는 결함이 있다는 점을 주의하시기 바랍니다.
애셋번들 로드하기(Loading AssetBundles) 섹션에서 설명했듯이, WWW 오브젝트는 다운로드 과정에서 애셋번들의 크기와 동일한 크기의 메모리를 소모합니다.
이로 인해서 허용할 수 없는 메모리 스파이크(Memory Spike)가 발생할 수 있습니다. 이 문제를 피할 수 있는 세 가지 방법이 있습니다:

  • 애셋번들의 크기를 작게 유지합니다. 애셋번들의 최대 크기는 프로젝트에서 애셋번들을 다운로드할 때 사용할 수 있는 전체 메모리 크기에 따라서 결정됩니다. “다운로드 중” 화면을 포함하는 프로그램(게임)은 일반적으로, 백그라운드에서 애샛번들을 스트리밍하는 프로그램보다 애셋번들을 다운로드하는데 더 많은 메모리를 사용할 수 있는 경우가 많습니다.
  • 유니티 5.3 이상의 버전을 사용하는 경우, WWW.LoadFromCacheOrDownload 대신, UnityWebRequest API의 DownloadHandlerAssetBundle 기능의 사용을 권장합니다. DownloadHandlerAssetBundle는 다운로드 과정에서 메모리 스파이크를 발생시키지 않습니다.
  • 커스텀 다운로더를 제작합니다. 이에 대한 더 자세한 내용은 커스텀 다운로더(Custom Downloader) 섹션을 참고하시기 바랍니다.

유니티 5.2 이전 버전을 사용하는 경우에는 WWW.LoadFromCacheOrDownload를 사용하고,사용이 가능한 경우에는 UnityWebRequest를 사용하는 것을 권장합니다. 특정 프로젝트에서, 내장 API의 메모리 소비 수준, 캐싱(caching) 동작, 성능이 허용할 수 없는 경우 또는 해당 프로젝트에서 특정 플랫폼에서만 동작하는 코드가 필요한 경우에만 커스텀 다운로드 시스템 제작에 투자하시기 바랍니다.

UnityWebRequest 또는 WWW.LoadFromCacheOrDownload의 사용하지 않는 것을 고려해볼 수 있는 예:

  • 애셋번들 캐시에 대한 정밀한 제어가 필요한 경우
  • 프로젝트에서 커스텀 압축 전략을 구현해야하는 경우
  • 비활성화 상태에서 데이터를 스트리밍 하는 것과 같이, 요구사항을 충족시키기 위해서 특정 플랫폼에서만 동작하는 API를 사용해야 하는 경우

   – 예: iOS의 Background Task를 이용해서, 백그라운드에서 데이터를 다운로드하는 경우

  • 유니티에서 적절한 SSL을 지원하지 않는 플랫폼(PC와 같이)에서 SSL을 통해서 애셋번들을 전달해야 하는 경우

 

 

4.2.3 내장 캐싱 (Built-in Caching)

유니티에는 WWW.LoadFromCacheOrDownload 또는 UnityWebRequest API를 통해서 다운로드 받은 애셋번들을 캐시(Cache)에 저장할 수 있는, 애셋번들 캐싱 시스템이 내장되어 있습니다.

이 두 API는 모두, 애셋번들의 버전을 나타내는 숫자 정보를 매개변수로 전달할 수 있는 오버로드(Overloaded)된 메소드를 제공합니다.
이 숫자 정보는 애셋번들 안에 저장되지 않고, 애셋번들 시스템에 의해서 생성되지도 않습니다.

애셋번들 캐싱 시스템은 WWW.LoadFromCacheOrDownload 또는 UnityWebRequest를 통해서 전달된 마지막 버전 숫자만 기록하고 추적합니다.두 API에 버전 정보가 전달되어 호출되면, 캐싱 시스템은 해당 애셋번들이 캐시에 저장되어 있는지 확인합니다.

해당 애셋번들이 캐시에 저장되어 있으면, 애셋번드이 처음 캐시에 저장되었을 때 전달된 버전 숫자와 현재 호출되어 전달된 버전 숫자를 비교합니다.
이 숫자가 서로 일치하면, 캐싱 시스템은 캐시에 저장된 애셋번들을 로드합니다.

숫자들이 서로 일치하지 않거나, 캐시에 저장된 애셋번들이 없는 경우, 유니티는 새 복사본을 다운로드 합니다. 이렇게 다운로드된 새 사본은 새로운 버전 정보 숫자와 연결되어 캐시에 저장됩니다.

캐싱 시스템에 있는 애셋번들은, 해당 애셋번들을 다운로드할 때 사용한 전체 URL이 아닌, 애셋번들의 파일 이름으로 식별됩니다.
즉, 동일한 파일 이름을 가진 애셋번들이 여러 위치에 저장될 가능성이 있습니다.
예를 들어, 애셋번드은 CDN(Content Delivery Network)의 여러 서버에 배치될 수 있습니다. 파일 이름이 동일하기만 하면, 캐싱 시스템은 모두 동일한 애셋번들로 인식합니다.

애셋번들에 버전 숫자를 할당하기 위한 적절한 규칙(전략)을 결정하고 이렇게 결정한 버전 숫자를 WWW.LoadFromCacheOrDownload에 전달하는 것은 전적으로 각 개별 프로그램(게임)의 몫입니다.
대부분의 프로그램(게임)에서 유니티 5의 AssetBundleManifest API를 사용할 수 있습니다.
이 API는 애셋번들이 포함하고 있는 콘텐츠의 MD5 해시(hash)값 계산을 통해서 각 애셋번들의 버전 정보를 생성합니다.
애셋번들이 변경되면, 애셋번들의 해시 값도 변경되고, 이 정보를 통해서 애셋번들을 새로 다운로드 해야 하는지 여부를 판단할 수 있습니다.

노트: 유니티 내장 캐시 구현의 특이사항으로 인해서, 캐시 저장소가 가득찰 때까지 오래된(기존의) 애셋번들이 삭제되지 않습니다. 유니티는 이 문제를 인식하고, 차후 릴리즈될 버전에서 이 특이사항을 다룰 예정입니다.

애셋번들로 패치하기 (Patching with AssetBundles) 섹션에서 자세한 정보를 확인하기 바랍니다.

Caching 오브젝트의 API 호출을 통해서 유니티 내장 캐싱 시스템을 제어할 수 있습니다.
Caching.expirationDelay와 Caching.maximumAvailableDiskSpace 값을 변경해서, 유니티 캐시의 동작을 제어할 수 있습니다.

Caching.expirationDelay는 애셋번들이 자동으로 삭제되기 전까지 경과해야(지나야)하는 최소 시간(단위:초)을 나타냅니다.
이 값에 설정된 시간동안 애셋번들을 사용하지 않으면, 자동으로 삭제됩니다.

Caching.maximumAvailableDiskSpace는 로컬 저장소에서 캐시를 저장하는데 사용할 최대 공간을 결정합니다.
expirationDelay에 설정된 시간에 비해서 사용했던 시점이 가장 오래된 애셋번들이 삭제됩니다.
공간의 크기는 바이트 단위로 계산합니다. 용량 한도에 도달하면, 유니티는 열어본지(또는 Caching.MarkAsUsed를 통해서 사용했다고 표시된) 가장 오래된 애셋번들을 먼저 캐시에서 삭제합니다.
유니티는 새로 애셋번들을 다운로드 하기 충분한 용량이 확보될 때까지 캐시를 삭제합니다.

노트: 현재 유니티 5.3 버전까지 내장 유니티 캐시를 제어하는 것이 매우 힘들어졌습니다. 캐시에서 특정 애셋번들을 삭제하는 삭제하는 것이 불가능합니다.
기간이 만료(expiration)되거나 캐시 저장 공간이 가득 차거나 Caching.CleanCache를 호출하는 경우에만 캐시가 삭제됩니다(Caching.CleanCache는 캐시에 저장되어 있는 모든 애셋번들을 삭제합니다.).

이는 어플리케이션(게임)에서 더 이상 사용하지 않는 애셋번들을 자동으로 삭제하지 않기 때문에, 개발 중 또는 라이브 서비스 중에 문제가 될 수 있습니다.

 

4.2.3.1 캐시 초기화 (Cache Primiing)

애셋번들의 파일 이름으로 애셋번들이 식별 되기 때문에, 어플리케이션(게임)과 함께 배포되는 애셋번들로 캐시를 “초기화”할 수 있습니다.
이렇게 하려면, 초기 버전 또는 기본 버전의 애셋번들을 /Assets/StreamingAssets/ 폴더에 저장해야 합니다.
이 과정은 프로젝트와 함께 배포하기 섹션에 설명된 과정과 동일합니다.

어플리케이션이 처음 실행될 때 Application.streamingAssetsPath로부터 애셋번들을 로딩해서 캐시를 채울 수 있습니다.
그런 다음, 일반적으로 WWW.LoadFromCacheOrDownload 또는 UnityWebRequest를 사용해서 애셋번들을 로드합니다.

 

4.2.4 커스텀 다운로더 (Custom Downloaders)

커스텀 다운로더를 제작하면, 애셋번들을 다운로드하는 방법, 애셋번들의 압축 해제 방법, 저장 방법을 모두 완전히 제어할 수 있습니다.
커스텀 다운로더의 제작은 야심작을 위해 대규모 팀을 구성할 수 있는 경우에만 권장합니다.
커스텀 다운로더를 제작할 때 고려해야하는 4가지 주요 문제점이 있습니다:

– 애셋번들을 다운로드하는 방법
– 애셋번들을 저장할 위치
– 애셋번들의 압축 여부 및 압축 방법
– 애셋번들을 패치(patch)하는 방법

애셋번들을 패치하는 방법에 대한 자세한 내용은, 애셋번들을 이용해서 패치하기(Patching with AssetBundles) 섹션을 참고하시기 바랍니다.

 

4.2.4.1 다운로드하기 (Downloading)

대부분의 어플리케이션에서, 애셋번들을 다운로드하는 가장 간단한 방법은 HTTP를 사용하는 것입니다.
하지만, HTTP기반 다운로더를 구현하는 작업은 그리 만만하지 않습니다.
커스텀 다운로더는 과도한 메모리 할당, 과도한 쓰레드 사용, 과도한 쓰레드 wakeup을 반드시 피해야합니다.
유니티의 WWW클래스는, 애셋번들 기초 장의 3.4.3 섹션에서, 모두 설명한 그 이유때문에 부적합 합니다.
WWW의 높은 메모리 비용으로 인해서, 어플리케이션에서 WWW.LoadFromCacheOrDownload.를 사용하지 않는 경우, 유니티의 WWW 클래스의 사용을 피하는 것이 좋습니다.

커스텀 다운로더를 제작할 때 3가지 옵션이 있습니다:

  • C#의 HttpWebRequest와 WebClient 클래스를 사용하는 방법
  • 커스텀 네이티브 플러그인(Custom Native Plugins)을 사용하는 방법
  • 애샛 스토어 패키지를 활용하는 방법

 

4.2.4.1.1 C# 클래스 (C# Classes)

HTTPS/SSL 지원을 요구하지 않는 어플리케이션의 경우, C#의 WebClient 클래스를 이용해서 가장 단순하게 애셋번들을 다운로드하는 기능을 제공할 수 있습니다.
이 클래스를 이용하면, 과도한 Managed 메모리(C# 메모리)의 할당 없이 직접 모든 파일을, 비동기 방식으로 로컬 저장소에 다운로드할 수 있습니다.

WebClient를 이용해서 애셋번들을 다운로드 하려면, 클래스의 인스턴스를 할당하고 다운로드할 애셋번들의 URL과 다운로드 대상 경로를 전달하면 됩니다.
다운로드 요청(Request) 매개변수에 대한 제어가 더 많이 필요한 경우, C#의 HttpWebRequest 클래스를 사용해서 다운로더를 제작할 수 있습니다:

  1. HttpWebResponse.GetResponseStream으로부터 바이트 스트림(Byte Stream)을 얻습니다.
  2. 스택(Stack)에 고정-크기 바이트 버퍼를 할당합니다.
  3. 응답 스트림(Response Stream) 버퍼로 읽어옵니다.
  4. C#의 File.IO API 또는 다른 종류의 스트리밍 IO 시스템을 이용해서 버퍼를 디스크에 씁니다(저장).

플랫폼 노트: iOS, 안드로이드, 윈도우즈 폰(Windows Phone)은, 유니티의 C# 런타임이 C# HTTP 클래스에 대한 HTTPS/SSL을 지원하는 유일한 플랫폼 입니다.
PC에서는, C#의 클래스를 통해서 HTTPS 서버에 접속을 시도하면, 인증서 유효성 검사에 실패합니다.

 

4.2.4.1.2 애샛 스토어 패키지 (Asset Store Package)

애셋 스토어 패키지 중 여럿이, 네이티브-코드(Native-Code)로 구현된, HTTP, HTTPS 또는 그외의 다른 프로토콜을 통해서 파일을 다운로드하는 기능을 제공합니다. 유니티에서 사용할 네이티브-코드 플러그인을 제작하기 전에, 애셋 스토어 패키지를 테스트해보기를 권장합니다.

 

4.2.4.1.3 커스텀 네이티브 플러그인 (Custom Native Plugins)

커스텀 네이티브 플러그인을 제작하는 과정을 가장 시간이 많이 걸리는 작업이지만, 유니티에서 데이터를 다운로드 하는데 가장 유연한 기능을 제공하는 방법이기도 합니다. 요구되는 프로그래밍-시간이 오래걸리고 높은 기술적 위험때문에, 어플리케이션에 요구되는 기능을 충족할 수 있는 다른 방법이 없는 경우에만 권장됩니다. 예를 들어, 윈도우즈, OSX, 리눅스와 같이, 유니티에서 C# SSL이 지원되지 않는 플랫폼에서 SSL 통신을 사용해야만하는 경우에는 커스텀 네이티브 플러그인이 필요할 수 있습니다.

커스텀 네이티브 플러그인은 일반적으로, 타겟 플랫폼의 Native 다운로드 API를 래핑(Wrapping)합니다. 예를 들면, iOS의 NSURLConnection과 안드로이드의 java.net.HttpURLConnection등이 있습니다. 이러한 API 사용에 대한 자세한 내용은 해당 플랫폼의 문서를 참고하시기 바랍니다.

 

4.2.4.2 저장소 (Storage)

모든 플랫폼에서, Application.persistentDataPath는 어플리케이션이 실행되는 동안에 유지해야하는 데이터를 저장하는데 사용할, 쓰기가 가능한 위치를 가리킵니다. 커스텀 다운로더(Custom Downloader)를 제작할때, Application.persistentDataPath 경로의 하위 디렉토리를 사용해서 다운로드한 데이터를 저장하는 것을 권장합니다.

Application.streamingAssetPath는 쓰기가 불가능하며, 애셋번들 캐시 용도로 적합하지 않습니다. Application.streamingAssetPath의 각 플랫폼별 위치의 예는 다음과 같습니다:

  • OSX: .app 패키지 내부; 쓰기 불가능
  • 윈도우즈: 설치 디렉토리 내부(예: Program Files); 대부분 쓰기 불가능
  • iOS: .ipa 패키지 내부; 쓰기 불가능
  • 안드로이드: 압축된 .jar 파일 내부; 쓰기 불가능

 

내용 끝까지 읽어주셔서 감사합니다.
배너 클릭은 저에게 많은 힘이 됩니다.
감사합니다 🙂

 

다음글 – 애셋 할당 전략

RonnieJ

프리랜서 IT강사로 활동하고 있습니다. 게임 개발, C++/C#, 1인 기업에 관심이 많습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

Please turn AdBlock off

Notice for AdBlock users

Please turn AdBlock off