바이브코딩 배포가 느린 진짜 이유: 코드보다 리전, 왕복 횟수, 이미지

바이브코딩 배포가 느린 진짜 이유: 코드보다 리전, 왕복 횟수, 이미지

바이브코딩으로 만든 앱이 로컬에서는 빠른데 배포하면 느려지는 이유는 대개 코드가 아니라 배포 구조입니다. 지연 시간은 "서버와 DB가 얼마나 멀리 있나"에 "페이지 하나에 몇 번 오가나"를 곱한 값이고, 여기에 원본 이미지를 서버가 직접 내려주는 무게가 더해집니다. Vercel은 새 프로젝트의 함수를 기본으로 미국 워싱턴 D.C.(iad1)에서 돌리는데, DB를 서울에 두면 쿼리 하나마다 태평양을 한 번씩 왕복합니다. 필자가 사무실 회선에서 재 보니 서울 리전까지는 18ms, 미국 동부까지는 196ms였습니다. 쿼리 21번을 차례로 보내는 N+1 코드라면 DB를 기다리는 시간만 4초가 넘습니다. 한 국내 개발자는 SQL은 한 줄도 고치지 않고 함수 리전만 서울로 바꿔 대시보드 응답을 2.5초에서 0.45초로 줄였습니다. 이 글은 그 세 가지 원인을 공식 문서, 측정값, 국내 경험담으로 확인하고 10분 안에 점검하는 순서를 정리합니다.

이 글은 2026년 10월 4일에 올라온 유튜브 영상 바이브코딩 하다 AWS 요금 폭탄 맞으신 분?(feat. 대안)(메이커 에반)을 계기로 썼습니다. 영상의 주장 35개 가운데 33개를 Vercel, Supabase, Cloudflare, Google 등의 공식 문서와 공개 측정 자료로 다시 확인했고(비용 추정 1개는 이전 글로 넘기고, 1개는 화자의 경험이라 확인할 수 없었습니다), 대부분 맞았지만 몇 가지는 바로잡았습니다. 영상이 다룬 AWS 비용 이야기는 서버를 이제 AWS에 올리지 않는 이유에 따로 정리했으므로, 이 글은 속도만 다룹니다. 필자는 『클로드 코드 제대로 시작하기』를 썼고, 어비스 홈페이지(aviss.kr)는 Cloudflare 뒤의 일반 웹 호스팅에서 운영합니다.

핵심 요약

  • 느린 이유는 거리와 왕복 횟수의 곱입니다. 필자 측정으로 국내 회선에서 서울 리전까지 18ms, 싱가포르 83ms, 미국 동부 196ms입니다. 서버가 DB에 질문할 때마다 이 거리를 한 번씩 오갑니다.
  • Vercel 함수의 기본 리전은 미국 동부(iad1)입니다. DB를 서울에 두고 기본값을 그대로 두면 쿼리마다 약 200ms가 붙습니다. Vercel 문서도 "함수는 데이터베이스와 같은 리전에서 실행해야 한다"고 씁니다. 서울은 icn1, 싱가포르는 sin1입니다. 최근 30일 GitHub에는 iad1과 supabase를 함께 언급하는 PR이 504건 올라왔고(3~4월 같은 기간 23건), 열어 본 표본은 거의 모두 리전을 DB 옆으로 옮기는 PR이었습니다.
  • N+1 쿼리는 그 지연을 곱합니다. 글 20개를 불러온 뒤 작성자를 하나씩 조회하면 쿼리가 21번 나갑니다. 미국 동부와 서울 사이라면 4.2초, 같은 리전이라면 0.2초 미만입니다. 조인이나 include 한 번이면 1~2번으로 줄어듭니다.
  • 이미지는 서버가 아니라 CDN이 내려줘야 합니다. 중앙값 모바일 홈페이지 2.6MB 중 이미지가 911KB로 가장 크고, 모바일 페이지의 76%는 가장 큰 화면 요소(LCP)가 이미지입니다. WebP는 같은 화질에서 JPEG보다 25~34% 작습니다.
  • 빨라도 위험한 구조가 따로 있습니다. UpGuard는 2026년 9월에 행 단위 보안(RLS)이 빠진 Supabase DB 16,326개를 찾았고, API로 만든 테이블은 RLS가 기본으로 꺼진다고 지적했습니다.

목차

로컬에서는 빨랐는데 왜 배포하면 느려지나요?

로컬에서는 앱 서버와 DB가 같은 컴퓨터 안에 있어서 둘 사이의 거리가 0에 가깝기 때문입니다. 배포하면 브라우저, 앱 서버(서버리스 함수), DB가 서로 다른 데이터센터에 흩어지고, 요청은 그 사이를 물리적으로 오가야 합니다. 이 왕복에 걸리는 시간을 RTT(round-trip time)라고 부릅니다. 『High Performance Browser Networking』의 저자 일리야 그리고릭은 이 점을 "대부분의 웹사이트에서 성능 병목은 대역폭이 아니라 지연 시간"이라고 정리했습니다(HPBN, Primer on Latency and Bandwidth). 회선을 아무리 빠르게 해도 빛이 태평양을 건너는 시간은 줄지 않습니다.

문제는 페이지 하나가 왕복 한 번으로 끝나지 않는다는 점입니다. 서버는 페이지를 그리면서 로그인 정보를 확인하고, 목록을 가져오고, 개수를 세고, 알림을 확인합니다. 질문 하나하나가 DB와의 왕복입니다. 그래서 배포 후의 응답 시간은 대략 다음처럼 쪼개 볼 수 있습니다.

  • 거리: 앱 서버와 DB 사이 RTT. 같은 리전이면 몇 ms, 서울과 미국 동부 사이라면 약 200ms입니다.
  • 왕복 횟수: 페이지 하나를 그리는 동안 차례로 보내는 쿼리 수. 반복문 안에서 DB를 부르면 수십 번이 됩니다.
  • 무게: 브라우저가 받아야 하는 파일 크기와 그 파일이 오는 거리. 가장 큰 몫은 이미지입니다(중앙값 모바일 홈페이지의 약 3분의 1).

Google의 web.dev 가이드도 같은 점을 짚습니다. 백엔드를 잘 최적화해도 "오리진 서버에서 멀리 있는 사용자는 실제 환경에서 여전히 높은 TTFB를 겪을 수 있다"는 것입니다(web.dev, Optimize TTFB, 2025-11-28). TTFB(첫 바이트까지 걸린 시간)는 0.8초 이하면 좋음, 1.8초를 넘으면 나쁨으로 봅니다(web.dev, TTFB). 바이브코딩 앱이 "코드를 봐도 문제가 없는데 느린" 이유는 대개 이 세 가지 중 하나이고, 셋 다 코드 리뷰로는 잘 보이지 않습니다.

서울에서 각 리전까지 얼마나 걸리나요?

거리를 감으로 말하지 않으려고 필자가 직접 재 봤습니다. 2026년 10월 7일 오전, 사무실 유선 회선에서 AWS 리전별 엔드포인트로 TCP 연결을 15번씩 맺고 걸린 시간의 중앙값을 냈습니다. TCP 연결 시간은 대략 왕복 한 번에 해당합니다.

