(3/3) 배포 전 자동 테스트 품질 게이트 — 실패하면 배포를 멈추기, 초보자용
- CodeCommit main 푸시 → CodeBuild 자동 빌드 → ECR(자동생성·latest) → EKS
- latest 졸업 — 커밋해시 태그로 EKS 자동 배포 (CodeBuild 에서 kubectl 권한 잡기)
- 배포 전 자동 테스트 품질 게이트 — 실패하면 배포를 멈추기 ← 지금 글
Summary
여기까지 오면 main 에 push 하는 순간 이미지가 빌드되고(1편), 커밋해시로 EKS 까지 자동 배포됩니다(2편). 빠르고 편하죠. 그런데 빠른 만큼 위험해요. 테스트도 안 거친 코드가 push 한 번에 운영까지 그대로 흘러가니까요.
그래서 마지막으로 품질 게이트를 답니다. 배포 전에 자동 테스트를 돌리고, 하나라도 실패하면 거기서 멈춰서 깨진 코드가 EKS 에 도달하지 못하게 막는 문이에요. 이 글에서는 그 문을 buildspec.yml 안에 만들고, 초보자가 꼭 만나는 함정 하나(post_build 는 앞 단계가 실패해도 돈다)를 같이 풀어봅니다.
💡 이 글에서 다루는 것
- 품질 게이트가 뭐고 왜 “배포 전” 인가
- 테스트를
buildspec.yml의 어느 단계에 넣나- 🚨 함정 —
post_build는 빌드가 실패해도 실행된다 → 가드 거는 법- 게이트가 걸린
buildspec.yml전체 모습- 한 단계 위 — 빌드된 이미지로 스모크 테스트
- 테스트 결과를 리포트로 보기
- 더 앞당기기 — 배포 전이 아니라 머지 전으로
1. 품질 게이트란
이름이 거창하지만 개념은 단순해요. “이 조건을 통과 못하면 다음으로 못 간다” 는 문이에요. 우리 경우엔 “테스트가 다 통과해야 배포한다” 가 됩니다.
| 게이트가 없을 때 | 게이트가 있을 때 |
|---|---|
| push → 무조건 빌드·배포 | push → 테스트 통과해야 빌드·배포 |
| 깨진 코드도 운영까지 감 | 깨진 코드는 빌드 단계에서 멈춤 |
| 장애를 운영에서 발견 | 장애를 파이프라인에서 발견 |
| 롤백으로 수습 | 애초에 배포가 안 됨 |
핵심은 “빠르게 실패하기(fail fast)” 예요. 문제를 운영에서 만나는 것보다, 파이프라인에서 1~2분 만에 잡는 게 훨씬 쌉니다.
2. 테스트를 어느 단계에 넣나
1편에서 buildspec.yml 이 install → pre_build → build → post_build 순으로 돈다고 했죠. 테스트는 이미지를 빌드하기 전, 즉 pre_build 에 넣는 게 자연스러워요. 빌드해봐야 어차피 버릴 코드라면 굽기 전에 거르는 게 빠르니까요.
phases:
install:
commands:
- npm ci # 의존성 설치
pre_build:
commands:
- echo "=== 품질 게이트 시작 ==="
- npm run lint # 코드 스타일 검사
- npm test # 유닛 테스트 — 실패하면 여기서 멈춤
npm test 가 0 이 아닌 종료코드로 끝나면 그 단계가 실패로 처리돼요. 그러면 뒤따르는 build 단계가 아예 실행되지 않습니다. 깨진 코드는 이미지조차 안 만들어지는 거죠.
💡 테스트 명령은 언어/도구에 맞게 바꾸면 돼요. 파이썬이면
pytest, 자바면mvn test, Go 면go test ./.... 핵심은 “실패하면 0이 아닌 코드로 종료되는 명령” 이라는 점이에요. 종료코드로 성공/실패를 판단하니까요.
3. 🚨 함정 — post_build 는 실패해도 돈다
여기가 이 글에서 제일 중요한 부분이에요. 직관과 다르거든요.
pre_build 에서 테스트가 실패하면 build 는 안 돈다고 했죠. 그런데 post_build 는 앞 단계가 실패해도 무조건 실행돼요. 프로그래밍의 finally 블록 같은 거예요. CodeBuild 가 “마무리 정리는 늘 하라” 는 의도로 그렇게 동작합니다.
문제는 우리 배포 명령(docker push·kubectl set image)이 바로 그 post_build 에 있다는 거예요. 그대로 두면 테스트가 깨져도 배포 단계가 돌아버려요. 게이트가 새는 겁니다.
그래서 post_build 맨 앞에 가드(guard) 를 둡니다. CodeBuild 가 채워주는 CODEBUILD_BUILD_SUCCEEDING 환경변수로 앞 단계 성공 여부를 확인해요. (성공이면 1, 실패면 0.)
post_build:
commands:
- |
if [ "$CODEBUILD_BUILD_SUCCEEDING" != "1" ]; then
echo "앞 단계(테스트/빌드)가 실패했어요. 배포를 건너뜁니다.";
exit 1;
fi
- echo "여기부터는 게이트를 통과한 경우에만 실행됨"
🚨 이 가드가 없으면 “테스트는 빨갰는데 배포는 됐다” 는 최악의 상황이 나옵니다.
post_build에 배포·push 가 있다면 반드시 맨 앞에CODEBUILD_BUILD_SUCCEEDING가드를 거세요. 초보자가 게이트를 만들고도 새는 가장 흔한 이유예요.
4. 게이트가 걸린 buildspec.yml 전체
앞의 조각을 합치면 이런 모습이에요. 1·2편의 빌드·배포 위에 테스트 게이트와 가드가 얹힌 완성본입니다.
version: 0.2
env:
variables:
IMAGE_REPO_NAME: "my-app"
phases:
install:
commands:
- npm ci
pre_build:
commands:
- echo "=== 품질 게이트: lint + 테스트 ==="
- npm run lint
- npm test
- echo "=== 게이트 통과 — 빌드 준비 ==="
- ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
- REGISTRY=$ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
- IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c1-7)
- aws ecr describe-repositories --repository-names $IMAGE_REPO_NAME || aws ecr create-repository --repository-name $IMAGE_REPO_NAME
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $REGISTRY
build:
commands:
- docker build -t $REGISTRY/$IMAGE_REPO_NAME:$IMAGE_TAG -t $REGISTRY/$IMAGE_REPO_NAME:latest .
post_build:
commands:
- |
if [ "$CODEBUILD_BUILD_SUCCEEDING" != "1" ]; then
echo "앞 단계 실패 — push/배포 건너뜀"; exit 1;
fi
- docker push $REGISTRY/$IMAGE_REPO_NAME:$IMAGE_TAG
- docker push $REGISTRY/$IMAGE_REPO_NAME:latest
- aws eks update-kubeconfig --name my-cluster --region $AWS_DEFAULT_REGION
- kubectl set image deployment/my-app my-app=$REGISTRY/$IMAGE_REPO_NAME:$IMAGE_TAG
- kubectl rollout status deployment/my-app --timeout=120s
reports:
unit-tests:
files:
- "junit.xml"
file-format: "JUNITXML"
흐름을 말로 풀면 이래요. 테스트 통과 → 이미지 빌드 → (가드 확인) → push → EKS 배포. 어느 한 곳이라도 실패하면 가드에 걸려 배포까지 못 가고 빌드가 빨갛게 끝납니다.
5. 한 단계 위 — 빌드된 이미지로 스모크 테스트
유닛 테스트는 “코드 조각” 을 검사하지만, “이미지가 실제로 뜨는지” 는 또 다른 문제예요. 그래서 한 단계 욕심내면, 빌드된 컨테이너를 잠깐 띄워보는 스모크 테스트를 build 단계 끝에 더할 수 있어요.
build:
commands:
- docker build -t $REGISTRY/$IMAGE_REPO_NAME:$IMAGE_TAG .
- echo "=== 스모크 테스트: 컨테이너가 뜨고 응답하나 ==="
- docker run -d --name smoke -p 8080:80 $REGISTRY/$IMAGE_REPO_NAME:$IMAGE_TAG
- sleep 3
- curl -fsS http://localhost:8080/ > /dev/null
- docker rm -f smoke
curl -fsS 의 -f 옵션은 HTTP 에러가 나면 0 이 아닌 코드로 끝나요. 즉 컨테이너가 안 뜨거나 200 이 아니면 이 단계가 실패 하고, 4장의 가드 덕분에 배포가 멈춥니다.
💡 “유닛 테스트(코드) + 스모크 테스트(이미지)” 두 겹이면 초보 단계에선 충분해요. 더 나아가면 배포 후 헬스체크·통합 테스트로 게이트를 늘려가는데, 처음부터 다 욕심내지 말고 이 두 겹부터 단단히 다지는 걸 추천드립니다.
6. 테스트 결과를 리포트로 보기
4장 buildspec.yml 끝의 reports: 블록이 그거예요. 테스트가 junit.xml 같은 결과 파일을 남기면, CodeBuild 가 그걸 읽어 테스트 리포트 화면으로 보여줘요. 몇 개 통과/실패했는지, 어떤 테스트가 깨졌는지 콘솔에서 한눈에 봅니다.
reports:
unit-tests:
files:
- "junit.xml" # 테스트 도구가 뱉는 결과 파일
file-format: "JUNITXML"
💡 로그를 일일이 스크롤하지 않아도 돼서 편해요. 대부분의 테스트 도구가 JUnit XML 형식을 지원하니(
pytest --junitxml=...,jest --reporters=jest-junit등), 결과 파일 경로만 맞춰주면 됩니다.
7. 더 앞당기기 — 배포 전이 아니라 머지 전으로
지금 게이트는 main 에 들어온 뒤 도는 “배포 전” 게이트예요. 한 발 더 나가면, 아예 main 에 머지되기 전(PR 단계)에 테스트를 돌려서 깨진 코드가 main 에 못 들어오게 막을 수 있어요.
| 게이트 위치 | 언제 도나 | 막는 것 |
|---|---|---|
| 배포 전 (이 글) | main push 후 | 깨진 코드의 배포 |
| 머지 전 (PR) | PR 올릴 때 | 깨진 코드의 main 진입 |
CodeCommit 이라면 PR 용 빌드를 따로 두고 Approval rule 과 묶어, 테스트가 통과해야 머지되게 할 수 있어요. (CodeCommit·GitHub 비교 글에서 다룬 Approval rule template 이 여기서 쓰여요.) 둘을 같이 두면 “main 에 들어오기 전” + “배포되기 전” 이중 게이트가 됩니다.
💡 처음부터 둘 다 만들 필요는 없어요. 이 글의 배포 전 게이트만으로도 사고는 크게 줄어요. 익숙해지면 머지 전 게이트로 한 칸 더 당기는 걸 권합니다.
8. 정리 — 3부작을 마치며
세 편을 거치며 만든 걸 다시 보면 이래요.
- 1편 — push 하면 이미지가 빌드돼 ECR 에 올라간다
- 2편 — 커밋해시 태그로 EKS 까지 자동 배포된다
- 3편(이 글) — 테스트를 통과한 코드만 그 길을 지나간다
이 글의 한 가지만 가져간다면 3장이에요. post_build 는 앞 단계가 실패해도 돈다 — 그래서 배포 명령 앞에 CODEBUILD_BUILD_SUCCEEDING 가드를 꼭 거는 것. 이거 하나로 “테스트는 깨졌는데 배포는 됐다” 는 사고를 막습니다.
이제 안심하고 git push origin main 을 누를 수 있어요. 깨진 코드는 게이트가 막아주니까요.
일단 오늘은 여기까지…..
CodeCommit 한 번의 push 가 테스트·빌드·배포까지 안전하게 이어지는 3부작을 여기서 마무리할게요. 다음엔 또 다른 주제로 찾아오겠습니다.
← 이전 글: (2/3) latest 졸업 — 커밋해시 태그로 EKS 자동 배포하기 (CodeBuild 에서 kubectl 권한 잡기)