C++ CI/CD 파이프라인: GitHub Actions를 이용한 멀티 OS 자동 빌드·테스트 가이드

C++ CI/CD 파이프라인: GitHub Actions를 이용한 멀티 OS 자동 빌드·테스트 가이드

이 글의 핵심

푸시·PR마다 Windows/macOS/Linux에서 자동으로 빌드하고 테스트하는 GitHub Actions 워크플로 설계를 다룹니다. vcpkg·Conan 연동, 캐시 최적화, 자주 발생하는 에러와 프로덕션 패턴까지 C++ 실전 가이드 시리즈에서 예제와 함께 다룹니다.

들어가며: 푸시하면 빌드·테스트까지

”내 PC에서는 되는데요”를 줄이기

40-1에서 vcpkg/Conan으로 의존성을 관리했다면, CI/CD(Continuous Integration/Continuous Deployment—코드 통합·배포를 자동화하는 방식. 푸시나 PR마다 빌드·테스트가 돌게 함)는 그 환경을 푸시·PR(Pull Request—변경 사항을 병합 요청하는 단위)마다 자동으로 돌려서 “다른 OS·다른 컴파일러”에서의 빌드·테스트를 보장합니다.
GitHub Actions는 워크플로를 YAML로 정의하고, 리포지토리 이벤트(push, pull_request 등)에 맞춰 Ubuntu, Windows, macOS 등 여러 러너에서 job을 실행할 수 있습니다. C++ 프로젝트는 CMake + 컴파일러 조합을 job별로 정의하고, 캐시(vcpkg, Conan, 빌드 트리)를 넣어 빌드 시간을 줄이는 것이 핵심입니다.

이 글에서 다루는 것:

  • 문제 시나리오: 로컬만 되고 CI에서 실패하는 상황
  • 워크플로 구조: trigger, job, matrix, cache
  • 멀티 OS·멀티 컴파일러 매트릭스
  • 완전한 CI/CD 예제: vcpkg·Conan 연동
  • 자주 발생하는 에러와 해결법
  • 최적화 팁프로덕션 패턴

개념을 잡는 비유

빌드·검사·배포 파이프라인은 공장 검수 라인과 비슷합니다. 같은 입력이면 같은 산출물이 나오게 고정하고, Sanitizer·정적 분석은 출하 전 불량 검사 역할을 합니다.

언어별로는 C++ Google Test·ctest·코드 커버리지와 묶고, Node·Python·Go 쪽은 Node.js GitHub Actions·Python pytest·매트릭스·Go go test와 같은 테스트→빌드 순서를 비교해 보세요. 배포 단계는 Node.js 배포 가이드와 연결됩니다.


목차

  1. 문제 시나리오: 로컬에서는 되는데 CI에서만 실패
  2. GitHub Actions 기본
  3. 멀티 OS·컴파일러 매트릭스
  4. 캐시와 테스트
  5. 완전한 CI/CD 예제 (vcpkg·Conan)
  6. 자주 발생하는 에러와 해결법
  7. 최적화 팁
  8. 프로덕션 패턴
  9. 정리

1. 문제 시나리오: 로컬에서는 되는데 CI에서만 실패

실제 겪는 상황

"로컬 Windows에서는 빌드되는데, GitHub Actions Ubuntu에서만 실패해요."
"팀원 A의 Mac에서는 되는데, 내 Linux에서는 링크 에러가 나요."
"vcpkg로 설치한 라이브러리가 CI에서는 찾을 수 없다고 해요."
"PR 올릴 때마다 20분씩 기다려야 빌드가 끝나요."
"캐시를 비우니까 갑자기 모든 job이 실패했어요."

원인 분석

  1. 환경 불일치: 로컬에만 설치된 라이브러리·툴체인에 의존
  2. 플랫폼별 차이: Windows/Linux/macOS에서 경로·컴파일러·ABI가 다름
  3. 의존성 미선언: vcpkg Classic 모드로 전역 설치한 패키지는 CI에 없음
  4. 캐시 의존: 캐시 키가 잘못되어 오래된/손상된 캐시 사용
  5. 타임아웃·리소스 부족: 무거운 빌드가 GitHub 무료 러너 제한에 걸림