국내 회선에서 AWS 리전까지 왕복 시간
필자 사무실 회선 한 곳에서 한 시각에 잰 값이라 회선과 시간대에 따라 달라집니다. 측정 명령은 이 글 아래 "이 글의 자료를 고른 방법"에 적었습니다.

다른 측정도 비슷합니다. Microsoft가 공개하는 Azure 백본 왕복 시간(30일 중앙값)에서 서울 리전 기준으로 도쿄 29ms, 싱가포르 68ms, 미국 동부 184ms이고, 같은 서울 리전 안에서는 8ms입니다(Azure network round-trip latency statistics, 2026-07-30). 공용 인터넷 ping을 매시간 재는 WonderNetwork는 서울에서 워싱턴까지 약 202ms를 기록했습니다(WonderNetwork, Seoul to Washington, 2026-10-06). 영상은 미국 동부까지 220ms 안팎이라고 했는데, 측정 출처별로 184~209ms이므로 약 200ms로 기억하면 충분합니다.

이 숫자를 한 번만 내면 사용자는 거의 느끼지 못합니다. 0.2초짜리 지연이 문제가 되는 것은 서버가 페이지 하나를 그리면서 이 거리를 여러 번 오갈 때입니다. 그 경우가 아래 첫 번째와 두 번째 이유입니다.

이유 1. 함수와 DB가 다른 대륙에 있다

가장 흔한 원인은 "DB는 서울, 서버 함수는 미국"인 조합입니다. Vercel 공식 문서는 "새 프로젝트의 Vercel Functions는 기본으로 미국 워싱턴 D.C.(iad1)에서 실행된다"고 밝히고, 그 이유를 "대부분의 외부 데이터 소스가 미국 동부에 있기 때문"이라고 설명합니다(Vercel Docs, Configuring regions, 2026-08-11). 미국 개발자에게는 합리적인 기본값이지만, 한국 사용자를 위해 Supabase 서울 리전에 DB를 만든 사람에게는 정반대입니다. Next.js에서 서버 렌더링(SSR)하는 페이지, 서버 액션, API 라우트가 모두 이 함수에서 돌기 때문입니다.

그래서 요청 하나가 이렇게 흐릅니다. 서울의 사용자가 페이지를 열면 가까운 Vercel 엣지(서울)가 요청을 받고, 그 요청을 워싱턴의 함수로 넘깁니다. 함수는 쿼리를 보낼 때마다 서울의 DB까지 약 200ms를 왕복하고, 다 그린 HTML을 다시 서울로 보냅니다. Vercel 문서의 표현대로 "함수가 데이터 소스에서 멀면 모든 데이터베이스 쿼리가 지연을 더합니다"(Vercel Docs, Debugging slow functions, 2026-06-25).

Vercel 프로젝트 설정의 Function Regions 화면. 대륙별 아코디언 아래 북미의 Washington, D.C. iad1과 San Francisco sfo1이 체크돼 있고, 아시아 태평양 항목은 접혀 있다.
Vercel 프로젝트의 Settings > Functions > Function Regions 화면입니다. 서울(icn1)은 Asia Pacific 항목 안에 있고, 바꾼 뒤 새로 배포해야 적용됩니다. 화면에는 "Pro 플랜은 최대 3개"라고 적혀 있지만 같은 문서의 한도 표(2026-08-11)는 Pro 5개, Hobby 1개입니다. 출처: Vercel Docs

국내 사례가 이 구조를 그대로 보여 줍니다. 한 개발자는 대시보드의 SQL 7개가 이미 Promise.all로 병렬 처리되고 각각 11~15ms에 끝나는데도 브라우저의 TTFB가 약 2.5초인 것을 보고 원인을 찾다가, 함수가 iad1에 있고 Supabase가 서울에 있다는 것을 발견했습니다. vercel.json에 리전 한 줄을 넣자 대시보드는 약 2,500ms에서 약 450ms로, OAuth 콜백은 2,600ms에서 1,145ms로 줄었고, 함수와 DB 사이 왕복은 5~6ms가 됐습니다. 그는 SQL을 한 줄도 고치지 않았다는 점을 강조했습니다(velog 최병현, 2026-08-31). 학급 웹앱을 직접 만든 한 유치원 교사는 기록을 한 번 저장할 때 Supabase와 4~5번 통신하는 구조였는데, 외부 API 캐시와 화면 구조 변경으로 사흘을 보낸 뒤에야 같은 원인을 찾았습니다(네이버 블로그 pengmomt, 2026-08-27).

이 문제는 한국만의 일이 아닙니다. 필자가 GitHub 검색 API로 본문에 iad1과 supabase가 함께 들어간 PR을 세어 보니 2026년 9월 7일부터 10월 7일까지 한 달 동안 504건이었고, 같은 조건으로 6월 7일부터 7월 7일까지는 93건, 3월 7일부터 4월 7일까지는 23건이었습니다. 504건 중 373건은 본문에 "Claude Code"가 들어 있었습니다. 검색어가 일치한 건수라 무관한 PR이 일부 섞일 수 있고, 같은 기간 Claude Code로 만든 PR 자체가 늘어난 효과도 따로 걸러 내지 않았습니다. 그래도 표본으로 열어 본 PR이 거의 모두 함수 리전을 DB 옆으로 옮기는 내용이었다는 점에서, 같은 기본값에 걸린 앱을 에이전트로 고치는 일이 크게 늘었다고 볼 수 있습니다.

