1. 개요

CDO 는 Class Default Object의 약자이며, 특정 UClass 의 기본값을 담고 있는 대표 객체이다

언리얼 엔진에서는 UObject 계열 클래스마다, 그 클래스의 기본 상태를 표현하는 특수한 객체 1개가 존재한다. 이 객체를 CDO라고 부른다.

CDO는 단순한 메타데이터가 아니라, 실제로 존재하는 객체 인스턴스다. 다만 월드에 배치되어 게임플레이를 수행하는 일반 인스턴스가 아니라, ”이 클래스의 기본 상태는 무엇인가?” 를 표현하기 위한 원본 객체라는 점이 다르다.


2. CDO의 핵심 개념

CDO를 가장 짧게 정의하면 다음과 같다.

CDO는 클래스의 기본 프로퍼티 값과 기본 서브로브젝트 구성을 담고 있는 원본 객체이다.

예를 들어 AMyActor라는 클래스가 있다면, 엔진은 내부적으로 다음 두 가지를 관리한다.

  • AMyActor 의 타입 정보 (UClass)
  • AMyActor 의 기본 상태를 담은 객체 (CDO)

즉 언리얼의 클래스 시스템은 단순히 “코드”만 들고 있는 것이 아니라, 그 클래스의 기본 상태를 실제 객체 형태로 들고 있다고 볼 수 있다.


3. 왜 CDO가 필요한가

CDO는 새 객체를 생성할 때 기본값의 기준점 역할을 한다.

새로운 객체 인스턴스를 만들 때마다 기본값을 하나하나 다시 계산하는 대신, 엔진은 미리 준비된 CDO를 참조해서 객체의 초기 상태를 구성한다.

이 구조의 장점은 다음과 같다.

  • 클래스 기본값을 일관되게 관리할 수 있다.
  • 새 인스턴스를 만들 때 초기화 기준이 명확하다.
  • 블루프론트에서 수정한 기본값을 자연스롭게 반영할 수 있다.
  • 직렬화/복제 시 “기본값과 무엇이 다른가”를 비교하기 쉬워진다.
  • 기본 컴포넌트 구조를 클래스 수준에서 정의할 수 있다.

즉 CDO는 언링러 오브젝트 시스템에서 기본값 관리의 중심축이다.


4. 예시

UCLASS()
class AMyActor : public AActor
{
    GENERATED_BODY()

public:
    AMyActor();

    UPROPERTY(EditAnywhere)
    int32 Health;

    UPROPERTY(EditAnywhere)
    float MoveSpeed;
};

AMyActor::AMyActor()
{
    Health = 100;
    MoveSpeed = 600.f;
}

위 클래스가 있으면 AMyActor 의 CDO는 대략 다음 기본값을 가지게 된다.

  • Health = 100
  • MoveSpeed = 600.f

이후 AMyActor 인스턴스를 생성하면, 그 인스턴스는 이 CDO의 기본값을 기준으로 초기 상태를 갖게 된다.


5. 생성자와 CDO의 관계

언리얼에서 생성자를 이해할 때 가장 중요한 점은 다음이다.

생성자는 단순히 런타임 인스턴스 초기화 코드가 아니라, CDO를 구성하는 코드이기도 하다.

즉 아래 코드

AMyActor::AMyActor()
{
    Health = 100;
}

는 단순히 “인스턴스가 생성될 때마다 Health를 100으로 넣는다” 정도로 이해하면 부족하다.

더 정확하게는 다음 순서로 이해하야 한다.

  1. AMyActor 클래스가 준비될 때 CDO가 생성된다.
  2. 생성자에서 지정한 기본값이 CDO에 반영된다.
  3. 이후 실제 인스턴스는 그 CDO를 기준으로 생성된다.

따라서 언리얼 생성자는 일반 C++의 순수 생성자보다 ”이 클래스의 기본 설계를 정의하는 장소”라는 성격이 더 강하다.


6. CDO와 실제 인스턴스의 차이

같은 클래스에 속해 있어도 CDO와 일반 인스턴스는 역할이 다르다.

