들어가기 전에
Next.js에 익숙하지 않은 상태에서 개발 블로그를 만들다가, Cloudflare Pages에 배포하면서 겪은 경험을 공유하려고 합니다.
왜 Cloudflare Pages를 선택했나?
Next.js를 Vercel에만 배포해 본 경험이 있어서, Cloudflare Pages도 한번 사용해보고 싶었습니다.
Cloudflare Pages는 무료 플랜도 제공하고 있습니다.
https://www.cloudflare.com/ko-kr/plans/developer-platform/
Cloudflare Pages 무료 플랜
정적 페이지 배포만 지원하는 플랫폼으로 알았지만, Full Stack(SSR)도 지원한다는 내용을 보고 사용해봐도 괜찮겠다는 생각을 했습니다.
https://developers.cloudflare.com/pages/framework-guides/nextjs/ssr/
배포시 겪은 문제
Learn how to deploy full-stack (SSR) Next.js apps to Cloudflare Pages.
문서를 참고하거나 구글에서 검색하면 배포 방법을 찾을 수 있습니다. 하지만 아래와 같은 문제들을 겪었습니다.
1. Node.JS Compatibility Error
앱을 배포하고 웹사이트를 열었을 때 아래와 같은 에러가 발생했습니다.
Node.JS Compatibility Error
이 문제는 앱 > 설정 > 런타임 > 호환성 플래그에서 nodejs_compat
플래그를 추가하면 해결됩니다.
호환성 플래그
nodejs_compat
2. export const runtime = 'edge';
/posts 페이지에서 searchParams를 사용해 동적 렌더링이 되는 상황이 있었습니다.
빌드 시 아래와 같은 에러가 발생했습니다.
23:28:16.516 ▲ Route (app) Size First Load JS
23:28:16.516 ▲ ┌ ○ / 141 B 87.3 kB
23:28:16.516 ▲ ├ ○ /_not-found 141 B 87.3 kB
23:28:16.516 ▲ ├ ○ /about 187 B 99.2 kB
23:28:16.516 ▲ ├ ○ /manifest.webmanifest 0 B 0 B
23:28:16.516 ▲ ├ ƒ /posts 189 B 99.2 kB
23:28:16.517 ▲ ├ ● /posts/[slug] 2.8 kB 102 kB
23:28:16.517 ▲ ├ ├ /posts/test3
23:28:16.517 ▲ ├ ├ /posts/test2
23:28:16.517 ▲ ├ └ /posts/test1
23:28:16.517 ▲ ├ ○ /robots.txt 0 B 0 B
23:28:16.517 ▲ └ ○ /sitemap.xml 0 B 0 B
23:28:16.517 ▲ + First Load JS shared by all 87.2 kB
23:28:16.517 ▲ ├ chunks/117-2982c3b28443478d.js 31.6 kB
23:28:16.518 ▲ ├ chunks/fd9d1056-920825da50076ee8.js 53.7 kB
23:28:16.518 ▲ └ other shared chunks (total) 1.94 kB
23:28:16.518 ▲ ○ (Static) prerendered as static content
23:28:16.518 ▲ ● (SSG) prerendered as static HTML (uses getStaticProps)
23:28:16.518 ▲ ƒ (Dynamic) server-rendered on demand
23:28:16.831 ▲ Traced Next.js server files in: 248.293ms
23:28:17.506 ▲ Created all serverless functions in: 674.293ms
23:28:17.517 ▲ Collected static files (public/, static/, .next/static): 6.565ms
23:28:17.570 ▲ Build Completed in .vercel/output [32s]
23:28:17.703 ⚡️ Completed `npx vercel build`.
23:28:17.754 ⚡️ Invalid prerender config for /posts/[slug]
23:28:17.755 ⚡️ Invalid prerender config for /posts/[slug].rsc
23:28:18.813
23:28:18.813 ⚡️ ERROR: Failed to produce a Cloudflare Pages build from the project.
23:28:18.813 ⚡️
23:28:18.814 ⚡️ The following routes were not configured to run with the Edge Runtime:
23:28:18.814 ⚡️ - /posts
23:28:18.814 ⚡️
23:28:18.814 ⚡️ Please make sure that all your non-static routes export the following edge runtime route segment config:
23:28:18.814 ⚡️ export const runtime = 'edge';
23:28:18.814 ⚡️
23:28:18.814 ⚡️ You can read more about the Edge Runtime on the Next.js documentation:
23:28:18.814 ⚡️ https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes
23:28:18.814
이에 따라 /posts 페이지에 export const runtime = 'edge';
를 추가한 뒤 다시 빌드했습니다.
하지만 이번에는 다른 에러가 발생했습니다.
23:30:24.523 ▲ ./node_modules/detect-libc/lib/detect-libc.js:6:1
23:30:24.523 ▲ Module not found: Can't resolve 'child_process'
23:30:24.523 ▲
23:30:24.523 ▲ https://nextjs.org/docs/messages/module-not-found
23:30:24.524 ▲
23:30:24.524 ▲ Import trace for requested module:
23:30:24.524 ▲ ./node_modules/sharp/lib/utility.js
23:30:24.524 ▲ ./node_modules/sharp/lib/index.js
23:30:24.524 ▲ ./node_modules/plaiceholder/dist/plaiceholder.esm.js
23:30:24.524 ▲ ./src/lib/getBase64.ts
23:30:24.527 ▲ ./src/lib/post.ts:1:1
23:30:24.527 ▲ Module not found: Can't resolve 'fs'
23:30:24.527 ▲ > 1 | import fs from "fs";
23:30:24.528 ▲ | ^
23:30:24.529 ▲ ./src/lib/post.ts:3:1
23:30:24.529 ▲ Module not found: Can't resolve 'path'
23:30:24.529 ▲ 1 | import fs from "fs";
23:30:24.529 ▲ 2 | import matter from "gray-matter";
23:30:24.530 ▲ > 3 | import path from "path";
23:30:24.530 ▲ | ^
post.ts, getBase64.ts 파일에서 fs와 path를 사용하는 것이 문제였습니다.
이 모듈들은 Node.js Runtime에서만 사용할 수 있으며, Cloudflare Pages는 Edge Runtime을 사용하기 때문에 지원되지 않습니다.
- https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes
- https://nextjs.org/docs/pages/api-reference/edge
Edge Runtime은 제한된 API만 제공하며, fs와 path는 사용할 수 없습니다.
따라서 Edge Runtime에 적합한 API를 사용해야 합니다.
문제는 제가 작성한 파일 외에도, 이미지 플레이스홀더(blur 효과)를 생성하는 데 사용하는 sharp 라이브러리가 detect-libc를 통해 Node.js의 child_process를 사용하고 있었습니다.
이 부분은 제가 수정할 수 없기 때문에 결국 Vercel에 배포했습니다.
마무리
Cloudflare Pages를 제대로 알아보지 않고 배포하려다 삽질을 했지만, 그 과정에서 많은 것을 배울 수 있었습니다.
정적 페이지 배포에는 문제가 없으니 Cloudflare Pages를 사용하는 것도 괜찮습니다.
또는 Edge Runtime에서 제공하는 API만 사용하는 단순한 기능의 웹페이지에 적합할 것 같습니다.