테마 전환

CF Pages 빌드 실패? 반나절의 디버깅 시간을 아껴 주는 8가지 일반적인 문제와 해결 방법

Easton editorial illustration: instruction-to-result workspace

Cloudflare Pages 빌드 로그에 빨간색으로 “Failed”가 표시됩니다. 오늘 밤에만 벌써 다섯 번째 실패이고, 내일 아침에는 고객에게 시연해야 합니다. 500줄에 달하는 빌드 로그는 빽빽하고 화면에는 npm ERR!가 가득한데 어느 줄부터 봐야 할지 막막합니다. 인터넷에서 찾은 방법을 시도했지만 어떤 것은 효과가 없고 어떤 것은 상황을 더 악화시킵니다.

대부분의 CF Pages 빌드 실패는 환경 차이, 의존성 설정, 버전 호환성이라는 세 범주를 벗어나지 않습니다. 이 규칙을 익히면 문제의 90%를 10분 안에 해결할 수 있습니다. 이 글에서는 Cloudflare Pages 빌드 환경을 체계적으로 살펴보고, 가장 흔한 8가지 빌드 실패 상황을 실제 오류 메시지와 전체 해결 단계와 함께 정리합니다. 예방을 위한 설정 방법도 다룹니다. 끝까지 읽으면 명확한 문제 해결 흐름을 세울 수 있을 것입니다.

1부: Cloudflare Pages 빌드 환경 이해하기

Pages 빌드 환경의 특수성

구체적인 문제를 조사하기 전에 먼저 알아야 할 사실이 있습니다. Cloudflare Pages의 빌드 환경은 로컬 개발 환경과 근본적으로 다릅니다. Pages 배포에서 오류가 나는 이유가 코드 자체의 잘못이 아니라 환경 차이인 경우가 많습니다.

기본 설정은 다음과 같습니다.

Ubuntu 22
운영체제
Build System V2에서 사용
18.17.1
Node 버전
기본 버전이 비교적 오래되어 새 패키지와 호환되지 않을 수 있음
20분
빌드 시간 제한
초과 시 종료되는 고정 제한
10MB
Worker 크기 상한
Functions 번들링 후 제한
  • 운영체제: Ubuntu(Build System V2는 Ubuntu 22 사용)
  • Node 버전: 18.17.1(맞습니다. 꽤 오래된 버전입니다.)
  • 패키지 관리자: 기본적으로 npm install이 아니라 npm clean-install 사용
  • 빌드 시간 제한: 최대 20분
  • Worker 크기: 최대 10MB

Node 버전이 왜 이렇게 오래되었는지 궁금할 수 있습니다. Cloudflare가 안정성을 중시하기 때문입니다. 하지만 많은 새 패키지는 이미 Node >= 18.18.0 또는 >= 20.0.0을 요구하므로 버전 충돌이 발생합니다.

로컬 환경과의 세 가지 핵심 차이:

  1. 파일 시스템의 대소문자 구분: Windows나 Mac에서 import Header from './header'라고 작성하면 파일명이 Header.js여도 실행될 수 있습니다. 그러나 Linux에서는 대소문자가 정확히 일치해야 합니다. 가장 놓치기 쉬운 문제입니다.
  2. 네트워크 환경 차이: 로컬에는 Taobao 같은 npm 미러가 설정되어 있을 수 있지만 Pages 빌드 환경은 npm 공식 레지스트리에 직접 연결하므로 시간 초과가 발생할 수 있습니다.
  3. 기본 빌드 명령의 차이: Cloudflare는 build command 앞에 npm clean-install --progress=false를 자동으로 실행합니다. 이 명령은 npm install보다 훨씬 엄격해서 package-lock.json과 package.json이 일치하지 않으면 오류가 발생합니다.

빠르게 문제를 찾는 방법

이제 환경이 다르다는 사실을 알았습니다. 그렇다면 Pages 배포 오류가 발생했을 때 실제 원인을 어떻게 빠르게 찾을 수 있을까요?

첫 번째 단계: 빌드 로그 읽기

빌드 로그는 수백 줄에 달하지만 실제로는 몇 군데만 보면 됩니다.

# 마지막 ERR! 또는 ERROR 찾기
npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
# Vite/Webpack 오류 확인
[vite]: Rollup failed to resolve import
# Git 관련 오류 확인
fatal: unable to access repository

제 경험상 “ERR!”을 느낌표까지 포함해 검색한 다음 위쪽 3~5줄을 보면 대개 문제의 근본 원인이 나옵니다. 앞부분의 긴 설치 출력에 현혹되지 마세요.

두 번째 단계: Deployment ID 저장

빌드가 실패할 때마다 Cloudflare는 고유한 Deployment ID를 생성합니다. 브라우저 주소 표시줄에서 다음과 같은 형태로 확인할 수 있습니다.

https://dash.cloudflare.com/xxx/pages/view/your-project/a398d794-7322-4c97-96d9-40b5140a8d9b
                                                          ↑ 이것이 Deployment ID입니다

이 ID를 저장하는 것은 매우 중요합니다. Cloudflare 지원팀에 문의하거나 커뮤니티에 도움을 요청할 때 ID가 있으면 다른 사람이 해당 빌드 기록을 바로 찾을 수 있습니다.

