[Unreal Engine 5] CDO(Class Default Object)

2026. 3. 12. 20:26·프로그래밍/Unreal Engine 5

📝 개요

언리얼 엔진에서 객체(UObject)를 다루다 보면 반드시 마주치게 되는 개념이 바로 CDO(Class Default Object)다. 엔진이 초기화될 때 각 클래스마다 단 하나씩 만들어지는 이 '기본 객체'는 도대체 왜 필요하며, 어떤 순서로 값이 정해지고, 어떻게 활용할 수 있는지 깊이 있게 정리해 본다.


1. 언리얼 엔진은 왜 CDO를 만드는가?

언리얼 엔진에서 몬스터 100마리를 스폰한다고 가정해 보자. 100마리가 각각 자신의 기본 체력, 기본 이동 속도, 기본 메시(Mesh) 데이터를 처음부터 끝까지 메모리에 새로 할당받고 계산한다면 엄청난 오버헤드가 발생할 것이다.

언리얼 엔진은 이 문제를 해결하기 위해 '미리 구워둔 붕어빵 틀(CDO)'을 사용한다.

  • 초고속 인스턴스화 (빠른 스폰): 새로운 액터를 스폰할 때, 생성자를 처음부터 끝까지 다시 실행하는 것이 아니라 미리 메모리에 만들어둔 CDO를 그대로 '복제(Memory Copy)'하여 스폰한다. 이를 통해 스폰 성능을 극대화한다.
  • 메모리 최적화: 모든 인스턴스가 공통으로 가져야 하는 기본값을 CDO 한 곳에서 관리하므로 메모리 낭비를 막아준다.
  • 에디터와 리플렉션(Reflection) 지원: 우리가 에디터 디테일 패널에서 변수 옆에 있는 '노란색 되돌리기(Reset to Default) 화살표'를 누르면 값이 원래대로 돌아온다. 이때 돌아가는 기준값이 바로 CDO에 저장된 값이다.

2. CDO 프로퍼티가 정해지는 순서 (초기화 파이프라인)

CDO의 프로퍼티(기본값)는 엔진이 켜질 때 딱 한 번 세팅되며, 크게 4단계를 거쳐 최종 값이 덮어씌워진다.

① 1단계: C++ 헤더 파일 (In-class 초기화)

가장 먼저 적용되는 값이다. 모던 C++ 문법을 통해 헤더에서 변수 선언과 동시에 값을 지정한다.

// Monster.h
UPROPERTY(EditAnywhere)
float MaxHealth = 100.0f; // 1단계 적용

② 2단계: C++ 생성자 (Constructor) - ⭐ Native CDO 생성

엔진이 초기화될 때, 모든 UCLASS의 생성자를 단 한 번씩 실행하여 CDO를 구워낸다. 이때 헤더에서 설정한 값을 덮어쓴다. (게임 플레이 도중 스폰할 때는 이 생성자 로직이 다시 불리는 것이 아니라, 복사만 일어난다!)

// Monster.cpp
AMonster::AMonster()
{
    // 2단계 적용: 100이었던 체력이 200으로 덮어씌워짐
    MaxHealth = 200.0f; 
}

③ 3단계: Config 파일 (.ini) 데이터 로드

클래스나 프로퍼티에 Config 속성이 지정되어 있다면, DefaultGame.ini 등의 설정 파일에서 값을 읽어와 CDO에 덮어씌운다.

④ 4단계: 블루프린트 (Class Defaults) - ⭐ Blueprint CDO 생성

C++ 클래스를 상속받아 블루프린트를 만들면, 엔진은 '블루프린트용 CDO'를 하나 더 만든다. 기획자가 에디터의 클래스 디폴트(Class Defaults) 창에서 수정한 값(예: 300)이 있다면, 이 값이 최종적으로 CDO에 덮어씌워진다.

💡 요약 파이프라인: C++ 헤더(100) ➔ C++ 생성자(200) ➔ INI 파일 적용 ➔ 블루프린트 디테일 패널 수정(300) ➔ 스폰 시 최종값 300으로 복제됨!


