C++ Quickstart — 설치부터 첫 요청까지¶
이 장을 읽고 나면
패키지를 설치하고, 두 process가 서로 호출하는 최소 project를 실행할 수 있다.
저장소의 framework/languages/cpp/quickstart/
프로젝트다. 아래 코드 블록은 사이트를 빌드할 때 그 파일에서 읽는다. location store 없이
process 둘이 서로의 endpoint를 직접 지정해 request/reply 한 번을 주고받는다.
0. 예제 저장소 clone¶
이 장의 project는 zlink-cpp-examples 저장소의 quickstart/이며, 기능 가이드가 읽는
program인 tutorial/과 samples/도 같은 저장소에 있다.
main은 최신 릴리스에 그 뒤의 수정을 더한 것이고, 패키지 버전은 그 릴리스에 맞춰져 있다.
이전 릴리스는 tag vA.B.C로 받는다(git checkout vA.B.C). 이슈와 PR은 zlink-systems/zlink로
보낸다.
1. 설치¶
- CMake 3.20 이상, C++20 컴파일러. framework가 C++20 coroutine을 사용한다.
bootstrap이 쓰는 preset을 읽는 IDE 경로는 3.21 이상이 필요하고,
bootstrap.cmake는 3.24 이상이다 - nlohmann_json·Boost·liblz4·libprotobuf·OpenSSL·opentelemetry-cpp
zlink는 아직 공식 vcpkg registry와 ConanCenter에 없다. 이 저장소가 overlay port와 Conan
recipe를 함께 담고 있으므로 설치 경로는 셋이다. 끝까지 검증한 것은 GitHub Release 경로다.
zlink-cpp-examples의 quickstart/·tutorial/·samples/에 있는 bootstrap.cmake가 이 경로를
자동화한다.
| 경로 | 지금 상태 |
|---|---|
| GitHub Release | 이 플랫폼의 framework prebuilt 하나를 받아 푼다. Core·C++ binding·framework library·nlohmann_json이 한 prefix에 들어 있다 |
| vcpkg overlay port | 릴리스마다 sync-recipes가 갱신한다. 이 문서의 검증 범위 밖이다 |
| Conan recipe | 릴리스마다 sync-recipes가 갱신한다. 이 문서의 검증 범위 밖이다 |
아카이브 경로의 명령은 프로젝트의
README.md에 있다.
1.1 vcpkg¶
검증 범위 밖
이 경로는 릴리스마다 갱신되지만 이 문서가 검증한 경로가 아니다. 첫 설치는 GitHub Release로 한다.
git clone https://github.com/zlink-systems/zlink.git
vcpkg install zlink zlink-cpp zlink-framework \
--overlay-ports=zlink/vcpkg/ports --triplet=x64-linux
소비자 프로젝트는 vcpkg toolchain을 사용한다.
1.2 Conan¶
검증 범위 밖
이 경로는 릴리스마다 갱신되지만 이 문서가 검증한 경로가 아니다. 첫 설치는 GitHub Release로 한다.
git clone https://github.com/zlink-systems/zlink.git
conan create zlink/core/packaging/conan --build=missing -s compiler.cppstd=gnu20
conan create zlink/bindings/cpp/packaging/conan --build=missing -s compiler.cppstd=gnu20
conan create zlink/framework/languages/cpp/packaging/conan --build=missing -s compiler.cppstd=gnu20
소비자 project의 conanfile.txt에 zlink-framework/0.16.0을 적고 conan install한다.
1.3 GitHub Release¶
세 아카이브를 차례로 빌드해 설치한다 — core/vX.Y.Z → cpp/vX.Y.Z → framework-cpp/vA.B.C.
순서와 실제 명령은 프로젝트의
README.md에 있다.
이 경로에서는 third-party 의존을 직접 설치한다. nlohmann_json·Boost·liblz4·libprotobuf·OpenSSL은
배포판 패키지로 설치하고, opentelemetry-cpp는 배포판 패키지가 없어 소스로 빌드한다.
framework가 링크하는 것은 opentelemetry-cpp::api 하나이므로 -DOTELCPP_WITH_API_ONLY=ON이면
헤더만 빌드된다.
1.4 필요할 때 추가하는 target¶
| target | 언제 추가하나 |
|---|---|
zlink::framework_locations_redis |
Redis location store로 자동 연결을 사용할 때(Location) |
zlink::framework_codec_protobuf · _messagepack |
기본 JSON codec 대신 사용할 때(Handler와 메시지 처리) |
zlink::stream_connector |
외부 client(게임 client·모바일)를 만들 때(STREAM) |
zlink::http_client |
서버에서 HTTP를 호출할 때(HTTP Client 가이드) |
라이선스는 계층마다 다르다 — core·binding은 MPL-2.0, framework는 FSL-1.1-ALv2,
zlink::http_client는 Apache-2.0이다. 서비스를 만들어 파는 데 드는 비용은 없다.
2. 소비자 쪽 CMake¶
세 단계 설치가 끝나면 소비자 project의 CMake는 다음과 같다.
cmake_minimum_required(VERSION 3.24)
project(zlink_quickstart_cpp LANGUAGES CXX)
# Everything the two processes need comes from this one package. The prefix
# it is found under comes from bootstrap.cmake (.zlink/install) or from the
# command line; see README.md.
find_package(zlink_framework CONFIG REQUIRED)
add_executable(quickstart_server Server/main.cpp)
target_link_libraries(quickstart_server PRIVATE zlink::framework)
target_compile_features(quickstart_server PRIVATE cxx_std_20)
add_executable(quickstart_client Client/main.cpp)
target_link_libraries(quickstart_client PRIVATE zlink::framework)
target_compile_features(quickstart_client PRIVATE cxx_std_20)
foreach(_target quickstart_server quickstart_client)
if(MSVC)
# The framework headers are large enough to exhaust MSVC's default object
# file section limit, and they carry UTF-8 source that the compiler reads
# as the system code page without /utf-8.
target_compile_options(${_target} PRIVATE /bigobj /utf-8)
endif()
if(WIN32)
# Windows has no RPATH: the loader looks next to the executable. Core is a
# DLL (zlink.dll) that no package manager deploys for us, so copy every
# runtime DLL this target links, Core included, beside it.
add_custom_command(TARGET ${_target} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
$<TARGET_RUNTIME_DLLS:${_target}> $<TARGET_FILE_DIR:${_target}>
COMMAND_EXPAND_LISTS)
endif()
endforeach()
3. 공유 계약¶
메시지 타입에 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE가 필요하다. 기본 JSON serializer가
ADL로 to_json/from_json을 찾으므로 bare struct는 직렬화되지 않는다.
#pragma once
#include <nlohmann/json.hpp>
#include <string>
// Request/reply contract shared by the server and client processes.
struct hello_t
{
static constexpr const char *packet_name = "Hello";
std::string name;
};
struct greeting_t
{
static constexpr const char *packet_name = "Greeting";
std::string text;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE (hello_t, name)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE (greeting_t, text)
4. 처리하는 쪽¶
object_role 기본값은 none이다. 이 구성은 channel만 쓰는 node임을 드러내기 위해
none을 명시한다. routing_id는 필수다. 이 예제는 127.0.0.1에 bind하므로 같은 host의 process만 접속하며, Windows에서
방화벽 허용 창이 뜨지 않는다. 다른 host나 container에서 접속해야 하면 접속 가능한 주소에 bind하고
advertise_host로 그 주소를 광고한다.
#include "../Shared/messages.hpp"
#include <zlink/framework.hpp>
namespace fw = zlink::framework;
// Handles one request on the "greeting" channel.
class hello_handler_t
{
public:
using request_type = hello_t;
using reply_type = greeting_t;
reply_type handle (const request_type &request) { return greeting_t{"hello, " + request.name}; }
};
int main (int argc, char **argv)
{
auto app = fw::app_t::create ();
app.add_zlink_framework ([] (fw::zlink_framework_options_t &options) {
// Names the mesh and opens this process's endpoint for peers to connect to.
auto mesh = options.add_route_mesh ("services")
.listen ("tcp://127.0.0.1:7301")
// No Object Server/Client role, so no Location Store is required for
// this endpoint-only quickstart.
.set_object_role (fw::object_role_t::none)
.set_routing_id (zlink::routing_id_t::from ("quickstart-server"))
.set_advertise_host ("127.0.0.1");
// This process handles the "greeting" channel.
mesh.channel_name ("greeting")
.server ()
.add_request_handler<hello_handler_t, hello_t, greeting_t> ();
});
return app.run (argc, argv);
}
5. 호출하는 쪽¶
HTTP handler는 경로 파라미터를 인자로 받지 않는다. http_request_t를 받아
request.route_values에서 꺼낸다.
#include "../Shared/messages.hpp"
#include <zlink/framework.hpp>
namespace fw = zlink::framework;
// Serves GET /hello/{name}: calls "greeting" on the mesh and returns the reply.
class hello_http_handler_t
{
public:
explicit hello_http_handler_t (fw::route_client_t &routes) : _routes (routes) {}
fw::task_t<fw::http_response_t> handle (const fw::http_request_t &request)
{
const auto name = request.route_values.at ("name");
auto reply = co_await _routes.request_to_channel ("greeting", hello_t{name})
.template async<greeting_t> ();
co_return fw::http_response_t{200, "\"" + reply.text + "\"", "application/json"};
}
private:
fw::route_client_t &_routes;
};
int main (int argc, char **argv)
{
auto app = fw::app_t::create ();
app.add_zlink_framework ([] (fw::zlink_framework_options_t &options) {
// This process also needs its own endpoint.
auto mesh = options.add_route_mesh ("services")
.listen ("tcp://127.0.0.1:7302")
.set_object_role (fw::object_role_t::none)
.set_routing_id (zlink::routing_id_t::from ("quickstart-client"))
.set_advertise_host ("127.0.0.1");
// This side only calls; it does not handle "greeting".
mesh.channel_name ("greeting").client ();
// Manual connection -- the server's endpoint is given directly.
mesh.peer_connections ().connect ("tcp://127.0.0.1:7301");
options.http ()
.listen ("http://127.0.0.1:5083")
.map_get<hello_http_handler_t> ("/hello/{name}");
});
return app.run (argc, argv);
}
6. 실행¶
bootstrap.cmake가 §1의 설치를 대신하고 이 project를 build/에 구성한다. 이 플랫폼의
framework prebuilt를 받아 .zlink/install/에 풀 뿐 아무것도 빌드하지 않는다. tutorial/을 먼저
bootstrap했으면 cmake -DZLINK_ROOT=../tutorial/.zlink -P bootstrap.cmake로 그 prefix를 재사용한다.
cd zlink-cpp-examples/quickstart
cmake -P bootstrap.cmake
cmake --build build --parallel
# 터미널 두 개. server를 먼저 실행한다.
./build/quickstart_server
./build/quickstart_client
curl http://127.0.0.1:5083/hello/world
§1의 세 단계를 직접 설치했다면 bootstrap 대신 cmake -S . -B build -DCMAKE_PREFIX_PATH=<framework
install prefix>로 구성한다. 응답은 "hello, world", 상태 코드 200이다.
7. IDE에서 열기¶
CMake project이므로 IDE는 CMakeLists.txt와 preset을 그대로 읽는다. bootstrap.cmake는 이
폴더를 구성한 인자(framework install prefix, C++ 표준, generator)를 그대로
CMakeUserPresets.json의 preset zlink로 남기므로, §6의 cmake -P bootstrap.cmake를 한 번
실행한 뒤 폴더를 열고 그 preset을 고르면 끝이다. 환경 변수는 필요 없다. preset은 §6과 같은
build/에 구성하므로 터미널에서 빌드한 결과를 IDE가 이어서 쓴다. CMakeUserPresets.json은
bootstrap이 매번 다시 쓰고 git이 무시하므로 손으로 고치지 않는다 — 값을 바꿔야 하면 bootstrap을
다시 실행한다. Windows preset은 Release 하나다 — prebuilt가 Release로 빌드되어 있어 Debug
consumer를 링크할 수 없다.
7.1 Visual Studio 2022 · 2026¶
C++를 사용한 데스크톱 개발 워크로드와 그 안의 Windows용 C++ CMake 도구 구성 요소가 필요하다.
- 터미널에서
cmake -P bootstrap.cmake를 실행한다(§6). - 파일 → 열기 → 폴더로
quickstart/를 연다. 솔루션 파일은 필요 없다. - 구성 드롭다운에서
zlink bootstrap (Release)를 고른다. - 빌드 → 모두 빌드.
-
시작 항목에서
quickstart_server.exe를 골라 실행하고, 이어서quickstart_client.exe를 실행한 뒤 터미널에서 확인한다.
7.2 Rider · CLion¶
JetBrains IDE(Rider의 C++ 지원, CLion)도 폴더를 열면 CMake project로 인식하고 preset을 profile로 읽는다.
- 터미널에서
cmake -P bootstrap.cmake를 실행한다(§6). WSL에서 빌드하려면 WSL 안에서 실행한다. - File → Open으로
quickstart/폴더를 연다. - Settings → Build, Execution, Deployment → CMake에 preset
zlink가 profile로 나열된다. 그것을 켠다. Toolchain은 Windows에서 Visual Studio, WSL에서 빌드하면 WSL toolchain을 고른다. -
CMake 로드가 끝나면 run configuration에
quickstart_server·quickstart_client가 생긴다.quickstart_server를 실행한 뒤quickstart_client를 실행하고 터미널에서 확인한다.
종료는 IDE의 Stop 버튼으로 한다.
8. 첫 실행이 안 될 때 확인할 항목¶
| 증상 | 확인할 항목 |
|---|---|
find_package가 실패한다 |
cmake -P bootstrap.cmake가 bootstrap complete로 끝났는지, .zlink/install/lib/cmake/zlink_framework/가 있는지 확인한다. 직접 설치했다면 CMAKE_PREFIX_PATH가 framework install prefix를 가리키는지 확인한다 |
| server가 location store를 요구한다 | set_object_role을 none으로 지정했는지 확인한다 |
| startup이 실패한다 | 두 process의 mesh 이름이 같은지, routing_id를 지정했는지 확인한다 |
| 메시지가 직렬화되지 않는다 | 메시지 타입에 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE를 붙였는지 확인한다 |
| 호출이 대상 없음으로 끝난다 | 받는 쪽이 그 channel 이름을 server 역할로 등록했는지, 두 process가 peer로 연결됐는지 확인한다 |
9. 옮겨 갈 것¶
| 파일 | 내용 |
|---|---|
CMakeLists.txt |
find_package(zlink_framework CONFIG REQUIRED)와 zlink::framework 링크 |
Shared/messages.hpp |
packet_name과 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE |
Server/main.cpp |
add_route_mesh → listen → set_object_role(none)·set_routing_id·set_advertise_host → handler 등록 |
Client/main.cpp |
client 역할 지정, peer_connections().connect(...), request_to_channel(...) |
10. 다음으로 읽을 것¶
이 두 process는 endpoint를 서로 직접 적어 연결한다. 서버를 늘리거나 다른 주소로 다시 시작해도 호출 코드를 그대로 두려면 자동 연결이 필요하고, 그것은 Location이 다룬다.
- 개념을 먼저 확인할 때 — 핵심 개념
- 이름으로 호출하는 경로 — Channel 메시징
- id로 호출하는 상태 객체 — Spot · Actor
- 완결된 업무 흐름을 볼 때 — 샘플 고르기