세 번째 단계: 로컬에서 문제 재현

많은 사람이 이 단계를 놓칩니다. 로컬 Linux 환경에서 다음과 같이 재현해 보세요.

# 방법 1: Docker로 Ubuntu 22 환경 재현
docker run -it ubuntu:22.04 bash
# 방법 2: Pages와 동일하게 npm ci 사용
npm ci
# 방법 3: nvm으로 Node 버전 지정
nvm use 18.17.1

로컬에서 npm ci를 실행해도 오류가 난다면 의존성 설정 문제입니다. Node 18.17.1로 바꿨을 때 실패한다면 버전 호환성 문제입니다.

2부: 가장 흔한 8가지 빌드 실패 상황과 해결 방법

문제 1: 의존성 설치 실패(npm install 오류)

대표적인 오류 메시지:

npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR! Fix the upstream dependency conflict, or retry this command
npm ERR! with --force or --legacy-peer-deps
또는
npm ERR! code ERR_SOCKET_TIMEOUT
npm ERR! network Socket timeout

제가 가장 자주 겪은 문제입니다. 로컬에서는 npm install이 잘 되는데 Pages에서는 ERESOLVE가 발생합니다. 이유는 간단합니다. Cloudflare는 기본적으로 매우 엄격한 npm ci를 사용합니다.

근본 원인:

  1. Cloudflare의 npm clean-install은 peer dependency 충돌을 자동으로 해결하지 않습니다.
  2. package-lock.json과 package.json이 동기화되지 않았습니다.
  3. 네트워크 시간 초과로 npm 공식 레지스트리에 연결하지 못합니다.

해결 방법(권장 순서):

방법 1: 기본 설치를 건너뛰고 명령 직접 지정

# Pages 설정에 환경 변수 추가
SKIP_DEPENDENCY_INSTALL=true
# Build command를 다음과 같이 변경
npm install --legacy-peer-deps && npm run build

가장 간단한 방법입니다. Cloudflare의 기본 명령을 사용하지 않고 의존성 설치를 직접 처리하도록 지정합니다.

방법 2: package-lock.json 복구

# 로컬에서 lock 파일 다시 생성
rm package-lock.json
npm install
git add package-lock.json
git commit -m "fix: regenerate package-lock.json"
git push

때로는 lock 파일이 꼬인 것이 전부이므로 다시 생성하기만 해도 해결됩니다.

방법 3: GitHub Actions가 빌드 담당

앞의 두 방법으로 해결되지 않으면 문제가 좀 더 복잡한 것입니다. GitHub Actions + cloudflare/pages-action으로 빌드하면 빌드 환경을 완전히 직접 제어할 수 있습니다.

# .github/workflows/deploy.yml
- name: Install dependencies
  run: npm install --force
- name: Build
  run: npm run build
- name: Deploy to Cloudflare Pages
  uses: cloudflare/pages-action@v1

예방 조치: 로컬에서 정기적으로 npm ci를 실행해 lock 파일의 동기화 상태를 확인하세요.

문제 2: Node 버전 비호환

대표적인 오류 메시지:

ERR_PNPM_UNSUPPORTED_ENGINE Unsupported environment
This package requires Node.js version ^18.18.0 or >=20.0.0
또는
The engine "node" is incompatible with this module.
Expected version ">=18.18.0". Got "18.17.1"

이런 오류는 대부분 Node 버전이 너무 오래되었을 때 발생합니다. TypeScript ESLint와 Next.js 14+를 비롯한 많은 새 패키지는 Node >= 18.18.0을 요구하지만 Pages의 기본 버전은 18.17.1입니다.

해결 방법(하나만 선택하면 됩니다):

방법 1: 환경 변수 설정(가장 권장)

Cloudflare Pages의 Settings > Environment variables에서 다음 값을 추가합니다.

변수명: NODE_VERSION
값: 20.11.0

공식적으로 권장되는 간단한 방법입니다.

방법 2: .node-version 파일 추가

프로젝트 루트에 .node-version 파일을 만듭니다.

echo "20.11.0" > .node-version
git add .node-version
git commit -m "chore: specify Node version for Cloudflare Pages"

방법 3: .nvmrc 파일 사용

앞의 방법과 같고 파일명만 다릅니다.

echo "20.11.0" > .nvmrc

권장 사항: 환경 변수와 .node-version 파일을 함께 사용하면 로컬과 온라인 환경의 버전을 맞출 수 있습니다. 버전을 선택할 때는 최신 버전보다 20.11.0 같은 안정적인 LTS 버전을 고르는 편이 안전합니다.

문제 3: 빌드 시간 초과(20분 초과)

대표적인 증상:

빌드 로그가 정확히 20분 동안 실행된 후 명확한 오류 없이 갑자기 종료됩니다. 다음 한 줄만 남습니다.

Build exceeded maximum time of 20 minutes

정보가 전혀 없어 특히 답답한 문제입니다. 대개 프로젝트가 크거나 의존성이 너무 많을 때 발생합니다.

