E2E 자동화 : buildId 폴링으로 배포 완료 시점 추적하기

2026년 4월 27일

#front

기존 E2E 스크립트를 직접 실행했던 환경에서, PR 생성시 E2E 결과를 자동으로 코멘트 남기는 워크플로우를 구축했었어요.

해당 작업도 유의미한 작업이긴 했지만, 자연스럽게 다음 과제로 이어나가게 되었어요.

“PR 단계가 아니라 staging 서버에 배포됐을 때 E2E를 실행시켜야 더 유의미하지 않을까? PR 단계는 실제 서버에 반영된 게 아닌데?”


기존 구조의 문제점

기존엔 스테이징 서버에 대한 E2E 테스트를 상용 배포 전 작업자가 수동으로 실행하였고, 해당 단계를 놓치는 경우가 빈번했어요.

팀 규칙이 점점 무너지는 것에 다들 공감하였고, 자동화를 해보기로 하였어요.

그래서 PR 생성시 E2E 를 실행시켰지만, 이 작업 또한 Stading 서버가 아닌 사실상 로컬에서 테스트를 하는 플로우였어요.

이것을 배포 시점에 자동화 하기 위해선, 배포 시점을 워크플로우가 어떻게 알 수 있을지 설계하는 과제가 있었습니다 💡

