블로그로 돌아가기

JetBrains Rider를 활용한 Godot Addon 개발 고속 가이드: Native C++ GDExtensions 및 Editor Plugins

게시일 2026년 5월 29일
JetBrains Rider를 활용한 Godot Addon 개발 고속 가이드: Native C++ GDExtensions 및 Editor Plugins

핵심 요약

JetBrains Rider 2026.2가 지원하는 GDExtension 및 GDScript 통합 툴체인을 통해 번거로운 수동 설정 단계를 없애고 고성능 Godot Addon을 빠르게 개발할 수 있는 워크플로우를 분석합니다. 하이브리드 아키텍처 환경에서 C++와 GDScript 디버깅 세션을 단일 세션에서 유기적으로 통합하는 기법과, 실시간 동기화를 구현할 때 인프라 구축 리소스를 절감할 수 있는 [horizOn](https://horizon.pm) Backend 연동법을 제시합니다. 아울러 네임스페이스 격리, CI/CD를 활용한 바이너리 컴파일 자동화, 그리고 안정적인 오프라인 폴백 처리 등 현업에 즉시 적용 가능한 5가지 핵심 실무 권장사항을 요약해 제공합니다.

커스텀 Godot Editor Plugin이나 Native C++ GDExtension을 빌드하는 작업은 대개 고통스러운 깨달음과 함께 시작됩니다. Plugin의 실제 로직을 작성하는 대신, SCons 컴파일 스크립트를 설정하고, 헤드리스 컴파일러 플래그와 씨름하며, 모호한 크로스 컴파일 에러를 분석하느라 벌써 3시간을 허비했다는 사실을 깨닫게 되기 때문입니다. Godot 4의 아키텍처는 Extension 개발에 엄청난 잠재력을 열어주었지만, 지금까지의 개발자 경험은 마치 이쑤시개로 서버 랙을 구성하는 것처럼 비효율적이었습니다. 하지만 새로운 Godot Asset Store가 출시되면서, IDE 제공사들도 마침내 Addon 개발을 일류 기능(first-class citizen)으로 대우하기 시작했습니다.

JetBrains Rider 2026.2는 Godot Asset Store를 위한 전용 템플릿, CMake 자동화, 그리고 멀티 언어 디버깅 환경을 제공하는 최초의 주요 IDE 툴 제공사 중 하나로 공식 등장했습니다. 이는 인디 개발자와 팀의 툴 엔지니어들에게 더 이상 지긋지긋한 보일러플레이트 설정 지옥을 겪지 않고, 신속하게 도구를 프로토타이핑할 수 있게 되었음을 의미합니다. 클릭 한 번으로 끝나는 마법사(wizard)를 통해 설정 시간을 단축함으로써, Godot의 핵심 Editor 인터페이스를 확장하는 장벽이 그 어느 때보다 낮아졌습니다.

GDExtension 병목 현상: 과거 Godot 툴링이 고통스러웠던 이유

내부적으로 Godot 4는 GDExtension을 통해 개발자가 엔진 전체를 재컴파일하지 않고도 엔진의 핵심 구조와 직접 상호작용하는 고성능 C++ 또는 Rust 코드를 작성할 수 있도록 지원합니다. 이는 Windows의 .dll, Linux의 .so, macOS의 .dylib와 같은 동적 라이브러리를 로드하여 이를 GDScript 인터페이스에 매핑하는 방식으로 동작합니다. 하지만 이를 수동으로 설정하려면 godot-cpp 바인딩 리포지토리를 가져오고, 정확한 엔진 버전 헤더를 맞추고, 커스텀 SCons 또는 CMake 스크립트를 작성해야 하며, 5가지 이상의 타겟 플랫폼에 맞춰 라이브러리 경로를 매핑하는 .gdextension 설정 파일을 구성해야 합니다.

설상가상으로, 이러한 네이티브 바이너리를 디버깅하는 과정은 크래시가 자주 발생하기로 악명이 높았습니다. 일반적인 트러블슈팅 워크플로우는 별도의 디버거(GDB나 LLDB 등)를 통해 Godot Editor를 실행하고, 외부 에디터에서 중단점(breakpoint)을 설정한 뒤, 핫 리로드(hot-reload)가 엔진의 메인 스레드에서 패닉을 일으켜 하드 크래시로 이어지지 않기를 기도하는 식이었습니다. 개발자가 커스텀 툴링, 특히 복잡한 데이터베이스 동기화 도구, 저지연 Netcode 인터페이스, 또는 에셋 파이프라인 등을 구축할 때 이러한 마찰은 생산성을 완전히 망쳐놓습니다.

Rider 2026.2: 새로운 Addon 툴체인 파헤치기

곧바로 사용 가능한 프로젝트 템플릿

Rider 2026.2는 Godot Extension 포맷의 거의 모든 범위를 아우르는 전용 마법사(wizard) 기반 템플릿을 제공합니다. 이제 더 이상 다른 프로젝트의 리포지토리 보일러플레이트를 복제하거나 폴더 구조를 복사해서 붙여넣을 필요가 없습니다. 대신 IDE가 GDScript Editor Plugin, C# Extension, C++ GDExtension에 맞춰 plugin.cfg부터 타겟 빌드 폴더에 이르기까지 모든 것이 사전 구성된 깔끔하고 체계적인 리포지토리를 직접 구축해 줍니다. 덕분에 몇 시간씩 걸리던 설정을 아낄 수 있으며, 초기 개발 단계에서 실패하는 가장 흔한 원인 중 하나인 매니페스트 파일 내 디렉토리 경로 불일치 문제를 원천 차단합니다.

C++를 위한 네이티브 CMake 통합

역사적으로 Godot의 C++ 바인딩은 빌드 시스템으로 SCons를 강하게 선호해 왔습니다. SCons는 강력하지만, Python 기반 설정 파일은 내부 구조를 파악하기 어렵고 IDE 자동 완성이 작동하지 않아 CI/CD 통합을 번거롭게 만들었습니다. Rider 2026.2는 GDExtension 프로젝트를 위해 강력한 네이티브 CMake 통합 기능을 도입했습니다. GDExtension Addon을 생성하면 Rider가 핵심 godot-cpp 바인딩 라이브러리와 커스텀 소스 코드를 연결해 주는 깔끔한 CMakeLists.txt 파일을 자동으로 생성합니다. 덕분에 추가 설정 없이도 Rider의 강력한 C++ 엔진을 활용하여 코드 내비게이션, 리팩토링, 정적 분석 기능을 마음껏 사용할 수 있습니다.

단일 세션에서 진행되는 이중 언어 디버깅

이번 업데이트의 핵심 중의 핵심 기능입니다. 고성능 Godot 툴을 작성하는 개발자가 단 하나의 언어만 사용하는 경우는 매우 드뭅니다. 일반적으로 무거운 데이터 처리나 복잡한 수학 연산에는 고성능 C++를 사용하고, GUI 독(dock)이나 Editor UI 패널에는 가벼운 GDScript를 사용하는 하이브리드 아키텍처를 채택하곤 합니다. 이전에는 이러한 하이브리드 아키텍처를 디버깅하기 위해 C++와 GDScript 각각에 대해 서로 다른 도구를 사용해야 했습니다. 하지만 Rider 2026.2는 통합 실행 구성(run configuration)을 자동으로 생성해 줍니다. 단 한 번의 "Debug" 버튼 클릭으로 Rider가 Godot Editor를 시작하고 프로세스에 디버거를 연결하여 GDScript와 C++ 양쪽의 실행을 동시에 추적합니다. UI의 GDScript에서 중단점(breakpoint)이 활성화된 상태에서 네이티브 C++ 함수 내부로 진입(step into)하면, Rider가 디버깅 세션을 유지한 채 매끄럽게 C++ 디버거로 전환해 줍니다.

즉시 배포 가능한 폴더 아키텍처

새로운 Godot Asset Store는 Addon들이 서로의 네임스페이스를 침범하지 않도록 엄격한 폴더 구조와 패키징 요구사항을 규정하고 있습니다. Rider의 템플릿은 처음부터 이러한 권장사항을 준수하도록 유도합니다. 런타임 파일과 에디터 전용 GUI 컴포넌트를 명확히 분리함으로써, 빌드를 출력할 때 스토어에 즉시 업로드할 수 있는 상태로 완성되도록 보장합니다. 이로써 잦은 머리 아픈 원인이었던 패키징 오류를 자동화된 당연한 과정으로 줄여줍니다.

GDExtension 수명 주기 들여다보기: C++ Backend 빌드

Rider가 제공하는 자동화의 가치를 제대로 이해하려면 코드 수준에서 GDExtension 프로젝트가 실제로 필요로 하는 요소를 뜯어봐야 합니다. 표준 GDExtension에서는 진입점(entry point) 라이브러리 이니셜라이저를 정의하고, 커스텀 클래스 타입을 Godot의 ClassDB에 등록하며, 모듈이 초기화 해제될 때 메모리 할당을 꼼꼼하게 정리해주어야 합니다. 다음 C++ 헤더와 소스 파일은 네이티브 커스텀 노드(여기서는 고성능 telemetry 핸들러)를 작성하는 데 필요한 최소한의 보일러플레이트를 보여줍니다.

// horizon_telemetry_node.h
#ifndef HORIZON_TELEMETRY_NODE_H
#define HORIZON_TELEMETRY_NODE_H

#include <godot_cpp/classes/node.hpp>
#include <godot_cpp/core/class_db.hpp>

namespace godot {

class HorizonTelemetryNode : public Node {
    GDCLASS(HorizonTelemetryNode, Node);

private:
    String session_id;
    int event_count;

protected:
    static void _bind_methods();

public:
    HorizonTelemetryNode();
    ~HorizonTelemetryNode();

    void initialize_telemetry(const String &p_session_id);
    void track_event(const String &p_event_name);
    String get_session_id() const;
};

}

#endif // HORIZON_TELEMETRY_NODE_H

다음으로 핵심 동작을 구현합니다. 구현 파일에서는 Godot의 런타임 리플렉션 엔진이 메서드에 접근할 수 있도록 _bind_methods() 내부에 메서드들을 등록해야 합니다.

// horizon_telemetry_node.cpp
#include "horizon_telemetry_node.h"
#include <godot_cpp/variant/utility_functions.hpp>

using namespace godot;

void HorizonTelemetryNode::_bind_methods() {
    ClassDB::bind_method(D_METHOD("initialize_telemetry", "session_id"), &HorizonTelemetryNode::initialize_telemetry);
    ClassDB::bind_method(D_METHOD("track_event", "event_name"), &HorizonTelemetryNode::track_event);
    ClassDB::bind_method(D_METHOD("get_session_id"), &HorizonTelemetryNode::get_session_id);

    ADD_PROPERTY(PropertyInfo(Variant::STRING, "session_id"), "", "get_session_id");
}

HorizonTelemetryNode::HorizonTelemetryNode() {
    event_count = 0;
    session_id = "";
}

HorizonTelemetryNode::HorizonTelemetryNode::~HorizonTelemetryNode() {
    // Clean up memory safely here
}

void HorizonTelemetryNode::initialize_telemetry(const String &p_session_id) {
    session_id = p_session_id;
    UtilityFunctions::print("Telemetry initialized for session: ", session_id);
}

void HorizonTelemetryNode::track_event(const String &p_event_name) {
    event_count++;
    UtilityFunctions::print("Event tracked: ", p_event_name, " (Total: ", event_count, ")");
}

String HorizonTelemetryNode::get_session_id() const {
    return session_id;
}

마지막으로, 초기화 함수를 사용하여 Godot에 모듈을 어떻게 로드할지 알려주어야 합니다. 이 register_types 파일은 GDExtension의 로딩 시스템을 통해 구동되는 라이브러리의 메인 진입점 역할을 합니다.

// register_types.cpp
#include "register_types.h"
#include "horizon_telemetry_node.h"
#include <gdextension_interface.h>
#include <godot_cpp/core/defs.hpp>
#include <godot_cpp/godot.hpp>

using namespace godot;

void initialize_horizon_plugin_module(ModuleInitializationLevel p_level) {
    if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
        return;
    }
    ClassDB::register_class<HorizonTelemetryNode>();
}

