leo.dev
infra

워크플로우 통합과 배포 순서 강제

2026.04.242 min read ci-cdgithub-actionsdeployment

1편에서 가장 위험한 문제로 꼽은 것: packages/**를 건드리면 FE와 BE가 동시에 트리거되고, 빠른 FE가 먼저 배포돼 구 BE를 호출한다. 원인은 워크플로우 3개가 서로를 모른다는 데 있다. 그러면 서로 알게 만들면 된다.

합치기 전

.github/workflows/에 독립 워크플로우 3개가 있었다.

  • deploy-backend.yml: apps/backend/** 또는 packages/**
  • deploy-frontend.yml: apps/frontend/** 또는 packages/**
  • deploy-batch.yml: apps/backend/** 또는 packages/**

packages/**가 바뀌면 셋 다 동시에 떴다. 조율할 방법이 없었다.

합친 후

3개 파일을 deploy.yml 하나로 통합하고, job 사이에 의존성을 걸었다.

detect-changes
├──▶ deploy-backend (backend 또는 packages 변경 시)
│ ├──▶ deploy-batch (백엔드 성공 시에만)
│ └──▶ deploy-frontend (프론트 변경 + 백엔드 성공 or skip 시)
└──▶ deploy-frontend (프론트만 변경된 경우 백엔드 skip → 즉시 실행)

통합 워크플로 — BE→FE 순서를 job 의존성으로 강제

변경 감지

detect-changes job이 커밋의 변경 파일을 분석해 boolean 두 개를 만든다.

deploy.yml
outputs:
backend: ${{ steps.filter.outputs.backend }}
frontend: ${{ steps.filter.outputs.frontend }}

packages/**는 backend·frontend 필터 양쪽에 들어 있다. 공유 패키지가 바뀌면 둘 다 true가 된다. (dorny/paths-filter 사용)

까다로운 건 프론트엔드 실행 조건

deploy.yml
deploy-frontend:
needs: [detect-changes, deploy-backend]
if: |
always() &&
needs.detect-changes.outputs.frontend == 'true' &&
(needs.deploy-backend.result == 'success' || needs.deploy-backend.result == 'skipped')

세 시나리오를 한 조건으로 처리한다.

시나리오backend 결과frontend 실행이유
packages/** 변경success백엔드 완료 후 순차 실행
packages/** 변경failure깨진 백엔드에 새 FE를 안 올린다
apps/frontend/**skipped백엔드 변경 없으니 즉시 실행

always()가 없으면 needs의 job이 skipped일 때 이 job 자체가 실행되지 않는다. always()로 일단 평가를 강제한 뒤, 조건을 직접 체크한다. GitHub Actions의 기본 동작(앞 job이 skip이면 뒤 job도 skip)을 우회하는 흔한 패턴이다.

배치도 같은 논리

deploy.yml
deploy-batch:
needs: [detect-changes, deploy-backend]
if: |
always() &&
needs.detect-changes.outputs.backend == 'true' &&
needs.deploy-backend.result == 'success'

배치는 백엔드가 성공했을 때만 돈다. 마이그레이션이 끝난 스키마를 전제로 동작해야 하기 때문이다. 백엔드 실패는 곧 마이그레이션 실패 가능성이므로, 그럴 땐 배치도 구버전을 유지한다. (1편의 문제 5 해결)

무엇이 바뀌었나

변경 전:

packages/** push
├─ (동시) deploy-backend → ~20분
└─ (동시) deploy-frontend → ~5분 → 먼저 완료
↑ 구 BE + 새 FE 공존

변경 후:

packages/** push
└─ detect-changes
└─ deploy-backend → 완료 후
└─ deploy-frontend

always() + 직접 조건 체크라는 작은 패턴 하나로, “FE가 먼저 나가는” 구조적 레이스를 없앴다.

남은 것: 마이그레이션은 성공했는데 ECS 배포가 실패하는 경우의 자동 롤백. 다음 편에서 다룬다.

↑↓ 이동 열기esc 닫기