6 분 소요

📦 CodeCommit → CodeBuild → EKS 자동화 3부작 — main 에 push 한 번이면 이미지가 빌드돼 ECR 에 올라가고(1편), 커밋해시 태그로 EKS 까지 자동 배포되며(2편), 배포 전 자동 테스트로 깨진 코드를 막는 품질 게이트(3편)까지, 초보자 눈높이로 한 줄씩 이어집니다. 전체 3편.
  1. CodeCommit main 푸시 → CodeBuild 자동 빌드 → ECR(자동생성·latest) → EKS
  2. latest 졸업 — 커밋해시 태그로 EKS 자동 배포 (CodeBuild 에서 kubectl 권한 잡기)
  3. 배포 전 자동 테스트 품질 게이트 — 실패하면 배포를 멈추기지금 글

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.ymlinstallpre_buildbuildpost_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 권한 잡기)