콘텐츠로 이동

C++ Quickstart — 설치부터 첫 요청까지

이 장을 읽고 나면

패키지를 설치하고, 두 process가 서로 호출하는 최소 project를 실행할 수 있다.

저장소의 framework/languages/cpp/quickstart/ 프로젝트다. 아래 코드 블록은 사이트를 빌드할 때 그 파일에서 읽는다. location store 없이 process 둘이 서로의 endpoint를 직접 지정해 request/reply 한 번을 주고받는다.

0. 예제 저장소 clone

이 장의 project는 zlink-cpp-examples 저장소의 quickstart/이며, 기능 가이드가 읽는 program인 tutorial/과 samples/도 같은 저장소에 있다.

git clone https://github.com/zlink-systems/zlink-cpp-examples.git
cd zlink-cpp-examples/quickstart

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을 사용한다.

cmake -S . -B build \
  -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake

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는 다음과 같다.

CMakeLists.txt
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는 직렬화되지 않는다.

Shared/messages.hpp
#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로 그 주소를 광고한다.

Server/main.cpp
#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에서 꺼낸다.

Client/main.cpp
#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 도구 구성 요소가 필요하다.

  1. 터미널에서 cmake -P bootstrap.cmake를 실행한다(§6).
  2. 파일 → 열기 → 폴더로 quickstart/를 연다. 솔루션 파일은 필요 없다.
  3. 구성 드롭다운에서 zlink bootstrap (Release) 를 고른다.
  4. 빌드 → 모두 빌드.
  5. 시작 항목에서 quickstart_server.exe를 골라 실행하고, 이어서 quickstart_client.exe를 실행한 뒤 터미널에서 확인한다.

    curl http://127.0.0.1:5083/hello/world
    

7.2 Rider · CLion

JetBrains IDE(Rider의 C++ 지원, CLion)도 폴더를 열면 CMake project로 인식하고 preset을 profile로 읽는다.

  1. 터미널에서 cmake -P bootstrap.cmake를 실행한다(§6). WSL에서 빌드하려면 WSL 안에서 실행한다.
  2. File → Open으로 quickstart/ 폴더를 연다.
  3. Settings → Build, Execution, Deployment → CMake에 preset zlink가 profile로 나열된다. 그것을 켠다. Toolchain은 Windows에서 Visual Studio, WSL에서 빌드하면 WSL toolchain을 고른다.
  4. CMake 로드가 끝나면 run configuration에 quickstart_server·quickstart_client가 생긴다. quickstart_server를 실행한 뒤 quickstart_client를 실행하고 터미널에서 확인한다.

    curl http://127.0.0.1:5083/hello/world
    

종료는 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이 다룬다.