push to release/** (깃헙 액션 트리거)
     ↓
stg_build_and_deploy.yml 실행 (배포)
     ↓
     ↓  ← 이 시점을 어떻게 추적할까?
     ↓
E2E 테스트 실행하고 싶음

아이디어들

아이디어 1. 배포 워크플로우 끝에 E2E를 직접 붙이기

가장 단순한 방법이에요. stg_build_and_deploy.yml 마지막 step에 E2E를 추가하는 거예요.

- name: Run E2E tests (Staging)
  env:
    NEXT_PUBLIC_E2E_BASE_URL: { 스테이징URL }
  run: pnpm exec playwright test --project=clinic
  • ✅ 구현이 단순함
  • ❌ 배포 워크플로우가 무거워짐
  • ❌ E2E 실패해도 배포는 이미 완료된 상태

아이디어 2. workflow_run 트리거로 별도 워크플로우 생성

배포 워크플로우 완료 이벤트를 감지하는 전용 E2E 워크플로우를 따로 만드는 방식이에요.

# e2e_staging.yml
on:
  workflow_run:
    workflows: ["Staging Environment Deployment Workflow"]
    types: [completed]
    branches: ["release/**"]

jobs:
  e2e:
    # 배포 성공시에만 E2E 테스트 실행
    if: github.event.workflow_run.conclusion == 'success'
  • ✅ 관심사 분리, 배포 워크플로우에 영향 없음
  • ❌ 이미 머지된 PR! PR 코멘트보다 Slack 등의 알림이 적합

저는 관심사가 분리되고, 결과를 협업 툴의 알림으로 받을 수 있는 2번 방식으로 진행하기로 했어요.

2번 방식의 문제점: 배포 완료 !== 새 버전 반영

아이디어 2로 방향을 잡으면서, 배포 완료 시점에 E2E를 바로 돌리면 되지 않을까 생각했어요.

그렇지만 놓친 점이 있었습니다.

배포가 완료되더라도, 새 버전이 우리 서비스에 반영되었다는 것을 보장할 수 없어요.

현재 제가 담당하는 프로젝트는 build 성공 이후, deploy가 실행되는데, 이 deploy 의 성공 시점을 받아오지 않고 있어요. 만약 deploy 성공 시점을 받아오더라도, 브라우저에 반영되었는지도 확인해야 했어요. 배포 완료는 새 버전 100% 반영을 의미하지 않았어요.

(Deploy 성공)
✅ 새 ECS 태스크 실행
✅ 헬스체크 통과
✅ ALB → 새 태스크로 트래픽 전환 완료
(이후 엣지 레벨)
⏳ 기존 연결 drain 중 (수십 초)
⏳ Route53 TTL 전파
⏳ CloudFront 캐시

ALB 레벨에선 전환이 완료됐더라도, 엣지에서 딜레이가 발생할 수 있어요. 그래서 단순히 HTTP 200 응답 여부만 확인하는 폴링으로는 정확하지 않습니다.

-> ALB (Application Load Balancer) 레벨: AWS에서 트래픽을 여러 서버(ECS 태스크 등)에 분산해주는 로드밸런서 계층. “우리 서버 바로 앞단”, 배포가 완료되면 ALB는 트래픽을 새 버전 서버로 전환하는데, 이건 비교적 빠르게 반영됨

-> 엣지(Edge) 레벨: 사용자와 가장 가까운 곳에 위치한 CDN 서버 계층. CloudFront 같은 서비스가 여기에 해당. ALB에서 새 버전으로 전환이 완료됐더라도, 전 세계에 퍼져있는 엣지 서버들이 캐시를 들고 있으면 그것이 만료되기 전까지는 구버전을 계속 서빙할 수 있음.


그래서 선택한 방법: HTML에서 buildId 직접 확인

Next.js는 빌드마다 고유한 buildId를 생성하고, 모든 페이지 헤더에 현재 실행 중인 buildId를 심어줘요. 이것을 활용해보았어요.

# 전 (S3 기준 — 부정확)
# S3 업로드만 되면 200 → 서버가 아직 구버전이어도 통과
curl "{서비스URL}/_next/static/{BUILD_ID}/_buildManifest.js"

# 후 (실제 서버 기준 — 정확)
# 루트 페이지 HTML에서 buildId 추출 → 실제 서버가 서빙 중인 버전 확인
RESPONSE=$(curl "{서비스URL}")
SERVED_BUILD_ID=$(echo "$RESPONSE" | grep -o '"buildId":"[^"]*"' | cut -d'"' -f4)

플로우는 아래와 같아요!

push to release/**
      ↓
      ↓
Job 1: build-and-deploy
  - Docker 이미지 빌드 → ECR 푸시
  - BUILD_ID 추출 (.next/BUILD_ID)
  - 정적 파일 → S3 업로드
  - ECS + CodeDeploy 배포
      ↓
      ↓ (성공 시에만)
      ↓
Job 2: e2e-and-notify
  - 새 버전 반영 확인 (buildId 폴링)
  - E2E 테스트 실행
  - 결과 Slack 전송

그럼에도 놓쳤던 작업

처음엔 /_next/static/{BUILD_ID}/_buildManifest.js 경로를 폴링하여 배포를 체크하였어요. 그런데 이 경로는 S3에서 서빙되고 있었고, 워크플로우를 보면 배포 전에 S3 업로드가 먼저 실행되고 있었습니다.

즉, 서버가 아직 구버전을 브라우저에 서빙하고 있어도 S3 업로드 직후부터 200이 내려오기 때문에 폴링이 너무 일찍 통과될 가능성이 높았어요.

그래서 정적 파일 경로 대신 루트 페이지 HTML에서 직접 buildId를 추출하는 방식으로 바꿨습니다. 실제 서버가 서빙 중인 버전을 정확하게 체크할 수 있어요.

워크플로우에서 저장한 타겟 buildId 와 HTML에서 읽어온 buildId가 일치할 때까지 폴링을 반복하고, 일치하면 E2E를 실행시켰습니다. 실제로는 약 8분 정도 소요되었고, 10분 정도 폴링이 반복하게끔 설정해놓았어요.


정리

방식 문제
배포 완료 즉시 E2E 실행 엣지 딜레이로 구버전 테스트 가능성
/_next/static 경로 폴링 S3 업로드 직후 200 반환 → 이른 통과
HTML에서 buildId 직접 확인 ✅ 실제 서버 버전 정확하게 체크 가능

workflow_run 트리거로 배포 워크플로우와 E2E 워크플로우를 분리하고, buildId 폴링으로 실제 새 버전 반영 시점을 정확하게 잡아냈어요. PR 코멘트 대신 Slack으로 결과를 전송하는 구조로 마무리했습니다.


앞으로 고민해야 할 문제

해결해야할 문제들이 더 남아있어요.

현재 E2E 실행시간까지 마무리되는데 약 20분이 소요되고 있어요. 이를 줄이기 위한 두 가지를 고민하고 있어요.

  1. GitHub Actions 비용: E2E 워크플로우가 오래 걸릴수록 비용에 영향을 주는데, 현재 규모에서 괜찮은 수준인지 검토가 필요해요.
  2. 캐싱 최적화: actions/cache로 pnpm store를 캐싱하면, 두 번째 실행부터 install 시간을 30~1분 정도 절약할 수 있다고 해요.

자동화에 과제는 성공하였고, 앞으로는 빠른 피드백 루프를 위해 실행 시간 단축을 고민해보려고 해요.


Profile picture

주희(Joy)
가치를 고민하는 과정을 함께해요