MIT Missing Semester의 ‘Metaprogramming’ 강의는 build system, dependency management, testing, CI를 한 흐름으로 묶는다. 일반적인 programming language 문맥에서 metaprogramming은 code를 생성·변환하는 program을 뜻하지만, 이 강의는 code 바깥에서 code를 build하고 검증하는 도구까지 넓게 다룬다.
둘을 같은 정의로 고정하기보다 이 글의 범위를 개발 workflow 자동화라고 먼저 밝히는 편이 정확하다.
source와 설정
↓ build system
재현 가능한 artifact
↓ automated test·static analysis
검증 결과
↓ CI에서 반복
merge 가능한 change

build system은 dependency graph를 실행한다
build system은 target, prerequisite, recipe의 관계를 선언하고 필요한 작업만 실행한다. make의 기본 rule은 다음 형태다.
target: prerequisites
recipe
예를 들어 data로 plot을 만든 뒤 TeX 문서를 PDF로 build한다.
paper.pdf: paper.tex plot-data.png
pdflatex paper.tex
plot-%.png: %.dat plot.py
./plot.py -i $< -o $@
$<는 첫 prerequisite, $@는 target 이름이다. plot-data.dat나 script가 바뀌면 plot을 다시 만들고, prerequisite가 target보다 새로울 때 PDF를 갱신한다.
timestamp만으로 충분하지 않은 build도 있다. compiler flag, environment variable, remote input처럼 output에 영향을 주는 값이 dependency graph에 없으면 stale artifact를 재사용할 수 있다. 어떤 build system이든 output을 바꾸는 input을 명시하는 것이 먼저다.
도구 선택은 project 조건으로 한다
| 질문 | 확인할 조건 |
|---|---|
| language·ecosystem 표준이 있는가 | Gradle, Cargo, npm script, CMake 등 |
| multi-language·monorepo인가 | graph 규모, remote cache, sandbox 지원 |
| local과 CI 결과가 같은가 | runtime·toolchain pin, container, lockfile |
| incremental build가 정확한가 | dependency 누락과 cache invalidation |
| 실패를 설명하기 쉬운가 | log, local reproduction, artifact 보존 |
Make가 작은 project에 늘 최선이고 Bazel이 큰 project에 늘 최선이라는 법칙은 없다. team이 이해하고 유지할 수 있는 복잡도, 기존 ecosystem, cache 필요성을 함께 본다.
SemVer는 public API가 있어야 의미가 생긴다
Semantic Versioning 2.0.0은 public API를 선언한 software를 전제로 한다.
- MAJOR: incompatible API change
- MINOR: backward-compatible functionality
- PATCH: backward-compatible bug fix
version number만 SemVer처럼 생겼다고 compatibility가 자동 보장되지는 않는다. publisher가 public API와 compatibility policy를 지켜야 하고, consumer package manager의 range 문법도 따로 확인해야 한다.
lockfile은 resolver가 선택한 exact dependency graph를 기록해 재현성을 높인다. 다만 runtime, OS·architecture, package registry, build script, external artifact까지 고정하는 것은 아니다. “lockfile이 있으니 어디서나 bit-for-bit 동일”이라는 결론은 피한다.
dependency update는 자동 생성과 자동 승인을 나눈다
Dependabot·Renovate 같은 tool이 update PR을 만드는 것과 merge를 승인하는 것은 다른 단계다. 안전한 기본 흐름은 다음과 같다.
- manifest와 lockfile change를 dependency review로 확인한다.
- source·license·release note와 transitive dependency를 본다.
- build, unit·integration test, security check를 실행한다.
- breaking change와 rollback 조건을 검토한 뒤 merge한다.
“보안 update는 모두 즉시 자동 merge”도, “lockfile 때문에 update를 미룬다”도 좋은 기본값이 아니다. exploitability와 영향 범위를 확인하되 emergency fix를 빠르게 통과시킬 별도 path를 둔다.
CI는 change마다 같은 검증을 반복한다
Continuous Integration은 branch를 오래 분리해 두는 대신 작은 change를 자주 통합하고, build와 test를 자동으로 확인하는 practice다. deployment까지 자동화하는 CD와 같은 말은 아니다.
CI pipeline은 빠른 feedback부터 배치한다.
format·lint
→ unit test
→ build
→ integration test
→ dependency·security review
→ artifact publish 또는 deploy gate
모든 job에 write token을 줄 필요는 없다. pull request 검증은 read-only permission을 기본으로 하고, artifact publish·deploy는 protected branch, environment approval, short-lived credential처럼 별도 gate로 분리한다.
실제 local CI/CD lab은 Jenkins·Gogs·Kind 기반 pipeline, cloud-native 흐름은 GitOps Cookbook의 CI/CD 정리로 이어진다.
테스트는 범위보다 실패 의미가 중요하다
- unit test: 작은 logic을 빠르게 확인
- integration test: database·queue·service boundary 연결 확인
- regression test: 고친 defect가 다시 발생하지 않는지 고정
- test double: 느리거나 제어하기 어려운 dependency를 대체하되 실제 contract test도 유지
test 수가 많다고 release risk가 자동으로 줄지는 않는다. 어떤 failure를 잡는지, nondeterministic한지, production과 다른 assumption이 무엇인지 기록해야 한다.
참고 자료
'배움과 성장 > DevOps·클라우드' 카테고리의 다른 글
| macOS GitOps 실습 준비: kind 클러스터와 Docker Hub 이미지 푸시 (0) | 2025.10.18 |
|---|---|
| GitOps란 무엇인가: OpenGitOps 4원칙과 CI/CD 차이 (0) | 2025.10.18 |
| 리눅스 셸 작업 관리: jobs·nohup·tmux·SSH 연결 구분하기 (2) | 2025.08.30 |
| grep 명령어 실전 가이드: 로그 검색부터 정규식·스크립트까지 (2) | 2025.06.03 |
| DevOps·SRE 지표 설계: 역할별 숫자보다 Delivery·Reliability 결과 보기 (0) | 2025.03.08 |
댓글