근본 원인:

  • 프로젝트 의존성이 너무 많아 npm install에만 15분이 걸립니다.
  • 빌드 스크립트에 전체 사이트를 매번 다시 생성하는 것과 같은 반복 작업이 많습니다.
  • 빌드 캐시를 활용하지 않습니다.

해결 방법:

방법 1: 빌드 캐시 삭제

때로는 캐시가 오히려 부담이 됩니다. Pages 설정에서 다음 메뉴로 이동합니다.

Settings > Builds & deployments > Clear build cache

캐시를 지운 뒤 다시 빌드하세요. 저도 여러 번 이 방법만으로 해결한 적이 있습니다.

방법 2: 의존성 분석 및 최적화

bundle analyzer로 대형 의존성을 찾습니다.

# Next.js 프로젝트
npm install --save-dev @next/bundle-analyzer
# next.config.js에서 활성화
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
})
module.exports = withBundleAnalyzer({
  // 설정
})

ANALYZE=true npm run build를 한 번 실행해 특별히 큰 패키지를 확인하세요. 예전에 프로젝트에서 moment.js 전체를 가져온 부분을 day.js로 바꿔 빌드 시간을 3분 줄인 적이 있습니다.

방법 3: 일부 작업을 CI로 이동

typecheck와 lint 같은 오래 걸리는 작업을 GitHub Actions로 옮기고 Pages는 빌드만 담당하게 합니다.

// package.json
{
  "scripts": {
    "build": "next build",  // 빌드만 수행하고 검사하지 않음
    "build:full": "npm run typecheck && npm run lint && npm run build"  // 전체 과정은 CI에서 사용
  }
}

방법 4: pnpm 사용

pnpm은 npm보다 의존성 설치가 훨씬 빠릅니다. Pages 설정에서 pnpm을 사용하도록 변경합니다.

Build command: pnpm install && pnpm run build

문제 4: 모듈 해석 오류(Module not found)

대표적인 오류 메시지:

Module not found: Error: Can't resolve './App' in '/opt/buildhome/repo/src'
Did you mean 'App.js'?
또는
[vite]: Rollup failed to resolve import '/src/components/Snackbar'
from '/opt/buildhome/repo/src/pages/Login.jsx'

아주 알아차리기 어려운 오류입니다. 로컬에서는 잘 실행되는데 Pages에서 갑자기 모듈을 찾지 못합니다. 원인의 99%는 대소문자 문제입니다.

근본 원인:

Linux 파일 시스템은 대소문자를 엄격히 구분하지만 Windows와 macOS는 기본적으로 구분하지 않습니다. 파일명이 App.js인데 로컬에서 import App from './app'로 작성하면 Windows에서는 작동해도 Linux에서는 오류가 발생합니다.

해결 방법:

방법 1: 모든 import 경로 수정

가장 근본적인 해결책입니다. 모든 import 문을 검사해 대소문자가 정확히 일치하도록 수정합니다.

// ❌ 잘못된 예
import Header from './header';  // 파일명은 Header.jsx
// ✅ 올바른 예
import Header from './Header';

수동 검사는 번거로우므로 ESLint 규칙으로 자동 검사하는 것을 권장합니다.

// .eslintrc.js
module.exports = {
  rules: {
    'import/no-unresolved': 'error',  // 해석할 수 없는 import 감지
  }
}

방법 2: 경로 별칭 사용

절대 경로나 별칭을 사용하면 많은 문제를 피할 수 있습니다.

// vite.config.js
export default {
  resolve: {
    alias: {
      '@': '/src',
      '@components': '/src/components'
    }
  }
}
// 별칭으로 import
import Header from '@components/Header';  // 명확하고 이해하기 쉬움

방법 3: 커뮤니티에서 나온 특이한 방법

한 사용자는 다소 황당하지만 실제로 효과가 있었던 방법을 공유했습니다. 폴더 이름을 다른 이름으로 바꾸고 한 번 커밋한 다음 원래 이름으로 되돌렸더니 해결되었다는 것입니다. 이유는 정확히 알 수 없지만 캐시 문제일 수 있습니다. 앞의 방법으로 해결되지 않는다면 이 방법도 시도해 볼 수 있습니다.

문제 5: 환경 변수 설정 오류

대표적인 증상:

console.log(process.env.API_KEY); // undefined

또는 빌드 중 특정 환경 변수를 찾을 수 없다는 오류가 발생합니다.

근본 원인:

빌드 타임 환경 변수와 런타임 환경 변수를 혼동하는 경우가 많습니다. 프레임워크마다 환경 변수의 명명 규칙이 다르다는 점도 원인입니다.

핵심 개념:

Cloudflare Pages의 환경 변수는 두 종류입니다.

  1. 빌드 타임 변수: npm run build 중 사용할 수 있으며 코드에 컴파일됩니다.
  2. 런타임 변수: Functions(엣지 함수)에서만 사용할 수 있습니다.

정적 사이트(순수 HTML/JS)에서는 런타임 변수를 가져올 수 없고 빌드 타임 변수만 사용할 수 있습니다.

해결 방법:

방법 1: 올바른 환경 변수 유형 설정

Cloudflare Pages 설정에서 변수를 추가할 때 다음 항목을 확인합니다.

  • “Production”과 “Preview” 환경 선택
  • 빌드 시 필요한 변수라면 “Build” 옵션을 반드시 선택

