언리얼 엔진 그리고 유니티 엔진을 사용해서 게임 프로그래밍을 할 때, 가장 길게 느껴지는 시간은 아무래도 컴파일 시간이다.
물론, 코딩을 잘해서 머릿속으로 코드를 돌려보고 작동까지 볼 수 있는 실력이 된다면 괜찮겠지만 안타깝게도 아직 그정도 수준까지는 멀었다.
이번에는 유니티에서 이러한 컴파일 시간을 줄여보는 방법에 대해 공부해보았다.
우선 DLL 파일과 PDB 파일에 대해 알아야한다.
1. DLL;Dynamic Link Library
라이브러리란, 컴퓨터 프로그램에서 사용하는 비휘발성 자원(함수, 데이터, 타입 등)의 모임을 뜻한다.
DLL은 동적으로 링크되고 실행되는 재사용 가능한 코드 모듈을 의미한다. 즉, 여러 프로그램에서 동시에 사용할 수 있는 코드와 데이터를 포함하는 라이브러리이다. 그리고 유니티에서 DLL 파일은 코드와 리소스를 캡슐화한 형태로 사용된다.
유저가 작성한 스크립트는 dll로 생성되어, 이는 Unity Editor에서 사용된다.
주요특징
- 코드 재사용: 반복적으로 사용되는 코드를 캡슐화하여 여러 프로젝트에서 재사용할 수 있다.
- 보안 및 은닉: 코드를 DLL로 컴파일하면 내부 소스를 숨길 수 있어 코드 보안을 강화할 수 있다.
- 성능 최적화: DLL은 미리 컴파일된 바이너리 파일이므로 실행 시 컴파일 과정을 생략하여 로드 속도가 빠르다.
- 플러그인 활용: 외부 라이브러리(예: .NET 라이브러리, C++ 라이브러리)를 유니티 프로젝트에 통합할 때 사용된다.
유니티에서 DLL 사용 시 주의점
- 호환성: DLL이 사용하는 .NET 프레임워크 버전이 유니티의 Mono/.NET 버전과 호환되어야 한다.
- 디버깅: DLL 파일은 이미 컴파일된 형태이기 때문에 디버깅이 어렵다.
디버깅을 위해서는 함께 제공되는 PDB 파일이 필요하다.
2. PDB;Program Database
PDB는 디버깅 정보를 포함하는 파일로, 앱의 디버그 구성에 대한 링크를 허용하는 디버깅 및 프로젝트 상태 정보가 저장된다. 이는 주로 DLL 파일이나 실행 파일과 함께 제공되며, 해당 바이너리를 디버깅하거나 오류를 추적하는 데 사용된다.
주요특징
- 디버깅 지원: PDB 파일에는 디버깅 심볼(메서드 이름, 변수 이름, 파일 위치 등)이 포함되어 있어 오류 발생 시 정확한 코드 위치를 추적할 수 있다.
- 코드-바이너리 연결: DLL이나 EXE 파일의 바이너리 코드와 원본 소스 코드를 연결하는 역할을 한다.
- 비개발 환경에서 불필요: 최종 배포 버전에서는 PDB 파일을 포함하지 않아도 동작에는 문제가 없으나, 디버깅이 어렵다.
유니티에서 DLL 사용 시 주의점
- 디버깅 DLL: DLL에서 발생한 오류를 추적하려면 PDB 파일이 필요하다. 즉, 디버깅을 위해서는 DLL과 PDB 파일 모두 필요하다. 보통 Visual Studio나 Rider같은 IDE에서 이러한 디버깅 기능을 제공한다.
- 심볼 서버 사용: 심볼 서버를 설정하여 PDB 파일을 중앙에서 관리하면, 필요한 경우 자동으로 로드하여 디버깅할 수 있다.
유니티에서 DLL 및 PDB 활용 방법
- Custom Scripts 라이브러리화: 공통으로 사용하는 유틸리티 함수를 DLL로 컴파일하여 여러 프로젝트에서 사용.
- 외부 라이브러리 통합: Newtonsoft.Json 같은 외부 라이브러리를 DLL 형식으로 추가하여 JSON 처리.
- 디버깅 서드파티 DLL: 서드파티에서 제공하는 DLL에서 오류가 발생했을 때, 제공된 PDB 파일로 원인을 추적.
DLL과 PDB 파일의 관계
- DLL 파일은 실행 가능한 코드와 리소스를 포함한다.
- PDB 파일은 디버깅에 필요한 메타데이터와 코드 맵핑 정보를 제공한다.
- PDB 파일이 없는 DLL은 디버깅이 불가능하거나 제한적이다.
유니티에서 DLL/PDB 관리 및 최적화 팁
- 필요한 DLL만 유지: 사용하지 않는 DLL은 프로젝트에서 제거하여 빌드 크기를 최적화한다.
- 릴리스 빌드에서 PDB 제거: 배포 환경에서는 디버깅 심볼이 포함되지 않도록 설정하여 보안을 강화한다.
- Assembly Definition 사용: 유니티의 Assembly Definition 파일을 활용하여 프로젝트의 컴파일 속도를 개선하고 의존성을 체계적으로 관리한다.
- 디버깅 도구 활용: Unity Debugger, IDE 디버깅 툴 등을 활용하여 PDB 정보를 효과적으로 분석한다.
유니티 내에서 이러한 DLL파일과 PDB 파일은 [프로젝트 폴더 > Library > ScriptAssemblies]에 들어있다.
만일 디버깅용 프로젝트를 빌드하고자 한다면, 유니티 빌드에서 다음과 같이 그 방법을 제공한다.
(기본적으로는 체크되어 있지 않다.)
그렇다면 당연하게도, 이 PDB 정보를 활용하면 Unity Debugger와 Profiling 도구를 통해 디버깅과 성능 최적화를 효과적으로 수행할 수 있을 것이다.
Assembly Definition
Unity는 기본적으로 모든 C# 스크립트를 하나의 어셈블리(Assembly-CSharp.dll)로 컴파일한다. 프로젝트가 커질수록 코드가 많아지고, 한 파일만 수정해도 전체 코드를 다시 컴파일해야 하므로 시간이 오래 걸릴 수 있다.
유니티에서 만일 어떠한 스크립트를 수정한다고 해보자. 가령, 다음과 같은 구조를 가지고 있을 때, Library.dll을 수정하면 유니티는 그 상위의 Stuff.dll, Main.dll 그리고 ThirdParty.dll 까지 컴파일하여 Assembly_CSharp.dll을 컴파일한다. 즉, 스크립트를 많이 추가할수록 컴파일 시간이 기하급수적으로 증가하게 된다.
만일 위 도면에서, Main.dll을 수정하면 Main.dll만 컴파일하거나, Stuff.dll을 수정하면 Stuff.dll 그리고 Main.dll만 컴파일 한다면 컴파일 시간을 단축할 수 있을 것이다. 스크립트에서 변경 사항을 적용할 때, 필요한 어셈블리만 다시 빌드되도록 하기 위해서는 프로젝트 스크립트를 종석성이 분명하게 정의된 여러 어셈블리로 분해해야 할 것이다. 그리고 이러한 분해를 담당하는 파일이 바로 Assembly Definition이다.
Assembly Definition 파일(.asmdef)을 사용하면 특정 폴더의 스크립트를 별도의 어셈블리로 분리할 수 있다. 이를 통해 컴파일 타임을 줄이고, 독립적인 모듈 단위로 코드를 관리할 수 있다.
Assembly Definition의 주요 기능
- 코드 컴파일 분리
.asmdef 파일이 포함된 폴더의 스크립트는 별도의 어셈블리로 컴파일된다. 따라서 해당 어셈블리와 관련 없는 코드는 다시 컴파일되지 않는다. - 의존성 관리
어셈블리 간 의존성을 정의하여, 각 어셈블리가 어떤 다른 어셈블리를 참조해야 하는지 명시할 수 있다. 즉, 불필요한 참조를 제거하면 프로젝트 구조가 더 명확해진다. - 테스트 코드 분리
Assembly Definition을 사용하여 테스트 코드를 일반 코드와 분리할 수 있다. 이를 통해 테스트 환경과 실제 코드가 독립적으로 관리된다.
Assembly Definition 설정 및 사용
1. .asmdef 파일 생성
다음 폴더는 UI 요소의 효과들을 담당하는 코드가 들어있는 Effects 폴더이다. 지금 이 폴더에는 UI 요소들의 페이드 효과를 담당하는 코드를 작성해두었다.
일반적으로 Assembly Definition을 생성하면 폴더의 이름과 동일하게 생성해야한다. 이때 보게되면 에러가 발생한 것을 볼 수 있다.
에러 로그를 보면, Data/Extension/FadeType을 찾을 수 없다고 한다.
내부 코드를 보면 다음과 같이 에러가 발생해있다.
여기에서 인스펙터 창을 통해, Assembly Definition을 보도록 하자.
주요 설정 항목을 정리해보면 다음과 같다.
Name: 어셈블리 이름을 지정. 이때 어셈블리 이름은 고유해야한다. 그리고 가급적 폴더의 이름과 일치시켜 주는것이 좋다.
Allow Unsafe Code: C#의 unsafe 코드를 허용할지 여부를 설정한다.
Assembly Definition References: 이 어셈블리가 참조할 다른 어셈블리를 추가한다.
Platforms: 어셈블리를 사용할 플랫폼을 선택한다.
Define Constraints: 특정 조건부 컴파일 심볼이 활성화된 경우에만 어셈블리가 활성화되도록 설정한다.
여기에서 Assembly Definition References에 Assembly Definition들을 추가해야 한다.
이 코드에서는 Data/Extension 폴더에 있는 Enums와 UnityExtension 파일이 여기에 해당되므로 다음과 같이 각각 Data와 Extensions Assembly Definition을 추가하고, Assembly Definition References에 이를 추가한다. 그러면 에러가 사라진다.
그리고 Library 폴더에 다음과 같이 각 Assembly Definition이 추가된 것을 볼 수 있다.
이러한 Assembly Definition을 설정하는 과정에서 프로젝트가 더 깔끔하게 정리되기도 하고, 의존성 관계를 명확하게 할 수 있다. 즉, 단순 컴파일 시간을 줄이는 것 뿐만 아니라 앞선 주요 기능에서 처럼 개발자에게 좋은 지표가 되어주기도 한다.
'Study > Unity Engine' 카테고리의 다른 글
[Unity] 유니티 프레임 워크, .NET과 GC (0) | 2025.01.24 |
---|---|
[Unity] Profiling (0) | 2025.01.18 |
[Unity] 컴파일 과정 (0) | 2025.01.18 |
[Unity] 오브젝트 검색 방법 (0) | 2025.01.18 |
[Unity] ScriptableObject (0) | 2025.01.18 |