이 글은 유니티 튜토리얼을 번역한 글 입니다. 원문은 여기에서 확인하실 수 있습니다.

이 글은 총 5개의 챕터로 구성되며 다음과 같습니다.

  1. [번역] 에셋번들과 리소스에 대한 가이드
  2. [번역] 에셋과 오브젝트, 그리고 직렬화
  3. [번역] RESOURCES 폴더
  4. [번역] 에셋번들 기초
  5. [번역] 에셋번들 사용 패턴




이 글은 [번역] 에셋번들과 리소스에 대한 가이드 시리즈의 두번째 챕터입니다.

이 챕터에서는 유니티의 직렬화 시스템에 대해 심도있게 다루고, 유니티 에디터에서 그리고 실행시(runtime)에 다른 오브젝트 간의 참조를 어떻게 유지하는지에 대해 이야기 합니다. 또한 여기에서는 오브젝트(Objects)와 에셋(Assets) 간의 기술적인 차이점에 대해서도 이야기 합니다. 여기에서 다루는 주제는 유니티에서 어떻게 효율적으로 에셋을 로드하고 언로드하는지에 대한 근본적인 이해를 다룹니다. 적절한 에셋 관리는 로딩 시간을 줄이고 메모리를 적게 사용하는 데에 있어 필수적인 요소입니다.

1.1. 에셋과 오브젝트의 내부(Inside Assets and Objects)

유니티에서 어떻게 데이터를 적절히 다루는지를 이해하기 위해, 유니티가 어떻게 데이터를 구별하고(identifies)1 직렬화하는지를 이해하는 것이 매우 중요합니다. 첫번째 핵심은 Asset과 UnityEngine.Object를 구별하는 것입니다.

에셋(Asset)은 유니티 프로젝트의 Assets 폴더에 저장되어 있는 디스크 상의 한 파일입니다. 예를들어 텍스쳐 파일들, 매터리얼 파일들, FBX 파일들이 모두 에셋입니다. 매터리얼과 같은 일부 에셋은 유니티의 네이티브 포맷 데이터를 포함하고 있습니다. FBX 파일 같은 다른 에셋들은 네이티브 포맷으로의 변환 작업이 필요합니다.

오브젝트(UnityEngine.Object) 또는 Object(대문자 O로 시작해야 함)는 리소스의 특정 인스턴스를 표현하는 직렬화된 데이터들의 집합입니다. 이는 유니티에서 사용하는 어떤 리소스 타입도 가능한데, 예를 들어, 메쉬(mesh), 스프라이트(sprite), AudioClip, AnimationClip 등이 있습니다. 모든 종류의 오브젝트는 UnityEngine.Object를 상속받은 하위 클래스입니다.

대부분의 오브젝트 타입은 유니티에 이미 만들어져 있는 타입(built-in)이지만, 2개의 특별한 타입 또한 존재합니다.

  1. ScriptableObject는 개발자들이 자신만의 데이터 타입을 정의할 수 있게 해주는 편리한 시스템입니다. 이러한 타입은 유니티에서 네이티브적으로 직렬화(serialized) 또는 역직렬화(deserialized)할 수 있고, 유니티 에디터의 인스펙터 창에서 조작할 수 있습니다.
  2. MonoBehaviourMonoScript로의 링크를 가지는 래퍼(wrapper)2를 제공합니다. MonoScript는 내부 데이터 타입인데, 유니티는 특정 스크립팅 클래스가 특정 어셈블리와 네임스페이스에 대한 참조를 유지하는데에 이 MonoScript를 사용합니다. MonoScript는 실질적인 실행 가능한 코드를 전혀 포함하지 않고 있습니다.

에셋과 오브젝트 간에는 일대다(one-to-many)의 관계가 있습니다 : 이 말은 어떤 하나의 에셋 파일이 한 개 이상의 오브젝트를 가진다는 말입니다.

1.2. 오브젝트 내부의 참조들(Inter-Object References)

모든 UnityEngine.Object는 다른 UnityEngine.Object에 대한 참조를 가질 수 있습니다. 여기에서 참조가 되는 오브젝트는 같은 에셋 파일 내부에 있을 수도 있고, 다른 에셋 파일에서 임포트될 수도 있습니다. 예를들어 하나의 매터리얼(Material) 객체는 일반적으로 한개 이상의 텍스쳐(Texture) 객체에 대한 참조를 가지고 있습니다. 또 이 텍스쳐 객체들은 일반적으로 한개 이상의 텍스쳐 에셋 파일(PNG나 JPG같이)로 부터 임포트됩니다.

