컨베이어 위의 여러 빌드 블록 중 앞선 대기 작업을 건너뛰고 최신 작업을 남기는 배포 큐 일러스트

Cloudflare Pages superseded queued builds 건너뛰기 전 배포 순서 확인

Cloudflare Pages superseded queued builds 변경 이후에는 연속으로 push한 커밋이 모두 Pages build command를 실행한다고 가정할 수 없다. 2026년 8월 11일부터 같은 프로젝트·브랜치·deployment target에 속한 더 최신 빌드도 queued 상태라면 앞선 queued build가 자동 생략 대상이 된다. 큐가 빨라지는 대신, Pages 빌드를 커밋별 테스트 기록으로 사용하던 운영 방식은 다시 확인해야 한다.

이 글은 2026년 8월 14일 기준 Cloudflare 공식 문서와 로컬 Node 상태 전이 모델을 대조한 결과다. 실제 Cloudflare 계정에서 동시 push나 dashboard 표시를 재현하지 않았다. 로컬 비교 역시 제품 스케줄러를 독립적으로 검증한 것이 아니라, 공개된 조건을 논리적으로 검증한 것이다. 아래에서는 어떤 작업이 남고 사라지는지부터 실제 production commit을 확인하는 지점까지 차례로 살펴본다.

20초 핵심 요약

  • 무엇: 같은 project·branch·deployment target 안에서 더 최신 빌드도 queued일 때 앞선 queued build를 생략하는 자동 동작이다.
  • : 생략된 빌드에서는 build command가 실행되지 않을 수 있어 Pages에 맡긴 커밋별 테스트와 감사 기록이 빠질 수 있다.
  • 어떻게: 필수 검증은 별도 CI로 옮기고 Deployments에서 branch, environment, stage/status, skipped 여부와 production commit을 대조한다.

큐가 줄어드는 단위는 최신 커밋 하나가 아니라 동일한 세 값이다

새 동작을 “항상 최신 커밋만 배포한다”로 요약하면 범위를 지나치게 넓힌다. Cloudflare changelog가 밝힌 조건은 다음 네 가지다.

  1. 앞선 build가 queued 상태다.
  2. 더 최신 build도 queued 상태다.
  3. 두 build의 project가 같다.
  4. branch와 deployment target도 각각 같다.

(project, branch, deployment target)을 하나의 큐 키로 보고, 그 키 안에서 뒤의 queued 작업이 앞의 queued 작업을 대체한다고 이해할 수 있다. 실행 중인 작업이나 다른 키에 속한 작업까지 큐 전체에서 지우는 규칙은 아니다.

push / deployment trigger
        │
        ▼
deployment 후보 생성
        │
        ├─ project가 같은가?
        ├─ branch가 같은가?
        ├─ deployment target이 같은가?
        └─ 더 최신 후보도 queued인가?
                 │
          모두 참 ──► 앞 queued build 생략 대상
          하나라도 거짓 ──► 별도 작업으로 유지

동시 빌드 제한이 있어 이 구분은 실제 운영에서도 의미를 갖는다. 확인일 기준 Pages의 account 단위 동시 빌드 수는 Free 1개, Pro 5개, Business 20개이고, 빌드는 20분 뒤 timeout된다. push 유입이 처리 속도보다 빠르면 큐가 생긴다. 오래된 queued 작업을 생략하면 최신 상태의 대기 시간을 줄일 수 있다. 다만 이는 Pages limits와 새 규칙을 연결한 운영 해석이다. Cloudflare가 절약 시간이나 내부 큐 알고리즘을 발표한 것은 아니다.

A·B·C·D 상태로 보면 running과 preview가 남는 이유가 보인다

같은 순서로 들어온 네 작업을 두고 기존 FIFO와 문서 조건 모델을 비교했다.

작업 상태 branch target 모델 결과
A running main production 유지
B queued main production D가 대신해 생략
C queued docs preview 유지
D queued main production 유지
$ node -e 'const jobs=[{id:"A",project:"site",branch:"main",target:"production",state:"running"},{id:"B",project:"site",branch:"main",target:"production",state:"queued"},{id:"C",project:"site",branch:"docs",target:"preview",state:"queued"},{id:"D",project:"site",branch:"main",target:"production",state:"queued"}]; const kept=jobs.filter((job,i)=>!(job.state==="queued"&&jobs.slice(i+1).some(newer=>newer.state==="queued"&&newer.project===job.project&&newer.branch===job.branch&&newer.target===job.target))); console.log(JSON.stringify({input:jobs.map(x=>x.id),baseline_fifo:jobs.map(x=>x.id),documented_rule_model:kept.map(x=>x.id),skipped:jobs.filter(x=>!kept.includes(x)).map(x=>x.id)}));'
{"input":["A","B","C","D"],"baseline_fifo":["A","B","C","D"],"documented_rule_model":["A","C","D"],"skipped":["B"]}
exit 0

