풀스택 개발자를 위한 CI/CD 시리즈 · 기초 1/13
시리즈 전체 구성
- 1강. CI/CD란 무엇인가: 개념과 전체 그림
- 2강. GitHub Actions 해부와 첫 워크플로
- 3강. 테스트 전략과 품질 게이트
- 4강. 파이프라인 최적화: 캐시, 매트릭스, 병렬, 동시성 제어
- 5강. 컨테이너 빌드와 이미지 레지스트리
- 6강. 배포 자동화: 서버 배포와 플랫폼 배포
- 7강. 시크릿, 환경 분리, OIDC
- 8강. 배포 전략, 롤백, DB 마이그레이션
- 9강. 공급망 보안: 스캔, SBOM, 서명
- 10강. 모노레포와 재사용 가능한 파이프라인
- 11강. 폐쇄망 환경의 CI/CD
- 12강. GitOps와 Kubernetes 배포
- 13강. 측정과 개선: DORA 지표, 그리고 마무리
1. 왜 CI/CD가 필요해졌나
CI/CD가 없던 팀에서 흔히 벌어지는 장면이 있습니다.
- 통합 지옥(Integration Hell): 각자 몇 주 동안 자기 브랜치에서 작업하다가 릴리스 직전에 한꺼번에 합칩니다. 충돌이 수십 개 터지고, 합친 코드가 왜 안 도는지 아무도 모릅니다.
- "내 PC에서는 되는데요": 개발자마다 Node 버전, 설치된 패키지, 환경변수가 달라서 누구의 결과가 맞는지 알 수 없습니다.
- 수동 배포의 실수: 서버에 접속해
git pull,npm install,npm run build, 프로세스 재시작을 손으로 합니다. 순서 하나를 빠뜨리거나 다른 서버에 명령을 치는 사고가 납니다. - 배포 공포: 배포가 무서우니 배포를 미룹니다. 미루니 한 번에 나가는 변경이 커지고, 변경이 크니 더 자주 터집니다.
마지막 항목이 핵심입니다. 배포가 드물수록 배포는 더 위험해집니다. CI/CD는 이 악순환을 끊기 위해 "작게, 자주, 자동으로"라는 방향으로 뒤집은 방법론입니다.
2. CI: Continuous Integration (지속적 통합)
CI는 개발자들이 자기 변경을 공유 브랜치(보통 main)에 자주 합치고, 합칠 때마다 자동으로 빌드와 테스트를 돌려 문제를 즉시 발견하는 관행입니다. 익스트림 프로그래밍(XP) 진영에서 정착했고, Martin Fowler의 글로 널리 알려졌습니다.
여기서 흔한 오해 하나를 짚어야 합니다.
CI 도구를 쓴다고 CI를 하는 것은 아닙니다.
GitHub Actions가 매 push마다 테스트를 돌리더라도 기능 브랜치가 3주씩 main과 떨어져 있다면, 그건 "자동 테스트"일 뿐 "지속적 통합"이 아닙니다. CI의 본질은 도구가 아니라 통합의 빈도입니다.
CI가 제대로 굴러가는 팀의 특징은 다음과 같습니다.
- 단일 소스 저장소: 빌드에 필요한 모든 것(코드, 설정, 스크립트, lockfile)이 저장소 안에 있습니다.
- 빌드 자동화: 명령 하나로 빌드가 끝나고, 사람의 손이 개입하지 않습니다.
- 자체 검증되는 빌드: 빌드 과정에 테스트가 포함되어 있어서, 테스트가 실패하면 빌드도 실패합니다.
- 최소 하루 한 번 이상 main에 통합: 브랜치 수명이 짧습니다.
- 깨진 빌드는 최우선으로 고친다: main이 빨간불이면 새 기능보다 복구가 먼저입니다.
- 빌드가 빠르다: 흔히 10분 이내를 목표로 합니다. 느리면 사람들이 결과를 기다리지 않게 되고, CI는 무력해집니다.
3. CD: Delivery와 Deployment
CD는 두 가지 의미로 쓰이고, 둘은 분명히 다릅니다.
Continuous Delivery (지속적 제공) main에 합쳐진 코드가 언제든 운영에 배포 가능한 상태로 자동 준비됩니다. 빌드, 테스트, 패키징, 스테이징 배포까지 자동으로 진행되고, 운영 배포는 사람이 버튼을 눌러 결정합니다.
Continuous Deployment (지속적 배포) 파이프라인을 모두 통과하면 사람의 개입 없이 운영까지 자동으로 배포됩니다.
빌드 → 테스트 → 패키징 → 스테이징 배포 → [운영 배포]
Continuous Delivery: 자동 자동 자동 자동 수동 승인
Continuous Deployment: 자동 자동 자동 자동 자동어느 쪽이 더 "좋은" 것은 아닙니다. 금융, 제조, 사내 시스템처럼 배포 시점을 업무 일정에 맞춰야 하거나 승인 절차가 필요한 조직에서는 Delivery가 현실적입니다. Deployment는 테스트와 모니터링에 대한 신뢰가 충분히 쌓였을 때 선택하는 단계입니다.
4. 파이프라인의 해부
CI/CD의 실체는 파이프라인, 즉 코드 변경이 운영에 도달하기까지 거치는 자동화된 단계의 연쇄입니다.
[트리거] push / PR / 태그 / 스케줄 / 수동 실행
│
▼
[소스] 코드 체크아웃
│
▼
[빌드] 의존성 설치 → 컴파일/번들링 ┐
│ │ CI 영역
▼ │
[검증] lint → 타입 체크 → 단위 테스트 → 통합 테스트 ┘
│
▼
[패키징] 배포 가능한 산출물(아티팩트) 생성 ┐
│ 예: Docker 이미지, 정적 파일 묶음 │
▼ │
[스테이징 배포] → E2E/스모크 테스트 │ CD 영역
│ │
▼ │
[운영 배포] (자동 또는 승인 후) │
│ │
▼ │
[확인] 헬스체크, 모니터링, 필요 시 롤백 ┘앞 단계가 실패하면 뒤 단계는 실행되지 않습니다. 이것이 게이트(gate) 역할이며, 문제 있는 코드가 운영까지 흘러가는 것을 막습니다.
5. 반드시 알아야 할 용어
| 용어 | 의미 |
|---|---|
| Pipeline / Workflow | 트리거부터 배포까지의 전체 자동화 흐름입니다. GitHub Actions에서는 workflow라고 부릅니다. |
| Stage / Job | 파이프라인을 구성하는 큰 단위입니다. job끼리는 기본적으로 병렬 실행되고, 의존 관계를 지정하면 순차 실행됩니다. |
| Step | job 안에서 순서대로 실행되는 개별 명령이나 액션입니다. |
| Trigger (Event) | 파이프라인을 시작시키는 사건입니다. push, PR, 태그 생성, cron 스케줄, 수동 실행 등이 있습니다. |
| Runner (Agent) | 실제로 job을 실행하는 머신입니다. 서비스가 제공하는 머신(hosted)이나 직접 운영하는 머신(self-hosted)을 씁니다. |
| Artifact | 파이프라인이 만든 산출물입니다. 빌드 결과, 테스트 리포트, Docker 이미지 등이 있습니다. |
| Environment | 배포 대상 환경입니다. dev, staging, production 등으로 나뉘며, 환경마다 설정과 승인 규칙이 따로 있습니다. |
| Gate | 다음 단계로 넘어가기 위한 조건입니다. 테스트 통과, 커버리지 기준, 수동 승인 등이 있습니다. |
6. CI/CD의 핵심 원칙
도구가 바뀌어도 변하지 않는 원칙들입니다. 이후 강의의 실습은 모두 이 원칙을 구현하는 방법이라고 보면 됩니다.
① Pipeline as Code 파이프라인 정의를 웹 UI 클릭이 아니라 저장소 안의 파일(YAML 등)로 관리합니다. 코드 리뷰를 받을 수 있고, 변경 이력이 남고, 브랜치별로 다르게 실험할 수 있습니다.
② Fail Fast (빠른 실패) 싸고 빠른 검사를 앞에, 비싸고 느린 검사를 뒤에 둡니다. lint가 5초 만에 잡을 오류를 E2E 테스트 10분 뒤에 발견하면 안 됩니다.
③ Build Once, Deploy Many 산출물은 한 번만 빌드하고, 같은 산출물을 스테이징과 운영에 차례로 배포합니다. 환경마다 다시 빌드하면 "스테이징에서 검증한 것"과 "운영에 나간 것"이 달라질 수 있습니다. 환경 차이는 빌드가 아니라 실행 시점의 설정(환경변수)으로 주입합니다.
④ 재현 가능성 (Reproducibility)
같은 커밋이면 언제 어디서 빌드해도 같은 결과가 나와야 합니다. lockfile을 커밋하고, npm install 대신 npm ci를 쓰고, 런타임과 베이스 이미지 버전을 고정하는 이유가 이것입니다.
⑤ 불변 산출물 (Immutable Artifacts) 한번 만든 산출물은 수정하지 않고, 고칠 게 있으면 새 버전을 만듭니다. 그래야 "지금 운영에 무엇이 떠 있는지"를 정확히 알 수 있고, 롤백이 "이전 산출물로 되돌리기"라는 단순한 작업이 됩니다.
⑥ main은 항상 배포 가능한 상태 미완성 기능은 짧은 브랜치에서 빨리 합치되, 필요하면 기능 플래그(feature flag)로 숨깁니다. "main을 배포하면 안 되는 기간"이 존재한다면 CD가 성립하지 않습니다.
7. 풀스택 개발자의 관점
풀스택 개발자는 한 번의 배포에서 성격이 다른 세 계층을 함께 다뤄야 합니다.
- 프론트엔드: 정적 파일 번들이나 SSR 서버입니다. 빌드 시점 환경변수(
NEXT_PUBLIC_*같은)가 산출물에 박히기 때문에 원칙 ③과 충돌하기 쉬워서 주의가 필요합니다. - 백엔드: 보통 컨테이너 이미지로 패키징되어 서버에서 실행됩니다.
- 데이터베이스: 코드처럼 "이전 버전으로 교체"할 수 없습니다. 스키마 변경은 되돌리기 어렵기 때문에 배포 순서와 하위 호환성을 설계해야 합니다. 8강에서 깊게 다룹니다.
즉 풀스택 개발자의 CI/CD는 "코드를 서버에 올리는 법"이 아니라 세 계층이 어긋나지 않게 함께 움직이게 하는 법입니다.
8. 도구 지형도
| 분류 | 대표 도구 | 특징 |
|---|---|---|
| 저장소 통합형 CI/CD | GitHub Actions, GitLab CI/CD | 코드 저장소와 한 몸이라 설정이 간단하고 생태계가 큽니다. |
| 독립 CI 서버 | Jenkins | 플러그인이 방대하고 설치형이라 폐쇄망과 대기업 환경에서 여전히 많이 쓰입니다. |
| 클라우드 CI 서비스 | CircleCI, Azure Pipelines 등 | 관리형 서비스라 빌드 인프라를 직접 운영할 필요가 없습니다. |
| 플랫폼형 배포 | Vercel, Netlify | 저장소만 연결하면 빌드, 배포, PR 미리보기까지 알아서 처리합니다. |
| GitOps 배포 | Argo CD, Flux | Git의 상태를 Kubernetes 클러스터에 자동으로 동기화합니다. |
이 시리즈는 GitHub Actions를 중심으로 진행합니다. 개념이 거의 그대로 다른 도구로 옮겨지므로, 하나를 제대로 익히면 GitLab CI나 Jenkins도 금방 읽을 수 있습니다. 폐쇄망을 다루는 11강에서는 GitLab CI 예제도 함께 봅니다.
Vercel은 무엇을 숨기고 있나
Vercel로 Next.js를 배포해 봤다면 이미 CI/CD를 쓰고 있었던 셈입니다. 저장소에 push하면 Vercel이 대신 다음 작업을 해 줍니다.
- 트리거 감지 (push, PR)
- 의존성 설치와 빌드
- 산출물 생성과 배포
- PR마다 미리보기(preview) 환경 생성
- 환경별 환경변수 주입
- 이전 배포로 즉시 롤백
편리하지만 이 과정이 블랙박스이기 때문에, 자체 서버나 폐쇄망처럼 Vercel을 쓸 수 없는 환경에서는 손을 쓸 수 없게 됩니다. 이 시리즈의 목표는 이 블랙박스를 직접 열어서 각 부품을 스스로 조립할 수 있게 되는 것입니다.
9. 정리
- CI/CD는 "작게, 자주, 자동으로" 통합하고 배포해서 배포의 위험을 낮추는 방법론입니다.
- CI의 본질은 도구가 아니라 통합의 빈도입니다.
- Continuous Delivery는 운영 배포를 사람이 결정하고, Continuous Deployment는 자동으로 진행합니다.
- 파이프라인은 트리거 → 빌드 → 검증 → 패키징 → 배포 → 확인의 연쇄이며, 각 단계가 게이트 역할을 합니다.
- 핵심 원칙은 Pipeline as Code, Fail Fast, Build Once Deploy Many, 재현 가능성, 불변 산출물, 항상 배포 가능한 main입니다.
확인 문제와 해설
Q1. 팀이 GitHub Actions로 모든 push에 테스트를 돌리고 있지만, 기능 브랜치를 평균 2주 뒤에 main에 합칩니다. 이 팀은 CI를 하고 있다고 볼 수 있을까요?
엄밀히는 아닙니다. 자동 테스트는 있지만 통합 빈도가 낮아서, 2주 동안 쌓인 변경이 한꺼번에 충돌하는 통합 지옥의 위험이 그대로 남아 있습니다. 브랜치를 작게 쪼개 하루 단위로 합치는 방향으로 개선해야 합니다.
Q2. 스테이징용 이미지와 운영용 이미지를 따로 빌드하는 파이프라인은 어떤 원칙을 어기고 있고, 어떤 위험이 생길까요?
Build Once, Deploy Many 원칙을 어깁니다. 두 빌드 사이에 의존성 버전이나 베이스 이미지가 바뀌면, 스테이징에서 검증한 것과 다른 결과물이 운영에 나갈 수 있습니다.
Q3. lint, E2E 테스트, 단위 테스트, 타입 체크는 어떤 순서가 적절할까요?
lint → 타입 체크 → 단위 테스트 → E2E 테스트 순서가 적절합니다. 빠르고 싼 검사를 앞에 두는 Fail Fast 원칙 때문입니다. lint와 타입 체크는 서로 독립적이라 병렬로 돌려도 됩니다.
더 깊이
- CI/CD를 인프라 전체 그림 속 한 조각으로 먼저 훑고 싶다면 풀스택 개발자를 위한 인프라 5강 — CI/CD가 이 13강 시리즈를 한 강으로 압축한 요약입니다.