방법 2: 프레임워크 명명 규칙 준수

프레임워크마다 요구 사항이 다릅니다.

# Vite 프로젝트: 반드시 VITE_로 시작
VITE_API_KEY=xxx
# Next.js 프로젝트: 공개 변수는 반드시 NEXT_PUBLIC_로 시작
NEXT_PUBLIC_API_KEY=xxx
# Nuxt 프로젝트: nuxt.config.js의 runtimeConfig 사용

방법 3: 민감한 정보는 Secret 유형 사용

Pages 설정의 환경 변수에는 두 가지 유형이 있습니다.

  • Text: 값이 표시됨
  • Secret: 값이 표시되지 않고 암호화되어 저장됨

API key와 데이터베이스 비밀번호는 반드시 Secret 유형을 사용하세요.

권장 사항:

  1. 로컬 개발에서는 .env.local 파일을 사용하고 .gitignore에 추가합니다.
  2. 온라인 환경에서는 Cloudflare Pages의 환경 변수 설정을 사용합니다.
  3. 환경마다 다른 값을 설정합니다(Preview에는 테스트 API, Production에는 프로덕션 API).

문제 6: Git 연동 문제

대표적인 증상:

  • 저장소 접근 권한을 부여할 수 없습니다.
  • “This repository is already in use by another Pages project” 오류가 발생합니다.
  • 코드를 Push해도 Pages 빌드가 자동으로 시작되지 않습니다.

근본 원인:

대개 GitHub/GitLab 권한 부여에 문제가 있거나 동일 저장소를 여러 계정에서 사용할 수 없다는 Cloudflare 제한을 위반한 경우입니다.

해결 방법:

방법 1: GitHub App 다시 승인

GitHub 설정에서 다음 메뉴로 이동합니다.

Settings > Applications > Cloudflare Pages > Configure > Uninstall

제거한 뒤 Cloudflare Dashboard에서 저장소를 다시 연결하면 재승인 절차가 시작됩니다.

방법 2: 저장소 사용 상태 확인

저장소가 이미 사용 중이라는 오류가 나오면 여러 Cloudflare 계정에서 같은 저장소를 사용하고 있는지 확인하세요. 이는 허용되지 않습니다. 다른 계정에서 해당 Pages 프로젝트를 삭제해야 합니다.

방법 3: GitHub 사용자 권한 확인

연동하려면 해당 저장소에 최소 Maintainer 권한이 필요합니다. Contributor 권한만으로는 연결할 수 없습니다.

방법 4: 특수문자 피하기

주의할 점이 있습니다. Commit message에 emoji나 특수문자를 사용하면 빌드 트리거가 실패할 수 있습니다. GitHub에서는 허용하지만 Cloudflare가 올바르게 해석하지 못할 수 있습니다.

알려진 제한: Fork 저장소의 PR은 미리보기 배포를 트리거하지 않습니다. Cloudflare는 향후 지원할 예정이라고 밝혔지만 현재는 지원되지 않습니다.

문제 7: Functions 배포 실패

대표적인 증상:

빌드는 성공했지만 마지막 배포 단계에서 실패하고 로그에는 유용한 정보가 거의 없습니다. 또는 다음 오류가 발생합니다.

Build failed: Functions bundle size exceeding limit

근본 원인:

  • Worker 함수 번들의 크기가 10MB 제한을 초과했습니다.
  • Functions의 Bindings(KV, D1, R2) 설정이 잘못되었습니다.
  • 엣지 환경에서 지원되지 않는 Node.js 전용 API를 사용했습니다.

해결 방법:

방법 1: Functions bundle 크기 분석

bundle analyzer로 어떤 항목이 큰지 확인합니다.

npm install --save-dev @next/bundle-analyzer

대개 tree-shaking이 되지 않아 라이브러리 전체가 번들에 포함된 경우입니다.

방법 2: Astro/SvelteKit adapter 설정 최적화

Astro 또는 SvelteKit을 사용한다면 Cloudflare adapter가 올바르게 설정되어 있는지 확인합니다.

// astro.config.mjs
import cloudflare from '@astrojs/cloudflare';
export default {
  output: 'hybrid',  // 또는 'server'
  adapter: cloudflare({
    mode: 'directory',  // 중요: 사전 렌더링 페이지의 불필요한 데이터 제거
  }),
};

Astro에서는 사전 렌더링 페이지까지 Functions에 포함되어 크기가 급격히 커질 수 있습니다. mode: 'directory'로 설정하면 해결할 수 있습니다.

방법 3: Bindings 설정 확인

Pages 설정에서 다음 메뉴로 이동합니다.

Settings > Functions > Bindings

코드에서 사용하는 KV, D1, R2가 모두 올바르게 설정되어 있는지 확인합니다.

방법 4: Node.js 전용 API 피하기

Cloudflare Workers는 완전한 Node.js가 아니라 V8 환경입니다. 다음 API는 사용할 수 없습니다.

  • fs(파일 시스템)
  • path(일부 미지원)
  • child_process
  • net / http(fetch 사용 필요)

