P0 장애 — 마케팅 페이지 4개 완전 불능 (300초 타임아웃)
해결: Vercel Function Region을 iad1(버지니아) → sin1(싱가포르)로 변경
개요
항목 | 내용 |
날짜 | 2026-03-07 |
심각도 | P0 (프로덕션 페이지 4개 완전 불능) |
영향 범위 | 마케팅 페이지 4개 + 홈페이지 포함 전체 SSR 페이지 성능 저하 |
해결 시간 | 진단 ~3시간, 수정 적용 5분 |
근본 원인 | Vercel Function Region(iad1, 버지니아)과 Supabase DB(sin1, 싱가포르) 간 16,000km 거리 |
증상
배포 직후(ISR 캐시 비어있는 상태) 아래 4개 마케팅 페이지에서 Vercel 함수 타임아웃(300초) 발생:
페이지 | 응답 시간 | 상태 |
/ko/categories/POPUP | 300,236ms | 타임아웃 |
/ko/categories/EXHIBITION | 300,388ms | 타임아웃 |
/ko/vocaloid | 300,224ms | 타임아웃 |
/ko/comic-world | 300,202ms | 타임아웃 |
Suspense fallback(스켈레톤)은 즉시 스트리밍되지만, 실제 데이터 컴포넌트가 300초 동안 응답 못 함.
ISR 캐시가 따뜻해지면 정상 동작 → 간헐적 문제로 오인.
진단 과정
Phase 1: DB 쿼리 자체가 느린가?
결론: 아니다. 로컬에서 프로덕션 DB 직접 연결 시 모든 쿼리 2초 이내 완료.
쿼리 그룹 | 로컬 실행 시간 |
POPUP (전체 시뮬레이션) | 889ms |
EXHIBITION | 1,230ms |
CONCERT (정상 페이지) | 906ms |
Vocaloid | 611ms |
Comic World | 475ms |
Phase 2: statement_timeout이 작동하는가?
결론: 완전히 무시됨. pgBouncer 트랜잭션 모드에서 SET 명령이 리셋됨.
statement_timeout: 3000설정 후pg_sleep(10)→ 10.4초에 완료 (타임아웃 미작동)
statement_timeout: 15000설정 후pg_sleep(20)→ 20.5초에 완료 (타임아웃 미작동)
- 원인: pgBouncer/Supavisor 트랜잭션 모드에서 트랜잭션 종료 시
SET명령 리셋
Phase 3: 근본 원인 — Vercel Function Region
근본 원인 발견! Vercel 함수가 DB와 반대편 대륙에서 실행 중
X-Vercel-Id: icn1::iad1::xxxxx ^^^^ Virginia, USA (미국 버지니아)
- Vercel Function:
iad1(미국 버지니아)
- Supabase DB:
aws-1-ap-southeast-1(싱가포르)
- 거리: ~16,000km, 쿼리당 왕복 300-400ms
마케팅 페이지는 4~5개 순차 DB 쿼리를 실행:
1. 카테고리 조회 ~400ms (본래 <1ms) 2. 상위 이벤트 ID 조회 ~400ms 3. 전체 데이터 JOIN ~400ms 4. 카테고리 맵 ~400ms 5. 원본 콘텐츠 맵 ~400ms = 레이턴시만 합산: ~2,000ms
콜드 스타트 시 TLS 핸드셰이크 + DNS 해석 + TCP 연결 수립이 대륙 간 왕복으로 인해 극도로 느려지고, 여러 페이지 동시 콜드 스타트 시 Supavisor 커넥션 경합으로 300초 타임아웃 도달.
Sentry 트랜잭션 분석
Sentry Discover에서
transaction.duration > 10000 조회 결과:- 홈페이지(
/:locale): 30,000ms (Sentry 캡 한도) — 여러 건
/vocaloid: 30,000ms
/comic-world: 20,641ms
/events/:slug: 19,382ms
특정 4개 페이지만의 문제가 아닌, 전체 SSR 페이지 성능 이슈 확인.
수정 내역
1. Vercel Function Region 변경 (핵심)
Vercel Dashboard → Settings → Functions → Function Region → Singapore (sin1)
vercel.json의 regions 설정은 Next.js SSR/ISR 페이지 라우트에 적용되지 않음. Vercel 대시보드 설정만 유효.2. client.ts — 무효한 statement_timeout 제거
// Before (무효 — pgBouncer가 무시) const client = postgres(connectionString, { connection: { statement_timeout: 15000 }, }); // After const client = postgres(connectionString, { connect_timeout: 10, idle_timeout: 20, max: 6, prepare: false, // NOTE: pgBouncer/Supavisor 트랜잭션 모드에서 무시됨 });
3. vercel.json 안전장치 추가
{ "regions": ["sin1"], "functions": { "apps/web/.next/server/**": { "maxDuration": 30 } } }
수정 후 결과
페이지 | Before (iad1) | After (sin1) | 개선 |
HOME | 30,000ms+ | 3.2s | ~10x |
POPUP | 300,000ms (타임아웃) | 1.2s | ~250x |
EXHIBITION | 300,000ms (타임아웃) | 0.8s | ~375x |
VOCALOID | 300,000ms (타임아웃) | 1.3s | ~230x |
COMIC_WORLD | 300,000ms (타임아웃) | 1.2s | ~250x |
CONCERT | 5,400ms | 0.5s | ~10x |
Before: X-Vercel-Id: icn1::iad1::xxxxx (Virginia) After: X-Vercel-Id: icn1::sin1::xxxxx (Singapore) ✅
교훈 (Lessons Learned)
1. Vercel Function Region은 DB와 같은 리전에 두어야 한다
Vercel 프로젝트 생성 시 기본 Function Region이
iad1 (Virginia)로 설정됨. Supabase를 싱가포르에 생성했다면 반드시 대시보드에서 Function Region도 변경해야 한다.2. Function Region ≠ Edge Network
- Edge Network: 전세계 PoP에서 캐시 서빙 — 유저와 가장 가까운 곳. 자동 분산.
- Function Region: SSR 코드가 실행되는 곳 — DB와 가까워야 함.
한국+일본 유저 91%라도, DB가 싱가포르에 있으면 Function도 싱가포르가 최적. ISR 캐시 히트 시 Function 자체가 실행되지 않으므로 유저 체감 영향 없음.
3. pgBouncer 트랜잭션 모드에서 SET 명령은 무시된다
postgres.js의 connection: { statement_timeout } 옵션은 무효. Supavisor 기본 타임아웃(2분)에 의존하거나 SET LOCAL 사용.4. ISR 캐시가 문제를 숨긴다
ISR(
revalidate = 86400)은 캐시가 따뜻하면 문제를 완전히 숨김. 배포 직후에만 증상이 나타나 "간헐적 장애"로 오인하기 쉬움.5. vercel.json의 regions는 SSR에 적용되지 않는다
vercel.json의 regions 설정은 API Routes에만 유효. Next.js SSR/ISR 페이지 라우트는 Vercel 대시보드 설정만 유효.타임라인
시간 | 행동 |
03-05 | 마케팅 페이지 300초 타임아웃 최초 인지 |
03-07 00:00 | 진단 시작 — 쿼리 타이밍 테스트 |
03-07 01:00 | statement_timeout 무효 확인 |
03-07 01:30 | X-Vercel-Id 헤더에서 iad1 발견 — 근본 원인 식별 |
03-07 02:00 | vercel.json regions가 SSR에 미적용 확인 |
03-07 02:05 | Sentry 조회 — 홈 포함 전체 SSR 영향 확인 |
03-07 02:10 | 대시보드에서 Function Region → sin1 변경 + Redeploy |
03-07 02:15 | 전 페이지 1초대 응답 확인 — 해결 완료 |