CI/CD가 해결하는 것

flowchart LR
  subgraph before["CI 없음 (Before)"]
    B1["개발자 Abr/Windows"] --> B2[로컬만 빌드]
    B3["개발자 Bbr/macOS"] --> B4[로컬만 빌드]
    B5[병합] --> B6["다른 OS에서br/런타임 크래시"]
  end
  subgraph after["CI/CD (After)"]
    A1[push/PR] --> A2[GitHub Actions]
    A2 --> A3[Ubuntu 빌드]
    A2 --> A4[Windows 빌드]
    A2 --> A5[macOS 빌드]
    A3 --> A6["모든 OS 검증 후br/안전한 병합"]
    A4 --> A6
    A5 --> A6
  end

Manifest 모드(vcpkg.json) 또는 conanfile로 의존성을 코드에 포함하고, 동일한 워크플로로 모든 OS에서 빌드하면 “로컬만 되는데” 문제가 사라집니다.

빠른 시작: 5분 만에 CI 올리기

  1. 프로젝트 루트에 .github/workflows/build.yml 생성 — 위 “최소 동작 예제”의 YAML 복사
  2. CMakeLists.txtcmake_minimum_required, project, add_executable 포함
  3. main.cpp#include <iostream>main() 함수
  4. git push — main 브랜치에 푸시하면 자동으로 빌드·테스트 실행

의존성(vcpkg/Conan)이 있다면 해당 섹션의 예제로 교체하면 됩니다.


2. GitHub Actions 기본

