# Vercel 배포 프론트엔드 설정 내역 > 작성일: 2026-02-09 --- ## 1. Puppeteer 패키지 교체 ### 변경 내용 | 항목 | Before | After | |------|--------|-------| | 패키지 | `puppeteer` | `puppeteer-core` + `@sparticuz/chromium` | ### 왜 교체해야 하는가 `puppeteer`는 설치 시 **Chromium 브라우저 전체(~170MB)**를 함께 다운로드한다. 이는 로컬/Docker 환경에서는 문제없지만, Vercel 서버리스 함수는 **패키지 크기 제한(50MB 압축, 250MB 비압축)**이 있어 배포 자체가 불가능하다. ### 로컬 vs Vercel 차이 | 환경 | Chromium 제공 방식 | 설정 | |------|-------------------|------| | **로컬 (macOS)** | 사용자 PC에 설치된 Google Chrome 사용 | `PUPPETEER_EXECUTABLE_PATH` 환경변수로 경로 지정 | | **Vercel (서버리스)** | `@sparticuz/chromium`이 AWS Lambda용 경량 Chromium 제공 | `chromium.executablePath()`로 자동 경로 획득 | ### 분기 처리 코드 (`src/app/api/pdf/generate/route.ts`) ```typescript const isVercel = process.env.VERCEL === '1'; // Vercel이 자동 주입하는 환경변수 const browser = await puppeteer.launch({ args: isVercel ? chromium.args : ['--no-sandbox', ...], executablePath: isVercel ? await chromium.executablePath() // Vercel: @sparticuz/chromium 경량 바이너리 : process.env.PUPPETEER_EXECUTABLE_PATH || '/usr/bin/google-chrome-stable', // 로컬: 시스템 Chrome headless: true, }); ``` - `process.env.VERCEL === '1'`: Vercel 배포 환경에서 자동으로 설정되는 값 - `chromium.args`: 서버리스 환경에 최적화된 Chromium 실행 인자 (싱글 프로세스, SwiftShader 등) ### 로컬 환경변수 설정 (`.env.local`) ``` PUPPETEER_EXECUTABLE_PATH=/Applications/Google Chrome.app/Contents/MacOS/Google Chrome ``` 기존 `puppeteer`는 Chromium을 자체 번들했으므로 경로 지정이 불필요했지만, `puppeteer-core`는 브라우저를 포함하지 않으므로 로컬에서도 Chrome 경로를 명시해야 한다. --- ## 2. next.config.ts 변경 ### serverExternalPackages | Before | After | |--------|-------| | `['puppeteer']` | `['puppeteer-core', '@sparticuz/chromium']` | Webpack이 이 패키지들을 번들에 포함하지 않고 Node.js 런타임에서 직접 로드하도록 지정. `@sparticuz/chromium`은 바이너리 파일을 포함하고 있어 번들링하면 깨진다. ### TypeScript / ESLint 빌드 검사 | 항목 | Before | After | 이유 | |------|--------|-------|------| | `typescript.ignoreBuildErrors` | `true` | `false` | Vercel 배포 시 타입 에러가 런타임 버그로 이어질 수 있으므로 빌드 단계에서 차단 | | `eslint.ignoreDuringBuilds` | `true` | `true` (유지) | 기존 미사용 import 에러 791개 존재, 점진적 해결 예정 | --- ## 3. vercel.json 생성 ```json { "regions": ["icn1"], "functions": { "src/app/api/pdf/generate/route.ts": { "memory": 1024, "maxDuration": 30 } } } ``` | 설정 | 값 | 이유 | |------|-----|------| | `regions` | `icn1` (서울) | 사용자가 한국에 위치, 백엔드 API도 한국 서버 | | `memory` | 1024MB | Chromium 브라우저 실행에 최소 512MB 이상 필요, PDF 렌더링 안정성 확보 | | `maxDuration` | 30초 | 복잡한 문서의 PDF 변환 시 기본 10초로는 부족 | --- ## 4. 환경변수 가이드 Vercel Dashboard에 등록해야 할 환경변수 목록은 `claudedocs/vercel/vercel-env-setup-guide.md` 참조. 핵심 포인트: - `API_KEY`: Sensitive 체크 필수, `NEXT_PUBLIC_` 접두사 절대 금지 - `PUPPETEER_EXECUTABLE_PATH`: Vercel에서는 설정 불필요 (`@sparticuz/chromium`이 처리) - `VERCEL=1`: Vercel이 자동 주입, 별도 설정 불필요 --- ## 5. TypeScript 에러 수정 `ignoreBuildErrors: false`로 변경하면서 기존에 숨겨져 있던 TS 에러 16개+ 수정. | 에러 유형 | 파일 수 | 원인 | |-----------|--------|------| | `WorkOrderItem` 프로퍼티 누락 | 5개 파일 | `orderNodeId`, `orderNodeName` 필드 추가 후 목업 데이터 미갱신 | | `WorkOrder` 프로퍼티 누락 | 3개 파일 | `shutterCount` 필드 추가 후 합성 객체 미갱신 | | `unknown` → `ReactNode` | 1개 파일 | `Record` 값을 JSX에 직접 사용 | | `object` 프로퍼티 접근 | 2개 파일 | `Object.entries()` 후 타입 narrowing 부족 | | 타입 미export | 1개 파일 | `BomCalculationResult` import만 하고 re-export 안 함 | | implicit `any` | 1개 파일 | 콜백 파라미터 타입 어노테이션 누락 | --- ## 변경 파일 전체 목록 ``` 수정: src/app/api/pdf/generate/route.ts # Puppeteer 교체 next.config.ts # 빌드 설정 package.json / package-lock.json # 패키지 교체 .env.local # Chrome 경로 추가 .env.example # Chrome 경로 가이드 추가 신규: vercel.json # Vercel 배포 설정 claudedocs/vercel/vercel-env-setup-guide.md # 환경변수 가이드 claudedocs/vercel/vercel-deployment-setup.md # 이 문서 TS 에러 수정 (13개 파일): src/app/[locale]/(protected)/quality/qms/components/InspectionModalV2.tsx src/app/[locale]/(protected)/quality/qms/mockData.ts src/components/material/ReceivingManagement/actions.ts src/components/orders/OrderSalesDetailView.tsx src/components/production/WorkerScreen/index.tsx src/components/production/WorkerScreen/WorkLogModal.tsx src/components/production/WorkOrders/documents/InspectionReportModal.tsx src/components/production/WorkOrders/WorkOrderDetail.tsx src/components/production/WorkOrders/WorkOrderEdit.tsx src/components/quotes/LocationDetailPanel.tsx src/components/quotes/QuoteSummaryPanel.tsx src/components/quotes/QuotePreviewContent.tsx src/components/quotes/actions.ts ``` --- ## 6. Vercel 비용 주의사항 ### 비용 발생 구조 Vercel은 **서버리스 함수 호출 횟수 + 실행 시간 + 대역폭**으로 과금된다. 우리 프로젝트에서 비용이 튈 수 있는 요소를 분석한다. ### 6-1. API Proxy 패턴 (가장 큰 비용 요소) 현재 구조: `클라이언트 → /api/proxy/* (Vercel 서버리스) → PHP 백엔드` | 문제 | 설명 | |------|------| | **이중 대역폭** | 모든 API 응답이 Vercel 서버리스를 거쳐 클라이언트로 전달 (대역폭 2배) | | **함수 호출 폭발** | 모든 API 요청이 서버리스 함수 1회 호출 = 페이지 로드마다 수~수십 회 | | **실행 시간 누적** | 각 함수가 백엔드 응답을 기다리는 시간만큼 과금 | **왜 이 패턴을 쓰는가**: HttpOnly 쿠키에 저장된 `access_token`은 JavaScript로 읽을 수 없어서, 서버(서버리스 함수)에서 쿠키를 읽어 `Authorization` 헤더로 변환해야 한다. **예상 영향**: 사용자 수가 늘어날수록 비용이 선형 증가. ERP 특성상 한 페이지에서 3-10개 API를 호출하므로, 동시 접속자 50명이면 분당 수백~수천 회 함수 호출 가능. ### 6-1-1. 30초 폴링 — 가장 심각한 비용 요소 프로젝트에 **30초 간격 폴링이 2개** 존재하며, 이것들이 서버리스 프록시를 통과한다. | 폴링 | 파일 | 간격 | 호출 API | |------|------|------|---------| | **메뉴 폴링** | `src/hooks/useMenuPolling.ts` | 30초 | `refreshMenus()` → `/api/proxy/*` | | **알림(Today Issue) 폴링** | `src/layouts/AuthenticatedLayout.tsx` | 30초 | `getUnreadTodayIssues()` → `/api/proxy/*` | **폴링만의 서버리스 함수 호출 추정 (동시접속 50명, 8시간 근무 기준)**: ``` 메뉴 폴링: 50명 × 2회/분 × 60분 × 8시간 = 48,000회/일 알림 폴링: 50명 × 2회/분 × 60분 × 8시간 = 48,000회/일 ───────────────────────────────────────────────────────── 합계: 96,000회/일 (폴링만으로, 일반 API 호출 제외) 월간: 96,000 × 22일(근무일) = 약 2,112,000회/월 ``` Vercel Pro 플랜 기준 서버리스 함수 포함 실행량 100만 회/월이므로, **폴링만으로 2배 초과**. 이 폴링 2개가 Edge Middleware로 전환 시 가장 큰 비용 절감 효과를 볼 수 있다. > 참고: 두 폴링 모두 탭 비활성 시 일시정지하는 최적화가 적용되어 있지만, 활성 탭 기준으로는 위 수치가 그대로 적용된다. ### 6-2. PDF 생성 함수 | 항목 | 값 | 비용 영향 | |------|-----|----------| | 메모리 | 1024MB | 기본(128MB) 대비 8배 비용 | | 최대 실행 시간 | 30초 | 긴 실행 = 높은 과금 | | 빈도 | 낮음 (수동 PDF 변환) | 자동 배치가 아니므로 실제 비용 영향은 적음 | PDF는 사용 빈도가 낮아 큰 문제는 아니지만, 메모리 1024MB로 설정했으므로 호출당 비용이 높다. ### 6-3. Server Actions (bodySizeLimit: 10MB) `next.config.ts`에서 `serverActions.bodySizeLimit: '10mb'`로 설정. 이미지 업로드 시 큰 페이로드가 서버리스 함수를 통과하므로 대역폭 비용 발생. 단, 이미지 저장 자체는 PHP 백엔드가 처리하므로 Vercel 스토리지 비용은 없음. ### 6-4. 비용 안전 요소 | 항목 | 상태 | 이유 | |------|------|------| | 이미지/파일 저장 | 안전 | PHP 백엔드 서버에 저장, Vercel에 저장 안 함 | | Vercel Image Optimization | 미사용 | `remotePatterns`에 `placehold.co`만 등록 (개발용) | | ISR/SSG | 미사용 | 전체 Client Component, 정적 생성 없음 | --- ## 7. API Proxy 비용 절감 방안 ### 현재 문제 ``` 클라이언트 → [Vercel 서버리스 함수] → PHP 백엔드 ↑ 매 API 요청마다 호출 ↑ Node.js Runtime (비쌈) ``` `/api/proxy/[...path]/route.ts`가 **모든 백엔드 API 호출을 중계**하므로 서버리스 함수 호출 횟수가 폭발적으로 증가한다. ### 방안 1: Edge Middleware Rewrite (프론트엔드 변경만으로 가능) ``` 클라이언트 → [Edge Middleware] → PHP 백엔드 (직접) ↑ Edge Runtime (매우 저렴) ↑ 쿠키 읽기 가능 ``` **원리**: 현재 `middleware.ts`는 이미 Edge Runtime에서 쿠키를 읽고 있다. 이를 확장하여 `/api/proxy/*` 요청을 Edge에서 백엔드로 직접 rewrite하면 서버리스 함수 호출을 제거할 수 있다. **장점**: - Edge 함수는 서버리스 대비 **10-100배 저렴** (실행 시간이 아닌 요청 수 기준) - 프론트엔드만 변경하면 됨 (백엔드 수정 불필요) - 응답 지연(latency)도 감소 (중간 서버리스 단계 제거) **한계**: - Edge Runtime은 Node.js API 일부를 사용 못함 - **토큰 자동 갱신(refresh)** 처리가 복잡해짐 — 현재 `authenticatedFetch`가 401 감지 → refresh → retry를 처리하는데, Edge Middleware의 rewrite에서는 이 패턴 구현이 어려움 - FormData/바이너리 응답 처리에 제약 가능 **적용 가능 범위**: 단순 GET/POST API 호출 (토큰 갱신 없이 정상 동작하는 요청) ### 방안 2: 백엔드 CORS 허용 + 직접 호출 (백엔드 변경 필요) ``` 클라이언트 → PHP 백엔드 (직접) ↑ Vercel을 거치지 않음 ↑ 서버리스 비용 0 ``` **원리**: PHP 백엔드가 Vercel 도메인에서의 CORS를 허용하고, 쿠키를 직접 설정(`SameSite=None; Secure`)하면 프록시 자체가 불필요해진다. **장점**: - 서버리스 함수 호출 **완전 제거** - 가장 큰 비용 절감 효과 - 응답 속도 최적 (중간 단계 없음) **한계**: - **백엔드 수정 필수**: CORS 헤더, 쿠키 SameSite 정책 변경 - **HttpOnly 쿠키 도메인 문제**: 프론트(Vercel)와 백엔드(PHP)가 다른 도메인이면 3rd-party 쿠키로 분류되어 브라우저 차단 가능 - 같은 도메인/서브도메인 구조가 아니면 추가 설정 필요 (`app.example.com` ↔ `api.example.com`) ### 방안 3: 하이브리드 (권장) | 요청 유형 | 처리 방식 | 비용 | |-----------|----------|------| | 단순 데이터 조회 (GET) | Edge Middleware rewrite | 매우 저렴 | | 데이터 변경 (POST/PUT/DELETE) | 기존 서버리스 프록시 유지 | 현행 유지 | | 파일 업로드 (multipart) | 기존 서버리스 프록시 유지 | 현행 유지 | | 토큰 갱신 필요 시 | 기존 서버리스 프록시 유지 | 현행 유지 | GET 요청이 전체 API 호출의 60-80%를 차지하므로, 이것만 Edge로 옮겨도 비용을 절반 이상 줄일 수 있다. ### 방안 비교 요약 | 방안 | 비용 절감 | 프론트 변경 | 백엔드 변경 | 복잡도 | 권장 | |------|----------|------------|------------|--------|------| | **1. Edge Rewrite** | 중 (60-80%) | O | X | 중 | 단기 | | **2. CORS 직접 호출** | 최대 (95%+) | O | O | 고 | 장기 | | **3. 하이브리드** | 중상 (60-80%) | O | X | 중 | **현실적 1순위** | ### 다음 단계 1. Vercel 배포 후 실제 함수 호출 횟수/비용 모니터링 2. 비용이 예상보다 높으면 방안 3(하이브리드) 우선 적용 3. 장기적으로 백엔드팀과 협의하여 방안 2(CORS) 검토 --- ## 8. 월간 비용 산정 (Vercel Pro, 서울 icn1 리전) > 산정일: 2026-02-09 / 출처: [Vercel Pricing](https://vercel.com/pricing), [Fluid Compute Pricing](https://vercel.com/docs/functions/usage-and-pricing) ### 8-1. Vercel 요금 구조 (Pro 플랜) | 항목 | Pro 포함량 | 초과 단가 | |------|-----------|----------| | **기본 요금** | $20/개발자 시트/월 | — | | **사용 크레딧** | $20/월 포함 (사용량 차감) | — | | **Invocations** (함수 호출) | 크레딧 차감 | $0.60 / 100만 회 | | **Active CPU** (icn1) | 크레딧 차감 | $0.169 / 시간 | | **Provisioned Memory** (icn1) | 크레딧 차감 | $0.014 / GB-시간 | | **Edge Requests** | 1,000만 회/월 | ~$2 / 100만 회 | | **Bandwidth** | 1TB/월 | 초과 시 GB당 과금 | > Fluid Compute 모델: Active CPU는 **코드 실행 중에만** 과금 (I/O 대기 시 과금 안 됨). Provisioned Memory는 **인스턴스 활성 시간** 전체 과금. ### 8-2. 트래픽 추정 전제 (실제 사업 구조 반영) | 전제 | 값 | 비고 | |------|-----|------| | 개발자 시트 | **2명** | 프론트엔드 2명 | | 서비스 회사 수 | **50개 이하** (초기) | 점진적 확장 | | 회사당 이용자 | **~5명** | ERP 실사용자 | | 총 등록 사용자 | **~250명** | 50사 × 5명 | | 동시접속률 | **30~40%** | ERP 특성 (전원 동시 사용 안 함) | | **동시접속 사용자** | **~75~100명** | 250명 × 30~40% | | 근무 시간 | 8시간/일 | | | 월 근무일 | 22일 | | | 메뉴 폴링 간격 | 30초 | `useMenuPolling.ts` | | 알림 폴링 간격 | 30초 | `AuthenticatedLayout.tsx` | | 페이지 이동 시 API 호출 | 평균 5개/페이지 | | | 페이지 이동 횟수 | 평균 50회/사용자/일 | | | PDF 생성 | 100건/월 | | ### 8-3. 월간 함수 호출 횟수 (동시접속 75명 기준) ``` ① 메뉴 폴링: 75명 × 2회/분 × 480분 × 22일 = 1,584,000회 ② 알림 폴링: 75명 × 2회/분 × 480분 × 22일 = 1,584,000회 ③ 페이지 API: 75명 × 250회/일 × 22일 = 412,500회 ④ PDF 생성: = 100회 ────────────────────────────────────────────────────────────── 합계: 3,580,600회/월 ``` ### 8-4. 시나리오 A — 현재 구조 (전부 서버리스 프록시) 모든 API 호출이 `/api/proxy/[...path]` 서버리스 함수를 경유. **API 프록시 1회당 비용 추정 (icn1)**: - Active CPU: ~30ms (쿠키 읽기, 헤더 구성, 응답 파싱) - Instance alive: ~300ms (백엔드 응답 대기 포함) - Memory: 128MB (0.128 GB) | 항목 | 계산 | 월 비용 | |------|------|--------| | **Invocations** | 3,580,000 × $0.60/1M | $2.15 | | **Active CPU** | 3,580,000 × 0.03s = 107,400s = 29.8hr × $0.169 | $5.04 | | **Provisioned Memory** | 3,580,000 × 0.128GB × 0.3s / 3600 = 38.2 GB-hr × $0.014 | $0.53 | | **PDF CPU** | 100 × 5s / 3600 × $0.169 | $0.02 | | **PDF Memory** | 100 × 1GB × 15s / 3600 × $0.014 | $0.01 | | **함수 사용량 소계** | | **$7.75** | | **기본 요금** | 개발자 2명 × $20 | **$40.00** | | **사용 크레딧** | -$20 (포함) | **-$20.00** | | **예상 월 합계** | | **~$28** | > 함수 사용량 $7.75는 $20 크레딧 이내. 초과 과금 없음. ### 8-5. 시나리오 B — 하이브리드 (폴링 + GET을 Edge로) 폴링 2개 + 일반 GET을 Edge Middleware rewrite로 전환. POST/PUT/DELETE/파일업로드만 서버리스 유지. ``` Edge로 이동: 폴링 3,168,000 + GET ~300,000 = ~3,468,000 → Edge Requests (10M 포함 이내) 서버리스 유지: POST/PUT/DELETE ~112,500 + PDF 100 = ~112,600 ``` | 항목 | 계산 | 월 비용 | |------|------|--------| | **Edge Requests** | 3,468,000회 (10M 포함 이내) | $0 | | **Invocations** | 112,600회 × $0.60/1M | $0.07 | | **Active CPU** | 112,600 × 0.05s / 3600 × $0.169 | $0.26 | | **Provisioned Memory** | 112,600 × 0.128GB × 0.4s / 3600 × $0.014 | $0.02 | | **PDF** | (위와 동일) | $0.03 | | **함수 사용량 소계** | | **$0.38** | | **기본 요금** | 개발자 2명 × $20 | **$40.00** | | **사용 크레딧** | -$20 (포함) | **-$20.00** | | **예상 월 합계** | | **~$20** | ### 8-6. 성장 단계별 비용 비교 | 단계 | 서비스 회사 | 총 사용자 | 동시접속 | 시나리오 A | 시나리오 B | |------|-----------|----------|---------|-----------|-----------| | **초기** | 10사 | 50명 | ~20명 | **~$22** (함수 $2) | **~$20** (함수 $0.1) | | **안정기** | 50사 | 250명 | ~75명 | **~$28** (함수 $8) | **~$20** (함수 $0.4) | | **성장기** | 100사 | 500명 | ~150명 | **~$35** (함수 $15) | **~$21** (함수 $0.8) | | **확장기** | 200사 | 1,000명 | ~300명 | **~$50** (함수 $30) | **~$22** (함수 $1.5) | > 모든 단계에서 고정 비용 = 개발자 2시트 $40 − 크레딧 $20 = **$20**. 함수 비용은 이 $20 크레딧에서 차감되며, 초기~안정기에는 크레딧 이내로 해결됨. ### 8-7. 결론 - **초기~안정기 (50사 이하)**: 월 **~$22~28**. 함수 비용 $8 이하로 **크레딧 $20 이내**. 최적화 불필요. - **성장기 (100사)**: 함수 비용 $15. 아직 크레딧 이내이지만 여유 줄어듦. 하이브리드 전환 검토 시점. - **확장기 (200사 이상)**: 함수 비용이 크레딧 초과 → 추가 과금 발생. 하이브리드 전환 필수. - **핵심 비용은 개발자 시트**. 프론트 2명 기준 고정 $20/월. 함수 비용은 초기에 미미. - 폴링 2개만 Edge로 전환해도 함수 비용 **95% 절감** 가능 (100사 기준 $15 → $0.8). --- ## 배포 전 체크리스트 - [x] `puppeteer-core` + `@sparticuz/chromium` 설치 - [x] PDF 라우트 로컬/Vercel 분기 처리 - [x] `next.config.ts` 설정 변경 - [x] `vercel.json` 생성 - [x] TypeScript 에러 0개 확인 - [x] 로컬 Chrome 경로 설정 (`.env.local`) - [ ] 로컬 PDF 변환 테스트 - [ ] Vercel Dashboard 환경변수 등록 - [ ] Vercel 배포 및 PDF 변환 테스트