FIFO 대조군은 A → B → C → D를 모두 유지했지만 문서 조건 모델은 A → C → D를 남겼다. B와 D만 project·branch·target이 같고 둘 다 queued라는 조건을 충족하기 때문이다. A는 이미 running이며, C는 branch와 target이 다르다.

다른 branch와 target까지 제거된다고 가정하니 비교는 실패했다.

$ node -e 'const assert=require("node:assert/strict"); const jobs=[{id:"C",project:"site",branch:"docs",target:"preview",state:"queued"},{id:"D",project:"site",branch:"main",target:"production",state:"queued"}]; const kept=jobs.filter((job,i)=>!(job.state==="queued"&&jobs.slice(i+1).some(newer=>newer.state==="queued"&&newer.project===job.project&&newer.branch===job.branch&&newer.target===job.target))); try{assert.deepEqual(kept.map(x=>x.id),["D"])}catch(error){console.error(`ASSERTION FAILED: expected ["D"], observed ${JSON.stringify(kept.map(x=>x.id))}; different branch/target is not superseded`); process.exitCode=1;}'
ASSERTION FAILED: expected ["D"], observed ["C","D"]; different branch/target is not superseded
exit 1

이 결과는 Cloudflare의 실제 scheduling 로그가 아니다. 공식 문장을 그대로 적용했을 때 어떤 가정이 모순되는지를 확인한 통제 비교다. 특히 changelog가 queued를 앞뒤 작업에 모두 붙였으므로, 새 작업이 들어오면 running build도 자동 취소된다고 단정할 근거는 없다.

검증과 배포를 한 줄에 놓았던 구조를 두 책임으로 나눈다

이전 workflow가 Pages의 build command 안에서 테스트까지 수행했다면 push 하나가 두 역할을 겸했다. 커밋을 검증하고, 성공한 산출물을 배포하는 역할이다. 이제 중간 queued build가 생략될 수 있으므로 두 역할의 실행 횟수가 같다는 전제가 깨진다. Build configuration에서 build command의 nonzero 종료는 실행된 빌드를 실패로 만들 뿐, 실행되지 않은 빌드의 검증을 보완하지 않는다.

운영 책임은 다음처럼 분리하는 편이 안전하다.

  • Git provider와 별도 CI는 모든 merge 대상 커밋의 테스트, 정책 검사, 승인과 merge gate를 맡는다.
  • Pages scheduler는 같은 큐 키 안에서 실제 실행할 queued 작업을 선택한다.
  • Pages deployment와 alias는 성공한 산출물을 production 또는 preview 주소에 연결한다.

이 구조는 중간 Pages 산출물이 없어도 커밋별 필수 검증을 보존할 수 있다는 장점이 있다. 대신 CI와 배포 상태를 두 시스템에서 함께 확인해야 한다. 따라서 최신 production 반영이 중요하고 중간 커밋별 Pages 산출물이 필요하지 않은 팀에 알맞다.

모든 커밋의 Pages 결과가 감사 증적이거나 배포 승인·직렬화·커밋 선택을 한 pipeline이 통제해야 한다면 다른 구조가 필요하다. Git integration 문서가 안내하듯 production과 preview의 자동 배포를 끄고 외부 CI에서 Wrangler로 명시적으로 배포할 수 있다. 이 선택은 순서를 통제하는 대신 인증정보 보호, 재시도, 멱등성 책임을 사용자 pipeline으로 옮긴다.

Production과 preview는 하나의 완료 순서로 합치지 않는다

Production branch의 커밋은 production deployment를 만든다. 그 밖의 branch는 설정에 따라 preview deployment를 만든다. Preview는 production에 영향을 주지 않는 별도 환경이다. 따라서 main/productiondocs/preview를 하나의 선에 세우고 어느 쪽이 먼저 배포돼야 한다고 판단하면 branch와 target 경계를 잃는다.

Preview deployments에 따르면 각 preview deployment에는 고유한 hash 기반 URL이 생기고 과거 URL도 직접 접근할 수 있다. 반면 branch alias는 해당 branch의 최신 deployment로 갱신된다. 고유 deployment URL은 특정 산출물을 확인하는 주소이고, branch alias는 최신 branch 상태를 보는 주소다.