void uninitialize_horizon_plugin_module(ModuleInitializationLevel p_level) {
    if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
        return;
    }
}

extern "C" {
// Initialization entry point called by Godot.
GDExtensionBool GDE_EXPORT horizon_plugin_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
    godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);

    init_obj.register_initializer(initialize_horizon_plugin_module);
    init_obj.register_terminator(uninitialize_horizon_plugin_module);
    init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);

    return init_obj.init();
}
}

Editor Plugin 개발: GDScript 파이프라인

GDExtension이 고성능 C++ Backend를 처리하는 동안, 하단 독(dock)에 커스텀 패널을 추가하거나 커스텀 인스펙터 노드를 만드는 등 Godot Addon의 UI 부분은 주로 @tool 어노테이션을 사용하는 GDScript로 작성됩니다. @tool 지시어는 게임이 실행 중일 때뿐만 아니라, 현재 실행 중인 에디터 인스턴스 내부에서 이 스크립트가 직접 실행되도록 Godot에 명령합니다.

툴 스크립트를 작성할 때는 철저한 수명 주기 관리(lifecycle management)가 필요합니다. _enter_tree()_exit_tree() 함수는 에디터 기능의 생성자 및 소멸자 역할을 담당합니다. 에디터가 언로드될 때 커스텀 UI 노드를 제대로 정리하지 않으면, 남겨진 고아(orphaned) GUI 노드들이 에디터의 메모리 공간을 차지하게 되며 결국 에디터 종료 시 크래시(crash-on-exit)를 유발하는 원인이 됩니다.