꼭 필요하다면 해당 로직을 빌드 시점으로 옮기는 방법을 고려하세요.

문제 8: 캐시 및 사용자 지정 도메인 문제

대표적인 증상:

  • 배포는 성공했지만 사이트에는 여전히 이전 콘텐츠가 표시됩니다.
  • 사용자 지정 도메인은 404가 발생하지만 .pages.dev 도메인은 정상입니다.
  • 홈페이지에 404 Not Found가 표시됩니다.

근본 원인:

  • Cloudflare의 Page Rules가 Pages의 캐시 메커니즘을 방해합니다.
  • 사용자 지정 도메인의 DNS 설정이 올바르지 않습니다.
  • index.html 파일이 없습니다.

해결 방법:

방법 1: Cache Everything Page Rule 제거

사용자 지정 도메인이 Proxied(주황색 구름) 상태라면 Zone 설정이 Pages에 영향을 줍니다. 다음 메뉴를 확인합니다.

Rules > Page Rules

“Cache Everything” 규칙이 있다면 삭제합니다. Pages에는 자체 캐시 메커니즘이 있으므로 Page Rule이 필요하지 않습니다.

방법 2: 사용자 지정 도메인을 DNS Only로 변경

앞의 방법이 효과가 없다면 DNS 레코드를 회색 구름(DNS Only)으로 바꿔 보세요.

DNS > Records > 해당 레코드 클릭 > DNS Only로 변경

이렇게 하면 Cloudflare 프록시를 거치지 않고 Pages에 직접 연결됩니다.

방법 3: index.html 존재 여부 확인

루트 경로(yourdomain.com/)에서 404가 표시되면 빌드 출력 디렉터리에 index.html이 있는지 확인하세요. 많은 프레임워크는 기본적으로 dist/index.html을 출력하므로 Pages의 “Build output directory”가 올바르게 설정되었는지 확인해야 합니다.

방법 4: 캐시 수동 삭제

캐시 때문에 새 콘텐츠가 반영되지 않는 경우 다음 메뉴를 사용합니다.

Caching > Configuration > Purge Everything

이 작업은 Zone 전체의 캐시를 삭제하므로 신중하게 사용하세요.

3부: 예방을 위한 권장 사항

빌드 설정 권장 사항

문제가 생긴 뒤 고치는 것보다 처음부터 올바르게 설정하는 편이 낫습니다. 다음은 제가 정리한 권장 사항입니다.

1. Node 버전 명시

기본 버전에 의존하지 말고 명시적으로 지정합니다.

# .node-version 파일
20.11.0
# Cloudflare Pages 환경 변수에도 함께 설정
NODE_VERSION=20.11.0

2. 브랜치마다 다른 빌드 명령 설정

CF_PAGES_BRANCH 환경 변수를 활용합니다.

// package.json
{
  "scripts": {
    "build": "node scripts/build.js",
    "build:production": "next build",
    "build:preview": "next build && next export"
  }
}
// scripts/build.js
const branch = process.env.CF_PAGES_BRANCH || 'main';
const command = branch === 'main' ? 'build:production' : 'build:preview';
// 해당 명령 실행

3. monorepo에서는 올바른 루트 디렉터리 지정

pnpm workspace나 Turborepo를 사용한다면 Pages 설정에서 Root directory를 지정해야 합니다.

Root directory: apps/web
Build command: pnpm run build

지속적인 모니터링 및 디버깅 요령

1. 로컬 디버깅 환경 구축

Docker로 Pages 환경을 재현합니다.

# Dockerfile
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y nodejs npm
RUN node -v  # 약 18.17.1이어야 함
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

2. Cloudflare Status 확인

때로는 Cloudflare 서비스 자체의 문제로 빌드가 실패합니다. 이상한 오류를 만났다면 먼저 다음 페이지를 확인하세요.

https://www.cloudflarestatus.com/

Pages 서비스에 문제가 있다면 복구될 때까지 기다리면 됩니다. 불필요하게 설정을 건드리지 마세요.

3. Cloudflare Support에 문의해야 할 때

다음 상황이라면 지원팀에 문의해야 합니다.

  • 모든 방법을 시도해도 해결되지 않습니다.
  • Cloudflare 플랫폼의 bug가 의심됩니다.
  • 빌드 제한 상향이 필요합니다(유료 사용자는 신청 가능).

Deployment ID와 자세한 오류 로그를 반드시 함께 보내세요.

결론

지금까지 많은 내용을 다뤘지만 CF Pages 빌드 실패는 결국 몇 가지 범주로 정리됩니다. 90%의 경우 환경 차이(Node 버전, 파일 시스템 대소문자), 의존성 설정(package-lock.json, peer dependency), 또는 Pages의 동작 방식에 대한 오해(환경 변수, 캐시 메커니즘)가 원인입니다.

체계적인 문제 해결 흐름을 만드는 것이 중요합니다.

  1. 먼저 빌드 로그에서 실제 오류 메시지를 찾습니다.
  2. 의존성, 버전, 경로, 설정 중 어떤 범주의 문제인지 판단합니다.
  3. 로컬 환경에서 문제를 재현합니다.
  4. 해당 해결 방법을 적용합니다.
  5. 예방 설정을 적용해 같은 문제의 재발을 막습니다.

