<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>데브 지노 - 지노윈의 개발 스토리</title>
    <link>https://devjino.tistory.com/</link>
    <description>훌륭한 프로그래머가 아니라, 훌륭한 습관을 가진 좋은 프로그래머.</description>
    <language>ko</language>
    <pubDate>Tue, 19 May 2026 00:09:55 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>지노윈</managingEditor>
    <image>
      <title>데브 지노 - 지노윈의 개발 스토리</title>
      <url>https://tistory1.daumcdn.net/tistory/3208245/attach/8c58a5ff74c54335837a71dadda8d6f9</url>
      <link>https://devjino.tistory.com</link>
    </image>
    <item>
      <title>Unreal Engine 에셋 매니저(Asset Manager)의 번들(Bundle) 기능</title>
      <link>https://devjino.tistory.com/457</link>
      <description>&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;비동기 로딩이 진행되는 동안 사용자에게 진행 상황을 보여주는 &lt;b data-index-in-node=&quot;34&quot; data-path-to-node=&quot;0&quot;&gt;로딩 화면(UI) 구현&lt;/b&gt;과 에셋을 그룹 단위로 관리하는 &lt;b data-index-in-node=&quot;64&quot; data-path-to-node=&quot;0&quot;&gt;에셋 매니저(Asset Manager)의 번들(Bundle)&lt;/b&gt; 기능에 대해 설명해 드릴게요.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;1&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;2&quot; data-ke-size=&quot;size23&quot;&gt;비동기 로딩과 UI 연동 (진척도 표시)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;비동기 로딩을 요청하면 TSharedPtr&amp;lt;FStreamableHandle&amp;gt;이라는 핸들을 반환받습니다. 이 핸들을 통해 현재 로딩이 얼마나 진행되었는지 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4&quot;&gt;[C++ 예시: 진척도 확인]&lt;/b&gt;&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwiB2Kj2geyRAxUAAAAAHQAAAAAQbA&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// 1. 로드 요청 시 핸들을 보관합니다.
TSharedPtr&amp;lt;FStreamableHandle&amp;gt; LoadHandle;

void AMyGameMode::StartLoading(TArray&amp;lt;FSoftObjectPath&amp;gt; AssetsToLoad)
{
    FStreamableManager&amp;amp; Loader = UAssetManager::GetStreamableManager();
    
    // 핸들을 저장하여 상태를 추적합니다.
    LoadHandle = Loader.RequestAsyncLoad(AssetsToLoad, FStreamableDelegate::CreateUObject(this, &amp;amp;AMyGameMode::OnLoadFinished));
}