# horizon_editor_plugin.gd
@tool
extends EditorPlugin

const PLUGIN_NAME = "HorizonBackendHelper"
var dock: Control

func _enter_tree() -> void {
    # Initialize the dock UI from our pre-packed scene
    dock = preload("res://addons/horizon_plugin/dock.tscn").instantiate()
    
    # Add the main panel to the editor dock on the upper-right slot
    add_control_to_dock(DOCK_SLOT_LEFT_UR, dock)
    
    # Register our custom backend node so developers can add it in the scene tree
    add_custom_type(
        "HorizonClientNode",
        "Node",
        preload("res://addons/horizon_plugin/horizon_client_node.gd"),
        preload("res://addons/horizon_plugin/icon.png")
    )
    print("Plugin ", PLUGIN_NAME, " successfully initialized in editor.")

func _exit_tree() -> void {
    # Clean up dock and custom types to prevent editor memory leaks
    if dock:
        remove_control_from_docks(dock)
        dock.queue_free()
    
    remove_custom_type("HorizonClientNode")
    print("Plugin ", PLUGIN_NAME, " cleaned up.")

연결성 문제: 원격 Backend 통합하기

Editor Plugin이나 GDExtension을 빌드할 때, 작성한 툴의 효용성은 대개 연동되는 Backend 서비스의 성능에 좌우되곤 합니다. 가령 인디 게임용 어드민 패널, 원격 레벨 에디터, 또는 엔진 내 telemetry 추적기를 빌드하는 경우, Plugin은 데이터베이스와 통신하고, 개발자 또는 플레이어 신원을 관리하며, 원격 상태를 동기화해야 합니다. 이를 직접 구축하려면 안전한 맞춤형 웹 서비스를 설계해야 합니다. 가상 사설 서버(VPS)를 띄우고, API 게이트웨이를 설정하고, 데이터베이스를 배포하며, 커스텀 사용자 인증 모델을 작성하고, SSL/TLS 인증서 로테이션을 구현해야 합니다. 이는 어마어마한 엔지니어링 오버헤드로, Plugin이 데이터베이스와 간단한 통신이라도 시작하기 전에 최소 4주에서 6주에 달하는 집중 개발 시간을 소모하게 만듭니다.

팀의 한정된 에너지를 물리적인 Backend 인프라 관리에 낭비하는 대신, horizOn을 핵심 게임 엔진 Backend로 도입할 수 있습니다. horizOn은 커스텀 Editor Addon에 바로 연결되는 네이티브 고성능 C# 및 GDScript SDK를 제공합니다. 데이터베이스를 구축하고 커스텀 WebSockets 핸들러를 작성하는 데 수 주를 소모할 필요 없이, 프로젝트에 horizOn 클라이언트를 탑재하여 즉시 안전한 인증, 실시간 데이터베이스 액세스, 플레이어 관리 기능을 연동할 수 있습니다. 복잡한 인프라 작업은 horizOn에 맡겨두고, 나중에 스토어에 출시할 때도 Backend가 매끄럽게 확장(scale)될 것이라는 신뢰 속에 개발자는 Addon의 UX와 게임플레이 툴을 정교하게 고도화하는 데만 전념할 수 있습니다.

Godot Addon 개발을 위한 5가지 검증된 Best Practices

1. 폴더 구조를 통한 네임스페이스 격리

항상 res://addons/your_unique_addon_name/ 하위에 고유한 네임스페이스 이름을 지정하여 Addon 폴더 및 스크립트 경로의 접두사(prefix)로 사용하십시오. Godot은 @icon 또는 class_name 지시어로 등록된 모든 커스텀 클래스에 대해 평탄한 전역 경로 네임스페이스를 공유합니다. 만약 NetworkManagerConfigHelper 같은 일반적인 클래스명을 사용할 경우, 개발자의 메인 프로젝트 코드나 다른 서드파티 익스텐션과 충돌을 유발하게 됩니다. 모든 유틸리티 스크립트는 항상 고유한 디렉토리 폴더 하위로 스코프를 제한해 관리해야 합니다.

2. 바이너리 컴파일 자동화 및 VCS 제외

용량이 큰 컴파일된 GDExtension 바이너리 파일(.dll, .so, .dylib)은 메인 Git 리포지토리 히스토리에 포함하지 마십시오. 개발 중 라이브러리를 재컴파일할 때마다 리포지토리 용량이 급격히 부풀어 오릅니다. 대신 .gitignore를 설정하여 빌드 디렉토리와 릴리즈 폴더를 추적 대상에서 제외하고, CMake 스크립트와 CI/CD 파이프라인(GitHub Actions 또는 GitLab CI 등)을 연동하여 여러 타겟 플랫폼용 바이너리를 자동으로 빌드하고 배포용 zip 파일에만 동봉하도록 설계해야 합니다.

3. GDScript와 C++ 경계 처리의 세심한 주의

언어 경계를 넘나들며 변수를 전달할 때 메모리가 어떻게 관리되는지 세심하게 살피셔야 합니다. GDScript는 RefCounted를 상속한 클래스(예: Resource)의 수명 주기를 자동으로 관리하지만, Object를 상속하는 객체(예: 순수 Node 객체 등)에 대해서는 수동 메모리 관리를 요구합니다. C++ GDExtension 코드 작성 시, 참조 카운팅되는 클래스에는 항상 Godot의 Ref<T> 스마트 래퍼를 사용하여 이중 해제(double-free) 오류나 메모리 누수를 예방해야 합니다. 일반 클래스의 경우 Object::cast_to<T>()를 이용해 방어적인 캐스팅을 수행하고, 네이티브 메서드를 호출하기 전 항상 널 포인터 체크를 거치는 습관이 중요합니다.

4. 실시간 상태 관리를 위해 WebSockets 및 영속적 연결 활용하기

공동 에디터 시스템이나 Backend 매치메이킹 툴처럼 실시간 동기화가 필요한 Plugin이라면 기존의 HTTP 폴링 방식을 피해야 합니다. 반복적인 HTTP 요청 전송은 불필요하게 높은 CPU 오버헤드를 발생시키고, Backend 서비스 측에서 빈번한 요청 제한(rate-limiting) 제재를 가하는 원인이 됩니다. 그 대신 HTTP 폴링 대신 WebSockets를 도입하여 상시 연결된 양방향 커넥션을 구성해야 합니다. 이를 통해 기존의 굼뜬 500ms대의 반응 속도를 10ms 미만으로 대폭 단축할 뿐만 아니라 전송되는 데이터 오버헤드도 원천적으로 차단할 수 있습니다.

5. 원격 클라우드 파이프라인을 위한 안정적인 폴백(Fallback) 시스템 설계

제작한 Addon이 원격 클라우드 서버와 통신하는 구조라면, 네트워크 중단으로 인해 Godot 에디터 스레드 전체가 멈추는 불상사가 절대 일어나선 안 됩니다. 동기식 웹 요청은 Godot의 메인 프로세스를 블로킹하여 에디터를 완전히 정지시킬 수 있습니다. 따라서 UI가 부드럽게 유지되도록 반드시 비동기 콜백이나 스레드 풀을 활용해야 합니다. 또한, 라이브 옵스(live-ops) 성격의 기능을 기획하고 있다면 stop killing games 캠페인과 라이브 옵스 서버 폴백(fallback) 비교 논의 등을 벤치마킹하여 견고한 파이프라인을 설계하는 기법을 공부하시기 바랍니다. 이를 통해 클라우드 엔드포인트에 접속할 수 없는 환경에서도 툴이 부드럽게 오프라인 모드로 전환(downgrade)되어 에디터 자체 기능은 안정적으로 유지될 수 있게 보장합니다.

결론: Godot 툴링 파이프라인의 효율성 극대화

JetBrains Rider 2026.2는 Godot Addon 개발 과정을 복잡한 시스템 설정과의 싸움에서 간결하고 생산성 높은 개발 워크플로우로 완전히 바꾸어 놓았습니다. GDExtension 스캐폴딩 자동화, 완벽한 CMake 통합, 그리고 GDScript와 C++ 동시 디버깅 지원을 통해 Rider는 까다로운 설정 피로도를 덜어주고 개발자가 훌륭한 도구를 개발하는 데 집중할 수 있도록 돕습니다. Rider의 강력한 템플릿과 확장 가능하며 완전히 관리되는 Backend 아키텍처를 결합하면 수동으로 서버 인프라를 구축하는 오버헤드 없이도 실시간으로 연결된 고성능 Plugin을 자유롭게 제작할 수 있습니다.

멀티플레이어 Backend를 안정적으로 설계할 준비가 되셨나요? 지금 horizOn을 무료로 체험해 보거나 API 문서를 확인해 보세요.


출처: JetBrains Rider brings support for Godot Asset Store addons

이 대시보드는 다음에 의해 애정을 담아 만들어졌습니다 Projectmakers

© 2026 projectmakers.de

unknown-v1.91.1 / unknown-v--