← 블로그 목록

바이브코더가 가장 자주 만나는 버그 TOP 3

로컬에서는 되는데 배포하면 안 되는 환경 변수 문제, Next.js hydration mismatch, CORS와 인증 오류까지 바이브코딩 프로젝트에서 자주 막히는 버그와 확인 순서를 정리했습니다.

바이브코딩을 하다 보면 이상한 순간이 옵니다.

화면도 나왔고, 로그인도 붙었고, API도 연결된 것 같습니다. 로컬에서는 버튼을 눌러도 문제없습니다. 그런데 배포를 누르는 순간부터 갑자기 안 됩니다.

"왜 내 컴퓨터에서는 되는데 실제 주소에서는 안 되지?"

이 질문이 나오면 보통 코드 한 줄의 문제가 아닙니다. AI가 기능 코드는 만들어줬지만, 실제 서비스가 돌아가는 환경까지 맞춰진 것은 아니기 때문입니다. 특히 아래 세 가지는 바이브코더가 거의 반드시 한 번씩 만나는 문제입니다.

1. 환경 변수와 배포 환경 문제

가장 흔하고, 가장 오래 붙잡게 되는 문제입니다.

대표적인 증상은 이렇습니다.

  • 로컬에서는 되는데 Vercel에 배포하면 안 된다
  • API 호출이 실패한다
  • Supabase나 Firebase 연결값이 undefined로 나온다
  • 카카오, 네이버, 구글 로그인에서 redirect mismatch가 뜬다
  • 로그인 후 localhost로 돌아가거나 흰 화면이 나온다

이 문제의 핵심은 로컬과 배포가 서로 다른 환경이라는 점입니다. 내 컴퓨터의 .env.local에 값이 들어 있어도, Vercel이나 Netlify가 그 값을 자동으로 가져가지는 않습니다. 배포 서비스의 Environment Variables에 따로 등록해야 합니다.

Next.js에서는 한 가지를 더 봐야 합니다. 브라우저에서 읽어야 하는 값에는 NEXT_PUBLIC_이 붙어 있어야 합니다. 예를 들어 Supabase URL이나 anon key를 클라이언트에서 쓴다면 NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY처럼 이름이 맞아야 합니다.

반대로 서버에서만 써야 하는 service role key, 결제 secret key 같은 값에는 NEXT_PUBLIC을 붙이면 안 됩니다. 붙이는 순간 브라우저에 노출될 수 있습니다.

환경 변수 문제를 볼 때의 순서

코드부터 고치기 전에 먼저 설정을 확인하는 편이 빠릅니다.

  1. 로컬 .env.local에 있는 키 이름과 배포 환경 변수 이름이 같은지 본다
  2. Vercel 기준 Production, Preview, Development 중 필요한 환경에 값이 들어 있는지 확인한다
  3. 환경 변수를 추가하거나 수정했다면 재배포한다
  4. OAuth를 쓴다면 운영 도메인이 Redirect URI와 Authorized domain에 등록되어 있는지 본다
  5. 코드 안에 localhost, 예전 preview URL, 임시 도메인이 남아 있지 않은지 검색한다

특히 OAuth는 한 글자만 달라도 실패합니다. https://example.com/auth/callback과 https://www.example.com/auth/callback은 다른 주소입니다. 끝의 슬래시 하나 때문에 막히는 경우도 있습니다.

이 단계에서 자주 필요한 것은 새 코드가 아니라 대시보드 확인입니다. AI에게 "코드 고쳐줘"라고 바로 시키면 멀쩡한 로그인 코드를 다시 짜기 시작할 수 있습니다.

관련해서는 Vercel 환경변수가 적용 안 될 때, 비개발자가 점검할 5가지카카오·네이버 로그인이 로컬에선 되는데 배포 후 안 될 때를 같이 보면 좋습니다.

2. Hydration mismatch

두 번째는 Next.js App Router에서 자주 보이는 hydration mismatch입니다.

에러 문구는 보통 이런 식입니다.

Hydration failed because the initial UI does not match what was rendered on the server.

