내배캠_Unreal10기

[TIL] 내일배움캠프 Unreal 10기 7일차

devdiary-sj 2026. 7. 7. 11:59

C++ 자원 관리와 언리얼 엔진의 GC, 리플렉션

학습 내용

오늘은 부트캠프 VOD를 시청하며 C++의 자원 관리 방식과 언리얼 엔진의 가비지 컬렉션, 리플렉션 시스템에 대해 학습했습니다.

C++은 C#이나 Java처럼 기본적으로 가비지 컬렉터가 자동으로 모든 메모리를 관리해주는 언어가 아닙니다. 따라서 개발자가 동적으로 할당한 메모리를 적절한 시점에 해제하지 않으면 Dangling Pointer나 Memory Leak과 같은 문제가 발생할 수 있습니다.

Dangling Pointer

Dangling Pointer는 이미 해제된 메모리를 여전히 가리키고 있는 포인터를 의미합니다.

예를 들어 두 개의 포인터가 같은 메모리 주소를 가리키고 있을 때, 하나의 포인터를 통해 해당 메모리를 해제하면 다른 포인터는 그 메모리가 해제되었다는 사실을 알 수 없습니다. 이 상태에서 다른 포인터로 메모리에 접근하면 정의되지 않은 동작이 발생할 수 있습니다.

즉, 프로그램이 바로 예외를 발생시킬 수도 있지만, 더 위험하게는 아무 문제 없는 것처럼 보이다가 나중에 예상하지 못한 버그를 만들 수도 있습니다.

Memory Leak

Memory Leak은 동적으로 할당한 메모리를 사용한 뒤 제대로 해제하지 않아, 더 이상 필요하지 않은 메모리가 계속 남아 있는 문제입니다.

작은 프로그램에서는 크게 티가 나지 않을 수 있지만, 게임처럼 오랫동안 실행되는 프로그램에서는 메모리 사용량이 계속 증가하여 성능 저하나 크래시로 이어질 수 있습니다. 따라서 C++에서는 메모리의 소유권을 명확히 관리하는 것이 중요합니다.

스마트 포인터

C++에서는 직접 new와 delete를 사용하는 대신 스마트 포인터를 통해 메모리 관리를 더 안전하게 할 수 있습니다.

unique_ptr은 객체에 대한 단일 소유권을 관리합니다. 하나의 객체는 하나의 unique_ptr만 소유할 수 있으며, move를 통해 소유권을 다른 포인터로 이동시킬 수 있습니다.

shared_ptr은 객체를 여러 포인터가 함께 소유할 수 있도록 해줍니다. 내부적으로 레퍼런스 카운트를 관리하며, 객체를 참조하는 shared_ptr의 개수가 0이 되면 자동으로 메모리를 해제합니다.

weak_ptr은 객체의 소유권을 공유하지 않는 약한 참조입니다. shared_ptr처럼 레퍼런스 카운트를 증가시키지 않기 때문에 순환 참조 문제를 해결할 때 사용할 수 있습니다. 예를 들어 두 객체가 서로를 shared_ptr로 참조하면 레퍼런스 카운트가 0이 되지 않아 메모리가 해제되지 않을 수 있습니다. 이때 한쪽 참조를 weak_ptr로 바꾸면 순환 구조가 끊어집니다.

언리얼 엔진의 가비지 컬렉션

언리얼 엔진은 UObject 기반 객체에 대해 가비지 컬렉션을 제공합니다. 언리얼의 GC는 마크 앤 스윕 방식으로 동작합니다.

먼저 루트셋에 포함된 객체들을 식별합니다. 루트셋은 항상 살아있다고 간주되는 객체들의 집합입니다.

그 다음 마크 단계에서 루트셋으로부터 직접 또는 간접적으로 참조되는 UObject들을 찾아 사용 중인 객체로 표시합니다.

마지막으로 스윕 단계에서 마크되지 않은 객체들을 더 이상 사용되지 않는 객체로 판단하고 메모리를 회수합니다.

다만 언리얼의 가비지 컬렉션은 일반 C++ 객체 전체를 자동으로 관리하는 것이 아니라, 기본적으로 UObject 시스템에 속한 객체를 대상으로 합니다. 따라서 언리얼 C++에서도 일반 C++ 객체나 포인터를 사용할 때는 여전히 소유권과 생명주기를 신경 써야 합니다.

언리얼 엔진의 리플렉션

언리얼 엔진의 리플렉션은 사용자가 정의한 클래스, 변수, 함수 등의 타입 정보를 엔진이 런타임에 인식할 수 있도록 하는 시스템입니다.

일반 C++은 런타임에 타입 정보를 자동으로 충분히 제공하지 않기 때문에, 언리얼은 UCLASS, UPROPERTY, UFUNCTION 같은 매크로를 사용해 엔진이 필요한 메타데이터를 수집할 수 있도록 합니다.

이 작업은 UHT, 즉 Unreal Header Tool이 담당합니다. UHT는 C++ 컴파일러가 실행되기 전에 먼저 동작하며, 헤더 파일을 분석해 언리얼 엔진이 사용할 수 있는 추가 코드를 생성합니다.

이를 통해 언리얼은 블루프린트 노출, 에디터 표시, 직렬화, 네트워크 복제, 가비지 컬렉션 참조 추적 같은 기능을 지원할 수 있습니다.

오늘의 정리

C++에서는 메모리 관리를 개발자가 직접 신경 써야 하며, 이를 잘못 처리하면 Dangling Pointer나 Memory Leak 문제가 발생할 수 있습니다. 이를 보완하기 위해 unique_ptr, shared_ptr, weak_ptr 같은 스마트 포인터를 사용할 수 있습니다.

언리얼 엔진은 UObject 기반 객체에 대해 가비지 컬렉션을 제공하지만, 모든 C++ 객체를 자동으로 관리하는 것은 아닙니다. 또한 언리얼의 리플렉션 시스템은 엔진이 사용자 정의 타입을 인식하고 블루프린트, 에디터, GC 등과 연결할 수 있게 해주는 핵심 구조입니다.

오늘 학습을 통해 C++의 자원 관리 방식과 언리얼 엔진이 이를 어떻게 보완하고 확장하는지 이해할 수 있었습니다.