Skip to content

Cloudflare 배포 (frontend)

frontend/ (Next.js 16, App Router) 를 Cloudflare Workers + Static Assets 로 배포한다. Vercel 에서 이전 (2026-05-09 결정, 2026-05-10 셋업).

어댑터: @opennextjs/cloudflare

  • Next.js 16+ 공식 CF 어댑터.
  • @cloudflare/next-on-pages 는 Next 15.5.2 이하만 지원 → 사용 X.
  • deploy 타겟은 Workers + Static Assets (Pages 가 아님). Pages 로 deploy 시 worker.js 가 실행 안 돼서 SSR/ISR 페이지 모두 404.

빌드 / 배포 명령

bash
cd frontend
npm run build:cf      # .open-next/ (worker.js + assets/) 산출
npm run preview:cf    # wrangler 로 로컬 프리뷰
npm run deploy:cf     # wrangler 로 CF Workers 에 업로드 (인증 필요)

설정 파일

  • open-next.config.ts — 어댑터 설정 (현재 기본값)
  • wrangler.toml — Workers 프로젝트 설정
    • name = "dartbrief" — dashboard Project name 과 일치 필수 (불일치 시 빌드 에러 또는 다른 Worker 에 배포)
    • main = ".open-next/worker.js" — SSR/ISR 진입점
    • compatibility_flags = ["nodejs_compat"] — OpenNext 어댑터 필수 (Node 빌트인 폴리필)
    • [assets] binding="ASSETS" directory=".open-next/assets" — 정적 자산 binding (Worker 가 매칭 안 되는 경로는 자산으로 서빙)

CF Dashboard 셋업

Workers & Pages → Create → Workers → Continue with GitHub:

필드
Project namedartbrief
Repositoryjunhyeon47/dartbrief
Production branchmain
Root directoryfrontend ⚠️ (monorepo, 누락 시 빌드 실패)
Build commandnpm run build:cf
Deploy commandnpx wrangler deploy

⚠️ CF Pages 로 셋업하면 안 됨worker.js 미실행으로 SSR/ISR 404.

환경 변수 (CF dashboard)

용도예시Type
API_BASE_URLserver-side fetch (ISR) 의 백엔드 basehttps://api.dartbrief.comPlaintext
WEB_SECRETbackend AppCheck 의 대안 ACL — X-Worker-Secret 헤더로 전송. 모바일은 AppCheck, 웹 Worker 는 이 secret 으로 인증(랜덤 32+자 문자열)Encrypted
NEXT_PUBLIC_SITE_PASSWORDSiteGate 비공개 게이트 (런치 전까지)(시크릿)Plaintext (빌드 시점 주입)
NEXT_PUBLIC_APPLE_APP_IDSmart App Banner App Store IDApp Store 등록 후Plaintext

NEXT_PUBLIC_* 만 클라이언트 노출. 그 외는 빌드/SSR 시점에만 사용.

등록 방법 — 변수 경로 3가지

CF Workers 는 빌드 시점 vs 런타임 경로가 분리되어 있다. 잘못 골라 등록하면 process.envundefined 로 번들링되거나 런타임에서 안 보인다.

경로가시성적합부적합
Settings → Build → Build Variables and Secrets (또는 로컬 .env.production / shell export)next build 시점에 process.env 로 노출 → client bundle 에 inlineNEXT_PUBLIC_* (예: NEXT_PUBLIC_SITE_PASSWORD, NEXT_PUBLIC_APPLE_APP_ID)런타임 시크릿 (빌드 산출물에 박혀버림)
wrangler.toml [vars] 또는 dashboard Variables (Plaintext)Worker 런타임에 env 바인딩server-side fetch base URL (예: API_BASE_URL)NEXT_PUBLIC_* (빌드 시점엔 안 보여 undefined 됨)
wrangler secret put (Encrypted)Worker 런타임에 env 바인딩, dashboard 에서 값 비공개진짜 런타임 시크릿 (예: WEB_SECRET — backend 호출 시 SSR 헤더용)빌드 시점에 필요한 값

NEXT_PUBLIC_SITE_PASSWORD 는 어차피 client bundle 에 inline 되어 노출되므로 secret 보호 의미가 없다. Build Variables 경로로 등록.

bash
cd frontend
# 진짜 런타임 시크릿 — Railway 의 WEB_SECRET 과 동일한 값 입력
npx wrangler secret put WEB_SECRET --name dartbrief

# 등록 확인 (값은 안 보임)
npx wrangler secret list --name dartbrief

⚠️ WEB_SECRET 누락 시 prod 홈/피드가 통째로 빈 목록으로 보임. 백엔드 App Check 가 SSR fetch 를 403 으로 차단하고, frontend getJSONSoft 가 throw 를 삼켜 "공시 목록을 불러올 수 없습니다." 만 렌더링. dashboard 에서 secret 표시 안 되더라도 wrangler secret list 로 검증할 것 (PR #67 머지 후 secret 등록 누락 사고 — 2026-05-10).

ISR / 라우트

경로모드revalidate
/, /privacy, /terms, /supportstatic
/disclosuresISR60s
/disclosures/[rceptNo]dynamic + revalidate300s
/companies/[corpCode]dynamic + revalidate300s
/api/stocks/searchroute handlerno-cache (백엔드가 10분 캐시)

서버 fetch 는 모두 백엔드 public 엔드포인트 (lazy auth, anonymous):

  • GET /api/v1/disclosures/recent
  • GET /api/v1/disclosures/{rceptNo}
  • GET /api/v1/disclosures?corpCode=X&limit=50 (limit 기본 50, 상한 200)
  • GET /api/v1/companies/{corpCode}
  • GET /api/v1/ai/analyze/{rceptNo} (anonymous → result omit, summary 만)

분석 본문(reveal) 은 quota 정책상 앱에서만. 웹은 한 줄 요약 + "앱에서 보기" CTA.

App Check

현재 strict/soft 모드 — 백엔드가 web origin 의 fetch 를 차단할 수 있음. 차단 시 별도 PR be-web-appcheck-exempt 로 origin whitelist 처리 (lazy).

비용

  • CF Workers 무료: 10만 요청/일 (≈300만/월)
  • 정적 자산: 무제한 무료 (CDN edge)
  • ISR 캐시 hit → Worker 호출 X, CDN 만 → 무료
  • Worker 호출은 캐시 미스 + API route + revalidate 만
  • MAU 5만 정도까진 무료 티어로 충분

흔한 함정

  1. pages_build_output_dir 셋업 → 404: OpenNext 의 worker.js 가 실행 안 됨. 반드시 Workers + [assets] binding 으로.
  2. nodejs_compat 누락 → 런타임 500: OpenNext 어댑터가 Node 빌트인 폴리필 사용. wrangler.toml 의 compatibility_flags 또는 dashboard 에 명시.
  3. esbuild / wrangler 미설치 → 빌드 실패: @opennextjs/cloudflare 의 peer dep. package.json devDependency 에 명시 (PR #65 fix).
  4. Root directory 누락 → 빌드 실패: monorepo 라 dashboard 에 frontend 명시 필수.
  5. WEB_SECRET 미등록 → 홈/피드 통째 빈 목록: secret 은 wrangler.toml 에 안 들어가므로 wrangler secret put 으로 별도 등록해야 함. 누락 시 SSR fetch 가 백엔드에서 403, getJSONSoft 가 swallow → "공시 목록을 불러올 수 없습니다." (런타임 에러로 안 떠서 발견 늦음). 배포 후 npx wrangler secret list --name dartbrief 로 검증 필수.