🚀 들어가며
이전 글에서는 Unreal Engine이 왜 C++ 기본 RTTI 대신 자체 Reflection 시스템을 사용하는지 살펴봤습니다.
그리고 그 과정에서 자연스럽게 등장했던 것이 바로
- UCLASS()
- UPROPERTY()
- UFUNCTION()
- GENERATED_BODY()
같은 매크로들입니다.
언리얼을 공부하다보면 이런 생각이 들 수 있습니다.
- GENERATED_BODY는 도대체 뭘 하는 걸까?
- 왜 .generated.h 파일이 생길까?
- UCLASS는 그냥 매크로 아닌가?
- UPROPERTY는 어떻게 GC와 연결될까?
사실 여기에는 매우 중요한 시스템이 숨어 있습니다.
바로 Unreal Header Tool(UHT)입니다.
UHT는 Unreal Engine Reflection 시스템의 핵심입니다.
이번 글에서는
- UHT가 왜 필요한지
- 실제로 어떤 일을 하는지
- .generated.h 파일은 왜 생기는지
- UCLASS / UPROPERTY / UFUNCTION이 어떻게 동작하는지
엔진 내부 구조 관점에서 정리해보겠습니다.
🧠 먼저 중요한 사실
언리얼의 Reflection 시스템은👉 C++ 컴파일러만으로는 구현하기 어렵습니다.
왜냐하면 C++는 기본적으로
- 멤버 변수 목록
- 함수 목록
- 메타데이터
- 에디터 노출 정보
같은 정보를 런타임에 제공하지 않기 때문입니다.
예를 들어
class Player
{
public:
int HP;
float Speed;
};
일반 C++에서는 런타임에
HP라는 멤버가 존재하는가? 타입은 int인가? 에디터에 표시 가능한가?
같은 정보를 쉽게 알 수 없습니다.
하지만 언리얼 엔진은 이런 정보가 필요합니다.
왜냐하면 언리얼은
- 에디터
- 블루프린트
- GC
- 직렬화
- 네트워크 복제
같은 시스템이 모두 연결되어 있기 때문입니다.
그래서 언리얼은 C++ 컴파일 이전에 헤더를 별도로 분석하는 시스템을 만들었습니다.
그게 바로 UHT(Unreal Header Tool)니다.
🎯 UHT는 언제 실행될까?
지금까지 설명한 Unreal Header Tool(UHT)의 전체 흐름을 그림으로 정리해보면 다음과 같습니다.
핵심은 UHT는 C++ 컴파일 이전에 헤더를 분석하고, Reflection 시스템에 필요한 메타데이터와 generated.h 코드를 생성한다는 점입니다.

