TIL_099 : 모듈-플러그인-빌드(UBT/UHT) (1)

김펭귄·2026년 1월 19일

Today What I Learned (TIL)

목록 보기
99/142

1. 언리얼 엔진 소스코드

1.1. Runtime 폴더

  • 런타임(게임 실행시)에 필요한 코드들

  • Core : 문자열 처리, 컨테이너(TArray, TMap), 로깅, 수학 함수

  • CoreUObject : UObject 시스템, 리플렉션, 가비지 컬렉션, 직렬화

  • Engine : 액터, 컴포넌트, 월드, 레벨 등 게임 프레임워크 핵심

  • Renderer : 렌더링 시스템 (Nanite, Lumen 포함)

  • PhysicsCore : 물리 엔진 인터페이스

  • AudioMixer : 오디오 시스템

  • Networking : 네트워크 복제, RPC

1.2. Editor 폴더

  • 언리얼 에디터 전용 코드. 패키징 시 필요없으므로 제외됨

  • UnrealEd : 에디터 핵심 프레임워크

  • LevelEditor : 레벨 편집 기능

  • MaterialEditor : 머티리얼 편집

  • BlueprintGraph : 블루프린트 노드 그래프 시스템

  • Kismet : 블루프린트 VM과 컴파일러

  • PropertyEditor : 디테일 패널

1.3. Developer 폴더

  • 개발 중에만 필요한 코드(프로파일링 도구, 자동화 테스트 등)

  • Shipping 빌드에서 제외

1.4. ThirdParty 폴더

  • 외부 라이브러리(zlib, libPNG, FreeType, OpenSSL, Vulkan, PhysX 등)

1.5. Programs 폴더

  • 빌드 및 실행 도구에 필요한 모듈들이 존재

  • UnrealBuildTool (UBT) : 빌드 시스템

  • UnrealHeaderTool (UHT) : 리플렉션 코드 생성기

2. Module vs Plugin

2.1. Module

  • 코드의 기본 빌딩 블록

  • 특정 기능(예: UI, AI, 물리)을 독립된 단위로 묶음

  • 각 모듈은 각각의 build.cs파일을 가지고, 해당파일에 모듈의 빌드 규칙과 의존성을 정의함

  • 하나의 모듈이 하나의 dll파일(정적 라이브러리)이 됨

  • 프로젝트를 생성하면 기본적으로 ProjectName이라는 메인 게임 모듈이 생김

  • 그래서 Games\ProjectName\SourceBuild.cs파일이 존재했던 것

  • 위에서 본, 엔진 소스코드 폴더들도 여러 모듈들을 폴더화 한 것

2.2. Plugin

  • 하나 이상의 모듈을 담는 패키지

  • 프로젝트와 완전히 독립적으로 존재할 수 있는 기능 묶음

  • 한 프로젝트에서 만든 플러그인을 다른 프로젝트로 쉽게 복사해서 사용 가능

  • 코드(모듈)뿐만 아니라 블루프린트, 에셋(텍스처, 모델링), 설정값 등을 모두 포함할 수 있음

  • 에디터에서 쉽게 On/Off가 가능

  • .uplugin : 플러그인의 메타데이터(이름, 버전, 모듈 목록 등)를 담은 JSON 형식의 파일 (Plugins 폴더에 존재)

3. 모듈

3.1. 모듈 사용 이유

  1. 컴파일 시간 단축

    • 모든 코드를 다 컴파일하면 시간이 엄청 오래 걸림

    • 코드를 모듈화 하여, 수정된 모듈만 컴파일하여 시간을 단축함

    • 특히 엔진 소스코드를 모듈화하여 시간을 매우 단축

  2. 기능 단위의 캡슐화

    • 복잡한 내부구조는 private에 두고, public을 통해서만 다른 모듈에서 include가능하도록 함

    • 따라서 내부구조가 수정되더라도, 외부 interface(public)는 그대로이기 때문에 해당 모듈을 include한 다른 모듈에서 재컴파일 안 해줘도 됨

MyAIModule/
├── Public/
│   ├── MyAIController.h      // 외부에서 사용할 클래스
│   └── AITypes.h             // 공개 타입 정의
└── Private/
    ├── BehaviorTree/         // 내부 구현
    │   ├── BTNode_Attack.cpp
    │   └── BTNode_Attack.h
    ├── Utility/              // 내부 유틸리티
    │   └── AIMathUtils.h
    └── MyAIController.cpp
  1. 블루프린트와 리플렉션의 연결

    • UnrealHeaderTool(UHT)가 모듈 단위로 동작하므로 필요함

    • Build.cs에 의존성이 설정되지 않으면 UHT가 해당 헤더를 인식하지 못함.

    • "UCLASS가 인식되지 않습니다" 에러의 흔한 원인: 모듈 의존성 설정 누락

3.2. 모듈 사용법

// Runtime 모듈 선언 예시
{
  "Name": "MyGame",
  "Type": "Runtime",
  "LoadingPhase": "Default"
}
  • 모듈 타입 종류

    • Runtime : 런타임중에 사용될 모듈. 패키징에 포함됨

    • Editor : 에디터 전용 모듈. 패키징에 포함 안 됨

    • Developer : 개발 빌드 전용(프로파일링용). Development모드에서만 포함됨

  • LoadingPhase : 모듈이 언제 로딩될 지에 대한 옵션. 보통 Default로 설정

    • EarliestPossible : 가장 빠른 시점 (저수준 모듈)

    • PostConfigInit : 설정 시스템 초기화 직후

    • PreDefault : Default보다 먼저 (커스텀 애니메이션 노드 등)

    • Default : 일반적인 게임플레이 모듈 (기본값)

    • PostEngineInit : 엔진 완전 초기화 후

    • None : 자동 로드 안 함 (수동 로드 필요)

