Cloudflare Pages 시작하기 3편 - 운영과 최적화
Cloudflare Pages 시작하기 3편 - 운영과 최적화
요약
이 글의 핵심 3가지
- 빌드 실패 해결: 의존성, 환경변수, 타임아웃 문제를 빠르게 디버깅하는 방법
- 롤백 전략: 문제가 생겼을 때 즉시 이전 버전으로 되돌리는 방법
- 성능 최적화: 이미지, 번들 크기, 보안 헤더로 프로덕션 품질 달성
읽는 시간: 20분 | 난이도: 중급
출처
이 글은 Cloudflare 공식 문서를 바탕으로 작성했습니다.
- Build configuration - 빌드 설정 및 디버깅
- Known issues - 알려진 이슈 및 해결법
- Limits - 플랫폼 제한사항
- Headers - 보안 헤더 설정
빌드 실패 디버깅
배포하다 보면 빌드가 실패하는 경우가 생깁니다. 대부분 몇 가지 공통적인 원인입니다.
1. 의존성 설치 실패
증상:
[Error] npm ERR! code ENOTFOUND[Error] npm ERR! network request to https://registry.npmjs.org failed[Error] npm ERR! network This is a problem related to network connectivity원인:
- Lock 파일 누락 (package-lock.json, pnpm-lock.yaml)
- 잘못된 레지스트리 URL
- private 패키지 접근 권한 부족
해결방법:
# 1. Lock 파일 커밋 확인git add package-lock.jsongit commit -m "Add lock file"git push origin main
# 2. 로컬에서 재생성rm -rf node_modules package-lock.jsonnpm installgit add package-lock.jsongit commit -m "Regenerate lock file"git push origin mainnpm registry 설정이 필요하다면:
# .npmrc 파일 생성registry=https://registry.npmjs.org/Private 패키지를 사용한다면 환경변수로 토큰을 추가해야 합니다:
# Environment variablesNPM_TOKEN=your-npm-token그리고 .npmrc에서 토큰을 참조:
//registry.npmjs.org/:_authToken=${NPM_TOKEN}2. 환경변수 누락
증상:
[Error] ReferenceError: process is not defined[Error] VITE_API_URL is not defined원인:
- 필요한 환경변수를 설정하지 않음
- 환경변수 이름 오타
- Production/Preview 구분 실수
해결방법:
- Dashboard → Settings → Environment variables
- 필요한 변수 추가
- Production과 Preview 환경 모두 설정
# 올바른 환경변수 설정 예시VITE_API_URL (Production): https://api.example.comVITE_API_URL (Preview): https://staging-api.example.comNODE_VERSION: 18환경변수를 추가한 후에는 반드시 재배포가 필요합니다:
git commit --allow-empty -m "Trigger rebuild"git push origin main3. 빌드 타임아웃
증상:
[Error] Build exceeded maximum time limit of 20 minutes[Error] Command timed out원인:
- 너무 큰 프로젝트 (대규모 모노레포)
- 비효율적인 빌드 스크립트
- 무한 루프나 hang 상태
제한사항:
빌드 시간 제한: - 일반적으로 약 20분 내외 - 계정/플랜/환경에 따라 달라질 수 있음해결방법: 빌드 시간을 줄이기
// 1. 불필요한 빌드 단계 제거// package.json{ "scripts": { "build": "vite build", // ❌ "build": "npm run lint && npm run test && vite build" }}
// 2. 소스맵 비활성화 (프로덕션)// vite.config.jsexport default { build: { sourcemap: false // 빌드 시간 20-30% 단축 }}
// 3. 청크 분할 최적화export default { build: { rollupOptions: { output: { manualChunks: { vendor: ['react', 'react-dom'] } } } }}대용량 에셋이 있다면 R2 (Object Storage)로 분리:
# 이미지, 폰트 같은 정적 파일을 R2에 업로드# 빌드 시 복사하지 않음그래도 타임아웃이 발생한다면 유료 플랜 업그레이드를 고려해야 합니다.
4. 잘못된 출력 디렉터리
증상:
[Error] No static files found in output directory[Error] Deployment failed원인:
- Build output directory 설정이 틀림
- 프레임워크가 다른 폴더에 빌드 결과 생성
해결방법:
프레임워크별 올바른 출력 디렉터리 확인:
Vite: distNext.js (Static Export): outAstro: distSvelteKit: buildNuxt (Static): distHugo: public로컬에서 확인:
npm run buildls -la dist/ # 또는 out/, build/ 등
# index.html이 있는지 확인# 없으면 설정이 잘못된 것5. Node.js 버전 불일치
증상:
[Error] error:0308010C:digital envelope routines::unsupported[Error] This is likely not a problem with npm원인:
- 로컬과 Cloudflare의 Node.js 버전 차이
- 특정 버전에서만 동작하는 패키지
해결방법:
환경변수로 Node.js 버전 명시:
# Environment variablesNODE_VERSION=18 # 또는 16, 20로컬 버전 확인:
node --version# v18.17.0
# package.json에 명시{ "engines": { "node": ">=18.0.0" }}배포 롤백하기
문제가 생겼을 때 빠르게 이전 버전으로 되돌리는 방법입니다.
방법 1: Dashboard에서 즉시 롤백
가장 빠른 방법입니다.
- Dashboard → Deployments 탭
- 정상 동작하던 배포 선택
- Rollback to this deployment 버튼 클릭
Rollback confirmation:Previous deployment (abc1234) will be restoredCurrent deployment (def5678) will remain in historyTraffic will be served from abc1234
Estimated time: 30 secondsConfirm 클릭하면 30초 이내에 이전 버전으로 전환됩니다.
롤백의 특징:
- Git 히스토리는 변경되지 않음 (코드는 그대로)
- 단순히 서빙되는 버전만 변경
- 언제든지 다시 최신 버전으로 전환 가능
- 새로운 배포로 기록됨 (Deployment 목록에 표시)
방법 2: Git 기반 롤백 (더 안전)
Git에서 직접 되돌리는 방법입니다.
# 1. 문제가 생긴 커밋 확인git log --oneline# def5678 Add new feature (문제 발생)# abc1234 Fix bug# 123abcd Initial commit
# 2. Revert로 되돌리기 (권장)git revert def5678git push origin main
# 또는 Reset (히스토리 변경, 주의)git reset --hard abc1234git push origin main --force # 팀 작업 시 위험!Revert vs Reset:
Revert: - 새로운 커밋 생성 (되돌리는 커밋) - 히스토리 보존 - 팀 작업에 안전 - 권장 방법
Reset: - 커밋 삭제 - 히스토리 변경 - 팀 작업 시 위험 - 혼자 작업할 때만Revert를 사용하면 Git 히스토리도 깔끔하게 유지되고, 나중에 다시 적용하기도 쉽습니다.
롤백 후 확인사항
롤백 후 반드시 확인해야 할 것들:
1. 사이트 접속 확인 - https://example.com 정상 동작 - 주요 페이지 테스트
2. API 연동 확인 - 데이터 로딩 정상 - 에러 로그 확인
3. Preview 배포 확인 - 다른 PR들 정상 동작 - Preview URL 접속 테스트
4. 모니터링 확인 - 에러율 감소 - 응답 시간 정상화성능 최적화
프로덕션 품질을 위한 성능 최적화 방법입니다.
1. 이미지 최적화
이미지는 보통 페이지 용량의 50-70%를 차지합니다. 최적화가 필수입니다.
WebP 포맷 사용:
# 기존 이미지를 WebP로 변환npm install -g sharp-cli
# 변환 실행sharp -i image.jpg -o image.webp --webpHTML에서 사용:
<picture> <source srcset="image.webp" type="image/webp" /> <img src="image.jpg" alt="Fallback for old browsers" /></picture>Lazy Loading:
<img src="hero.jpg" alt="Hero" loading="eager" /><img src="below-fold.jpg" alt="Below" loading="lazy" />최적화 전후 비교:
최적화 전: image.jpg: 2.5MB image.png: 3.2MB Total: 5.7MB
최적화 후: image.webp: 180KB image-small.webp: 45KB Total: 225KB
개선: 96% 용량 감소2. JavaScript 번들 크기 최적화
큰 번들은 로딩 시간을 늘립니다.
번들 분석:
# Vitenpm install -D rollup-plugin-visualizer
# vite.config.jsimport { visualizer } from 'rollup-plugin-visualizer';
export default { plugins: [ visualizer({ open: true }) ]}
# 빌드 후 stats.html 생성npm run build불필요한 의존성 제거:
{ "dependencies": { // ❌ "lodash": "^4.17.21" // 100KB, 일부만 사용 "lodash-es": "^4.17.21" // 필요한 것만 import }}Tree Shaking 활용:
// ❌ 전체 importimport _ from "lodash";_.debounce(fn, 300);
// ✓ 필요한 것만 importimport { debounce } from "lodash-es";debounce(fn, 300);Code Splitting:
// React 예시import { lazy, Suspense } from "react";
// ❌ 초기 번들에 포함import HeavyComponent from "./HeavyComponent";
// ✓ 필요할 때만 로드const HeavyComponent = lazy(() => import("./HeavyComponent"));
function App() { return ( <Suspense fallback={<div>Loading...</div>}> <HeavyComponent /> </Suspense> );}최적화 목표:
번들 크기 가이드: 초기 로드: < 200KB (gzip) 전체 JS: < 500KB (gzip) 각 청크: < 100KB3. CSS 최적화
불필요한 CSS 제거 (PurgeCSS):
npm install -D @fullhuman/postcss-purgecss
# postcss.config.jsmodule.exports = { plugins: [ require('@fullhuman/postcss-purgecss')({ content: ['./src/**/*.{js,jsx,ts,tsx,html}'], defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || [] }) ]}Critical CSS:
<!-- 중요한 CSS는 인라인 --><style> /* Above-the-fold 스타일 */ .hero { /* ... */ }</style>
<!-- 나머지는 비동기 로드 --><link rel="stylesheet" href="styles.css" media="print" onload="this.media='all'"/>4. 캐싱 전략
Cloudflare Pages는 자동으로 정적 파일을 캐싱하지만, 직접 제어할 수도 있습니다.
_headers 파일 생성:
# public/_headers 또는 dist/_headers/* Cache-Control: public, max-age=3600
/*.js Cache-Control: public, max-age=31536000, immutable
/*.css Cache-Control: public, max-age=31536000, immutable
/*.woff2 Cache-Control: public, max-age=31536000, immutable
/index.html Cache-Control: no-cache설명:
- HTML: 항상 최신 버전 확인 (max-age=0)
- JS/CSS: 1년 캐싱, 파일명에 해시 포함 (immutable)
- 폰트: 1년 캐싱
보안 헤더 설정
보안 헤더는 XSS, clickjacking 같은 공격을 방어합니다.
_headers 파일로 보안 헤더 추가
/* X-Frame-Options: DENY X-Content-Type-Options: nosniff Referrer-Policy: strict-origin-when-cross-origin Permissions-Policy: geolocation=(), microphone=(), camera=()*/Content-Security-Policy (CSP):
/* Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://api.example.com*/위 CSP는 예시용입니다.
참고: 실제 운영에서는 unsafe-inline 대신 nonce 또는 hash 기반 정책을 사용하세요.
HSTS (HTTP Strict Transport Security)
/* Strict-Transport-Security: max-age=31536000; includeSubDomains; preloadHSTS는 브라우저가 HTTPS만 사용하도록 강제합니다.
참고: preload는 모든 서브도메인이 HTTPS를 지원할 때만 사용하세요.
보안 헤더 테스트
Security Headers에서 테스트할 수 있습니다.
목표 등급: A+
필수 헤더: ✓ X-Frame-Options ✓ X-Content-Type-Options ✓ Referrer-Policy ✓ Permissions-Policy ✓ Content-Security-Policy모니터링 및 알림
문제가 생기면 빠르게 알아야 합니다.
Cloudflare Web Analytics
무료 프라이버시 친화적 분석 도구입니다.
참고: Web Analytics는 사용자 이벤트 추적이나 세션 기반 분석은 제공하지 않습니다.
활성화:
- Dashboard → Analytics 탭
- Enable Web Analytics
- 사이트에 스크립트 추가
<!-- HTML head에 추가 --><script defer src="https://static.cloudflareinsights.com/beacon.min.js" data-cf-beacon='{"token": "your-token"}'></script>제공 정보:
- 페이지 뷰
- 고유 방문자
- 국가별 트래픽
- 브라우저/기기 통계
- Core Web Vitals (LCP, INP, CLS)
빌드 실패 알림
Dashboard에서 알림을 설정할 수 있습니다.
- Dashboard → Notifications
- Add 클릭
- Email 또는 Webhook 선택
알림 유형: - Deployment Failed - Deployment Success - Custom Domain SSL IssuedSlack 연동 (Webhook):
Webhook URL: https://hooks.slack.com/services/YOUR/WEBHOOK/URL
# Cloudflare에서 자동으로 Slack에 메시지 전송Uptime 모니터링
외부 도구로 사이트 상태를 모니터링하세요.
추천 도구:
- UptimeRobot - 무료, 5분 간격
- Pingdom - 유료, 1분 간격
- StatusCake - 무료/유료
설정 예시: Monitor URL: https://example.com Check interval: 5분 Alert when: Down for 2 checks Notification: Email, SMS에러 트래킹
JavaScript 에러를 추적하려면 별도 도구가 필요해요.
Sentry 연동:
npm install @sentry/browser
# main.jsimport * as Sentry from '@sentry/browser';
Sentry.init({ dsn: 'https://your-dsn@sentry.io/project-id', environment: import.meta.env.MODE, // production or development tracesSampleRate: 1.0});Sentry는 에러가 발생하면 즉시 알림을 보내고, 스택 트레이스와 사용자 정보를 제공합니다.
참고: 트래픽이 많은 서비스에서는
tracesSampleRate를 0.1 이하부터 시작하세요.
성능 측정
Lighthouse 점수 확인
Chrome DevTools에서 Lighthouse를 실행하세요.
목표 점수: Performance: 90+ Accessibility: 90+ Best Practices: 90+ SEO: 90+Core Web Vitals
Google이 중요하게 보는 3가지 지표입니다.
LCP (Largest Contentful Paint): < 2.5초 - 메인 콘텐츠 로딩 속도
INP (Interaction to Next Paint): < 200ms - 첫 번째 인터랙션 반응 속도
CLS (Cumulative Layout Shift): < 0.1 - 레이아웃 안정성Cloudflare Web Analytics에서 자동으로 측정됩니다.
문제 해결 체크리스트
배포나 운영 중 문제가 생겼을 때 확인할 사항입니다.
빌드 실패 시
1. 빌드 로그 확인: ✓ 에러 메시지 전체 복사 ✓ 어느 단계에서 실패했는지 확인
2. 로컬에서 재현: ✓ npm run build 실행 ✓ 동일한 에러 발생하는지 확인
3. 환경 확인: ✓ Node.js 버전 (NODE_VERSION) ✓ 환경변수 누락 여부 ✓ Lock 파일 커밋 여부
4. 의존성 확인: ✓ package.json 문법 오류 ✓ 호환되지 않는 패키지 버전 ✓ Private 패키지 접근 권한배포 성공했지만 사이트 안 보임
1. Build output directory 확인: ✓ 올바른 폴더 설정 (dist, out, build) ✓ index.html 파일 존재 여부
2. 브라우저 개발자 도구 확인: ✓ Console 에러 (404, CORS 등) ✓ Network 탭에서 실패한 요청
3. 환경변수 확인: ✓ API URL 올바른지 ✓ Production/Preview 구분성능 문제
1. Lighthouse 실행: ✓ Performance 점수 확인 ✓ 개선 제안 사항 확인
2. 번들 크기 분석: ✓ 큰 의존성 찾기 ✓ Code splitting 적용
3. 이미지 최적화: ✓ WebP 사용 ✓ Lazy loading 적용
4. 캐싱 확인: ✓ _headers 파일 설정 ✓ Cache-Control 헤더시리즈 마무리
3편까지 Cloudflare Pages의 모든 과정을 다뤘습니다.
1편 - 첫 배포:
- Git 저장소 연동
- 자동 배포 파이프라인
- Preview 배포 활용
2편 - 도메인 설정:
- 커스텀 도메인 연결
- www 리다이렉트
- 환경변수 관리
- 빌드 최적화
3편 - 운영과 최적화:
- 빌드 실패 디버깅
- 롤백 전략
- 성능 최적화
- 보안 헤더
- 모니터링
이제 여러분은 Cloudflare Pages로 안정적인 웹 서비스를 운영할 수 있습니다.
참고 자료
공식 문서
- Build configuration - 빌드 설정
- Known issues - 알려진 이슈
- Limits - 제한사항
- Headers - 보안 헤더
- Redirects - 리다이렉트
성능 & 보안
- Lighthouse - 성능 측정
- Web.dev - Core Web Vitals
- Security Headers - 보안 헤더 테스트
- WebPageTest - 상세 성능 분석
모니터링 도구
- Cloudflare Web Analytics - 무료 분석
- Sentry - 에러 트래킹
- UptimeRobot - Uptime 모니터링
공유
이 글이 도움이 되었다면 다른 사람과 공유해주세요!