다만 changelog는 deployment target의 내부 식별 방식을 설명하지 않는다. Deployments API의 environmentpreview 또는 production으로 표현되므로 최소한의 경계 단서로 사용할 수 있지만, 서로 다른 trigger source나 같은 preview 환경 안의 세부 target을 어떻게 비교하는지는 공개 자료만으로 확정할 수 없다.

배포 목록에서는 생성 순서보다 상태와 연결된 commit을 확인한다

연속 push 뒤 “무엇이 실제 production인가”를 알아내려면 commit 생성 시각만 정렬해서는 부족하다. 다음 항목을 함께 봐야 한다.

  1. 연속 push의 commit SHA와 branch를 기록한다.
  2. Pages의 Deployments 목록에서 각 항목의 environment를 구분한다.
  3. is_skipped, latest_stage, stages를 통해 queued·build·deploy 상태를 나눈다.
  4. production URL에 연결된 commit이 의도한 최신 유효 commit인지 확인한다.
  5. preview는 고유 deployment URL과 branch alias 중 어떤 주소를 보고 있는지 구분한다.

Deployments API는 deployment URL, environment, skipped 여부와 stage/status를 제공한다. 빌드 안에서 식별 정보를 남겨야 한다면 CF_PAGES_COMMIT_SHA, CF_PAGES_BRANCH, CF_PAGES_URL을 산출물이나 안전한 공개 메타데이터에 연결할 수 있다. 이 값들은 실행된 build를 식별할 뿐, 생략된 build의 테스트를 대신하지 않는다.

GitHub check가 없다는 사실 하나만으로 superseded 생략이라고 판정해서도 안 된다. commit message의 skip, build watch paths, branch controls 등 check run이나 commit status가 나타나지 않을 수 있는 다른 경로가 있다. 더구나 확인일 현재 API 문서의 공개 skip_reason 열거형에서는 superseded 전용 값을 확인하지 못했다. API가 해당 사유를 표현할 수 없다는 뜻이 아니라, 공개 문서에서 전용 값을 찾지 못했다는 뜻이다.

자동 생략을 받아들이기 어려운 세 경우

첫째, 모든 커밋의 Pages build 결과가 감사 기록이어야 한다면 새 동작과 요구사항이 충돌한다. 별도 CI에 검증 증적을 남기거나 자동 Pages 배포를 끄고 명시적 pipeline으로 전환해야 한다.

둘째, 목적이 큐 압축이 아니라 불필요한 trigger 자체를 막는 것이라면 다른 제어가 맞다. Branch deployment controls의 all·none·custom 범위, build watch paths, commit message skip은 작업이 큐에 들어오기 전에 trigger를 제한한다. 필요한 preview까지 제외할 수 있으므로 superseded 생략과 같은 기능으로 취급해서는 안 된다.

셋째, 생략된 commit으로 즉시 rollback해야 한다면 요구가 성립하지 않는다. Rollbacks의 대상은 성공적으로 build된 production deployment다. Preview deployment나 성공 산출물이 없는 생략 commit을 만능 복구 지점으로 볼 수 없다.

확인한 changelog에는 superseded 자동 생략만 끄는 전용 설정도 제시되지 않았다. 설정을 찾는 대신 “중간 Pages 산출물이 필요한가”와 “커밋별 검증이 이미 Pages 밖에서 보장되는가”를 먼저 답해야 한다.

적용 전에는 최신 배포 속도보다 빠진 증적을 먼저 찾는다

채택 조건은 한 문장으로 확인할 수 있다. 중간 커밋의 Pages 산출물은 필요하지 않고, Pages build가 생략돼도 커밋별 필수 검증은 별도 CI에 남는가. 이 조건을 충족한다면 자동 큐 압축을 받아들이고 Pages를 최신 branch/target의 배포기로 두는 선택이 맞다.

반대로 하나라도 충족하지 못하면 Pages deployment를 모든 커밋의 테스트 기록으로 사용하지 않는다. 실제 프로젝트의 Deployments 목록에서 연속 커밋의 branch, environment, stage/status, skipped 여부와 production에 연결된 commit을 먼저 확인한다. Dashboard 문구, superseded API payload, 월간 build count 반영, 내부의 newer 판정 기준은 아직 공개 근거와 실제 계정 검증이 부족하므로 그 결과까지 추정해서 운영 규칙으로 만들면 안 된다.

참고 링크

비슷한 글

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다