언리얼 엔진 C++ 프로젝트를 진행하면서 Delegate를 사용하는 경우가 종종 있었다.
예를 들어 체력이 변경되었을 때 UI에 알리거나,
몬스터가 죽었을 때 방이나 퀘스트 시스템에 알리는 것처럼
특정 이벤트가 발생했다는 사실을 다른 객체에 전달할 때 델리게이트를 사용했다.
지금까지는 델리게이트를 주로 “이벤트 발생을 알리는 기능” 정도로 이해하고 사용했다.
하지만 글을 정리하면서 다시 보니
Singlecast와 Multicast, Dynamic과 Native, 바인딩 방식처럼
구분해서 알아두면 좋은 개념들이 있었다.
그래서 이번 글에서는 언리얼 엔진의 델리게이트에 대해 간단히 정리해보려고 한다.
Delegate란?
Delegate는 특정 이벤트가 발생했을 때
미리 바인딩해둔 함수를 호출할 수 있게 해주는 타입 안전한 콜백 시스템이다.
일반적인 함수 호출은 호출하는 쪽이 호출 대상 객체와 함수를 직접 알고 있어야 한다.
하지만 델리게이트를 사용하면
이벤트를 발생시키는 객체가 이벤트를 처리할 객체의 구체적인 타입을 몰라도 된다.
예를 들어 플레이어의 체력이 변경되었을 때
플레이어는 UI 위젯을 직접 알 필요 없이 델리게이트를 호출할 수 있다.
OnHealthChanged.Broadcast(CurrentHealth);
그러면 이 델리게이트에 바인딩된 객체들이 체력 변경 이벤트를 받아 각자 필요한 처리를 할 수 있다.
이렇게 하면 객체 간 의존성을 줄일 수 있다.
다만 델리게이트를 많이 사용하면
이벤트가 어디에서 바인딩되고 호출되는지 추적하기 어려워질 수 있으므로 주의해야 한다.
Singlecast Delegate와 Multicast Delegate
델리게이트는 크게 하나의 함수만 연결할 수 있는지,
여러 함수를 연결할 수 있는지에 따라 나눌 수 있다.
Singlecast Delegate
Singlecast Delegate는 하나의 함수만 바인딩할 수 있는 델리게이트이다.
새로운 함수를 다시 바인딩하면 기존 바인딩이 대체될 수 있다.
DECLARE_DELEGATE(FOnTaskFinished);
FOnTaskFinished OnTaskFinished;
바인딩한 함수를 실행할 때는 Execute() 또는 ExecuteIfBound()를 사용할 수 있다.
OnTaskFinished.ExecuteIfBound();
Execute()는 델리게이트가 바인딩되어 있다는 확신이 있을 때 사용해야 한다.
바인딩 여부가 불확실하다면 ExecuteIfBound()를 사용하는 것이 안전하다.
Multicast Delegate
Multicast Delegate는 여러 함수를 바인딩할 수 있는 델리게이트이다.
여러 객체가 같은 이벤트를 구독해야 할 때 사용할 수 있다.
DECLARE_MULTICAST_DELEGATE(FOnDead);
FOnDead OnDead;
호출할 때는 Broadcast()를 사용한다.
OnDead.Broadcast();
예를 들어 몬스터가 죽었을 때 방 시스템, 퀘스트 시스템, UI 시스템이 각각 반응해야 한다면
Multicast Delegate를 사용할 수 있다.
다만 바인딩한 함수들이 호출되는 순서에 의존해야 하는 경우는 피하는 것이 좋다.
Dynamic Delegate와 Native Delegate
델리게이트는 리플렉션을 지원하는지에 따라Dynamic Delegate와 일반 C++용 델리게이트로 나눌 수 있다.
Dynamic Delegate
Dynamic Delegate는 언리얼 리플렉션 시스템을 지원하는 델리게이트이다.
블루프린트와 연동해야 하거나,UPROPERTY(BlueprintAssignable)로 에디터에서 이벤트를 바인딩하고 싶을 때 사용할 수 있다.
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDoorOpened);
UPROPERTY(BlueprintAssignable)
FOnDoorOpened OnDoorOpened;
Dynamic Delegate에 C++ 함수를 바인딩하려면 대상 함수에 UFUNCTION()이 필요하다.
UFUNCTION()
void HandleDoorOpened();
OnDoorOpened.AddDynamic(this, &AMyActor::HandleDoorOpened);
즉, 블루프린트에서 이벤트처럼 사용하고 싶다면
Dynamic Multicast Delegate를 사용하는 경우가 많다.
Native Delegate(C++ 전용)
리플렉션이나 블루프린트 연동이 필요 없다면 일반 C++용 델리게이트를 사용할 수 있다.
이 경우 AddUObject, BindUObject, AddLambda 같은 함수를 사용해 바인딩할 수 있다.
OnDead.AddUObject(this, &AMyActor::HandleDead);
Native Delegate는 C++ 전용으로 사용할 때 적합하고 Dynamic Delegate보다 가볍게 사용할 수 있다.
델리게이트 바인딩 함수
델리게이트를 바인딩할 때는 델리게이트 종류에 따라 사용하는 함수 이름이 조금 다르다.
Singlecast Delegate는 보통 Bind 계열 함수를 사용한다.
MyDelegate.BindUObject(this, &AMyActor::HandleEvent);
Multicast Delegate는 보통 Add 계열 함수를 사용한다.
MyDelegate.AddUObject(this, &AMyActor::HandleEvent);
대표적인 바인딩 함수는 다음과 같다.
| 함수 | 용도 |
|---|---|
BindUObject / AddUObject |
UObject 멤버 함수 바인딩 |
BindRaw / AddRaw |
일반 C++ 객체 raw pointer 바인딩 |
BindSP / AddSP |
TSharedPtr 기반 객체 바인딩 |
BindLambda / AddLambda |
람다 바인딩 |
BindDynamic / AddDynamic |
Dynamic Delegate에 UFUNCTION 바인딩 |
BindRaw나 AddRaw는 객체의 수명을 자동으로 관리해주지 않기 때문에
대상 객체가 먼저 사라지지 않도록 주의해야 한다.
람다 바인딩 시 주의할 점
델리게이트에는 람다도 바인딩할 수 있다.
OnDead.AddLambda([]()
{
UE_LOG(LogTemp, Log, TEXT("Dead"));
});
하지만 람다를 사용할 때는 캡처에 주의해야 한다.
특히 타이머, 비동기 콜백, 델리게이트처럼
나중에 실행될 수 있는 코드에서는 캡처한 값의 수명이 문제가 될 수 있다.
값 타입인 int32, float, FName, FVector 같은 값은 비교적 안전한 편이다.
하지만 참조 캡처를 사용하면 람다가 실행되는 시점에 이미 지역 변수가 사라져 있을 수 있다.
int32 Count = 10;
OnDead.AddLambda([&]()
{
UE_LOG(LogTemp, Log, TEXT("%d"), Count);
});
위와 같은 코드는 람다가 나중에 실행된다면 위험할 수 있다.
this 캡처도 마찬가지다.
OnDead.AddLambda([this]()
{
DoSomething();
});
람다가 실행되기 전에 Actor나 UObject가 먼저 파괴되면 잘못된 포인터에 접근할 수 있다.
이럴 때는 TWeakObjectPtr을 캡처한 뒤, 실행 시점에 유효성을 확인하는 방식이 더 안전하다.
TWeakObjectPtr<AMyActor> WeakThis = this;
OnDead.AddLambda([WeakThis]()
{
if (WeakThis.IsValid())
{
WeakThis->DoSomething();
}
});
TWeakObjectPtr은 객체의 생존을 보장하는 것은 아니지만,
객체가 아직 유효한지 확인할 수 있게 해준다.
정리
델리게이트는 특정 이벤트가 발생했을 때 미리 바인딩된 함수를 호출하기 위한 콜백 시스템이다.
객체가 다른 객체의 구체적인 타입을 몰라도
이벤트를 전달할 수 있기 때문에 객체 간 의존성을 줄이는 데 도움이 된다.
간단히 정리하면 다음과 같다.
| 구분 | 설명 |
|---|---|
Singlecast Delegate |
하나의 함수만 바인딩 |
Multicast Delegate |
여러 함수를 바인딩하고 Broadcast()로 호출 |
Dynamic Delegate |
리플렉션 지원, 블루프린트 연동 가능 |
Native Delegate |
C++ 전용, 상대적으로 가벼움 |
델리게이트는 UI 갱신, 상태 변경 알림, 이벤트 전달처럼
어떤 일이 발생했다는 사실을 다른 객체에게 알릴 때 유용하다.
마무리
이번에 델리게이트를 정리하면서
지금까지 프로젝트에서 사용했던 델리게이트를 조금 더 명확하게 이해할 수 있었다.
기존에는 주로 체력 변경, 몬스터 사망, 방 클리어처럼
어떤 이벤트가 발생했다는 사실을 다른 객체에 알리는 용도로 사용했다.
정리해보니 델리게이트는 단순히 함수를 대신 호출하는 기능이라기보다,
이벤트를 발생시키는 객체와 처리하는 객체 사이의 의존성을 줄여주는 구조에 가깝다는 것을 알게 되었다.
또한 Singlecast와 Multicast, Dynamic과 Native처럼
사용 목적에 따라 델리게이트 종류를 구분해야 한다는 점도 중요했다.
앞으로 델리게이트를 사용할 때는 단순히 이벤트를 알린다는 관점에서만 쓰기보다,
몇 개의 함수가 반응해야 하는지, 블루프린트 연동이 필요한지,
바인딩된 객체의 수명은 안전한지까지 함께 고려해야겠다.
'언리얼 엔진' 카테고리의 다른 글
| [언리얼 엔진] GAS GameplayAbility와 GameplayEvent 정리 (0) | 2026.06.18 |
|---|---|
| [언리얼 엔진] GAS 기본 구성 요소 정리 (0) | 2026.06.17 |
| [언리얼 엔진] 스마트 포인터 정리 (0) | 2026.06.15 |
| [언리얼 엔진] Replication이란? Actor와 Property 복제 정리 (0) | 2026.06.12 |
| [언리얼 엔진] std::map과 TMap 차이 정리 (0) | 2026.06.11 |