# 유니티 빌드 과정
유니티는 스크립트(C# 등)와 분리된 계층에서 실행된다
이 스크립트들은 빌드를 진행하면 아래와 같은 단계로 실행된다
1. IL 코드 생성:
개발자가 작성한 C# 코드는 먼저 IL(중간 언어)로 컴파일된다
2. IL2CPP 변환:
IL 코드를 IL2CPP 백엔드가 받아서 C++ 소스 코드로 변환한다
3. 네이티브 컴파일:
변환된 C++ 소스 코드는 각 플랫폼(iOS, Android, Windows 등)에 맞는
네이티브 컴파일러 (ex. iOS는 Clang, Android는 GCC/Clang, Windows는 MSVC)를 통해
해당 플랫폼의 기계어 코드로 최종 컴파일된다
# IL2CPP의 장점
1. 런타임 성능
IL2CPP는 모든 IL 코드를 미리 C++ 코드를 거쳐 네이티브 기계어로 컴파일한다
이렇게 하면 런타임에 추가적인 컴파일 과정이 필요 없어져 실행 속도가 더 빠르고,
런타임 오버헤드가 줄어든다
특히 JIT 컴파일이 불가능한 플랫폼(예: iOS)에서는 필수적이며,
Android 같은 플랫폼에서도 좋은 성능을 보여준다
C++ 컴파일러는 코드 최적화에 매우 강력하기에
IL2CPP가 생성한 C++ 코드를
네이티브 컴파일러가 다시 한번 강력하게 최적화하여
최종 기계어 코드를 생성한다
2. 크로스 플랫폼 호환성
IL2CPP가 생성하는 C++ 코드는 거의 모든 플랫폼에서 네이티브 컴파일이 가능하다
이 덕분에 유니티가 다양한 플랫폼(모바일, 콘솔, 데스크톱, WebGL 등)을 지원할 수 있다
3. 보안 강화
IL 코드는 역컴파일하기 비교적 쉽다
하지만 IL2CPP를 사용하면 C++ 코드를 거쳐 네이티브 코드로 변환되므로,
코드를 역으로 분석하기 훨씬 어려워져 게임의 보안이 강화된다
# IL2CPP의 단점
1. 빌드 시간 증가
IL 코드를 C++로 변환하고, 다시 C++ 코드를 네이티브로 컴파일하는 과정이
추가되므로, 빌드 시간이 훨씬 길어진다
2. 빌드된 파일 크기 증가
생성되는 네이티브 코드의 크기가 큰 경향이 있다
3. 디버깅 복잡성 증가
특정 상황에서 C# 디버깅이 아닌 C++ 레벨의 디버깅이 필요할 수 있어
복잡도가 증가할 수 있습니다
# C++ DLL 직접 빌드
IL2CPP는 빌드 과정의 비효율성(시간, 크기)이라는 단점이 있지만,
실제 게임 플레이 시의 성능(런타임 성능)은 오히려 향상하는 기술이다
그러나 빌드의 효율성을 증가시키기 위해서
네이티브 플러그인(Native Plugin) 또는 네이티브 라이브러리(Native Library)를 사용한다
이는 C++ DLL을 직접 빌드하여 사용하는 방식이다
IL2CPP가 아무리 C# 코드를 C++로 변환하여 최적화한다고 해도,
아래와 같은 경우에는 C++로 직접 DLL을 빌드하여 사용하는 것이 훨씬 좋다
1. 최고의 성능이 필요한 임계 영역 (Critical Sections)
IL2CPP가 생성하는 C++ 코드는 일반적인 C# 코드에 대한 최적화된 변환이지만,
특정 알고리즘(예: 복잡한 수학 연산, 커스텀 물리 엔진, 대규모 데이터 처리)에서는
C++ 개발자가 직접 작성하고 최적화한 코드가 훨씬 빠를 수 있다
특히 CPU 캐시 효율성, 메모리 직접 접근 제어 등 저수준 최적화가 필요한 경우에 그렇다
IL2CPP는 C#의 가비지 컬렉션(GC) 등의 런타임 환경을
C++로 에뮬레이션해야 하므로, 순수 C++ 만큼의 자유도를 가지지는 못한다
2. 플랫폼별 네이티브 API 직접 접근
운영체제(Windows, iOS, Android 등)나 특정 하드웨어(콘솔 기기)에서 제공하는
저수준의 독점적인 API를 직접 호출해야 할 때 C++ DLL이 필요하다
C#에서는 이러한 API를 직접 호출하기 어렵거나, 유니티가 래핑 해주지 않은 경우에 사용한다
3. 기존 C/C++ 라이브러리 활용
이미 잘 구축되어 있고 최적화된 C/C++ 기반의 라이브러리
(예: 복잡한 시뮬레이션 라이브러리, 특정 코덱, 하드웨어 드라이버)가 있다면,
이를 유니티 프로젝트에 통합하기 위해 C++ DLL 형태로 가져와 사용한다
4. 코드 보안 강화
게임의 핵심 로직이나 치명적인 알고리즘이 역컴파일 되는 것을 막기 위해,
C++ DLL로 만들면 코드 분석이 훨씬 더 어려워져 보안성이 높아집니다.
IL2CPP도 보안에 기여하지만, 순수 C++ DLL만큼 강력하지 않다
# C++ DLL 직접 빌드 예시 코드
유니티 C# 스크립트에서 C++ DLL 내의 함수를 호출하려면
P/Invoke (Platform Invoke)라는 메커니즘을 사용한다
using System;
using System.Runtime.InteropServices; // DllImport를 위해 필요
public class MyNativePlugin : MonoBehaviour
{
// [DllImport] 속성을 사용하여 외부 DLL의 함수를 선언
// "MyCppPlugin"은 빌드된 C++ DLL 파일의 이름 (ex. MyCppPlugin.dll, libMyCppPlugin.so 등)
[DllImport("MyCppPlugin")]
private static extern int AddNumbers(int a, int b);
[DllImport("MyCppPlugin")]
private static extern void ProcessHeavyData(IntPtr dataPtr, int dataSize);
void Start()
{
// DLL의 AddNumbers 함수 호출
int result = AddNumbers(10, 20);
Debug.Log($"Result from C++ DLL: {result}");
// 예시: C# 배열 데이터를 C++ 함수로 전달
byte[] myData = new byte[1000];
// 데이터를 채우는 로직...
// GCHandle을 사용하여 C# 배열의 메모리 주소를 고정하고 C++로 전달
GCHandle handle = GCHandle.Alloc(myData, GCHandleType.Pinned);
try
{
ProcessHeavyData(handle.AddrOfPinnedObject(), myData.Length);
Debug.Log("Heavy data processed by C++ DLL.");
}
finally
{
handle.Free(); // 메모리 고정 해제
}
}
}
# C++ DLL 직접 빌드의 단점
유니티에서 모든 모듈이 C++로의 변환을 지원하는 것은 아니기에,
개발 중에도 수시로 빌드를 진행하여 오류나 충돌 여부를 확인하는 것이 중요하다
실무 환경에서는 Jenkins 같은 자동 빌드 도구를 통해
주기적으로 빌드가 이루어지기도 하지만,
개인 개발자라면 직접 자주 빌드하는 습관이 매우 중요하다
즉, IL2CPP가 아닌 C++ 직접 빌드를 하는 것은
성능 향상을 위한 강력한 도구이지만, 그만큼 진입장벽도 있다
그래서 실무에서도 꼭 필요한 부분에만 C++을 쓰고,
나머지는 C#으로 처리하는 하이브리드 방식이 추천된다
'게임 엔진 - 유니티 > [최적화] 유니티' 카테고리의 다른 글
| [유니티 최적화] Garbage Collector (0) | 2025.06.21 |
|---|---|
| [유니티 최적화] 메시 압축 (0) | 2025.06.21 |
| [유니티 최적화] 배칭 (3) | 2025.06.21 |