C++ CMake 완벽 가이드 | 크로스 플랫폼 빌드·최신 CMake 3.28+ 기능·프리셋·모듈

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 입문 글의 CargoNode.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+ 권장)


목차

  1. 문제 시나리오: 빌드 관리의 어려움
  2. 최소 CMakeLists.txt
  3. 타겟: 실행 파일과 라이브러리
  4. CMake 프리셋 (3.19+)
  5. 외부 라이브러리 찾기: find_package
  6. C++20 모듈 지원 (3.28+)
  7. 빌드 타입과 컴파일 옵션
  8. 프로젝트 구조와 서브디렉토리
  9. 완전한 예제: 멀티 타겟 프로젝트
  10. 자주 발생하는 문제와 해결법
  11. 성능 최적화
  12. 모범 사례·베스트 프랙티스
  13. 프로덕션 패턴
  14. 정리 및 체크리스트

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.19CMakePresets.json 도입
3.20C++23 지원
3.23FILE_SET 도입 (헤더 관리 개선)
3.25LINUX 변수 추가
3.26SYSTEM 속성 개선
3.28C++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.hPUBLIC으로 추가합니다. myappmylib를 링크하면 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)

관련 글: vcpkgConan을 사용하면 더 체계적인 패키지 관리가 가능합니다.

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년 기준)

컴파일러버전모듈 지원
GCC14+완전 지원
Clang17+완전 지원
MSVC19.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 효율적으로 사용하기

  1. 빌드 디렉토리 여러 개 유지

    # 디버그, 릴리스, 프로파일링 동시 유지
    cmake --preset=debug
    cmake --preset=release
    cmake --preset=profile
    
    # 필요할 때 각각 빌드
    cmake --build build/debug
    cmake --build build/release
  2. 컴파일 명령 내보내기 (LSP 서버용)

    # compile_commands.json 생성 (clangd, ccls 등에서 사용)
    set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
    
    # 루트에 심볼릭 링크 생성
    # ln -s build/compile_commands.json .
  3. 빌드 시간 측정

    # 빌드 시간 측정
    time cmake --build build
    
    # 또는 CMake 내장 기능
    cmake --build build --verbose
  4. 타겟별 선택 빌드

    # 특정 타겟만 빌드 (전체 빌드 불필요)
    cmake --build build --target mylib
    cmake --build build --target tests
    
    # 여러 타겟 동시 빌드
    cmake --build build --target mylib --target myapp
  5. 캐시 변수 확인

    # 현재 캐시 변수 확인
    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 foundinclude 경로 누락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:

  1. 라이브러리 설치 확인
  2. CMAKE_PREFIX_PATH 지정
  3. 패키지 매니저 사용 (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:

  1. Ninja 사용 (cmake -G Ninja)
  2. 병렬 빌드 (cmake --build . -j$(nproc))
  3. ccache 활성화
  4. PCH (프리컴파일 헤더) 사용
  5. 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:

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 |