워크플로 파일 위치와 트리거

  • .github/workflows/*.yml에 워크플로를 두면, 지정한 이벤트에 따라 자동 실행됩니다. 예: push (특정 브랜치), pull_request.
  • job: 하나의 러너에서 실행되는 단위. runs-on: ubuntu-latest 등으로 OS를 지정합니다.
  • steps: job 안의 단계. uses로 액션 재사용, run으로 셸 명령 실행. working-directory로 프로젝트 루트를 지정할 수 있습니다.

워크플로 실행 흐름

sequenceDiagram
  participant Dev as 개발자
  participant GH as GitHub
  participant Runner as Actions Runner

  Dev->>GH: push / pull_request
  GH->>Runner: 워크플로 트리거
  Runner->>Runner: checkout 소스
  Runner->>Runner: CMake 설정
  Runner->>Runner: 빌드
  Runner->>Runner: 테스트
  Runner->>GH: 결과 보고
  GH->>Dev: ✅/❌ 상태 표시

on에서 push·pull_requestmain 브랜치에서만 워크플로가 돌고, actions/checkout@v4로 소스를 받은 뒤 cmake -B build로 설정, cmake —build build로 빌드, ctest로 테스트를 실행합니다. 한 job이 이 순서대로 실행되며, 어느 step이든 실패하면 job이 실패로 끝납니다.

최소 동작 예제

실행 가능 예제 (워크플로가 빌드·테스트하는 최소 main):

프로젝트 구조:

my-cpp-app/
├── .github/
│   └── workflows/
│       └── build.yml
├── CMakeLists.txt
└── src/
    └── main.cpp
# CMakeLists.txt - 최소 구성
cmake_minimum_required(VERSION 3.20)
project(my-cpp-app LANGUAGES CXX)
add_executable(my-app src/main.cpp)
target_compile_features(my-app PRIVATE cxx_std_17)
enable_testing()
add_test(NAME my-app-test COMMAND my-app)
// src/main.cpp - 복사해 붙여넣은 뒤: 로컬에서 g++ -std=c++17 -o main main.cpp && ./main 또는 CI에서 cmake --build build
#include <iostream>
int main() {
    std::cout << "CI에서 빌드·테스트됩니다.\n";
    return 0;
}
# .github/workflows/build.yml
name: Build and Test
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Configure
        run: cmake -B build -DCMAKE_BUILD_TYPE=Release
      - name: Build
        run: cmake --build build
      - name: Test
        run: ctest --test-dir build --output-on-failure

3. 멀티 OS·컴파일러 매트릭스

strategy.matrix로 조합 늘리기

  • strategy.matrixos, compiler 등을 리스트로 두면, 조합만큼 job이 생성됩니다. 예: ubuntu + gcc, ubuntu + clang, windows + msvc, macos + apple-clang.
  • 각 job에서 matrix.osruns-on을 정하고, matrix.compiler에 따라 CC/CXX 또는 툴체인을 설정합니다.
  • fail-fast: false로 두면 한 조합이 실패해도 다른 조합은 끝까지 실행됩니다.

매트릭스 조합 시각화

flowchart TB
  subgraph matrix["strategy.matrix"]
    M1[ubuntu + gcc]
    M2[ubuntu + clang]
    M3[windows + msvc]
    M4[macos + apple-clang]
  end
  Push[push/PR] --> matrix
  M1 --> J1[Job 1]
  M2 --> J2[Job 2]
  M3 --> J3[Job 3]
  M4 --> J4[Job 4]
  J1 --> R[모두 통과 시 병합 가능]
  J2 --> R
  J3 --> R
  J4 --> R

matrix.include로 (os, compiler, cc, cxx) 조합

matrix.include로 (os, compiler, cc, cxx) 조합을 나열하면 그 수만큼 병렬 job이 생깁니다. runs-on: ${{ matrix.os }}로 해당 OS 러너에서 돌고, Configure 단계에서 matrix.cc·matrix.cxx가 있으면 CMAKE_C_COMPILER·CMAKE_CXX_COMPILER를 넘겨 그 컴파일러로 빌드합니다.

# .github/workflows/matrix-build.yml
jobs:
  build:
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            compiler: gcc
            cc: gcc
            cxx: g++
          - os: ubuntu-latest
            compiler: clang
            cc: clang
            cxx: clang++
          - os: windows-latest
            compiler: msvc
            cc: ''
            cxx: ''
          - os: macos-latest
            compiler: apple-clang
            cc: clang
            cxx: clang++
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - name: Configure
        run: |
          if [ -n "${{ matrix.cc }}" ]; then
            cmake -B build -DCMAKE_BUILD_TYPE=Release \
              -DCMAKE_C_COMPILER=${{ matrix.cc }} \
              -DCMAKE_CXX_COMPILER=${{ matrix.cxx }}
          else
            cmake -B build -DCMAKE_BUILD_TYPE=Release
          fi
        shell: bash
      - name: Build
        run: cmake --build build

주의: Windows에서는 shell: bash가 Git Bash를 사용하므로 if [ -n "" ]가 false가 되어 cmake -B build -DCMAKE_BUILD_TYPE=Release만 실행됩니다. Windows MSVC는 기본 컴파일러이므로 별도 지정이 필요 없습니다.

Windows 전용: MSVC 설정

Windows에서 Visual Studio 개발자 명령 프롬프트 환경이 필요할 때는 msvc 설정 액션을 사용합니다.

# Windows job에서 MSVC 환경 설정
- uses: microsoft/setup-msbuild@v2
  # 또는
- uses: ilammy/msvc-dev-cmd@v1
  with:
    arch: x64

4. 캐시와 테스트

vcpkg·Conan·빌드 캐시

  • actions/cache로 vcpkg 빌드 결과, Conan 캐시, build 디렉터리(선택)를 캐시하면 두 번째 빌드부터 시간이 크게 줄어듭니다.
  • keyrunner.os, hashFiles(‘vcpkg.json’) 등을 넣어 환경·의존성 변경 시에만 캐시가 갱신되게 합니다.

vcpkg 캐시 예제

- name: Cache vcpkg
  uses: actions/cache@v4
  with:
    path: |
      ${{ github.workspace }}/vcpkg/buildtrees
      ${{ github.workspace }}/vcpkg/packages
      ${{ github.workspace }}/vcpkg/downloads
    key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json', 'vcpkg.lock') }}
    restore-keys: |
      vcpkg-${{ runner.os }}-

Conan 캐시 예제

- name: Cache Conan
  uses: actions/cache@v4
  with:
    path: ~/.conan2
    key: conan-${{ runner.os }}-${{ hashFiles('conanfile.txt', 'conanfile.lock') }}
    restore-keys: |
      conan-${{ runner.os }}-

테스트 실행

  • 테스트: ctest 또는 ./test_runner를 step으로 실행합니다.
  • CTEST_OUTPUT_ON_FAILURE=1로 실패 시 로그를 남기고, 필요하면 테스트 결과 업로드 액션으로 JUnit 등으로 수집할 수 있습니다.
- name: Test
  run: ctest --test-dir build --output-on-failure
  env:
    CTEST_OUTPUT_ON_FAILURE: 1

실패 시 알림

  • if: failure()로 이메일·Slack 알림 step을 붙이거나, 브랜치 보호 규칙으로 main 병합 전 CI 통과를 요구하면 품질이 유지됩니다.
- name: Notify on failure
  if: failure()
  run: |
    echo "Build failed! Check the workflow run."
    # curl로 Slack webhook 호출 등

5. 완전한 CI/CD 예제 (vcpkg·Conan)

예제 1: vcpkg Manifest 모드 + 멀티 OS

프로젝트에 vcpkg를 서브모듈로 두고, vcpkg.json으로 의존성을 선언한 경우입니다.

my-app/
├── .github/
│   └── workflows/
│       └── ci.yml
├── CMakeLists.txt
├── vcpkg.json
├── vcpkg/          # git submodule
└── src/
    └── main.cpp
# .github/workflows/ci.yml
name: CI (vcpkg)
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            triplet: x64-linux
          - os: windows-latest
            triplet: x64-windows
          - os: macos-latest
            triplet: x64-osx
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Cache vcpkg
        uses: actions/cache@v4
        with:
          path: ${{ github.workspace }}/vcpkg/buildtrees
          key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json', 'vcpkg.lock') }}
          restore-keys: vcpkg-${{ runner.os }}-

      - name: Configure
        run: |
          cmake -B build -S . \
            -DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake" \
            -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} \
            -DCMAKE_BUILD_TYPE=Release

      - name: Build
        run: cmake --build build

      - name: Test
        run: ctest --test-dir build --output-on-failure

예제 2: Conan 2.x + 멀티 OS

my-app/
├── .github/
│   └── workflows/
│       └── ci.yml
├── CMakeLists.txt
├── conanfile.txt
└── src/
    └── main.cpp
# .github/workflows/ci.yml
name: CI (Conan)
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            profile: default
          - os: windows-latest
            profile: default
          - os: macos-latest
            profile: default
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install Conan
        run: pip install conan

      - name: Cache Conan
        uses: actions/cache@v4
        with:
          path: ~/.conan2
          key: conan-${{ runner.os }}-${{ hashFiles('conanfile.txt', 'conanfile.lock') }}
          restore-keys: conan-${{ runner.os }}-

      - name: Configure
        run: |
          conan install . --output-folder=build --build=missing
          cmake -B build -S . \
            -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/build/conan_toolchain.cmake \
            -DCMAKE_BUILD_TYPE=Release

      - name: Build
        run: cmake --build build

      - name: Test
        run: ctest --test-dir build --output-on-failure

예제 3: 매트릭스 + vcpkg + 캐시 (통합)

# .github/workflows/full-ci.yml
name: Full CI
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            compiler: gcc
            cc: gcc
            cxx: g++
            triplet: x64-linux
          - os: ubuntu-latest
            compiler: clang
            cc: clang
            cxx: clang++
            triplet: x64-linux
          - os: windows-latest
            compiler: msvc
            cc: ''
            cxx: ''
            triplet: x64-windows
          - os: macos-latest
            compiler: apple-clang
            cc: clang
            cxx: clang++
            triplet: x64-osx
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Cache vcpkg
        uses: actions/cache@v4
        with:
          path: |
            ${{ github.workspace }}/vcpkg/buildtrees
            ${{ github.workspace }}/vcpkg/packages
            ${{ github.workspace }}/vcpkg/downloads
          key: vcpkg-${{ runner.os }}-${{ matrix.compiler }}-${{ hashFiles('vcpkg.json', 'vcpkg.lock') }}
          restore-keys: |
            vcpkg-${{ runner.os }}-${{ matrix.compiler }}-

      - name: Configure
        run: |
          EXTRA_ARGS=""
          if [ -n "${{ matrix.cc }}" ]; then
            EXTRA_ARGS="-DCMAKE_C_COMPILER=${{ matrix.cc }} -DCMAKE_CXX_COMPILER=${{ matrix.cxx }}"
          fi
          cmake -B build -S . \
            -DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake" \
            -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} \
            -DCMAKE_BUILD_TYPE=Release \
            $EXTRA_ARGS
        shell: bash

      - name: Build
        run: cmake --build build --config Release

      - name: Test
        run: ctest --test-dir build --output-on-failure -C Release

6. 자주 발생하는 에러와 해결법

에러 1: “Could not find a package configuration file”

증상:

CMake Error: Could not find a package configuration file provided by "spdlog"

원인: vcpkg/Conan 툴체인을 CMake에 전달하지 않음. 또는 Manifest 모드가 아닌 Classic 모드로 로컬에만 설치한 상태.

해결법:

# vcpkg: CMAKE_TOOLCHAIN_FILE 필수
- name: Configure
  run: cmake -B build -S . \
    -DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake"
# Conan: conan install 후 툴체인 지정
- name: Configure
  run: |
    conan install . --output-folder=build --build=missing
    cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/build/conan_toolchain.cmake

에러 2: “vcpkg submodule not found” / “vcpkg.lock not found”

증상:

CMake Error: Could not find vcpkg root

원인: vcpkg를 서브모듈로 쓸 때 checkout에서 submodules: recursive를 빼먹음.

해결법:

- uses: actions/checkout@v4
  with:
    submodules: recursive

vcpkg.lock이 없다면 vcpkg.json만 hashFiles에 넣거나, vcpkg가 lock 파일을 생성하도록 한 번 빌드한 뒤 커밋합니다.

에러 3: Windows에서 “cmake: command not found” 또는 “cl: command not found”

증상:

'cmake' is not recognized as an internal or external command

원인: Windows 러너에 CMake가 PATH에 없거나, MSVC 환경이 활성화되지 않음.

해결법:

# CMake 설치 (Windows)
- uses: microsoft/setup-msbuild@v2
- name: Install CMake
  uses: jwlawson/actions-setup-cmake@v2
  with:
    cmake-version: '3.28.0'

또는 windows-latest 러너에는 CMake가 기본 포함되어 있으므로, Visual Studio 컴포넌트가 있는 이미지를 사용하는지 확인합니다. windows-latest는 보통 포함합니다.

에러 4: “Cache key too long” / “Cache size exceeded”

증상:

Warning: Cache size of 10GB exceeded. Caches are limited to 10GB per repository.

원인: vcpkg/Conan 캐시가 너무 커짐. 또는 key에 해시를 과도하게 넣어 key가 너무 김.

해결법:

  • 캐시할 경로를 줄입니다. buildtrees만 캐시하고 packages는 제외하는 등.
  • restore-keys로 fallback을 두어 부분 히트라도 활용합니다.
  • 10GB 제한을 고려해 불필요한 아티팩트는 캐시에서 제외합니다.
# 최소 경로만 캐시
path: ${{ github.workspace }}/vcpkg/buildtrees
# packages는 빌드 시 재생성 가능

에러 5: “Job timed out” / “The job running on runner has exceeded the maximum execution time”

증상:

The job running on runner GitHub Actions has exceeded the maximum execution time of 360 minutes.

원인: 무료 GitHub Actions는 job당 6시간 제한. vcpkg에서 많은 패키지를 처음 빌드할 때 시간이 오래 걸림.

해결법:

  • 캐시를 적극 활용해 두 번째 빌드부터 시간을 줄입니다.
  • 매트릭스 job을 나눠 병렬로 돌리되, 각 job의 빌드량을 줄입니다.
  • vcpkg.json에서 불필요한 의존성을 제거합니다.
  • self-hosted runner를 사용하면 제한이 완화됩니다.

에러 6: macOS에서 “xcrun: error: invalid active developer path”

증상:

xcrun: error: invalid active developer path

원인: Xcode 명령줄 도구가 설치되지 않음. 일부 macOS 러너 이미지에서 발생할 수 있음.

해결법:

- name: Install Xcode CLI
  run: xcode-select --install
  # 또는
- name: Setup Xcode
  uses: maxim-lobanov/setup-xcode@v1
  with:
    xcode-version: latest-stable

일반적으로 macos-latest에서는 이미 설치되어 있으나, 특정 버전 이미지에서는 필요할 수 있습니다.

에러 7: “conan: command not found”

증상:

conan: command not found

원인: Conan이 설치되지 않음. Python/pip 환경이 없거나 Conan이 PATH에 없음.

해결법:

- uses: actions/setup-python@v5
  with:
    python-version: '3.11'
- run: pip install conan
- run: conan --version

에러 8: 매트릭스에서 “matrix.cc”가 빈 문자열인데 컴파일러 옵션이 잘못 전달됨

증상:

CMake Error: CMAKE_C_COMPILER not set

원인: Windows 등에서 matrix.cc가 비어 있는데, -DCMAKE_C_COMPILER=처럼 빈 값이 전달되어 CMake가 기본 컴파일러를 찾지 못함.

해결법:

# 조건부로만 컴파일러 옵션 전달
- name: Configure
  run: |
    if [ -n "${{ matrix.cc }}" ]; then
      cmake -B build -DCMAKE_C_COMPILER=${{ matrix.cc }} -DCMAKE_CXX_COMPILER=${{ matrix.cxx }}
    else
      cmake -B build
    fi
  shell: bash

7. 최적화 팁

1. 캐시 키 전략

  • key: ${{ runner.os }}-${{ hashFiles('vcpkg.json', 'vcpkg.lock') }} — OS와 의존성 해시로 정확한 캐시 히트
  • restore-keys: ${{ runner.os }}- — 의존성 변경 시 이전 캐시로 fallback하여 부분 히트 활용

2. 병렬 job 활용

  • strategy.matrix로 OS×컴파일러 조합을 늘리면 job이 병렬 실행됩니다.
  • max-parallel을 설정해 동시 실행 수를 제한할 수 있습니다 (무료 플랜에서 유용).
strategy:
  max-parallel: 4
  matrix:
    include: [...]

3. 불필요한 job 건너뛰기

  • 문서만 수정했을 때 빌드를 건너뛰려면 paths-filter 액션을 사용합니다.
- uses: dorny/paths-filter@v3
  id: filter
  with:
    filters: |
      src:
        - 'src/**'
      cmake:
        - 'CMakeLists.txt'
        - 'vcpkg.json'
- name: Build
  if: steps.filter.outputs.src == 'true' || steps.filter.outputs.cmake == 'true'
  run: cmake --build build

4. 빌드 디렉터리 캐시 (선택)

  • CMake 빌드 결과를 캐시하면 증분 빌드가 가능합니다. 다만 캐시 크기가 커지므로, vcpkg/Conan 캐시만으로도 충분한 경우가 많습니다.
- name: Cache build
  uses: actions/cache@v4
  with:
    path: build
    key: build-${{ runner.os }}-${{ hashFiles('/CMakeLists.txt', '/vcpkg.json') }}

5. ccache로 컴파일 가속 (Linux/macOS)

- name: Setup ccache
  uses: hendrikmuhs/ccache-action@v2
  with:
    key: ccache-${{ runner.os }}-${{ hashFiles('**/*.cpp', '**/*.h') }}
- name: Configure
  run: cmake -B build -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
  run: cmake --build build

6. 의존성 최소화

  • vcpkg.json에서 사용하지 않는 패키지를 제거하면 빌드 시간이 줄어듭니다.
  • 헤더 전용 라이브러리는 빌드 없이 사용 가능하므로, 가능하면 헤더 전용을 선택합니다.

7. 트리거 세밀하게 제어하기

특정 경로·브랜치에서만 워크플로를 실행할 수 있습니다.

on:
  push:
    branches: [main, develop]
    paths:
      - 'src/**'
      - 'CMakeLists.txt'
      - 'vcpkg.json'
      - '.github/workflows/**'
  pull_request:
    branches: [main]
    paths-ignore:
      - 'docs/**'
      - '*.md'

paths를 지정하면 해당 파일이 변경된 push/PR에서만 실행됩니다. 문서만 수정하면 빌드를 건너뛸 수 있어 분을 절약합니다.

8. 빌드 시간 비교 (캐시 유무)

상황vcpkg (fmt+spdlog)Conan (fmt+spdlog)
캐시 없음 (첫 빌드)약 5–8분약 3–5분
캐시 히트약 1–2분약 1–2분

8. 프로덕션 패턴

패턴 1: 브랜치 보호 규칙

  • main 병합 전에 CI가 통과해야 하도록 설정합니다.
  • GitHub: Settings → Branches → Add rule → Require status checks to pass

패턴 2: PR 체크리스트

## PR 체크리스트
- [ ] CI가 모든 플랫폼에서 통과함
- [ ] vcpkg.json / conanfile.txt 변경 시 lock 파일 업데이트
- [ ] 새 의존성 추가 시 팀에 공유

패턴 3: 단계별 워크플로 (build → test → deploy)

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      build-result: ${{ steps.build.outcome }}
    steps:
      - uses: actions/checkout@v4
      - name: Build
        id: build
        run: cmake --build build

  test:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Test
        run: ctest --test-dir build

  deploy:
    needs: test
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - name: Deploy
        run: echo "Deploy to production"

패턴 4: 시크릿과 환경 변수

  • API 키·배포 자격 증명은 Secrets에 저장하고, 워크플로에서 ${{ secrets.DEPLOY_KEY }}로 참조합니다.
  • 환경별 변수는 Environments를 사용해 분리합니다.

패턴 5: Self-hosted Runner (대규모 프로젝트)

  • 무료 플랜의 시간 제한·동시 job 제한을 피하려면 자체 호스팅 러너를 사용합니다.
  • 사내 서버에 runner를 설치하고 runs-on: self-hosted로 지정합니다.
jobs:
  build:
    runs-on: [self-hosted, linux, x64]

패턴 6: 아티팩트 업로드 (빌드 결과물 보관)

- name: Upload artifacts
  uses: actions/upload-artifact@v4
  with:
    name: build-${{ matrix.os }}-${{ matrix.compiler }}
    path: build/

패턴 7: 린트·포맷 검사 (선택)

코드 스타일을 CI에서 검증하려면 clang-format, clang-tidy를 추가합니다.

- name: Check formatting
  run: |
    find src -name "*.cpp" -o -name "*.h" | xargs clang-format --dry-run -Werror
- name: Run clang-tidy
  run: |
    cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
    run-clang-tidy -p build

패턴 8: 커버리지 리포트 (선택)

테스트 커버리지를 수집해 Codecov 등에 업로드할 수 있습니다.

- name: Build with coverage
  run: |
    cmake -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="--coverage"
    cmake --build build
- name: Run tests
  run: ctest --test-dir build
- name: Upload coverage
  uses: codecov/codecov-action@v4
  with:
    files: ./build/coverage.info

구현 체크리스트

CI/CD 구축 시 확인할 항목:

  • .github/workflows/*.yml 파일 생성
  • actions/checkoutsubmodules: recursive (vcpkg 서브모듈 시)
  • vcpkg: CMAKE_TOOLCHAIN_FILE 지정
  • Conan: conan installCMAKE_TOOLCHAIN_FILE 지정
  • actions/cache로 vcpkg/Conan 캐시 설정
  • strategy.matrix로 멀티 OS·컴파일러 검증
  • fail-fast: false로 일부 실패 시에도 나머지 job 실행
  • ctest 또는 테스트 실행 step 추가
  • 브랜치 보호 규칙으로 main 병합 전 CI 통과 요구
  • paths-filter로 불필요한 빌드 건너뛰기 (선택)

같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

  • C++ 패키지 관리 실무: vcpkg와 Conan으로 외부 라이브러리 의존성 지옥 탈출 [#40-1]
  • CMake “Could NOT find” 에러
  • C++ 패키지 매니저 | vcpkg·Conan으로 “라이브러리 설치 지옥” 탈출하기

이 글에서 다루는 키워드 (관련 검색어)

CI/CD, GitHub Actions, C++ 빌드, vcpkg CI, Conan CI, 멀티 OS 빌드 등으로 검색하시면 이 글이 도움이 됩니다.


9. 정리

항목요약
트리거push / pull_request 등에서 워크플로 실행
매트릭스os × compiler 조합으로 멀티 OS·멀티 컴파일러 검증
캐시vcpkg/Conan/빌드 디렉터리로 재빌드 시간 단축
테스트ctest + 실패 시 로그·알림
에러 대응툴체인 지정, 서브모듈, 캐시 키, 타임아웃 등
프로덕션브랜치 보호, 단계별 job, 시크릿, 아티팩트

40-2로 “푸시하면 멀티 OS에서 빌드·테스트까지” 자동화하면, 40-1 패키지 관리와 함께 재현 가능·안정적인 빌드가 완성됩니다.

실전 체크리스트

실무에서 이 개념을 적용할 때 확인해야 할 사항입니다.

코드 작성 전

  • 이 기법이 현재 문제를 해결하는 최선의 방법인가?
  • 팀원들이 이 코드를 이해하고 유지보수할 수 있는가?
  • 성능 요구사항을 만족하는가?

코드 작성 중

  • 컴파일러 경고를 모두 해결했는가?
  • 엣지 케이스를 고려했는가?
  • 에러 처리가 적절한가?

코드 리뷰 시

  • 코드의 의도가 명확한가?
  • 테스트 케이스가 충분한가?
  • 문서화가 되어 있는가?

이 체크리스트를 활용하여 실수를 줄이고 코드 품질을 높이세요.


자주 묻는 질문 (FAQ)

Q. 이 내용을 실무에서 언제 쓰나요?

A. 푸시·PR마다 Windows/macOS/Linux에서 자동으로 빌드하고 테스트하는 GitHub Actions 워크플로 설계를 다룹니다. vcpkg·Conan과 연동한 완전한 예제, 자주 발생하는 에러와 해결법, 최적화 팁, 프로덕션 패턴까지 실무에서 바로 적용할 수 있습니다.

Q. vcpkg와 Conan 중 어떤 걸 CI에 쓰나요?

A. 40-1에서 선택한 패키지 매니저를 그대로 CI에 연동하면 됩니다. vcpkg는 Manifest 모드 + 서브모듈, Conan은 conanfile + pip install conan으로 CI를 구성합니다. 둘 다 멀티 OS·캐시 지원이 가능합니다.

Q. 무료 GitHub Actions 제한이 걱정돼요.

A. 퍼블릭 리포는 무제한, 프라이빗은 월 2,000분(무료 플랜)입니다. 캐시로 빌드 시간을 줄이고, paths-filter로 불필요한 빌드를 건너뛰며, max-parallel로 동시 job을 제한하면 분을 아낄 수 있습니다. 대규모 프로젝트는 self-hosted runner를 고려하세요.

Q. 선행으로 읽으면 좋은 글은?

A. 각 글 하단의 이전 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.

Q. 더 깊이 공부하려면?

A. GitHub Actions 문서, vcpkg CI 가이드, Conan CI 문서를 참고하세요.

한 줄 요약: GitHub Actions로 멀티 OS·멀티 컴파일러 자동 빌드·테스트를 구성할 수 있습니다. vcpkg·Conan 연동, 캐시 최적화, 자주 발생하는 에러와 프로덕션 패턴까지 실무에 바로 적용하세요.

이전 글: DevOps for C++ #40-1: vcpkg·Conan

다음 글: [DevOps for C++ #40-3] 컨테이너 기반 개발: Docker로 빌드 환경 표준화 및 배포 이미지 최적화


관련 글

  • C++ 패키지 관리 실무: vcpkg와 Conan으로 외부 라이브러리 의존성 지옥 탈출 [#40-1]
  • C++23 핵심 기능 완벽 가이드 | std::expected·mdspan
  • C++26 핵심 기능 완벽 가이드 | 리플렉션 ^^· std::execution
  • C++ 컨테이너 기반 개발: Docker로 빌드 환경 표준화 및 배포 이미지 최적화 [#40-3]
  • C++ CI/CD GitHub Actions 완벽 가이드 | 워크플로·매트릭스·캐싱·아티팩트·배포