왜 아무도 C++ 모듈을 사용하지 않는가

ko생성일: 2025. 8. 13.갱신일: 2025. 8. 18.

C++20 모듈의 2025년 현황을 정리하고, 컴파일러·빌드 시스템 간 지원 격차, 표준 라이브러리 모듈화의 불완전성, 마이그레이션 난점 등 커뮤니티의 불만과 실제 예제로 드러나는 함정을 설명하며, 실무 도입에는 보수적 접근을 권고한다.

C++20에서 도입된 C++ 모듈은 2025년 8월 기준으로 크게 발전했지만, 도입과 구현 수준은 컴파일러와 빌드 시스템마다 제각각이다.

C++ 모듈은 전통적인 헤더 파일 체계를 대체하는 것을 목표로 하며, 더 빠른 컴파일, 더 나은 캡슐화, 향상된 의존성 관리를 제공한다. 모듈 인터페이스 유닛(선언을 export)과 구현 유닛으로 구성되며, 네임드 모듈과 헤더 임포트가 핵심 기능이다.

C++20에서 표준화되었음에도 불구하고, 실사용은 여전히 복잡한 도구 체계와 생태계 문제로 인해 성숙 단계에 있다.

As of Clang 19(2024년 9월 출시), 모듈은 네임드 모듈과 헤더 임포트 모두에 대해 충분히 잘 지원된다. Clang의 구현은 가장 성숙한 편으로 평가되며, 모듈 컴파일과 링크에 대한 지원이 견고하다. 최근 개선 사항에는 글로벌 모듈 프래그먼트 처리와 표준 라이브러리 모듈(예: std 모듈) 지원 향상이 포함된다.

GCC 15(2025년 예정)는 모듈을 실험적으로 지원하지만, Clang에 비해 완성도가 낮다. 네임드 모듈은 동작하나 표준 라이브러리 모듈화와 번역 단위 간 의존성 처리에 문제가 남아 있다. 전체 지원은 아직 진행 중이다.

Microsoft Visual C++(MSVC)은 Visual Studio 2022(17.10 이상)에서 모듈을 지원한다. 네임드 모듈과 헤더 임포트를 처리하지만, 특정 프로젝트 설정이 필요하다. MSVC의 빌드 시스템 통합은 개선되고 있지만, 일부 모서리 사례에서는 Clang에 뒤처진다.

C++20 표준 라이브러리는 부분적으로만 모듈화되어 있다. 표준 라이브러리 전체를 아우르는 std 모듈과 std.compat 같은 서브모듈이 Clang에는 제공되고, MSVC에는 일부 제공된다. GCC의 표준 라이브러리 모듈 지원은 미완성이며 안정화 작업이 진행 중이다.

컴파일러들은 컴파일 시간을 줄이기 위해 사전 컴파일된 표준 라이브러리 모듈로 나아가는 중이지만, 아직 보편적이지는 않다.

CMake 3.28+(2023)은 CXX_MODULES와 FILE_SET 기능을 통해 C++ 모듈을 지원한다. 그러나 의존성 스캐닝과 모듈 캐싱은 여전히 발전 중이며, 교차 컴파일러 호환성은 과제로 남아 있다.

Bazel은 모듈을 실험적으로 지원하지만, 복잡한 프로젝트를 위한 프로덕션 수준에는 이르지 못했다.

Ninja나 Meson 같은 도구도 적응 중이지만, BMI(Binary Module Interface) 파일을 위한 의존성 스캐닝 등 모듈 특화 워크플로는 아직 매끄럽지 않다.

현재 모듈은 주로 신규 또는 실험적인 프로젝트에서 사용된다. 대규모 코드베이스(예: 게임 엔진, 레거시 시스템)는 마이그레이션 비용과 미완성 도구 체계 때문에 종종 헤더를 유지한다.

일부 오픈소스 프로젝트와 초기 도입자들(예: 게임 개발, HPC) 중에는 모듈을 통해 컴파일 속도가 30~50% 개선되었다는 보고가 있으나, 상당한 리팩터링이 필요했다.

C++ 커뮤니티는 컴파일 속도와 코드 구성 개선이라는 잠재력에도 불구하고 C++ 모듈에 여러 불만을 제기해 왔다. 포럼, 블로그, 최근 소셜 미디어에서의 논의를 바탕으로 보면, 2025년 8월 현재 도입과 구현의 실질적인 난제로부터 비롯된 문제들이 많다.

