C++ CMake 완벽 가이드 | 크로스 플랫폼 빌드·최신 CMake 3.28+ 기능·프리셋·모듈
이 글의 핵심
C++ CMake 완벽 가이드에 대한 실전 가이드입니다. 크로스 플랫폼 빌드·최신 CMake 3.28+ 기능·프리셋·모듈 등을 예제와 함께 상세히 설명합니다.
들어가며: “플랫폼마다 빌드 설정이 달라서 관리가 힘들어요”
실무에서 겪는 빌드 문제들
C++ 프로젝트를 여러 플랫폼에서 개발하다 보면 이런 문제를 겪습니다:
- 플랫폼별 빌드 파일 — Windows는
.vcxproj, Linux는Makefile, macOS는 Xcode 프로젝트를 따로 관리 - 팀원마다 다른 환경 — 누구는 Visual Studio, 누구는 CLion, 누구는 VS Code
- 의존성 지옥 — Boost, OpenSSL 등 외부 라이브러리 경로가 환경마다 다름
- CI/CD 설정 중복 — GitHub Actions, GitLab CI에서 각각 빌드 스크립트 작성
- 빌드 시간 증가 — 프로젝트가 커질수록 빌드가 느려짐
CMake로 해결:
# ❌ 기존 방법 (플랫폼별 관리)
# Windows: msbuild MyProject.vcxproj
# Linux: make -f Makefile.linux
# macOS: xcodebuild -project MyProject.xcodeproj
# ✅ CMake 방법 (한 번 작성, 어디서나 빌드)
mkdir build && cd build
cmake ..
cmake --build .
flowchart LR
subgraph input["입력"]
cmake["CMakeLists.txt"]
preset["CMakePresets.json (3.19+)"]
end
subgraph cmake_tool["CMake"]
gen["Generator"]
end
subgraph output["출력"]
make["Makefile (Linux)"]
vs["Visual Studio (Windows)"]
xcode["Xcode (macOS)"]
ninja["Ninja (모든 플랫폼)"]
end
cmake --> gen
preset --> gen
gen --> make
gen --> vs
gen --> xcode
gen --> ninja
CMake의 핵심 개념
- 빌드 시스템 생성기: CMake 자체는 빌드를 하지 않고, 플랫폼별 빌드 시스템(Makefile, .sln 등)을 생성합니다.
- 타겟 기반:
add_executable,add_library로 빌드 타겟을 정의하고,target_link_libraries,target_include_directories로 의존성을 명시합니다. - 프리셋 (3.19+):
CMakePresets.json으로 빌드 설정을 공유하고 재사용합니다. - C++20 모듈 (3.28+): C++20 모듈을 네이티브로 지원합니다.
CMake는 C++에서 사실상 표준에 가까운 빌드 생성기입니다. Rust 입문 글의 Cargo나 Node.js의 npm·모듈 해석처럼 빌드와 패키지가 한 도구에 묶여 있지는 않지만, 크로스 플랫폼 빌드와 vcpkg·Conan 연동으로 비슷한 목표를 달성합니다. Go 모듈·go.sum의 의존성 고정, Python의 pip·uv·Poetry의 락·가상환경 개념과 나란히 보면 의존성 관리 차이가 선명해집니다. 언어별 빌드 철학·도구 스택 비교는 C++ 빌드 시스템 완전 비교를 참고하세요. 병렬 빌드·캐시(ccache 등)는 Makefile 가이드와도 이어집니다.
목표:
- CMake 기초 (최소 설정, 타겟, 의존성)
- 최신 CMake 3.28+ 기능 (프리셋, C++20 모듈, FetchContent 개선)
- 실전 패턴 (멀티 플랫폼, CI/CD, 의존성 관리)
- 성능 최적화 (병렬 빌드, ccache, PCH)
- 자주 하는 실수와 해결법
- 프로덕션 패턴
요구 환경: CMake 3.20 이상 (최신 기능은 3.28+ 권장)
목차
- 문제 시나리오: 빌드 관리의 어려움
- 최소 CMakeLists.txt
- 타겟: 실행 파일과 라이브러리
- CMake 프리셋 (3.19+)
- 외부 라이브러리 찾기: find_package
- C++20 모듈 지원 (3.28+)
- 빌드 타입과 컴파일 옵션
- 프로젝트 구조와 서브디렉토리
- 완전한 예제: 멀티 타겟 프로젝트
- 자주 발생하는 문제와 해결법
- 성능 최적화
- 모범 사례·베스트 프랙티스
- 프로덕션 패턴
- 정리 및 체크리스트
1. 문제 시나리오: 빌드 관리의 어려움
시나리오 1: “팀원마다 빌드 설정이 달라요”
상황: Windows 개발자는 Visual Studio, Linux 개발자는 Makefile, macOS 개발자는 Xcode 사용
문제: 각자 다른 빌드 파일을 관리하다 보니 설정이 동기화되지 않음
결과: "내 환경에서는 빌드되는데요?" 문제 발생
→ CMake + CMakePresets.json으로 통일된 빌드 설정 공유
주의사항: “내 PC에서는 된다”는 문제는 보통 생성기·플래그·경로가 커밋되지 않아서 생기므로, 재현 가능한 한 줄짜리 프리셋이 더 중요합니다.
시나리오 2: “외부 라이브러리 경로가 환경마다 달라요”
상황: Boost가 Windows는 C:\boost, Linux는 /usr/local/boost, macOS는 /opt/homebrew/boost
문제: 하드코딩된 경로로 인해 다른 환경에서 빌드 실패
결과: 팀원마다 CMakeLists.txt를 수정해야 함
→ find_package + CMAKE_PREFIX_PATH로 자동 탐지
시나리오 3: “빌드가 너무 느려요”
상황: 프로젝트가 커지면서 풀 빌드에 30분 소요
문제: 매번 모든 파일을 재컴파일, 헤더 파싱 반복
결과: 개발 생산성 저하
→ ccache, PCH, Ninja, 병렬 빌드로 최적화
시나리오 4: “CI/CD에서 빌드 설정이 복잡해요”
상황: GitHub Actions, GitLab CI, Jenkins에서 각각 다른 빌드 스크립트
문제: 빌드 설정 변경 시 모든 CI 파일 수정 필요
결과: 유지보수 부담 증가
→ CMakePresets.json으로 CI 설정 통일
flowchart TB
subgraph Problems["빌드 관리 문제"]
P1[플랫폼별 빌드 파일]
P2[의존성 경로 불일치]
P3[느린 빌드]
P4[CI/CD 설정 중복]
end
subgraph Solutions["CMake 해결책"]
S1[CMakeLists.txt 통일]
S2[find_package 자동 탐지]
S3[ccache + Ninja + PCH]
S4[CMakePresets.json]
end
P1 --> S1
P2 --> S2
P3 --> S3
P4 --> S4
2. 최소 CMakeLists.txt
Hello World 빌드
# CMakeLists.txt - CMake 설정 파일
# CMake 최소 버전 지정 (3.20 이상 필요)
cmake_minimum_required(VERSION 3.20)
# 프로젝트 정보 설정 (이름, 버전, 언어)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
# C++ 표준 설정
set(CMAKE_CXX_STANDARD 20) # C++20 사용
set(CMAKE_CXX_STANDARD_REQUIRED ON) # C++20 미지원 시 에러
set(CMAKE_CXX_EXTENSIONS OFF) # GNU 확장 등 비활성화 (이식성 향상)
# 실행 파일 타겟 생성
# myapp: 타겟 이름 (빌드 결과물 이름)
# main.cpp: 소스 파일
add_executable(myapp main.cpp)
// main.cpp
#include <iostream>
int main() {
std::cout << "Hello, CMake!" << std::endl;
return 0;
}
# 빌드 디렉토리 생성 (out-of-source build)
mkdir build
cd build
# CMake 실행 (빌드 시스템 생성)
cmake ..
# 빌드 (생성된 빌드 시스템으로 컴파일)
cmake --build .
# 실행
./myapp # Linux/macOS
# myapp.exe # Windows
핵심 명령어 설명
| 명령어 | 설명 |
|---|---|
cmake_minimum_required(VERSION 3.20) | 최소 CMake 버전 지정 |
project(MyProject VERSION 1.0.0) | 프로젝트 이름과 버전 설정 |
set(CMAKE_CXX_STANDARD 20) | C++20 표준 사용 |
set(CMAKE_CXX_EXTENSIONS OFF) | GNU 확장 등 비활성화 (이식성 향상) |
add_executable(myapp main.cpp) | 실행 파일 타겟 생성 |
cmake .. | 부모 디렉토리의 CMakeLists.txt로 빌드 시스템 생성 |
cmake --build . | 플랫폼 독립적 빌드 명령 |
CMake 버전별 주요 기능
| 버전 | 주요 기능 |
|---|---|
| 3.19 | CMakePresets.json 도입 |
| 3.20 | C++23 지원 |
| 3.23 | FILE_SET 도입 (헤더 관리 개선) |
| 3.25 | LINUX 변수 추가 |
| 3.26 | SYSTEM 속성 개선 |
| 3.28 | C++20 모듈 네이티브 지원 |
3. 타겟: 실행 파일과 라이브러리
실행 파일
# 단일 소스
add_executable(myapp main.cpp)
# 여러 소스
add_executable(myapp
src/main.cpp
src/utils.cpp
src/config.cpp
)
# 변수 사용
set(SOURCES
src/main.cpp
src/utils.cpp
)
add_executable(myapp ${SOURCES})
라이브러리
# 정적 라이브러리 (.a, .lib)
add_library(mylib STATIC
src/lib.cpp
src/helper.cpp
)
# 동적 라이브러리 (.so, .dll)
add_library(mylib SHARED
src/lib.cpp
src/helper.cpp
)
# 헤더 전용 라이브러리
add_library(mylib INTERFACE)
target_include_directories(mylib INTERFACE include)
# 실행 파일에 링크
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE mylib)
주의사항: SHARED는 런타임에 .so/.dll 경로가 잡혀야 하므로 설치·배포 규칙(RUNTIME DESTINATION 등)을 함께 설계하세요.
PUBLIC vs PRIVATE vs INTERFACE
flowchart TB
subgraph LibA["라이브러리 A"]
A_PRIVATE["PRIVATE 헤더br/src/internal/"]
A_PUBLIC["PUBLIC 헤더br/include/"]
end
subgraph LibB["라이브러리 B"]
B_CODE[B의 코드]
end
subgraph App["애플리케이션"]
APP_CODE[App 코드]
end
A_PRIVATE -.->|사용 가능| LibA
A_PUBLIC -->|사용 가능| LibA
A_PUBLIC -->|사용 가능| LibB
A_PUBLIC -->|사용 가능| App
LibB -->|링크| LibA
App -->|링크| LibB
style A_PRIVATE fill:#FFB6C1
style A_PUBLIC fill:#90EE90
# PRIVATE: 타겟 자신만 사용
target_include_directories(mylib PRIVATE src/internal)
# PUBLIC: 타겟 + 이 타겟을 링크하는 타겟도 사용
target_include_directories(mylib PUBLIC include)
# INTERFACE: 이 타겟을 링크하는 타겟만 사용 (헤더 전용 라이브러리)
target_include_directories(mylib INTERFACE include)
실무 예시: mylib가 내부적으로 src/internal/impl.h를 쓰면 PRIVATE로 추가하고, 외부에 노출하는 include/mylib/api.h는 PUBLIC으로 추가합니다. myapp이 mylib를 링크하면 PUBLIC 헤더만 접근할 수 있습니다.
4. CMake 프리셋 (3.19+)
프리셋이란?
문제: 팀원마다 다른 CMake 명령어를 사용합니다.
# 개발자 A
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=g++ ..
# 개발자 B
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=clang++ -DENABLE_TESTS=ON ..
# CI/CD
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=... ..
주의사항: 긴 cmake 한 줄을 문서에 복붙하는 방식은 깨지기 쉬우니, 아래 프리셋으로 옮기는 것이 목적입니다.
해결: CMakePresets.json으로 빌드 설정을 공유합니다.
CMakePresets.json 기본 구조
{
"version": 6,
"cmakeMinimumRequired": {
"major": 3,
"minor": 25,
"patch": 0
},
"configurePresets": [
{
"name": "default",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CMAKE_CXX_STANDARD": "20",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
},
{
"name": "debug",
"displayName": "Debug Build",
"description": "Debug build with sanitizers",
"inherits": "default",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_CXX_FLAGS": "-fsanitize=address,undefined"
}
},
{
"name": "release",
"displayName": "Release Build",
"description": "Optimized release build",
"inherits": "default",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "windows-msvc",
"displayName": "Windows MSVC",
"inherits": "default",
"generator": "Visual Studio 17 2022",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
}
],
"buildPresets": [
{
"name": "debug",
"configurePreset": "debug",
"configuration": "Debug"
},
{
"name": "release",
"configurePreset": "release",
"configuration": "Release",
"jobs": 8
}
],
"testPresets": [
{
"name": "default",
"configurePreset": "debug",
"output": {
"outputOnFailure": true
}
}
]
}
프리셋 사용법
# 사용 가능한 프리셋 확인
cmake --list-presets
# 프리셋으로 구성
cmake --preset=debug
# 프리셋으로 빌드
cmake --build --preset=debug
# 프리셋으로 테스트
ctest --preset=default
실전 프리셋 예제: 멀티 플랫폼
{
"version": 6,
"configurePresets": [
{
"name": "linux-gcc",
"displayName": "Linux GCC",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/linux-gcc",
"cacheVariables": {
"CMAKE_CXX_COMPILER": "g++",
"CMAKE_BUILD_TYPE": "Release"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
}
},
{
"name": "linux-clang",
"displayName": "Linux Clang",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/linux-clang",
"cacheVariables": {
"CMAKE_CXX_COMPILER": "clang++",
"CMAKE_BUILD_TYPE": "Release"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
}
},
{
"name": "macos",
"displayName": "macOS",
"generator": "Xcode",
"binaryDir": "${sourceDir}/build/macos",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Darwin"
}
},
{
"name": "windows",
"displayName": "Windows MSVC",
"generator": "Visual Studio 17 2022",
"binaryDir": "${sourceDir}/build/windows",
"architecture": "x64",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
}
]
}
주의사항: 조건 프리셋이 많아지면 이름 규칙을 팀 문서로 고정하지 않으면 “어떤 프리셋이 공식인지” 혼란이 생깁니다.
CI/CD에서 프리셋 활용
# .github/workflows/build.yml
name: Build
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
preset: [debug, release]
steps:
- uses: actions/checkout@v3
- name: Install CMake
uses: lukka/get-cmake@latest
- name: Configure
run: cmake --preset=${{ matrix.preset }}
- name: Build
run: cmake --build --preset=${{ matrix.preset }}
- name: Test
run: ctest --preset=default
사용자별 프리셋 (CMakeUserPresets.json)
팀 공유 설정은 CMakePresets.json, 개인 설정은 CMakeUserPresets.json에 작성합니다.
// CMakeUserPresets.json (git ignore에 추가)
{
"version": 6,
"configurePresets": [
{
"name": "my-dev",
"inherits": "debug",
"cacheVariables": {
"CMAKE_PREFIX_PATH": "/home/myuser/local",
"ENABLE_EXPERIMENTAL_FEATURES": "ON"
}
}
]
}
주의사항: CMakeUserPresets.json은 .gitignore에 넣되, 팀원 온보딩용 예시 파일(CMakeUserPresets.json.example)은 두는 편이 좋습니다.
5. 외부 라이브러리 찾기: find_package
Boost 예제
find_package(Boost 1.70 REQUIRED COMPONENTS filesystem system)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE Boost::filesystem Boost::system)
OpenSSL 예제
find_package(OpenSSL REQUIRED)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE OpenSSL::SSL OpenSSL::Crypto)
주의사항: 시스템 OpenSSL과 번들 OpenSSL이 섞이면 런타임 크래시가 날 수 있으므로, 배포 시 동일 빌드를 쓰는지 확인하세요.
찾기 실패 시 처리
find_package(SomeLib)
if(SomeLib_FOUND)
target_link_libraries(myapp PRIVATE SomeLib::SomeLib)
else()
message(WARNING "SomeLib not found, using fallback")
endif()
pkg-config 사용
find_package(PkgConfig REQUIRED)
pkg_check_modules(CURL REQUIRED libcurl)
target_link_libraries(myapp PRIVATE ${CURL_LIBRARIES})
target_include_directories(myapp PRIVATE ${CURL_INCLUDE_DIRS})
주의사항: 변수 기반(CURL_LIBRARIES)은 현대적인 IMPORTED 타겟보다 전파·도구 지원이 약하므로, 가능하면 공식 CMake 패키지나 PkgConfig::libcurl 래퍼를 쓰는 편이 낫습니다.
FetchContent로 의존성 자동 다운로드 (3.11+)
include(FetchContent)
# GoogleTest 자동 다운로드
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0
)
# 사용 가능하게 만들기
FetchContent_MakeAvailable(googletest)
# 링크
add_executable(tests test.cpp)
target_link_libraries(tests PRIVATE gtest_main)
관련 글: vcpkg나 Conan을 사용하면 더 체계적인 패키지 관리가 가능합니다.
3.24+ 개선사항: DOWNLOAD_EXTRACT_TIMESTAMP 옵션으로 타임스탬프 경고 제거
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 10.0.0
DOWNLOAD_EXTRACT_TIMESTAMP ON # 3.24+
)
주의사항: FetchContent는 네트워크 의존이므로 SBOM·보안 스캔 프로세스와 함께 쓸지 결정해야 합니다.
6. C++20 모듈 지원 (3.28+)
C++20 모듈이란?
기존 헤더 방식의 문제:
- 매번 헤더 파싱 (느린 컴파일)
- 매크로 오염
- 순환 의존성 문제
C++20 모듈 장점:
- 한 번만 컴파일 (빠른 빌드)
- 명시적 export (깨끗한 인터페이스)
- 순환 의존성 방지
CMake 3.28+에서 모듈 사용
cmake_minimum_required(VERSION 3.28)
project(ModuleExample CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 모듈 지원 활성화
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
# 라이브러리 (모듈 포함)
add_library(mylib)
target_sources(mylib
PUBLIC
FILE_SET CXX_MODULES FILES
src/mymodule.cppm # 모듈 인터페이스 파일
PRIVATE
src/impl.cpp
)
# 실행 파일
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE mylib)
모듈 파일 예제
// src/mymodule.cppm (모듈 인터페이스)
export module mymodule;
export namespace mylib {
int add(int a, int b);
int multiply(int a, int b);
}
// src/impl.cpp (모듈 구현)
module mymodule;
namespace mylib {
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
}
주의사항: 구현 파일에서 잘못된 module 선언은 링크·가시성 오류로 이어지므로, 인터페이스와 구현 쌍을 한 세트로 관리하세요.
// main.cpp (모듈 사용)
import mymodule;
#include <iostream>
int main() {
std::cout << mylib::add(2, 3) << std::endl; // 5
std::cout << mylib::multiply(4, 5) << std::endl; // 20
return 0;
}
모듈 + 헤더 혼용
add_library(mylib)
target_sources(mylib
PUBLIC
FILE_SET HEADERS FILES
include/legacy.h # 기존 헤더
FILE_SET CXX_MODULES FILES
src/newmodule.cppm # 새 모듈
PRIVATE
src/legacy.cpp
src/newmodule.cpp
)
target_include_directories(mylib PUBLIC include)
컴파일러별 모듈 지원 상황 (2026년 기준)
| 컴파일러 | 버전 | 모듈 지원 |
|---|---|---|
| GCC | 14+ | 완전 지원 |
| Clang | 17+ | 완전 지원 |
| MSVC | 19.36+ (VS 2022) | 완전 지원 |
7. 빌드 타입과 컴파일 옵션
빌드 타입
# 기본값 설정
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
# 빌드 타입별 플래그
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -DDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG")
주의사항: 멀티 설정 생성기(Visual Studio, Xcode)는 CMAKE_BUILD_TYPE 대신 --config로 선택하므로, 이 패턴과 혼동하지 마세요.
# Debug 빌드
cmake -DCMAKE_BUILD_TYPE=Debug ..
# Release 빌드
cmake -DCMAKE_BUILD_TYPE=Release ..
# 디버그 정보 포함 최적화
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
타겟별 컴파일 옵션
add_executable(myapp main.cpp)
# 경고 활성화
target_compile_options(myapp PRIVATE
-Wall -Wextra -Wpedantic
$<$<CONFIG:Debug>:-Werror> # Debug에서만 경고를 에러로
)
# 전처리 정의
target_compile_definitions(myapp PRIVATE
APP_VERSION="1.0"
$<$<CONFIG:Debug>:DEBUG_MODE>
)
# 헤더 경로
target_include_directories(myapp PRIVATE
${CMAKE_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}/generated
)
Generator Expression
# 빌드 타입별 분기
target_compile_options(myapp PRIVATE
$<$<CONFIG:Debug>:-O0 -g>
$<$<CONFIG:Release>:-O3>
)
# 컴파일러별 분기
target_compile_options(myapp PRIVATE
$<$<CXX_COMPILER_ID:GNU>:-fno-exceptions>
$<$<CXX_COMPILER_ID:MSVC>:/EHsc>
)
주의사항: 제너레이터 표현식은 문자열이라 디버깅이 어렵고, 복잡해지면 target_compile_options를 여러 줄로 나누거나 커스텀 속성으로 감싸 가독성을 지키세요.
8. 프로젝트 구조와 서브디렉토리
권장 디렉토리 구조
project/
├── CMakeLists.txt # 루트 CMake 설정
├── src/
│ ├── CMakeLists.txt # 소스 빌드 설정
│ ├── main.cpp
│ └── lib/
│ ├── CMakeLists.txt
│ ├── lib.cpp
│ └── lib.h
├── include/ # 공개 헤더
│ └── mylib/
│ └── api.h
├── tests/
│ ├── CMakeLists.txt
│ └── test_main.cpp
├── external/ # 서브모듈, 외부 라이브러리
└── build/ # 빌드 출력 (git ignore)
루트 CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# 옵션
option(BUILD_TESTS "Build tests" ON)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
# 서브디렉토리
add_subdirectory(src)
if(BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
src/CMakeLists.txt
# 라이브러리
add_library(mylib
lib/lib.cpp
lib/lib.h
)
target_include_directories(mylib
PUBLIC ${CMAKE_SOURCE_DIR}/include
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
)
# 실행 파일
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE mylib)
tests/CMakeLists.txt
find_package(GTest REQUIRED)
add_executable(tests
test_main.cpp
test_lib.cpp
)
target_link_libraries(tests PRIVATE
mylib
GTest::gtest
GTest::gtest_main
)
add_test(NAME MyTests COMMAND tests)
9. 완전한 예제: 멀티 타겟 프로젝트
문제 1: 절대 경로 사용
원인: 절대 경로를 하드코딩하면 다른 환경에서 빌드가 깨집니다.
# ❌ 잘못된 사용
target_include_directories(myapp PRIVATE /usr/local/include)
# ✅ 올바른 사용: CMake 변수 사용
target_include_directories(myapp PRIVATE ${CMAKE_SOURCE_DIR}/include)
주의사항: 설치된 패키지 경로가 아니라 소스 트리 변수를 쓰는지 항상 확인하세요.
문제 2: 소스 디렉토리에서 빌드
원인: 소스 디렉토리에서 cmake .를 실행하면 빌드 파일이 소스와 섞입니다.
# ❌ 잘못된 사용
cd /project
cmake .
# ✅ 올바른 사용: out-of-source build
mkdir build
cd build
cmake ..
문제 3: 캐시 문제
증상: CMake 설정을 바꿔도 반영되지 않음.
원인: CMakeCache.txt에 이전 설정이 캐시됨.
# 해결법 1: 캐시 삭제
rm CMakeCache.txt
cmake ..
# 해결법 2: 빌드 디렉토리 재생성
cd ..
rm -rf build
mkdir build && cd build
cmake ..
주의사항: 삭제 전에 중요한 -D 플래그를 메모하거나 프리셋으로 옮기세요.
문제 4: 링크 순서
원인: 라이브러리 링크 순서가 잘못되면 undefined reference 에러가 발생합니다.
# ❌ 잘못된 순서: lib1이 lib2에 의존하는데 lib2가 뒤에
target_link_libraries(myapp PRIVATE lib2 lib1)
# ✅ 올바른 순서: 의존하는 라이브러리가 먼저
target_link_libraries(myapp PRIVATE lib1 lib2)
문제 5: PUBLIC/PRIVATE 혼동
증상: 헤더를 찾지 못하거나, 불필요한 헤더가 노출됨.
# ❌ 잘못된 사용: 내부 헤더를 PUBLIC으로
target_include_directories(mylib PUBLIC src/internal)
# ✅ 올바른 사용
target_include_directories(mylib
PUBLIC include # 외부에 노출
PRIVATE src/internal # 내부 전용
)
문제 6: find_package 실패
증상: Could not find package SomeLib.
원인: 라이브러리가 설치되지 않았거나, CMake가 찾을 수 없는 위치에 있음.
# 해결법 1: 패키지 설치
sudo apt install libboost-dev # Linux
brew install boost # macOS
# 해결법 2: CMAKE_PREFIX_PATH 지정
cmake -DCMAKE_PREFIX_PATH=/custom/install/path ..
# 해결법 3: 수동으로 경로 지정
cmake -DBoost_ROOT=/usr/local/boost ..
10. 자주 발생하는 문제와 해결법
패턴 1: 컴파일 경고를 에러로
function(enable_strict_warnings target)
if(MSVC)
target_compile_options(${target} PRIVATE /W4 /WX)
else()
target_compile_options(${target} PRIVATE
-Wall -Wextra -Wpedantic -Werror
)
endif()
endfunction()
add_executable(myapp main.cpp)
enable_strict_warnings(myapp)
패턴 2: 빌드 타입별 후처리
# Release 빌드 시 strip으로 심볼 제거
if(CMAKE_BUILD_TYPE STREQUAL "Release")
add_custom_command(TARGET myapp POST_BUILD
COMMAND ${CMAKE_STRIP} $<TARGET_FILE:myapp>
)
endif()
패턴 3: 설치 규칙
# 실행 파일 설치
install(TARGETS myapp
RUNTIME DESTINATION bin
)
# 라이브러리 설치
install(TARGETS mylib
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
# 헤더 설치
install(DIRECTORY include/
DESTINATION include
)
# 설정 파일 설치
install(FILES config.json
DESTINATION etc
)
# 설치
cmake --build . --target install
# 또는
make install
패턴 4: 버전 정보 생성
# version.h.in
#define APP_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define APP_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define APP_VERSION_PATCH @PROJECT_VERSION_PATCH@
# CMakeLists.txt
project(MyProject VERSION 1.2.3)
configure_file(version.h.in ${CMAKE_BINARY_DIR}/version.h)
target_include_directories(myapp PRIVATE ${CMAKE_BINARY_DIR})
패턴 5: 조건부 컴파일
option(ENABLE_LOGGING "Enable logging" ON)
if(ENABLE_LOGGING)
target_compile_definitions(myapp PRIVATE ENABLE_LOGGING)
target_sources(myapp PRIVATE src/logger.cpp)
endif()
패턴 6: 플랫폼별 소스
if(WIN32)
target_sources(myapp PRIVATE src/platform/windows.cpp)
elseif(APPLE)
target_sources(myapp PRIVATE src/platform/macos.cpp)
elseif(UNIX)
target_sources(myapp PRIVATE src/platform/linux.cpp)
endif()
문제 6: find_package 실패
증상: Could not find package SomeLib.
원인: 라이브러리가 설치되지 않았거나, CMake가 찾을 수 없는 위치에 있음.
# 해결법 1: 패키지 설치
sudo apt install libboost-dev # Linux
brew install boost # macOS
# 해결법 2: CMAKE_PREFIX_PATH 지정
cmake -DCMAKE_PREFIX_PATH=/custom/install/path ..
# 해결법 3: 수동으로 경로 지정
cmake -DBoost_ROOT=/usr/local/boost ..
문제 7: 프리셋이 작동하지 않음
증상: cmake --preset=debug 실행 시 “No such preset” 에러.
원인: CMake 버전이 3.19 미만이거나, JSON 문법 오류.
# 해결법 1: CMake 버전 확인
cmake --version # 3.19 이상 필요
# 해결법 2: JSON 문법 검증
# VSCode나 온라인 JSON validator 사용
# 해결법 3: 프리셋 목록 확인
cmake --list-presets
11. 성능 최적화
병렬 빌드
# 모든 CPU 코어 사용
cmake --build . -j$(nproc)
# 또는 make
make -j$(nproc)
Ninja 사용
Ninja는 Make보다 빠른 빌드 시스템입니다.
# Ninja로 빌드 시스템 생성
cmake -G Ninja ..
# 빌드
ninja
ccache로 컴파일 캐싱
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
endif()
프리컴파일 헤더 (PCH)
target_precompile_headers(myapp PRIVATE
<iostream>
<vector>
<string>
)
Unity 빌드 (3.16+)
여러 소스 파일을 하나로 합쳐 컴파일 시간 단축.
set(CMAKE_UNITY_BUILD ON)
set(CMAKE_UNITY_BUILD_BATCH_SIZE 16)
add_executable(myapp ${SOURCES})
12. 모범 사례·베스트 프랙티스
1. 타겟 기반 접근 (Modern CMake)
# ❌ 구식 방법 (전역 변수)
include_directories(include)
link_libraries(mylib)
add_definitions(-DDEBUG)
# ✅ 현대적 방법 (타겟 기반)
target_include_directories(myapp PRIVATE include)
target_link_libraries(myapp PRIVATE mylib)
target_compile_definitions(myapp PRIVATE DEBUG)
2. Generator Expression 활용
# 빌드 타입별 플래그
target_compile_options(myapp PRIVATE
$<$<CONFIG:Debug>:-O0 -g>
$<$<CONFIG:Release>:-O3 -DNDEBUG>
)
# 컴파일러별 플래그
target_compile_options(myapp PRIVATE
$<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra>
$<$<CXX_COMPILER_ID:MSVC>:/W4>
$<$<CXX_COMPILER_ID:Clang>:-Weverything>
)
# 플랫폼별 링크
target_link_libraries(myapp PRIVATE
$<$<PLATFORM_ID:Linux>:pthread>
$<$<PLATFORM_ID:Windows>:ws2_32>
)
3. 인터페이스 라이브러리 활용
# 공통 설정을 인터페이스 라이브러리로
add_library(common_settings INTERFACE)
target_compile_features(common_settings INTERFACE cxx_std_20)
target_compile_options(common_settings INTERFACE
$<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra -Wpedantic>
$<$<CXX_COMPILER_ID:MSVC>:/W4>
)
# 모든 타겟에 적용
target_link_libraries(myapp PRIVATE common_settings)
target_link_libraries(mylib PRIVATE common_settings)
4. 버전 정보 자동 생성
# version.h.in
#pragma once
#define PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@
#define PROJECT_VERSION "@PROJECT_VERSION@"
# CMakeLists.txt
project(MyProject VERSION 1.2.3)
configure_file(
${CMAKE_SOURCE_DIR}/version.h.in
${CMAKE_BINARY_DIR}/generated/version.h
@ONLY
)
target_include_directories(myapp PRIVATE ${CMAKE_BINARY_DIR}/generated)
5. 설치 규칙 명확히
# 실행 파일 설치
install(TARGETS myapp
RUNTIME DESTINATION bin
)
# 라이브러리 설치
install(TARGETS mylib
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
)
# 헤더 설치 (FILE_SET 사용, 3.23+)
install(TARGETS mylib
FILE_SET HEADERS DESTINATION include
)
# Config 파일 생성 (다른 프로젝트에서 find_package 가능)
install(EXPORT MyLibTargets
FILE MyLibTargets.cmake
NAMESPACE MyLib::
DESTINATION lib/cmake/MyLib
)
13. 프로덕션 패턴
패턴 1: 조건부 기능 활성화
option(ENABLE_LOGGING "Enable logging" ON)
option(ENABLE_PROFILING "Enable profiling" OFF)
option(BUILD_EXAMPLES "Build examples" OFF)
if(ENABLE_LOGGING)
target_compile_definitions(myapp PRIVATE ENABLE_LOGGING)
target_sources(myapp PRIVATE src/logger.cpp)
endif()
if(ENABLE_PROFILING)
target_link_libraries(myapp PRIVATE profiler)
endif()
if(BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
패턴 2: 플랫폼별 소스 관리
# 공통 소스
set(COMMON_SOURCES
src/main.cpp
src/core.cpp
)
# 플랫폼별 소스
if(WIN32)
list(APPEND PLATFORM_SOURCES src/platform/windows.cpp)
elseif(APPLE)
list(APPEND PLATFORM_SOURCES src/platform/macos.cpp)
elseif(UNIX)
list(APPEND PLATFORM_SOURCES src/platform/linux.cpp)
endif()
add_executable(myapp ${COMMON_SOURCES} ${PLATFORM_SOURCES})
패턴 3: 컴파일 경고를 에러로 (CI/CD)
function(enable_warnings_as_errors target)
if(MSVC)
target_compile_options(${target} PRIVATE /W4 /WX)
else()
target_compile_options(${target} PRIVATE
-Wall -Wextra -Wpedantic -Werror
)
endif()
endfunction()
# CI 환경에서만 활성화
if(DEFINED ENV{CI})
enable_warnings_as_errors(myapp)
endif()
패턴 4: 빌드 타입별 후처리
# Release 빌드 시 스트립
if(CMAKE_BUILD_TYPE STREQUAL "Release" AND UNIX)
add_custom_command(TARGET myapp POST_BUILD
COMMAND ${CMAKE_STRIP} --strip-all $<TARGET_FILE:myapp>
COMMENT "Stripping binary"
)
endif()
# 빌드 정보 출력
add_custom_command(TARGET myapp POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Build complete: $<TARGET_FILE:myapp>"
COMMAND ${CMAKE_COMMAND} -E echo "Build type: ${CMAKE_BUILD_TYPE}"
)
패턴 5: 의존성 버전 고정
include(FetchContent)
# 버전 변수로 관리
set(GOOGLETEST_VERSION "1.14.0")
set(FMT_VERSION "10.0.0")
set(SPDLOG_VERSION "1.12.0")
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v${GOOGLETEST_VERSION}
GIT_SHALLOW ON # 최신 커밋만 다운로드
)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG ${FMT_VERSION}
GIT_SHALLOW ON
)
FetchContent_MakeAvailable(googletest fmt)
패턴 6: 개발자 모드
option(DEVELOPER_MODE "Enable developer mode" OFF)
if(DEVELOPER_MODE)
# 모든 경고 활성화
add_compile_options(-Wall -Wextra -Wpedantic)
# Sanitizer 활성화
add_compile_options(-fsanitize=address,undefined)
add_link_options(-fsanitize=address,undefined)
# 컴파일 명령 내보내기 (clangd, ccls 등에서 사용)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# 테스트 빌드
set(BUILD_TESTS ON)
endif()
14. 정리 및 체크리스트
CMake 핵심 개념 요약
| 개념 | 설명 | 예제 |
|---|---|---|
| 타겟 | 빌드 산출물 (실행 파일, 라이브러리) | add_executable, add_library |
| 속성 | 타겟별 설정 | target_include_directories |
| 의존성 | 타겟 간 링크 관계 | target_link_libraries |
| 프리셋 | 재사용 가능한 빌드 설정 | CMakePresets.json |
| Generator | 플랫폼별 빌드 시스템 생성 | Ninja, Make, Visual Studio |
CMake 도입 체크리스트
# ✅ 기본 설정
- [ ] CMake 3.20 이상 설치 (3.28+ 권장)
- [ ] out-of-source build 사용 (build/ 디렉토리)
- [ ] C++ 표준 설정 (CMAKE_CXX_STANDARD)
- [ ] CMAKE_CXX_EXTENSIONS OFF 설정
# ✅ 타겟 관리
- [ ] 타겟 기반 접근 (add_executable, add_library)
- [ ] PUBLIC/PRIVATE/INTERFACE 명확히 구분
- [ ] target_* 명령어 사용 (전역 명령어 피하기)
# ✅ 의존성 관리
- [ ] find_package로 외부 라이브러리 찾기
- [ ] FetchContent로 자동 다운로드 (선택)
- [ ] 버전 고정 (재현 가능한 빌드)
# ✅ 빌드 설정
- [ ] CMakePresets.json 작성 (팀 공유)
- [ ] 빌드 타입 설정 (Debug/Release)
- [ ] 경고 활성화 (-Wall -Wextra 또는 /W4)
- [ ] CI/CD에서 경고를 에러로 (-Werror)
# ✅ 테스트
- [ ] enable_testing() + add_test()
- [ ] CTest 통합
# ✅ 설치 및 배포
- [ ] install() 규칙 작성
- [ ] Config 파일 생성 (find_package 지원)
# ✅ 성능
- [ ] 병렬 빌드 활성화 (-j)
- [ ] Ninja 사용 고려
- [ ] ccache 설정
- [ ] PCH 활용 (큰 프로젝트)
# ✅ 최신 기능 (선택)
- [ ] C++20 모듈 지원 (3.28+)
- [ ] FILE_SET 사용 (3.23+)
- [ ] Unity 빌드 (3.16+)
버전별 권장 기능
| CMake 버전 | 권장 사항 |
|---|---|
| 3.20+ | 기본 기능 사용 가능 |
| 3.23+ | FILE_SET으로 헤더 관리 |
| 3.25+ | LINUX 변수 사용 |
| 3.28+ | C++20 모듈 지원 |
실전 팁: CMake 효율적으로 사용하기
-
빌드 디렉토리 여러 개 유지
# 디버그, 릴리스, 프로파일링 동시 유지 cmake --preset=debug cmake --preset=release cmake --preset=profile # 필요할 때 각각 빌드 cmake --build build/debug cmake --build build/release -
컴파일 명령 내보내기 (LSP 서버용)
# compile_commands.json 생성 (clangd, ccls 등에서 사용) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # 루트에 심볼릭 링크 생성 # ln -s build/compile_commands.json . -
빌드 시간 측정
# 빌드 시간 측정 time cmake --build build # 또는 CMake 내장 기능 cmake --build build --verbose -
타겟별 선택 빌드
# 특정 타겟만 빌드 (전체 빌드 불필요) cmake --build build --target mylib cmake --build build --target tests # 여러 타겟 동시 빌드 cmake --build build --target mylib --target myapp -
캐시 변수 확인
# 현재 캐시 변수 확인 cmake -L build # 고급 변수까지 확인 cmake -LA build # 특정 변수 확인 cmake -L build | grep CMAKE_CXX_COMPILER
빠른 참조: CMake 명령어 치트시트
# 🏗️ 프로젝트 설정
cmake -S . -B build # 소스: 현재, 빌드: build/
cmake --preset=debug # 프리셋 사용 (3.19+)
cmake -DCMAKE_BUILD_TYPE=Release # 빌드 타입 지정
# 🔨 빌드
cmake --build build # 빌드 실행
cmake --build build -j8 # 병렬 빌드 (8 코어)
cmake --build build --target myapp # 특정 타겟만
# 🧪 테스트
ctest --test-dir build # 테스트 실행
ctest --preset=default # 프리셋으로 테스트
# 📦 설치
cmake --install build # 설치 실행
cmake --install build --prefix /usr/local # 경로 지정
# 🧹 정리
cmake --build build --target clean # 빌드 결과물 삭제
rm -rf build # 완전 초기화
트러블슈팅: 빠른 문제 해결
| 증상 | 원인 | 해결법 |
|---|---|---|
| Could not find package | 라이브러리 미설치 | apt install, brew install 또는 CMAKE_PREFIX_PATH 지정 |
| Undefined reference | 링크 순서 오류 | target_link_libraries 순서 확인 |
| Header not found | include 경로 누락 | target_include_directories 추가 |
| 캐시 문제 | CMakeCache.txt 오래됨 | rm CMakeCache.txt 또는 cmake --fresh |
| 프리셋 없음 | CMake 버전 낮음 | CMake 3.19+ 업그레이드 |
| 빌드 느림 | Make 사용 | Ninja로 전환 (-G Ninja) |
빌드 시스템 성능 비교
| 빌드 시스템 | 속도 | 병렬화 | 크로스 플랫폼 | 학습 곡선 |
|---|---|---|---|---|
| Make | 보통 | 제한적 | Linux/macOS | 쉬움 |
| Ninja | 빠름 | 우수 | 모든 플랫폼 | 쉬움 |
| Visual Studio | 보통 | 우수 | Windows | 쉬움 |
| Xcode | 보통 | 우수 | macOS | 중간 |
권장: Ninja (가장 빠르고 모든 플랫폼 지원)
다음 단계
- CMake Targets 심화에서 타겟 속성 상세 학습
- find_package 완벽 가이드에서 의존성 관리 심화
- 빌드 시스템 비교에서 CMake vs 다른 도구 비교
- vcpkg 기초에서 패키지 매니저 활용법 학습
- Conan 기초에서 또 다른 패키지 매니저 학습
- CI/CD with GitHub Actions에서 자동화 빌드 구축
FAQ
Q1: CMake vs Make 차이는?
A: CMake는 빌드 시스템을 생성하는 도구(메타 빌드 시스템)이고, Make는 실제로 빌드를 수행하는 도구입니다. CMake가 Makefile을 생성하면, Make가 그 Makefile을 읽어 컴파일합니다.
CMakeLists.txt → [CMake] → Makefile → [Make] → 실행 파일
Q2: out-of-source build가 뭔가요?
A: 소스 디렉토리와 빌드 디렉토리를 분리하는 것입니다. build/ 디렉토리를 만들어 그 안에서 cmake ..를 실행하면, 빌드 파일이 build/에만 생성되어 소스가 깨끗하게 유지됩니다.
# ✅ 올바른 방법
mkdir build && cd build
cmake ..
# ❌ 잘못된 방법 (소스와 빌드 파일 섞임)
cmake .
Q3: PUBLIC vs PRIVATE vs INTERFACE 언제 쓰나요?
A:
- PRIVATE: 이 타겟 내부에서만 사용 (구현 세부사항)
- PUBLIC: 이 타겟 + 이 타겟을 링크하는 타겟도 사용 (공개 API)
- INTERFACE: 이 타겟을 링크하는 타겟만 사용 (헤더 전용 라이브러리)
# 내부 헤더
target_include_directories(mylib PRIVATE src/internal)
# 공개 헤더
target_include_directories(mylib PUBLIC include)
# 헤더 전용 라이브러리
target_include_directories(header_only INTERFACE include)
Q4: CMakePresets.json은 언제 사용하나요?
A: 팀 프로젝트나 CI/CD에서 빌드 설정을 공유할 때 사용합니다. 개인 설정은 CMakeUserPresets.json에 작성하고 git ignore에 추가합니다.
# 프리셋 없이 (복잡)
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=g++ -DENABLE_TESTS=ON ..
# 프리셋 사용 (간단)
cmake --preset=debug
Q5: find_package가 실패하면?
A:
- 라이브러리 설치 확인
CMAKE_PREFIX_PATH지정- 패키지 매니저 사용 (vcpkg, conan)
# 1. 설치
sudo apt install libboost-dev # Linux
brew install boost # macOS
# 2. 경로 지정
cmake -DCMAKE_PREFIX_PATH=/usr/local ..
# 3. vcpkg 사용
cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg.cmake ..
Q6: C++20 모듈을 사용해야 하나요?
A: 2026년 현재, 주요 컴파일러(GCC 14+, Clang 17+, MSVC 19.36+)가 모두 지원하므로 새 프로젝트에서는 사용을 권장합니다. 기존 프로젝트는 점진적으로 마이그레이션하세요.
장점: 빠른 컴파일, 깨끗한 인터페이스, 순환 의존성 방지 단점: 빌드 시스템 복잡도 증가, 레거시 코드와 혼용 시 주의 필요
Q7: 빌드가 느린데 어떻게 최적화하나요?
A:
- Ninja 사용 (
cmake -G Ninja) - 병렬 빌드 (
cmake --build . -j$(nproc)) - ccache 활성화
- PCH (프리컴파일 헤더) 사용
- Unity 빌드 고려 (큰 프로젝트)
# ccache 활성화
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
endif()
# Unity 빌드
set(CMAKE_UNITY_BUILD ON)
Q8: CMake 학습 리소스는?
A:
- CMake 공식 문서 (최신 기능 확인)
- “Professional CMake: A Practical Guide” (실전 패턴)
- CMake Tutorial (단계별 학습)
- Effective Modern CMake (베스트 프랙티스)
Q9: 기존 Makefile 프로젝트를 CMake로 마이그레이션하려면?
A: 단계별로 점진적으로 마이그레이션하세요.
# 1단계: 최소 CMakeLists.txt 작성
cmake_minimum_required(VERSION 3.20)
project(MyProject)
file(GLOB_RECURSE SOURCES "src/*.cpp") # 임시로 GLOB 사용
add_executable(myapp ${SOURCES})
# 2단계: 소스 파일 명시적으로 나열
set(SOURCES
src/main.cpp
src/module1.cpp
# ...
)
# 3단계: 타겟별로 분리
add_library(mylib src/lib.cpp)
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE mylib)
# 4단계: 서브디렉토리 구조화
add_subdirectory(src)
add_subdirectory(tests)
Q10: CMake 캐시를 완전히 초기화하려면?
A:
# 방법 1: 빌드 디렉토리 삭제 (가장 확실)
rm -rf build
mkdir build && cd build
cmake ..
# 방법 2: CMakeCache.txt만 삭제
rm CMakeCache.txt
cmake ..
# 방법 3: CMake 명령으로 초기화
cmake --fresh .. # CMake 3.24+
Q11: 여러 빌드 타입을 동시에 유지하려면?
A: 빌드 디렉토리를 분리하거나 프리셋을 사용하세요.
# 방법 1: 디렉토리 분리
mkdir build-debug && cd build-debug
cmake -DCMAKE_BUILD_TYPE=Debug ..
mkdir build-release && cd build-release
cmake -DCMAKE_BUILD_TYPE=Release ..
# 방법 2: 프리셋 사용 (권장)
cmake --preset=debug
cmake --preset=release
# build/debug, build/release에 각각 생성됨
Q12: CMake에서 Git 커밋 해시를 버전에 포함하려면?
A:
# Git 커밋 해시 가져오기
execute_process(
COMMAND git rev-parse --short HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# 버전 헤더 생성
configure_file(
${CMAKE_SOURCE_DIR}/version.h.in
${CMAKE_BINARY_DIR}/version.h
)
# version.h.in
#define VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define VERSION_MINOR @PROJECT_VERSION_MINOR@
#define GIT_COMMIT "@GIT_COMMIT_HASH@"
한 줄 요약: CMake 3.28+로 C++20 모듈을 포함한 크로스 플랫폼 프로젝트를 효율적으로 빌드할 수 있습니다. CMakePresets.json으로 팀 설정을 공유하고, 최신 기능을 활용하여 생산성을 높이세요.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ CMake Targets 완벽 가이드 | 타겟 기반 빌드 시스템
- C++ CMake find_package 완벽 가이드 | 외부 라이브러리 통합
- C++ Conan 완벽 가이드 | 현대적인 C++ 패키지 관리
- C++ vcpkg 완벽 가이드 | Microsoft C++ 패키지 관리자
- C++ 빌드 시스템 완전 비교 | CMake·Meson·Bazel·Makefile·패키지 매니저 선택 가이드
실전 체크리스트
실무에서 이 개념을 적용할 때 확인해야 할 사항입니다.
코드 작성 전
- 이 기법이 현재 문제를 해결하는 최선의 방법인가?
- 팀원들이 이 코드를 이해하고 유지보수할 수 있는가?
- 성능 요구사항을 만족하는가?
코드 작성 중
- 컴파일러 경고를 모두 해결했는가?
- 엣지 케이스를 고려했는가?
- 에러 처리가 적절한가?
코드 리뷰 시
- 코드의 의도가 명확한가?
- 테스트 케이스가 충분한가?
- 문서화가 되어 있는가?
이 체크리스트를 활용하여 실수를 줄이고 코드 품질을 높이세요.
관련 글
- C++ CMake find_package 완벽 가이드 | 외부 라이브러리 통합
- C++ CMake Targets 완벽 가이드 | 타겟 기반 빌드 시스템
- C++ Conan 완벽 가이드 | 현대적인 C++ 패키지 관리
- C++ Makefile |
- C++ Benchmarking |