위 구조처럼 Unreal Engine의 Reflection 시스템은 단순 매크로 확장 수준이 아니라, UHT가 생성한 메타데이터를 기반으로 동작합니다.
특히
- UCLASS / UPROPERTY / UFUNCTION 분석
- .generated.h 생성
- GENERATED_BODY 코드 삽입
- GC / 블루프린트 / 직렬화 연결
과정이 서로 연결되면서 언리얼 엔진의 메타 시스템 전체를 구성하게 됩니다.
GENERATED_BODY()를 컴파일러가 처리한다고 생각할 수 있습니다.
하지만 실제 흐름은 조금 다릅니다.
언리얼 빌드 흐름은 대략 이렇습니다.
UHT 실행 ↓ .generated.h 생성 ↓ C++ 컴파일 ↓ 엔진 Reflection 시스템 연결
즉, 👉 UHT는 C++ 컴파일 이전 단계에서 실행됩니다.
여기서 중요한 포인트는 “UHT는 “C++ 전체를 컴파일”하는 것이 아니라 언리얼 매크로를 분석하는 전처리 도구에 가깝다”는 점입니다.
🧩 UHT는 실제로 무엇을 분석할까?
예를 들어, 다음과 같은 코드를 생각해봅시다.
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
int HP;
UFUNCTION(BlueprintCallable)
void Attack();
};
UHT는 이 헤더를 읽고
- UCLASS 발견
- UPROPERTY 발견
- UFUNCTION 발견
같은 정보를 수집합니다.
그리고 내부적으로
- 이 클래스는 Reflection 대상이다
- HP는 int 타입 프로퍼티다
- Attack은 BlueprintCallable 함수다
같은 메타데이터를 생성합니다.
즉, UHT는 언리얼 전용 메타데이터 생성기라고 볼 수 있습니다.
🎮 GENERATED_BODY()는 실제로 뭘 할까?
GENERATED_BODY()를 단순 매크로처럼 생각할 수 있습니다.
하지만 실제로는 UHT가 생성한 코드가 삽입되는 위치입니다.
즉
GENERATED_BODY()
는 내부적으로
- StaticClass()
- Reflection 등록 코드
- Serializer 코드
- GC 관련 코드
- RPC 관련 코드
등이 들어갈 수 있는 자리입니다.
🧠 generated.h는 왜 생길까?
언리얼 프로젝트를 보면 항상 이런 코드가 있습니다.
#include "MyActor.generated.h"
처음 보면 굉장히 이상합니다.
- generated.h는 누가 만드는 걸까?
정답은
👉 UHT가 생성합니다.
예를 들어
MyActor.h
를 분석하면
MyActor.generated.h
를 자동 생성합니다.
그리고 그 안에는
- Reflection 등록 코드
- StaticClass 관련 코드
- 프로퍼티 메타데이터
- 함수 메타데이터
- 직렬화 코드
등이 들어갈 수 있습니다.
즉 .generated.h는 UHT가 생성한 Reflection 연결 코드입니다.
⚠️ 왜 generated.h는 마지막 include여야 할까?
언리얼에서 자주 보는 오류 중 하나입니다.
#include found after .generated.h file
왜 이런 제약이 있을까요?
이유는 generated.h 안에
- 매크로 확장 코드
- 타입 선언 보조 코드
- Reflection 연결 코드
등이 들어가기 때문입니다.
즉, include 순서가 꼬이면 Reflection 코드 구조가 깨질 수 있습니다.
그래서 아래 코드처럼 generated.h를 마지막에 include해야 합니다.
#include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "MyActor.generated.h"
🎯 UPROPERTY는 실제로 어떻게 사용될까?
예를 들어, 아래와 같이 변수에 UPROPERTY() 매크로를 추가하면,
UPROPERTY(EditAnywhere, BlueprintReadWrite) float MoveSpeed;
UHT는 이를 분석해서
- 변수 이름
- 타입
- Blueprint 노출 여부
- 에디터 수정 가능 여부
같은 메타데이터를 생성합니다.
이 정보는 이후
- 에디터
- 블루프린트
- GC
- 직렬화
시스템에서 사용됩니다.
즉,
UPROPERTY ↓ UHT 분석 ↓ Reflection 메타데이터 생성 ↓ 엔진 시스템 사용
흐름입니다.
🎯 UFUNCTION은 실제로 어떻게 연결될까?
예를 들어, 함수 선언 위에 UFUNCTION() 매크로를 추가하면,
UFUNCTION(BlueprintCallable) void Attack();
이 코드 역시 UHT가 분석합니다.
그리고
- 함수 이름
- 인자 정보
- 반환 타입
- BlueprintCallable 여부
등을 Reflection 시스템에 등록합니다.
그래야 블루프린트에서 “Attack 노드 생성” 같은 것이 가능합니다.
즉 블루프린트 함수 노드는 UHT 메타데이터 기반으로 생성됩니다.
🧩 GC와도 연결된다
이전 글에서 설명했던 GC 구조도 여기와 연결됩니다.
예를 들어,
UPROPERTY() UObject* Target;
UHT는 이 정보를 Reflection 시스템에 등록합니다.
그리고 GC는
- 이 클래스에 UObject 포인터가 존재하는지
- 어떤 프로퍼티를 추적해야 하는지
알 수 있게 됩니다.
즉
UPROPERTY ↓ UHT 분석 ↓ Reflection 등록 ↓ GC 추적 가능
구조입니다.
그래서 UHT는 GC 시스템과도 직접 연결됩니다.
🧠 Unreal Build Tool(UBT)과는 다르다
여기서 자주 헷갈리는 것이 있습니다.
- UHT(Unreal Header Tool)
- UBT(Unreal Build Tool)
둘은 다릅니다.
UHT
역할
- 헤더 분석
- Reflection 코드 생성
- generated.h 생성
UBT
역할
- 프로젝트 빌드
- 모듈 관리
- 컴파일 설정
- 플랫폼 빌드
즉,
- UHT = Reflection 코드 생성기
- UBT = 빌드 시스템
으로 이해하면 됩니다.
🎮 왜 이런 구조를 만들었을까?
사실 핵심은 이것입니다.
언리얼 엔진은 단순 C++ 라이브러리가 아닙니다.
- 에디터
- 스크립트 시스템
- GC
- 직렬화
- 네트워크 복제
를 모두 포함한 게임 제작 플랫폼입니다.
그런데 C++ 기본 기능만으로는 엔진이 객체를 충분히 이해하기 어렵습니다.
그래서 언리얼은
- UCLASS
- UPROPERTY
- UFUNCTION
같은 메타 시스템을 만들었고, 그 메타 시스템을 실제 코드로 연결하는 도구가 UHT입니다.
🔥 중요한 포인트
여기서 중요한 점은 UHT는 단순 코드 생성기가 아닙니다.
실제로는 언리얼 Reflection 시스템의 시작점입니다.
왜냐하면
- 에디터 노출
- 블루프린트 연결
- GC 추적
- 직렬화
- RPC
전부 UHT가 생성한 메타데이터를 기반으로 동작하기 때문입니다.
즉,
UHT ↓ Reflection 생성 ↓ 엔진 전체 시스템 연결
구조입니다.
🎯 핵심 정리
- UHT는 Unreal Header Tool의 약자입니다.
- UHT는 C++ 컴파일 이전에 헤더를 분석합니다.
- UCLASS, UPROPERTY, UFUNCTION 등을 분석해 Reflection 메타데이터를 생성합니다.
- .generated.h 파일은 UHT가 자동 생성합니다.
- GENERATED_BODY는 UHT 생성 코드가 삽입되는 위치입니다.
- GC, 블루프린트, 에디터, 직렬화 시스템은 모두 UHT 메타데이터와 연결됩니다.
🧩 마무리
언리얼을 공부하다 보면
- 왜 generated.h가 생기지?
- 왜 GENERATED_BODY가 필요하지?
- 왜 UPROPERTY를 붙여야 하지?
같은 부분이 굉장히 낯설게 느껴질 수 있습니다.
하지만 내부 구조를 이해해보면
언리얼은 C++ 위에 자체 Reflection 기반 객체 시스템을 구축한 엔진이라는 점이 보이기 시작합니다.
그리고 그 중심에는 UHT(Unreal Header Tool)가 있습니다.
즉, UHT는 단순 빌드 도구가 아니라, 언리얼 엔진 전체 메타 시스템을 연결하는 핵심 구성 요소라고 볼 수 있습니다.