언리얼 엔진의 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();
});

실행 시점에 HealthComponentnullptr일 수 있음.


안전한 패턴

재바인딩

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