상품과 계산대 화면이 있는 브라질의 한 앱은 Supabase가 상파울루에, 함수가 iad1에 있었는데, Claude Code가 만든 리전 변경 PR 하나로 상품 페이지 TTFB가 1.56초에서 0.26초로, 로그인부터 대시보드까지가 7.9초에서 2.5초로 줄었습니다. PR 본문에는 "코드는 한 줄도 바뀌지 않는다"고 적혀 있습니다(gaveta PR #48, 2026-09-16).

거리가 미국 동부와 서부 정도여도 마찬가지입니다. 함수는 iad1, Supabase는 미국 서부(us-west-2)에 둔 한 여행 앱 개발자는 Supabase 요청 로그 하루치를 분석해, 요청 시간의 중앙값은 122ms인데 Postgres 실행 시간은 평균 1.51ms였다며 "Supabase 요청마다 약 120ms는 DB가 아니라 미국을 가로지르는 값"이라고 정리했습니다(buddytrip #933, 2026-09 측정 댓글).

고치는 방법은 한 줄입니다. 대시보드에서 바꿔도 되고, 저장소에 남기려면 vercel.json에 적습니다.

{
  "regions": ["icn1"]
}

바꿨는지는 응답 헤더로 확인합니다. Vercel 문서에 따르면 x-vercel-id 헤더에는 요청이 거친 리전과 함수가 실행된 리전이 담깁니다(Vercel Docs, Response headers, 2026-08-11). 국내 블로그들이 진단에 쓴 방법도 이것입니다.

curl -sI https://내-도메인/대시보드 | grep -i x-vercel-id
# x-vercel-id: icn1::iad1::...   서울 엣지가 받아서 미국 동부 함수가 처리
# x-vercel-id: icn1::icn1::...   서울에서 받아 서울에서 처리

Next.js 라우트 파일에 preferredRegion을 적는 방법을 소개하는 글도 많은데, 2026년 Next.js 문서는 이 설정을 더 이상 쓰지 말고 지우라고 안내합니다(Next.js Docs, preferredRegion, 2026-04-30). 리전은 Vercel 설정이나 vercel.json으로 정하는 것이 맞습니다.

한 가지 알아 둘 점은 리전에 따라 단가가 다르다는 것입니다. Vercel 공식 가격표에서 활성 CPU는 iad1이 시간당 0.128달러, icn1이 0.169달러이고, 메모리는 GB-시간당 0.0106달러와 0.0140달러입니다(Vercel Docs, Fluid compute pricing, 2026-06-16). 다만 같은 문서는 함수가 I/O를 기다리는 동안 CPU 과금은 멈추지만 메모리 과금은 계속된다고 설명합니다. 함수가 태평양 건너 DB를 기다리는 시간이 길수록 메모리 요금도 그만큼 쌓이므로, 작은 앱에서 리전 단가 차이를 이유로 먼 리전에 남을 이유는 크지 않다는 것이 필자의 판단입니다.

이유 2. 페이지 하나에 DB를 몇 번 부르나: N+1

리전을 맞춰도 느리다면 다음으로 볼 것은 왕복 횟수입니다. 바이브코딩 앱에서 가장 흔한 패턴은 N+1 쿼리입니다. Prisma 문서는 이를 "쿼리 결과를 반복문으로 돌면서 결과마다 쿼리를 하나씩 더 보내는 문제"라고 정의합니다(Prisma Docs, Query optimization). 글 목록 20개를 한 번에 가져온 뒤, 화면에 작성자 이름을 띄우려고 글마다 작성자를 따로 조회하면 쿼리는 1번이 아니라 21번 나갑니다. Rails 가이드의 예시도 같습니다. 책 10권을 찾는 쿼리 1번에 저자를 불러오는 쿼리 10번, "모두 11번"입니다(Rails Guides, Active Record Query Interface).

이 패턴은 로컬에서는 거의 티가 나지 않습니다. 왕복이 0.1ms라면 21번을 해도 2ms이기 때문입니다. 배포한 뒤에는 앞 절의 거리가 쿼리 횟수만큼 곱해집니다.

쿼리 횟수와 거리에 따른 DB 대기 시간 계산
계산값입니다. 같은 리전 8ms는 Microsoft가 공개한 Azure 서울 리전 내부 왕복(30일 중앙값), 싱가포르와 미국 동부는 앞 절의 측정값을 반올림했습니다. 새 연결을 맺는 비용(TCP, TLS 핸드셰이크)은 넣지 않았으므로 실제로는 더 걸릴 수 있습니다.

국내 사례에서는 분 단위까지 갔습니다. 한 개발자는 Claude가 짜 준 출석 동기화 코드가 900여 건을 건마다 SELECT한 뒤 INSERT하거나 UPDATE하는 바람에 DB 왕복이 1,800회를 넘었고, 한 번 돌리는 데 약 270초가 걸렸다고 기록했습니다. 서버는 Railway 싱가포르, DB는 Supabase 도쿄로 같은 아시아 안이었는데도 그랬습니다. 한 번에 넣고 겹치면 고치는 Bulk UPSERT로 바꾸자 약 1.5초가 됐습니다(네이버 블로그 oooolivelove, 2026-08-09).

해외 바이브코딩 커뮤니티에도 같은 사례가 올라옵니다. Lovable과 Supabase로 앱을 만든 한 사용자는 브라우저 Network 탭에서 작성자를 하나씩 부르는 요청 21개를 발견하고, Supabase의 관계 임베딩으로 한 요청에 묶어 페이지 로드를 3.16초에서 1.71초로 줄였습니다(r/lovable, 2026-09-23). 영상 화자도 "AI가 짜 준 코드에는 이런 패턴이 생각보다 자주 나온다"고 말합니다. 다만 AI가 쓴 코드에 N+1이 더 많다는 정량 연구는 이번 조사에서 찾지 못했으므로, 이 부분은 경험담으로 읽어야 합니다.

고치는 방법은 ORM마다 이름만 다를 뿐 같습니다. 관계를 한 번에 가져오거나, ID를 모아 한 번에 조회합니다. Prisma라면 이렇습니다.

// 느린 코드: 글마다 작성자를 따로 조회해 쿼리 21번
const posts = await prisma.post.findMany({ take: 20 });
for (const post of posts) {
  post.author = await prisma.user.findUnique({ where: { id: post.authorId } });
}

// 고친 코드: 관계를 함께 가져와 쿼리 1~2번
const postsWithAuthor = await prisma.post.findMany({
  take: 20,
  include: { author: true },
});

Prisma 문서는 include를 쓰면 "SQL 쿼리 두 개만 실행된다"고 설명하고, relationLoadStrategy: "join"으로 한 번에 조인하는 방법도 제공합니다. Supabase 클라이언트를 바로 쓰는 앱이라면 select('id, title, author:profiles(name)')처럼 관계 테이블을 한 요청에 묶어서 가져오고, Django는 select_related, Rails는 includes를 씁니다. 서로 의존하지 않는 쿼리는 하나씩 기다리지 말고 Promise.all로 동시에 보내라는 것이 Vercel 문서의 권고입니다(Vercel Docs, Debugging slow functions). 영상의 "쿼리 5번이면 1초가 넘는다"는 이렇게 쿼리를 차례로 보낼 때의 이야기입니다.

쿼리 수가 같아도 연결 설정 때문에 왕복이 늘어나기도 합니다. 호텔 소프트웨어를 만드는 한 팀은 몇 주씩 떠 있는 Node 서버에서 Prisma를 Supabase 트랜잭션 풀러(6543 포트)에 pgbouncer=true로 붙여 두었는데, 쿼리 하나마다 BEGIN, DEALLOCATE ALL, SELECT, COMMIT 네 번이 오가고 있었습니다. 세션 모드(5432 포트)로 한 줄을 바꾸자 예약 페이지 뒤의 가용성 조회가 8.06초에서 1.88초가 됐습니다. 이 팀은 "DB는 아무것도 하지 않고 있었고, 시간은 전부 회선에서 쓰였다"고 썼고, 출시 나흘 뒤 이 비용을 재고 계산까지 맞게 해 놓고는 원인을 지리 탓으로 돌렸다고 털어놓았습니다(simbastack 포스트모템, 2026-09-15). 원문도 밝히듯 트랜잭션 모드 설정은 요청마다 연결을 여는 서버리스 함수용이라, 서버 형태에 맞는 연결 방식을 쓰고 있는지 ORM 쿼리 로그로 한 번 확인해 볼 만합니다.

AI에게 맡길 때는 "N+1 쿼리를 찾아 고쳐 줘"보다 범위를 좁혀 주는 편이 정확합니다. 예를 들면 이렇게 씁니다. "이 페이지를 한 번 렌더할 때 DB에 보내는 쿼리를 모두 나열하고, 반복문 안에서 await로 DB를 부르는 곳을 찾아 관계 조회나 IN 조회로 바꿔 줘. 바꾸기 전후 쿼리 수를 적어 줘." 쿼리 수를 세게 하면 고친 결과를 숫자로 확인할 수 있습니다.

이유 3. 이미지를 서버가 원본 그대로 내려준다

리전과 쿼리가 서버 쪽 지연이라면, 이미지는 브라우저 쪽 무게입니다. HTTP Archive가 2025년 7월에 수집한 데이터로 낸 Web Almanac 2025에서 중앙값 모바일 홈페이지는 2.6MB였고, 그중 이미지가 911KB로 가장 큰 단일 항목이었습니다. 자바스크립트가 632KB로 바로 뒤를 이었습니다(Web Almanac 2025, Page Weight, 2026-01-15). 영상은 "웹페이지 용량 대부분이 이미지와 동영상"이라고 했지만, 중앙값으로 보면 이미지는 약 3분의 1이고 대부분의 페이지에는 동영상이 없습니다. 그래도 가장 크고, 가장 줄이기 쉬운 항목인 것은 맞습니다.

Web Almanac 2025의 중앙값 모바일 페이지 무게를 자원 유형별로 나눈 막대그래프. 홈페이지 기준 HTML 22KB, CSS 77KB, 폰트 122KB, 자바스크립트 632KB, 이미지 911KB이고 내부 페이지의 이미지는 354KB다.
중앙값 모바일 페이지의 자원 유형별 무게(KB)입니다. 연한 막대가 홈페이지, 진한 막대가 내부 페이지이고, 홈페이지에서는 이미지(911KB)가 가장 무겁습니다. 출처: HTTP Archive Web Almanac 2025, Page Weight

무게보다 중요한 것은 사용자가 체감하는 첫 화면입니다. 화면에서 가장 큰 요소가 그려지는 시간(LCP)의 대상이 이미지인 페이지는 데스크톱 85.3%, 모바일 76.0%입니다(Web Almanac 2025, Performance). 상단의 큰 사진 한 장이 늦게 오면 나머지가 아무리 빨라도 "느린 사이트"가 됩니다. Google은 LCP를 2.5초 이하로 두라고 권합니다(web.dev, Optimize LCP).

Web Almanac 2025의 LCP 요소 유형 막대그래프. 이미지가 데스크톱 85.3퍼센트, 모바일 76.0퍼센트이고 텍스트는 데스크톱 14.4퍼센트, 모바일 23.7퍼센트, 인라인 이미지는 1퍼센트 미만이다.
LCP 요소가 무엇인지 유형별로 나눈 비율입니다. 데스크톱 페이지의 85.3%, 모바일 페이지의 76.0%에서 가장 큰 화면 요소가 이미지입니다. 출처: HTTP Archive Web Almanac 2025, Performance

해결은 세 단계입니다.

  1. 파일은 서버 디스크가 아니라 오브젝트 스토리지에 둡니다. PaaS는 배포할 때마다 서버를 새로 띄우는 경우가 많아 디스크에 올린 파일이 사라질 수 있고, 비싼 서버 시간을 파일 전송에 쓰게 됩니다. S3 서울은 GB당 월 0.025달러입니다(AWS S3 가격표). CloudFront를 붙이면 종량제 요금 기준으로 매달 1TB까지 전송이 무료입니다(CloudFront 가격). 별도의 정액제 요금제는 무료 범위가 따로 정해져 있으니 고를 때 CloudFront 요금 페이지에서 확인하세요. Cloudflare R2는 GB당 월 0.015달러이고 밖으로 나가는 전송 요금이 없습니다(Cloudflare R2 pricing, 2026-10-01).
  2. 사용자에게는 CDN 주소로 내려줍니다. CDN은 파일 복사본을 세계 여러 곳에 캐시해 두고, 사용자에게 가장 가까운 곳에서 보냅니다. 원본 서버가 싱가포르든 미국이든 한국 사용자는 가까운 복사본을 받습니다.
  3. 크기와 형식을 맞춰서 보냅니다. Google에 따르면 WebP 무손실 이미지는 PNG보다 26% 작고, 손실 이미지는 같은 화질(SSIM)에서 JPEG보다 25~34% 작으며, 투명 배경이 있는 손실 WebP는 PNG보다 보통 3배 작습니다(Google for Developers, WebP, 2025-08-07). 목록 카드에는 썸네일 크기로 줄여 보냅니다.
Vercel CDN 세계 지도. 진한 점으로 표시된 접속 거점(PoP)이 북미, 유럽, 아시아, 남미, 아프리카, 오세아니아에 퍼져 있고, 연한 원으로 표시된 함수 실행 리전 19곳이 그보다 적게 있다.
Vercel CDN의 접속 거점(진한 점, 126곳)과 함수가 실행되는 리전(연한 원, 19곳)입니다. 정적 파일과 캐시된 응답은 가까운 거점에서 나가지만, 서버 함수는 지정한 리전 한 곳에서만 돕니다. 이미지 상단 문구의 119곳은 범례와 다르며, Vercel 리전 문서 본문은 126곳으로 적습니다. 출처: Vercel Docs, Regions

Next.js를 Vercel에 배포했다면 <img> 대신 next/image를 쓰는 것만으로 2번과 3번이 대부분 해결됩니다. Next.js 문서는 이 컴포넌트가 기기별로 알맞은 크기를 WebP 같은 최신 형식으로 자동 제공하고, 원격 서버의 이미지도 요청할 때 줄여 준다고 설명합니다(Next.js Docs, Images, 2026-04-01). 첫 화면 이미지에 쓰던 priority 속성은 Next.js 16부터 지원 중단(deprecated)되어, 문서는 대부분의 경우 loading="eager"나 fetchPriority="high"를 쓰라고 안내합니다(Next.js Docs, Image Component, 2026-09-28). Cloudflare를 쓴다면 이미지 주소 앞에 /cdn-cgi/image/width=400,format=auto/처럼 옵션을 붙여 원본 하나에서 크기별 사본을 받을 수 있습니다(Cloudflare Images, Transform via URL).

import Image from "next/image";

// 목록 카드: 원본이 몇 MB여도 화면 폭에 맞는 WebP를 받는다
// (외부 주소의 이미지는 next.config의 images.remotePatterns에 등록해야 한다)
<Image src={post.coverUrl} alt={post.title} width={400} height={250} />

// 첫 화면의 큰 이미지: 먼저 받아 오도록 우선순위를 올린다
// (Next.js 16부터 priority 대신 preload 또는 fetchPriority="high")
<Image src={hero} alt="" width={1200} height={630} fetchPriority="high" />

용량만 줄인다고 끝나지는 않습니다. 한 개발자는 JPEG를 WebP로 바꿔 이미지 787kB를 84.4kB로 줄이고 LCP를 6.1초에서 5.1초로 당겼지만(velog yejiin_21, 2025-07-01), 다른 개발자는 900KB를 300KB로 줄였는데도 점수가 그대로였습니다. 이미지가 API 응답이 온 뒤에야 화면에 등장해서 요청 자체가 늦었기 때문이고, preload와 fetchpriority="high"로 요청 시점을 당겨서 해결했습니다(velog jjangminii, 2026-04-07). 첫 화면의 큰 이미지는 "작게"와 "빨리"를 둘 다 챙겨야 합니다.

한국 사용자 위주의 서비스라면 확인할 것이 하나 더 있습니다. 국내 개발자들은 Cloudflare 무료 CDN이 한국 요청을 홍콩 같은 해외 거점으로 보낸다고 보고한 적이 있습니다. 2023년의 한 측정에서는 113KB 이미지가 S3와 CloudFront로는 0.005초, R2와 Cloudflare CDN으로는 홍콩을 거쳐 0.3초 걸렸습니다(marinesnow34, 2023-09-23). 2023년 측정이라 지금 경로는 다를 수 있으므로, 응답 헤더 cf-ray 값 끝에 붙은 세 글자 공항 코드(ICN이면 서울)로 직접 확인해 보는 것이 안전합니다. Cloudflare 문서에 따르면 이 코드는 응답한 데이터센터의 위치입니다(Cloudflare Docs, HTTP headers).

빨라도 위험한 구조: Supabase RLS

속도 이야기에서 조금 벗어나지만, 영상이 짚은 것 중 바이브코딩 앱에 가장 큰 사고를 내는 것은 보안이라 함께 다룹니다. Supabase는 브라우저가 DB에 바로 요청을 보내는 구조라 중간에 직접 만든 서버가 없습니다. 그래서 누가 어떤 행을 읽고 쓸 수 있는지를 DB 안의 규칙, 즉 RLS(Row Level Security, 행 단위 보안)로 걸어야 합니다. Supabase 문서는 이렇게 씁니다. "노출된 스키마에서 RLS가 없는 테이블은 권한(grant)이 있는 모든 역할이 읽고 쓸 수 있습니다"(Supabase Docs, Row Level Security). 규칙 하나를 빠뜨리면 브라우저에 그대로 노출되는 공개 키만으로 남의 데이터를 꺼낼 수 있다는 뜻입니다.

실제로 그런 일이 대규모로 일어났습니다. 보안 회사 UpGuard는 Supabase를 쓰는 흔적이 있는 도메인 약 30만 개를 조사해 읽을 수 있는 테이블이 노출된 DB 16,326개를 찾았고, 노출된 테이블의 55.8%에 개인정보 필드가 있었다고 발표했습니다. UpGuard에 따르면 Supabase는 테이블 편집기 화면으로 만든 테이블에는 RLS를 기본으로 켜도록 바꿨지만, "코딩 에이전트가 Supabase와 상호작용하는 방식인 API로 만든 테이블은 RLS가 기본으로 켜지지 않습니다"(UpGuard, Systemic Data Exposure in Supabase Apps, 2026-09-24 게시, 10-01 수정). 2025년에는 Lovable로 만든 사이트들의 RLS 미흡이 CVE-2025-48757(CVSS 9.3)로 등록됐는데, 이 CVE는 Lovable이 이의를 제기한 상태입니다(NVD, CVE-2025-48757).

UpGuard의 노출된 데이터 유형 막대그래프. 노출된 테이블 가운데 개인정보 필드가 있는 비율 55.8퍼센트, 이름 42.8퍼센트, 이메일 38.8퍼센트, 전화번호 25.8퍼센트, 주소 16.0퍼센트, 결제 시스템 12.1퍼센트, 자격 증명 9.7퍼센트, 비밀번호나 PIN 8.2퍼센트, 생년월일 3.6퍼센트.
UpGuard가 찾은 노출 테이블에서 각 필드가 스키마에 있는 비율입니다. 개인정보 필드가 있는 테이블이 55.8%이고, 이미지 하단에 적힌 UpGuard의 설명대로, 결제 항목(12.1%)은 카드 번호가 아니라 Stripe 같은 결제 연동 ID가 대부분입니다. 출처: UpGuard, 2026-09-24

국내도 남의 일이 아닙니다. 국민일보는 위협분석그룹 다크넷즈의 조사를 인용해 바이브코딩 특징을 가진 사이트 중 개인정보 노출 가능성이 있는 한국 사이트가 147개였고, 한 사이트에서는 개발자 도구만 열어도 회원 930명의 정보가 보였다고 보도했습니다(국민일보, 2026-10-02). 기사에는 어떤 DB 서비스였는지 나오지 않으므로 Supabase 사고라고 단정할 수는 없지만, "화면은 잘 뜨는데 데이터는 다 열려 있는" 구조가 국내에도 있다는 점은 분명합니다.

Supabase도 기본값을 바꾸고 있습니다. 2026년 4월 공지에 따르면 5월 30일부터 새 프로젝트에서, 10월 30일부터는 모든 기존 프로젝트에서 public 스키마에 새로 만든 테이블이 Data API(supabase-js, PostgREST, GraphQL)에 자동으로 노출되지 않습니다. 기존 테이블의 권한은 그대로이고, 새 테이블을 브라우저에서 쓰려면 grant와 RLS 정책을 명시해야 합니다(Supabase Changelog, 2026-04-28). 보안에는 좋은 변화지만, 10월 말 이후 AI가 만든 새 테이블이 "느린" 게 아니라 "빈 결과"를 돌려주는 혼란이 생길 수 있으니 알아 두는 편이 좋습니다.

RLS가 "켜져 있다"는 표시만 믿어서도 안 됩니다. 레딧 r/Supabase의 한 사용자는 에이전트가 한 세션에서 테이블 세 개를 만들었고 대시보드에는 셋 다 RLS가 켜져 있었지만, 익명 키로 직접 요청해 보니 하나는 남아 있던 USING (true) 정책 때문에 모든 행을 돌려줬다고 썼습니다(r/Supabase, 2026-10-01).

영상의 결론은 "RLS를 직접 설계하고 검증할 수 있을 때만 Supabase를 쓰고, 처음이라면 서버를 하나 두고 DB는 그 서버만 접근하게 하라"였습니다. 필자는 Supabase를 쓰더라도 최소한 아래 두 가지는 배포 전에 확인하라고 권합니다.

  • 대시보드의 Security Advisor에서 rls_disabled_in_public 경고가 0개인지 확인합니다. Supabase는 이 경고를 오류(ERROR) 등급으로 분류합니다(Supabase Docs, Advisors).
  • 에이전트가 만든 마이그레이션에 테이블마다 alter table ... enable row level security;와 정책이 함께 있는지 보고, 정책이 using (true)처럼 모두에게 열려 있지 않은지 확인합니다. 가장 확실한 방법은 익명 키로 직접 요청해 남의 행이 보이는지 보는 것입니다. 코드 리뷰를 맡길 때도 "테이블을 만든 모든 마이그레이션에 RLS 활성화와 정책이 있는지 표로 정리해 줘"처럼 확인할 항목을 정해 줍니다.

DB가 어디 있느냐에 따라 함수를 어디에 두나

원칙은 하나입니다. 서버 함수, 백엔드, DB는 같은 리전에 둡니다. 문제는 서비스마다 고를 수 있는 리전이 다르다는 점입니다. Supabase에는 서울(ap-northeast-2)이 있지만, Neon의 AWS 리전에는 서울과 도쿄가 없고 아시아는 싱가포르와 시드니뿐이며 만든 뒤에는 리전을 바꿀 수 없습니다(Neon Docs, Regions). Railway와 Render는 아시아 리전이 싱가포르 한 곳이고, Fly.io는 도쿄(nrt)와 싱가포르가 있지만 서울은 없습니다(Railway Docs, Render Docs, Fly.io Docs).

DB가 있는 곳 Vercel 함수 리전 메모
Supabase 서울 icn1 가장 흔한 조합. 기본값 iad1이면 쿼리마다 약 200ms
Supabase 도쿄 hnd1 Vercel 도쿄는 CPU 단가가 아시아에서 높은 편(시간당 0.202달러)
Neon (아시아는 싱가포르) sin1 Neon은 생성 후 리전 변경 불가. 처음 만들 때 고른다
Railway Postgres (싱가포르) sin1 백엔드 서버도 Railway 싱가포르에 둔다
Fly.io Postgres 도쿄 hnd1 앱 서버도 nrt에 둔다
Cloudflare D1 (Workers는 엣지에서 실행) DB 본체는 만든 곳 근처 한 위치에 있다. DB 하나당 10GB, 늘릴 수 없다

싱가포르는 서울에서 왕복 70~85ms라 멀어 보이지만, 함수와 DB가 같은 싱가포르에 있으면 쿼리를 열 번 해도 그 사이는 몇 ms씩입니다. 사용자는 페이지 요청 한 번에 싱가포르 왕복 한 번만 더 기다리면 됩니다. 영상 화자가 "Railway 싱가포르를 쓰는데 거의 못 느낀다"고 한 조건이 바로 이것이었고, 한 국내 개발자는 같은 계산 끝에 서울 리전이 없는 Railway를 빼고 Cloud Run 서울과 Cloud SQL 서울 조합을 골랐습니다(velog kswift, 2026-04-03). 어느 쪽이든 "사용자에서 서버까지 한 번, 서버에서 DB까지 여러 번"이라는 구조를 기억하면 무엇을 붙여 두어야 하는지 보입니다.

Cloudflare Workers는 코드가 사용자 가까운 엣지에서 돌지만, D1 데이터베이스의 본체는 만들 때 요청한 곳 근처의 한 위치에 생깁니다(Cloudflare D1 data location, 2026-10-02). 엣지의 Worker가 먼 D1에 쿼리를 여러 번 보내면 이 글이 말하는 "거리 x 왕복 횟수" 문제가 그대로 생기므로, 읽기 복제본(read replication)을 켜거나 쿼리 수를 줄여야 합니다. 또 D1은 SQLite 기반이고 DB 하나당 10GB이며 Cloudflare 문서는 이 한도를 "더 늘릴 수 없다"고 못 박습니다(Cloudflare D1 limits, 2026-04-21). 영상이 확장성을 이유로 Postgres를 권한 것도 이 때문입니다. Workers에서 Postgres를 쓰려면 Hyperdrive로 외부 DB를 붙일 수 있습니다(Cloudflare Hyperdrive).

10분 점검 순서

배포한 앱이 느리다면 코드를 고치기 전에 이 순서로 봅니다. 앞의 것일수록 고치는 비용은 작고 효과는 큽니다.

  1. 함수 리전과 DB 리전을 적어 봅니다. Vercel은 Settings > Functions, Supabase는 프로젝트 설정의 Region에서 봅니다. 다르면 같은 곳으로 맞추고 새로 배포합니다.
  2. 응답 헤더로 확인합니다. 느린 페이지에 curl -sI를 보내 x-vercel-id의 두 번째 리전이 DB와 같은지 봅니다.
  3. 페이지 하나가 보내는 쿼리 수를 셉니다. ORM 쿼리 로그를 켜거나 Vercel 런타임 로그를 보고, 반복문 안의 await 쿼리를 찾습니다. 서로 의존하지 않는 쿼리는 Promise.all로 묶습니다.
  4. 첫 화면의 가장 큰 이미지를 봅니다. 브라우저 개발자 도구의 Network 탭에서 이미지 크기와 응답 서버를 확인하고, 원본을 서버가 직접 내려주고 있다면 스토리지와 CDN으로 옮기고 next/image 같은 변환을 붙입니다.
  5. RLS 경고를 확인합니다. Supabase를 쓴다면 Security Advisor에서 RLS 관련 경고가 0개인지 봅니다. 속도와는 상관없지만 가장 비싼 사고를 막습니다.

인덱스, 캐시, 커넥션 풀 튜닝은 그다음입니다. 영상 화자의 표현대로 "N+1만 신경 써서 리팩터링해 두면 웬만해서는 쿼리가 무거워질 일이 없고, 인덱스나 캐시 같은 튜닝은 그다음에 생각해도 늦지 않습니다."

이 방법이 안 맞는 경우

이 글의 처방은 사용자가 한국에 몰려 있고, 서비스를 막 시작한 단계(영상 화자의 기준으로 사용자 10명에서 10만 명 사이)를 기준으로 합니다. 다음 경우에는 판단이 달라집니다.

  • 리전 한 줄로 모든 지연이 사라지지는 않습니다. 프랑크푸르트로 함수 리전을 옮긴 한 학습 앱은 설정 화면이 1,754ms에서 519ms로 줄었지만, 대시보드는 3,840ms에서 3,521ms로 거의 그대로였습니다. 개발자는 "대시보드는 느린 RPC에 묶여 있다"고 적었습니다(lmsplus_v2 #1409, 2026-10-01). DB 함수나 쿼리 자체가 느리면 그때는 인덱스와 쿼리를 봐야 합니다.
  • 사용자가 여러 대륙에 퍼져 있다면 함수 리전 하나로는 모두를 가깝게 할 수 없습니다. 이때는 DB 읽기 복제본을 여러 리전에 두거나, 페이지를 정적으로 만들어 CDN에서 내려주는 쪽이 낫습니다.
  • 애초에 서버가 그릴 필요가 없는 페이지라면 리전을 옮기기보다 정적으로 돌려놓는 편이 빠릅니다. 한 국내 개발자는 리전을 옮기는 대신 루트 레이아웃에서 요청 헤더를 읽던 한 줄을 고쳐 블로그를 정적 페이지로 되돌렸고, 첫 바이트가 2.2초에서 0.13초로 줄었습니다(dev.ootssu.com, 2026-08). 정적 페이지는 CDN 거점에서 바로 나가므로 함수 리전 문제가 생기지 않습니다. 이런 일이 왜 생기는지는 Next.js 링크가 느린 이유: cookies() 하나가 prefetch를 바꾼다에서 자세히 다뤘습니다.
  • 외부 API 호출이 대부분이라면 함수는 DB가 아니라 그 API 가까이에 두는 편이 낫습니다. Vercel 문서도 "외부 서비스와 통신한다면 그 서비스에 가까운 리전만 고르라"고 씁니다(Vercel Docs, Configuring regions).
  • 트래픽이 아주 커지면 PaaS보다 AWS 같은 클라우드가 더 싸고 유연해질 수 있습니다. 그 시점의 비용 계산은 서버를 이제 AWS에 올리지 않는 이유에 정리했습니다.
  • Railway, Render처럼 아시아 리전이 싱가포르 한 곳뿐인 서비스는 그 리전에 장애가 나면 가까운 대안이 없습니다. 영상 화자도 이 점을 단점으로 들었습니다.

이 글의 자료를 고른 방법

이 글은 2026년 10월 7일에 확인한 자료로 썼습니다. Vercel, Supabase, Neon, Railway, Render, Fly.io, Cloudflare, AWS의 리전과 가격은 각 공식 문서와 가격표를 직접 열어 확인했고, 문서에 표시된 수정일을 함께 적었습니다. 웹 페이지 무게와 LCP 통계는 HTTP Archive의 Web Almanac 2025, WebP 압축률은 Google 개발자 문서, Supabase 노출 규모는 UpGuard 연구 원문을 썼습니다. 영상의 주장 35개 중 33개를 공식 자료와 대조해 대부분 맞는 것을 확인했습니다. 미국 동부 왕복 시간(220ms)과 페이지 무게에서 이미지가 차지하는 비중은 측정값과 공식 통계로 바로잡았고, "쿼리 5번이면 1초"에는 쿼리를 차례로 보낼 때라는 단서를 달았습니다. R2 저장 단가는 자동 자막이 깨져 영상이 말한 값을 확인할 수 없어 공식 값(0.015달러)만 적었습니다. AWS 월 비용 추정은 이전 글에서 다뤘으므로 다시 계산하지 않았고, "pgvector로 월 사용자 10만 명까지 충분하다"는 화자의 경험이라 확인할 수 없었습니다.

지연 시간은 세 가지로 교차 확인했습니다. Microsoft가 공개하는 Azure 백본 30일 중앙값, WonderNetwork의 공용 인터넷 ping, 그리고 필자의 직접 측정입니다. 필자 측정은 2026년 10월 7일 08시 49분, 사무실 유선 회선에서 리전마다 15번씩 DNS 조회를 포함한 TCP 연결 시간을 잰 중앙값이며, 명령은 다음과 같습니다.

for r in ap-northeast-2 ap-northeast-1 ap-southeast-1 us-west-2 us-east-1; do
  for i in $(seq 1 15); do
    curl -so /dev/null -w '%{time_connect}\n' --max-time 5 https://dynamodb.$r.amazonaws.com/
  done | sort -n | awk -v r=$r '{a[NR]=$1} END{printf "%s median=%.1fms\n", r, a[int((NR+1)/2)]*1000}'
done

최근 30일(2026년 9월 7일~10월 7일) 커뮤니티 반응은 GitHub 이슈와 PR, 레딧, 해커뉴스, dev.to를 봤고, X, Medium, 유튜브에서는 이 기간의 관련 글이 거의 없었습니다. GitHub PR 수는 iad1 supabase is:pr created:<기간> 검색 API의 집계값이며, 표본 40건을 열어 대부분이 리전 고정 PR인 것을 확인했습니다. 인용한 해외 사례의 전후 수치는 PR 본문과 댓글 원문에서 확인했습니다.

국내 블로그(velog, 네이버 블로그, 개인 블로그)의 수치는 개인 경험으로만 소개했고, 사실 주장의 근거로는 쓰지 않았습니다. 리전, Supabase 서울 리전 코드, 가격처럼 블로그마다 다르게 적힌 내용은 공식 문서를 따랐습니다. 국민일보 기사는 저장소 제품명이 나오지 않아 Supabase 사례로 분류하지 않았습니다. "AI가 짠 코드에 N+1이 많다"는 주장을 뒷받침하는 정량 연구는 찾지 못해 경험담으로만 다뤘습니다.

본문의 이미지는 직접 만든 차트 2개와 섬네일을 빼면 Vercel 공식 문서, HTTP Archive Web Almanac, UpGuard 연구의 원본 이미지이고, 해당 자료를 소개하려고 인용하면서 캡션에 출처를 달았습니다.

필자의 한계. 필자의 측정은 회선 한 곳, 한 시각의 값입니다. 쿼리 횟수 차트는 측정값을 곱한 계산이며, 실제 앱에서는 연결 수립, 커넥션 풀, 콜드 스타트에 따라 더 길거나 짧아집니다. 리전 변경 전후 효과는 필자가 직접 운영한 앱이 아니라 국내외 개발자들의 기록에서 가져왔습니다.

자주 묻는 질문

Vercel 무료(Hobby) 플랜에서도 함수 리전을 서울로 바꿀 수 있나요?

바꿀 수 있습니다. Vercel 문서의 한도 표에서 Hobby는 리전 1개, Pro는 5개, Enterprise는 전체를 고를 수 있습니다. 무료 플랜도 리전 하나를 iad1이 아닌 icn1로 정할 수 있고, 바꾼 뒤에는 새로 배포해야 적용됩니다.

리전을 서울로 바꾸면 Vercel 요금이 오르나요?

단가는 오릅니다. 활성 CPU가 iad1 시간당 0.128달러에서 icn1 0.169달러로, 메모리는 GB-시간당 0.0106달러에서 0.0140달러로 바뀝니다. 다만 Vercel은 함수가 I/O를 기다리는 동안에도 메모리 요금을 받으므로, DB를 멀리서 기다리던 시간이 줄면 그만큼 메모리 사용 시간도 줄어듭니다. 작은 앱에서는 차이가 크지 않을 가능성이 높으니 사용량 화면에서 전후를 비교해 보길 권합니다.

정적 페이지도 리전의 영향을 받나요?

거의 받지 않습니다. 빌드할 때 미리 만든 HTML과 이미지는 CDN 거점에서 바로 나가므로 함수 리전과 상관이 없습니다. 영향을 받는 것은 요청마다 서버에서 그리는 페이지(SSR), 서버 액션, API 라우트입니다. 쿠키나 요청 헤더를 읽는 코드 한 줄 때문에 정적이던 페이지가 동적으로 바뀌는 경우가 있으니, 빌드 로그에서 페이지 종류를 확인해 보세요.

Supabase를 서울로 옮기려면 어떻게 하나요?

기존 프로젝트의 리전은 바꿀 수 없습니다. Supabase 문서는 프로젝트가 고른 리전의 하드웨어에 묶여 있다며, 원하는 리전에 새 프로젝트를 만들고 마이그레이션 가이드대로 옮기라고 안내합니다(Supabase Docs, Change project region). API 주소와 키가 바뀌므로 호스팅의 환경 변수도 함께 고쳐야 합니다. 사용자가 적은 초기에 하는 편이 쉽습니다. 옮기기 어렵다면 반대로 Vercel 함수 리전을 DB가 있는 곳(예: 도쿄 hnd1, 싱가포르 sin1)으로 맞추면 쿼리마다 붙던 대륙 간 왕복은 사라지고, 사용자와 함수 사이 왕복 한 번만 남습니다.

Cloudflare R2와 S3 중 무엇을 써야 하나요?

전송량이 많고 사용자가 여러 나라에 있다면 R2의 전송 요금 0이 유리하고, 한국 사용자 위주라면 CDN이 어느 거점에서 응답하는지 먼저 확인하세요. 국내에서는 Cloudflare 무료 CDN이 해외 거점으로 연결된다는 보고가 있었으므로, cf-ray 헤더의 공항 코드나 실제 다운로드 시간을 재 보고 고르는 것이 안전합니다. S3는 CloudFront를 붙이면 종량제 요금 기준으로 월 1TB까지 전송이 무료입니다.

마치며

바이브코딩 배포가 느린 진짜 이유를 한 줄로 줄이면 지연은 코드가 아니라 거리와 왕복 횟수의 곱이고, 거기에 원본 이미지의 무게가 더해진다는 것입니다.

  • 서버 함수와 DB는 같은 리전에 둡니다. Vercel 기본값 iad1과 서울 DB의 조합은 쿼리마다 약 200ms를 붙입니다.
  • 반복문 안에서 DB를 부르는 N+1을 관계 조회로 바꿉니다. 같은 21번의 쿼리가 미국 동부에서는 4.2초, 같은 리전에서는 0.2초 미만입니다.
  • 이미지는 오브젝트 스토리지에 두고 CDN으로 내려주며, WebP와 썸네일 크기로 보냅니다.
  • Supabase를 쓴다면 배포 전에 RLS 경고부터 확인합니다.

지금 바로 할 일은 Vercel 설정의 Functions 메뉴를 열어 함수 리전이 어디로 되어 있는지 보는 것입니다. Vercel이나 Supabase의 리전과 가격 정책이 바뀌면 이 글을 갱신하겠습니다. AI 에이전트에게 이런 점검을 맡기는 방법은 Claude Code 팁 11가지에서 이어서 읽을 수 있습니다.

sources:auto

출처

  1. 바이브코딩 하다 AWS 요금 폭탄 맞으신 분?(feat. 대안): youtube.com/watch?v=VoqXu3oFsk4
  2. HPBN, Primer on Latency and Bandwidth: hpbn.co/primer-on-latency-and-bandwidth
  3. web.dev, Optimize TTFB, 2025-11-28: web.dev/articles/optimize-ttfb
  4. web.dev, TTFB: web.dev/articles/ttfb
  5. Azure network round-trip latency statistics, 2026-07-30: learn.microsoft.com/en-us/azure/networking/azure-network-latency
  6. WonderNetwork, Seoul to Washington, 2026-10-06: wondernetwork.com/pings/Seoul/Washington
  7. Vercel Docs, Configuring regions, 2026-08-11: vercel.com/docs/functions/configuring-functions/region
  8. Vercel Docs, Debugging slow functions, 2026-06-25: vercel.com/docs/functions/debug-slow-functions
  9. velog 최병현, 2026-08-31: velog.io/@cokid7979/쿼리가-느린-줄-알았는데-문제는-Vercel과-DB-리전이었다
  10. 네이버 블로그 pengmomt, 2026-08-27: blog.naver.com/pengmomt/224392353730
  11. gaveta PR #48, 2026-09-16: github.com/drizaogythub97/gaveta/pull/48
  12. buddytrip #933, 2026-09: github.com/zgrether/buddytrip/issues/933
  13. Vercel Docs, Response headers, 2026-08-11: vercel.com/docs/headers/response-headers
  14. Next.js Docs, preferredRegion, 2026-04-30: nextjs.org/docs/app/api-reference/file-conventions/route-segment-config…
  15. Vercel Docs, Fluid compute pricing, 2026-06-16: vercel.com/docs/functions/usage-and-pricing
  16. Prisma Docs, Query optimization: prisma.io/docs/orm/prisma-client/queries/query-optimization-performance
  17. Rails Guides, Active Record Query Interface: guides.rubyonrails.org/active_record_querying.html
  18. 네이버 블로그 oooolivelove, 2026-08-09: blog.naver.com/oooolivelove/224373253387
  19. r/lovable, 2026-09-23: reddit.com/r/lovable/comments/1wo8i7i/real_users_are_why_i_fixed_this_2…
  20. simbastack 포스트모템, 2026-09-15: blog.simbastack.com/four-round-trips-six-months
  21. HTTP Archive Web Almanac 2025, Page Weight, 2026-01-15: almanac.httparchive.org/en/2025/page-weight
  22. HTTP Archive Web Almanac 2025, Performance: almanac.httparchive.org/en/2025/performance
  23. web.dev, Optimize LCP: web.dev/articles/optimize-lcp
  24. AWS S3 가격표: pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonS3/current/ap-nor…
  25. CloudFront 가격: aws.amazon.com/cloudfront/pricing/pay-as-you-go
  26. CloudFront 요금 페이지: aws.amazon.com/cloudfront/pricing
  27. Cloudflare R2 pricing, 2026-10-01: developers.cloudflare.com/r2/pricing
  28. Google for Developers, WebP, 2025-08-07: developers.google.com/speed/webp
  29. Vercel Docs, Regions: vercel.com/docs/regions
  30. Next.js Docs, Images, 2026-04-01: nextjs.org/docs/app/getting-started/images
  31. Next.js Docs, Image Component, 2026-09-28: nextjs.org/docs/app/api-reference/components/image
  32. Cloudflare Images, Transform via URL: developers.cloudflare.com/images/transform-images/transform-via-url
  33. velog yejiin_21, 2025-07-01: velog.io/@yejiin_21/WebP-Performance-Analysis
  34. velog jjangminii, 2026-04-07: velog.io/@jjangminii/이미지-최적화-삽질기-LCP는-용량-문제가-아니었다
  35. marinesnow34, 2023-09-23: marinesnow34.github.io/2023/09/23/s3r2
  36. Cloudflare Docs, HTTP headers: developers.cloudflare.com/fundamentals/reference/http-headers
  37. Supabase Docs, Row Level Security: supabase.com/docs/guides/database/postgres/row-level-security
  38. UpGuard, Systemic Data Exposure in Supabase Apps, 2026-09-24: upguard.com/blog/everything-everywhere-systemic-data-exposure-in-supaba…
  39. NVD, CVE-2025-48757: nvd.nist.gov/vuln/detail/CVE-2025-48757
  40. 국민일보, 2026-10-02: kmib.co.kr/article/view.asp?arcid=9000019252
  41. Supabase Changelog, 2026-04-28: supabase.com/changelog/45329-breaking-change-tables-not-exposed-to-data…
  42. r/Supabase, 2026-10-01: reddit.com/r/Supabase/comments/1wv5k7i/anyone_else_not_trusting_rls_ena…
  43. Supabase Docs, Advisors: supabase.com/docs/guides/observability/advisors
  44. Neon Docs, Regions: neon.com/docs/introduction/regions
  45. Railway Docs: docs.railway.com/reference/deployment-regions
  46. Render Docs: render.com/docs/regions
  47. Fly.io Docs: fly.io/docs/reference/regions
  48. velog kswift, 2026-04-03: velog.io/@kswift/Firebase에서-Rust로-서버-뼈대-잡기
  49. Cloudflare D1 data location, 2026-10-02: developers.cloudflare.com/d1/configuration/data-location
  50. Cloudflare D1 limits, 2026-04-21: developers.cloudflare.com/d1/platform/limits
  51. Cloudflare Hyperdrive: developers.cloudflare.com/hyperdrive
  52. lmsplus_v2 #1409, 2026-10-01: github.com/okpilot/lmsplus_v2/issues/1409
  53. dev.ootssu.com, 2026-08: dev.ootssu.com/ko/posts/the-lang-tag-crossed-the-pacific
  54. Supabase Docs, Change project region: supabase.com/docs/guides/troubleshooting/change-project-region-eWJo5Z
/sources:auto

글쓴이

주홍철은 네이버 출신 개발자이자 AI 핀테크 스타트업 어비스(AVISS)의 대표입니다. 경제·증시 분석 AI, AI 에이전트, 데이터 파이프라인을 직접 설계하고 개발하며, 『면접을 위한 CS 전공지식 노트』와 『클로드 코드 제대로 시작하기』(길벗)를 썼습니다. 회사 소개는 어비스 홈페이지에, 다른 글은 어비스 블로그에 있으며, 글에 대한 정정 요청이나 문의는 [email protected]으로 보내 주세요.