C++20 모듈(Modules) 기능은 대규모 C++ 프로젝트의 빌드 속도와 의존성 관리를 획기적으로 개선할 잠재력을 지니고 있습니다. 과거에는 헤더 가드, 전방 선언, #pragma once를 통해 복잡한 인클루드 그래프를 관리해 왔지만, 모듈 도입으로 불필요한 중복 파싱을 줄이고, 빌드 시간 최적화를 이룰 수 있습니다. 그러나 아직 모듈은 모든 빌드 시스템과 툴체인에서 광범위하게 지원되지 않으므로, 전환 과정에서 스타일 가이드 정립이 필요합니다.
이번 글에서는 구글 C++ 스타일 가이드, LLVM 스타일 가이드, 모질라 스타일 가이드 등 다양한 가이드와 실제 프로젝트 사례를 바탕으로, 빌드 시스템 선택(CMake, Bazel, Meson 등), 모듈 도입 전략, 프로젝트 디렉토리 구조, 그리고 모듈-헤더 혼합 환경에서의 스타일 이슈를 살펴봅니다.
다양한 스타일 가이드와 사례
- 구글 C++ 스타일 가이드:
- 아직 모듈에 대한 구체적 권장사항은 제한적이지만, 헤더 가드와 #pragma once 정책을 명확히 함
- 빌드 시스템(CMake/Bazel)에서 일관된 규칙을 적용, 디렉토리 구조 문서화
- 모듈 도입 시 점진적 변환 및 기존 헤더와 공존 전략 권장
- LLVM 스타일 가이드:
- LLVM 프로젝트는 전통적으로 CMake 사용, 헤더/소스 구조 명확
- 모듈 지원 성숙 시 LLVM 코드베이스에 모듈 부분 도입 가능성 고려
- 공용 헤더/라이브러리 부분을 모듈화해 빌드 시간 최적화 시도 가능
- 모질라 스타일 가이드:
- 대규모 프로젝트(파이어폭스)에서 빌드 시스템과 인클루드 관리 중요성 강조
- 모듈화 지원 시 CI/CD 파이프라인에 통합, 문서화 필요
- 모듈과 기존 헤더 방식을 혼합 사용 시 규칙 정립 권장
장점 및 단점 분석
모듈 도입
장점:
- 빌드 시간 단축: 헤더 중복 파싱 감소
- 의존성 그래프 명확화: 인터페이스와 구현을 모듈 경계로 구분
- 인클루드 순서 문제 감소
단점:
- 모든 컴파일러/빌드 시스템에서 아직 완전한 지원 X
- 기존 대규모 코드베이스 전환 시 마이그레이션 비용
- 초기 표준 도입 시기에는 도구 성숙도 부족
빌드 시스템과 프로젝트 구조
장점(명확한 디렉토리 구조 및 빌드 스크립트 스타일):
- 코드 탐색 용이, 신규 기여자 온보딩 쉬움
- CI/CD 파이프라인 효율 개선
- 모듈 도입 시 명확한 인터페이스/구현 분리
단점:
- 초기 설정 및 합의 필요
- 변경 시 팀원들 습관 변환 및 문서 업데이트 필요
헤더와 모듈 혼합 환경
장점:
- 점진적 변환 가능. 기존 코드를 전부 수정할 필요 없음
- 선택적으로 성숙한 부분만 모듈화해 성능/가독성 향상
단점:
- 혼용 규칙 없으면 혼란
- 모듈 인터페이스 파일(.cppm)과 기존 헤더(.h) 공존 시 파편화 가능
어떤 경우 어떤 선택을 할까?
- C++20 모듈 적극 도입 가능한 환경:
- 주요 라이브러리나 빈번히 인클루드되는 공용 헤더를 모듈화
- 빌드 시스템(CMake 최신 버전, Ninja, ccache)과 함께 사용해 빌드 시간 최적화
- 프로젝트 루트에 모듈 폴더를 만들고, 모듈 인터페이스 파일과 구현 파일을 명확히 분리
- 모듈 도입 어려운 레거시 환경:
- 헤더 가드, #pragma once, 전방 선언 활용해 빌드 시간 관리
- 빌드 시스템 스크립트(cmake 파일 등)에서 인클루드 디렉토리 관리 명확히
- 모듈 지원이 성숙해지면 부분적으로 모듈 도입 테스트
- 혼합 전략:
- 핵심 유틸리티나 공용 타입 정의를 모듈화
- 나머지 코드와 헤더는 기존 스타일 유지
- 문서화 및 팀 교육을 통해 모듈-헤더 혼합 규칙 명확히
실제 예제 코드 비교
// 모듈 인터페이스 예 (C++20)
export module my_project.utils;
import <string>;
import <vector>;
export std::string JoinStrings(const std::vector<std::string>& parts);
// 기존 헤더 예
#ifndef MY_PROJECT_UTILS_H_
#define MY_PROJECT_UTILS_H_
#include <string>
#include <vector>
std::string JoinStrings(const std::vector<std::string>& parts);
#endif // MY_PROJECT_UTILS_H_
위 예제에서 같은 기능(문자열 조합 함수)을 모듈과 헤더 형태로 표현했습니다. 모듈 사용 시 인클루드 대신 import를 통해 의존성 관리, 빌드 시간 단축이 가능하지만 현재 빌드 환경이 이를 지원하는지 확인 필요합니다.
마무리
빌드 시스템과 모듈 스타일은 C++ 코드베이스 규모가 커질수록 중요해집니다. C++20 모듈 도입으로 헤더 기반 인클루드 시스템의 복잡성을 줄이고 빌드 시간을 개선할 수 있지만, 이는 팀의 빌드 파이프라인, 툴체인 지원, 문서화, 교육과 같은 준비가 필수적입니다. 일관된 디렉토리 구조, 빌드 스크립트 규칙, 모듈-헤더 혼합 시 명확한 정책 수립을 통해 점진적으로 현대적 C++ 빌드 스타일을 정착시킬 수 있습니다.
다음 편에서는 각 시리즈 주제를 총정리하며, 다양하고 상이한 스타일 선택지 사이에서 팀이 일관성과 합의를 통해 장기적 유지보수성과 가독성을 확보하는 전략에 대해 마무리 논평을 제시하겠습니다.