์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿ—๏ธ C++ ๋นŒ๋“œ ์‹œ์Šคํ…œ๊ณผ CMake ยท Make๋ฅผ ํ™œ์šฉํ•œ ํ”„๋กœ์ ํŠธ ๋นŒ๋“œ ๊ด€๋ฆฌ ์™„์ „ ์ •๋ณต
๐Ÿ› ๏ธ C++ ๋นŒ๋“œ ์‹œ์Šคํ…œ

๐Ÿ—๏ธ C++ ๋นŒ๋“œ ์‹œ์Šคํ…œ๊ณผ CMake ยท Make๋ฅผ ํ™œ์šฉํ•œ ํ”„๋กœ์ ํŠธ ๋นŒ๋“œ ๊ด€๋ฆฌ ์™„์ „ ์ •๋ณต

๋ณต์žกํ•œ ๋นŒ๋“œ ์„ธ๊ณ„, ์ด์ œ ์‰ฝ๊ณ  ์žฌ๋ฏธ์žˆ๊ฒŒ ๋งˆ์Šคํ„ฐํ•ด๋ณด์ž! ๐Ÿš€

์†Œ์Šค ์ฝ”๋“œ .cpp / .h ํŒŒ์ผ๋“ค CMake CMakeLists.txt ์„ค์ • ํŒŒ์ผ Make Makefile ๋นŒ๋“œ ๊ทœ์น™ ์ปดํŒŒ์ผ๋Ÿฌ g++ / clang++ ์ปดํŒŒ์ผ ์‹คํ–‰ ์‹คํ–‰ ํŒŒ์ผ ๋ฐ”์ด๋„ˆ๋ฆฌ ์™„์„ฑ! ๐ŸŽ‰ C++ ๋นŒ๋“œ ํŒŒ์ดํ”„๋ผ์ธ ์ „์ฒด ํ๋ฆ„ ์†Œ์Šค์ฝ”๋“œ โ†’ CMake โ†’ Make โ†’ ์ปดํŒŒ์ผ๋Ÿฌ โ†’ ์‹คํ–‰ํŒŒ์ผ ๐Ÿ”‘ CMake๋Š” ๋นŒ๋“œ ์„ค์ •์„ ์ƒ์„ฑํ•˜๊ณ , Make๋Š” ์‹ค์ œ ๋นŒ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์—ญํ• ์„ ๋‹ด๋‹นํ•ด! ๊ฐ ๋‹จ๊ณ„๊ฐ€ ์œ ๊ธฐ์ ์œผ๋กœ ์—ฐ๊ฒฐ๋˜์–ด ์ตœ์ข… ์‹คํ–‰ ํŒŒ์ผ์ด ๋งŒ๋“ค์–ด์ง€๋Š” ๊ตฌ์กฐ์•ผ ๐Ÿ˜Š 1 2 3 4 5
๐Ÿค” ๋นŒ๋“œ ์‹œ์Šคํ…œ์ด ๋ญ”๋ฐ? ์™œ ํ•„์š”ํ•ด?
๐Ÿ’ฌ "์•ผ C++ ์ฝ”๋“œ ์งฐ๋Š”๋ฐ ๊ทธ๋ƒฅ g++ main.cpp ํ•˜๋ฉด ๋˜๋Š” ๊ฑฐ ์•„๋‹ˆ์•ผ? ๋นŒ๋“œ ์‹œ์Šคํ…œ์ด ์™œ ํ•„์š”ํ•ด?"
๐Ÿค– "ํŒŒ์ผ์ด 10๊ฐœ๋ฉด ๋ชฐ๋ผ๋„... ํŒŒ์ผ์ด 100๊ฐœ, 1000๊ฐœ๊ฐ€ ๋˜๋ฉด? ์˜์กด์„ฑ์ด ๋ณต์žกํ•ด์ง€๋ฉด? ๊ทธ๋•Œ๋„ ๊ทธ ๋ง ํ•  ์ˆ˜ ์žˆ์–ด? ใ…‹ใ…‹ใ…‹"

๋งž์•„, ์ฒ˜์Œ C++ ๋ฐฐ์šธ ๋•Œ๋Š” ๊ทธ๋ƒฅ g++ main.cpp -o myapp ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋์ด์ž–์•„.
๊ทผ๋ฐ ์‹ค์ œ ํ”„๋กœ์ ํŠธ๋Š” ๊ทธ๋ ‡๊ฒŒ ๋‹จ์ˆœํ•˜์ง€ ์•Š์•„. ํŒŒ์ผ์ด ์ˆ˜์‹ญ ๊ฐœ, ์ˆ˜๋ฐฑ ๊ฐœ๊ฐ€ ๋˜๊ณ , ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋„ ๋ถ™๊ณ , ํ”Œ๋žซํผ๋„ ๋‹ฌ๋ผ์ง€๊ณ ... ์ด๊ฑธ ์ผ์ผ์ด ์†์œผ๋กœ ๊ด€๋ฆฌํ•˜๋ฉด ์ง„์งœ ์ง€์˜ฅ์ด ๋”ฐ๋กœ ์—†์Œ ๐Ÿ˜ฑ

๋นŒ๋“œ ์‹œ์Šคํ…œ(Build System)์ด๋ž€ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ํ”„๋กœ๊ทธ๋žจ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ „์ฒด ๊ณผ์ •์„ ์ž๋™ํ™”ํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๋„๊ตฌ์•ผ.
๋‹จ์ˆœํžˆ ์ปดํŒŒ์ผ๋งŒ ํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ์˜์กด์„ฑ ์ถ”์ , ์ฆ๋ถ„ ๋นŒ๋“œ(๋ณ€๊ฒฝ๋œ ํŒŒ์ผ๋งŒ ์žฌ์ปดํŒŒ์ผ), ๋งํ‚น, ์„ค์น˜ ๋“ฑ ๋ชจ๋“  ๊ณผ์ •์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์ค˜.

๐Ÿ’ก ๋นŒ๋“œ ์‹œ์Šคํ…œ์ด ํ•ด๊ฒฐํ•˜๋Š” ํ•ต์‹ฌ ๋ฌธ์ œ๋“ค

โ‘  ์˜์กด์„ฑ ๊ด€๋ฆฌ : A.cpp๊ฐ€ B.h๋ฅผ includeํ•˜๋ฉด, B.h๊ฐ€ ๋ฐ”๋€” ๋•Œ A.cpp๋„ ์žฌ์ปดํŒŒ์ผํ•ด์•ผ ํ•ด
โ‘ก ์ฆ๋ถ„ ๋นŒ๋“œ : 1000๊ฐœ ํŒŒ์ผ ์ค‘ 1๊ฐœ๋งŒ ๋ฐ”๊ฟจ๋Š”๋ฐ ์ „์ฒด ๋‹ค์‹œ ๋นŒ๋“œํ•˜๋ฉด ์‹œ๊ฐ„ ๋‚ญ๋น„ ๊ทน์‹ฌ
โ‘ข ํฌ๋กœ์Šค ํ”Œ๋žซํผ : Windows, Linux, macOS์—์„œ ๋™์ผํ•œ ์ฝ”๋“œ๋ฅผ ๋นŒ๋“œํ•ด์•ผ ํ•  ๋•Œ
โ‘ฃ ๋นŒ๋“œ ์„ค์ • ๊ด€๋ฆฌ : Debug/Release ๋ชจ๋“œ, ์ปดํŒŒ์ผ ์˜ต์…˜, ๋งํฌ ์˜ต์…˜ ๋“ฑ ์ฒด๊ณ„์  ๊ด€๋ฆฌ

C++ ์ƒํƒœ๊ณ„์—์„œ ๋Œ€ํ‘œ์ ์ธ ๋นŒ๋“œ ๋„๊ตฌ๋“ค์„ ๊ฐ„๋‹จํžˆ ์‚ดํŽด๋ณด๋ฉด:

๋„๊ตฌ ์—ญํ•  ํŠน์ง•
Make ๋นŒ๋“œ ์ž๋™ํ™” ์œ ๋‹‰์Šค ์ „ํ†ต์˜ ๋นŒ๋“œ ๋„๊ตฌ, Makefile ๊ธฐ๋ฐ˜
CMake ๋นŒ๋“œ ์‹œ์Šคํ…œ ์ƒ์„ฑ๊ธฐ ํฌ๋กœ์Šค ํ”Œ๋žซํผ, Makefile/Ninja ๋“ฑ ์ƒ์„ฑ
Ninja ๋นŒ๋“œ ์ž๋™ํ™” Make๋ณด๋‹ค ๋น ๋ฅธ ๋นŒ๋“œ ์†๋„
Bazel ๋นŒ๋“œ ์ž๋™ํ™” Google ๊ฐœ๋ฐœ, ๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ์šฉ
Meson ๋นŒ๋“œ ์‹œ์Šคํ…œ ์ƒ์„ฑ๊ธฐ Python ๊ธฐ๋ฐ˜, ๋น ๋ฅด๊ณ  ํ˜„๋Œ€์ 

์ด ์ค‘์—์„œ ์˜ค๋Š˜ ์šฐ๋ฆฌ๊ฐ€ ์ง‘์ค‘์ ์œผ๋กœ ํŒŒ๋ณผ ๊ฑด Make์™€ CMake์•ผ.
์ด ๋‘ ๊ฐœ๊ฐ€ C++ ์„ธ๊ณ„์—์„œ ๊ฐ€์žฅ ๋„๋ฆฌ ์“ฐ์ด๋Š” ์กฐํ•ฉ์ด๊ฑฐ๋“ . ํŠนํžˆ ์˜คํ”ˆ์†Œ์Šค ํ”„๋กœ์ ํŠธ์˜ ๋Œ€๋ถ€๋ถ„์ด CMake๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด์„œ ์ด๊ฑธ ๋ชจ๋ฅด๋ฉด ๋‚จ์˜ ์ฝ”๋“œ ๋นŒ๋“œํ•˜๋Š” ๊ฒƒ๋„ ํž˜๋“ค์–ด ใ…‹ใ…‹ใ…‹


๐Ÿ”ง Make์™€ Makefile - ๋นŒ๋“œ ์ž๋™ํ™”์˜ ์›์กฐ