말은 어렵지만 원리는 단순합니다. 서버가 처음 만든 화면과 브라우저가 다시 그린 첫 화면이 서로 다를 때 생기는 문제입니다.

바이브코딩 프로젝트에서 자주 나오는 원인은 이렇습니다.

  • Date.now()로 현재 시간을 바로 화면에 찍는다
  • Math.random()으로 매번 다른 값을 만든다
  • window, document를 서버 렌더링 중에 접근한다
  • localStorage 값을 처음 렌더링부터 읽는다
  • 브라우저 크기에 따라 첫 화면 구성이 달라진다

로컬 개발 모드에서는 대충 넘어간 것처럼 보여도, 빌드하거나 배포한 뒤에는 더 예민하게 드러나는 경우가 많습니다.

Hydration mismatch를 줄이는 방법

첫 화면에서 서버와 브라우저가 같은 결과를 내도록 만드는 것이 핵심입니다.

예를 들어 현재 시간, 랜덤 값, localStorage, window.innerWidth 같은 값은 브라우저에서만 확실히 알 수 있습니다. 이런 값은 처음 렌더링에 바로 넣지 말고, 컴포넌트가 브라우저에서 열린 뒤에 처리하는 편이 안전합니다.

확인 순서는 이렇습니다.

  1. 에러가 나는 화면에서 Date.now, new Date, Math.random을 검색한다
  2. window, document, localStorage를 렌더링 중에 바로 쓰는지 본다
  3. 브라우저 전용 값은 useEffect 안으로 옮긴다
  4. 처음 화면에는 "확인 중", 빈 상태, 기본값처럼 서버와 클라이언트가 같은 값을 보여준다
  5. 꼭 클라이언트에서만 그려야 하는 UI라면 해당 부분을 client component로 분리한다

중요한 건 "일단 에러를 숨기는 것"이 아니라 첫 화면의 기준을 맞추는 것입니다. suppressHydrationWarning 같은 옵션으로 덮을 수도 있지만, 원인을 모른 채 쓰면 나중에 더 찾기 어려워집니다.

3. CORS와 인증 문제

세 번째는 CORS와 인증 문제입니다. Supabase, Firebase, 자체 API, OAuth, 쿠키 인증이 들어가면 자주 만납니다.

대표적인 증상은 이렇습니다.

  • Access to fetch has been blocked by CORS policy가 뜬다
  • 로그인은 된 것 같은데 API는 401 또는 403을 반환한다
  • 로컬에서는 세션이 유지되는데 배포하면 새로고침 후 로그아웃된다
  • 쿠키가 저장되지 않는다
  • Supabase에서는 데이터가 있는데 화면에는 빈 배열이 나온다

CORS는 브라우저가 다른 도메인으로 요청을 보낼 때 생기는 보안 규칙입니다. 프론트엔드 주소가 https://my-service.com이고 API 주소가 https://api.my-service.com이라면, API 서버가 "이 프론트엔드에서 오는 요청을 받아도 된다"고 허용해줘야 합니다.

쿠키 인증까지 들어가면 한 단계 더 까다롭습니다. 요청에는 credentials: include가 필요할 수 있고, 서버는 Access-Control-Allow-Credentials를 허용해야 합니다. 이때 Access-Control-Allow-Origin을 *로 둘 수 없고, 실제 운영 도메인을 정확히 지정해야 합니다.

CORS와 인증 문제를 볼 때의 순서

이 문제는 에러 메시지만 보고 코드를 바꾸면 자주 돌아갑니다. 먼저 요청 흐름을 작게 나눠보는 편이 좋습니다.

  1. 프론트엔드 운영 도메인과 API 도메인을 정확히 적는다
  2. API 서버나 인증 서비스의 허용 도메인 목록에 운영 도메인이 있는지 확인한다
  3. 쿠키 인증이면 클라이언트 요청에 credentials 옵션이 들어가는지 본다
  4. 쿠키의 Secure, SameSite, domain 값이 운영 HTTPS 환경에 맞는지 확인한다
  5. Supabase라면 RLS 정책 때문에 select나 insert가 막힌 것은 아닌지 본다
  6. Firebase나 OAuth라면 Authorized domains, Callback URL, Redirect URI를 다시 확인한다

