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 배포 가이드와 연결됩니다.
목차
- 문제 시나리오: 로컬에서는 되는데 CI에서만 실패
- GitHub Actions 기본
- 멀티 OS·컴파일러 매트릭스
- 캐시와 테스트
- 완전한 CI/CD 예제 (vcpkg·Conan)
- 자주 발생하는 에러와 해결법
- 최적화 팁
- 프로덕션 패턴
- 정리
1. 문제 시나리오: 로컬에서는 되는데 CI에서만 실패
실제 겪는 상황
"로컬 Windows에서는 빌드되는데, GitHub Actions Ubuntu에서만 실패해요."
"팀원 A의 Mac에서는 되는데, 내 Linux에서는 링크 에러가 나요."
"vcpkg로 설치한 라이브러리가 CI에서는 찾을 수 없다고 해요."
"PR 올릴 때마다 20분씩 기다려야 빌드가 끝나요."
"캐시를 비우니까 갑자기 모든 job이 실패했어요."
원인 분석
- 환경 불일치: 로컬에만 설치된 라이브러리·툴체인에 의존
- 플랫폼별 차이: Windows/Linux/macOS에서 경로·컴파일러·ABI가 다름
- 의존성 미선언: vcpkg Classic 모드로 전역 설치한 패키지는 CI에 없음
- 캐시 의존: 캐시 키가 잘못되어 오래된/손상된 캐시 사용
- 타임아웃·리소스 부족: 무거운 빌드가 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 올리기
- 프로젝트 루트에
.github/workflows/build.yml생성 — 위 “최소 동작 예제”의 YAML 복사 - CMakeLists.txt —
cmake_minimum_required,project,add_executable포함 - main.cpp —
#include <iostream>와main()함수 - 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_request 시 main 브랜치에서만 워크플로가 돌고, 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.matrix에 os, compiler 등을 리스트로 두면, 조합만큼 job이 생성됩니다. 예: ubuntu + gcc, ubuntu + clang, windows + msvc, macos + apple-clang.
- 각 job에서 matrix.os로 runs-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 디렉터리(선택)를 캐시하면 두 번째 빌드부터 시간이 크게 줄어듭니다.
- key에 runner.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/checkout에submodules: recursive(vcpkg 서브모듈 시) - vcpkg:
CMAKE_TOOLCHAIN_FILE지정 - Conan:
conan install후CMAKE_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 완벽 가이드 | 워크플로·매트릭스·캐싱·아티팩트·배포