Stack Overflow의 한 댓글(레딧 r/cpp 스레드에서 링크됨, 2023년 논의 참고)은 이렇게 말한다. “현재 Makefile 생성 지원이 없습니다… 이런 상태에선 지금으로선 사실상 쓸 수 없고, 모든 의존성을 손으로 일일이 적어 넣고 싶다면… 악몽이죠!

레딧 r/cpp의 한 게시물은 모듈과 비-export 클래스 관련 MSVC C++20 컴파일러 버그를 다루며, 이렇게 언급한다. “MSVC의 C++ 모듈 구현은 꽤 인상적입니다. 이 프로젝트의 소스를 헤더에서 모듈로 변환하는 데 성공적으로 수없이 많은 시간을 썼습니다.” 그러나 비-export 클래스에서 버그가 발생해 컴파일 오류를 일으킨다고 지적한다.

Stack Overflow 게시물에서 참조된 레딧 스레드는 모듈의 목적을 논의하며 한 사용자가 이렇게 댓글을 남겼다. “모듈은 전통적인 헤더+소스 파일 구조의 대안입니다. 언제 사용하느냐—컴파일러에 광범위한 지원이 갖춰지기 전까지는 절대 사용하지 마세요. 그 후에는—항상 사용(단, 다른 파일의 매크로를 import해야 하는 등 사용할 수 없는 경우는 예외).

C++20 모듈과 Boost에 대한 r/cpp 스레드에서는 이렇게 언급한다. “이 프로젝트는 C++20 모듈을 광범위하게 사용하며, C++ 표준 라이브러리와 서드파티 라이브러리를 모두 캡슐화합니다. 소스 코드의 일부에서 모듈 파티션을 실험해 보기는 했으나, 유의미한 이점을 찾지 못해 폭넓게 도입하지는 않았습니다.

같은 r/cpp 스레드는 모듈 파티션(이 글의 math_ops 예시와 같은)에 대해 “큰 이점을 찾지 못했다”고 덧붙인다.

아래는 주요 커뮤니티 불만 사항이다.

  • CMake, Bazel, Ninja 같은 빌드 시스템은 모듈 의존성 스캐닝과 BMI(Binary Module Interface) 생성에 애를 먹어 복잡한 설정을 요구한다. Clang, GCC, MSVC 간 의존성 관리의 일관성 부족으로 인해 대규모 프로젝트에서 모듈 설정이 번거롭다는 보고가 많다. CMake의 CXX_MODULES 지원(3.28 도입)은 진전이지만 여전히 실험적이라는 인상이 강하고, 느린 스캐닝과 취약한 크로스 플랫폼 이식성이 문제로 꼽힌다.
  • Clang이 모듈 지원을 선도하는 반면 GCC와 MSVC는 뒤처져 플랫폼 간 동작이 일관적이지 않다. GCC 15(2025)의 모듈 지원은 여전히 실험적이며, 표준 라이브러리 모듈과 번역 단위 간 의존성 처리에 문제가 있다. MSVC는 특정 프로젝트 설정을 요구하고, 복잡한 사례에서의 견고함이 부족하다. 이런 파편화로 인해 크로스 플랫폼 프로젝트에서는 모듈 사용을 회피하게 된다.
  • C++ 표준 라이브러리의 모듈화(예: std 모듈)는 불완전하거나 지원 상태가 미흡하다. Clang은 std 및 std.compat 모듈을 지원하지만, GCC의 구현은 부분적이며 MSVC의 지원은 제한적이다. 컴파일 시간을 줄여 줄 것으로 기대되던 사전 컴파일 표준 라이브러리 모듈이 보편적으로 제공되지 않아 모듈의 이점이 반감된다.
  • 레거시 코드를 모듈로 리팩터링하는 일은 시간도 많이 들고 오류도 잦다. 헤더를 모듈 인터페이스로 변환하려면 export를 통한 가시성 재설계, 글로벌 모듈 프래그먼트 처리, 비모듈 코드와의 호환성 확보가 필요하다. 수천 개의 헤더를 가진 대규모 프로젝트는 큰 장벽에 부딪히며, 자동 마이그레이션 도구의 부재가 문제를 키운다.
  • 모듈의 문법과 의미론(예: export, import, 모듈 파티션)은 혼란스럽고 문서화가 부족하다. 모듈 가시성, 도달성, 링키지 규칙은 헤더에 비해 직관적이지 않다는 평가가 많다. 모듈과 전통적 헤더의 상호작용(예: 헤더 임포트)은 혼합 코드베이스에서 복잡성을 키운다. 커뮤니티 피드백에서는 모듈 파티션에 대한 혼란과 더 나은 튜토리얼의 필요성이 자주 언급되며, 일부는 모듈이 “오버엔지니어링”되었다고도 한다.
  • 서드파티 라이브러리와 프레임워크는 모듈을 거의 지원하지 않아, 개발자들이 모듈과 헤더를 혼용하게 만든다. Boost, Qt 같은 인기 라이브러리는 여전히 헤더 기반이며, 이들의 느린 모듈 도입 속도가 모듈식 코드베이스의 이점을 제한한다. 생태계가 따라올 때까지 대기해야 하는 의존성 병목이 생긴다.
  • 약속된 컴파일 속도 개선은 일관되지 않거나 과장되었다는 지적이 있다. 모듈이 컴파일 시간을 줄일 수는 있지만(일부 사례 30~50%), 이는 프로젝트와 도구에 따라 달라진다. 의존성 스캐닝과 BMI 생성은 특히 최적화가 미흡한 빌드 시스템에서 오버헤드를 유발할 수 있다. 상당한 리팩터링 후에도 개선이 미미했다는 보고가 있고, 스캐너 비효율이나 컴파일러 버그로 인해 오히려 빌드가 느려졌다는 사례도 있다.
  • 모듈 관련 오류는 난해하고 디버깅이 어렵다. 누락된 export나 순환 의존성과 같은 모듈 특화 이슈에 대해 컴파일러가 모호한 오류 메시지를 내는 경우가 많다. 모듈 초심자에게 특히 좌절감을 주며, 디버깅에는 모듈 메커닉스에 대한 깊은 이해가 요구된다.