직렬화되었을 때 이 참조들은 두 개의 데이터 조각으로 구성됩니다 : File GUID와 Local ID로 말입니다. File GUID는 에셋 파일의 타겟 리소스가 어디에 저장되어 있는지를 가리키기 위해 사용됩니다. 내부적으로 유일한 Local ID는 에셋 파일 내의 각 오브젝트를 가리키는데, 이는 하나의 에셋 파일에 여러개의 오브젝트가 있을 수 있기 때문입니다.

File GUID는 .meta 파일에 저장되어 있습니다. 이 .meta 파일은 유니티가 처음 어떤 에셋을 임포트할 때 생성이 되는데 그 에셋이 있는 같은 폴더에 저장됩니다.

위에서 말한 식별화(identification)와 참조 시스템(referencing system)은 텍스트 에디터에서 확인이 가능합니다 : 새 유니티 프로젝트를 생성하고 Editor Settings에서 Visible Meta Files가 나오도록 설정하고, 에셋을 텍스트로 직렬화하도록 설정합니다. 매터리얼을 생성하고 프로젝트에 텍스쳐를 하나 임포트합니다. 이 매터리얼을 씬(Scene)에 있는 큐브에 할당하고 씬을 저장합니다.

텍스트 에디터를 이용해서 이 매터리얼과 관련된 .meta 파일을 엽니다. 파일의 거의 제일 윗 부분에 "guid"라 적혀 있는 줄을 볼 수 있을 것입니다. 이 줄이 매터리얼 에셋의 File GUID를 정의하는 줄입니다. Local ID를 찾으려면 매터리얼 파일을 텍스트 에디터에서 여십시오. 매터리얼 객체의 정의는 다음과 같을 것입니다.

--- !u!21 &2100000
Material:
 serializedVersion: 3
 ... more data ...

위의 예에서 앰퍼샌드( &)로 시작하는 숫자가 이 매터리얼의 Local ID입니다. 만약 이 매터리얼 객체가 File GUID "abcdefg"인 에셋 안에 위치해 있다면, 이 매터리얼 객체는 File GUID "abcdefg"와 Local ID "2100000"을 조합해서 유일한 식별자를 가질 수 있습니다.

1.3. 왜 File GUID와 Local ID인가?

왜 유니티의 File GUID와 Local ID 시스템이 필요한 것일까요? 그에 대한 답은 강건함(robustness)과 플랫폼에 독립적인 워크 플로우를 제공하기 위해서 입니다.

File GUID는 한 파일의 특정 위치에 대한 추상화를 제공합니다. 특정 File GUID가 특정 파일과 연관이 있을 수 있는 한, 디스크 상에서의 그 파일의 위치는 무관해집니다. 그렇기 때문에 한 파일은 그 파일을 참조하고 있는 모든 오브젝트를 업데이트할 필요없이 마음대로 옮길 수 있습니다.

어떤 에셋 파일은 여러 개의 UnityEngine.Object 리소스를 가질 수 있기 때문에, Local ID는 각 오브젝트들을 명확하게 구분하는 데에 필요합니다.

어떤 에셋 파일과 관련된 File GUID를 잃어버리게 된다면, 그 에셋 파일에 있는 모든 오브젝트에 대한 참조를 잃어버리게 됩니다. 이러한 이유 때문에 .meta 파일이 같은 폴더에 같은 파일명으로 저장되어야만 하는 것입니다. 유니티는 삭제되거나 위치가 잘 못된 .meta 파일을 재생성한다는 것을 알아두면 좋겠습니다.

유니티 에디터는 특정 파일 경로가 File GUID를 가리키고 있는 맵(map)을 하나 가지고 있습니다. 맵의 각 항목은 에셋이 로드되거나 임포트될 때마다 기록되어집니다. 맵 항목은 에셋의 특정 경로와 그 에셋의 File GUID 간의 링크를 가지고 있습니다. 만약 유니티 에디터가 켜졌을 때 .meta 파일이 없어진 상태이고 그 에셋의 경로가 바뀌지 않았다면, 에디터는 해당 에셋이 같은 File GUID를 보유하고 있다고 확신하게 됩니다.