이 글을 저장해 문제 해결 안내서로 활용하세요. 다음에 빌드 실패를 만나면 이 흐름에 따라 대부분 10분 안에 해결할 수 있을 것입니다. 초록색 ”✓ Deployed” 표시를 볼 때의 안도감은 정말 말로 표현하기 어렵습니다.

다른 Cloudflare Pages 문제를 겪었다면 댓글로 공유해 주세요. 더 많은 사람에게 도움이 될 수 있습니다.

Cloudflare Pages 빌드 실패를 해결하는 전체 문제 해결 절차

빌드 환경 이해부터 가장 흔한 8가지 문제 해결까지 다루는 체계적인 방법으로, 문제의 90%를 10분 안에 해결합니다.

Estimated time: PT10M

  1. 1

    Step 1: Cloudflare Pages 빌드 환경의 특수성 이해

    기본 설정:
  2. 2

    Step 2: 빠른 문제 파악: 빌드 로그 읽기와 Deployment ID 저장

    빌드 로그 읽기:
  3. 3

    Step 3: 로컬 문제 재현과 의존성 설치 실패 해결

    로컬에서 문제 재현:
  4. 4

    Step 4: Node 버전 비호환 및 빌드 시간 초과 해결

    Node 버전 비호환 해결:
  5. 5

    Step 5: 모듈 해석 오류 및 환경 변수 설정 오류 해결

    모듈 해석 오류 해결:
  6. 6

    Step 6: Git 연동 문제와 Functions 배포 실패 해결

    Git 연동 문제 해결:

FAQ

Cloudflare Pages 빌드 환경의 기본 설정은 무엇이며 로컬 환경과 어떻게 다른가요?
기본 설정:
• 운영체제: Ubuntu 22(Build System V2에서 사용)
• Node 버전: 18.17.1(비교적 오래되어 새 패키지와 호환되지 않을 수 있음)
• 패키지 관리자: 기본적으로 npm install이 아니라 npm clean-install 사용
• 빌드 시간 제한: 최대 20분
• Worker 크기: 최대 10MB

로컬 환경과의 세 가지 핵심 차이:
1) 파일 시스템의 대소문자 구분:
• Linux는 대소문자를 엄격히 구분하지만 Windows/Mac은 기본적으로 구분하지 않음
• 파일명이 Header.js인데 import Header from './header'로 작성하면 Linux에서 오류 발생
• 가장 놓치기 쉬운 문제

2) 네트워크 환경 차이:
• 로컬에는 Taobao 같은 npm 미러가 설정되어 있을 수 있음
• Pages 빌드 환경은 npm 공식 레지스트리에 직접 연결하므로 시간 초과가 발생할 수 있음

3) 기본 빌드 명령의 차이:
• Cloudflare는 build command 전에 npm clean-install --progress=false를 자동 실행
• 이 명령은 npm install보다 엄격해서 package-lock.json과 package.json이 일치하지 않으면 오류 발생

Node 버전이 왜 이렇게 오래되었는지 궁금할 수 있습니다. Cloudflare가 안정성을 중시하기 때문입니다. 하지만 많은 새 패키지가 Node >= 18.18.0 또는 >= 20.0.0을 요구하므로 버전 충돌이 생길 수 있습니다.
Cloudflare Pages 빌드 실패 원인을 빠르게 찾으려면 어떻게 해야 하나요?
첫 번째 단계: 빌드 로그 읽기
빌드 로그가 수백 줄에 달해도 몇 군데만 확인하면 됩니다.
• 마지막 ERR! 또는 ERROR 찾기(npm ERR! code ERESOLVE, npm ERR! ERESOLVE could not resolve)
• Vite/Webpack 오류 확인([vite]: Rollup failed to resolve import)
• Git 관련 오류 확인(fatal: unable to access repository)

제 경험상 'ERR!'을 느낌표까지 포함해 검색한 다음 위쪽 3~5줄을 보면 대개 문제의 근본 원인이 나옵니다. 앞부분의 긴 설치 출력에 현혹되지 마세요.

두 번째 단계: Deployment ID 저장
빌드가 실패할 때마다 Cloudflare는 고유한 Deployment ID를 생성하며 브라우저 주소 표시줄에서 확인할 수 있습니다.
https://dash.cloudflare.com/xxx/pages/view/your-project/a398d794-7322-4c97-96d9-40b5140a8d9b

Cloudflare 지원팀에 문의하거나 커뮤니티에 도움을 요청할 때 이 ID가 있으면 다른 사람이 해당 빌드 기록을 바로 찾을 수 있으므로 반드시 저장해 두세요.

세 번째 단계: 로컬에서 문제 재현
로컬 Linux 환경에서 다음과 같이 재현해 보세요.
• Docker로 Ubuntu 22 환경 재현: docker run -it ubuntu:22.04 bash
• Pages와 동일하게 npm ci 사용
• nvm으로 Node 버전 지정: nvm use 18.17.1

