TSubclassOf<T>는 언리얼 엔진 C++ 프로그래밍, 특히 콘텐츠와 코드의 분리(Data-Driven Design)를 구현할 때 가장 빈번하게 사용되는 핵심 템플릿 클래스이다.
간단히 말해, "특정 클래스(또는 그 자식 클래스)의 원본(Type/Class Information)을 담는 안전한 변수"라고 정의할 수 있다.
1. TSubclassOf란 무엇인가?
C++의 UClass*는 모든 언리얼 오브젝트의 메타데이터(클래스 정보)를 담을 수 있는 포인터이다. 하지만 UClass*는 너무 범위가 넓다. AActor가 필요한 자리에 UTexture의 클래스 정보가 들어와도 컴파일러는 막지 못한다. TSubclassOf<T>는 이 문제를 해결할 수 있다.
- 타입 안정성 (Type Safety): 템플릿 인자
T로 지정된 클래스나, 그T를 상속받은 자식 클래스만 할당할 수 있다. - 에디터 편의성: 에디터의 '디테일 패널(Details Panel)'에서 드롭다운 메뉴를 열었을 때, 관련 없는 클래스는 필터링하고 호환되는 클래스만 보여준다.
2. 기본 사용법 및 시나리오
가장 흔한 사용 사례는 "C++에서 스폰(Spawn)할 액터의 종류를 블루프린트에서 지정하고 싶을 때"이다.
시나리오: 무기 스폰 시스템
C++ 코드는 무기를 스폰하는 로직(SpawnActor)만 작성하고, 실제로 스폰될 무기가 '라이플'인지 '로켓 런처'인지는 에디터에서 설정.
헤더 파일 (.h)
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyCharacter.generated.h"
// 전방 선언 (컴파일 시간 단축)
class AMyWeapon;
UCLASS()
class MYGAME_API AMyCharacter : public ACharacter
{
GENERATED_BODY()
public:
// 에디터에서 할당할 무기 클래스 변수
// AMyWeapon을 상속받은 블루프린트 클래스만 선택 가능.
UPROPERTY(EditDefaultsOnly, Category = "Combat")
TSubclassOf<AMyWeapon> WeaponClass;
// 실제 스폰된 무기 인스턴스를 가리키는 포인터 (UE5: TObjectPtr 권장)
UPROPERTY(Transient)
TObjectPtr<AMyWeapon> CurrentWeapon;
virtual void BeginPlay() override;
};
소스 파일 (.cpp)
#include "MyCharacter.h"
#include "MyWeapon.h"
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
// 1. 유효성 검사 (매우 중요)
// 에디터에서 WeaponClass를 비워뒀을 경우 크래시 방지
if (WeaponClass)
{
// 2. 스폰 파라미터 설정
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = this;
SpawnParams.Instigator = GetInstigator();
// 3. 월드에 액터 스폰
// GetWorld()->SpawnActor<T>(...)를 사용하면 캐스팅 없이 바로 T* 타입을 반환받는다.
CurrentWeapon = GetWorld()->SpawnActor<AMyWeapon>(WeaponClass, GetActorTransform(), SpawnParams);
if (CurrentWeapon)
{
// 무기 장착 로직 등...
UE_LOG(LogTemp, Log, TEXT("Weapon Spawned Successfully: %s"), *CurrentWeapon->GetName());
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("WeaponClass is not set in %s"), *GetName());
}
}
3. UE5 최적화 및 주의사항 (Critical)
오픈 월드나 규모가 큰 게임에서는 TSubclassOf 사용 시 메모리 관리에 주의해야 한다.
A. 하드 레퍼런스 (Hard Reference) 문제
UPROPERTY로 선언된 TSubclassOf<AActor> MyActorClass에 특정 블루프린트(예: BP_HeavyTank)를 할당하면, 이 캐릭터가 로드될 때 BP_HeavyTank도 메모리에 강제로 함께 로드된다.
만약 BP_HeavyTank가 거대한 텍스처와 사운드를 참조하고 있다면, 단순히 클래스 정보만 필요했을 뿐인데 수백 MB의 메모리가 낭비될 수 있다.
B. 해결책: TSoftClassPtr (소프트 레퍼런스)
즉시 스폰해야 하는 경우가 아니라면, 혹은 에셋 사이즈가 크다면 TSoftClassPtr를 사용하여 필요할 때 비동기 로딩(Async Load)하는 것이 UE5의 표준 최적화 방식이다.
// 헤더: 소프트 레퍼런스로 선언 (메모리에 즉시 로드되지 않음)
UPROPERTY(EditDefaultsOnly, Category = "Combat")
TSoftClassPtr<AMyWeapon> SoftWeaponClass;
// 구현: 필요할 때 로드 (동기 로드 예시, 실제론 AssetManager를 통한 비동기 권장)
void AMyCharacter::EquipWeapon()
{
if (SoftWeaponClass.IsPending())
{
// 에셋을 동기적으로 로드 (프레임 드랍 발생 가능, 비동기 로딩 권장)
SoftWeaponClass.LoadSynchronous();
}
TSubclassOf<AMyWeapon> LoadedClass = SoftWeaponClass.Get();
if (LoadedClass)
{
GetWorld()->SpawnActor<AMyWeapon>(LoadedClass, ...);
}
}
4. TSubclassOf vs UClass* 비교

5. 자주 발생하는 실수 및 팁
- 인스턴스가 아님:
- 자주 하는 실수는
WeaponClass->Fire()처럼 호출하려 하는 것이다.WeaponClass는 설계도(Class)이지 실제 만들어진 물체(Instance)가 아니다. 반드시SpawnActor를 통해 인스턴스화해야 한다.
- 자주 하는 실수는
- 블루프린트 전용 클래스:
- C++ 클래스 없이 블루프린트로만 만든 클래스(
Blueprint Type)도TSubclassOf<AActor>나TSubclassOf<UUserWidget>등에 할당할 수 있다. 이것이 C++와 블루프린트 하이브리드 개발의 핵심.
- C++ 클래스 없이 블루프린트로만 만든 클래스(
- 순환 의존성 (Circular Dependency):
- 헤더 파일에서
TSubclassOf를 쓸 때, 해당 클래스의 헤더를#include하지 말고class AMyWeapon;처럼 전방 선언(Forward Declaration)을 사용해야 한다. 컴파일 시간을 줄이고 의존성 꼬임을 방지.
- 헤더 파일에서
'프로그래밍 > Unreal Engine 5' 카테고리의 다른 글
| [Unreal Engine 5] GAS(Gameplay Ability System) (0) | 2026.01.26 |
|---|---|
| [Unreal Engine 5] 충돌처리를 위해 바인딩(Binding)을 하는 이유 (0) | 2026.01.23 |
| [Unreal Engine 5] UPROPERTY()와 지정자(Specifier)들 (0) | 2026.01.15 |
| [Unreal Engine 5] 액터의 라이프 사이클 (2) | 2026.01.09 |
| [Unreal Engine 5] 언리얼 엔진 5의 스마트 포인터 (1) | 2026.01.08 |