유니티 에디터가 닫힐 때 .meta 파일이 없어진 상태거나, 에셋의 경로는 바뀌었는데 .meta 파일은 해당 에셋과 같은 경로로 이동하지 않았다면, 해당 에셋에 있는 모든 객체에 대한 참조는 깨지게 됩니다.

1.4. 복합 에셋(Composite Assets)과 임포터(Importers)

에셋과 오브젝트의 내부에서 이야기했듯이, 네이티브가 아닌 타입의 에셋은 유니티에 임포트되어져야만 합니다. 이는 에셋 임포터를 통해서 이루어 집니다. 이러한 임포터는 보통은 자동으로 호출되어지지만, AssetImporter API와 AssetImporter의 하위 클래스를 통해서 스크립트로도 노출되어져 있습니다. 예를 들면, TextureImporter API는 PNG나 JPG같은 개별 텍스쳐 에셋을 임포트할 때 사용되는 셋팅에 대한 접근을 제공합니다.

임포트 프로세스의 결과는 한 개 이상의 UnityEngine.Object 입니다. 이들은 부모 에셋(parent Asset) 안에 있는 여러 개의 서브 에셋(sub-assets) 형태로 유니티 에디터에서 볼 수 있는데, 예를 들면 sprite atlas로 임포트되어진 하나의 텍스쳐 에셋에 보면 여러개의 sprite들이 있는 것을 알 수 있습니다. 이런 오브젝트들은 각각 하나의 File GUID를 공유하게 되는데, 이는 이들의 소스 데이터가 같은 에셋 파일에 저장되어 있기 때문입니다. 이들은 임포트된 텍스쳐 에셋 내에서 Local ID를 이용해 구분되어 집니다.

임포트 프로세스는 소스 에셋을 유니티 에디터에서 선택한 타겟 플랫폼에 맞는 포맷으로 변환합니다. 임포트 프로세스에는 텍스쳐 압축과 같은 매우 무거운 작업들을 포함시킬 수도 있습니다. 유니티 에디터가 열릴 때마다 임포트 프로세스를 수행하는 것은 엄청나게 비효율적일 수 있습니다.

대신에, 임포트된 에셋의 결과물은 Library 폴더에 캐쉬됩니다. 특히 임포트 프로세스의 결과물은 에셋의 File GUID 의 앞 두자리로 된 폴더에 저장됩니다. 이 폴더는 Library/metadata/ 폴더 안에 있습니다. 각 오브젝트는 하나의 바이너리 파일로 직렬화되는데, 이 파일의 이름은 에셋의 File GUID와 동일합니다.

이 내용은 네이티브가 아닌 에셋(non-native Assets)에 대해서만이 아닌 모든 에셋에 대해서 적용되는 내용입니다. 하지만, 네이티브 에셋의 경우는 긴 변환 프로세스나 재직렬화(re-serialization) 작업이 필요하지는 않습니다.

1.5. 직렬화(Serialization)와 인스턴스(Instances)3

File GUID와 Local ID는 강건하지만, GUID 비교는 느리고 런타임에 더 높은 퍼포먼스를 가진 시스템이 필요합니다. 유니티는 내부적으로 캐쉬를 유지하고 있는데, 이는 File GUID와 Local ID를 하나의 세션 동안에만 유일함이 보장되는 간단한 정수로 변환합니다. 이를 Instance ID라 부르고, 새로운 오브젝트가 캐쉬에 등록될 때 단조롭게 증가되는(monotonically-increasing) 값입니다.

이 캐쉬는 오브젝트의 소스 데이터 위치를 정의하는 Instance ID, File GUID, Local ID 와 메모리 상의 오브젝트 인스턴스 사이의 매핑을 가지고 있습니다. 이로 인해 UnityEngine.Object가 서로 간의 참조를 강건하게 유지할 수 있게 됩니다. Instance ID 참조를 제거하는 것은 해당 Instance ID를 가지고 있는 로드된 오브젝트를 바로 반환하는 결과를 낳을 수 있습니다. 만약 타겟 오브젝트가 아직 로드되지 않았다면, 해당 File GUID와 Local ID를 이용해 오브젝트의 소스 데이터를 확인할 수 있고, 유니티는 그 때 곧바로 해당 오브젝트를 로드할 수 있습니다.

