Cloudflare Pages 시작하기 3편 - 운영과 최적화

Cloudflare Pages 시작하기 3편 - 운영과 최적화#

요약#

이 글의 핵심 3가지

  1. 빌드 실패 해결: 의존성, 환경변수, 타임아웃 문제를 빠르게 디버깅하는 방법
  2. 롤백 전략: 문제가 생겼을 때 즉시 이전 버전으로 되돌리는 방법
  3. 성능 최적화: 이미지, 번들 크기, 보안 헤더로 프로덕션 품질 달성

읽는 시간: 20분 | 난이도: 중급

출처#

이 글은 Cloudflare 공식 문서를 바탕으로 작성했습니다.

빌드 실패 디버깅#

배포하다 보면 빌드가 실패하는 경우가 생깁니다. 대부분 몇 가지 공통적인 원인입니다.

1. 의존성 설치 실패#

증상:

Terminal window
[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 패키지 접근 권한 부족

해결방법:

Terminal window
# 1. Lock 파일 커밋 확인
git add package-lock.json
git commit -m "Add lock file"
git push origin main
# 2. 로컬에서 재생성
rm -rf node_modules package-lock.json
npm install
git add package-lock.json
git commit -m "Regenerate lock file"
git push origin main

npm registry 설정이 필요하다면:

Terminal window
# .npmrc 파일 생성
registry=https://registry.npmjs.org/

Private 패키지를 사용한다면 환경변수로 토큰을 추가해야 합니다:

# Environment variables
NPM_TOKEN=your-npm-token

그리고 .npmrc에서 토큰을 참조:

Terminal window
//registry.npmjs.org/:_authToken=${NPM_TOKEN}

2. 환경변수 누락#

증상:

Terminal window
[Error] ReferenceError: process is not defined
[Error] VITE_API_URL is not defined

원인:

  • 필요한 환경변수를 설정하지 않음
  • 환경변수 이름 오타
  • Production/Preview 구분 실수

해결방법:

  1. Dashboard → Settings → Environment variables
  2. 필요한 변수 추가
  3. Production과 Preview 환경 모두 설정
# 올바른 환경변수 설정 예시
VITE_API_URL (Production): https://api.example.com
VITE_API_URL (Preview): https://staging-api.example.com
NODE_VERSION: 18

환경변수를 추가한 후에는 반드시 재배포가 필요합니다:

Terminal window
git commit --allow-empty -m "Trigger rebuild"
git push origin main

3. 빌드 타임아웃#

증상:

Terminal window
[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.js
export default {
build: {
sourcemap: false // 빌드 시간 20-30% 단축
}
}
// 3. 청크 분할 최적화
export default {
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom']
}
}
}
}
}

대용량 에셋이 있다면 R2 (Object Storage)로 분리:

Terminal window
# 이미지, 폰트 같은 정적 파일을 R2에 업로드
# 빌드 시 복사하지 않음

그래도 타임아웃이 발생한다면 유료 플랜 업그레이드를 고려해야 합니다.

4. 잘못된 출력 디렉터리#

증상:

Terminal window
[Error] No static files found in output directory
[Error] Deployment failed

원인:

  • Build output directory 설정이 틀림
  • 프레임워크가 다른 폴더에 빌드 결과 생성

해결방법:

프레임워크별 올바른 출력 디렉터리 확인:

Vite: dist
Next.js (Static Export): out
Astro: dist
SvelteKit: build
Nuxt (Static): dist
Hugo: public

로컬에서 확인:

Terminal window
npm run build
ls -la dist/ # 또는 out/, build/ 등
# index.html이 있는지 확인
# 없으면 설정이 잘못된 것

5. Node.js 버전 불일치#

증상:

Terminal window
[Error] error:0308010C:digital envelope routines::unsupported
[Error] This is likely not a problem with npm

원인:

  • 로컬과 Cloudflare의 Node.js 버전 차이
  • 특정 버전에서만 동작하는 패키지

해결방법:

환경변수로 Node.js 버전 명시:

# Environment variables
NODE_VERSION=18 # 또는 16, 20

로컬 버전 확인:

Terminal window
node --version
# v18.17.0
# package.json에 명시
{
"engines": {
"node": ">=18.0.0"
}
}

배포 롤백하기#

문제가 생겼을 때 빠르게 이전 버전으로 되돌리는 방법입니다.

방법 1: Dashboard에서 즉시 롤백#

가장 빠른 방법입니다.

  1. Dashboard → Deployments
  2. 정상 동작하던 배포 선택
  3. Rollback to this deployment 버튼 클릭
Rollback confirmation:
Previous deployment (abc1234) will be restored
Current deployment (def5678) will remain in history
Traffic will be served from abc1234
Estimated time: 30 seconds

Confirm 클릭하면 30초 이내에 이전 버전으로 전환됩니다.