CDO

  • 클래스당 1개 존재
  • 기본값 보관용
  • 월드에서 게임플레이를 수행하지 않음
  • 클래스 수준의 기존 객체

일반 인스턴스

  • 여러 개 생성 가능
  • 월드에 존재하며 실제 게임 상태를 가짐
  • 위치, 회전, 체력 등 런타임 상태가 계속 변함

예를 들어 몬스터 클래스의 기본 체력이 100이라면

  • CDO의 체력: 100
  • 몬스터 A 현재 체력 : 73
  • 몬스터 B 현재 체력 : 41
  • 몬스터 C 현재 체력 : 100

이처러 CDO는 기본값의 원본일 뿐, 실제 인스턴스의 현재 상태와는 별개다.


7. Default Subobject와 CDO

CDO는 단순히 숫자나 포인터 같은 기본 프로퍼티 값만 저장하는 것이 아니다. 기본 서브오브젝트 구조도 함께 정의한다.

예를 들어

AMyCharacter::AMyCharacter()
{
    RootComp = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
    MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
    CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
}

이 코드는 단순히 런타임에 컴포넌트를 세 개 생성하는 것 이상의 의미를 가진다.

이 생성자 코드는 사실상 다음을 정의한다.

  • 이 클래스는 기본적으로 RootComp 를 가진다.
  • 이 클래스는 기본적으로 MeshComp 를 가진다.
  • 이 클래스는 기본적으로 CameraComp 를 가진다.

기본 컴포넌트 트리의 원형이 CDO에 반영된다고 볼 수 있다.


8. Blueprint와 CDO

CDO는 C++ 클래스뿐만 아니라 Blueprint 클래스에서도 매우 중요하다.

예를 들어 C++ 클래스 AMyEnemy 가 다음 기본값을 가진다고 하자.

  • Health = 100
  • Speed = 400

그리고 이를 상속한 블루프린트 BP_FastEnemy 에서 에디터에서 값을 수정해

  • Health = 150
  • Speed = 700

으로 바꾸었다면, BP_FastEnemy 역시 자기만의 CDO를 가지며 그 안에는 수정된 기본값이 들어간다.

즉, 블루프린트에서 클래스 디폴트를 조정하는 행위는 사실상 그 블루프린트 클래스의 CDO를 편집하는 것에 가깝다.


9. CDO는 언제 사용되는가

CDO는 엔진 내부에서 여러 상황에 사용된다.

9.1 새 객체 생성 시 기본값 제공

새 객체를 만들 때 어떤 기본 상태로 시작할지 결정하는 기준이 된다.

9.2 에디터 기본값 표시

Details 패널에서 보이는 클래스 디폴트 값의 실체가 된다.

9.3 블루프린트 기본값 저장

블루프린트 에셋에서 설정한 기본값이 반영되는 대상이다.

9.4 직렬화와 차이 비교

객체가 기본값에서 얼마나 달라졌는지 비교하는 기준점으로 활용된다.

9.5 리플렉션 기반 기본값 조회

실제 객체를 만들지 않고도 해당 클래스의 기본 설정값을 읽어올 수 있다.


10. 코드에서 CDO 접근하기

CDO는 다음과 같은 방식으로 접근할 수 있다.

const AMyActor* DefaultActor = GetDefault<AMyActor>();

이 코드는 AMyActor 의 CDO를 가져온다.

예를 들어

int32 DefaultHealth = GetDefalut<AMyActor()->Health;

처럼 실제 인스턴스를 생성하지 않고도 기본값을 읽을 수 있다.

수정 가능한 CDO가 필요하면

AMyActor* MutableDefault = GetMutableDefault<AMyActor>();

를 사용할 수 있다.

다만 GetMutableDefault클래스 기본값 자체를 건드리는 행위이므로 주의해서 사용해야 한다.


11. 생성자에 넣어야하는 것과 넣지 말아야 하는 것

CDO 관점에서 보면 생성자에는 기본 상태 정의가 들어가야 한다.

