티스토리 뷰

🟩 학습 목표

  • Blueprint가 단순한 시각적 스크립팅 도구를 넘어 엔진 통합 객체 정의 방식임을 이해한다.
  • UPROPERTY와 UFUNCTION이 변수와 함수에 엔진의 기능을 어떻게 연결하는지 파악한다.
  • Dynamic Delegate 및 Timer 등 콜백 시스템에서 UFUNCTION이 필수적인 이유를 학습한다.
  • 대규모 프로젝트에서 핵심 로직을 C++로, 연출과 튜닝을 Blueprint로 분리하는 실무적 이유를 정리한다.

🟧 1. Blueprint를 클래스 시스템으로 보는 핵심 구조

🟦 단순 로직 도구가 아닌 엔진 통합 객체 정의 방식

Blueprint는 단순히 그래프에 선을 연결하는 도구가 아니라, 클래스 정의, 디폴트 데이터(CDO), 에디터 편집, 직렬화, 가비지 컬렉션(GC) 연결을 포함하는 엔진 통합 객체 정의 방식이다. 즉, 엔진의 타입 시스템 위에 올라간 콘텐츠 제작 단위로 봐야 한다.

🟦 자산(Asset)이면서 클래스(Generated Class)

BP_MyActor를 만들면 내부적으로 UBlueprint 자산이 생기며, 컴파일 시 BP_MyActor_C와 같은 GeneratedClass(UClass)가 생성된다. 이 과정에서 디폴트 값은 CDO로 고정되어 스폰 가능한 타입이 된다.

🟦 데이터 저장소로서의 가치

Blueprint는 C++ 베이스 클래스의 UPROPERTY들을 디폴트 값으로 채우는 도구로 쓸 때 생산성이 가장 높다. 로직 그래프는 필요할 때만 추가하고, 많은 경우 데이터만 담는 용도로 사용해도 충분히 강력하다.


🟧 2. UPROPERTY: 변수와 엔진 시스템의 연결 고리

🟦 주요 역할

UPROPERTY는 단순한 변수 선언이 아니라 엔진 기능을 붙이는 연결 포인트 역할을 한다.

  • 에디터 노출 : Details 패널에서 작업자가 파라미터를 조절할 수 있게 한다.
  • 그래프 접근 : Blueprint 그래프에서 값을 읽거나 쓸 수 있는 권한을 부여한다.
  • 직렬화 및 저장 : 레벨에 배치한 인스턴스의 오버라이드 값이나 SaveGame 데이터를 저장하고 로드한다.
  • GC 추적 : TObjectPtr 등을 통해 GC가 객체를 추적할 수 있게 하여 댕글링 포인터 위험을 방지한다.
  • 네트워크 복제 : Replicated 지정을 통해 변수 단위의 서버-클라이언트 동기화를 지원한다.

🟦 핵심 스펙 지정자 묶음

  • 에디터 편집 범위 : EditDefaultsOnly(클래스 단위), EditInstanceOnly(배치 인스턴스 단위), EditAnywhere(둘 다 가능).
  • BP 그래프 접근 : BlueprintReadOnly(읽기 전용), BlueprintReadWrite(읽기/쓰기 가능).
  • UI 및 품질 : meta=(ClampMin="0", UIMin="0") 등을 통해 디자이너가 실수하지 않도록 가이드를 제공한다.
  • 스폰 주입 : meta=(ExposeOnSpawn=true)를 설정하면 SpawnActor 노드에서 입력 핀으로 노출된다.

🟧 3. UFUNCTION: 함수 메타데이터와 콜백 시스템 연동

🟦 UFUNCTION의 역할

함수에 엔진 메타데이터를 붙여서 Blueprint 호출/오버라이드, 네트워크 RPC, Delegate(Dynamic) 바인딩, 콘솔 호출 등 엔진 시스템과 연결한다.

🟦 콜백 시스템과 UFUNCTION의 관계

  • Dynamic Delegate : DECLARE_DYNAMIC_MULTICAST_DELEGATE 등 리플렉션 기반 델리게이트를 사용할 때, AddDynamic으로 바인딩할 대상 함수는 반드시 UFUNCTION이어야 한다.
  • Timer 콜백 : 함수 포인터 방식은 일반 멤버함수도 가능하지만, BindUFunction처럼 이름/문자열 기반으로 콜백을 걸 때는 UFUNCTION이 필수다.
  • OnRep(RepNotify) : 변수 값이 복제될 때 실행되는 콜백 함수는 보통 UFUNCTION으로 선언하여 엔진이 호출할 수 있게 한다.
  • 엔진 이벤트 : OnComponentBeginOverlap 같은 표준 이벤트도 Dynamic Multicast 구조이므로 바인딩 대상에 UFUNCTION이 필요하다.

🟧 4. 현업에서 핵심 로직을 C++로 분리하는 이유

🟦 유지보수 및 디버깅 효율

  • 유지보수 비용 : Blueprint 그래프가 커지면 의존 관계 파악이 어렵고 리팩토링이 느려진다.
  • 코드 리뷰 : C++은 텍스트 기반으로 변경 의도를 빠르게 검증할 수 있지만, Blueprint는 노드 단위 변경이라 리뷰가 어렵다.
  • 디버깅 : C++은 브레이크포인트, 콜스택, 크래시 덤프 분석이 표준화되어 있어 복잡한 타이밍 문제를 추적하기 유리하다.

🟦 성능 및 협업 최적화

  • 런타임 성능 : 틱, AI, 전투 판정 등 핵심 루프에서 발생하는 병목 현상은 메모리와 캐시 관리가 용이한 C++이 훨씬 강력하다.
  • 협업 충돌 : Blueprint 자산은 이진(Binary) 데이터라 여러 사람이 동시에 수정하면 병합 충돌 해결 비용이 매우 크다.

🟦 설계 안정성 및 보안

  • 네트워크 권한 : 서버 권한 검증 및 치트 방지 로직은 실수 비용이 크기 때문에 C++ 레이어에서 견고하게 관리한다.
  • 장기 운영성 : 대규모 프로젝트일수록 API 경계와 모듈화, 테스트 가능성이 중요해지는데, 이는 시스템 언어인 C++의 강점이다.

🟧 5. 결론 : 역할의 분리

Blueprint 로직이 나빠서 안 쓰는 것이 아니라, 핵심 규칙과 시스템은 운영 비용과 성능을 위해 C++에 두고, Blueprint는 이를 조립하여 콘텐츠, 연출, UI, 수치 튜닝에 특화하여 사용하는 것이 현업의 표준이다.