1. 개요
시리즈 마지막 편이다. “CI/CD 파이프라인” 이라는 말은 흔하게 쓰지만, 정작 그 안의 빌드·테스트·배포 단계가 각각 왜 나뉘어 있는지, 어느 단계가 실패하면 뭘 의미하는지는 두루뭉술하게 넘어가기 쉽다. 이번 편에서는 이 단계들을 하나씩 뜯어보고, 왜 하나로 합치지 않고 나눠두는지를 정리한다.
2. 핵심 내용
2-1. CI 와 CD 는 사실 다른 질문에 답한다
- CI(Continuous Integration): “이 코드 변경이 기존 코드와 합쳤을 때 안전한가” 를 확인한다. 빌드와 테스트가 여기 속한다.
- CD(Continuous Delivery/Deployment): “이 검증된 산출물을 실제 환경에 어떻게 내보낼 것인가” 를 다룬다. 배포와 그 이후 검증이 여기 속한다.
이 둘을 나누는 이유는 단순하다. CI 는 코드를 신뢰할 수 있는지 판단하는 절차고, CD 는 신뢰할 수 있다고 판단된 것을 옮기는 절차다. 둘을 섞어버리면 “테스트는 통과했는데 배포 스크립트 문법 오류로 실패” 같은 상황에서 원인이 코드 문제인지 배포 절차 문제인지 헷갈린다.
2-2. 빌드: 소스를 실행 가능한 산출물로
빌드는 소스 코드를 컴파일하고 의존성을 묶어서 실행 가능한 형태(JAR, 컨테이너 이미지 등)로 만드는 단계다. 여기서 실패하면 대개 문법 오류나 의존성 버전 충돌처럼 명확한 원인이 있다. 컨테이너 기반 배포라면 이 단계에서 이미지를 만들어 레지스트리(GHCR, ECR 등)에 올린다.
2-3. 테스트: 산출물이 아니라 동작을 검증
빌드가 “컴파일이 되는가” 를 확인한다면, 테스트는 “의도한 대로 동작하는가” 를 확인한다. 보통 여러 계층으로 나뉜다.
- 단위 테스트: 함수/클래스 하나의 로직만 검증한다. 빠르고 많이 돌린다.
- 통합 테스트: DB, 캐시 같은 실제 의존성과 붙여서 검증한다. 느리지만 실제 동작에 가깝다.
- e2e 테스트: 사용자 시나리오 전체를 실제로 실행해서 검증한다. 가장 느리고 가장 비싸다.
여기서 중요한 건 각 계층이 잡아내는 버그의 종류가 다르다는 점이다. 단위 테스트만 초록불이라고 시스템 전체가 안전하다는 뜻은 아니다. 단위 테스트는 개별 함수가 맞게 짜였는지만 보지, 여러 컴포넌트가 실제로 맞물릴 때 생기는 문제(트랜잭션 경계, 동시성, 네트워크 타임아웃)는 통합·e2e 계층에서만 드러난다.
2-4. 배포: 검증된 산출물을 실제로 옮긴다
배포 단계는 빌드된 이미지를 실제 서버(또는 쿠버네티스 클러스터)에 반영하는 절차다. 여기서 자주 나오는 질문이 “빌드할 때 쓴 것과 배포할 때 뜨는 게 정말 같은 바이트인가” 다. 빌드를 여러 환경(스테이징, 운영)마다 따로 돌리면, 같은 소스라도 빌드 시점 의존성 버전이 미묘하게 달라져서 “스테이징에서는 됐는데 운영에서는 안 된다” 는 상황이 생길 수 있다. 그래서 빌드는 한 번만 하고, 그 산출물에 태그만 바꿔 붙여서 여러 환경에 재사용하는 방식(불변 아티팩트)을 많이 쓴다.
이 패턴이 실제로 어떻게 굴러가는지는 DoEatFit 의 배포 파이프라인에 자세히 정리해뒀다. 거기서는 main 브랜치에 머지된 커밋 하나를 빌드해서 prod-<sha7> 태그로 올려두고, 나중에 릴리스가 나갈 때는 그 이미지를 재빌드하지 않고 vX.Y.Z 태그만 새로 붙인다. 빌드에 30~40분이 걸리는 프로젝트라 릴리스마다 다시 빌드할 여유가 없기도 했지만, 더 중요한 건 “테스트를 통과한 바로 그 바이트” 를 운영에 올린다는 보장이다. 재빌드를 허용하면 그 사이 의존성이 달라졌을 가능성을 완전히 배제할 수 없다.
이 불변 아티팩트 방식도 레지스트리 정리 정책을 잘못 짜면 무력화된다. “최근 3개는 남긴다” 는 설정이 실제로는 실효 태그 하나만 남기고 배포 중이던 이미지를 지워버린 사고도 있었다(자세한 원인).
2-5. 왜 단계를 나누는가
빌드·테스트·배포를 하나의 스크립트로 합쳐버릴 수도 있다. 그런데 그러면 실패했을 때 “어디서” 실패했는지 로그를 처음부터 다 읽어야 한다. 단계를 나누면 실패 지점 자체가 정보가 된다. 빌드 단계 실패는 코드 문법 문제, 테스트 단계 실패는 로직 문제, 배포 단계 실패는 인프라/권한/설정 문제일 가능성이 높다는 식으로 원인 범위를 바로 좁힐 수 있다. 이게 CI/CD 파이프라인을 여러 단계로 쪼개서 설계하는 가장 실용적인 이유다.
3. 마무리
요약
- CI 는 “코드를 합쳐도 안전한가” 를 빌드·테스트로 검증하고, CD 는 그 검증된 산출물을 실제 환경에 옮긴다.
- 단위·통합·e2e 테스트는 계층마다 잡아내는 버그의 종류가 다르다. 단위 테스트 초록불이 전체 안전을 보장하지 않는다.
- 빌드를 환경마다 다시 하지 않고 한 번 만든 산출물에 태그만 바꿔 재사용하면, “스테이징과 운영이 실제로 같은 바이트” 라는 보장을 얻을 수 있다.
- 단계를 나누면 실패 지점 자체가 원인 범위를 좁혀주는 정보가 된다.
이 시리즈는 여기서 마친다. 인프라 용어들을 원리부터 다시 짚어보고 싶을 때 참고할 수 있는 여덟 편이 되었으면 한다.