C++ 커뮤니티는 모듈의 잠재력을 인정하면서도 미성숙한 도구, 일관되지 않은 컴파일러 지원, 높은 도입 비용에 큰 좌절을 느끼고 있다. 가파른 학습 곡선, 제한적인 생태계 지원, 일부 사례에서 기대에 못 미치는 성능 향상도 회의론을 키운다. Clang과 CMake가 진전을 이루고 있지만, 특히 대규모 혹은 레거시 프로젝트에서는 아직 광범위한 프로덕션 도입에 이르지 못했다고 보는 시각이 많다.

C++ 모듈이 까다로운 이유를 보여 주기 위해, 먼저 모듈 가시성, export/import 의미론, 모듈과 헤더 혼용 문제 같은 흔한 함정을 드러내는 간단한 예제를 하나 제시한다. 이어서 순환 의존성 위험, 파티션 복잡성, 표준 라이브러리 지원의 불일치, 빌드 시스템 문제를 강조하는 두 번째 예제를 제공한다.

Clang 19 또는 MSVC(Visual Studio 2022 17.10+)를 사용하라. GCC 15도 동작할 수 있으나 신뢰도는 낮다. 제공된 CMakeLists.txt와 함께 CMake 3.28+를 사용하라. cmake -S . -B build, cmake --build build를 실행한다. main.cpp에서 문제 줄(예: subtract나 Point)을 주석 처리하면 컴파일된다. 그대로 두면 가시성 문제를 드러내는 오류가 발생한다.

이 예제는 모듈화된 라이브러리와 메인 프로그램을 포함하며, 누락된 export, 순환 의존성, 빌드 시스템 문제 같은 이슈를 부각한다. 까다로운 측면에 대한 설명은 코드 주석에 포함되어 있으며, 이후에 요약한다.

이미지 1

CMakeLists.txt

이 CMakeLists.txt는 모듈 전용 플래그(예: Clang의 -fmodules)와 실험적 CMake 기능이 필요함을 보여 준다. 대형 프로젝트에서는 모듈 의존성 스캐닝이 느리고 오류가 잦다. 컴파일러마다 설정이 달라 교차 플랫폼 빌드가 까다롭다. 안전하게 가려면 최신 빌드 시스템(CMake 3.28+)을 쓰고 구성을 철저히 테스트하라.