void AMyGameMode::Tick(float DeltaTime)
{
    if (LoadHandle.IsValid() &amp;amp;&amp;amp; LoadHandle-&amp;gt;IsLoadingInProgress())
    {
        // 2. 0.0 ~ 1.0 사이의 진행률을 가져와 UI(ProgressBar)에 전달합니다.
        float Progress = LoadHandle-&amp;gt;GetProgress();
        MyLoadingWidget-&amp;gt;UpdateProgressBar(Progress);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-path-to-node=&quot;6&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;7&quot; data-ke-size=&quot;size23&quot;&gt;에셋 매니저와 번들(Bundles) 활용&lt;/h3&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;번들은 에셋들을 특정 상황(예: &quot;Lobby&quot;, &quot;InGame&quot;, &quot;BossFight&quot;)에 맞춰 그룹화하는 기능입니다. 데이터 테이블 내의 TSoftObjectPtr에 번들 이름을 지정해두면, 에셋 매니저가 한 번에 해당 그룹을 로드하거나 해제할 수 있습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9&quot;&gt;[설계 방법]&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;Primary Data Asset 사용:&lt;/b&gt; UPrimaryDataAsset을 상속받은 클래스를 만들고, 에셋 매니저에 등록합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;번들 태그 지정:&lt;/b&gt; UPROPERTY 메타데이터에 AssetBundles를 추가합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwiB2Kj2geyRAxUAAAAAHQAAAAAQbQ&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;UCLASS()
class UMyItemAsset : public UPrimaryDataAsset
{
    GENERATED_BODY()

public:
    // 이 메쉬는 'Visual'이라는 번들 이름을 가집니다.
    UPROPERTY(EditAnywhere, BlueprintReadWrite, AssetRegistrySearchable, meta = (AssetBundles = &quot;Visual&quot;))
    TSoftObjectPtr&amp;lt;UStaticMesh&amp;gt; ItemMesh;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;12&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;번들 단위 로드:&lt;/b&gt; 에셋 매니저의 LoadPrimaryAsset 함수를 호출할 때, 로드하고 싶은 번들 리스트(Visual)를 넘겨주면 해당 그룹의 소프트 포인터들만 한꺼번에 로드됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-path-to-node=&quot;13&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;14&quot; data-ke-size=&quot;size23&quot;&gt;실무 로딩 화면 시퀀스&lt;/h3&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;일반적으로 다음과 같은 순서로 로직을 구성합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;16&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,0,0&quot;&gt;UI 생성:&lt;/b&gt; 로딩 바가 있는 위젯을 화면에 띄웁니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,1,0&quot;&gt;로드 요청:&lt;/b&gt; 에셋 매니저에게 필요한 에셋(또는 번들) 로드를 요청하고 핸들을 받습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,2,0&quot;&gt;상태 체크:&lt;/b&gt; Tick이나 Timer를 통해 핸들의 GetProgress()를 UI에 동기화합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,3,0&quot;&gt;완료 콜백:&lt;/b&gt; 로드가 완료되면 핸들의 Delegate가 실행되고, 로딩 UI를 제거한 뒤 게임을 시작합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-path-to-node=&quot;17&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;18&quot; data-ke-size=&quot;size23&quot;&gt;주의할 점: 메모리 누수 방지&lt;/h3&gt;
&lt;p data-path-to-node=&quot;19&quot; data-ke-size=&quot;size16&quot;&gt;비동기 로드 핸들(FStreamableHandle)은 로드가 끝난 후에도 핸들을 &lt;b&gt;명시적으로 해제(Release)&lt;/b&gt;하지 않으면 메모리에 해당 에셋들을 계속 붙잡고 있을 수 있습니다. TSharedPtr인 핸들이 유효 범위를 벗어나거나 수동으로 CancelHandle()을 호출하여 관리해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>게임 개발/Unreal Engine 기본</category>
      <author>지노윈</author>
      <guid isPermaLink="true">https://devjino.tistory.com/457</guid>
      <comments>https://devjino.tistory.com/457#entry457comment</comments>
      <pubDate>Fri, 2 Jan 2026 18:14:35 +0900</pubDate>
    </item>
    <item>
      <title>Unreal Engine의 Data Table과 TSoftObjectPtr 활용한 응</title>
      <link>https://devjino.tistory.com/456</link>
      <description>&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;데이터 테이블(Data Table)과 TSoftObjectPtr을 결합하는 방식은 대규모 RPG나 수집형 게임에서 &lt;b data-index-in-node=&quot;64&quot; data-path-to-node=&quot;0&quot;&gt;메모리 효율을 극대화하는 핵심 설계 패턴&lt;/b&gt;입니다. 모든 아이템 정보를 담은 데이터 테이블이 메모리에 올라와 있어도, 실제 사용되는 리소스(메쉬, 텍스처)는 필요한 순간에만 로드하기 때문입니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;2&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size23&quot;&gt;구조체(Struct) 정의&lt;/h3&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;먼저 데이터 테이블의 행(Row)이 될 구조체를 정의합니다. 이때 리소스는 반드시 TSoftObjectPtr로 선언합니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwiB2Kj2geyRAxUAAAAAHQAAAAAQWg&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;&lt;span&gt;C++&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;USTRUCT(BlueprintType)
struct FItemData : public FTableRowBase
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString ItemName;

    // 하드 참조가 아닌 소프트 참조로 선언!
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    TSoftObjectPtr&amp;lt;UStaticMesh&amp;gt; ItemMesh;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    TSoftObjectPtr&amp;lt;UTexture2D&amp;gt; ItemIcon;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-path-to-node=&quot;6&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;7&quot; data-ke-size=&quot;size23&quot;&gt;에셋 관리자(Asset Manager)를 통한 로드 제어&lt;/h3&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;데이터 테이블에서 특정 아이템 정보를 가져와 비동기 로드하는 로직을 구현합니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwiB2Kj2geyRAxUAAAAAHQAAAAAQWw&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;void AMyCharacter::EquipItem(FName ItemRowName)
{
    // 1. 데이터 테이블에서 정보 가져오기
    static const FString ContextString(TEXT(&quot;ItemContext&quot;));
    FItemData* Data = ItemDataTable-&amp;gt;FindRow&amp;lt;FItemData&amp;gt;(ItemRowName, ContextString);

    if (Data)
    {
        // 2. 이미 로드되어 있는지 확인
        if (Data-&amp;gt;ItemMesh.IsValid())
        {
            ApplyItemMesh(Data-&amp;gt;ItemMesh.Get());
        }
        else
        {
            // 3. 비동기 로드 요청
            FStreamableManager&amp;amp; Loader = UAssetManager::GetStreamableManager();
            
            // 람다(Lambda)를 사용하면 콜백 함수를 따로 만들지 않아도 되어 편리합니다.
            Loader.RequestAsyncLoad(Data-&amp;gt;ItemMesh.ToSoftObjectPath(), 
                FStreamableDelegate::CreateLambda([this, Data]() {
                    if (Data-&amp;gt;ItemMesh.IsValid())
                    {
                        ApplyItemMesh(Data-&amp;gt;ItemMesh.Get());
                    }
                })
            );
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;이 구조를 사용하면 데이터 테이블 자체는 가벼운 &quot;텍스트/경로 정보&quot;만 들고 있게 됩니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;13&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;14&quot; data-ke-size=&quot;size23&quot;&gt;실무에서 아주 유용한 &quot;Primary Asset Label&quot; 팁&lt;/h3&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;데이터 테이블에 등록된 수많은 TSoftObjectPtr 에셋들을 나중에 한꺼번에 패키징하거나 관리하기 어려울 때가 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;16&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,0,0&quot;&gt;Asset Manager 설정:&lt;/b&gt; 프로젝트 세팅의 Asset Manager에서 특정 폴더나 데이터 테이블을 등록해두면, 게임 빌드 시 해당 에셋들이 누락되지 않도록 자동으로 관리할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,1,0&quot;&gt;번들(Bundles):&lt;/b&gt; &quot;InGame&quot;, &quot;Menu&quot;, &quot;Cinematic&quot; 같은 태그를 붙여, 특정 상황에서 필요한 에셋 뭉치만 한 번에 로드하는 기능도 지원합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;17&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;18&quot; data-ke-size=&quot;size23&quot;&gt;요약: 데이터 테이블 활용 전략&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;19&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,0,0&quot;&gt;데이터 테이블 로드:&lt;/b&gt; 게임 시작 시 혹은 필요한 시점에 테이블을 로드합니다 (텍스트 데이터라 매우 가벼움).&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,1,0&quot;&gt;리소스 참조:&lt;/b&gt; 테이블 내부의 TSoftObjectPtr을 통해 필요한 에셋의 경로만 파악합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,2,0&quot;&gt;지연 로딩:&lt;/b&gt; 실제 아이템이 화면에 그려지기 직전에 비동기로 로드합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,3,0&quot;&gt;메모리 해제:&lt;/b&gt; 해당 아이템을 더 이상 쓰지 않으면 참조를 해제하여 GC가 수거하게 합니다.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>게임 개발/Unreal Engine 기본</category>
      <author>지노윈</author>
      <guid isPermaLink="true">https://devjino.tistory.com/456</guid>
      <comments>https://devjino.tistory.com/456#entry456comment</comments>
      <pubDate>Fri, 2 Jan 2026 14:07:58 +0900</pubDate>
    </item>
    <item>
      <title>Unreal Engine 비동기 로딩</title>
      <link>https://devjino.tistory.com/455</link>
      <description>&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;비동기 로딩은 게임의 끊김(Stuttering)을 방지하는 핵심 기술입니다. TSoftObjectPtr을 실제 프로젝트에서 효과적으로 사용하는 두 가지 핵심 방법(C++와 블루프린트)을 정리해 드릴게요.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;1&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;2&quot; data-ke-size=&quot;size23&quot;&gt;1. C++: FStreamableManager를 이용한 비동기 로딩&lt;/h3&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;가장 권장되는 방식입니다. 엔진의 전역 매니저를 사용하여 백그라운드 스레드에서 에셋을 로드하고, 완료되면 콜백 함수를 실행합니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4&quot;&gt;[코드 예시]&lt;/b&gt;&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwiB2Kj2geyRAxUAAAAAHQAAAAAQSg&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// Header (.h)
UPROPERTY(EditAnywhere, Category = &quot;Assets&quot;)
TSoftObjectPtr&amp;lt;UStaticMesh&amp;gt; SubMeshPtr;

void OnMeshLoaded(); // 로드 완료 후 실행될 함수

// Source (.cpp)
void AMyActor::StartAsyncLoad()
{
    if (SubMeshPtr.IsPending())
    {
        // 1. 스트리밍 매니저 가져오기 (보통 GameInstance나 전역으로 관리)
        FStreamableManager&amp;amp; AssetLoader = UAssetManager::GetStreamableManager();
        
        // 2. 비동기 로드 요청
        AssetLoader.RequestAsyncLoad(SubMeshPtr.ToSoftObjectPath(), 
            FStreamableDelegate::CreateUObject(this, &amp;amp;AMyActor::OnMeshLoaded));
    }
}

void AMyActor::OnMeshLoaded()
{
    // 3. 로드가 완료되었으므로 Get()을 통해 실제 포인터를 가져옴
    UStaticMesh* LoadedMesh = SubMeshPtr.Get();
    if (LoadedMesh)
    {
        MyMeshComponent-&amp;gt;SetStaticMesh(LoadedMesh);
        UE_LOG(LogTemp, Log, TEXT(&quot;Async Load Complete!&quot;));
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-path-to-node=&quot;6&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;7&quot; data-ke-size=&quot;size23&quot;&gt;2. 블루프린트: Async Load Asset 노드 사용&lt;/h3&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;C++보다 훨씬 직관적이며, 복잡한 로직이 필요 없는 UI나 간단한 액터 로직에 자주 쓰입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;9&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;변수 생성:&lt;/b&gt; 변수 타입을 원하는 객체(예: Static Mesh)의 &lt;b data-index-in-node=&quot;38&quot; data-path-to-node=&quot;9,0,0&quot;&gt;Soft Object Reference&lt;/b&gt;로 설정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,0&quot;&gt;노드 배치:&lt;/b&gt; &lt;b data-index-in-node=&quot;7&quot; data-path-to-node=&quot;9,1,0&quot;&gt;Async Load Asset&lt;/b&gt; 노드를 배치하고 Soft Object Reference를 연결합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,2,0&quot;&gt;완료 처리:&lt;/b&gt; Completed 핀이 실행되면, Get Asset 노드를 통해 로드된 객체를 사용합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-path-to-node=&quot;10&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;11&quot; data-ke-size=&quot;size23&quot;&gt;3. 실무 활용 팁: &quot;언제 로드하고 언제 해제할까?&quot;&lt;/h3&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;TSoftObjectPtr을 효율적으로 쓰기 위한 전략입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;13&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,0,0&quot;&gt;인벤토리 시스템:&lt;/b&gt; 아이템 데이터 테이블에는 모든 아이콘을 TSoftObjectPtr로 담아둡니다. 인벤토리 창을 &lt;b data-index-in-node=&quot;63&quot; data-path-to-node=&quot;13,0,0&quot;&gt;열 때만&lt;/b&gt; 화면에 보이는 아이콘들을 비동기 로드하고, 창을 &lt;b data-index-in-node=&quot;95&quot; data-path-to-node=&quot;13,0,0&quot;&gt;닫으면&lt;/b&gt; 참조를 해제(null 대입)하여 메모리를 확보합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,1,0&quot;&gt;보스전:&lt;/b&gt; 플레이어가 보스 방 근처 트리거에 진입했을 때 보스의 고해상도 메쉬와 이펙트를 로드하기 시작합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,2,0&quot;&gt;참조 해제:&lt;/b&gt; TSoftObjectPtr 자체는 스마트 포인터이므로, 이를 소유한 클래스가 파괴되거나 해당 변수에 다른 값을 넣으면 가비지 컬렉터(GC)가 판단하여 메모리에서 해제합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;14&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;15&quot; data-ke-size=&quot;size23&quot;&gt;4. 주의사항: &quot;Hard Reference&quot;를 조심하세요!&lt;/h3&gt;
&lt;p data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;만약 TSoftObjectPtr 변수를 선언해두고, 코드 어딘가에서 이 변수를 &lt;b data-index-in-node=&quot;44&quot; data-path-to-node=&quot;16&quot;&gt;일반 포인터(UStaticMesh*) 변수에 담아버리면&lt;/b&gt;, 그 즉시 하드 참조가 되어 비동기 로딩의 이점이 사라집니다. 로드가 끝난 후에도 필요한 짧은 순간에만 지역 변수로 받아 쓰고, 멤버 변수로는 계속 TSoftObjectPtr 상태를 유지하는 것이 좋습니다.&lt;/p&gt;</description>
      <category>게임 개발/Unreal Engine 기본</category>
      <author>지노윈</author>
      <guid isPermaLink="true">https://devjino.tistory.com/455</guid>
      <comments>https://devjino.tistory.com/455#entry455comment</comments>
      <pubDate>Fri, 2 Jan 2026 14:03:28 +0900</pubDate>
    </item>
    <item>
      <title>Unreal Engine의 TSoftObjectPtr 이란?</title>
      <link>https://devjino.tistory.com/454</link>
      <description>&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;TObjectPtr이 &quot;이미 로드된 객체를 어떻게 가리킬 것인가&quot;에 집중한다면,&amp;nbsp; &lt;b&gt;TSoftObjectPtr은 &quot;아직 로드되지 않은 객체를 어떻게 안전하게 가리키고, 원할 때 로드할 것인가&quot;&lt;/b&gt;에 특화된 스마트 포인터입니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;2&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size23&quot;&gt;1. Hard Reference vs Soft Reference&lt;/h3&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;가장 큰 차이는 &lt;b data-index-in-node=&quot;9&quot; data-path-to-node=&quot;4&quot;&gt;메모리 로드 타이밍&lt;/b&gt;입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;Hard Reference (TObjectPtr, UPROPERTY 포인터):&lt;/b&gt; A 클래스가 B 에셋을 하드 참조하면, A가 메모리에 로드될 때 B도 &lt;b data-index-in-node=&quot;83&quot; data-path-to-node=&quot;5,0,0&quot;&gt;반드시&lt;/b&gt; 함께 로드됩니다. 이를 &quot;줄줄이 비엔나&quot;처럼 연쇄 로딩이 발생한다고 표현하며, 메모리 낭비의 주범이 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;Soft Reference (TSoftObjectPtr):&lt;/b&gt; A가 B를 소프트 참조하면, A가 로드되어도 B는 로드되지 않습니다. 대신 B가 있는 &lt;b data-index-in-node=&quot;81&quot; data-path-to-node=&quot;5,1,0&quot;&gt;경로(Path) 문자열&lt;/b&gt;만 가지고 있습니다. 실제 객체는 개발자가 코드로 &quot;지금 로드해!&quot;라고 명령할 때만 메모리에 올라옵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;6&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;7&quot; data-ke-size=&quot;size23&quot;&gt;2. 주요 특징 및 장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;메모리 최적화:&lt;/b&gt; 게임 시작 시 모든 무기, 캐릭터, 맵 데이터를 로드할 필요 없이, 플레이어가 해당 아이템을 획득하거나 지역에 진입할 때만 로드할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;비동기 로딩 지원:&lt;/b&gt; 게임이 멈추지 않게 백그라운드에서 에셋을 로드하는 FStreamableManager와 함께 자주 사용됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,0&quot;&gt;안전한 참조:&lt;/b&gt; 객체가 메모리에서 해제되었는지 여부를 추적할 수 있어, 댕글링 포인터(Dangling Pointer) 문제를 방지합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;9&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;10&quot; data-ke-size=&quot;size23&quot;&gt;3. 실전 사용법 (C++)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;TSoftObjectPtr은 일반 포인터처럼 바로 쓸 수 없고, &lt;b data-index-in-node=&quot;36&quot; data-path-to-node=&quot;11&quot;&gt;동기화(Load)&lt;/b&gt; 과정이 필요합니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwiB2Kj2geyRAxUAAAAAHQAAAAAQMA&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// 1. 선언 (헤더 파일)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = &quot;Assets&quot;)
TSoftObjectPtr&amp;lt;UStaticMesh&amp;gt; MeshPtr;

// 2. 사용 (소스 파일)
// 현재 메모리에 로드되어 있는지 확인
if (MeshPtr.IsPending()) 
{
    // 방법 A: 동기 로드 (잠깐 멈춤 발생 가능)
    UStaticMesh* LoadedMesh = MeshPtr.LoadSynchronous();
}

// 이미 로드되어 있다면 Get()으로 바로 가져올 수 있음
UStaticMesh* Mesh = MeshPtr.Get(); 
if (Mesh)
{
    // 로직 수행
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-path-to-node=&quot;13&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;14&quot; data-ke-size=&quot;size23&quot;&gt;4. 언제 사용해야 할까요?&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;15&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;상황&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;추천하는 포인터&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,1,0,0&quot;&gt;항상 사용되는 필수 컴포넌트 (예: 캐릭터의 Mesh)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,1,1,0&quot;&gt;TObjectPtr (Hard)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,2,0,0&quot;&gt;UI 아이콘, 특정 퀘스트에서만 나오는 NPC&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,2,1,0&quot;&gt;TSoftObjectPtr (Soft)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,3,0,0&quot;&gt;수백 개의 아이템 데이터 시트&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,3,1,0&quot;&gt;TSoftObjectPtr (Soft)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,4,0,0&quot;&gt;레벨에 배치된 다른 액터 참조&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,4,1,0&quot;&gt;TSoftObjectPtr (Soft)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-path-to-node=&quot;16&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;17&quot; data-ke-size=&quot;size23&quot;&gt;5. 요약하자면&lt;/h3&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;TSoftObjectPtr은 &lt;b data-index-in-node=&quot;16&quot; data-path-to-node=&quot;18&quot;&gt;&quot;주소&quot; 대신 &quot;주소록(경로)&quot;을 들고 있는 것&lt;/b&gt;과 같습니다. 주소록만으론 사람(객체)을 직접 만날 수 없지만, 필요할 때 전화를 걸어(Load) 불러낼 수 있는 것과 같은 원리입니다.&lt;/p&gt;</description>
      <category>게임 개발/Unreal Engine 기본</category>
      <author>지노윈</author>
      <guid isPermaLink="true">https://devjino.tistory.com/454</guid>
      <comments>https://devjino.tistory.com/454#entry454comment</comments>
      <pubDate>Fri, 2 Jan 2026 14:00:48 +0900</pubDate>
    </item>
    <item>
      <title>블루프린트의 Construction Script(CS) 노드와 SCS</title>
      <link>https://devjino.tistory.com/453</link>
      <description>&lt;h2 data-path-to-node=&quot;2&quot; data-ke-size=&quot;size26&quot;&gt;1. SCS vs Construction Script (CS)&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;3&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;SCS (Simple Construction Script)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Construction Script (블루프린트 노드)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;3,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,0,0&quot;&gt;정체&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;3,1,1,0&quot;&gt;컴포넌트 구조의 &lt;b data-index-in-node=&quot;9&quot; data-path-to-node=&quot;3,1,1,0&quot;&gt;데이터(설계도)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;3,1,2,0&quot;&gt;컴포넌트 생성 후 실행되는 &lt;b data-index-in-node=&quot;15&quot; data-path-to-node=&quot;3,1,2,0&quot;&gt;로직(함수)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;3,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,2,0,0&quot;&gt;하는 일&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;3,2,1,0&quot;&gt;&quot;이 액터는 메쉬 1개, 라이트 1개가 있다&quot;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;3,2,2,0&quot;&gt;&quot;메쉬의 색상을 랜덤하게 바꾸고, 라이트 밝기를 조절해라&quot;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;3,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,3,0,0&quot;&gt;실행 시점&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;3,3,1,0&quot;&gt;액터 생성 직후, 컴포넌트 탄생 단계&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;3,3,2,0&quot;&gt;SCS가 컴포넌트 조립을 마친 직후&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;3,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,4,0,0&quot;&gt;편집 방법&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;3,4,1,0&quot;&gt;컴포넌트 패널에서 마우스 드래그&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;3,4,2,0&quot;&gt;그래프 에디터에서 노드 연결&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-path-to-node=&quot;4&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;5&quot; data-ke-size=&quot;size26&quot;&gt;2. 데이터의 흐름 (생성 순서)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;액터가 월드에 배치되거나 스폰될 때, 데이터는 다음과 같은 순서로 흐릅니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;7&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;SCS 단계 (정적 구성):&lt;/b&gt; 에디터에서 미리 붙여둔 컴포넌트들이 실제 메모리에 할당됩니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;7,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이때 변수들은 &lt;b data-index-in-node=&quot;8&quot; data-path-to-node=&quot;7,0,1,0,0&quot;&gt;CDO&lt;/b&gt;에 저장된 기본값을 따릅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,1,0&quot;&gt;변수 주입:&lt;/b&gt; Expose on Spawn으로 설정된 변수들이 이 시점에 들어옵니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,2,0&quot;&gt;Construction Script 단계 (동적 수정):&lt;/b&gt; 이제 막 생성된 컴포넌트들을 노드 로직을 통해 &lt;b data-index-in-node=&quot;60&quot; data-path-to-node=&quot;7,2,0&quot;&gt;수정&lt;/b&gt;합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;7,2,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어, &quot;나의 팀 ID 변수가 1이면 메쉬를 빨간색으로, 2이면 파란색으로 바꿔라&quot; 같은 로직이 여기서 돌아갑니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-path-to-node=&quot;8&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size26&quot;&gt;3. 런타임 컴포넌트 추가와의 차이점&lt;/h2&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;게임 도중에 AddComponent 노드로 컴포넌트를 추가하는 것과 SCS의 차이점은 &lt;b&gt;직렬화(Serialization)&lt;/b&gt;와 &lt;b data-index-in-node=&quot;72&quot; data-path-to-node=&quot;10&quot;&gt;에디터 가시성&lt;/b&gt;입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,0&quot;&gt;SCS/CS 컴포넌트:&lt;/b&gt; 에디터 뷰포트에서 즉시 보이며, 레벨을 저장할 때 해당 컴포넌트의 설정값도 함께 저장됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0&quot;&gt;런타임 추가 컴포넌트:&lt;/b&gt; 게임이 실행된 후에만 존재하며, 에디터 상의 액터 데이터에는 포함되지 않습니다. (휘발성)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;12&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;13&quot; data-ke-size=&quot;size26&quot;&gt;실무 팁: 최적화 주의사항&lt;/h2&gt;
&lt;p data-path-to-node=&quot;14&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14&quot;&gt;Construction Script&lt;/b&gt;는 에디터에서 액터를 &lt;b data-index-in-node=&quot;31&quot; data-path-to-node=&quot;14&quot;&gt;살짝만 움직여도 매 프레임 재실행&lt;/b&gt;됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;15&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;따라서 CS 노드 안에 무거운 계산이나 복잡한 루프를 넣으면 에디터가 매우 버벅이게 됩니다.&lt;/li&gt;
&lt;li&gt;반면 &lt;b data-index-in-node=&quot;3&quot; data-path-to-node=&quot;15,1,0&quot;&gt;SCS&lt;/b&gt;는 단순히 &quot;데이터&quot;를 나열한 것이므로 성능 부하가 거의 없습니다. 가능하면 시각적인 구조는 SCS(컴포넌트 패널)에서 잡는 것이 좋습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>게임 개발/Unreal Engine 기본</category>
      <author>지노윈</author>
      <guid isPermaLink="true">https://devjino.tistory.com/453</guid>
      <comments>https://devjino.tistory.com/453#entry453comment</comments>
      <pubDate>Tue, 30 Dec 2025 14:01:57 +0900</pubDate>
    </item>
    <item>
      <title>Unreal Engine SCS(Simple Construction Script)</title>
      <link>https://devjino.tistory.com/452</link>
      <description>&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;언리얼 엔진에서 &lt;b&gt;SCS(Simple Construction Script)&lt;/b&gt;는 우리가 블루프린트 에디터의 &lt;b data-index-in-node=&quot;61&quot; data-path-to-node=&quot;0&quot;&gt;'컴포넌트(Components)' 패널&lt;/b&gt;에서 컴포넌트를 추가하고, 계층 구조를 잡고, 설정을 변경하는 모든 시각적 작업을 코드로 관리하는 시스템입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;쉽게 말해, &lt;b&gt;&quot;블루프린트에서 마우스로 컴포넌트를 조립한 내역을 담고 있는 청사진&quot;&lt;/b&gt;이라고 정의할 수 있습니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;2&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size26&quot;&gt;1. SCS의 핵심 역할&lt;/h2&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;우리가 블루프린트 에디터에서 StaticMesh를 추가하고 SceneComponent 아래에 배치하면, 엔진 내부적으로는 다음과 같은 일이 일어납니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;계층 구조 저장:&lt;/b&gt; 어떤 컴포넌트가 부모이고 자식인지에 대한 정보를 저장합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;프로퍼티 기록:&lt;/b&gt; 컴포넌트의 위치(Transform), 할당된 메쉬, 라이트 강도 등의 수정 사항을 기록합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,0&quot;&gt;인스턴스화:&lt;/b&gt; 게임이 실행되거나 액터가 월드에 배치될 때, SCS에 기록된 정보를 바탕으로 실제 컴포넌트 객체들을 생성하고 부착합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-path-to-node=&quot;6&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;7&quot; data-ke-size=&quot;size26&quot;&gt;2. SCS와 C++의 관계&lt;/h2&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;SCS는 주로 &lt;b data-index-in-node=&quot;8&quot; data-path-to-node=&quot;8&quot;&gt;블루프린트에서 추가된 컴포넌트&lt;/b&gt;를 다룹니다. C++ 코드와 비교하면 이해가 빠릅니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;9&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;C++ 방식:&lt;/b&gt; CreateDefaultSubobject&amp;lt;T&amp;gt;()를 호출하여 생성자에서 직접 컴포넌트를 만듭니다. 이 정보는 앞서 배운 &lt;b data-index-in-node=&quot;75&quot; data-path-to-node=&quot;9,0,0&quot;&gt;CDO&lt;/b&gt;에 포함됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,0&quot;&gt;SCS 방식:&lt;/b&gt; 블루프린트 에디터에서 'Add Component' 버튼으로 추가합니다. 이 정보는 &lt;b data-index-in-node=&quot;54&quot; data-path-to-node=&quot;9,1,0&quot;&gt;SCS&lt;/b&gt;라는 데이터 구조에 저장되었다가, 액터가 생성될 때 실행됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-path-to-node=&quot;10&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-path-to-node=&quot;10,0&quot; data-ke-size=&quot;size16&quot;&gt;[!NOTE] 만약 C++ 클래스를 상속받은 블루프린트를 만들면, 해당 액터는 **[C++에서 만든 CDO의 컴포넌트] + [블루프린트 SCS에서 추가한 컴포넌트]**가 합쳐진 형태가 됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-path-to-node=&quot;11&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;12&quot; data-ke-size=&quot;size26&quot;&gt;3. 액터 생성 시 동작 순서&lt;/h2&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;액터가 생성될 때 컴포넌트들이 조립되는 과정은 대략 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;14&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,0,0&quot;&gt;C++ 생성자 호출:&lt;/b&gt; C++에 정의된 기본 컴포넌트들이 먼저 생성됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,1,0&quot;&gt;SCS 실행:&lt;/b&gt; 블루프린트에서 추가한 컴포넌트들이 생성되고 계층 구조에 따라 부착(Attach)됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,2,0&quot;&gt;Construction Script 실행:&lt;/b&gt; 사용자가 블루프린트의 Construction Script 그래프에 짠 로직이 실행되며 최종 세팅을 마칩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-path-to-node=&quot;15&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;16&quot; data-ke-size=&quot;size26&quot;&gt;4. 왜 SCS라고 부르나요?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;17&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,0,0&quot;&gt;Simple:&lt;/b&gt; 복잡한 프로그래밍 없이 시각적인 도구로 구성할 수 있다는 의미입니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,1,0&quot;&gt;Construction:&lt;/b&gt; 액터가 &quot;건설(생성)&quot;되는 과정에서 필요한 설계도라는 의미입니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,2,0&quot;&gt;Script:&lt;/b&gt; 명령문처럼 차례대로 컴포넌트를 생성하고 설정하는 절차를 담고 있기 때문입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;18&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;19&quot; data-ke-size=&quot;size23&quot;&gt;요약하자면&lt;/h3&gt;
&lt;p data-path-to-node=&quot;20&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20&quot;&gt;SCS&lt;/b&gt;는 블루프린트 에디터에서 우리가 조립한 &lt;b data-index-in-node=&quot;25&quot; data-path-to-node=&quot;20&quot;&gt;컴포넌트들의 설계도 정보&lt;/b&gt;이며, 이를 통해 엔진은 액터가 생성될 때마다 매번 똑같은 구조의 컴포넌트들을 자동으로 생성하고 붙여줄 수 있습니다.&lt;/p&gt;</description>
      <category>게임 개발/Unreal Engine 기본</category>
      <author>지노윈</author>
      <guid isPermaLink="true">https://devjino.tistory.com/452</guid>
      <comments>https://devjino.tistory.com/452#entry452comment</comments>
      <pubDate>Tue, 30 Dec 2025 13:59:32 +0900</pubDate>
    </item>
    <item>
      <title>Unreal Engine CDO 사용 방법</title>
      <link>https://devjino.tistory.com/451</link>
      <description>&lt;h2 data-path-to-node=&quot;2&quot; data-ke-size=&quot;size26&quot;&gt;1. 코드에서 CDO에 접근하는 방법&lt;/h2&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;실행 시간에 특정 클래스의 기본값을 가져오고 싶을 때, 액터를 스폰(SpawnActor)하지 않고도 아래의 함수들을 사용하여 CDO에 접근할 수 있습니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;4&quot; data-ke-size=&quot;size23&quot;&gt;GetDefault&amp;lt;T&amp;gt;() 사용 (가장 권장됨)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;템플릿 기반으로 작성되어 가독성이 좋고 안전합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;zephir&quot;&gt;&lt;code&gt;// AMyWeapon 클래스의 CDO에서 기본 데미지 값을 가져오기
AMyWeapon* DefaultWeapon = GetDefault&amp;lt;AMyWeapon&amp;gt;();
float Damage = DefaultWeapon-&amp;gt;BaseDamage;

// 클래스 변수(UClass*)가 있을 때
UClass* WeaponClass = AMyWeapon::StaticClass();
const AMyWeapon* DefaultObj = GetDefault&amp;lt;AMyWeapon&amp;gt;(WeaponClass);
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;7&quot; data-ke-size=&quot;size23&quot;&gt;GetDefaultObject() 사용&lt;/h3&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;UClass 포인터를 가지고 있을 때 유용하며, UObject 타입을 반환하므로 형변환이 필요합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;UClass* MyClass = AMyActor::StaticClass();
AMyActor* CDO = MyClass-&amp;gt;GetDefaultObject&amp;lt;AMyActor&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-path-to-node=&quot;10&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;11&quot; data-ke-size=&quot;size26&quot;&gt;2. 생성자(Constructor)와 CDO의 관계&lt;/h2&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;언리얼 엔진에서 &lt;b data-index-in-node=&quot;9&quot; data-path-to-node=&quot;12&quot;&gt;생성자는 주로 CDO를 설정하기 위해 실행&lt;/b&gt;됩니다. 우리가 생성자 내부에 작성하는 코드들은 사실상 &quot;이 클래스의 마스터 복사본(CDO)은 이렇게 생겨야 한다&quot;라고 정의하는 과정입니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;13&quot; data-ke-size=&quot;size23&quot;&gt;생성자에서 주로 하는 일&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;14&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,0,0&quot;&gt;컴포넌트 생성:&lt;/b&gt; CreateDefaultSubobject를 사용하여 기본 컴포넌트 구조를 잡습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,1,0&quot;&gt;기본값 할당:&lt;/b&gt; 변수의 초기값을 설정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,2,0&quot;&gt;에셋 레퍼런스 지정:&lt;/b&gt; FObjectFinder 등을 통해 기본 메쉬나 텍스처를 연결합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;AMyCharacter::AMyCharacter()
{
    // CDO에 포함될 기본 컴포넌트 생성
    MeshComponent = CreateDefaultSubobject&amp;lt;UStaticMeshComponent&amp;gt;(TEXT(&quot;VisualMesh&quot;));
    
    // CDO의 기본 변수값 설정
    MoveSpeed = 600.0f;
    bCanDoubleJump = false;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-path-to-node=&quot;16&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;17&quot; data-ke-size=&quot;size26&quot;&gt;3. CDO 사용 시 주의할 점 (심화)&lt;/h2&gt;
&lt;h3 data-path-to-node=&quot;18&quot; data-ke-size=&quot;size23&quot;&gt;런타임 수정 금지&lt;/h3&gt;
&lt;p data-path-to-node=&quot;19&quot; data-ke-size=&quot;size16&quot;&gt;앞서 말씀드렸듯 CDO는 &lt;b data-index-in-node=&quot;14&quot; data-path-to-node=&quot;19&quot;&gt;전역 마스터&lt;/b&gt;입니다. 만약 게임 도중에 GetDefault&amp;lt;AMyCharacter&amp;gt;()-&amp;gt;MoveSpeed = 1000.0f; 처럼 값을 수정하면, 그 이후에 생성되는 모든 캐릭터의 기본 이동 속도가 1000으로 고정됩니다. (이것은 의도치 않은 버그의 원인이 됩니다.)&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;20&quot; data-ke-size=&quot;size23&quot;&gt;가비지 컬렉션 (GC)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;21&quot; data-ke-size=&quot;size16&quot;&gt;CDO는 엔진이 켜져 있는 동안 &lt;b data-index-in-node=&quot;18&quot; data-path-to-node=&quot;21&quot;&gt;절대 파괴되지 않습니다.&lt;/b&gt; 따라서 CDO가 참조하고 있는 에셋(메쉬, 텍스처 등)들도 게임 내내 메모리에 상주하게 됩니다. 생성자에서 너무 무거운 에셋을 너무 많이 참조하지 않도록 주의해야 하는 이유입니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;22&quot; data-ke-size=&quot;size23&quot;&gt;로직 실행 자제&lt;/h3&gt;
&lt;p data-path-to-node=&quot;23&quot; data-ke-size=&quot;size16&quot;&gt;생성자는 엔진 초기화 단계(에디터 로딩 중 포함)에서 실행되므로, 월드에 접근하거나(GetWorld()), 다른 액터를 참조하는 등의 &lt;b data-index-in-node=&quot;75&quot; data-path-to-node=&quot;23&quot;&gt;동적인 로직은 절대 생성자에 넣으면 안 됩니다.&lt;/b&gt; 그런 로직은 BeginPlay()에서 처리해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>게임 개발/Unreal Engine 기본</category>
      <author>지노윈</author>
      <guid isPermaLink="true">https://devjino.tistory.com/451</guid>
      <comments>https://devjino.tistory.com/451#entry451comment</comments>
      <pubDate>Tue, 30 Dec 2025 13:18:49 +0900</pubDate>
    </item>
    <item>
      <title>Unreal Engine CDO 사용 이유</title>
      <link>https://devjino.tistory.com/450</link>
      <description>&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;언리얼 엔진에서 &lt;b&gt;CDO(Class Default Object)&lt;/b&gt;는 클래스당 딱 하나씩 존재하는 &lt;b&gt;'원본 마스터 객체'&lt;/b&gt;입니다. 엔진이 초기화될 때 각 클래스의 생성자를 호출하여 이 객체를 메모리에 미리 만들어 둡니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;단순히 &quot;기본값 저장소&quot; 이상의 의미를 갖는데, CDO를 사용하는 핵심적인 이유 4가지는 다음과 같습니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;2&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size26&quot;&gt;1. 메모리와 성능 최적화 (Template Role)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;새로운 액터나 오브젝트를 스폰할 때, 맨땅에서 모든 변수를 초기화하는 것은 비효율적입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;동작 방식:&lt;/b&gt; 엔진은 CDO라는 완성된 '복사본'을 미리 가지고 있다가, 새로운 인스턴스가 생성되면 &lt;b&gt;CDO의 메모리 상태를 그대로 복사(Serialization)&lt;/b&gt;해서 넘겨줍니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;이점:&lt;/b&gt; 복잡한 생성 로직을 매번 실행할 필요 없이 메모리 복사만으로 빠르게 객체를 생성할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size26&quot;&gt;2. 변수 기본값의 기준점 (Default values)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;에디터의 &lt;b&gt;디테일 패널(Details Panel)&lt;/b&gt;에서 우측의 '노란색 화살표(Reset to Default)' 버튼을 본 적이 있으실 겁니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;기준점:&lt;/b&gt; 사용자가 수정한 값이 &quot;기본값과 다른지&quot; 판단할 때, 그 비교 대상이 바로 CDO입니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;직렬화 최적화:&lt;/b&gt; 게임을 저장하거나 네트워크로 데이터를 보낼 때, &lt;b data-index-in-node=&quot;36&quot; data-path-to-node=&quot;8,1,0&quot;&gt;기본값(CDO 값)과 다른 변수만&lt;/b&gt; 골라서 저장/전송하면 되므로 데이터 크기를 획기적으로 줄일 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size26&quot;&gt;3. 데이터 기반 프로그래밍 (Data-Driven Access)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;객체를 실제로 월드에 배치(Spawn)하지 않고도, &lt;b data-index-in-node=&quot;29&quot; data-path-to-node=&quot;10&quot;&gt;그 클래스가 어떤 정보를 가지고 있는지 미리 알아야 할 때&lt;/b&gt; 사용합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,0&quot;&gt;예시:&lt;/b&gt; 인벤토리 UI에서 아이템 아이콘을 표시해야 한다고 가정해 봅시다. 아이템 액터를 월드에 스폰하지 않고도, 아이템 클래스의 CDO에 접근하여 ItemIcon 변수값만 읽어올 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0&quot;&gt;코드 예시:&lt;/b&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;// 액터를 생성하지 않고 클래스 정보만으로 기본값 접근
AMyWeapon* DefaultWeapon = GetDefault&amp;lt;AMyWeapon&amp;gt;(MyWeaponClass);
float Power = DefaultWeapon-&amp;gt;BaseDamage;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;12&quot; data-ke-size=&quot;size26&quot;&gt;4. 리플렉션 및 에디터 통합&lt;/h2&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;언리얼 엔진의 강력한 리플렉션(UProperty 등) 시스템은 CDO를 기반으로 작동합니다. 엔진은 CDO를 검사하여 이 클래스가 어떤 컴포넌트를 기본으로 가지는지, 어떤 기본 에셋을 참조하는지를 파악하고 이를 에디터 UI에 노출합니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;14&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;15&quot; data-ke-size=&quot;size26&quot;&gt;주의사항: CDO는 '불변'이어야 합니다&lt;/h2&gt;
&lt;p data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;CDO는 해당 클래스의 모든 인스턴스가 공유하는 &lt;b data-index-in-node=&quot;27&quot; data-path-to-node=&quot;16&quot;&gt;원본&lt;/b&gt;입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;17&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 런타임 중에 &lt;b data-index-in-node=&quot;10&quot; data-path-to-node=&quot;17,0,0&quot;&gt;CDO의 값을 수정해버리면&lt;/b&gt;, 이후에 생성되는 해당 클래스의 모든 객체들이 바뀐 값을 가지고 태어나는 대참사가 발생할 수 있습니다.&lt;/li&gt;
&lt;li&gt;따라서 CDO는 값을 &lt;b&gt;읽기 전용(Read-only)&lt;/b&gt;으로 사용하는 것이 원칙입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;18&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;19&quot; data-ke-size=&quot;size23&quot;&gt;요약하자면&lt;/h3&gt;
&lt;blockquote data-path-to-node=&quot;20&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-path-to-node=&quot;20,0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,0&quot;&gt;CDO는 클래스의 '설계도'와 '실제 객체' 사이의 가교 역할을 하며, &quot;빠른 생성, 기본값 관리, 효율적인 데이터 접근&quot;을 위해 존재합니다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>게임 개발/Unreal Engine 기본</category>
      <author>지노윈</author>
      <guid isPermaLink="true">https://devjino.tistory.com/450</guid>
      <comments>https://devjino.tistory.com/450#entry450comment</comments>
      <pubDate>Tue, 30 Dec 2025 13:14:50 +0900</pubDate>
    </item>
    <item>
      <title>unreal engine assertion들 check, ensure, verify 등등...</title>
      <link>https://devjino.tistory.com/449</link>
      <description>&lt;h2 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size26&quot;&gt;1. check(조건)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;가장 강력하고 엄격한 어설션입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;동작:&lt;/b&gt; 조건이 거짓이면 &lt;b&gt;게임을 즉시 중단(Crash)&lt;/b&gt;시키고 로그를 남깁니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;특징:&lt;/b&gt; &lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;5,1,0&quot;&gt;Shipping 빌드(배포판)에서는 코드 자체가 삭제&lt;/b&gt;됩니다. 즉, 개발 단계에서만 동작하며 릴리즈된 게임의 성능에는 영향을 주지 않습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,0&quot;&gt;사용 예시:&lt;/b&gt; &quot;이 포인터는 여기서 절대 null일 수 없다&quot;고 확신할 때 사용합니다.
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;check(MeshComponent != nullptr);
MeshComponent-&amp;gt;SetVisibility(true); // 만약 null이었다면 위에서 이미 멈췄을 것
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size26&quot;&gt;2. ensure(조건)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;조금 더 유연한 어설션입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;동작:&lt;/b&gt; 조건이 거짓이면 &lt;b data-index-in-node=&quot;13&quot; data-path-to-node=&quot;8,0,0&quot;&gt;콜스택(Call Stack)을 로그에 출력&lt;/b&gt;하지만, 게임은 &lt;b data-index-in-node=&quot;45&quot; data-path-to-node=&quot;8,0,0&quot;&gt;멈추지 않고 계속 실행&lt;/b&gt;됩니다. (에디터에서는 한 번만 멈추고 디버거를 띄워줍니다.)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;특징:&lt;/b&gt; check와 마찬가지로 &lt;b data-index-in-node=&quot;17&quot; data-path-to-node=&quot;8,1,0&quot;&gt;Shipping 빌드에서는 제거&lt;/b&gt;됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,0&quot;&gt;사용 예시:&lt;/b&gt; &quot;문제가 있긴 하지만, 당장 크래시를 낼 정도는 아닐 때&quot; 또는 &quot;문제를 인지만 하고 실행은 계속하고 싶을 때&quot; 사용합니다. if문과 조합해서 자주 씁니다.
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;if (ensure(WeaponActor != nullptr))
{
    WeaponActor-&amp;gt;Fire();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size26&quot;&gt;3. verify(조건)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;check와 거의 동일하지만, 한 가지 결정적인 차이가 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,0&quot;&gt;동작:&lt;/b&gt; 조건이 거짓이면 **즉시 중단(Crash)**됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0&quot;&gt;특징:&lt;/b&gt; &lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;11,1,0&quot;&gt;Shipping 빌드에서도 코드가 삭제되지 않고 실행&lt;/b&gt;됩니다. 단, Shipping 빌드에서는 &quot;조건 검사&quot;는 수행하지만 &quot;실패 시 크래시&quot; 기능은 비활성화됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,2,0&quot;&gt;사용 예시:&lt;/b&gt; &lt;b data-index-in-node=&quot;7&quot; data-path-to-node=&quot;11,2,0&quot;&gt;조건식 안에 실제 로직(함수 호출)이 포함되어 있을 때&lt;/b&gt; 사용합니다. check를 쓰면 릴리즈 시 함수 호출 자체가 사라져버리지만, verify는 함수를 실행해 줍니다.
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;// Shipping 빌드에서도 OpenDoor()는 실행되어야 함
verify(OpenDoor() == true); 
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;12&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;13&quot; data-ke-size=&quot;size26&quot;&gt;  한눈에 비교하기&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;매크로&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실패 시 결과&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Shipping 빌드 포함 여부&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;용도&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,1,0,0&quot;&gt;check&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,1,1,0&quot;&gt;즉시 중단 (Crash)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,1,2,0&quot;&gt;제거됨&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,1,3,0&quot;&gt;치명적인 오류 확인 (개발용)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,2,0,0&quot;&gt;ensure&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,2,1,0&quot;&gt;로그 출력 후 계속 실행&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,2,2,0&quot;&gt;제거됨&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,2,3,0&quot;&gt;경고 및 비치명적 오류 확인&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,3,0,0&quot;&gt;verify&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,3,1,0&quot;&gt;즉시 중단 (Crash)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,3,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,3,2,0&quot;&gt;표현식은 유지됨&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,3,3,0&quot;&gt;반환값이 있는 로직 확인&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-path-to-node=&quot;15&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;16&quot; data-ke-size=&quot;size26&quot;&gt;4. 기타 유용한 변형들&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;17&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,0,0&quot;&gt;checkNoEntry()&lt;/b&gt;: 절대 도달해서는 안 되는 코드 경로에 둡니다. (예: switch문의 default 케이스)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,1,0&quot;&gt;checkf(조건, L&quot;메시지&quot;)&lt;/b&gt;: 실패했을 때 사용자 정의 메시지를 로그에 함께 출력합니다. (check, ensure, verify 모두 뒤에 f를 붙인 버전이 존재합니다.)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,2,0&quot;&gt;unimplemented()&lt;/b&gt;: 아직 구현되지 않은 함수임을 표시하며, 호출 시 크래시를 발생시킵니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>게임 개발/Unreal Engine 기본</category>
      <author>지노윈</author>
      <guid isPermaLink="true">https://devjino.tistory.com/449</guid>
      <comments>https://devjino.tistory.com/449#entry449comment</comments>
      <pubDate>Tue, 30 Dec 2025 11:38:52 +0900</pubDate>
    </item>
    <item>
      <title>TObjectPtr를 사용하는 이유</title>
      <link>https://devjino.tistory.com/448</link>
      <description>&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;언리얼 엔진 5(UE5)에서 도입된 TObjectPtr은 기존의 Raw Pointer(C++ 기본 포인터, 예: UObject*)를 대체하기 위해 설계된 &lt;b data-index-in-node=&quot;90&quot; data-path-to-node=&quot;0&quot;&gt;엔진 전용 스마트 포인터 시스템&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;단순히 주소를 가리키는 것을 넘어, 에디터 환경과 메모리 관리 효율을 위해 도입되었습니다. 주요 사용 이유 4가지는 다음과 같습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size26&quot;&gt;1. 지연 로딩 (Lazy Loading) 및 성능 최적화&lt;/h2&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;가장 큰 기술적 이유는 &lt;b data-index-in-node=&quot;13&quot; data-path-to-node=&quot;4&quot;&gt;에디터 빌드에서 오브젝트를 실제로 필요할 때까지 로드하지 않기 위해서&lt;/b&gt;입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;Raw Pointer:&lt;/b&gt; 포인터 변수가 선언되어 있으면 해당 오브젝트를 즉시 메모리에 올려야 할 때가 많습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;TObjectPtr:&lt;/b&gt; 에디터 환경에서 해당 객체가 실제로 접근(Access)되기 전까지는 로딩을 지연시키거나, 경로 정보만 가지고 있을 수 있습니다. 이는 특히 대규모 프로젝트에서 에디터 로딩 속도를 획기적으로 줄여줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size26&quot;&gt;2. 엔진 시스템과의 긴밀한 통합 (Access Tracking)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;TObjectPtr은 단순한 주소값이 아니라 &lt;b data-index-in-node=&quot;25&quot; data-path-to-node=&quot;7&quot;&gt;객체 접근을 추적&lt;/b&gt;할 수 있는 래퍼(Wrapper)입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;디버깅 및 통계:&lt;/b&gt; 어떤 오브젝트가 얼마나 자주 참조되는지, 언제 로드되는지 엔진이 모니터링하기 쉬워집니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;가비지 컬렉션(GC):&lt;/b&gt; 엔진의 가비지 컬렉터가 객체를 더 효율적으로 식별하고 관리할 수 있도록 돕습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size26&quot;&gt;3. 에디터에서의 안전성 및 유연성&lt;/h2&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;에디터에서 블루프린트나 에셋을 수정할 때, TObjectPtr은 &lt;b data-index-in-node=&quot;36&quot; data-path-to-node=&quot;10&quot;&gt;Optional하게 동작&lt;/b&gt;할 수 있는 구조를 제공합니다. 에셋의 주소가 바뀌거나 재로드되는 상황에서도 Raw Pointer보다 훨씬 유연하게 대응합니다.&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;11&quot; data-ke-size=&quot;size26&quot;&gt;4. 최신 표준 및 가시성 (Modernization)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;UE5부터 엔진 소스 코드 전반에 적용되었으며, 프로그래머에게 이 변수가 &lt;b&gt;&quot;언리얼 오브젝트(UObject)를 가리키고 있으며, 엔진에 의해 관리되고 있음&quot;&lt;/b&gt;을 명확히 알려줍니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;13&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;14&quot; data-ke-size=&quot;size26&quot;&gt;핵심 차이점 요약&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;15&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Raw Pointer (UObject*)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;TObjectPtr&amp;lt;T&amp;gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,1,0,0&quot;&gt;권장 버전&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,1,1,0&quot;&gt;UE4 이하&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,1,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,1,2,0&quot;&gt;UE5 이상 필수 권장&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,2,0,0&quot;&gt;로딩 방식&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,2,1,0&quot;&gt;즉시 로딩 위주&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,2,2,0&quot;&gt;에디터에서 지연 로딩 지원&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,3,0,0&quot;&gt;안전성&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,3,1,0&quot;&gt;수동 관리 요소 많음&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,3,2,0&quot;&gt;엔진 시스템(GC, 에디터) 최적화&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,4,0,0&quot;&gt;런타임 오버헤드&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,4,1,0&quot;&gt;없음&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;15,4,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,4,2,0&quot;&gt;거의 없음&lt;/b&gt; (릴리즈 빌드에선 Raw Pointer로 컴파일됨)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-path-to-node=&quot;16&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[IMPORTANT]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 게임 배포(Shipping) 빌드에서는 TObjectPtr이 내부적으로 Raw Pointer와 동일하게 컴파일되도록 설계되어 있습니다. 따라서 런타임 성능 저하 없이 개발 단계의 이점만 챙길 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-path-to-node=&quot;17&quot; data-ke-size=&quot;size23&quot;&gt;사용 예시 (C++)&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;UPROPERTY(EditAnywhere, Category = &quot;MyCategory&quot;)
TObjectPtr&amp;lt;UStaticMeshComponent&amp;gt; MeshComponent;

UPROPERTY(EditAnywhere, Category = &quot;MyCategory&quot;)
TObjectPtr&amp;lt;UTexture2D&amp;gt; BackgroundTexture;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-path-to-node=&quot;19&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>게임 개발/Unreal Engine 기본</category>
      <author>지노윈</author>
      <guid isPermaLink="true">https://devjino.tistory.com/448</guid>
      <comments>https://devjino.tistory.com/448#entry448comment</comments>
      <pubDate>Tue, 30 Dec 2025 11:04:51 +0900</pubDate>
    </item>
  </channel>
</rss>