Cloudflare Pages에 올렸더니 빈 화면, 원인은 비어 있던 빌드 설정
9월 18일, 아이와 같이 만든 게임을 올려두는 사이트 Juni Space를 Cloudflare Pages에 처음 배포했습니다. 화면이 하얗게 나왔습니다. 원인은 Pages 프로젝트의 빌드 명령과 출력 폴더가 비어 있던 것이었습니다. 빌드하지 않은 저장소가 그대로 서빙되고 있었습니다.
같은 증상을 검색해서 들어오신 분을 위해 결론부터 적습니다.
- 배포된 페이지의 HTML 소스를 열어 보세요.
<script type="module" src="/src/main.ts">처럼 소스 파일을 가리키고 있으면 빌드 안 된 원본이 올라간 겁니다. 정상 배포라면/assets/index-xxxx.js같은 빌드 결과물을 가리킵니다. - Pages 설정에서 Build command(Vite라면 보통
npm run build)와 Build output directory(dist)를 확인하세요. - 설정을 저장해도 지금 떠 있는 배포는 바뀌지 않습니다. 새 배포가 한 번 돌아야 적용됩니다.
증상: 전부 정상인데 화면만 하얗다
순서대로 적으면 이랬습니다.
- Pages 프로젝트에 사용자 지정 도메인
junispace.com을 연결했습니다. 대시보드에는 Active, SSL enabled로 떴습니다. - 바로 열어 보니 522 오류가 났습니다.
- 조금 뒤
junispace.pages.dev주소로 열어 보니 이번엔 오류는 없었습니다. 브라우저 탭에 사이트 제목도 떴습니다. 그런데 본문이 하얬습니다.
도메인 쪽을 의심하기 딱 좋은 상황입니다. 방금 도메인을 붙였고, 첫 응답이 522였으니까요. DNS가 아직 덜 퍼졌거나 SSL 발급이 덜 끝났겠거니 하고 기다리는 게 가장 그럴듯해 보입니다.
하지만 pages.dev 주소도 똑같이 하얬습니다. 이 주소는 제 도메인과 무관합니다. 도메인 문제라면 여기는 멀쩡해야 합니다.
원인 1: 빌드 설정이 비어 있었다
Pages 프로젝트 설정을 열어 보니 Framework preset은 None이었고, Build command와 Build output directory가 둘 다 비어 있었습니다.
이 상태에서 Pages는 아무것도 빌드하지 않고 저장소 파일을 그대로 올립니다. 이 사이트는 Vite + TypeScript 프로젝트입니다. 저장소 루트의 index.html은 개발 서버용이라서 이렇게 생겼습니다.
<script type="module" src="/src/main.ts"></script>
개발 서버(vite)는 이 .ts 파일을 요청받는 순간 자바스크립트로 변환해서 내줍니다. 그래서 로컬에서는 아무 문제가 없습니다. 반면 정적 호스팅은 변환해 주지 않고 main.ts 파일을 그대로 내보냅니다.
탭에 제목이 뜬 이유도 여기서 설명됩니다. <title>은 HTML에 그대로 적혀 있으니 표시됩니다. 화면을 그리는 코드는 전부 main.ts 안에 있는데, 그게 실행되지 않으니 본문이 비어 있던 겁니다.
원인 2: .ts 파일이 동영상으로 서빙됐다
공개 주소에서 /src/main.ts를 직접 요청해 보니 응답의 Content-Type이 video/mp2t였습니다.
.ts라는 확장자는 TypeScript만 쓰는 게 아닙니다. 방송·스트리밍에 쓰는 MPEG 전송 스트림(Transport Stream) 파일도 .ts입니다. 웹 서버 입장에서는 .ts가 동영상 확장자인 거죠.
type="module" 스크립트는 응답이 자바스크립트 MIME 타입이 아니면 브라우저가 실행을 거부합니다. 설령 서버가 TypeScript 문법을 자바스크립트라고 우겨서 내보냈더라도, 브라우저는 TypeScript를 실행할 수 없습니다. 어느 쪽이든 빌드가 빠지면 실행될 길이 없었습니다.
원인 3: 설정을 고쳤는데도 그대로였다
Build command에 npm run build, Build output directory에 dist를 넣고 저장했습니다. 그런데 다시 열어 봐도 하얬습니다.
두 주소를 다시 확인했습니다. 둘 다 HTTP 200이었고, 응답 HTML은 여전히 /src/main.ts를 가리키고 있었습니다. 빌드 결과물이라면 /assets/ 아래의 해시 붙은 .js를 가리켜야 합니다.
설정 저장은 다음 배포부터 적용됩니다. 이미 떠 있는 배포는 예전 설정으로 만들어진 것이라 그대로 남아 있습니다. 제 경우는 설정만 고쳐 두고 재배포를 하지 않은 상태였습니다. 결국 그날 오후 다음 커밋을 push해서 새 배포가 돌고 난 뒤에야 정상 화면이 나왔습니다.
돌아보면
이 일에서 제가 틀린 판단은 **“200이 나오고 대시보드가 Active니까 배포는 됐다”**였습니다.
- Active는 도메인 연결 상태입니다. 그 도메인 뒤에서 무엇이 서빙되는지는 말해 주지 않습니다.
- 200은 파일이 있다는 뜻입니다. 그 파일이 빌드 결과물인지 원본 소스인지는 말해 주지 않습니다.
- 522는 연결 직후에 잠깐 본 것이었습니다. 그걸 계속 원인으로 붙들고 있었다면 DNS가 퍼지기를 한참 기다렸을 겁니다.
pages.dev주소도 하얗다는 사실 하나로 도메인 쪽은 배제할 수 있었습니다.
다음에 정적 사이트를 처음 배포할 때는 확인 순서를 바꾸려고 합니다. 대시보드 상태가 아니라 실제 응답 HTML에서 스크립트 경로부터 봅니다. /src/가 보이면 빌드가 빠진 것이고, /assets/가 보이면 그다음 문제를 찾습니다. 화면을 보는 것보다 빠르고, 원인이 훨씬 좁혀집니다.