생성자에 적합한 것

  • 기본 프로퍼티 값 설정
  • CreateDefaultSubobject 를 통한 기본 컴포넌트 생성
  • Attach 구조 설정
  • Tick 기본 설정
  • 렌더링/물리/충돌 관련 기본 플래그 설정
AMyActor::AMyActor()
{
    PrimaryActorTick.bCanEverTick = true;
    Health = 100;

    Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
    Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
    Mesh->SetupAttachment(Root);
}

생성자에 부적합한 것

  • GetWorld() 의존 로직
  • 플레이어 컨트롤러 접근
  • 게임 모드 접근
  • 런타임 객체 검색
  • 동적 스폰/파괴
  • BeginPlay 성격의 게임플레이 로직
  • 월드 상태를 가정한 처리
AMyActor::AMyActor()
{
    GetWorld()->GetFirstPlayerController(); // 부적절
}

이런 코드는 CDO 생성 시점에 정상적으로 동작하지 않거나 의도와 다른 결과를 만들 수 있다.


12. 자주 하는 오해

12.1 CDO는 단순한 클래스 메타데이터다.

아니다. 메타데이터는 UClass 이고, CDO는 실제 객체다.

12.2 생성자는 오직 인스턴스 초기화용이다

언리얼에서는 생성자가 CDO 구성에도 직접 관여한다.

12.3 CDO는 월드에 숨어 있는 엑터다.

아니다. CDO는 게임 우러드에 배체되어 플레이되는 엑터가 아니라 기본값 원본 객체다.

12.4 CDO를 바꾸면 기존 인스턴스가 전부 자동으로 바뀐다.

보통은 그렇지 않다. 기존 인스턴스는 이미 자신만의 런타임 상태를 가지고 있다.


13. 개념적으로 어떻게 이해하면 좋은가

언리얼의 객체 시스템은 다음 세 층으로 나눠서 보면 이해가 쉽다.

13.1 UClass

  • 이 타입은 무엇인가?
  • 부모 클래스는 무엇인가?
  • 어떤 프로퍼티를 가지는가?

13.2 CDO

  • 이 타입의 기본 상태는 무엇인가?
  • 기본 프로퍼티 값은 무엇인가?
  • 기본 컴포넌트 구조는 어떤가?

13.3 실제 인스턴스

  • 월드에 존재하는 실제 객체
  • CDO를 기준으로 시작하지만, 이후 상태는 독립적으로 변함

즉,

  • UClass = 타입 정보
  • CDO = 기본 상태 원본
  • Instance = 실제 게임 객체

이렇게 구분하면 횔씬 명확하다.


14. 엔진 구현 관점에서의 의미

직접 Unreal 스타일 오브젝트 시스템을 설계한다고 가정하면,

CDO는 다음과 같은 역할을 갖는 설계 요소다.

  • 각 타입(UClass)은 하나의 Default Object를 가진다.
  • 생성자는 그 Default Object의 기본 상태를 정의한다.
  • 새 객체는 Default Object를 기준으로 초기화된다.
  • 직렬화/복제/에디터 디폴트 편집의 기준점이 된다.

즉 CDO는 단순한 편의 기능이 아니라,

언리얼 객체 시스템 전체를 일관되게 유지하기 위한 핵심 장치라고 볼 수 있다.


15. 정리

CDO는 Unreal Engine에서 매우 중요한 개념이며, 핵심은 다음 한 문장으로 요약할 수 있다.

CDO는 특정 클래스의 기본 상태를 실제 객체 형태로 보관하는 원본 객체이다.

그리고 그 의미를 풀어쓰면 다음과 같다.

  • 클래스마다 하나의 CDO가 존재한다.
  • 생성자는 CDO의 기본값을 정의하는 성격이 강하다.
  • 실제 인스턴스는 CDO를 기준으로 초기화된다.
  • CDO는 기본 프로퍼티 값뿐 아니라 기본 컴포넌트 구조도 담는다.
  • 블루프린트의 클래스 디폴트 역시 CDO에 반영된다.
  • 에디터, 직렬화, 복제, 기본값 비교 등 다양한 시스템의 기준점이 된다.