로컬에서 npm ci를 실행해도 오류가 난다면 의존성 설정 문제입니다. Node 18.17.1로 바꿨을 때 실패한다면 버전 호환성 문제입니다.
의존성 설치 실패(npm install 오류)는 어떻게 해결하나요?
대표적인 오류 메시지:
• npm ERR! code ERESOLVE
• npm ERR! ERESOLVE could not resolve
• npm ERR! Fix the upstream dependency conflict, or retry this command with --force or --legacy-peer-deps
• 또는 npm ERR! code ERR_SOCKET_TIMEOUT, npm ERR! network Socket timeout

제가 가장 자주 겪은 문제입니다. 로컬의 npm install은 정상인데 Pages에서는 ERESOLVE가 발생합니다. Cloudflare가 기본적으로 매우 엄격한 npm ci를 사용하기 때문입니다.

근본 원인:
• Cloudflare의 npm clean-install은 peer dependency 충돌을 자동으로 해결하지 않음
• package-lock.json과 package.json이 동기화되지 않음
• 네트워크 시간 초과로 npm 공식 레지스트리에 연결하지 못함

해결 방법(권장 순서):

방법 1: 기본 설치를 건너뛰고 명령 직접 지정
• Pages 설정에 환경 변수 SKIP_DEPENDENCY_INSTALL=true 추가
• Build command를 npm install --legacy-peer-deps && npm run build로 변경
• Cloudflare의 기본 명령 대신 직접 의존성 설치를 제어하는 가장 간단한 방법

방법 2: package-lock.json 복구
• 로컬에서 lock 파일 다시 생성:
rm package-lock.json
npm install
git add package-lock.json
git commit -m 'fix: regenerate package-lock.json'
git push
• lock 파일이 꼬였다면 다시 생성하는 것만으로 해결될 수 있음

방법 3: GitHub Actions가 빌드 담당
• 앞의 두 방법으로 해결되지 않으면 GitHub Actions + cloudflare/pages-action으로 빌드
• 빌드 환경을 완전히 직접 제어할 수 있음

예방 조치: 로컬에서 정기적으로 npm ci를 실행해 lock 파일의 동기화 상태를 확인하세요.
Node 버전이 호환되지 않을 때는 어떻게 해결하고, 빌드 시간 초과는 어떻게 처리하나요?
Node 버전 비호환:

대표적인 오류 메시지:
• ERR_PNPM_UNSUPPORTED_ENGINE Unsupported environment
• This package requires Node.js version ^18.18.0 or >=20.0.0
• 또는 The engine 'node' is incompatible with this module. Expected version '>=18.18.0'. Got '18.17.1'

이런 오류는 대부분 Node 버전이 너무 오래되었을 때 발생합니다. TypeScript ESLint와 Next.js 14+를 비롯한 많은 새 패키지는 Node >= 18.18.0을 요구하지만 Pages의 기본 버전은 18.17.1입니다.

해결 방법:

방법 1: 환경 변수 설정(권장)
• Cloudflare Pages의 Settings > Environment variables에서 추가
• 변수명: NODE_VERSION
• 값: 20.11.0
• 공식적으로 권장되는 간단한 방법

방법 2: .node-version 파일 추가
• 프로젝트 루트에 생성: echo '20.11.0' > .node-version

방법 3: .nvmrc 파일 사용
• 같은 방식으로 생성: echo '20.11.0' > .nvmrc

권장 사항:
환경 변수와 .node-version 파일을 함께 사용하면 로컬과 온라인 환경의 버전을 맞출 수 있습니다. 최신 버전보다는 20.11.0 같은 안정적인 LTS 버전을 선택하는 편이 안전합니다.

빌드 시간 초과:

대표적인 증상:
빌드 로그가 정확히 20분 동안 실행된 후 명확한 오류 없이 갑자기 종료되고 Build exceeded maximum time of 20 minutes라는 한 줄만 남습니다.

해결 방법:

방법 1: 빌드 캐시 삭제
• Pages 설정에서 Settings > Builds & deployments > Clear build cache 선택
• 캐시를 지운 뒤 다시 빌드하면 해결되는 경우가 있음

방법 2: 의존성 분석 및 최적화
• bundle analyzer로 대형 의존성 확인
• 예전에 프로젝트에서 moment.js 전체를 가져온 부분을 day.js로 바꿔 빌드 시간을 3분 줄인 적이 있음

방법 3: 일부 작업을 CI로 이동
• typecheck와 lint 같은 오래 걸리는 작업을 GitHub Actions로 이동
• Pages는 빌드만 담당

방법 4: pnpm 사용
• pnpm은 npm보다 의존성 설치가 훨씬 빠름
• Pages의 Build command를 pnpm install && pnpm run build로 변경
모듈 해석 오류(Module not found)는 어떻게 해결하고 환경 변수 설정 오류는 어떻게 처리하나요?
모듈 해석 오류:

대표적인 오류 메시지:
• Module not found: Error: Can't resolve './App' in '/opt/buildhome/repo/src'
• Did you mean 'App.js'?
• 또는 [vite]: Rollup failed to resolve import '/src/components/Snackbar' from '/opt/buildhome/repo/src/pages/Login.jsx'

로컬에서는 잘 실행되지만 Pages에서 모듈을 찾지 못하는 매우 알아차리기 어려운 오류입니다. 원인의 99%는 대소문자 문제입니다.

