← 아티클 목록

개발일지WhoAmI — 공부 기록을 발행 가능한 형태로 좁힌 파이프라인 →

배포 경로를 갈아타고, 옛 경로를 지우기까지 닷새가 걸렸다

임시 주소의 사이트를 Cloudflare Workers Static Assets로 옮긴 뒤 옛 빌드 경로를 걷어 냈다. 당시의 정적 전용 배포와 지금의 /share 전용 Worker, 검증된 dist를 올리는 현재 배포 경계를 시점별로 다시 정리한 기록.

목차 5

2026-08-09 정정 — 7월 30일의 정적 자산 전용 배포와 8월 6일 이후의 /share/* Worker·KV 구조를 구분했다. 현재 배포의 라우팅과 CI·로컬 진입점도 실제 구성에 맞게 바로잡았다.

첫 배포는 7월 17일이었다. 정적 사이트를 만들어 올리는 일 자체가 확인해 보고 싶은 대상이었으므로 임시로 받은 하위 도메인에 그냥 올려 봤다.

개인 도메인을 붙이려고 보니 그 서비스에서는 내가 가진 apex 도메인을 연결하거나, 나중에 옛 주소에서 새 주소로 보내는 301 리다이렉트를 설정할 수 없었다. 주소를 옮기는 비용은 검색 노출과 외부 링크가 쌓일수록 커진다. 아직 검색엔진에 등록하지 않았고 외부 링크도 쌓이기 전이라, 도메인을 붙이기 전에 배포 경로부터 옮겼다.

7월 30일에는 Worker 스크립트 없이 정적 자산만 올렸다

Git 이력의 7월 30일 wrangler.jsonc를 다시 열어 보니 main은 없었다. 배포 내용은 assets.directory: "./dist", not_found_handling: "404-page", HTML 경로 처리처럼 정적 자산 설정으로만 이뤄져 있었다. 옛 경로에서 404 폴백을 위해 만들던 서버 진입 파일을 정적 자산 설정으로 바꾼 시점이다. Cloudflare의 SSG·404 문서도 자산과 일치하지 않는 요청에 가장 가까운 404.html을 404 상태로 내보내는 이 구성을 설명한다.

따라서 당시 설정에는 실행할 Worker 코드가 없었고, 정적 자산 요청으로만 구성됐다. 이것은 7월 30일 설정과 Cloudflare 문서를 대조한 결론이지, 당시의 호출·과금 대시보드를 측정한 결과는 아니다.

전환 당시에는 npm run deploy가 빌드를 먼저 돌고 wrangler deploy로 이어지게 했다. 옛 경로로 빌드한 직후의 dist를 그대로 올리면 사이트가 깨질 수 있었기 때문이다. 현재는 이 명령 앞부분이 단순 빌드가 아니라 release 검증으로 바뀌었다.

도메인 자체는 마지막에 붙였다. 페이지 안의 canonical·robots.txt·RSS 같은 공개 주소는 src/data/site.ts의 SITE_URL을 함께 참조하므로 그 값 한 곳을 바꿨다. 배포 쪽에서는 별도로 wrangler.jsonc의 Custom Domain route에 apex 도메인을 추가했고, www 리다이렉트는 Cloudflare 대시보드 규칙으로 뒀다. 실제 7월 30일 도메인 전환 커밋은 이 두 설정뿐 아니라 README와 배포·SEO 문서, 프로젝트의 주소 표기까지 모두 여덟 파일을 고쳤다. 콘텐츠가 가리키는 주소와 트래픽이 들어오는 주소, 운영 문서의 주소는 서로 다른 수정 지점이었다.

지금은 /share/*에서만 Worker를 먼저 실행한다

8월 6일, 사이트에 싣지 않을 문서를 기한 있는 링크로 보여 주는 기능이 들어오면서 구조가 달라졌다. 현재 wrangler.jsonc에는 세 가지가 함께 있다.

{
  "main": "worker/index.js",
  "kv_namespaces": [{ "binding": "SHARE", "id": "..." }],
  "assets": {
    "directory": "./dist",
    "binding": "ASSETS",
    "run_worker_first": ["/share/*"],
    "not_found_handling": "404-page"
  }
}

일반 글·이미지·CSS처럼 dist의 자산과 일치하는 요청은 Worker 코드보다 자산이 먼저 처리된다. /share/*만 run_worker_first에 명시돼 worker/index.js가 먼저 받고, SHARE KV binding에서 문서를 읽는다. 공유 화면의 껍데기가 필요할 때는 Worker가 ASSETS binding으로 빌드된 정적 페이지를 읽는다. Cloudflare의 Worker 스크립트 라우팅 문서는 배열에 적은 경로만 Worker-first로 두는 동작을, KV binding 문서는 설정의 binding이 Worker의 env에 연결되는 방식을 설명한다.

여기서 범위를 더 좁혀야 한다. /share/*는 Worker를 먼저 실행하도록 강제한 유일한 업무 경로지만, Worker가 실행될 수 있는 유일한 경우는 아니다. Cloudflare의 SSG 라우팅 도표와 현재 설정을 대조하면 세 갈래로 나뉜다.

  • 요청과 일치하는 일반 정적 자산이 있으면 Worker를 실행하지 않고 자산을 서빙한다.
  • /share/*는 자산 유무와 관계없이 Worker를 먼저 실행한다.
  • /share/* 밖에서 자산과 일치하지 않으면, 브라우저 navigation 요청은 현재 compatibility date와 not_found_handling에 따라 404 자산 처리로 가지만 non-navigation 요청은 Worker를 실행할 수 있다. 이때 현재 코드는 env.ASSETS.fetch(request)로 다시 넘긴다.

Cloudflare 과금 문서 기준으로 정적 자산 요청 자체는 무료이고, Worker 스크립트가 실제로 호출된 요청은 Workers 과금 규칙을 따른다. 이번에는 실제 요청 수나 청구 내역을 측정하지 않았으므로 위 라우팅 경계까지만 확인했다.

되돌릴 길을 남겼고, 그 길이 위험으로 남았다

옛 빌드 경로를 바로 지우지 않았다. 첫 Cloudflare 배포가 실제로 잘 되는지 확인하기 전까지 돌아갈 곳을 닫고 싶지 않아서, build:sites 명령과 그 스크립트를 그대로 뒀다. 스크립트 안에는 「첫 배포 후 지운다」고 주석까지 적어 두었다.

닷새 뒤 저장소를 감사하다 그 판단의 대가가 나왔다. 옛 스크립트는 dist/를 dist/client와 dist/server로 재배치하고, Cloudflare Static Assets 설정은 dist/ 전체를 배포 대상으로 읽었다. 즉 두 빌드 경로가 같은 dist/를 서로 다른 모양으로 쓰고 있었다. 코드 경로를 대조하면 build:sites 뒤에 wrangler deploy를 직접 실행할 경우 재배치된 트리를 배포 대상으로 고르게 된다. 실제로 그 순서로 오배포를 실행한 것은 아니며, 당시 문서에 그 직접 명령이 남아 있어 가능한 실패 경로로 판정했다.

당시 저장소를 검색한 범위에서는 그 순서를 자동으로 부르는 곳이 없었다. 위험은 「사람이 순서를 잘못 밟으면」이었고, 그래서 심각도는 Low로 뒀다. 그래도 지운 이유는 되돌릴 길로 남겨 둔 목적이 도메인 전환 완료와 함께 사라졌기 때문이다.

공개 범위 점검 스크립트에 있던 dist/client 폴백도 함께 지웠다. 그 구조를 만드는 것은 옛 스크립트뿐이었으니, 폴백만 남기면 현재 빌드 산출물과 다른 디렉터리를 검사 대상으로 받아들이는 규칙이 계속 남는다. 배포 대상과 검사 대상을 dist/ 하나로 맞췄다.

지운 뒤에도 기록은 남겼다

코드는 지웠지만 문서는 지우지 않았다. README의 「배포 이력」에 이전 주소, 그 스크립트가 하던 일, 옮긴 이유, 바로 지우지 않은 이유, 남아 있던 위험을 표로 남겼다. 실행 안내만 뺐다.

지금은 진입점이 달라도 같은 dist 경계를 지난다

현재 배포 실행 진입점은 하나가 아니다.

  • main 푸시 — GitHub Actions가 npm run release를 통과한 뒤 같은 작업의 dist/에 npx wrangler deploy를 실행한다.
  • npm run deploy — 로컬에서 색인 차단 모드의 release를 거친 뒤 wrangler deploy를 실행한다.
  • npm run deploy:public — 로컬에서 색인 허용 모드의 release:public을 거친 뒤 같은 배포 명령을 실행한다.

세 진입점의 공통 경계는 release 계열 명령이 검증과 dist/ 빌드를 끝낸 뒤에만 Wrangler를 실행한다는 것이다. wrangler deploy는 그 dist/를 다시 빌드하지 않고, 현재 worker/index.js와 정적 자산을 한 배포로 묶으며 KV binding과 라우팅 설정도 적용한다. CI의 npx wrangler deploy는 앞 단계와 한 워크플로에 묶여 있지만, 그 명령만 따로 실행하면 release를 건너뛸 수 있다. 그래서 로컬 문서는 npm run deploy 또는 npm run deploy:public을 진입점으로 둔다.

이번 정정에서 직접 확인한 것은 현재 package.json, wrangler.jsonc, worker/index.js, GitHub Actions와 7월 30일·8월 4일·8월 6일 Git 이력이다. 요청 라우팅은 Cloudflare 공식 문서와 이 설정을 대조했고, 실제 호출 수나 비용은 대시보드로 측정하지 않았다.

남은 규칙은 하나다. 되돌릴 길을 남길 때는 언제 닫을지를 함께 정한다. 조건 없이 남긴 경로는 목적이 끝난 뒤에도 저장소에 남고, 그때부터는 되돌릴 길이 아니라 잘못 밟을 수 있는 길이다.

함께 보면 좋은 글

이 기록과 태그가 겹치는 아티클입니다.