TIL: Unreal Engine 모듈 간 상호작용과 독립 플러그인 생성 및 검증 이해하기
오늘 배운 내용
오늘은 Unreal Engine에서 서로 다른 모듈이 어떻게 상호작용하는지, 그리고 프로젝트 안에 독립 플러그인(Temporary) 을 직접 구성하고 프로젝트에 연결하는 과정을 정리했다.
처음에는 Primary 모듈, Test 모듈, Temporary 플러그인이라는 표현이 헷갈렸다.
하지만 정리해보면 다음과 같다.
ModuleAndPlugin 모듈
→ 현재 프로젝트의 주 게임 모듈
Test 모듈
→ 별도로 만든 실습용 Runtime 모듈
Temporary 플러그인
→ 프로젝트의 Plugins 폴더 아래에 만든 독립 플러그인
이번 실습의 핵심은 단순히 클래스를 만드는 것이 아니라, 모듈과 플러그인이 서로 어떻게 분리되고 연결되는지를 이해하는 것이다.
특히 TestActor를 Test 모듈에 만들고, 주 게임 모듈인 ModuleAndPlugin에서 include한 뒤 SpawnActor로 생성하면서 모듈 간 참조 구조를 확인했다.
또 Temporary 플러그인을 직접 만들면서 .uplugin, Build.cs, StartupModule(), ShutdownModule(), .uproject 등록 과정까지 확인했다.
핵심은 다음과 같다.
모듈 간 상호작용
→ 한 모듈에서 만든 클래스를 다른 모듈에서 include하고 사용하는 구조
플러그인
→ 프로젝트에 붙였다 뗄 수 있는 독립적인 기능 패키지
.uplugin
→ 플러그인의 설명서
Build.cs
→ 모듈의 빌드 의존성 설정 파일
StartupModule()
→ 플러그인 모듈이 로드될 때 실행
ShutdownModule()
→ 플러그인 모듈이 언로드될 때 실행
1. 모듈 간 상호작용 구현의 의미
과제에서 나온 내용은 다음과 같았다.
TestActor 생성
→ Test 모듈에 속한 C++ 클래스 Actor 부모를 생성하고 BeginPlay에서 로그 출력
Primary 모듈에서 참조
→ 주 게임 모듈의 캐릭터 클래스에서 TestActor.h를 include하고 SpawnActor 로직 작성
처음에는 Primary 모듈이라는 말 때문에 Primary라는 이름의 모듈을 새로 만들어야 하는 것처럼 보였다.
하지만 여기서 말하는 Primary 모듈은 새 모듈 이름이 아니라, 현재 프로젝트의 주 게임 모듈을 의미한다.
현재 프로젝트 기준으로는 다음과 같이 이해하면 된다.
Primary 모듈 역할
→ ModuleAndPlugin 모듈
TestActor를 만드는 위치
→ Test 모듈
TestActor를 참조하고 생성하는 위치
→ ModuleAndPlugin 모듈
즉 새로 Primary라는 모듈을 만드는 것이 아니라, 기존 주 게임 모듈인 ModuleAndPlugin에서 Test 모듈을 참조하는 구조였다.
2. TestActor를 Test 모듈에 만들어야 하는 이유
과제 문장에는 Test 모듈에 속한 C++ 클래스라고 되어 있었다.
따라서 TestActor는 주 게임 모듈인 ModuleAndPlugin이 아니라, Test 모듈에 생성되어야 한다.
정상적인 구조는 다음과 같다.
Source
├─ ModuleAndPlugin
│ ├─ ModuleAndPlugin.Build.cs
│ ├─ Public
│ └─ Private
│
└─ Test
├─ Test.Build.cs
├─ Public
│ └─ TestActor.h
└─ Private
└─ TestActor.cpp
중요한 점은 TestActor.h를 Test/Public 폴더에 두는 것이다.
다른 모듈에서 include해야 하는 헤더
→ Public 폴더
해당 모듈 내부에서만 사용하는 구현 파일
→ Private 폴더
ModuleAndPlugin 모듈에서 TestActor.h를 include해야 하므로, TestActor.h는 반드시 Test 모듈의 Public 폴더에 있어야 한다.
3. C++ 클래스 생성 화면에서 주의할 점
처음 C++ 클래스를 만들 때 Class Type을 Public으로 선택한 것은 맞았다.
Class Type: Public
→ 맞음
하지만 모듈 선택이 중요했다.
처음 화면에서는 모듈이 다음처럼 되어 있었다.
ModuleAndPlugin (Runtime)
이 상태로 만들면 생성 경로가 다음처럼 잡힌다.
Source/ModuleAndPlugin/Public/TestActor.h
Source/ModuleAndPlugin/Private/TestActor.cpp
이렇게 되면 TestActor는 Test 모듈이 아니라 ModuleAndPlugin 모듈에 속하게 된다.
과제 의도는 TestActor를 Test 모듈에 만드는 것이므로, 모듈 선택을 다음처럼 바꿔야 했다.
Module: Test (Runtime)
그러면 경로가 다음처럼 바뀌는 것이 정상이다.
Source/Test/Public/TestActor.h
Source/Test/Private/TestActor.cpp
4. API 매크로의 의미
모듈이 달라지면 클래스 앞에 붙는 API 매크로도 달라진다.
만약 TestActor를 ModuleAndPlugin 모듈에 만들면 클래스 선언이 다음처럼 생성될 수 있다.
class MODULEANDPLUGIN_API ATestActor : public AActor
하지만 Test 모듈에 제대로 만들면 다음처럼 되어야 한다.
class TEST_API ATestActor : public AActor
여기서 TEST_API는 Test 모듈 밖에서도 이 클래스를 사용할 수 있도록 내보내는 역할을 한다.
즉 ModuleAndPlugin 모듈에서 ATestActor를 사용하려면 ATestActor 클래스에 TEST_API가 붙어 있어야 한다.
정리하면 다음과 같다.
Test 모듈 소속 클래스
→ TEST_API
ModuleAndPlugin 모듈 소속 클래스
→ MODULEANDPLUGIN_API
5. TestActor.h 작성
TestActor.h는 다음과 같이 구성할 수 있다.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TestActor.generated.h"
UCLASS()
class TEST_API ATestActor : public AActor
{
GENERATED_BODY()
public:
ATestActor();
protected:
virtual void BeginPlay() override;
};
여기서 중요한 부분은 다음이다.
class TEST_API ATestActor : public AActor
이 클래스가 Test 모듈 소속이고, 다른 모듈에서도 사용할 수 있게 공개된다는 의미다.
6. TestActor.cpp 작성
TestActor.cpp에서는 BeginPlay()에 로그와 화면 메시지를 넣어 생성 여부를 확인할 수 있다.
#include "TestActor.h"
#include "Engine/Engine.h"
ATestActor::ATestActor()
{
PrimaryActorTick.bCanEverTick = false;
}
void ATestActor::BeginPlay()
{
Super::BeginPlay();
UE_LOG(LogTemp, Warning, TEXT("ATestActor BeginPlay"));
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(
-1,
5.0f,
FColor::Green,
TEXT("ATestActor BeginPlay")
);
}
}
이 코드는 ATestActor가 실제로 월드에 생성되고 BeginPlay()가 호출되면 로그와 화면 메시지를 출력한다.
즉 이 Actor가 정상적으로 생성되었는지 확인하는 테스트용 코드다.
7. ModuleAndPlugin 모듈에서 Test 모듈 의존성 추가
ModuleAndPlugin 모듈에서 TestActor.h를 include하려면, 먼저 ModuleAndPlugin.Build.cs에 Test 모듈 의존성을 추가해야 한다.
파일 위치는 다음과 같다.
Source/ModuleAndPlugin/ModuleAndPlugin.Build.cs
기존 의존성 목록에 "Test"를 추가한다.
PublicDependencyModuleNames.AddRange(new string[]
{
"Core",
"CoreUObject",
"Engine",
"InputCore",
"Test"
});
이 코드는 다음과 같은 의미다.
ModuleAndPlugin 모듈은 Test 모듈의 Public 헤더와 기능을 사용하겠다.
즉 다른 모듈의 클래스를 include하기 위해서는 단순히 헤더 파일만 있는 것이 아니라, Build.cs에서 모듈 의존성을 연결해야 한다.
8. 주 게임 모듈의 캐릭터에서 TestActor include
이제 ModuleAndPlugin 모듈의 캐릭터 cpp 파일에서 TestActor.h를 include할 수 있다.
예를 들어 다음 파일에서 작업할 수 있다.
Source/ModuleAndPlugin/Private/ModuleAndPluginCharacter.cpp
상단에 include를 추가한다.
#include "TestActor.h"
만약 이 include에서 오류가 난다면 보통 다음 원인을 확인해야 한다.
1. TestActor.h가 Source/Test/Public에 있는지
2. ModuleAndPlugin.Build.cs에 "Test"를 추가했는지
3. 프로젝트 파일을 다시 생성하고 빌드했는지
9. Character BeginPlay에서 SpawnActor 작성
캐릭터의 BeginPlay()에서 ATestActor를 생성한다.
void AModuleAndPluginCharacter::BeginPlay()
{
Super::BeginPlay();
if (UWorld* World = GetWorld())
{
FVector SpawnLocation = GetActorLocation() + FVector(200.0f, 0.0f, 100.0f);
FRotator SpawnRotation = FRotator::ZeroRotator;
ATestActor* SpawnedActor = World->SpawnActor<ATestActor>(
ATestActor::StaticClass(),
SpawnLocation,
SpawnRotation
);
if (SpawnedActor)
{
UE_LOG(LogTemp, Warning, TEXT("Spawned ATestActor Successfully"));
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(
-1,
5.0f,
FColor::Yellow,
TEXT("Spawned ATestActor from ModuleAndPlugin")
);
}
}
}
}
이 코드는 주 게임 모듈의 캐릭터가 시작될 때, Test 모듈에 있는 ATestActor를 월드에 생성하는 코드다.
전체 흐름은 다음과 같다.
게임 시작
↓
ModuleAndPlugin 모듈의 Character BeginPlay 실행
↓
TestActor.h include
↓
SpawnActor<ATestActor>() 실행
↓
Test 모듈의 ATestActor 생성
↓
ATestActor::BeginPlay() 실행
↓
로그와 화면 메시지 출력
10. 모듈 간 상호작용 실습의 의미
이번 실습은 단순히 Actor 하나를 Spawn하는 것이 목적이 아니다.
진짜 목적은 다음 구조를 확인하는 것이다.
Test 모듈
→ ATestActor 클래스를 제공
ModuleAndPlugin 모듈
→ Test 모듈을 의존성에 추가
→ TestActor.h include
→ ATestActor를 SpawnActor로 생성
즉 한 모듈에서 만든 클래스를 다른 모듈에서 사용하려면 다음 조건이 필요하다.
1. 사용될 클래스의 헤더가 Public 폴더에 있어야 한다.
2. 클래스에 올바른 API 매크로가 붙어 있어야 한다.
3. 사용하는 쪽 모듈의 Build.cs에 대상 모듈 의존성이 추가되어야 한다.
4. cpp에서 헤더를 include해야 한다.
이 조건들이 맞아야 모듈 간 상호작용이 정상적으로 가능하다.
11. 독립 플러그인 Temporary 생성의 의미
다음 단계에서는 Temporary라는 독립 플러그인을 만들었다.
이 플러그인은 프로젝트의 Source 폴더 안에 만드는 것이 아니라, 프로젝트 루트의 Plugins 폴더 아래에 만든다.
최종 구조는 다음과 같다.
ModuleAndPlugin
├─ Config
├─ Content
├─ Source
├─ Plugins
│ └─ Temporary
│ ├─ Temporary.uplugin
│ ├─ Content
│ └─ Source
│ └─ Temporary
│ ├─ Temporary.Build.cs
│ ├─ Public
│ │ └─ Temporary.h
│ └─ Private
│ └─ Temporary.cpp
└─ ModuleAndPlugin.uproject
중요한 점은 Plugins/Temporary 위치다.
올바른 위치
→ 프로젝트루트/Plugins/Temporary
잘못된 위치
→ Source/ModuleAndPlugin/Plugins
플러그인은 프로젝트의 주 소스코드 안에 넣는 것이 아니라, 프로젝트 루트의 Plugins 폴더 안에 독립적으로 둔다.
12. Temporary.uplugin 작성
Temporary.uplugin 파일은 플러그인의 설명서 역할을 한다.
위치는 다음과 같다.
Plugins/Temporary/Temporary.uplugin
예시는 다음과 같다.
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0",
"FriendlyName": "Temporary",
"Description": "Temporary plugin for module practice.",
"Category": "Practice",
"CreatedBy": "Me",
"CanContainContent": true,
"IsBetaVersion": false,
"Installed": false,
"Modules": [
{
"Name": "Temporary",
"Type": "Runtime",
"LoadingPhase": "Default"
}
]
}
여기서 중요한 부분은 다음이다.
"CanContainContent": true
이 값은 이 플러그인이 Content 폴더를 가질 수 있다는 의미다.
또 중요한 부분은 Modules 항목이다.
"Modules": [
{
"Name": "Temporary",
"Type": "Runtime",
"LoadingPhase": "Default"
}
]
이것은 다음과 같은 의미다.
이 플러그인 안에는 Temporary라는 Runtime 모듈이 있다.
기본 로딩 단계에서 이 모듈을 로드한다.
13. Temporary.Build.cs 작성
Temporary.Build.cs는 플러그인 내부의 Temporary 모듈이 어떤 Unreal 모듈을 사용할지 설정하는 파일이다.
위치는 다음과 같다.
Plugins/Temporary/Source/Temporary/Temporary.Build.cs
예시는 다음과 같다.
using UnrealBuildTool;
public class Temporary : ModuleRules
{
public Temporary(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[]
{
"Core",
"CoreUObject",
"Engine"
});
PrivateDependencyModuleNames.AddRange(new string[]
{
});
}
}
이 코드는 다음과 같은 의미다.
Temporary 모듈은 Core, CoreUObject, Engine 기능을 사용한다.
빌드할 때 이 모듈들을 참조해야 한다.
즉 Build.cs는 모듈의 빌드 의존성을 관리하는 파일이다.
14. Temporary.h 작성
Temporary.h에서는 IModuleInterface를 상속받은 모듈 클래스를 선언한다.
위치는 다음과 같다.
Plugins/Temporary/Source/Temporary/Public/Temporary.h
내용은 다음과 같다.
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
class FTemporaryModule : public IModuleInterface
{
public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
여기서 IModuleInterface는 Unreal 모듈이 로드되고 언로드될 때 호출되는 기본 인터페이스다.
StartupModule()
→ 모듈이 로드될 때 호출
ShutdownModule()
→ 모듈이 언로드될 때 호출
15. Temporary.cpp 작성
Temporary.cpp에서는 StartupModule()과 ShutdownModule()을 구현한다.
위치는 다음과 같다.
Plugins/Temporary/Source/Temporary/Private/Temporary.cpp
내용은 다음과 같다.
#include "Temporary.h"
#define LOCTEXT_NAMESPACE "FTemporaryModule"
void FTemporaryModule::StartupModule()
{
UE_LOG(LogTemp, Warning, TEXT("Temporary Plugin Module Startup"));
}
void FTemporaryModule::ShutdownModule()
{
UE_LOG(LogTemp, Warning, TEXT("Temporary Plugin Module Shutdown"));
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FTemporaryModule, Temporary)
가장 중요한 부분은 다음이다.
IMPLEMENT_MODULE(FTemporaryModule, Temporary)
이 코드는 Unreal에게 다음과 같이 알려주는 역할을 한다.
Temporary라는 모듈은 FTemporaryModule 클래스로 구현되어 있다.
만약 이 코드가 없으면 Unreal이 모듈을 정상적으로 등록하지 못한다.
16. StartupModule과 ShutdownModule의 의미
StartupModule()은 플러그인 모듈이 로드될 때 실행된다.
예를 들어 플러그인이 켜질 때 로그를 출력할 수 있다.
void FTemporaryModule::StartupModule()
{
UE_LOG(LogTemp, Warning, TEXT("Temporary Plugin Module Startup"));
}
나중에는 이 함수 안에서 다음과 같은 일을 할 수 있다.
에디터 메뉴 등록
커스텀 툴 등록
외부 라이브러리 초기화
데이터 매니저 초기화
공통 시스템 초기화
반대로 ShutdownModule()은 모듈이 언로드될 때 실행된다.
void FTemporaryModule::ShutdownModule()
{
UE_LOG(LogTemp, Warning, TEXT("Temporary Plugin Module Shutdown"));
}
이 함수에서는 다음과 같은 정리 작업을 할 수 있다.
등록한 메뉴 제거
사용하던 리소스 해제
외부 라이브러리 종료
생성한 객체 정리
즉 StartupModule()과 ShutdownModule()은 플러그인 모듈의 시작과 종료 시점을 관리하는 함수다.
17. .uproject에 Temporary 플러그인 등록
플러그인을 만들었다고 해서 프로젝트가 자동으로 사용하는 것은 아니다.
프로젝트에서 사용하려면 .uproject 파일의 Plugins 항목에 등록해야 한다.
예를 들어 ModuleAndPlugin.uproject 파일에 다음처럼 추가한다.
"Plugins": [
{
"Name": "Temporary",
"Enabled": true
}
]
이 코드는 다음과 같은 의미다.
이 프로젝트에서 Temporary 플러그인을 사용하겠다.
이미 다른 플러그인이 등록되어 있다면, 같은 객체 안에 이름을 여러 개 넣는 것이 아니라 플러그인마다 객체를 따로 추가해야 한다.
18. .uproject Plugins 항목 작성 시 실수
처음에는 다음처럼 잘못 작성했다.
"Plugins": [
{
"Name": "ModelingToolsEditorMode", "Temporary",
"Enabled": true,
"TargetAllowList": [
"Editor"
]
}
]
이 방식은 잘못된 JSON 구조다.
Name 하나에는 플러그인 이름 하나만 들어가야 한다.
올바른 방식은 플러그인 하나당 객체 하나를 따로 만드는 것이다.
"Plugins": [
{
"Name": "ModelingToolsEditorMode",
"Enabled": true,
"TargetAllowList": [
"Editor"
]
},
{
"Name": "Temporary",
"Enabled": true
}
]
즉 구조는 다음과 같다.
Plugins 배열
├─ ModelingToolsEditorMode 플러그인 객체
└─ Temporary 플러그인 객체
JSON에서는 항목 사이 쉼표와 마지막 항목 뒤 쉼표 여부도 중요하다.
항목 사이
→ 쉼표 필요
마지막 항목 뒤
→ 쉼표 없음
19. Temporary 플러그인 이름 일치 확인
Temporary 플러그인이 정상 인식되려면 관련 이름들이 모두 일치해야 한다.
확인해야 할 항목은 다음과 같다.
.uproject 등록 이름
→ Temporary
플러그인 폴더 이름
→ Plugins/Temporary
플러그인 파일 이름
→ Temporary.uplugin
Source 내부 모듈 폴더
→ Source/Temporary
Build.cs 파일 이름
→ Temporary.Build.cs
Build.cs 클래스 이름
→ public class Temporary : ModuleRules
IMPLEMENT_MODULE 이름
→ IMPLEMENT_MODULE(FTemporaryModule, Temporary)
이 중 하나라도 이름이 다르면 Unreal이 플러그인이나 모듈을 제대로 찾지 못할 수 있다.
20. 플러그인 활성화 확인
.uproject에 등록한 뒤 에디터에서 Edit → Plugins를 확인했다.
검색창에 tempo를 입력했을 때 Temporary 플러그인이 보였고, 체크되어 있었다.
이것은 다음을 의미한다.
Temporary 플러그인 등록 성공
Temporary 플러그인 활성화 성공
Unreal Editor가 플러그인을 인식함
즉 이 단계에서 플러그인 자체는 정상적으로 프로젝트에 연결된 상태였다.
21. 콘텐츠 브라우저에서 플러그인 폴더가 안 보인 이유
플러그인은 정상적으로 활성화되어 있었지만, 처음에는 콘텐츠 브라우저에서 플러그인 폴더가 보이지 않았다.
이유는 보통 다음 중 하나다.
1. Content Browser에서 Show Plugin Content가 꺼져 있음
2. Temporary.uplugin에서 CanContainContent가 true가 아님
3. Plugins/Temporary/Content 폴더가 비어 있음
4. C++ Source 폴더를 콘텐츠 브라우저에서 보려고 함
콘텐츠 브라우저는 기본적으로 에셋을 보여주는 곳이다.
따라서 다음 파일들은 콘텐츠 브라우저에 그대로 보이는 대상이 아니다.
Temporary.h
Temporary.cpp
Temporary.Build.cs
Temporary.uplugin
콘텐츠 브라우저에 보이는 것은 주로 플러그인의 Content 폴더 안에 있는 에셋이다.
예를 들면 다음과 같다.
Blueprint
Material
Texture
DataAsset
Widget Blueprint
Sound
Animation
Map
22. Show Plugin Content 설정
플러그인 콘텐츠를 보려면 콘텐츠 브라우저에서 설정을 켜야 한다.
경로는 다음과 같다.
Content Browser
↓
Settings 또는 톱니바퀴
↓
Show Plugin Content 체크
이 설정을 켜야 플러그인의 Content 폴더가 콘텐츠 브라우저에 표시된다.
정상적으로 보이면 다음과 같은 형태로 나타날 수 있다.
Plugins
└─ Temporary Content
또는 버전에 따라 Temporary Content가 별도로 보일 수 있다.
23. CanContainContent의 의미
Temporary.uplugin에는 다음 옵션이 있어야 한다.
"CanContainContent": true
이 옵션은 다음과 같은 의미다.
이 플러그인은 Content 폴더를 가질 수 있다.
이 플러그인 안에 에셋을 포함할 수 있다.
만약 이 값이 false이거나 없으면, 플러그인의 콘텐츠 폴더가 콘텐츠 브라우저에 표시되지 않을 수 있다.
따라서 콘텐츠를 포함하는 플러그인이라면 CanContainContent를 true로 설정하는 것이 중요하다.
24. Source 폴더가 콘텐츠 브라우저에 안 보이는 것은 정상
플러그인 안에는 다음과 같은 C++ 소스 파일이 있었다.
Plugins/Temporary/Source/Temporary/Public/Temporary.h
Plugins/Temporary/Source/Temporary/Private/Temporary.cpp
하지만 이 파일들은 콘텐츠 브라우저에서 보이는 대상이 아니다.
왜냐하면 콘텐츠 브라우저는 C++ 소스 폴더를 보여주는 곳이 아니라, Unreal 에셋을 보여주는 곳이기 때문이다.
또 FTemporaryModule은 UCLASS 기반 클래스가 아니다.
class FTemporaryModule : public IModuleInterface
이 클래스는 Actor나 UObject가 아니기 때문에 콘텐츠 브라우저의 C++ Classes에서도 일반 Actor 클래스처럼 보이지 않을 수 있다.
즉 플러그인 모듈 클래스는 콘텐츠 브라우저가 아니라 빌드와 로그로 확인하는 것이 핵심이다.
25. 플러그인 검증 완료
최종적으로 다음 상태를 확인했다.
Edit → Plugins에서 Temporary 플러그인이 보임
Temporary 플러그인이 체크되어 있음
Content Browser에서 Plugin Content 표시 해결
Temporary 플러그인 콘텐츠 영역 확인
따라서 Temporary 플러그인 연결은 정상적으로 완료된 것이다.
이번 단계에서 성공한 전체 흐름은 다음과 같다.
Temporary 플러그인 폴더 생성
↓
Temporary.uplugin 작성
↓
Temporary 모듈 Source 구성
↓
StartupModule / ShutdownModule 구현
↓
.uproject에 Temporary 플러그인 등록
↓
Edit → Plugins에서 Temporary 활성화 확인
↓
Content Browser에서 Plugin Content 표시 확인
26. 우리가 만든 플러그인의 정체
우리가 만든 Temporary 플러그인은 아직 실제 게임 기능은 거의 없다.
현재는 다음 정도만 한다.
플러그인이 로드될 때 로그 출력
플러그인이 언로드될 때 로그 출력
Content 폴더를 가질 수 있음
프로젝트에서 활성화 가능
즉 완성된 기능 플러그인이 아니라, 플러그인이 Unreal 프로젝트에 어떻게 등록되고 로드되는지 확인하는 기본 골격이다.
다르게 표현하면 다음과 같다.
완성된 인벤토리 시스템을 만든 것이 아님
→ 인벤토리 시스템을 넣을 수 있는 플러그인 구조를 만든 것
완성된 퀘스트 시스템을 만든 것이 아님
→ 퀘스트 시스템을 독립 기능으로 만들 수 있는 기반을 배운 것
27. 플러그인을 배운 이유
처음에는 Temporary 플러그인이 실제 기능이 없어 보여서 어디에 써먹는지 헷갈렸다.
하지만 플러그인을 배우는 이유는 기능을 독립적인 부품처럼 분리하기 위해서다.
예를 들어 게임 프로젝트에 모든 기능을 한 모듈 안에 넣으면 나중에 구조가 복잡해진다.
Source/ModuleAndPlugin
├─ Inventory
├─ Quest
├─ Dialogue
├─ Minimap
├─ Save
├─ UI
└─ Combat
작은 프로젝트에서는 괜찮을 수 있지만, 프로젝트가 커질수록 관리가 어려워진다.
그래서 재사용 가능하거나 독립성이 높은 기능은 플러그인으로 분리할 수 있다.
Plugins
├─ InventoryPlugin
├─ QuestPlugin
├─ DialoguePlugin
├─ MinimapPlugin
└─ SaveSystemPlugin
이렇게 하면 기능별로 관리하기 쉽고, 다른 프로젝트로 옮겨 쓰기도 편하다.
28. 플러그인을 실제로 써먹는 예시
플러그인은 다음과 같은 기능을 만들 때 사용할 수 있다.
인벤토리 시스템
퀘스트 시스템
대화 시스템
미니맵 시스템
세이브 시스템
공통 UI 시스템
네트워크 유틸
상호작용 시스템
에디터 자동화 도구
예를 들어 인벤토리 시스템을 플러그인으로 만들면 다음과 같은 구조가 될 수 있다.
Plugins
└─ InventoryPlugin
├─ InventoryPlugin.uplugin
├─ Source
│ └─ InventoryPlugin
│ ├─ Public
│ └─ Private
└─ Content
├─ BP_InventoryWidget
├─ DA_ItemData
├─ T_ItemIcon_Sword
└─ T_ItemIcon_Potion
이렇게 만든 플러그인은 다른 프로젝트에서도 재사용할 수 있다.
RPG 프로젝트
→ InventoryPlugin 사용
생존게임 프로젝트
→ InventoryPlugin 복사해서 사용
멀티플레이 프로젝트
→ InventoryPlugin 수정해서 사용
즉 플러그인의 가장 큰 장점은 재사용성과 독립성이다.
29. 에디터 도구 플러그인 예시
플러그인은 게임 실행 중 기능만 만드는 것이 아니다.
Unreal Editor에서 사용하는 도구도 플러그인으로 만들 수 있다.
예를 들면 다음과 같다.
아이템 스폰 위치 자동 생성
데이터 테이블 자동 검사
잘못된 에셋 이름 검사
맵에 몬스터 자동 배치
퀘스트 데이터 자동 생성
커스텀 에디터 창 만들기
이런 기능은 게임 실행 중에는 필요 없지만, 개발할 때 반복 작업을 줄여준다.
그래서 이런 기능은 Editor Plugin으로 만들 수 있다.
MapToolPlugin
DataValidationPlugin
QuestEditorPlugin
팀 프로젝트에서는 이런 에디터 플러그인을 만들어 작업 효율을 높일 수 있다.
30. 이번 실습에서 진짜 배운 것
이번 실습에서 배운 것은 단순히 Temporary라는 이름의 플러그인을 만든 것이 아니다.
진짜 배운 내용은 다음과 같다.
1. 프로젝트는 여러 모듈로 구성될 수 있다.
2. 한 모듈에서 만든 클래스를 다른 모듈에서 사용할 수 있다.
3. 모듈 간 참조를 위해서는 Build.cs 의존성 설정이 필요하다.
4. 다른 모듈에서 사용할 헤더는 Public 폴더에 있어야 한다.
5. 다른 모듈에서 사용할 클래스에는 올바른 API 매크로가 필요하다.
6. 플러그인은 프로젝트에 붙였다 뗄 수 있는 독립 기능 패키지다.
7. .uplugin은 플러그인의 설명서 역할을 한다.
8. 플러그인도 내부에 Source와 Content를 가질 수 있다.
9. 플러그인 모듈도 Build.cs를 통해 의존성을 관리한다.
10. StartupModule과 ShutdownModule로 플러그인 모듈의 시작과 종료를 처리할 수 있다.
11. .uproject에 등록해야 프로젝트에서 플러그인을 활성화할 수 있다.
12. 콘텐츠 브라우저에서 플러그인 콘텐츠를 보려면 Show Plugin Content를 켜야 한다.
즉 이번 실습은 Unreal 프로젝트를 단순히 하나의 코드 덩어리로 보는 것이 아니라, 모듈과 플러그인 단위로 분리해서 설계하는 방법을 배우는 과정이었다.
오늘 헷갈렸던 점
오늘 헷갈렸던 첫 번째 부분은 Primary 모듈이라는 표현이었다.
처음에는 Primary라는 이름의 모듈을 새로 만들어야 하는 것처럼 보였지만, 실제로는 현재 프로젝트의 주 게임 모듈을 의미했다.
현재 프로젝트에서는 ModuleAndPlugin 모듈이 그 역할을 한다.
Primary 모듈
→ 새로 만들 모듈 이름이 아님
→ 현재 프로젝트의 주 게임 모듈
현재 프로젝트 기준
→ ModuleAndPlugin 모듈
두 번째로 헷갈렸던 부분은 TestActor를 어디에 만들어야 하는지였다.
처음에는 ModuleAndPlugin 모듈에 생성할 뻔했지만, 과제에서는 Test 모듈에 속한 C++ 클래스라고 되어 있었다.
따라서 TestActor는 Source/Test/Public, Source/Test/Private 아래에 만들어야 했다.
세 번째로 헷갈렸던 부분은 .uproject의 Plugins 항목 작성 방식이었다.
처음에는 Name에 두 플러그인 이름을 한 번에 넣으려고 했지만, 올바른 방식은 플러그인마다 객체를 따로 만드는 것이었다.
네 번째로 헷갈렸던 부분은 콘텐츠 브라우저에서 플러그인 폴더가 보이지 않는 문제였다.
플러그인은 정상 활성화되어 있었지만, 콘텐츠 브라우저에서 Show Plugin Content 설정이 꺼져 있거나, 플러그인 Content 폴더가 비어 있으면 안 보이는 것처럼 느껴질 수 있었다.
마지막으로 헷갈렸던 부분은 우리가 만든 플러그인이 실제로 무엇에 쓰이는지였다.
Temporary 플러그인은 아직 기능이 거의 없는 기본 골격이지만, 이 구조를 알면 나중에 인벤토리, 퀘스트, 대화 시스템, 공통 UI, 에디터 도구 같은 기능을 독립 플러그인으로 만들 수 있다.
오늘의 핵심 정리
- Primary 모듈은 새로 만들 모듈 이름이 아니라 주 게임 모듈을 의미한다.
- 현재 프로젝트에서 주 게임 모듈은 ModuleAndPlugin이다.
- TestActor는 과제 기준으로 Test 모듈에 생성해야 한다.
- TestActor.h는 다른 모듈에서 include해야 하므로 Test/Public에 있어야 한다.
- TestActor.cpp는 구현 파일이므로 Test/Private에 있으면 된다.
- ATestActor 클래스에는 TEST_API 매크로가 붙어야 한다.
- 다른 모듈에서 클래스를 사용하려면 해당 모듈의 Build.cs에 의존성을 추가해야 한다.
- ModuleAndPlugin.Build.cs에 "Test"를 추가하면 Test 모듈의 Public 헤더를 사용할 수 있다.
- 캐릭터 cpp에서 #include "TestActor.h"로 다른 모듈의 클래스를 참조할 수 있다.
- SpawnActor<ATestActor>()를 통해 Test 모듈의 Actor를 월드에 생성할 수 있다.
- 모듈 간 상호작용은 한 모듈이 다른 모듈의 Public 클래스를 참조하고 사용하는 구조다.
- 플러그인은 프로젝트에 붙였다 뗄 수 있는 독립적인 기능 패키지다.
- 플러그인은 프로젝트 루트의 Plugins 폴더 아래에 둔다.
- Temporary.uplugin은 Temporary 플러그인의 설명서 역할을 한다.
- .uplugin의 Modules 항목은 플러그인 안의 모듈 정보를 Unreal에 알려준다.
- Temporary.Build.cs는 Temporary 모듈의 빌드 의존성을 관리한다.
- Temporary.h에서는 IModuleInterface를 상속받은 모듈 클래스를 선언한다.
- StartupModule()은 모듈이 로드될 때 호출된다.
- ShutdownModule()은 모듈이 언로드될 때 호출된다.
- IMPLEMENT_MODULE(FTemporaryModule, Temporary)는 Temporary 모듈의 구현 클래스를 Unreal에 등록한다.
- .uproject의 Plugins 항목에 Temporary를 등록해야 프로젝트에서 사용할 수 있다.
- Plugins 배열에는 플러그인 하나당 객체 하나를 따로 작성해야 한다.
- 플러그인 이름, 폴더 이름, .uplugin 이름, 모듈 이름, Build.cs 이름은 서로 일치해야 한다.
- Edit → Plugins에서 Temporary가 보이고 체크되어 있으면 플러그인 활성화는 성공이다.
- 콘텐츠 브라우저에서 플러그인 콘텐츠를 보려면 Show Plugin Content를 켜야 한다.
- CanContainContent: true는 플러그인이 Content 폴더와 에셋을 가질 수 있다는 뜻이다.
- 플러그인의 C++ Source 폴더는 콘텐츠 브라우저에 그대로 보이는 대상이 아니다.
- 콘텐츠 브라우저에 보이는 것은 주로 플러그인의 Content 폴더 안에 있는 에셋이다.
- 우리가 만든 Temporary 플러그인은 아직 실제 기능이 없는 기본 골격이다.
- 이 골격 위에 인벤토리, 퀘스트, 대화 시스템, 공통 UI, 에디터 도구 같은 기능을 추가할 수 있다.
- 플러그인을 쓰면 기능을 독립적으로 관리하고 다른 프로젝트에 재사용하기 쉽다.
- 큰 프로젝트에서는 기능을 모듈과 플러그인 단위로 나누는 것이 구조 관리에 유리하다.
정리하면 오늘 배운 내용은 Unreal Engine에서 기능을 모듈과 플러그인 단위로 분리하고 연결하는 방법이다.
TestActor 실습을 통해 모듈 간 참조와 Build.cs 의존성의 중요성을 배웠고, Temporary 플러그인 실습을 통해 .uplugin, 플러그인 모듈, 프로젝트 등록, 콘텐츠 브라우저 표시까지 전체 흐름을 확인했다.
결국 이번 실습의 핵심은 단순히 플러그인을 하나 만드는 것이 아니라, Unreal 프로젝트를 더 큰 규모로 관리하기 위해 기능을 독립적인 단위로 분리하고 재사용 가능한 구조로 설계하는 방법을 배우는 것이었다.
'unreal 8기' 카테고리의 다른 글
| 언리얼 팀 프로젝트(멀티플레이) - 제시어 도형으로만들고 맞추기(1) (0) | 2026.06.30 |
|---|---|
| 2026-6-22 TIL (0) | 2026.06.22 |
| 2026-6-19 TIL (0) | 2026.06.19 |
| 2026-6-18 TIL (0) | 2026.06.19 |
| 2026-6-17 TIL (0) | 2026.06.17 |