근본 원인:
Linux 파일 시스템은 대소문자를 엄격히 구분하지만 Windows와 macOS는 기본적으로 구분하지 않습니다. 파일명이 App.js인데 로컬에서 import App from './app'로 작성하면 Windows에서는 작동해도 Linux에서는 오류가 발생합니다.

해결 방법:

방법 1: 모든 import 경로 수정
• 모든 import 문을 검사해 대소문자가 파일명과 정확히 일치하도록 수정
• ESLint 자동 검사 권장: .eslintrc.js에 rules: { 'import/no-unresolved': 'error' } 추가

방법 2: 경로 별칭 사용
• 절대 경로나 별칭으로 많은 문제를 방지
• vite.config.js에 resolve.alias 설정
• import Header from '@components/Header'처럼 별칭으로 import

환경 변수 설정 오류:

Cloudflare Pages의 환경 변수는 두 종류입니다.
• 빌드 타임 변수: npm run build 중 사용할 수 있으며 코드에 컴파일됨
• 런타임 변수: Functions 엣지 함수에서만 사용 가능

정적 사이트(순수 HTML/JS)에서는 런타임 변수를 가져올 수 없고 빌드 타임 변수만 사용할 수 있습니다.

해결 방법:

방법 1: 올바른 환경 변수 유형 설정
• Cloudflare Pages 설정에서 변수를 추가할 때 'Production'과 'Preview' 환경 선택
• 빌드 시 필요한 변수라면 'Build' 옵션을 반드시 선택

방법 2: 프레임워크 명명 규칙 준수
• Vite 프로젝트는 VITE_로 시작
• Next.js 프로젝트의 공개 변수는 NEXT_PUBLIC_로 시작
• Nuxt 프로젝트는 nuxt.config.js의 runtimeConfig 사용

방법 3: 민감한 정보는 Secret 유형 사용
• API key와 데이터베이스 비밀번호는 반드시 Secret 유형 사용
Git 연동 문제와 Functions 배포 실패는 어떻게 해결하나요?
Git 연동 문제:

대표적인 증상:
• 저장소 접근 권한을 부여할 수 없고 'This repository is already in use by another Pages project' 오류 발생
• 코드를 Push해도 Pages 빌드가 자동으로 시작되지 않음

근본 원인은 대개 GitHub/GitLab 권한 부여 문제이거나 동일 저장소를 여러 계정에서 사용할 수 없다는 Cloudflare 제한을 위반한 경우입니다.

해결 방법:

방법 1: GitHub App 다시 승인
• GitHub 설정에서 Settings > Applications > Cloudflare Pages > Configure > Uninstall 선택
• 제거 후 Cloudflare Dashboard에서 저장소를 다시 연결하면 재승인이 시작됨

방법 2: 저장소 사용 상태 확인
• 저장소가 이미 사용 중이라는 오류가 나오면 여러 Cloudflare 계정에서 같은 저장소를 사용 중인지 확인
• 허용되지 않으므로 다른 계정의 Pages 프로젝트를 삭제

방법 3: GitHub 사용자 권한 확인
• 연동하려면 해당 저장소에 최소 Maintainer 권한이 필요
• Contributor 권한만으로는 연결할 수 없음

방법 4: 특수문자 피하기
• Commit message에 emoji나 특수문자를 사용하지 않기
• GitHub에서는 허용해도 Cloudflare가 올바르게 해석하지 못해 빌드 트리거가 실패할 수 있음

Functions 배포 실패:

대표적인 증상:
• 빌드는 성공했지만 마지막 배포 단계에서 실패
• 로그에 유용한 정보가 거의 없음
• 또는 Build failed: Functions bundle size exceeding limit 오류 발생

근본 원인:
• Worker 함수 번들의 크기가 10MB 제한 초과
• Functions의 Bindings(KV, D1, R2) 설정 오류
• 엣지 환경에서 지원되지 않는 Node.js 전용 API 사용

해결 방법:

방법 1: Functions bundle 크기 분석
• bundle analyzer로 어떤 항목이 큰지 확인
• 대개 tree-shaking이 되지 않아 라이브러리 전체가 번들에 포함된 경우

방법 2: Astro/SvelteKit adapter 설정 최적화
• Cloudflare adapter가 올바르게 설정되었는지 확인
• Astro에서는 사전 렌더링 페이지까지 Functions에 포함되어 크기가 커질 수 있음
• mode: 'directory'로 설정하면 해결 가능

방법 3: Bindings 설정 확인
• Pages의 Settings > Functions > Bindings로 이동
• 코드에서 사용하는 KV, D1, R2가 모두 올바르게 설정되었는지 확인

방법 4: Node.js 전용 API 피하기
• Cloudflare Workers는 완전한 Node.js가 아니라 V8 환경
• fs, 일부 path, child_process, net/http API는 사용할 수 없음
• 꼭 필요하다면 해당 로직을 빌드 시점으로 이동

4분 읽기 · 게시일: 2025년 12월 1일 · 수정일: 2026년 9월 4일

댓글

GitHub로 로그인하여 댓글을 남기세요

Easton BlogEaston Blog