롤백의 특징:

  • Git 히스토리는 변경되지 않음 (코드는 그대로)
  • 단순히 서빙되는 버전만 변경
  • 언제든지 다시 최신 버전으로 전환 가능
  • 새로운 배포로 기록됨 (Deployment 목록에 표시)

방법 2: Git 기반 롤백 (더 안전)#

Git에서 직접 되돌리는 방법입니다.

Terminal window
# 1. 문제가 생긴 커밋 확인
git log --oneline
# def5678 Add new feature (문제 발생)
# abc1234 Fix bug
# 123abcd Initial commit
# 2. Revert로 되돌리기 (권장)
git revert def5678
git push origin main
# 또는 Reset (히스토리 변경, 주의)
git reset --hard abc1234
git push origin main --force # 팀 작업 시 위험!

Revert vs Reset:

Revert:
- 새로운 커밋 생성 (되돌리는 커밋)
- 히스토리 보존
- 팀 작업에 안전
- 권장 방법
Reset:
- 커밋 삭제
- 히스토리 변경
- 팀 작업 시 위험
- 혼자 작업할 때만

Revert를 사용하면 Git 히스토리도 깔끔하게 유지되고, 나중에 다시 적용하기도 쉽습니다.

롤백 후 확인사항#

롤백 후 반드시 확인해야 할 것들:

Terminal window
1. 사이트 접속 확인
- https://example.com 정상 동작
- 주요 페이지 테스트
2. API 연동 확인
- 데이터 로딩 정상
- 에러 로그 확인
3. Preview 배포 확인
- 다른 PR들 정상 동작
- Preview URL 접속 테스트
4. 모니터링 확인
- 에러율 감소
- 응답 시간 정상화

성능 최적화#

프로덕션 품질을 위한 성능 최적화 방법입니다.

1. 이미지 최적화#

이미지는 보통 페이지 용량의 50-70%를 차지합니다. 최적화가 필수입니다.

WebP 포맷 사용:

Terminal window
# 기존 이미지를 WebP로 변환
npm install -g sharp-cli
# 변환 실행
sharp -i image.jpg -o image.webp --webp

HTML에서 사용:

<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 번들 크기 최적화#

큰 번들은 로딩 시간을 늘립니다.

번들 분석:

Terminal window
# Vite
npm install -D rollup-plugin-visualizer
# vite.config.js
import { visualizer } from 'rollup-plugin-visualizer';
export default {
plugins: [
visualizer({
open: true
})
]
}
# 빌드 후 stats.html 생성
npm run build

불필요한 의존성 제거:

package.json
{
"dependencies": {
// ❌ "lodash": "^4.17.21" // 100KB, 일부만 사용
"lodash-es": "^4.17.21" // 필요한 것만 import
}
}

Tree Shaking 활용:

// ❌ 전체 import
import _ from "lodash";
_.debounce(fn, 300);
// ✓ 필요한 것만 import
import { 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)
각 청크: < 100KB

3. CSS 최적화#

불필요한 CSS 제거 (PurgeCSS):

Terminal window
npm install -D @fullhuman/postcss-purgecss
# postcss.config.js
module.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 파일 생성:

Terminal window
# 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 파일로 보안 헤더 추가#

public/_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):

Terminal window
/*
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)#

Terminal window
/*
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

HSTS는 브라우저가 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는 사용자 이벤트 추적이나 세션 기반 분석은 제공하지 않습니다.

활성화:

  1. Dashboard → Analytics 탭
  2. Enable Web Analytics
  3. 사이트에 스크립트 추가
<!-- 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에서 알림을 설정할 수 있습니다.

  1. Dashboard → Notifications
  2. Add 클릭
  3. Email 또는 Webhook 선택
알림 유형:
- Deployment Failed
- Deployment Success
- Custom Domain SSL Issued

Slack 연동 (Webhook):

Terminal window
Webhook URL: https://hooks.slack.com/services/YOUR/WEBHOOK/URL
# Cloudflare에서 자동으로 Slack에 메시지 전송

Uptime 모니터링#

외부 도구로 사이트 상태를 모니터링하세요.

추천 도구:

설정 예시:
Monitor URL: https://example.com
Check interval: 5분
Alert when: Down for 2 checks
Notification: Email, SMS

에러 트래킹#

JavaScript 에러를 추적하려면 별도 도구가 필요해요.

Sentry 연동:

Terminal window
npm install @sentry/browser
# main.js
import * 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로 안정적인 웹 서비스를 운영할 수 있습니다.

참고 자료#

공식 문서#

성능 & 보안#

모니터링 도구#

공유

이 글이 도움이 되었다면 다른 사람과 공유해주세요!

Cloudflare Pages 시작하기 3편 - 운영과 최적화
https://moodturnpost.net/posts/cloudflare/cloudflare-starter-3/
작성자
Moodturn
게시일
2026-01-01
Moodturn

목차