3.3. MODULE_API 매크로

UCLASS()
class MYGAME_API AMyCharacter : public ACharacter
{
    // ...
};
  • MYGAME_API의 의미가 해당 모듈에서 클레스에 접근 가능하게 하는 매크로

4. Plugin

4.1. 플러그인 파일

// MyPlugin.uplugin
{
  // ... //
  "CanContainContent": true,		// Content 폴더(에셋) 포함 가능 여부
  "Modules": [						// Plugin에 포함된 모듈 목록
    {
      "Name": "MyPlugin",
      "Type": "Runtime",
      "LoadingPhase": "Default"
    },
    {
      "Name": "MyPluginEditor",
      "Type": "Editor",
      "LoadingPhase": "Default"
    }
  ],
  "Plugins": [						// 의존하는 다른 플러그인(로딩 순서보장)
    {
      "Name": "EnhancedInput",
      "Enabled": true
    }
  ]
}
// MyPluginModule.cpp
IMPLEMENT_MODULE(FMyPluginModule, MyPlugin)
  • 필수적으로 cpp파일에 IMPLEMENT_MODULE 매크로로 모듈 등록해야함

4.2. 플러그인 폴더 구조 예시

MyPlugin/
├── MyPlugin.uplugin					// 플러그인 파일
├── Content/
└── Source/
    ├── MyPlugin/                 		// 모듈
    │   ├── MyPlugin.Build.cs	    	// 모듈의 build.cs 파일
    │   ├── Private/
    │   └── Public/
    └── MyPluginEditor/           		// 모듈
        ├── MyPluginEditor.Build.cs		// 모듈의 build.cs 파일
        ├── Private/
        └── Public/

4.3. 플러그인 연결 위치

  • Engine\Plugin : 모든 프로젝트에서 사용 가능 (엔진 내장)

  • 프로젝트\Plugins : 해당 프로젝트 전용

  • Engine\Plugins\Marketplace : 에픽 런처로 설치한 플러그인

5. Build.cs

using UnrealBuildTool;
using System.IO;

public class MyGame : ModuleRules
{
    public MyGame(ReadOnlyTargetRules Target) : base(Target)
    {
        // PCH 사용 방식 (보통 이대로 사용)
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

        bEnforceIWYU = true;	// 불필요한 include 시 경고 발생여부

        // Public 의존성
        PublicDependencyModuleNames.AddRange(new string[]
        {
            "Core",
            "CoreUObject",
            "Engine"
        });

        // Private 의존성
        PrivateDependencyModuleNames.AddRange(new string[]
        {
            "Slate",
            "SlateCore"
        });

        // 에디터 전용 의존성
        if (Target.bBuildEditor)
        {
            PrivateDependencyModuleNames.Add("UnrealEd");
        }
    }
}
  • PCH(PreCompiledHeader)방식을 통해 자주 사용하는 헤더들을 미리 컴파일함

  • PublicDependencyModuleNames : 이 모듈의 Public 헤더(.h)에서 사용하는 모듈을 등록

    • Core, CoreUObject, UMG 등을 추가해줘야 해당 모듈들을 사용 가능.

    • 의존성을 가지는데, A모듈이 B에 의존하게 되면, B가 의존하는 다른 모듈들에도 자동으로 의존하게 됨. 그래서 똑같은거 여러번 안 써줘도 됨

ModuleA (PublicDependency: Engine, Core)

ModuleB (PublicDependency: ModuleA)
->B모듈은 자동으로 Engine, Core 의존성도 받음

  • PrivateDependencyModuleNames : 똑같은 의존인데, 얘는 전파가 안 됨

ModuleA (PrivateDependency: Slate)

ModuleB (PublicDependency: ModuleA)
->ModuleB는 Slate에 접근 불가. 직접 의존성 추가해야함

5.1. 실제 예시

// .h (Public 폴더)
#include "GameFramework/Character.h"  // Engine 모듈
#include "AbilitySystemInterface.h"    // GameplayAbilities 모듈

// .cpp (Private 폴더)
#include "Components/WidgetComponent.h" // UMG 모듈
#include "NavigationSystem.h"           // NavigationSystem 모듈

// Build.cs
// 헤더에서 사용하는 모듈들 추가
PublicDependencyModuleNames.AddRange(new string[]
{
    "Core", "CoreUObject", "Engine",
    "GameplayAbilities"  
});

// cpp에서 사용하는 모듈들 추가
PrivateDependencyModuleNames.AddRange(new string[]
{
    "UMG",               // cpp에서만 사용
    "NavigationSystem"
});

5.2. Build.cs와 리플렉션의 관계

  • Build.cs에 의존하는 모듈 추가하지 않고 그냥 사용하면 UHT가 해당 모듈의 타입을 인식 못 해에러남

  • "UCLASS가 인식되지 않습니다" 에러의 원인 중 대부분을 차지

#include "Abilities/GameplayAbility.h"  // 모듈 그냥 사용
#include "UMyAbility.generated.h"  // 반드시 마지막!

UCLASS()
class UMyAbility : public UGameplayAbility  // 부모 클래스 인식 불가!
{
    GENERATED_BODY()
};
  • 따라서 Build.cs에 의존성 추가하고, .uproject에 플러그인 활성화 필요
// Build.cs
PublicDependencyModuleNames.Add("GameplayAbilities");

// .uproject
{
  "Plugins": [
    { "Name": "GameplayAbilities", "Enabled": true }
  ]
}
profile
반갑습니다

0개의 댓글