Make๋Š” 1976๋…„์— Stuart Feldman์ด ๋งŒ๋“  ์œ ์„œ ๊นŠ์€ ๋นŒ๋“œ ๋„๊ตฌ์•ผ. ๊ฑฐ์˜ 50๋…„ ๋œ ๋„๊ตฌ์ธ๋ฐ ์•„์ง๋„ ํ˜„์—ญ์ด๋ผ๋Š” ๊ฒŒ ๋ ˆ์ „๋“œ ใ„ทใ„ท
GNU Make๋Š” ๊ทธ ์ค‘์—์„œ๋„ ๊ฐ€์žฅ ๋„๋ฆฌ ์“ฐ์ด๋Š” ๋ฒ„์ „์œผ๋กœ, Linux/macOS ํ™˜๊ฒฝ์—์„œ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณต๋ผ.

๐Ÿ“„ Makefile ๊ธฐ๋ณธ ๊ตฌ์กฐ

Make๋Š” Makefile์ด๋ผ๋Š” ํŒŒ์ผ์„ ์ฝ์–ด์„œ ๋นŒ๋“œ๋ฅผ ์ˆ˜ํ–‰ํ•ด.
Makefile์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ๋Š” ํƒ€๊ฒŸ(target) : ์˜์กด์„ฑ(dependencies) ํ˜•ํƒœ์•ผ.

# Makefile ๊ธฐ๋ณธ ๋ฌธ๋ฒ•
ํƒ€๊ฒŸ : ์˜์กด์„ฑ1 ์˜์กด์„ฑ2
	๋ช…๋ น์–ด   โ† ๋ฐ˜๋“œ์‹œ ํƒญ(Tab)์œผ๋กœ ๋“ค์—ฌ์“ฐ๊ธฐ!

# ์˜ˆ์‹œ
myapp : main.o utils.o
	g++ -o myapp main.o utils.o

main.o : main.cpp utils.h
	g++ -c main.cpp

utils.o : utils.cpp utils.h
	g++ -c utils.cpp
โš ๏ธ Makefile ์ตœ๋Œ€ ์ฃผ์˜์‚ฌํ•ญ!
๋ช…๋ น์–ด ์•ž์˜ ๋“ค์—ฌ์“ฐ๊ธฐ๋Š” ๋ฐ˜๋“œ์‹œ ํƒญ(Tab) ๋ฌธ์ž์—ฌ์•ผ ํ•ด. ์ŠคํŽ˜์ด์Šค๋กœ ํ•˜๋ฉด ์—๋Ÿฌ๋‚จ. ์ด๊ฑฐ ๋•Œ๋ฌธ์— ์ดˆ๋ณด์ž๋“ค์ด ์ง„์งœ ๋งŽ์ด ์‚ฝ์งˆํ•จ ใ…‹ใ…‹ใ…‹ ์—๋””ํ„ฐ ์„ค์ • ๊ผญ ํ™•์ธํ•ด!
๐Ÿ—๏ธ ์‹ค์ „ Makefile ์ž‘์„ฑํ•˜๊ธฐ

์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ ์“ธ ์ˆ˜ ์žˆ๋Š” ์ข€ ๋” ์™„์„ฑ๋„ ์žˆ๋Š” Makefile์„ ๋งŒ๋“ค์–ด๋ณด์ž.
๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ •ํ• ๊ฒŒ:

# ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ
myproject/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ main.cpp
โ”‚   โ”œโ”€โ”€ calculator.cpp
โ”‚   โ””โ”€โ”€ utils.cpp
โ”œโ”€โ”€ include/
โ”‚   โ”œโ”€โ”€ calculator.h
โ”‚   โ””โ”€โ”€ utils.h
โ”œโ”€โ”€ build/
โ””โ”€โ”€ Makefile
# ์‹ค์ „ Makefile ์˜ˆ์‹œ

# ๋ณ€์ˆ˜ ์ •์˜
CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra -I./include
LDFLAGS = 
TARGET = myapp
BUILD_DIR = build
SRC_DIR = src

