Pregoi infrastructure: R2 URL and API Gateway
Purpose: Align code and docs with Cloudflare infra: R2 custom domain (static.pregoi.com), API Gateway (Zuplo), multi-tenant and typography. Plan only; implementation references and verification checklist included.
See also: font-pc-mobile-parity-plan.md, font-r2-and-local-setup.md.
English {#english}
Overview
- Fonts: Use
NEXT_PUBLIC_FONTS_BASE_URLonly (e.g.https://static.pregoi.com). No hardcoded font base URL. Prebuild runsgenerate-fonts-css.js; R2 serves fonts.css and WOFF2 with cache-busting hashes. - API: Use
NEXT_PUBLIC_API_URL(e.g. Zuplo URL) for all API calls; injectcompanyIdin path and headers (e.g.X-Prego-Company). No hardcoded API host. - Verification: See §6 checklist (env vars, R2 MIME/CORS, preload, API gateway).
Infra and URLs
flowchart LR
subgraph Client
W[client-web]
end
subgraph Env
F[NEXT_PUBLIC_FONTS_BASE_URL\nstatic.pregoi.com]
A[NEXT_PUBLIC_API_URL\nZuplo]
end
W --> F
W --> A
F --> R2[R2\nfonts]
A --> GW[Gateway]
한국어 {#korean}
1. 폰트 인프라 및 에셋 URL 변경
1.1 베이스 URL 통일
폰트 다운로드를 위한 R2 URL은 환경 변수 NEXT_PUBLIC_FONTS_BASE_URL만 사용한다. 코드에는 폰트 베이스 URL을 하드코딩하지 않고, 해당 환경 변수를 읽어 사용한다.
| 항목 | 현재 | 변경 목표 |
|---|---|---|
| 환경 변수 | NEXT_PUBLIC_FONTS_BASE_URL (값: https://pub-xxx.r2.dev 또는 미설정) | NEXT_PUBLIC_FONTS_BASE_URL 사용. 값은 https://static.pregoi.com 으로 설정. |
| 폰트 다운로드 R2 URL | R2 공개 URL(r2.dev) 또는 동일 오리진 | R2 커스텀 도메인. 모든 폰트 요청(fonts.css, WOFF2)은 NEXT_PUBLIC_FONTS_BASE_URL 에 설정된 오리진(예: https://static.pregoi.com)을 사용. |
변경 대상
- 환경 변수:
.env,.env.example, Cloudflare Pages 대시보드 Environment Variables 에NEXT_PUBLIC_FONTS_BASE_URL=https://static.pregoi.com설정. 폰트 관련 코드는 이 변수만 참조하도록 유지. - 문서:
apps/client-web/scripts/README-fonts.md,docs/planning/font-pc-mobile-parity-plan.md등에 기재된 예시 URL을https://static.pregoi.com및NEXT_PUBLIC_FONTS_BASE_URL기준으로 수정.
검증
- Pages 대시보드에서
NEXT_PUBLIC_FONTS_BASE_URL=https://static.pregoi.com설정 여부 최종 확인.
1.2 CSS 생성 스크립트 (generate-fonts-css.js)
| 항목 | 내용 |
|---|---|
| 파일 | apps/client-web/scripts/generate-fonts-css.js |
| 역할 | 빌드 전 NEXT_PUBLIC_FONTS_BASE_URL을 읽어 public/fonts/fonts.css 생성. @font-face의 src: url()이 해당 베이스 + /fonts/{locale}/{filename} 형태가 되도록 함. |
검토·유지 사항
- 베이스 URL: 이미
process.env.NEXT_PUBLIC_FONTS_BASE_URL사용 중. 값을https://static.pregoi.com으로 설정하면 출력 CSS의 URL이https://static.pregoi.com/fonts/en/Inter-Regular.[hash].woff2등이 됨. 추가 로직 변경 없이 환경 변수만 변경하면 됨. - R2 커스텀 도메인: 스크립트는 “fetch”가 아니라 로컬 manifest 기준으로 URL 문자열만 조합하므로, R2에서 실제로
static.pregoi.com으로 서빙되는지만 인프라에서 보장하면 됨. - 주석: 파일 상단 주석에 예시 URL을
https://static.pregoi.com또는 “R2 custom domain”으로 갱신 권장.
1.3 Cache Busting (해시 파일명)
| 항목 | 현재 | 변경 목표 |
|---|---|---|
| 파일명 | font-manifest.json 및 fonts.css에 Inter-Regular.8d71c824.woff2 등 해시 포함명 사용 | 유지. R2 버킷에 이미 font.[hash].woff2 형태로 올라가 있다면, manifest/생성 스크립트가 그 파일명을 참조하도록 일치시킴. |
변경 대상
generate-fonts-css.js:font-manifest.json에서 locale별 파일명을 읽고 있음. R2에 올라간 파일명(해시 포함)과 manifest가 일치하는지 확인.build_fonts.py/upload_fonts_to_r2_wrangler.sh: 업로드 시 해시된 파일명 사용 여부 확인. 현재 구조 유지 시 추가 코드 변경 없이 기획서에 “R2 버킷 파일명과 manifest 일치 확인”만 체크리스트로 명시.
2. 글로벌 타이포그래피 및 Fallback 전략
2.1 언어별 폰트 스택 (:lang() 및 data-locale)
| 항목 | 현재 | 변경 목표 |
|---|---|---|
| 선택자 | app/globals.css에서 :root[data-locale="xx"] 및 :root:lang(xx) body 등 사용 | 유지. :lang() 및 data-locale 기반 로직 유지. |
| KO fallback | 'Noto Sans KR', 'Pretendard', -apple-system, 'Apple SD Gothic Neo', 'Malgun Gothic', sans-serif | 'Pregoi-KR', -apple-system, 'Apple SD Gothic Neo', 'Malgun Gothic', sans-serif (폰트 패밀리 이름이 Pregoi 전용으로 바뀌는 경우에 한함) |
| AR fallback | 'Noto Sans Arabic', 'IBM Plex Sans Arabic', 'Segoe UI Arabic', 'Geeza Pro', sans-serif | 'Pregoi-AR', 'Segoe UI Arabic', 'Geeza Pro', sans-serif |
| JP fallback | 'Noto Sans JP', 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', 'Meiryo', 'Yu Gothic', sans-serif | 'Pregoi-JP', 'Hiragino Sans', 'Meiryo', sans-serif |
참고
- 폰트 패밀리 이름: R2/빌드에서 사용하는 실제
@font-face의font-family이름이 Pregoi-KR / Pregoi-AR / Pregoi-JP로 배포된다면,globals.css와locales/*.css의--locale-font및@font-face의 family 이름을 위 스택에 맞춰 변경. - 현재는 Noto Sans KR, Noto Sans Arabic, Noto Sans JP 등으로 되어 있으므로, “Pregoi-*”로의 전환은 폰트 에셋·@font-face 정의가 바뀌는 시점에 맞춰 진행. 기획서에는 “변경 목표”만 명시.
변경 대상
apps/client-web/app/globals.css::root[data-locale="ko"],:root[data-locale="ar"],:root[data-locale="ja"]내--locale-font값.apps/client-web/public/locales/ko.css,ar.css,ja.css: 동일 fallback 스택으로 정렬.apps/client-web/scripts/generate-fonts-css.js:FONT_FAMILIES에서 ko/ar/ja의font-family이름을 Pregoi-*로 바꿀 경우 함께 수정.
2.2 렌더링 안정화 (Windows/Chrome)
| 항목 | 현재 | 변경 목표 |
|---|---|---|
| -webkit-font-smoothing | body에 -webkit-font-smoothing: antialiased 적용됨 | 유지 |
| transform hack | h1, h2, h3, .smooth-text에 transform: rotate(0.03deg), text-shadow 적용됨 | 유지 |
변경 대상
- 없음. 이미 반영되어 있으면 기획서에 “확인만” 표시.
3. 멀티테넌트 라우팅 및 API Gateway 연동
3.1 API 베이스 URL 및 환경 변수 통일
원칙: 향후 API 베이스 도메인 변경을 쉽게 하기 위해, API 베이스 URL은 반드시 환경 변수로만 관리하고 코드 내에는 하드코딩하지 않는다.
| 항목 | 현재 | 변경 목표 |
|---|---|---|
| 클라이언트 API 베이스 | /api/... 상대 경로 또는 미정의 | Zuplo URL 등 실제 주소는 환경 변수 값 사용 (기본값 예: https://prego-main-bb45748.d2.zuplo.dev) |
| 환경 변수 이름 | (없을 수 있음) | NEXT_PUBLIC_API_URL 로 통일. 모든 API 호출이 이 변수를 참조하도록 구현. |
| 도메인 변경 | — | 배포 환경별·시점별로 .env 또는 Pages env에서 NEXT_PUBLIC_API_URL만 변경하면 되도록, 코드 전반에 해당 환경 변수 적용. |
변경 대상
- 환경 변수:
.env,.env.example, Cloudflare Pages Environment Variables에NEXT_PUBLIC_API_URL=https://prego-main-bb45748.d2.zuplo.dev(또는 운영 도메인) 설정. - API 호출 레이어:
fetch('/api/...')또는 하드코딩된 호스트를 사용하는 모든 위치에서, 베이스 URL을process.env.NEXT_PUBLIC_API_URL(또는 이를 읽는 공통 유틸)로만 구성.- 예:
apps/client-web/app/page.tsx,apps/client-web/app/notices/page.tsx,apps/client-web/app/leaves/page.tsx등. apps/client-web/app/signin,app/signup및components/signin-form등에서 사용하는/api/auth/signin,/api/auth/signup호출.
- 예:
- 공통화:
lib/api-client.ts또는config/env.ts등에서getApiBaseUrl()→process.env.NEXT_PUBLIC_API_URL반환하도록 하고, 코드 어디에도 API 베이스 URL 문자열을 직접 쓰지 않도록 모든 API 호출이 이 함수/변수를 사용하도록 변경.
3.2 Tenant Context (companyId) 주입
| 항목 | 내용 |
|---|---|
| 라우팅 | app/[companyId]/... 경로에서 companyId는 이미 URL에서 추출됨. CompanyLocaleProvider, layout params 등에서 사용 중. |
| API 경로 | 요청 경로를 ${NEXT_PUBLIC_API_URL}/${companyId}/api/... 형태로 구성 (실제 값은 env에서 로드). |
| 헤더 | X-Prego-Company: ${companyId} 를 모든 Gateway 요청에 포함. |
변경 대상
- API를 호출하는 컴포넌트/훅:
companyId를 props/context에서 받아:- URL:
${apiBase}/${companyId}/api/... - Headers:
{ 'X-Prego-Company': companyId }
- URL:
contexts/company-locale-context.tsx: 이미companyId제공. API 클라이언트가 이 context를 사용하거나, 호출부에서companyId를 넘기도록 정책 통일.
3.3 Zuplo 대응 헤더
| 항목 | 내용 |
|---|---|
| 헤더 | Gateway(Zuplo)에서 사용할 x-prego-email, x-prego-employee-id 등을 fetch 옵션의 headers에 포함. |
| 적용 범위 | 인증/세션 정보가 있는 요청(예: 로그인 후 API 호출)에서 해당 값을 읽어 헤더로 설정. |
변경 대상
- API 클라이언트 또는 인증 훅: 세션/쿠키에서 email·employee-id를 조회한 뒤,
headers['x-prego-email'],headers['x-prego-employee-id']로 설정. (실제 키 이름은 Zuplo 설정에 맞춤.)
4. 빌드 설정 (package.json)
| 항목 | 현재 | 변경 목표 |
|---|---|---|
| pages:build | node scripts/generate-fonts-css.js && NEXT_BUILD_WORKERS=0 npx @cloudflare/next-on-pages | 유지. 이미 generate-fonts-css.js가 next-on-pages 이전에 실행됨. |
| 환경 변수 | 빌드 시 process.env.NEXT_PUBLIC_FONTS_BASE_URL 사용 | Cloudflare Pages 빌드 환경에 NEXT_PUBLIC_FONTS_BASE_URL=https://static.pregoi.com 설정되어 있는지 확인. |
변경 대상
- 코드 변경 없음. 문서·체크리스트에 “Pages 빌드 환경 변수에
NEXT_PUBLIC_FONTS_BASE_URL설정 확인” 항목 추가.
5. 보안·메타데이터·CORS
5.1 CORS 정렬
| 항목 | 현재 | 변경 목표 |
|---|---|---|
| R2 CORS | Pulumi/대시보드: app.pregoi.com, pregoi.com, prego-web-anm.pages.dev, localhost | https://x.pregoi.com 등 새 오리진이 있다면 CORS Allowed Origins에 추가. 하드코딩된 오리진 검사가 있으면 충돌하지 않도록 정리. |
| 클라이언트 | 클라이언트 코드 내 하드코딩된 origin 검사가 있다면 x.pregoi.com 등 새 도메인 반영. | 필요 시 허용 목록을 env 또는 설정에서 읽도록 변경. |
변경 대상
prego-pulumi/__main__.py: R2 CORS의allowedOrigins에https://x.pregoi.com추가 (해당 도메인이 프론트 서빙에 사용되는 경우).- 기타 CORS/allowlist가 하드코딩된 파일 검색 후,
x.pregoi.com반영 또는 설정 기반으로 전환.
5.2 Preload 태그 (layout)
| 항목 | 현재 | 변경 목표 |
|---|---|---|
| 폰트 preload | app/layout.tsx에서 fontEnHref 등으로 preload. fontsBaseTrimmed 사용 시 R2 URL로 이미 설정됨. | 베이스 URL이 https://static.pregoi.com 이면 preload URL이 https://static.pregoi.com/fonts/en/... 가 되어 FOIT 최소화. |
변경 대상
- 코드 변경 없음.
NEXT_PUBLIC_FONTS_BASE_URL=https://static.pregoi.com설정만 하면 layout의 기존 로직으로static.pregoi.com도메인 preload 적용됨. - 기획서에 “Preload가 static.pregoi.com을 사용하는지 배포 후 검증” 항목 추가.
6. 테스트·검증 체크리스트 (SRE 관점)
다음 항목을 배포 후 또는 인프라 변경 후 확인한다.
| # | 확인 항목 | 방법 | 기대 결과 |
|---|---|---|---|
| 1 | NEXT_PUBLIC_FONTS_BASE_URL | Pages 대시보드 Environment Variables | https://static.pregoi.com |
| 2 | 폰트 MIME Type | R2 대시보드 → 버킷 → Objects → 파일 Metadata | Content-Type: font/woff2 |
| 3 | 폰트 URL 응답 | 브라우저/curl로 https://static.pregoi.com/fonts/fonts.css 요청 | 200, Content-Type: text/css, @font-face 내용 |
| 4 | WOFF2 URL 응답 | https://static.pregoi.com/fonts/en/Inter-Regular.[hash].woff2 요청 | 200, Content-Type: font/woff2 |
| 5 | 브라우저 캐시 | 폰트 요청 응답 헤더 | cf-cache-status: HIT (MISS/DYNAMIC만 나오면 static.pregoi.com에 Cache Everything 페이지 규칙 검토) |
| 6 | CORS | 앱 페이지에서 폰트 요청 시 Response Headers | Access-Control-Allow-Origin에 앱 오리진 포함 |
| 7 | API Gateway | 로그인/데이터 로드 시 Network 탭 | NEXT_PUBLIC_API_URL에 설정된 도메인으로 ${companyId}/api/... 호출, 필요 시 X-Prego-Company, x-prego-email 등 헤더 포함 |
| 8 | Preload | 페이지 소스 또는 Network 탭 | <link rel="preload" href="https://static.pregoi.com/..."> 존재 |
7. 변경 대상 파일 요약 (구현 시 참고)
| 구분 | 파일/위치 | 변경 요약 |
|---|---|---|
| 폰트 URL | .env, .env.example, Pages env | NEXT_PUBLIC_FONTS_BASE_URL=https://static.pregoi.com |
| API URL | .env, .env.example, Pages env | NEXT_PUBLIC_API_URL (예: https://prego-main-bb45748.d2.zuplo.dev). 코드에는 하드코딩 없이 이 env만 사용. |
| 폰트 CSS 생성 | apps/client-web/scripts/generate-fonts-css.js | 주석/예시 URL 갱신. (선택) FONT_FAMILIES를 Pregoi-*로 변경 시 ko/ar/ja 반영 |
| 타이포그래피 | apps/client-web/app/globals.css | KO/AR/JP fallback 스택을 Pregoi-* 기준으로 변경 (에셋 전환 시) |
| 로케일 CSS | apps/client-web/public/locales/ko.css, ar.css, ja.css | 위와 동일 fallback 정렬 |
| Layout | apps/client-web/app/layout.tsx | 로직 변경 없음. env만 static.pregoi.com으로 설정 시 동작 |
| API 베이스 | 신규 또는 기존 lib/api-client.ts, config 등 | NEXT_PUBLIC_API_URL 환경 변수만 참조, 하드코딩 없음. companyId 경로/헤더 주입. |
| API 호출처 | app/page.tsx, notices/page.tsx, leaves/page.tsx, signin·signup 페이지·컴포넌트 등 | 공통 API 클라이언트 사용, companyId·헤더 주입 |
| CORS | prego-pulumi/__main__.py, Cloudflare R2/대시보드 | https://x.pregoi.com 등 필요 오리진 추가 |
| 문서 | README-fonts.md, font-pc-mobile-parity-plan.md 등 | static.pregoi.com, Zuplo URL 예시 및 체크리스트 반영 |
8. 기획서 상태
- 코드 생성 없음: 본 문서는 변경 계획만 기술.
- 구현 시: TypeScript 타입 안전성 유지, 기존 비즈니스 로직 변경 최소화.
- 배포 후: §6 체크리스트로 폰트·캐시·API·CORS 검증.