언리얼 엔진의 Delegate는 함수 포인터를 안전하고 확장 가능하게 감싼 이벤트 시스템이다.
특정 시점에 등록된 함수를 호출하여 객체 간 결합도를 낮추는 데 사용된다.
Delegate 종류
DECLARE_DELEGATE(FOnFinished);
FOnFinished OnFinished;
OnFinished.BindUObject(this, &AMyActor::HandleFinished);
OnFinished.ExecuteIfBound();
특징
- 하나의 함수만 등록 가능
- 가장 가볍고 빠름
- 반환값 지원 가능
여러 함수를 등록할 수 있다.
DECLARE_MULTICAST_DELEGATE(FOnHealthChanged);
FOnHealthChanged OnHealthChanged;
OnHealthChanged.AddUObject(this, &AMyActor::HandleHealthChanged);
OnHealthChanged.Broadcast();
특징
- 여러 리스너 등록 가능
- 반환값 없음
- Observer 패턴 구현에 적합
Reflection 기반으로 동작하여 Blueprint와 연동 가능하다.
DECLARE_DYNAMIC_DELEGATE(FOnCompleted);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnClicked);
OnClicked.AddDynamic(this, &AMyActor::OnClickedHandler);
특징
UFUNCTION()필요- 직렬화 가능
- Blueprint 노출 가능
- 일반 Delegate보다 느림
메모리 절약형 Dynamic Multicast Delegate
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE(...)
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_OneParam(...)
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_TwoParams(...)
특징
- Dynamic Multicast 계열
- Blueprint 노출 가능
- 바인딩된 객체만 별도 저장소 사용
- 많은 객체 중 일부만 이벤트를 쓸 때 유리
외부에서는 호출할 수 없고, 선언한 클래스 내부에서만 Broadcast할 수 있는 Multicast Delegate의 래퍼
DECLARE_EVENT(AMyActor, FOnStateChanged);
특징
- 캡슐화 강화
- 외부는 Add/Remove만 가능
- Broadcast는 소유 클래스만 가능
| 종류 | 함수 수 | Blueprint | 반환값 | 성능 |
|---|---|---|---|---|
| Delegate | 1개 | X | O | 매우 빠름 |
| Multicast Delegate | 여러 개 | X | X | 빠름 |
| Dynamic Delegate | 1개 | O | 제한적 | 느림 |
| Dynamic Multicast | 여러 개 | O | X | 가장 느림 |
| Event | 여러 개 | X | X | 빠름 |
Bind / Add 계열 함수
BindRaw : 일반 C++ 객체에 바인딩.
Delegate.BindRaw(this, &FMyClass::Func);
- UObject 아님
- 수명 직접 관리 필요
- 객체가 삭제되면 위험
BindUObject : UObject에 바인딩.
Delegate.BindUObject(this, &UMyObject::Func);
- Weak reference 사용
- UObject가 파괴되면 자동 무효화
BindSP : TSharedPtr 기반 객체.
Delegate.BindSP(SharedThis(this), &FMyClass::Func);
BindStatic : 정적 함수.
Delegate.BindStatic(&MyStaticFunc);
BindLambda : 람다 함수.
Delegate.BindLambda([]() {});
Single-cast의 Bind* 대신 Add*를 사용
AddRaw
AddUObject
AddSP
AddStatic
AddLambda
OnChanged.AddUObject(this, &UMyObject::OnChanged);
Multicast에 등록된 함수를 나중에 제거하려면 FDelegateHandle을 저장한다.
FDelegateHandle Handle = OnChanged.AddUObject(this, &UMyObject::OnChanged);
OnChanged.Remove(Handle);
Multicast Delegate에 동일한 함수가 중복 등록되는 것을 방지한다.
OnHealthChanged.AddUniqueUObject(this, &UMyWidget::OnHealthChanged);
아래와 같은 코드 흐름은 같은 함수를 여러번 추가한다.
void UMyWidget::NativeConstruct()
{
Super::NativeConstruct();
HealthComponent->OnHealthChanged.AddUObject(
this,
&UMyWidget::UpdateHealthBar);
}
UpdateHealthBar()
UpdateHealthBar()
UpdateHealthBar()
| 일반 추가 | 중복 방지 |
|---|---|
AddUObject |
AddUniqueUObject |
AddDynamic |
AddUniqueDynamic |
AddRaw |
AddUniqueRaw |
AddLambda |
AddUniqueLambda (제한적) |
BindDynamic
AddDynamic
RemoveDynamic
OnClicked.AddDynamic(this, &UMyWidget::OnClicked);
Add vs Bind 차이
| 함수 | 대상 |
|---|---|
| Bind* | Single-cast Delegate |
| Add* | Multicast Delegate |
| AddDynamic | Dynamic Multicast |
| BindDynamic | Dynamic Single-cast |
Execute / Broadcast
| Delegate 종류 | 호출 함수 |
|---|---|
| Single-cast | Execute() |
| Single-cast 안전 호출 | ExecuteIfBound() |
| Multicast | Broadcast() |
UPROPERTY 변수와 Delegate 사용 시 주의점
코드 전제
UPROPERTY()
UHealthComponent* HealthComponent;
OnChanged.AddUObject(HealthComponent, &UHealthComponent::OnChanged);
문제 1. GC로 인해 객체가 교체
HealthComponent = NewObject<UHealthComponent>();
이전 객체에 등록된 Delegate는 그대로 남아 있고, 새 객체는 등록되지 않는다.
문제 2. Delegate는 UPROPERTY를 추적하지 않음
Delegate는 현재 포인터 값을 기준으로 바인딩함.
HealthComponent ----> ObjectA
바인딩 후:
HealthComponent = ObjectB;
Delegate는 여전히 ObjectA를 호출한다.
문제 3. Lambda 캡처 문제
OnChanged.AddLambda([this]()
{
HealthComponent->DoSomething();
});
실행 시점에 HealthComponent가 nullptr일 수 있음.
안전한 패턴
재바인딩
if (OldComponent)
{
OnChanged.RemoveAll(OldComponent);
}
HealthComponent = NewComponent;
if (HealthComponent)
{
OnChanged.AddUObject(HealthComponent,
&UHealthComponent::OnChanged);
}
Weak Pointer 사용
TWeakObjectPtr<UHealthComponent> WeakComp = HealthComponent;
OnChanged.AddLambda([WeakComp]()
{
if (WeakComp.IsValid())
{
WeakComp->DoSomething();
}
});
AddUObject는 내부적으로 TWeakObjectPtr를 사용하기 때문에
객체가 GC되면 자동으로 무효화되고 호출 시 자동으로 건너뛴다.
하지만 멤버 변수가 다른 객체를 가리키도록 변경되어도 자동으로 재바인딩되지는 않는다.
정리
Delegate는 UPROPERTY 변수 자체를 추적하는 것이 아니라 바인딩 당시의 객체 주소를 저장한다.
UPROPERTY()
UObject* Ptr;
Delegate.AddUObject(Ptr, &UObject::Func);
이후
Ptr = NewObject<UObject>();
- Delegate는 이전 객체에 계속 연결됨
- 새 객체는 호출되지 않음
- 필요 시 직접 Remove 후 Add 해야 함
'Unreal Engine' 카테고리의 다른 글
| GameplayMessageRouter Plugin 2 (1) | 2026.06.11 |
|---|---|
| GameplayMessageRouter Plugin 1 (0) | 2026.06.10 |
| Niagara (0) | 2026.05.20 |
| State Tree, Behavior Tree (0) | 2026.05.07 |
| Unreal Engine MCP (0) | 2026.04.21 |