# ์†Œ์Šค ํŒŒ์ผ ์ž๋™ ํƒ์ƒ‰
SRCS = $(wildcard $(SRC_DIR)/*.cpp)
# SRCS = src/main.cpp src/calculator.cpp src/utils.cpp

# ์˜ค๋ธŒ์ ํŠธ ํŒŒ์ผ ๊ฒฝ๋กœ ๋ณ€ํ™˜
OBJS = $(patsubst $(SRC_DIR)/%.cpp, $(BUILD_DIR)/%.o, $(SRCS))

# ๊ธฐ๋ณธ ํƒ€๊ฒŸ (all)
.PHONY: all clean debug release

all: $(BUILD_DIR)/$(TARGET)

# ๋งํ‚น ๋‹จ๊ณ„
$(BUILD_DIR)/$(TARGET): $(OBJS)
	@mkdir -p $(BUILD_DIR)
	$(CXX) $(LDFLAGS) -o $@ $^
	@echo "๋นŒ๋“œ ์™„๋ฃŒ! โ†’ $@"

# ์ปดํŒŒ์ผ ๋‹จ๊ณ„ (ํŒจํ„ด ๊ทœ์น™)
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
	@mkdir -p $(BUILD_DIR)
	$(CXX) $(CXXFLAGS) -c $< -o $@

# ๋””๋ฒ„๊ทธ ๋นŒ๋“œ
debug: CXXFLAGS += -g -DDEBUG
debug: all

# ๋ฆด๋ฆฌ์ฆˆ ๋นŒ๋“œ
release: CXXFLAGS += -O2 -DNDEBUG
release: all

# ํด๋ฆฐ
clean:
	rm -rf $(BUILD_DIR)
	@echo "๋นŒ๋“œ ํŒŒ์ผ ์‚ญ์ œ ์™„๋ฃŒ!"

# ์˜์กด์„ฑ ์ž๋™ ์ƒ์„ฑ (ํ—ค๋” ๋ณ€๊ฒฝ ๊ฐ์ง€)
-include $(OBJS:.o=.d)

$(BUILD_DIR)/%.d: $(SRC_DIR)/%.cpp
	@mkdir -p $(BUILD_DIR)
	$(CXX) $(CXXFLAGS) -MM -MT $(@:.d=.o) $< > $@
๐Ÿ” Makefile ํ•ต์‹ฌ ๋ฌธ๋ฒ• ์ •๋ฆฌ
๋ฌธ๋ฒ• ์˜๋ฏธ ์˜ˆ์‹œ
$@ ํ˜„์žฌ ํƒ€๊ฒŸ ์ด๋ฆ„ myapp
$^ ๋ชจ๋“  ์˜์กด์„ฑ ๋ชฉ๋ก main.o utils.o
$< ์ฒซ ๋ฒˆ์งธ ์˜์กด์„ฑ main.cpp
$(wildcard *.cpp) ํŒจํ„ด ๋งค์นญ ํŒŒ์ผ ๋ชฉ๋ก ๋ชจ๋“  .cpp ํŒŒ์ผ
$(patsubst ...) ํŒจํ„ด ์น˜ํ™˜ .cpp โ†’ .o ๋ณ€ํ™˜
.PHONY ๊ฐ€์งœ ํƒ€๊ฒŸ ์„ ์–ธ clean, all ๋“ฑ
@ ์ ‘๋‘์‚ฌ ๋ช…๋ น์–ด ์ถœ๋ ฅ ์ˆจ๊ธฐ๊ธฐ @echo "์™„๋ฃŒ"
๐Ÿ’ก .PHONY๊ฐ€ ์™œ ํ•„์š”ํ•ด?
๋งŒ์•ฝ ํ”„๋กœ์ ํŠธ ํด๋”์— clean์ด๋ผ๋Š” ์ด๋ฆ„์˜ ํŒŒ์ผ์ด ์‹ค์ œ๋กœ ์กด์žฌํ•˜๋ฉด, Make๋Š” "์ด๋ฏธ ์ตœ์‹  ์ƒํƒœ"๋ผ๊ณ  ํŒ๋‹จํ•ด์„œ clean ๋ช…๋ น์„ ์‹คํ–‰ ์•ˆ ํ•ด๋ฒ„๋ ค.
.PHONY: clean์œผ๋กœ ์„ ์–ธํ•˜๋ฉด "์ด๊ฑด ํŒŒ์ผ ์ด๋ฆ„์ด ์•„๋‹ˆ๋ผ ๋ช…๋ น์–ด์•ผ!"๋ผ๊ณ  ์•Œ๋ ค์ฃผ๋Š” ๊ฑฐ์•ผ.
โšก Make ๋ณ‘๋ ฌ ๋นŒ๋“œ

Make์˜ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋ณ‘๋ ฌ ๋นŒ๋“œ์•ผ. -j ์˜ต์…˜์œผ๋กœ ๋™์‹œ์— ์—ฌ๋Ÿฌ ํŒŒ์ผ์„ ์ปดํŒŒ์ผํ•  ์ˆ˜ ์žˆ์–ด:

# CPU ์ฝ”์–ด ์ˆ˜๋งŒํผ ๋ณ‘๋ ฌ ๋นŒ๋“œ
make -j4        # 4๊ฐœ ๋ณ‘๋ ฌ
make -j$(nproc) # CPU ์ฝ”์–ด ์ˆ˜ ์ž๋™ ๊ฐ์ง€ (Linux)
make -j8        # 8๊ฐœ ๋ณ‘๋ ฌ

# ๋นŒ๋“œ ์ง„ํ–‰ ์ƒํ™ฉ ํ™•์ธ
make -j4 VERBOSE=1

ํŒŒ์ผ์ด ๋งŽ์„์ˆ˜๋ก ๋ณ‘๋ ฌ ๋นŒ๋“œ์˜ ํšจ๊ณผ๊ฐ€ ๊ทน์ ์œผ๋กœ ๋‚˜ํƒ€๋‚˜. 100๊ฐœ ํŒŒ์ผ ๋นŒ๋“œํ•  ๋•Œ -j8 ์˜ต์…˜ ์“ฐ๋ฉด ๊ฑฐ์˜ 8๋ฐฐ ๋น ๋ฅด๊ฒŒ ๋นŒ๋“œ๋˜๋Š” ๊ฑฐ ์ฒด๊ฐํ•  ์ˆ˜ ์žˆ์–ด ใ„ทใ„ท


CMake ๋™์ž‘ ์›๋ฆฌ ํ•œ๋ˆˆ์— ๋ณด๊ธฐ CMakeLists.txt ํ”„๋กœ์ ํŠธ ์„ค์ • ๋นŒ๋“œ ๊ทœ์น™ ์ •์˜ ์˜์กด์„ฑ ๋ช…์‹œ โš™๏ธ CMake ์—”์ง„ ํ”Œ๋žซํผ ๊ฐ์ง€ ์ปดํŒŒ์ผ๋Ÿฌ ํƒ์ƒ‰ ์˜์กด์„ฑ ํ•ด๊ฒฐ ๋นŒ๋“œ ํŒŒ์ผ ์ƒ์„ฑ Makefile Linux / macOS Visual Studio .sln / .vcxproj Ninja build.ninja CMake๋Š” ํ•˜๋‚˜์˜ CMakeLists.txt๋กœ ์—ฌ๋Ÿฌ ํ”Œ๋žซํผ์˜ ๋นŒ๋“œ ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด์ค˜! Write Once, Build Everywhere ๐ŸŒ ์†Œ์Šค ํŒŒ์ผ๋“ค main.cpp *.cpp / *.h
๐Ÿš€ CMake - ํ˜„๋Œ€ C++ ๋นŒ๋“œ์˜ ํ‘œ์ค€

CMake๋Š” 2000๋…„์— Kitware์—์„œ ๊ฐœ๋ฐœํ•œ ํฌ๋กœ์Šค ํ”Œ๋žซํผ ๋นŒ๋“œ ์‹œ์Šคํ…œ ์ƒ์„ฑ๊ธฐ์•ผ.
์ค‘์š”ํ•œ ํฌ์ธํŠธ๋Š” CMake ์ž์ฒด๊ฐ€ ๋นŒ๋“œ๋ฅผ ํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ๋‹ค๋ฅธ ๋นŒ๋“œ ์‹œ์Šคํ…œ(Make, Ninja, Visual Studio ๋“ฑ)์„ ์œ„ํ•œ ๋นŒ๋“œ ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด์ค€๋‹ค๋Š” ๊ฑฐ์•ผ.

๐Ÿ’ฌ "๊ทธ๋Ÿผ CMake๊ฐ€ Makefile ๋งŒ๋“ค์–ด์ฃผ๋ฉด, ๊ทธ Makefile๋กœ Make๊ฐ€ ์‹ค์ œ ๋นŒ๋“œํ•˜๋Š” ๊ฑฐ์•ผ?"
๐Ÿค– "์ •ํ™•ํ•ด! CMake๋Š” ๋ฉ”ํƒ€ ๋นŒ๋“œ ์‹œ์Šคํ…œ์ด๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ์ด์œ ๊ฐ€ ๊ทธ๊ฑฐ์•ผ. ๋นŒ๋“œ ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๋Š” ๋นŒ๋“œ ์‹œ์Šคํ…œ ใ…‹ใ…‹ใ…‹"
๐Ÿ“ฆ CMake ์„ค์น˜ ๋ฐ ๋ฒ„์ „ ํ™•์ธ
# Ubuntu/Debian
sudo apt-get install cmake

# macOS (Homebrew)
brew install cmake

# Windows (winget)
winget install Kitware.CMake

# ๋ฒ„์ „ ํ™•์ธ
cmake --version
# cmake version 3.28.1 (์˜ˆ์‹œ)

# ์ตœ์†Œ ๋ฒ„์ „์€ 3.15 ์ด์ƒ ๊ถŒ์žฅ (Modern CMake)
๐Ÿ“ CMakeLists.txt ๊ธฐ๋ณธ ๊ตฌ์กฐ

CMake ํ”„๋กœ์ ํŠธ์˜ ํ•ต์‹ฌ์€ CMakeLists.txt ํŒŒ์ผ์ด์•ผ.
ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ์— ๋ฐ˜๋“œ์‹œ ์žˆ์–ด์•ผ ํ•˜๊ณ , ์„œ๋ธŒ ๋””๋ ‰ํ† ๋ฆฌ์—๋„ ์ถ”๊ฐ€๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด.

# CMakeLists.txt ๊ธฐ๋ณธ ์˜ˆ์‹œ

# CMake ์ตœ์†Œ ๋ฒ„์ „ ์š”๊ตฌ์‚ฌํ•ญ (ํ•„์ˆ˜!)
cmake_minimum_required(VERSION 3.15)

# ํ”„๋กœ์ ํŠธ ์„ ์–ธ
project(MyProject
    VERSION 1.0.0
    DESCRIPTION "๋‚˜์˜ ์ฒซ CMake ํ”„๋กœ์ ํŠธ"
    LANGUAGES CXX
)

# C++ ํ‘œ์ค€ ์„ค์ •
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# ์‹คํ–‰ ํŒŒ์ผ ํƒ€๊ฒŸ ์ƒ์„ฑ
add_executable(myapp
    src/main.cpp
    src/calculator.cpp
    src/utils.cpp
)

# ํ—ค๋” ํŒŒ์ผ ๊ฒฝ๋กœ ์ถ”๊ฐ€
target_include_directories(myapp PRIVATE
    ${CMAKE_SOURCE_DIR}/include
)

# ์ปดํŒŒ์ผ ์˜ต์…˜ ์ถ”๊ฐ€
target_compile_options(myapp PRIVATE
    -Wall
    -Wextra
)
๐Ÿƒ CMake ๋นŒ๋“œ ์‹คํ–‰ ๊ณผ์ •
1
๋นŒ๋“œ ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ (Out-of-source build)
mkdir build && cd build
์†Œ์Šค ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๊นจ๋—ํ•˜๊ฒŒ ์œ ์ง€ํ•˜๋Š” ๋ชจ๋ฒ” ์‚ฌ๋ก€์•ผ. ๋นŒ๋“œ ํŒŒ์ผ๋“ค์ด src ํด๋”์— ์„ž์ด์ง€ ์•Š์•„์„œ ๊น”๋”ํ•จ ๐Ÿ‘
2
CMake ๊ตฌ์„ฑ(Configure) ๋‹จ๊ณ„
cmake .. ๋˜๋Š” cmake -S . -B build
CMakeLists.txt๋ฅผ ์ฝ์–ด์„œ Makefile(๋˜๋Š” ๋‹ค๋ฅธ ๋นŒ๋“œ ํŒŒ์ผ)์„ ์ƒ์„ฑํ•ด
3
๋นŒ๋“œ ์‹คํ–‰
cmake --build . ๋˜๋Š” make
์ƒ์„ฑ๋œ ๋นŒ๋“œ ํŒŒ์ผ๋กœ ์‹ค์ œ ์ปดํŒŒ์ผ ๋ฐ ๋งํ‚น ์ˆ˜ํ–‰
4
์„ค์น˜ (์„ ํƒ์‚ฌํ•ญ)
cmake --install . --prefix /usr/local
๋นŒ๋“œ๋œ ํŒŒ์ผ์„ ์‹œ์Šคํ…œ์— ์„ค์น˜
# ์‹ค์ œ ๋นŒ๋“œ ๋ช…๋ น์–ด ํ๋ฆ„

# ๋ฐฉ๋ฒ• 1: ์ „ํ†ต์ ์ธ ๋ฐฉ์‹
mkdir build
cd build
cmake ..
make -j4

# ๋ฐฉ๋ฒ• 2: ํ˜„๋Œ€์ ์ธ ๋ฐฉ์‹ (CMake 3.13+)
cmake -S . -B build
cmake --build build --parallel 4

# ๋ฐฉ๋ฒ• 3: ๋นŒ๋“œ ํƒ€์ž… ์ง€์ •
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release

# ๋ฐฉ๋ฒ• 4: Ninja ์‚ฌ์šฉ
cmake -S . -B build -G Ninja
cmake --build build

๐ŸŽฏ Modern CMake - ํƒ€๊ฒŸ ๊ธฐ๋ฐ˜ ์ ‘๊ทผ๋ฒ•

CMake 3.0 ์ดํ›„๋ถ€ํ„ฐ Modern CMake๋ผ๋Š” ํŒจ๋Ÿฌ๋‹ค์ž„์ด ๋“ฑ์žฅํ–ˆ์–ด.
ํ•ต์‹ฌ ๊ฐœ๋…์€ "ํƒ€๊ฒŸ(Target) ์ค‘์‹ฌ์œผ๋กœ ์ƒ๊ฐํ•˜๋ผ"๋Š” ๊ฑฐ์•ผ.
์˜ˆ์ „ ๋ฐฉ์‹(Old CMake)๊ณผ ๋น„๊ตํ•ด๋ณด๋ฉด ์ฐจ์ด๊ฐ€ ํ™• ๋А๊ปด์ ธ:

# โŒ Old CMake ๋ฐฉ์‹ (๋น„์ถ”์ฒœ)
include_directories(include)        # ์ „์—ญ์œผ๋กœ ์˜ํ–ฅ
add_definitions(-DDEBUG)            # ์ „์—ญ์œผ๋กœ ์˜ํ–ฅ
link_libraries(mylib)               # ์ „์—ญ์œผ๋กœ ์˜ํ–ฅ

# โœ… Modern CMake ๋ฐฉ์‹ (๊ถŒ์žฅ)
target_include_directories(myapp PRIVATE include)
target_compile_definitions(myapp PRIVATE DEBUG)
target_link_libraries(myapp PRIVATE mylib)
๐Ÿ”‘ PRIVATE / PUBLIC / INTERFACE ํ‚ค์›Œ๋“œ

Modern CMake์—์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฐœ๋… ์ค‘ ํ•˜๋‚˜์•ผ. ์ด๊ฑธ ์ดํ•ดํ•˜๋ฉด ์˜์กด์„ฑ ์ „ํŒŒ๋ฅผ ์ œ๋Œ€๋กœ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์–ด:

ํ‚ค์›Œ๋“œ ํ˜„์žฌ ํƒ€๊ฒŸ ์˜์กดํ•˜๋Š” ํƒ€๊ฒŸ ์‚ฌ์šฉ ์‹œ์ 
PRIVATE โœ… ์ ์šฉ โŒ ๋ฏธ์ „ํŒŒ ๋‚ด๋ถ€ ๊ตฌํ˜„์—๋งŒ ํ•„์š”ํ•  ๋•Œ
PUBLIC โœ… ์ ์šฉ โœ… ์ „ํŒŒ ํ—ค๋”์—์„œ๋„ ์‚ฌ์šฉ๋˜๋Š” ๊ฒฝ์šฐ
INTERFACE โŒ ๋ฏธ์ ์šฉ โœ… ์ „ํŒŒ ํ—ค๋” ์ „์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
# PRIVATE / PUBLIC / INTERFACE ์‹ค์ „ ์˜ˆ์‹œ

# ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ƒ์„ฑ
add_library(mylib STATIC
    src/mylib.cpp
)

# mylib์˜ include ๋””๋ ‰ํ† ๋ฆฌ ์„ค์ •
target_include_directories(mylib
    PUBLIC  ${CMAKE_SOURCE_DIR}/include   # ์™ธ๋ถ€์—๋„ ๊ณต๊ฐœ
    PRIVATE ${CMAKE_SOURCE_DIR}/src       # ๋‚ด๋ถ€์—์„œ๋งŒ ์‚ฌ์šฉ
)

# ์‹คํ–‰ ํŒŒ์ผ์ด mylib์— ๋งํฌ
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE mylib)
# myapp์€ mylib์˜ PUBLIC include ๊ฒฝ๋กœ๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์†๋ฐ›์•„!
๐Ÿ“š ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํƒ€์ž… (STATIC / SHARED / INTERFACE)
# ์ •์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (.a / .lib)
add_library(mystaticlib STATIC
    src/lib1.cpp
    src/lib2.cpp
)

# ๋™์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (.so / .dll)
add_library(mysharedlib SHARED
    src/lib1.cpp
    src/lib2.cpp
)

# ํ—ค๋” ์ „์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (์ปดํŒŒ์ผ ์—†์Œ)
add_library(myheaderlib INTERFACE)
target_include_directories(myheaderlib INTERFACE
    ${CMAKE_SOURCE_DIR}/include
)

# BUILD_SHARED_LIBS ๋ณ€์ˆ˜๋กœ ๊ธฐ๋ณธ ํƒ€์ž… ์ œ์–ด
# cmake -DBUILD_SHARED_LIBS=ON ..
add_library(mylib  # ํƒ€์ž… ๋ฏธ์ง€์ • ์‹œ BUILD_SHARED_LIBS ๋”ฐ๋ฆ„
    src/lib.cpp
)

๐Ÿ” CMake ๋ณ€์ˆ˜์™€ ์บ์‹œ ์‹œ์Šคํ…œ

CMake์˜ ๋ณ€์ˆ˜ ์‹œ์Šคํ…œ์€ ์ฒ˜์Œ์—” ์ข€ ํ—ท๊ฐˆ๋ฆด ์ˆ˜ ์žˆ์–ด. ์ผ๋ฐ˜ ๋ณ€์ˆ˜์™€ ์บ์‹œ ๋ณ€์ˆ˜๊ฐ€ ๋”ฐ๋กœ ์žˆ๊ฑฐ๋“  ใ…‹ใ…‹ใ…‹
์ œ๋Œ€๋กœ ์ดํ•ดํ•˜๋ฉด ์—„์ฒญ ๊ฐ•๋ ฅํ•œ ์„ค์ • ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์–ด.

๐Ÿ“Œ ๋ณ€์ˆ˜ ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•
# ๋ณ€์ˆ˜ ์„ค์ •
set(MY_VAR "Hello CMake")
set(MY_LIST item1 item2 item3)

# ๋ณ€์ˆ˜ ์ฐธ์กฐ
message(STATUS "๋ณ€์ˆ˜๊ฐ’: ${MY_VAR}")
message(STATUS "๋ฆฌ์ŠคํŠธ: ${MY_LIST}")

# ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์ ‘๊ทผ
message(STATUS "HOME: $ENV{HOME}")

# ์œ ์šฉํ•œ ๋‚ด์žฅ ๋ณ€์ˆ˜๋“ค
message(STATUS "์†Œ์Šค ๋””๋ ‰ํ† ๋ฆฌ: ${CMAKE_SOURCE_DIR}")
message(STATUS "๋นŒ๋“œ ๋””๋ ‰ํ† ๋ฆฌ: ${CMAKE_BINARY_DIR}")
message(STATUS "ํ˜„์žฌ ์†Œ์Šค: ${CMAKE_CURRENT_SOURCE_DIR}")
message(STATUS "ํ˜„์žฌ ๋นŒ๋“œ: ${CMAKE_CURRENT_BINARY_DIR}")
message(STATUS "ํ”„๋กœ์ ํŠธ๋ช…: ${PROJECT_NAME}")
message(STATUS "ํ”„๋กœ์ ํŠธ ๋ฒ„์ „: ${PROJECT_VERSION}")
โš™๏ธ ์บ์‹œ ๋ณ€์ˆ˜ (Cache Variables)

์บ์‹œ ๋ณ€์ˆ˜๋Š” cmake-gui๋‚˜ ccmake์—์„œ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๊ณ , ์ปค๋งจ๋“œ๋ผ์ธ์—์„œ -D ์˜ต์…˜์œผ๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์–ด:

# ์บ์‹œ ๋ณ€์ˆ˜ ์„ ์–ธ
option(ENABLE_TESTS "ํ…Œ์ŠคํŠธ ๋นŒ๋“œ ํ™œ์„ฑํ™”" ON)
option(ENABLE_LOGGING "๋กœ๊น… ํ™œ์„ฑํ™”" OFF)

set(MY_INSTALL_PREFIX "/usr/local" CACHE PATH "์„ค์น˜ ๊ฒฝ๋กœ")
set(MAX_CONNECTIONS 100 CACHE STRING "์ตœ๋Œ€ ์—ฐ๊ฒฐ ์ˆ˜")

# ์บ์‹œ ๋ณ€์ˆ˜ ํƒ€์ž…: BOOL, PATH, FILEPATH, STRING, INTERNAL

# ์กฐ๊ฑด๋ถ€ ๋นŒ๋“œ
if(ENABLE_TESTS)
    add_subdirectory(tests)
    message(STATUS "ํ…Œ์ŠคํŠธ ๋นŒ๋“œ ํ™œ์„ฑํ™”๋จ")
endif()

if(ENABLE_LOGGING)
    target_compile_definitions(myapp PRIVATE ENABLE_LOGGING)
endif()

# ์ปค๋งจ๋“œ๋ผ์ธ์—์„œ ์„ค์ •
# cmake -DENABLE_TESTS=ON -DENABLE_LOGGING=OFF ..
๐Ÿ”„ ์ œ๋„ˆ๋ ˆ์ดํ„ฐ ํ‘œํ˜„์‹ (Generator Expressions)

CMake์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์•ผ. ๋นŒ๋“œ ํƒ€์ž…์ด๋‚˜ ํ”Œ๋žซํผ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๊ฐ’์„ ์„ค์ •ํ•  ๋•Œ ์‚ฌ์šฉํ•ด:

# ์ œ๋„ˆ๋ ˆ์ดํ„ฐ ํ‘œํ˜„์‹ ๊ธฐ๋ณธ ๋ฌธ๋ฒ•: $<์กฐ๊ฑด:๊ฐ’>

# ๋นŒ๋“œ ํƒ€์ž…์— ๋”ฐ๋ฅธ ์ปดํŒŒ์ผ ์˜ต์…˜
target_compile_options(myapp PRIVATE
    $<$:-g -O0 -DDEBUG>
    $<$:-O3 -DNDEBUG>
)

# ์ปดํŒŒ์ผ๋Ÿฌ์— ๋”ฐ๋ฅธ ์˜ต์…˜
target_compile_options(myapp PRIVATE
    $<$:-Wall -Wextra>
    $<$:/W4>
    $<$:-Wall -Wextra>
)

# ํ”Œ๋žซํผ์— ๋”ฐ๋ฅธ ์ •์˜
target_compile_definitions(myapp PRIVATE
    $<$:WINDOWS_BUILD>
    $<$:LINUX_BUILD>
)

์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—ฐ๋™ ๋ฐฉ๋ฒ• ๋น„๊ต find_package() ์‹œ์Šคํ…œ์— ์„ค์น˜๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํƒ์ƒ‰ โœ… OpenSSL, Boost, Qt ๋“ฑ โœ… ์‹œ์Šคํ…œ ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ €์™€ ์—ฐ๋™ โš ๏ธ ์‹œ์Šคํ…œ์— ๋ฏธ๋ฆฌ ์„ค์น˜ ํ•„์š” โš ๏ธ ๋ฒ„์ „ ๊ด€๋ฆฌ ์–ด๋ ค์›€ ๊ฐ€์žฅ ์ „ํ†ต์ ์ธ ๋ฐฉ๋ฒ• FetchContent ๋นŒ๋“œ ์‹œ ์ž๋™ ๋‹ค์šด๋กœ๋“œ โœ… CMake 3.11+ ๋‚ด์žฅ โœ… ๋ฒ„์ „ ๊ณ ์ • ๊ฐ€๋Šฅ โœ… ์„ค์น˜ ๋ถˆํ•„์š” โš ๏ธ ๋นŒ๋“œ ์‹œ๊ฐ„ ์ฆ๊ฐ€ ํ˜„๋Œ€์ ์ธ ๊ถŒ์žฅ ๋ฐฉ๋ฒ• vcpkg / Conan ์ „์šฉ ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ € โœ… ๋ฐฉ๋Œ€ํ•œ ํŒจํ‚ค์ง€ ์ƒํƒœ๊ณ„ โœ… ์˜์กด์„ฑ ์ž๋™ ํ•ด๊ฒฐ โœ… ํฌ๋กœ์Šค ํ”Œ๋žซํผ โš ๏ธ ๋ณ„๋„ ๋„๊ตฌ ์„ค์น˜ ํ•„์š” ๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ ์ถ”์ฒœ ์ƒํ™ฉ์— ๋งž๋Š” ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•˜๋Š” ๊ฒŒ ํ•ต์‹ฌ์ด์•ผ! ๐ŸŽฏ ์†Œ๊ทœ๋ชจ โ†’ find_package | ์ค‘๊ทœ๋ชจ โ†’ FetchContent | ๋Œ€๊ทœ๋ชจ โ†’ vcpkg/Conan ๐Ÿ” ๐Ÿ“ฅ ๐Ÿ“ฆ
๐Ÿ“ฆ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—ฐ๋™ํ•˜๊ธฐ

์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋Œ€๋ถ€๋ถ„์ด์•ผ.
CMake์—์„œ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์—ฐ๋™ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ํ•˜๋‚˜์”ฉ ์‚ดํŽด๋ณด์ž.

๐Ÿ” find_package() - ์‹œ์Šคํ…œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํƒ์ƒ‰
# OpenSSL ์—ฐ๋™ ์˜ˆ์‹œ
find_package(OpenSSL REQUIRED)

if(OpenSSL_FOUND)
    message(STATUS "OpenSSL ๋ฒ„์ „: ${OPENSSL_VERSION}")
    target_link_libraries(myapp PRIVATE OpenSSL::SSL OpenSSL::Crypto)
endif()

# Boost ์—ฐ๋™ ์˜ˆ์‹œ
find_package(Boost 1.70 REQUIRED COMPONENTS
    filesystem
    system
    thread
)
target_link_libraries(myapp PRIVATE
    Boost::filesystem
    Boost::system
    Boost::thread
)

# Qt6 ์—ฐ๋™ ์˜ˆ์‹œ
find_package(Qt6 REQUIRED COMPONENTS Core Widgets)
target_link_libraries(myapp PRIVATE Qt6::Core Qt6::Widgets)

# ์ปค์Šคํ…€ ๊ฒฝ๋กœ ์ง€์ •
find_package(MyLib REQUIRED
    PATHS /opt/mylib
    NO_DEFAULT_PATH
)
๐Ÿ“ฅ FetchContent - ์ž๋™ ๋‹ค์šด๋กœ๋“œ (Modern CMake ๊ถŒ์žฅ)

CMake 3.11๋ถ€ํ„ฐ ๋‚ด์žฅ๋œ FetchContent๋Š” ๋นŒ๋“œ ์‹œ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ž๋™์œผ๋กœ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ  ๋นŒ๋“œํ•ด์ค˜.
GitHub ํ”„๋กœ์ ํŠธ๋ฅผ ์ง์ ‘ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์–ด์„œ ์ง„์งœ ํŽธํ•จ ใ…‹ใ…‹ใ…‹

include(FetchContent)

# Google Test ๊ฐ€์ ธ์˜ค๊ธฐ
FetchContent_Declare(
    googletest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG        v1.14.0
)
FetchContent_MakeAvailable(googletest)

# spdlog (๋กœ๊น… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ) ๊ฐ€์ ธ์˜ค๊ธฐ
FetchContent_Declare(
    spdlog
    GIT_REPOSITORY https://github.com/gabime/spdlog.git
    GIT_TAG        v1.12.0
)
FetchContent_MakeAvailable(spdlog)

# nlohmann/json ๊ฐ€์ ธ์˜ค๊ธฐ
FetchContent_Declare(
    nlohmann_json
    GIT_REPOSITORY https://github.com/nlohmann/json.git
    GIT_TAG        v3.11.3
)
FetchContent_MakeAvailable(nlohmann_json)

# ์ด์ œ ๋ฐ”๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ!
target_link_libraries(myapp PRIVATE
    GTest::gtest_main
    spdlog::spdlog
    nlohmann_json::nlohmann_json
)
๐Ÿ’ก FetchContent ์˜คํ”„๋ผ์ธ ์บ์‹ฑ ํŒ
FETCHCONTENT_BASE_DIR ๋ณ€์ˆ˜๋กœ ๋‹ค์šด๋กœ๋“œ ์บ์‹œ ์œ„์น˜๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์–ด.
cmake -DFETCHCONTENT_BASE_DIR=/path/to/cache ..
ํ•œ ๋ฒˆ ๋‹ค์šด๋กœ๋“œํ•˜๋ฉด ๋‹ค์Œ ๋นŒ๋“œ์—์„œ๋Š” ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋น ๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ๋ผ!

๐Ÿงช ํ…Œ์ŠคํŠธ ํ†ตํ•ฉ - CTest์™€ Google Test

CMake์—๋Š” CTest๋ผ๋Š” ํ…Œ์ŠคํŠธ ์‹คํ–‰ ๋„๊ตฌ๊ฐ€ ๋‚ด์žฅ๋˜์–ด ์žˆ์–ด.
Google Test, Catch2 ๋“ฑ ๋‹ค์–‘ํ•œ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ์™€ ์‰ฝ๊ฒŒ ์—ฐ๋™ํ•  ์ˆ˜ ์žˆ์–ด.

๐Ÿ”ฌ Google Test ์—ฐ๋™ ์˜ˆ์‹œ
# CMakeLists.txt (๋ฃจํŠธ)
cmake_minimum_required(VERSION 3.15)
project(MyProject)

set(CMAKE_CXX_STANDARD 17)

# ํ…Œ์ŠคํŠธ ํ™œ์„ฑํ™”
enable_testing()

# ๋ฉ”์ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
add_library(mylib STATIC
    src/calculator.cpp
    src/utils.cpp
)
target_include_directories(mylib PUBLIC include)

# ์‹คํ–‰ ํŒŒ์ผ
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE mylib)

# ํ…Œ์ŠคํŠธ ์„œ๋ธŒ๋””๋ ‰ํ† ๋ฆฌ
add_subdirectory(tests)
# tests/CMakeLists.txt

include(FetchContent)
FetchContent_Declare(
    googletest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG v1.14.0
)
# Windows์—์„œ gtest์˜ ๋Ÿฐํƒ€์ž„ ์„ค์ • ๊ณต์œ 
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

# ํ…Œ์ŠคํŠธ ์‹คํ–‰ ํŒŒ์ผ
add_executable(mylib_tests
    test_calculator.cpp
    test_utils.cpp
)

target_link_libraries(mylib_tests PRIVATE
    mylib
    GTest::gtest_main
)

# CTest์— ํ…Œ์ŠคํŠธ ๋“ฑ๋ก
include(GoogleTest)
gtest_discover_tests(mylib_tests)
# ํ…Œ์ŠคํŠธ ์‹คํ–‰ ๋ฐฉ๋ฒ•

# ๋นŒ๋“œ ํ›„ ํ…Œ์ŠคํŠธ ์‹คํ–‰
cmake --build build
cd build
ctest                          # ๋ชจ๋“  ํ…Œ์ŠคํŠธ ์‹คํ–‰
ctest -V                       # ์ƒ์„ธ ์ถœ๋ ฅ
ctest -R "calculator"          # ํŠน์ • ํ…Œ์ŠคํŠธ๋งŒ ์‹คํ–‰
ctest --parallel 4             # ๋ณ‘๋ ฌ ํ…Œ์ŠคํŠธ
ctest --output-on-failure      # ์‹คํŒจ ์‹œ ์ถœ๋ ฅ ํ‘œ์‹œ

๐Ÿข ๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐํ™”

ํ”„๋กœ์ ํŠธ๊ฐ€ ์ปค์ง€๋ฉด CMakeLists.txt๋„ ์ฒด๊ณ„์ ์œผ๋กœ ๊ตฌ์กฐํ™”ํ•ด์•ผ ํ•ด.
add_subdirectory()๋ฅผ ํ™œ์šฉํ•œ ๊ณ„์ธต์  ๊ตฌ์กฐ๊ฐ€ ํ•ต์‹ฌ์ด์•ผ.

๐Ÿ“ ๊ถŒ์žฅ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ
MyProject/
โ”œโ”€โ”€ CMakeLists.txt          โ† ๋ฃจํŠธ (ํ”„๋กœ์ ํŠธ ์ „์ฒด ์„ค์ •)
โ”œโ”€โ”€ cmake/
โ”‚   โ”œโ”€โ”€ FindMyLib.cmake     โ† ์ปค์Šคํ…€ Find ๋ชจ๋“ˆ
โ”‚   โ””โ”€โ”€ CompilerOptions.cmake
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ CMakeLists.txt      โ† ๋ฉ”์ธ ์†Œ์Šค
โ”‚   โ”œโ”€โ”€ main.cpp
โ”‚   โ””โ”€โ”€ core/
โ”‚       โ”œโ”€โ”€ CMakeLists.txt  โ† ์ฝ”์–ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
โ”‚       โ”œโ”€โ”€ core.cpp
โ”‚       โ””โ”€โ”€ core.h
โ”œโ”€โ”€ libs/
โ”‚   โ”œโ”€โ”€ CMakeLists.txt
โ”‚   โ””โ”€โ”€ mylib/
โ”‚       โ”œโ”€โ”€ CMakeLists.txt  โ† ์„œ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
โ”‚       โ””โ”€โ”€ ...
โ”œโ”€โ”€ tests/
โ”‚   โ”œโ”€โ”€ CMakeLists.txt      โ† ํ…Œ์ŠคํŠธ
โ”‚   โ””โ”€โ”€ ...
โ”œโ”€โ”€ docs/
โ””โ”€โ”€ include/
    โ””โ”€โ”€ myproject/          โ† ๊ณต๊ฐœ ํ—ค๋”
๐Ÿ“‹ ๋ฃจํŠธ CMakeLists.txt ์™„์„ฑ ์˜ˆ์‹œ
cmake_minimum_required(VERSION 3.20)

project(MyProject
    VERSION 2.1.0
    DESCRIPTION "๋Œ€๊ทœ๋ชจ C++ ํ”„๋กœ์ ํŠธ ์˜ˆ์‹œ"
    HOMEPAGE_URL "https://example.com"
    LANGUAGES CXX
)

# ===== ์ „์—ญ ์„ค์ • =====
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# ๋นŒ๋“œ ํƒ€์ž… ๊ธฐ๋ณธ๊ฐ’
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "๋นŒ๋“œ ํƒ€์ž…" FORCE)
endif()

# cmake/ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๋ชจ๋“ˆ ๊ฒฝ๋กœ์— ์ถ”๊ฐ€
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")

# ===== ์˜ต์…˜ =====
option(BUILD_TESTS "ํ…Œ์ŠคํŠธ ๋นŒ๋“œ" ON)
option(BUILD_DOCS "๋ฌธ์„œ ์ƒ์„ฑ" OFF)
option(ENABLE_SANITIZERS "AddressSanitizer ํ™œ์„ฑํ™”" OFF)

# ===== ์ปดํŒŒ์ผ๋Ÿฌ ์˜ต์…˜ ๋ชจ๋“ˆ ํฌํ•จ =====
include(CompilerOptions)

# ===== ์„œ๋ธŒ๋””๋ ‰ํ† ๋ฆฌ =====
add_subdirectory(libs)
add_subdirectory(src)

if(BUILD_TESTS)
    enable_testing()
    add_subdirectory(tests)
endif()

# ===== ์„ค์น˜ ์„ค์ • =====
include(GNUInstallDirs)
install(TARGETS myapp
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
install(DIRECTORY include/
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
๐Ÿ›ก๏ธ cmake/CompilerOptions.cmake ๋ชจ๋“ˆ
# cmake/CompilerOptions.cmake

# ์ธํ„ฐํŽ˜์ด์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์ปดํŒŒ์ผ ์˜ต์…˜ ๋ฌถ๊ธฐ
add_library(project_options INTERFACE)
add_library(project_warnings INTERFACE)

# C++ ํ‘œ์ค€
target_compile_features(project_options INTERFACE cxx_std_20)

# ๊ฒฝ๊ณ  ์˜ต์…˜
if(MSVC)
    target_compile_options(project_warnings INTERFACE
        /W4 /WX /permissive-
    )
else()
    target_compile_options(project_warnings INTERFACE
        -Wall -Wextra -Wpedantic -Werror
        -Wshadow -Wnon-virtual-dtor
        -Wold-style-cast -Wcast-align
    )
endif()

# Sanitizer ์˜ต์…˜
if(ENABLE_SANITIZERS)
    target_compile_options(project_options INTERFACE
        -fsanitize=address,undefined
        -fno-omit-frame-pointer
    )
    target_link_options(project_options INTERFACE
        -fsanitize=address,undefined
    )
endif()

# ์‚ฌ์šฉ๋ฒ•: target_link_libraries(myapp PRIVATE project_options project_warnings)

๐Ÿ”„ CMake ํ”„๋ฆฌ์…‹ (CMakePresets.json)

CMake 3.19๋ถ€ํ„ฐ ๋„์ž…๋œ CMakePresets.json์€ ๋นŒ๋“œ ์„ค์ •์„ ํŒŒ์ผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค˜.
ํŒ€ ์ „์ฒด๊ฐ€ ๋™์ผํ•œ ๋นŒ๋“œ ์„ค์ •์„ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์–ด์„œ "๋‚ด ์ปดํ“จํ„ฐ์—์„œ๋Š” ๋๋Š”๋ฐ?" ๊ฐ™์€ ์ƒํ™ฉ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์–ด ใ…‹ใ…‹ใ…‹

// CMakePresets.json
{
    "version": 6,
    "cmakeMinimumRequired": {
        "major": 3,
        "minor": 19,
        "patch": 0
    },
    "configurePresets": [
        {
            "name": "base",
            "hidden": true,
            "generator": "Ninja",
            "binaryDir": "${sourceDir}/build/${presetName}",
            "cacheVariables": {
                "CMAKE_CXX_STANDARD": "20"
            }
        },
        {
            "name": "debug",
            "displayName": "Debug ๋นŒ๋“œ",
            "inherits": "base",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "Debug",
                "ENABLE_SANITIZERS": "ON"
            }
        },
        {
            "name": "release",
            "displayName": "Release ๋นŒ๋“œ",
            "inherits": "base",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "Release",
                "BUILD_TESTS": "OFF"
            }
        },
        {
            "name": "ci",
            "displayName": "CI ๋นŒ๋“œ",
            "inherits": "base",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "RelWithDebInfo",
                "BUILD_TESTS": "ON"
            }
        }
    ],
    "buildPresets": [
        {
            "name": "debug",
            "configurePreset": "debug",
            "jobs": 8
        },
        {
            "name": "release",
            "configurePreset": "release",
            "jobs": 8
        }
    ],
    "testPresets": [
        {
            "name": "debug",
            "configurePreset": "debug",
            "output": {"outputOnFailure": true},
            "execution": {"jobs": 4}
        }
    ]
}
# ํ”„๋ฆฌ์…‹ ์‚ฌ์šฉ๋ฒ•
cmake --list-presets                    # ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํ”„๋ฆฌ์…‹ ๋ชฉ๋ก
cmake --preset debug                    # debug ํ”„๋ฆฌ์…‹์œผ๋กœ ๊ตฌ์„ฑ
cmake --build --preset debug            # debug ํ”„๋ฆฌ์…‹์œผ๋กœ ๋นŒ๋“œ
ctest --preset debug                    # debug ํ”„๋ฆฌ์…‹์œผ๋กœ ํ…Œ์ŠคํŠธ

โšก ๋นŒ๋“œ ์ตœ์ ํ™” ํŒ

๋นŒ๋“œ๊ฐ€ ๋А๋ฆฌ๋ฉด ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ์ด ํ™• ๋–จ์–ด์ง€์ž–์•„. ๋นŒ๋“œ ์†๋„๋ฅผ ๋†’์ด๋Š” ์‹ค์šฉ์ ์ธ ํŒ๋“ค์„ ์•Œ์•„๋ณด์ž!

๐Ÿš€ ccache - ์ปดํŒŒ์ผ ์บ์‹ฑ

ccache๋Š” ์ปดํŒŒ์ผ ๊ฒฐ๊ณผ๋ฅผ ์บ์‹ฑํ•ด์„œ ๋™์ผํ•œ ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ์ปดํŒŒ์ผํ•  ๋•Œ ์บ์‹œ์—์„œ ๊ฐ€์ ธ์™€.
ํด๋ฆฐ ๋นŒ๋“œ๋ฅผ ์ž์ฃผ ํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์—์„œ ์—„์ฒญ๋‚œ ์‹œ๊ฐ„ ์ ˆ์•ฝ์ด ๋ผ:

# ccache ์„ค์น˜
sudo apt-get install ccache  # Ubuntu
brew install ccache          # macOS

# CMakeLists.txt์—์„œ ccache ํ™œ์„ฑํ™”
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
    set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
    set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
    message(STATUS "ccache ํ™œ์„ฑํ™”: ${CCACHE_PROGRAM}")
endif()

# ๋˜๋Š” ์ปค๋งจ๋“œ๋ผ์ธ์—์„œ
cmake -DCMAKE_CXX_COMPILER_LAUNCHER=ccache ..

# ccache ํ†ต๊ณ„ ํ™•์ธ
ccache --show-stats
โšก Unity Build (Jumbo Build)

์—ฌ๋Ÿฌ .cpp ํŒŒ์ผ์„ ํ•˜๋‚˜๋กœ ํ•ฉ์ณ์„œ ์ปดํŒŒ์ผํ•˜๋Š” ๊ธฐ๋ฒ•์ด์•ผ. ํ—ค๋” ํŒŒ์‹ฑ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์ค„์—ฌ์„œ ๋นŒ๋“œ๊ฐ€ ๋นจ๋ผ์ ธ:

# CMake 3.16+ Unity Build ์ง€์›
set_target_properties(myapp PROPERTIES
    UNITY_BUILD ON
    UNITY_BUILD_BATCH_SIZE 10  # 10๊ฐœ์”ฉ ๋ฌถ์–ด์„œ ์ปดํŒŒ์ผ
)

# ์ „์—ญ์œผ๋กœ ํ™œ์„ฑํ™”
set(CMAKE_UNITY_BUILD ON)

# ํŠน์ • ํŒŒ์ผ ์ œ์™ธ (ODR ์œ„๋ฐ˜ ๊ฐ€๋Šฅ์„ฑ ์žˆ๋Š” ํŒŒ์ผ)
set_source_files_properties(problematic.cpp PROPERTIES
    SKIP_UNITY_BUILD_INCLUSION ON
)
๐Ÿ”ฅ Precompiled Headers (PCH)
# CMake 3.16+ PCH ์ง€์›
# pch.h ํŒŒ์ผ ์ƒ์„ฑ
target_precompile_headers(myapp PRIVATE
    pch.h
)

# ๋˜๋Š” ์ง์ ‘ ํ—ค๋” ์ง€์ •
target_precompile_headers(myapp PRIVATE
    <vector>
    <string>
    <memory>
    <algorithm>
    <iostream>
    "include/common.h"
)

# ์—ฌ๋Ÿฌ ํƒ€๊ฒŸ ๊ฐ„ PCH ๊ณต์œ 
target_precompile_headers(myapp REUSE_FROM mylib)
๐ŸŽ๏ธ Ninja ๋นŒ๋“œ ์‹œ์Šคํ…œ ์‚ฌ์šฉ
# Ninja ์„ค์น˜
sudo apt-get install ninja-build  # Ubuntu
brew install ninja                # macOS

# CMake์—์„œ Ninja ์‚ฌ์šฉ
cmake -G Ninja -S . -B build
cmake --build build

# Ninja๋Š” Make๋ณด๋‹ค ์ผ๋ฐ˜์ ์œผ๋กœ 2~3๋ฐฐ ๋น ๋ฅธ ๋นŒ๋“œ ์†๋„๋ฅผ ๋ณด์—ฌ์ค˜!
์ตœ์ ํ™” ๊ธฐ๋ฒ• ํšจ๊ณผ ์ ์šฉ ๋‚œ์ด๋„
๋ณ‘๋ ฌ ๋นŒ๋“œ (-j) โญโญโญโญโญ ๋งค์šฐ ์‰ฌ์›€
ccache โญโญโญโญโญ ์‰ฌ์›€
Ninja ์‚ฌ์šฉ โญโญโญโญ ์‰ฌ์›€
PCH (๋ฏธ๋ฆฌ ์ปดํŒŒ์ผ๋œ ํ—ค๋”) โญโญโญโญ ๋ณดํ†ต
Unity Build โญโญโญ ๋ณดํ†ต
๋ถ„์‚ฐ ๋นŒ๋“œ (distcc) โญโญโญโญโญ ์–ด๋ ค์›€

๐Ÿ”ง CMake ๋””๋ฒ„๊น…๊ณผ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

CMake ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๊ฐ€ ์ฒ˜์Œ์—” ์ง„์งœ ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ค์›Œ ใ…‹ใ…‹ใ…‹
์ž์ฃผ ๋งŒ๋‚˜๋Š” ๋ฌธ์ œ๋“ค๊ณผ ํ•ด๊ฒฐ๋ฒ•์„ ์ •๋ฆฌํ•ด๋ดค์–ด.

๐Ÿ” CMake ๋””๋ฒ„๊น… ๋ช…๋ น์–ด
# ์ƒ์„ธ ์ถœ๋ ฅ ๋ชจ๋“œ
cmake -S . -B build --log-level=DEBUG
cmake -S . -B build --trace
cmake -S . -B build --trace-expand

# ๋ณ€์ˆ˜ ๊ฐ’ ํ™•์ธ
cmake -S . -B build -L    # ์บ์‹œ ๋ณ€์ˆ˜ ๋ชฉ๋ก
cmake -S . -B build -LA   # ๊ณ ๊ธ‰ ์บ์‹œ ๋ณ€์ˆ˜ ํฌํ•จ
cmake -S . -B build -LH   # ๋„์›€๋ง ํฌํ•จ

# CMakeLists.txt ์•ˆ์—์„œ ๋””๋ฒ„๊น…
message(STATUS "๋ณ€์ˆ˜ ํ™•์ธ: ${MY_VAR}")
message(WARNING "๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€")
message(FATAL_ERROR "์—๋Ÿฌ ๋ฐœ์ƒ - ๋นŒ๋“œ ์ค‘๋‹จ")

# ํƒ€๊ฒŸ ์†์„ฑ ํ™•์ธ
get_target_property(INCLUDES myapp INCLUDE_DIRECTORIES)
message(STATUS "Include ๊ฒฝ๋กœ: ${INCLUDES}")

# ๋นŒ๋“œ ์ƒ์„ธ ์ถœ๋ ฅ
cmake --build build --verbose
make VERBOSE=1
๐Ÿšจ ์ž์ฃผ ๋งŒ๋‚˜๋Š” ์—๋Ÿฌ์™€ ํ•ด๊ฒฐ๋ฒ•
โš ๏ธ ์—๋Ÿฌ: "Could not find package XXX"
โ†’ find_package(XXX)๊ฐ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ชป ์ฐพ๋Š” ๊ฒฝ์šฐ
โ†’ ํ•ด๊ฒฐ: cmake -DCMAKE_PREFIX_PATH=/path/to/lib .. ๋˜๋Š” ํŒจํ‚ค์ง€ ์„ค์น˜ ํ™•์ธ
โ†’
cmake --find-package -DNAME=XXX -DCOMPILER_ID=GNU -DLANGUAGE=CXX -DMODE=EXIST
โš ๏ธ ์—๋Ÿฌ: "target_link_libraries called with wrong number of arguments"
โ†’ Modern CMake์—์„œ PRIVATE/PUBLIC/INTERFACE ํ‚ค์›Œ๋“œ ๋ˆ„๋ฝ
โ†’ ํ•ด๊ฒฐ: target_link_libraries(myapp PRIVATE mylib) ํ˜•ํƒœ๋กœ ์ˆ˜์ •
โš ๏ธ ์—๋Ÿฌ: "undefined reference to ..."
โ†’ ๋งํ‚น ๋‹จ๊ณ„์—์„œ ์‹ฌ๋ณผ์„ ๋ชป ์ฐพ๋Š” ๊ฒฝ์šฐ
โ†’ ํ•ด๊ฒฐ: target_link_libraries()์— ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”๊ฐ€
โ†’ ๋งํฌ ์ˆœ์„œ ํ™•์ธ (์˜์กดํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋’ค์— ์™€์•ผ ํ•จ)
# CMake ์บ์‹œ ์ดˆ๊ธฐํ™” (์„ค์ • ๊ผฌ์˜€์„ ๋•Œ)
rm -rf build/CMakeCache.txt build/CMakeFiles/
# ๋˜๋Š”
rm -rf build/
cmake -S . -B build  # ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ

# ํŠน์ • ์บ์‹œ ๋ณ€์ˆ˜๋งŒ ์‚ญ์ œ
cmake -U MY_VARIABLE -S . -B build

๐Ÿ”„ CI/CD ํŒŒ์ดํ”„๋ผ์ธ๊ณผ CMake

ํ˜„๋Œ€ ๊ฐœ๋ฐœ์—์„œ CI/CD๋Š” ํ•„์ˆ˜์ž–์•„. CMake ํ”„๋กœ์ ํŠธ๋ฅผ GitHub Actions์™€ ์—ฐ๋™ํ•˜๋Š” ์˜ˆ์‹œ๋ฅผ ๋ณด์—ฌ์ค„๊ฒŒ:

# .github/workflows/build.yml
name: C++ CMake ๋นŒ๋“œ ๋ฐ ํ…Œ์ŠคํŠธ

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        build_type: [Debug, Release]
        
    runs-on: ${{ matrix.os }}
    
    steps:
    - uses: actions/checkout@v4
    
    - name: ccache ์„ค์ •
      uses: hendrikmuhs/ccache-action@v1.2
      
    - name: CMake ๊ตฌ์„ฑ
      run: |
        cmake -S . -B build \
          -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
          -DBUILD_TESTS=ON \
          -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
          
    - name: ๋นŒ๋“œ
      run: cmake --build build --parallel 4
      
    - name: ํ…Œ์ŠคํŠธ
      working-directory: build
      run: ctest --output-on-failure --parallel 4
๐Ÿ’ก ์žฌ๋Šฅ๋„ท์—์„œ CMake ๊ด€๋ จ ๋„์›€ ๋ฐ›๊ธฐ
CMake ์„ค์ •์ด ๋ณต์žกํ•˜๊ฑฐ๋‚˜ ํฌ๋กœ์Šค ํ”Œ๋žซํผ ๋นŒ๋“œ ํ™˜๊ฒฝ ๊ตฌ์ถ•์ด ์–ด๋ ค์šธ ๋•Œ, ์žฌ๋Šฅ๋„ท์—์„œ C++ ๋นŒ๋“œ ์‹œ์Šคํ…œ ์ „๋ฌธ๊ฐ€๋ฅผ ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ์–ด. ๋‹ค์–‘ํ•œ ๊ฐœ๋ฐœ ์žฌ๋Šฅ์„ ๊ฐ€์ง„ ๋ถ„๋“ค์ด ํ”„๋กœ์ ํŠธ ๋นŒ๋“œ ํ™˜๊ฒฝ ์„ค์ •๋ถ€ํ„ฐ ์ตœ์ ํ™”๊นŒ์ง€ ๋„์›€์„ ์ค„ ์ˆ˜ ์žˆ๊ฑฐ๋“ !

์™„์ „ํ•œ CMake ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ ํ•œ๋ˆˆ์— ๋ณด๊ธฐ ๐Ÿ“ MyProject/ CMakeLists.txt (๋ฃจํŠธ) ๐Ÿ“ src/ CMakeLists.txt main.cpp *.cpp ์†Œ์Šค๋“ค ๐Ÿ“ include/ ๊ณต๊ฐœ ํ—ค๋” ํŒŒ์ผ *.h / *.hpp API ์ธํ„ฐํŽ˜์ด์Šค ๐Ÿ“ cmake/ Find๋ชจ๋“ˆ.cmake CompilerOptions ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋ชจ๋“ˆ ๐Ÿ“ tests/ CMakeLists.txt test_*.cpp Google Test ๐Ÿ“ build/ ์ƒ์„ฑ๋œ Makefile ์˜ค๋ธŒ์ ํŠธ ํŒŒ์ผ ์ตœ์ข… ์‹คํ–‰ํŒŒ์ผ ํ•ต์‹ฌ ํŒŒ์ผ๋“ค CMakeLists.txt ๋นŒ๋“œ ์„ค์ •์˜ ํ•ต์‹ฌ CMakePresets.json ๋นŒ๋“œ ํ”„๋ฆฌ์…‹ ๊ด€๋ฆฌ .github/workflows CI/CD ์ž๋™ํ™” vcpkg.json / conanfile ํŒจํ‚ค์ง€ ์˜์กด์„ฑ ์ฒด๊ณ„์ ์ธ ๊ตฌ์กฐ๊ฐ€ ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ๊ฒฐ์ •ํ•ด! ๐Ÿ—๏ธ ์ฒ˜์Œ๋ถ€ํ„ฐ ์ž˜ ์žก์•„๋‘๋ฉด ๋‚˜์ค‘์— ๊ณ ์ƒ ์•ˆ ํ•ด๋„ ๋ผ ๐Ÿ˜Š
๐ŸŽ“ ์‹ค์ „ ์™„์„ฑ ์˜ˆ์ œ - ๊ณ„์‚ฐ๊ธฐ ํ”„๋กœ์ ํŠธ

์ง€๊ธˆ๊นŒ์ง€ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์ข…ํ•ฉํ•ด์„œ ์‹ค์ œ๋กœ ๋™์ž‘ํ•˜๋Š” ์™„์„ฑ๋œ CMake ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž!
๊ฐ„๋‹จํ•œ ๊ณ„์‚ฐ๊ธฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ + ์‹คํ–‰ ํŒŒ์ผ + ํ…Œ์ŠคํŠธ ๊ตฌ์กฐ์•ผ.

๐Ÿ“ ํ”„๋กœ์ ํŠธ ํŒŒ์ผ ๊ตฌ์กฐ
calculator_project/
โ”œโ”€โ”€ CMakeLists.txt
โ”œโ”€โ”€ CMakePresets.json
โ”œโ”€โ”€ include/
โ”‚   โ””โ”€โ”€ calculator/
โ”‚       โ””โ”€โ”€ calculator.h
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ CMakeLists.txt
โ”‚   โ”œโ”€โ”€ main.cpp
โ”‚   โ””โ”€โ”€ calculator.cpp
โ””โ”€โ”€ tests/
    โ”œโ”€โ”€ CMakeLists.txt
    โ””โ”€โ”€ test_calculator.cpp
๐Ÿ“‹ include/calculator/calculator.h
#pragma once
#include <stdexcept>

namespace calc {

class Calculator {
public:
    double add(double a, double b) const;
    double subtract(double a, double b) const;
    double multiply(double a, double b) const;
    double divide(double a, double b) const;
    // 0์œผ๋กœ ๋‚˜๋ˆ„๋ฉด std::invalid_argument ๋˜์ง
};

} // namespace calc
๐Ÿ“‹ src/calculator.cpp
#include "calculator/calculator.h"

namespace calc {

double Calculator::add(double a, double b) const {
    return a + b;
}

double Calculator::subtract(double a, double b) const {
    return a - b;
}

double Calculator::multiply(double a, double b) const {
    return a * b;
}

double Calculator::divide(double a, double b) const {
    if (b == 0.0) {
        throw std::invalid_argument("0์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์—†์–ด์š”!");
    }
    return a / b;
}

} // namespace calc
๐Ÿ“‹ ๋ฃจํŠธ CMakeLists.txt
cmake_minimum_required(VERSION 3.20)

project(CalculatorProject
    VERSION 1.0.0
    DESCRIPTION "CMake ์‹ค์ „ ์˜ˆ์ œ - ๊ณ„์‚ฐ๊ธฐ"
    LANGUAGES CXX
)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# ccache ์ž๋™ ๊ฐ์ง€
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
    set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
endif()

option(BUILD_TESTS "ํ…Œ์ŠคํŠธ ๋นŒ๋“œ" ON)

# ๊ณ„์‚ฐ๊ธฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
add_library(calculator STATIC
    src/calculator.cpp
)
target_include_directories(calculator PUBLIC
    ${CMAKE_SOURCE_DIR}/include
)
target_compile_options(calculator PRIVATE
    $<$<CXX_COMPILER_ID:GNU,Clang>:-Wall -Wextra>
    $<$<CXX_COMPILER_ID:MSVC>:/W4>
)

# ์‹คํ–‰ ํŒŒ์ผ
add_executable(calc_app src/main.cpp)
target_link_libraries(calc_app PRIVATE calculator)

# ํ…Œ์ŠคํŠธ
if(BUILD_TESTS)
    enable_testing()
    add_subdirectory(tests)
endif()

# ์„ค์น˜
include(GNUInstallDirs)
install(TARGETS calc_app RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
๐Ÿ“‹ tests/CMakeLists.txt
include(FetchContent)

FetchContent_Declare(
    googletest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG v1.14.0
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

add_executable(calculator_tests
    test_calculator.cpp
)

target_link_libraries(calculator_tests PRIVATE
    calculator
    GTest::gtest_main
)

include(GoogleTest)
gtest_discover_tests(calculator_tests)
๐Ÿ“‹ tests/test_calculator.cpp
#include <gtest/gtest.h>
#include "calculator/calculator.h"

class CalculatorTest : public ::testing::Test {
protected:
    calc::Calculator calc;
};

TEST_F(CalculatorTest, ๋ง์…ˆ_๊ธฐ๋ณธํ…Œ์ŠคํŠธ) {
    EXPECT_DOUBLE_EQ(calc.add(2.0, 3.0), 5.0);
    EXPECT_DOUBLE_EQ(calc.add(-1.0, 1.0), 0.0);
}

TEST_F(CalculatorTest, ๋บ„์…ˆ_๊ธฐ๋ณธํ…Œ์ŠคํŠธ) {
    EXPECT_DOUBLE_EQ(calc.subtract(5.0, 3.0), 2.0);
}

TEST_F(CalculatorTest, ๊ณฑ์…ˆ_๊ธฐ๋ณธํ…Œ์ŠคํŠธ) {
    EXPECT_DOUBLE_EQ(calc.multiply(4.0, 3.0), 12.0);
}

TEST_F(CalculatorTest, ๋‚˜๋ˆ—์…ˆ_๊ธฐ๋ณธํ…Œ์ŠคํŠธ) {
    EXPECT_DOUBLE_EQ(calc.divide(10.0, 2.0), 5.0);
}

TEST_F(CalculatorTest, ๋‚˜๋ˆ—์…ˆ_0์œผ๋กœ๋‚˜๋ˆ„๊ธฐ_์˜ˆ์™ธ๋ฐœ์ƒ) {
    EXPECT_THROW(calc.divide(10.0, 0.0), std::invalid_argument);
}
๐Ÿš€ ๋นŒ๋“œ ๋ฐ ์‹คํ–‰
# ๋นŒ๋“œ
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build --parallel 4

# ์‹คํ–‰
./build/calc_app

# ํ…Œ์ŠคํŠธ
cd build
ctest --output-on-failure

# ๋˜๋Š” ์ง์ ‘ ์‹คํ–‰
./build/tests/calculator_tests

๐Ÿ“Š CMake vs Make ์ง์ ‘ ๋น„๊ต

"๊ทธ๋ž˜์„œ CMake ์“ฐ๋Š” ๊ฒŒ ๋‚˜์•„? Make ์“ฐ๋Š” ๊ฒŒ ๋‚˜์•„?" ๋ผ๋Š” ์งˆ๋ฌธ์— ๋Œ€ํ•œ ๋ช…ํ™•ํ•œ ๋‹ต์„ ๋“œ๋ฆด๊ฒŒ:

๋น„๊ต ํ•ญ๋ชฉ Make (Makefile) CMake
ํ•™์Šต ๊ณก์„  ์ƒ๋Œ€์ ์œผ๋กœ ๋‚ฎ์Œ ์ดˆ๊ธฐ ์ง„์ž… ์žฅ๋ฒฝ ์žˆ์Œ
ํฌ๋กœ์Šค ํ”Œ๋žซํผ โŒ Unix ์ค‘์‹ฌ โœ… ์™„์ „ ์ง€์›
IDE ํ†ตํ•ฉ ์ œํ•œ์  โœ… VS, CLion, VSCode ๋“ฑ
์˜์กด์„ฑ ๊ด€๋ฆฌ ์ˆ˜๋™ ๊ด€๋ฆฌ โœ… ์ž๋™ํ™”
๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ ๋ณต์žกํ•ด์ง โœ… ๊ณ„์ธต์  ๊ตฌ์กฐ ์ง€์›
์ƒํƒœ๊ณ„ ์„ฑ์ˆ™ํ•จ โœ… ๋งค์šฐ ํ™œ๋ฐœ
๋นŒ๋“œ ์†๋„ ๋ณดํ†ต Ninja ์—ฐ๋™ ์‹œ ๋น ๋ฆ„
๋””๋ฒ„๊น… ์ง๊ด€์  ๋ณต์žกํ•  ์ˆ˜ ์žˆ์Œ
๐Ÿ’ฌ "๊ฒฐ๋ก ์ ์œผ๋กœ ๋ญ˜ ์จ์•ผ ํ•ด?"
๐Ÿค– "์‹ ๊ทœ ํ”„๋กœ์ ํŠธ๋ผ๋ฉด ๋ฌด์กฐ๊ฑด CMake! ๊ธฐ์กด Makefile ํ”„๋กœ์ ํŠธ ์œ ์ง€๋ณด์ˆ˜๋ผ๋ฉด Make ๊ณ„์† ์จ๋„ ๋ผ. ๊ทผ๋ฐ ํฌ๋กœ์Šค ํ”Œ๋žซํผ์ด๋‚˜ ํŒ€ ํ˜‘์—…์ด ํ•„์š”ํ•˜๋ฉด CMake๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฐ•๋ ฅ ์ถ”์ฒœ์ด์•ผ ใ…‹ใ…‹ใ…‹"
๐Ÿ’ก CMake ํ•™์Šต ๋กœ๋“œ๋งต

1๋‹จ๊ณ„ : cmake_minimum_required, project, add_executable ๊ธฐ๋ณธ ์ตํžˆ๊ธฐ
2๋‹จ๊ณ„ : target_* ํ•จ์ˆ˜๋“ค, PRIVATE/PUBLIC/INTERFACE ์ดํ•ด
3๋‹จ๊ณ„ : find_package, FetchContent๋กœ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—ฐ๋™
4๋‹จ๊ณ„ : ๋ฉ€ํ‹ฐ ์„œ๋ธŒ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ, ์ปค์Šคํ…€ ๋ชจ๋“ˆ ์ž‘์„ฑ
5๋‹จ๊ณ„ : CMakePresets, CI/CD ํ†ตํ•ฉ, ํŒจํ‚ค์ง€ ๋ฐฐํฌ

๋‹จ๊ณ„๋ณ„๋กœ ์ฐจ๊ทผ์ฐจ๊ทผ ์ตํžˆ๋ฉด ์–ด๋ ต์ง€ ์•Š์•„! ์žฌ๋Šฅ๋„ท(jaenung.net)์—์„œ C++ ๋ฉ˜ํ† ๋ฅผ ์ฐพ์•„ 1:1๋กœ ๋ฐฐ์šฐ๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ ๐Ÿ˜Š

๐ŸŒŸ ๋งˆ๋ฌด๋ฆฌ - ํ•ต์‹ฌ ์ •๋ฆฌ

์ž, ์—ฌ๊ธฐ๊นŒ์ง€ C++ ๋นŒ๋“œ ์‹œ์Šคํ…œ๊ณผ CMake/Make์— ๋Œ€ํ•ด ์—„์ฒญ ๋งŽ์€ ๋‚ด์šฉ์„ ๋‹ค๋ค˜์–ด!
ํ•ต์‹ฌ ํฌ์ธํŠธ๋งŒ ๋‹ค์‹œ ์ •๋ฆฌํ•ด๋ณผ๊ฒŒ:

โœ“
Make๋Š” Makefile ๊ธฐ๋ฐ˜์˜ ์ „ํ†ต์ ์ธ ๋นŒ๋“œ ๋„๊ตฌ. ํƒญ ๋“ค์—ฌ์“ฐ๊ธฐ ํ•„์ˆ˜, ํŒจํ„ด ๊ทœ์น™์œผ๋กœ ํšจ์œจ์ ์ธ ๋นŒ๋“œ ๊ฐ€๋Šฅ
โœ“
CMake๋Š” ๋ฉ”ํƒ€ ๋นŒ๋“œ ์‹œ์Šคํ…œ. CMakeLists.txt โ†’ Makefile/Ninja ๋“ฑ ์ƒ์„ฑ โ†’ ์‹ค์ œ ๋นŒ๋“œ
โœ“
Modern CMake๋Š” ํƒ€๊ฒŸ ์ค‘์‹ฌ ์ ‘๊ทผ๋ฒ•. target_* ํ•จ์ˆ˜๋“ค๊ณผ PRIVATE/PUBLIC/INTERFACE ํ‚ค์›Œ๋“œ๊ฐ€ ํ•ต์‹ฌ
โœ“
์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” find_package(), FetchContent, vcpkg/Conan ์ค‘ ์ƒํ™ฉ์— ๋งž๊ฒŒ ์„ ํƒ
โœ“
๋นŒ๋“œ ์ตœ์ ํ™”๋Š” ccache + Ninja + ๋ณ‘๋ ฌ ๋นŒ๋“œ ์กฐํ•ฉ์ด ๊ฐ€์žฅ ํšจ๊ณผ์ 
โœ“
CMakePresets.json์œผ๋กœ ํŒ€ ์ „์ฒด์˜ ๋นŒ๋“œ ์„ค์ •์„ ํ‘œ์ค€ํ™”ํ•˜๊ณ  CI/CD์™€ ์—ฐ๋™

๋นŒ๋“œ ์‹œ์Šคํ…œ์€ ์ฒ˜์Œ์—” ๋ณต์žกํ•ด ๋ณด์ด์ง€๋งŒ, ํ•œ ๋ฒˆ ์ œ๋Œ€๋กœ ์„ค์ •ํ•ด๋‘๋ฉด ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ์ด ์—„์ฒญ๋‚˜๊ฒŒ ์˜ฌ๋ผ๊ฐ€.
ํŠนํžˆ ํŒ€ ํ”„๋กœ์ ํŠธ์—์„œ "๋‚ด ์ปดํ“จํ„ฐ์—์„œ๋Š” ๋๋Š”๋ฐ?" ๊ฐ™์€ ์ƒํ™ฉ์„ ๋ฐฉ์ง€ํ•˜๋Š” ๋ฐ CMake๊ฐ€ ์ •๋ง ํฐ ์—ญํ• ์„ ํ•ด ใ…‹ใ…‹ใ…‹

์˜ค๋Š˜ ๋ฐฐ์šด ๋‚ด์šฉ์œผ๋กœ ์—ฌ๋Ÿฌ๋ถ„์˜ C++ ํ”„๋กœ์ ํŠธ ๋นŒ๋“œ ํ™˜๊ฒฝ์„ ํ•œ ๋‹จ๊ณ„ ์—…๊ทธ๋ ˆ์ด๋“œํ•ด๋ด! ํ™”์ดํŒ…! ๐Ÿš€

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0