시작 시점에, Instance ID 캐쉬는 해당 프로젝트에 들어 있는(예를 들면, 씬 내부에 참조되어 있는 오브젝트) 모든 오브젝트와 Resources 폴더에 있는 모든 오브젝트에 대한 데이터를 가지고 초기화되어 집니다. 추가적인 캐쉬는 런타임에 새로운 에셋이 임포트되거나 에셋번들(AssetBundle)을 통해서 오브젝트가 로드될 때 생성됩니다. Instance ID 항목은 필요없어 질 경우에만 캐쉬에서 제거됩니다. 이는 특정 File GUID와 Local ID에 대한 접근을 제공하는 에셋번들이 언로드될 때 발생합니다.

에셋번들을 언로드함으로써 Instance ID가 쓸모없게 되어 버리면, Instance ID와 File GUID, Local ID 간의 매핑은 메모리 절약을 위해 지워지게 됩니다. 만약 에셋번들이 다시 로드되면, 해당 에셋번들에서 로드된 각 오브젝트에 새로운 Instance ID가 할당되게 됩니다.

에셋번들의 언로딩에 관련된 더 심도있는 토론을 보고 싶다면, 에셋번들 사용 패턴 글에서 로드된 에셋의 관리 섹션을 보시면 됩니다.

특정 플랫폼에서 어떤 이벤트들이 오브젝트들을 강제로 메모리 상에서 제거하는 일도 있습니다. 예를 들어, iOS에서 앱이 중지됐을 때(suspended) 그래픽 에셋이 그래픽 메모리에서 언로드될 수 있습니다. 만약 이 오브젝트들이 에셋번들에서 로드한 객체이고 그 에셋번들이 이미 언로드된 상태라면, 유니티는 해당 오브젝트에 대한 소스 데이터를 다시 로드할 수 없을 것 입니다. 또한 언로드된 오브젝트를 참조하고 있는 다른 살아있는 오브젝트 또한 제대로 동작할 수 없는 상태가 됩니다. 앞선 예에서, 메쉬나 모델의 경우는 보이지 않게 되고, 텍스쳐와 매터리얼의 경우에는 보라색(magenta)으로 칠해지게 됩니다.

구현 노트 : 런타임에 위의 제어 흐름은 완전히 정확하게 발생하는 것은 아닙니다. 런타임에 File GUID와 Local ID를 비교하는 것은 무거운 로딩 작업을 할 때 좋지 않은 퍼포먼스를 낼 수도 있습니다. 유니티 프로젝트를 빌드할 때, File GUID와 Local ID는 결정적으로 더 간단한 포맷으로 매핑되어 집니다. 하지만, 위에서 말한 컨셉은 그대로 유지되고, 런타임에 File GUID와 Local ID에 대해 고려하는 것은 유용할 수 있습니다.

이러한 이유로 런타임에 에셋의 File GUID를 알아낼 수 없습니다.

1.6. MonoScripts

하나의 MonoBehavior는 하나의 MonoScript에 대한 참조를 가지고 있고, MonoScript는 단순히 특정 스크립트 클래스를 위치시키기 위한 정보만 가지고 있다는 것을 이해해야 합니다. 어떤 타입의 오브젝트도 스크립트 클래스에 대한 실행 가능한 코드를 가지고 있지는 않습니다.

MonoScript는 3개의 문자열을 가지고 있습니다 : 어셈블리 이름, 클래스 이름, 네임 스페이스

프로젝트를 빌드할 때, 유니티는 에셋 폴더에 있는 스크립트 파일들을 모으고, 이들을 Mono 어셈블리에 컴파일합니다. 특히, 유니티는 Assets 폴더 안에 있는 각 언어별로 어셈블리를 생성하게 되고, 각각의 언어별 어셈블리들은 Assets/Plugins 폴더 아래에 위치하게 됩니다. Plugins 하위 폴더의 밖에 있는 C# 스크립트는 Assembly-CSharp.dll에 위치하게 되고, Plugins 하위 폴더 안에 있는 스크립트는 Assembly-CSharp-firstpass.dll에 위치하게 됩니다.

이런 어셈블리 파일들은 (미리 빌드된 어셈블리 DLL도 포함해서) 유니티 애플리케이션의 최종 빌드에 포함되게 되고, 이들을 MonoScript가 참조하게 됩니다. 다른 리소스와는 다르게, 유니티 애플리케이션에 포함된 모든 어셈블리들은 애플리케이션이 최초 실행될 때 로드됩니다.

이런 MonoScript 오브젝트가 있기 때문에, 에셋번들(또는 Scene이나 Prefab)이 실행 가능한 코드를 어떤 MonoBehavior Component에도 포함시키지 않는 이유가 됩니다. 그렇게 때문에 MonoBehavior들이 서로 다른 에셋번들에 위치해 있더라도, 각각의 MonoBehavior들이 특정 공유 클래스를 참조할 수 있는 것입니다.