3. CDO의 실무 활용 방안 (최적화 기법)

CDO의 개념을 알면, "오브젝트를 스폰(Spawn)하지 않고도 그 클래스의 정보를 알아내는" 강력한 최적화 기법을 쓸 수 있다.

활용방안 A: 스폰 없이 무기/캐릭터의 기본 스탯 읽어오기

상점 UI를 만들 때, 판매 중인 100개의 무기 공격력을 UI에 띄워야 한다고 가정해 보자.
공격력을 알기 위해 무기 액터 100개를 보이지 않는 곳에 임시로 스폰하는 것은 최악의 방법이다. 이때 CDO를 사용한다.

// 무기 클래스의 CDO를 가져와서, 스폰 없이 기본 공격력만 쏙 빼온다!
if (TSubclassOf<AF4Weapon> WeaponClass = ItemDataTable->WeaponClass)
{
    // GetDefault<T>() 또는 GetDefaultObject()를 사용
    AF4Weapon* WeaponCDO = Cast<AF4Weapon>(WeaponClass->GetDefaultObject());

    if (WeaponCDO)
    {
        float BaseDamage = WeaponCDO->AttackDamage;
        UE_LOG(LogTemp, Log, TEXT("이 무기를 스폰하면 기본 데미지는 %f 입니다."), BaseDamage);
    }
}

활용방안 B: 현재 상태가 기본 상태인지 비교 (런타임 초기화)

캐릭터가 디버프나 버프를 받아 스탯이 변했을 때, 원래 상태(기본값)로 되돌리고 싶다면 CDO의 값을 참조하여 복구할 수 있다.

void AF4PlayerCharacter::ResetToDefaultSpeed()
{
    // 내 클래스의 원본(CDO)을 가져온다.
    AF4PlayerCharacter* MyCDO = GetClass()->GetDefaultObject<AF4PlayerCharacter>();

    // 현재 내 속도를 붕어빵 틀(CDO)의 초기 속도로 되돌린다.
    CurrentMoveSpeed = MyCDO->CurrentMoveSpeed;
}

'프로그래밍 > Unreal Engine 5' 카테고리의 다른 글

[Unreal Engine 5] NetRole (Authority와 Proxy)  (0) 2026.03.17
[Unreal Engine 5] 언리얼 엔진 멀티플레이: 서버의 종류와 데디케이티드 서버 실행 흐름  (0) 2026.03.13
[Unreal Engine 5] 언리얼 엔진 스마트 포인터와 메모리 관리 총정리 (TObjectPtr, TSoftObjectPtr, TSubclassOf 등)  (0) 2026.03.11
[Unreal Engine 5] 동기(Synchronous) VS 비동기(Asynchronous)  (0) 2026.03.09
[Unreal Engine 5] 언리얼 엔진 인풋(Input) 아키텍처 파헤치기: F4 vs Lyra 비교 및 CS 관점의 동작 원리  (0) 2026.03.05
'프로그래밍/Unreal Engine 5' 카테고리의 다른 글
  • [Unreal Engine 5] NetRole (Authority와 Proxy)
  • [Unreal Engine 5] 언리얼 엔진 멀티플레이: 서버의 종류와 데디케이티드 서버 실행 흐름
  • [Unreal Engine 5] 언리얼 엔진 스마트 포인터와 메모리 관리 총정리 (TObjectPtr, TSoftObjectPtr, TSubclassOf 등)
  • [Unreal Engine 5] 동기(Synchronous) VS 비동기(Asynchronous)
hanong8
hanong8
hanong8 님의 블로그 입니다.
  • hanong8
    HaNong
    hanong8
  • 전체
    오늘
    어제
    • 분류 전체보기 (102) N
      • 프로그래밍 (99) N
        • Unreal Engine 5 (45)
        • C++ (22)
        • UML (2)
        • 자료구조 (2)
        • 알고리즘 (9)
        • 개발일지 (4)
        • DirectX11 (5)
        • Git (2)
        • 코드카타 (8) N
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
hanong8
[Unreal Engine 5] CDO(Class Default Object)
상단으로

티스토리툴바