이미지 2

mathlib.cpp

mathlib.cpp에서 subtract 함수와 Point 구조체는 export되지 않았다. 모듈은 엄격한 가시성을 적용하기 때문에 main.cpp에서 이들을 사용하려 하면 컴파일 오류가 발생한다. 개발자들이 필요한 엔티티를 export하는 것을 종종 잊어버리며, 그 결과 "symbol not found" 같은 난해한 오류가 발생한다. 이 오류를 피하려면 export int subtract(int, int);, export struct Point;로 subtract와 Point를 명시적으로 export하라.

두 번째 문제: mathlib.cpp에서 <vector>를 include하면 문제가 생길 수 있다. 헤더는 본질적으로 모듈이 아니기 때문이다. main.cpp가 std::vector를 사용하려 하면, <vector>가 모듈로 import되지 않았기 때문에 엄격 모듈 모드(예: Clang의 -fmodules-strict)에서 실패할 수 있다. <iostream>에 대한 글로벌 모듈 프래그먼트(module : global;)는 일종의 우회책이지만 복잡성을 더한다. 컴파일러가 표준 라이브러리 모듈을 지원한다면 import <vector>;를 사용하거나, 모듈 인터페이스에서는 헤더 사용을 피하라.

이미지 3

main.cpp

main.cpp에서의 import 사용은 컴파일러가 표준 라이브러리 모듈을 지원한다고 가정한다. 그렇지 않다면(예: GCC 15의 불완전한 지원), #include로 되돌아가야 하며, 이는 모듈의 순도를 깨뜨리고 충돌 가능성을 낳는다.

export, import, 글로벌 모듈 프래그먼트 문법은 헤더에 익숙한 개발자들에게 낯설다. 예를 들어 누락된 export로 인해 “entity not declared in module purview” 같은 혼란스러운 메시지가 나오는데, 모듈에 대한 깊은 지식 없이는 디버깅이 어렵다.

C++ 모듈이 까다로운 이유를 더 보여 주기 위해, 추가적인 일반적 이슈를 강조하는 두 번째 예제를 제시한다. 이 예제는 순환 의존성 위험, 파티션 복잡성, 표준 라이브러리 지원의 불일치, 빌드 시스템 문제를 부각한다. 이는 C++ 모듈에 대한 커뮤니티의 핵심 불만과 맞닿아 있다.

이런 이슈는 엄격한 의미론, 미성숙한 도구, 컴파일러 간 가변성 때문에 모듈을 까다롭게 만든다. 모듈은 더 나은 캡슐화와 컴파일 속도를 약속하지만, 실무에서는 이러한 장애물 때문에 특히 복잡한 프로젝트에서 도입이 더디다.

이는 앞선 예제의 export 가시성과 헤더 상호작용 초점을 보완하는, C++ 커뮤니티에서 자주 제기되는 고충들이다. 예제는 두 개의 모듈, 하나의 메인 프로그램, 빌드 설정을 포함하며, 까다로운 부분을 설명하는 주석이 포함되어 있다.

이미지 4

CMakeLists.txt

이 CMakeLists.txt는 명시적인 모듈 소스 선언과 컴파일러별 플래그(예: Clang의 -fmodules, MSVC의 /experimental:module)가 필요하다. 모듈 파티션은 의존성 스캐닝을 복잡하게 만들며, 빌드 시스템이 math_ops:basic과 math_ops:impl을 제대로 해석하지 못할 수 있다.

잘못된 의존성 스캐닝은 BMI(Binary Module Interface) 파일 누락이나 잘못된 컴파일 순서로 이어져 빌드 실패를 야기한다. CMake는 다음과 같은 오류로 실패할 수 있다. “cannot find module math_ops:basic BMI.” 커뮤니티 논의에서 자주 지적되는 좌절 포인트로, CMake의 모듈 지원은 여전히 실험적이다.

이미지 5

math_ops.cpp

문제: 아래의 math_utils.cpp에서 print_complex가 실패하는 이유는 Complex가 위의 비-export 파티션인 math_ops:impl에 있기 때문이다.

math_ops 모듈은 코드를 구성하기 위해 파티션(math_ops:basic, math_ops:impl)을 사용한다. 하지만 파티션은 명시적 import(예: math_utils.cpp에서 math_ops:basic)가 필요하며, 구현 파티션에 있는 비-export 엔티티(예: Complex)는 접근할 수 없다. 이 때문에 math_utils.cpp와 main2.cpp(아래)에서 오류가 발생한다.