특히 "로그인은 됐는데 API 호출만 실패"한다면 로그인 화면보다 API 요청에 인증 정보가 실려 가는지를 봐야 합니다. 반대로 "로그인 직후 새로고침하면 풀린다"면 쿠키나 세션 저장 쪽을 먼저 의심하는 편이 빠릅니다.

Supabase에서 데이터가 안 보이는 문제는 CORS처럼 보이지만 실제로는 RLS 정책 문제일 때도 많습니다. 이 경우는 Supabase RLS 때문에 데이터가 안 보일 때, 비개발자 체크 순서를 먼저 확인해보세요.

AI에게 다시 물어볼 때 이렇게 말하기

이 세 가지 문제는 AI에게 던지는 방식도 중요합니다.

그냥 "배포 오류 고쳐줘"라고 하면 AI는 눈앞의 코드부터 바꾸려고 합니다. 하지만 환경 변수, OAuth, CORS, hydration 문제는 코드와 설정이 함께 엮인 경우가 많습니다.

차라리 이렇게 물어보는 편이 낫습니다.

  • 로컬에서는 되는데 배포에서만 실패한다. 코드 수정 전에 환경 변수와 도메인 설정부터 확인할 항목을 정리해줘.
  • 이 Next.js 화면에서 hydration mismatch를 만들 수 있는 부분을 찾아줘. 바로 수정하지 말고 원인 후보부터 설명해줘.
  • CORS 에러가 나는데, 프론트 도메인과 API 도메인, credentials, 쿠키 설정 중 어디를 확인해야 하는지 순서대로 봐줘.
  • Supabase 요청이 빈 배열을 반환한다. 쿼리 코드보다 RLS 정책 가능성을 먼저 점검해줘.

"바로 고쳐줘"보다 "어디를 봐야 하는지 먼저 좁혀줘"가 이런 문제에서는 더 안전합니다.

그래도 해결이 안 된다면

이 세 가지 버그는 초보라서 만나는 문제가 아닙니다. 실제 서비스에서도 자주 터집니다. 다만 바이브코딩 프로젝트에서는 AI가 만들어준 코드와 사람이 직접 맞춰야 하는 운영 설정 사이가 비어 있어서 더 자주 드러납니다.

혼자 확인할 때는 아래 다섯 가지만 먼저 정리해도 좋습니다.

  1. 로컬에서는 되는지, 배포에서만 안 되는지
  2. 정확한 에러 메시지가 무엇인지
  3. 사용 중인 배포 서비스와 도메인이 무엇인지
  4. Supabase, Firebase, OAuth, 자체 API 중 무엇이 연결되어 있는지
  5. 마지막으로 AI나 사람이 수정한 파일이 무엇인지

여기까지 봤는데도 같은 문제가 반복된다면, 코드를 더 덧대기 전에 한 번 멈추는 게 좋습니다. 환경 변수, hydration, CORS와 인증 문제는 잘못 손대면 해결보다 부작용이 커질 수 있습니다.

LastFix는 이런 식으로 막힌 바이브코딩 프로젝트를 먼저 원인부터 좁혀봅니다. 에러 메시지, 배포 주소, 문제가 생기는 화면, 사용 중인 외부 서비스만 있어도 진단을 시작할 수 있습니다.

관련 글: 비개발자의 바이브코딩, 왜 배포에서 자주 막힐까?, AI 코딩 에이전트가 고치지 못하는 버그의 3가지 특징, AI로 만든 서비스를 개발자에게 맡기기 전 준비할 것

AI·바이브코딩으로 해결되지 않는 개발 이슈, 먼저 원인부터 확인하세요

Cursor 에러, Vercel·Next.js 배포 실패, 결제·예약 연동 오류, 소규모 기능 개발을 단건 중심으로 진단합니다. 월 구독 파트너는 정식 상품을 준비 중입니다.