콘텐츠로 이동

C++20 Modules 기초와 실전 활용

C++ 모듈은 40년 이상 사용된 #include 기반 헤더 시스템을 대체하는 새로운 코드 조직화 메커니즘입니다. 빌드 속도 향상, 매크로 격리, 명시적 인터페이스 정의 등 여러 장점을 제공합니다.


// 헤더 파일의 전통적 문제들
#include <vector> // 전체 vector 구현 코드 복붙
#include <string> // 다시 복붙
// 컴파일 단위마다 반복, 빌드 시간 폭증
// 매크로 오염
#define MAX_SIZE 100 // 전역으로 퍼짐

모듈은 이를 해결합니다. 모듈 인터페이스는 한 번만 컴파일되고 바이너리 형태로 재사용됩니다.


2.1 모듈 인터페이스 단위 (.ixx 또는 .cppm)

섹션 제목: “2.1 모듈 인터페이스 단위 (.ixx 또는 .cppm)”
math.ixx
export module math; // 모듈 선언
import <string>; // 표준 라이브러리 임포트 (모듈화된 경우)
// export: 외부에서 사용 가능
export int add(int a, int b) {
return a + b;
}
export double pi = 3.14159265358979;
// export 없음: 모듈 내부에서만 사용
static int helper(int x) {
return x * 2;
}
main.cpp
import math; // 모듈 임포트
#include <iostream> // 헤더와 혼용 가능
int main() {
std::cout << add(3, 4) << "\n"; // 7
std::cout << pi << "\n"; // 3.14159...
// helper(5); // 오류: export 되지 않음
return 0;
}

export module geometry;
export {
struct Point { double x, y; };
struct Rect { Point tl, br; };
double area(const Rect& r);
}
// 구현 (export 불필요)
double area(const Rect& r) {
return (r.br.x - r.tl.x) * (r.br.y - r.tl.y);
}
export module mylib;
export import math; // math 모듈의 export를 재내보냄
export import geometry; // geometry도 재내보냄

대형 모듈을 논리적 파티션으로 분할할 수 있습니다.

// engine-render.ixx (파티션)
export module engine:render; // engine 모듈의 render 파티션
export void renderFrame();
export void setResolution(int w, int h);
void renderFrame() { /* 구현 */ }
void setResolution(int w, int h) { /* 구현 */ }
engine-physics.ixx
export module engine:physics;
export void updatePhysics(float dt);
void updatePhysics(float dt) { /* 구현 */ }
// engine.ixx (기본 모듈 인터페이스)
export module engine;
export import :render; // 파티션 재내보내기
export import :physics;
main.cpp
import engine; // render, physics 모두 접근 가능
int main() {
renderFrame();
updatePhysics(0.016f);
return 0;
}

5. 구현 단위 (Module Implementation Unit)

섹션 제목: “5. 구현 단위 (Module Implementation Unit)”

인터페이스와 구현을 분리할 수 있습니다.

// logger.ixx (인터페이스)
export module logger;
export class Logger {
public:
void log(const char* msg);
void warn(const char* msg);
void error(const char* msg);
};
// logger.cpp (구현 단위)
module logger; // export 없음 = 구현 단위
#include <cstdio>
void Logger::log(const char* msg) {
std::printf("[LOG] %s\n", msg);
}
void Logger::warn(const char* msg) {
std::printf("[WARN] %s\n", msg);
}
void Logger::error(const char* msg) {
std::printf("[ERROR] %s\n", msg);
}

기존 헤더 파일을 모듈처럼 임포트할 수 있습니다.

// 헤더 단위로 임포트 (컴파일러 지원 필요)
import <vector>;
import <string>;
import "my_legacy_header.h";

이는 완전한 모듈은 아니지만 빌드 속도를 개선하는 중간 단계로 유용합니다.


cmake_minimum_required(VERSION 3.28)
project(MyProject)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
add_executable(app
main.cpp
math.ixx
geometry.ixx
)
cl /std:c++20 /experimental:module /interface math.ixx
cl /std:c++20 /experimental:module main.cpp math.ixx.obj

기존 코드베이스를 한 번에 모듈로 전환하는 것은 현실적이지 않습니다.

legacy_wrapper.ixx
// 전략 1: 헤더 파일 래핑
export module legacy;
// 기존 헤더를 global module fragment에 포함
module; // global module fragment 시작
#include "old_library.h" // 매크로, 전처리기 내용 포함 가능
export module legacy; // named module 시작
// old_library.h의 타입을 재내보내거나 래핑
export using OldType = ::OldLibraryType;

항목헤더 파일모듈
빌드 속도파일마다 재파싱한 번만 컴파일
매크로 격리전역으로 누출모듈 내부 격리
순환 의존성문제 발생명시적 오류
include guard필요불필요
도입 비용없음빌드 시스템 지원 필요

C++20 모듈은 C++ 빌드 시스템의 근본적인 개선을 가져옵니다. 컴파일 시간 단축, 명확한 인터페이스 정의, 매크로 오염 방지라는 세 가지 핵심 이점을 제공합니다. 2024년 기준으로 MSVC, GCC 15, Clang 16+ 모두 기본적인 모듈 지원을 제공하므로 새 프로젝트부터 점진적으로 도입해볼 것을 권장합니다.