파티션은 대형 모듈을 구성하는 데 강력하지만, 장황한 문법과 엄격한 가시성 규칙을 수반한다. 인터페이스 파티션과 구현 파티션의 구분에 많은 개발자들이 어려움을 겪으며, "entity not found in module purview" 같은 오류를 일으키기 쉽다.

이미지 6

math_utils.cpp

math_utils.cpp의 import math_ops;는 math_ops가 math_utils를 import하는 경우 잠재적인 순환 의존성을 만든다. 만약 math_ops가 math_utils를 import하면, Clang은 “error: cyclic module dependency between math_ops and math_utils.”를 보고할 수 있다.

이 예제는 직접적인 순환을 피하지만, 실제 프로젝트에서는 모듈이 상호 의존하는 경우(예: 유틸리티 모듈이 코어 모듈의 타입을 필요로 하고, 반대로도 필요한 경우) 순환이 흔히 발생한다.

순환 의존성은 “module not found”나 “cyclic dependency detected” 같은 모호한 오류와 함께 컴파일 실패를 초래한다. 이를 해결하려면 코드를 재구성해야 하며, 제3의 모듈을 도입하거나 전방 선언을 사용하는 등 추가적인 복잡성이 따른다.

또 다른 이슈: GCC에서는 “error: module 'std.vector' not found.”가 나올 수 있다. 이는 math_utils.cpp의 import <vector>;가 컴파일러가 표준 라이브러리 모듈을 지원한다고 가정하기 때문이다.

2025년 8월 현재 Clang 19는 이를 지원하지만, GCC 15는 부분적이며 MSVC의 지원도 일관되지 않다. 컴파일러가 import <vector>를 지원하지 않으면 빌드가 실패하거나 #include <vector>로 되돌아가야 하며, 이는 모듈의 순도를 깨뜨린다.

개발자는 표준 라이브러리 모듈에 대한 컴파일러별 지원 상태를 확인해야 하므로 이식성이 떨어진다. 이는 GCC의 표준 라이브러리 모듈화가 더디다는 지적과 함께 소셜 미디어에서 자주 보이는 불만이다.

이미지 7

main2.cpp

main2.cpp나 math_utils.cpp에서 Complex에 접근하려다 발생하는 오류들은(예: “Complex not declared in module math_ops”) 모듈의 엄격한 캡슐화(비-export 엔티티의 은닉) 때문에 발생한다.

모듈 관련 오류를 디버깅하려면 모듈의 관할 범위(purview)와 export 규칙을 이해해야 하는데, 많은 개발자들에게 이는 직관적이지 않다. “끔찍한 오류 메시지”가 가장 큰 장애라는 지적이 소셜 미디어에서 빈번하다.

C++23은 명시적 import/export 제어와 헤더 임포트와의 통합 개선 등으로 모듈을 다듬었다. C++26(2026년 예상)은 표준 라이브러리 모듈화와 빌드 시스템 통합 같은 남은 고충을 해결할 가능성이 크다.

CMake와 Bazel 같은 도구가 성숙하고, 컴파일러들이 구현 세부에 합의해 가면서 더 넓은 도입이 기대된다.

소셜 미디어에서는 C++ 모듈의 완전한 채택에 회의적인 시각도 있지만, 결코 채택되지 않을 것이라고 단정하는 의견은 드물다. 생태계 미성숙, 도구의 불일치, 마이그레이션 비용, 순환 의존성 같은 이슈로 인해 단기간(5~10년) 내 광범위한 도입은 어렵다는 시각이 우세하다. 하지만 초기 도입자들 사이에서는 낙관론도 존재하며, 컴파일러(예: Clang 19, MSVC)와 빌드 시스템(예: CMake 3.28+)이 성숙해질수록 모듈이 점차 입지를 넓힐 것이라는 전망이 있다. 헤더는 특히 레거시 코드에서는 완전히 대체되기보다 모듈과 공존할 것으로 보인다.

지금, 모듈에 대한 나의 솔직한 의견은 이렇다. 보수적으로 접근하되, 개인 프로젝트에서 시도해 보고, 충분히 성숙할 때까지는 실무에서는 거리를 두자.