1.7. 리소스 생명주기(Resource Lifecycle)

UnityEngine.Object는 어떤 정해진 시간에 메모리에 로드되고 언로드됩니다. 로딩 시간을 줄이고 애플리케이션의 메모리 공간을 관리하기 위해서 UnityEngine.Object의 리소스 생성주기를 이해하는 것은 중요합니다.

UnityEngine.Object를 로드하는 데에는 두 가지 방법이 있습니다 : 자동이거나 명시적이거나. Instance ID가 매핑되었는데, 해당 오브젝트의 참조가 끊어졌고, 그 오브젝트가 메모리에 로드되지 않은 상태이며, 그 오브젝트의 소스 데이터를 로드할 수 있는 상태일 때에 해당 오브젝트는 자동으로 로드되게 됩니다. 반면에 명시적으로 로드할 때에는 직접 오브젝트를 생성하거나 리소스 로딩 API(예를 들면, AssetBundle.LoadAsset)를 사용하는 방법이 있습니다.

오브젝트가 로드될 때, 유니티는 각 참조에 대한 File GUID와 Local ID를 Instance ID로 변환함으로써 해당 오브젝트의 참조를 연결하게 됩니다.

아래의 2가지 항목이 만족되면서 Instance ID가 처음으로 역참조(dereferenced)될 때, 그 객체가 로드되어 집니다.

  1. Instance ID가 아직 로드되지 않은 오브젝트를 참조할 때
  2. Instance ID가 캐쉬에 등록된 유효한 File GUID와 Local ID를 가지고 있을 때

일반적으로 참조가 로드되고 연결되는데에는 굉장히 짧은 시간이 걸립니다.

만약 File GUID와 Local ID가 Instance ID를 가지고 있지 않거나, 언로드된 오브젝트의 Instance ID가 유효하지 않은 File GUID와 Local ID를 참조하고 있다면, 참조는 유지가 되지만 실제 오브젝트는 로드되지 않습니다. 이는 유니티 에디터 상에서 "(Missing)" 참조로 보여질 것 입니다. 동작 중인 애플리케이션에서나 Scene 뷰에서 "(Missing)" 오브젝트는 타입에 따라 다른 방식으로 보여질 것 입니다 : 메쉬는 보이지 않고, 텍스쳐는 magenta 색상으로 보이는 등 말이죠.

오브젝트는 3가지의 특정 시나리오대로 언로드 됩니다.

  1. 사용하지 않는 에셋에 대한 청소(cleanup)가 발생할 때 오브젝트는 자동으로 언로드됩니다. 이 프로세스는 scene이 변경되거나(Application.LoadLevel 처럼 additive 방식이 아닌 씬 변경 API를 사용하는 경우), 스크립트가 Resources.UnloadUnusedAssets API를 호출함으로써 발생합니다. 이 프로세스는 참조되지 않은 오브젝트만 언로드합니다 : 아무 Mono 변수도 해당 오브젝트를 참조하지 않고, 그 오브젝트를 참조하고 있는 다른 살아있는 오브젝트가 하나도 없는 경우에 그 오브젝트는 언로드됩니다.
  2. Resources 폴더에서 생성한 오브젝트는 Resources.UnloadAsset API를 호출함으로써 명시적으로 언로드될 수 있습니다. 이러한 오브젝트의 Instance ID는 유효한 상태로 남아있게 되고, 유효한 File GUID와 Local ID 항목을 포함하게 됩니다. 만약 Resources.UnloadAsset을 이용해서 언로드한 오브젝트에 대한 참조를 어떤 Mono 변수나 다른 오브젝트가 가지고 있다면, 살아있는 참조에 대한 역참조가 발생하자마자 언로드한 오브젝트는 다시 로드되게 됩니다.
  3. Asset Bundle에서 생성한 오브젝트는 AssetBundle.Unload API를 호출하면 즉시 자동 언로드되게 됩니다. 이는 해당 오브젝트의 Instance ID의 File GUID와 Local ID에 대한 참조를 무효화시키고(invalidates), 언로드한 오브젝트를 참조하고 있는 살아있는 어떤 참조들은 "(Missing)" 참조 상태가 될 것 입니다. C# 스크립트에서 언로드한 오브젝트의 메서드나 프로퍼티에 접근하려고 시도하면 NullReferenceException을 발생시키게 됩니다.

만약 AssetBundle.Unload 가 호출되면, 언로드 된 에셋번들에서 생성된 살아있는 오브젝트들은 파괴되지 않고, 유니티는 그런 오브젝트의 Instance ID에 대한 File GUID와 Local ID 참조를 무효화시킵니다. 만약 이런 오브젝트가 나중에 메모리에서 언로드되면 유니티에서는 이런 오브젝트들을 리로드할 수 없게 되고, 언로드된 오브젝트에 대한 살아있는 참조는 여전히 남아있게 됩니다.

1.8. 큰 Hierarchies를 로드하는 것(Loading Large Hierarchies)4

유니티 GameObject의 hierarchies를 직렬화할 때(예를 들어 Prefab), hierarchy 전체가 직렬화된다는 사실을 알아야 합니다. 그 말인 즉슨, 그 hierarchy에 있는 모든 GameObject와 Component들은 개별적으로 직렬화된 데이터 형태로 표현이 된다는 말입니다. 이는 GameObject의 hierarchy를 로드하고 생성하는데에 걸리는 시간에 흥미로운 영향을 미칩니다.

어떤 코드 덩어리가 같은 갯수의 최종 GameObject를 생성한다고 가정하면, 매우 큰 hierarchy를 가지는 하나의 prefab을 생성하는 것이 작은 오브젝트를 각각 생성해서 런타임에 조립하는 것보다 더 많은 CPU 시간을 소모합니다.

deep profiling5 자료를 분석해보면 GameObject를 생성하고 깨우는데에(awaken) 위의 두 시나리오가 거의 같습니다. 하나의 큰 hierarchy를 가지는 prefab의 경우가 아주 약간의 CPU 시간에 있어서 이득을 가집니다(trampolining6과 SendTransformChanged 콜백 때문에). 하지만 이런 약간의 시간 절약이 데이터를 읽고 역직렬화하는 데에 사용되는 시간에 있어서 매우 중요합니다.

앞에서 말했듯이, 단일 prefab을 직렬화할 때 각각의 모든 GameObject와 컴포넌트의 데이터가 개별적으로 직렬화됩니다 - 데이터가 중복인 경우에도 개별적으로 되기는 마찬가지 입니다. 30개의 동일한 요소가 있는 UI 화면은 그 동일한 요소를 30번 직렬화할 것 입니다. 이는 아주 큰 크기의 바이너리 데이터를 생성하게 됩니다. 로드 시점에 이 중복된 30개의 요소에 대한 모든 GameObject와 Component를 위한 데이터는 새로 생성된 데이터에 전송되기 전에 디스크에서 읽어야 합니다. 이렇게 큰 prefab을 생성하는데에 가장 많은 영향을 미치는 부분이 파일을 읽는 시간입니다.

유니티가 중첩된(nested) prefab을 지원하지 전까지는, 매우 큰 GameObject의 hierarchy를 생성하는 프로젝트에서 유니티의 직렬화와 prefab 시스템에 전적으로 의존하기보다는, 재사용되는 요소를 이용해 prefab을 분리 시키고 런타임에 이를 생성함으로써 큰 prefab에 대한 로딩 시간을 극적으로 줄일 수 있을지도 모릅니다.


  1. identify를 어떻게 번역하는게 좋을까 고민하다가 여기에서는 다른 것과 다름을 구별한다의 의미가 강하므로 구별하다로 표현했습니다.

  2. wrapper를 한글로 어떻게 번역하는게 좋을지 모르겠네요. 평소에 그냥 래퍼라고 발음 그대로 읽어왔기 때문에 따로 번역할 단어를 못 찾겠습니다.

  3. instance를 객체라고 번역할까 하다가 Object와 혼돈의 여지가 너무 큰 것 같아서 그냥 인스턴스라고 표기했습니다.

  4. hierarchy를 계층이라고 표현할 수도 있지만 유니티에서 흔히 사용되는 용어이므로 계층이라고 번역하는 것보다는 그냥 용어 자체를 고유명사처럼 사용하는게 더 나을 것 같습니다.

  5. deep profiling은 유니티에서 제공하는 기능이므로 굳이 한글로 번역하지 않는 것이 더 나을 것 같습니다.

  6. trampolining이 컴퓨터 분야에서 어떤 의미로 쓰이는지 모르겠네요.

+ Recent posts