Claude나 Cursor로 앱을 만들어서 유료 클라이언트에게 넘기려고 한다면 잠깐. AI가 생성한 코드에는 예측 가능한 보안 구멍이 많다 — 노출된 API 키, 누락된 입력 검증, 모든 사용자가 다른 사용자의 데이터를 볼 수 있는 기본 데이터베이스 권한. 이런 것들은 엣지 케이스가 아니다. AI가 생성한 거의 모든 코드베이스에서 일어난다.

이 가이드는 출시 전 보안 체크리스트다. 실제 사용자가 앱에 접근하기 전에 단계별로 따라하자. 가장 일반적인 바이브 코딩 스택(Next.js + Supabase + Vercel)을 기준으로 작성했지만, 원칙은 어떤 도구를 사용하든 적용된다.

Quick Facts
완료 시간
일반적인 앱 기준 2–4시간
필요한 스킬
보안 배경 없음 — 단계별 체크리스트
가정되는 스택
Next.js + Supabase + Vercel (적응 가능)
수정할 항목
인증, 데이터 격리, API 키, 검증, 속도 제한, 환경 변수
언제 해야 하는가
실제 사용자나 클라이언트가 앱에 접근하기 전
마지막 확인
2026년 4월

AI가 생성한 코드에 보안 문제가 있는 이유

AI 모델은 "작동하는가?"를 최적화하지 "안전한가?"를 최적화하지 않는다. Claude에게 "사용자 계정이 있는 작업 관리자를 만들어 달라"고 하면, 사용자를 생성하고, 작업을 저장하고, 표시하는 코드를 생성한다. 하지만 자동으로 하지 않는 것들이 있다: 사용자 A가 사용자 B의 작업을 볼 수 없도록 보장하기, 입력 필드가 악성 스크립트를 받아들이지 않도록 검증하기, API 키를 브라우저 개발자 도구에서 숨기기, 엔드포인트 공격을 방지하기 위한 속도 제한 추가하기.

이것들은 AI의 실패가 아니다 — 프롬프트의 간격이다. AI는 요청한 것을 만든다. 기능에 초점을 맞춰서 아마 보안을 요청하지 않았을 것이다. 이제 돌아가서 추가할 시간이다.

1단계: 환경 변수 감사

이것이 바이브 코딩 앱에서 가장 흔하고 가장 위험한 실수다. 프로젝트의 모든 파일에서 하드코딩된 API 키, 데이터베이스 URL 또는 시크릿을 확인하자.

찾아야 할 것: sk-, eyJ, sbp_, supabase, postgres:// 또는 기타 긴 무작위 문자열로 시작하는 문자열을 찾아보자. 다음 파일들을 특별히 확인하자: /app 또는 /pages 디렉토리의 모든 파일, 모든 컴포넌트 파일, next.config.js, 모든 유틸리티 파일.

해결책: 모든 시크릿을 환경 변수로 옮기자. Next.js에서 NEXT_PUBLIC_으로 시작하는 변수만 브라우저에 노출된다. 데이터베이스 URL, 서비스 역할 키, API 시크릿은 절대 이 접두사를 가져서는 안 된다.

.env.local
# .env.local (절대 커밋하지 마시오)
SUPABASE_SERVICE_ROLE_KEY=your-secret-key
DATABASE_URL=postgres://...

# 이것들은 브라우저에 노출해도 괜찮다:
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key

확인: .gitignore 파일에 .env.local이 포함되어 있는지 확인하자. 이미 시크릿을 Git에 커밋했다면, 삭제 후에도 히스토리에 남아 있다 — 노출된 모든 키를 즉시 재생성(로테이션)하자.

2단계: Supabase에서 행 레벨 보안 활성화

Supabase를 사용 중이라면 이것이 가장 중요한 단계다. 기본적으로 Supabase 테이블에는 접근 제한이 없다 — anon 키를 가진 누구나 모든 테이블의 모든 행을 읽고 쓸 수 있다. 이는 사용자 A가 간단한 API 호출로 사용자 B의 데이터를 볼 수 있다는 의미다.

해결책: 모든 테이블에서 행 레벨 보안(RLS)을 활성화한 다음 접근을 제한하는 정책을 만들자.

Supabase 대시보드 → 테이블 에디터 → 각 테이블 선택 → "RLS Disabled" 클릭하여 활성화. 그 다음 정책을 추가하자:

사용자가 자신의 데이터만 볼 수 있는 일반적인 앱의 경우, SELECT 정책을 만들자: auth.uid() = user_id. INSERT, UPDATE, DELETE에 대해서도 유사한 정책을 만들자.

테스트: 사용자 A로 로그인하고 API를 통해 사용자 B의 데이터에 접근해 보자. 볼 수 있다면 정책이 잘못된 것이다. Supabase에는 정책을 직접 테스트할 수 있는 SQL 에디터가 있다.

흔한 AI 실수: Claude는 종종 Supabase 쿼리를 service_role 키(RLS를 무시함)를 사용하여 생성하는데, anon 키와 적절한 RLS 정책을 대신 사용해야 한다. 클라이언트 측 코드가 anon 키만 사용하는지 확인하자. 서비스 역할 키는 서버 측 코드(API 라우트, 서버 액션)에만 존재해야 하며 브라우저에 노출되어서는 안 된다.

3단계: 인증 제대로 추가하기

AI가 생성한 인증 코드는 작동하지만 지름길을 택하는 경우가 많다. 이 특정 문제들을 확인하자:

세션 관리: 세션이 만료되는지 확인하자. 인증 설정에 합리적인 세션 타임아웃이 포함되어 있는지 확인하자(Supabase 기본값은 일반적으로 괜찮지만 확인하자). 로그아웃이 실제로 세션을 무효화하고 로컬 쿠키만 지우는 것이 아닌지 확인하자.

비밀번호 요구사항: 이메일/비밀번호 인증이 있다면 최소 비밀번호 길이(8자 이상)를 강제하자. Supabase는 프로젝트 설정 → 인증 → 비밀번호 요구사항에서 이를 처리한다.

보호된 라우트: 사용자별 데이터를 표시하는 모든 페이지는 인증 미들웨어가 필요하다. Next.js App Router에서 유효한 세션을 확인하고 인증되지 않은 사용자를 로그인 페이지로 리다이렉트하는 미들웨어를 만들자. 클라이언트 측 확인만 믿지 말자 — 사용자는 API를 직접 호출하여 이를 무시할 수 있다.

이메일 확인: Supabase 인증 설정에서 이메일 확인을 활성화하자. 이는 가짜 이메일 주소로 계정을 만드는 것을 방지하고 기본적인 계정 유효성 검사 계층을 추가한다.

가치를 얻고 있는가? 우리는 AI 도구, 워크플로우, 실용적인 가이드에 대한 심층 분석을 주당 하나씩 발행한다. 먼저 받는 독자들과 함께하자 →

4단계: 모든 입력 검증하기

AI가 생성한 폼은 보통 기본 검증(필수 필드, 이메일 형식)을 가지지만 악성 입력으로부터 보호하는 경우는 거의 없다.

추가할 것:

모든 API 엔드포인트에서 서버 측 검증. 클라이언트 측 검증만 믿지 말자 — API에 직접 요청을 보내서 무시할 수 있다. Zod(TypeScript용)와 같은 검증 라이브러리를 사용하여 앱이 받아들이는 모든 데이터에 대한 스키마를 정의하자.

브라우저에 표시되는 사용자 생성 콘텐츠의 HTML을 정제하자. 앱에 댓글, 설명 또는 렌더링되는 텍스트 필드가 있다면, DOMPurify와 같은 라이브러리를 사용하여 위험한 스크립트를 제거하자. 없으면 누군가 다른 사용자의 세션을 훔치는 JavaScript를 주입할 수 있다(교차 사이트 스크립팅 / XSS).

파일 업로드 크기와 유형을 제한하자(앱이 파일 업로드를 받아들이는 경우). AI가 생성한 업로드 핸들러는 종종 제한이 없어서 누군가 2GB 파일이나 실행 파일을 업로드할 수 있다. 크기 제한(대부분의 앱에는 5MB가 합리적)을 추가하고 파일 유형을 실제로 필요한 것으로 제한하자(이미지, PDF 등).

5단계: API 라우트 보호하기

앱의 모든 API 라우트 또는 서버 액션에서 이 문제들을 확인하자:

모든 엔드포인트에서 인증. 사용자 데이터를 반환하거나 수정하는 모든 API 라우트는 먼저 사용자의 세션을 확인해야 한다. AI는 종종 누구의 요청이든 받아들이는 API 라우트를 생성한다.

인증 이상의 권한 부여. 사용자가 로그인했음을 확인한 후에도, 그들이 요청하는 특정 리소스에 접근할 수 있는지 확인하자. "사용자 A가 로그인했다"는 "사용자 A가 사용자 B의 프로필을 편집할 수 있다"는 뜻이 아니다. 모든 데이터 접근에서 소유권을 확인하자.

속도 제한. 속도 제한 없이, 누군가 초당 수천 개의 요청을 API에 보낼 수 있는데, 데이터를 스크래핑하거나 서버를 압도하려고 할 수 있다. rate-limiter-flexible 같은 라이브러리를 사용하거나 Edge Functions에서 Vercel의 내장 속도 제한을 사용하여 기본 속도 제한을 추가하자.

HTTP 메서드. API 라우트가 응답해야 할 HTTP 메서드만 응답하도록 확인하자. POST 요청을 처리하는 라우트는 명시적으로 설계하지 않는 한 DELETE 요청에도 응답하지 않아야 한다.

6단계: 배포 구성 확인하기

배포 플랫폼에는 AI가 구성하지 않는 보안 설정이 있다.

확인할 Vercel 설정: "배포 보호" 활성화(미리보기 배포 보기에 인증 필요 — 클라이언트가 실수로 진행 중인 작업을 노출하는 미리보기 URL을 공유하는 것을 방지). 지출 제한을 설정하여 트래픽 스파이크 시 예상치 못한 요금을 방지하자. CORS 헤더에서 허용된 도메인을 구성하자.

커스텀 도메인 및 SSL: 클라이언트에게 이를 전달하려면, 커스텀 도메인을 HTTPS로 설정하자. Vercel과 Netlify는 SSL을 자동으로 처리한다. 클라이언트 앱을 .vercel.app 서브도메인에 제공하지 말자 — 비전문적으로 보이고 클라이언트가 쉽게 전송할 수 없다.

헤더: next.config.js 또는 vercel.json에 보안 헤더를 추가하자: X-Content-Type-Options: nosniff, X-Frame-Options: DENY(사이트가 클릭재킹을 위해 iframe에 내장되는 것을 방지), Strict-Transport-Security(HTTPS를 강제). 이것들은 전체 공격 클래스를 방지하는 일회성 추가다.

7단계: 최종 보안 스캔 실행하기

클라이언트에게 무언가를 넘기기 전에 이 무료 확인들을 실행하자:

npm audit: 프로젝트 디렉토리에서 npm audit을 실행하자. 종속성의 알려진 취약성을 표시한다. 중요 및 높은 심각도 문제를 수정하자. 가능한 경우 npm audit fix로 자동 수정을 실행하자.

Lighthouse: Chrome에서 배포된 사이트를 열고, DevTools를 열고, Lighthouse 감사를 실행하자. "Best Practices" 점수를 확인하자 — HTTPS 누락, 취약한 라이브러리, 안전하지 않은 헤더 같은 일반적인 보안 문제를 잡는다.

수동 테스팅: 한 명의 사용자로 로그인하고, URL이나 API 호출을 수정하여 다른 사용자의 데이터에 접근해 보자. 빈 폼, 과도한 입력, 특수 문자(인코딩된 XSS 페이로드 같은)를 제출해 보자. 이 중 하나라도 작동한다면, 수정해야 할 문제가 있다.

출시 전 체크리스트

이것을 인쇄하고 실행 전에 모든 항목을 확인하자:

  • 모든 시크릿이 환경 변수에 있음(하드코딩된 키 없음)
  • .env.local.gitignore에 있음(그리고 Git 히스토리에 시크릿 없음)
  • 모든 테이블에서 Supabase RLS 활성화됨
  • RLS 정책이 테스트됨(사용자 A가 사용자 B의 데이터를 볼 수 없음)
  • 클라이언트 측 코드가 anon 키만 사용(서비스 역할 키는 서버 측만)
  • 모든 API 라우트에서 인증됨
  • 권한 부여 확인(소유권 확인)이 데이터 접근 시 수행됨
  • 모든 폼에서 입력 검증됨(클라이언트 측만 아님, 서버 측)
  • 파일 업로드 제한이 있음(크기와 유형, 해당하는 경우)
  • API 엔드포인트에 속도 제한이 있음
  • 보안 헤더가 구성됨
  • npm audit이 실행되고 중요 문제가 수정됨
  • 커스텀 도메인이 SSL로 구성됨
  • 미리보기 배포가 보호됨
  • 수동 교차 사용자 데이터 접근 테스트가 통과됨

결론

바이브 코딩 앱을 보호하는 것은 보안 전문가가 되는 것에 관한 것이 아니다. 이것은 AI가 남긴 예측 가능한 간격을 잡는 체크리스트를 실행하는 것이다. 위의 단계들은 2–4시간이 소요되고 가장 흔한 취약성을 방지한다. 클라이언트 대면 앱의 경우, 이것은 선택 사항이 아니다 — 전문 작업과 책임 사이의 차이다.

첫 번째 바이브 코딩 앱을 만드는 경우, 바이브 코딩 완전 가이드로 시작하자. Claude나 Cursor와 함께 사용하는 프롬프트를 개선하고 싶다면, 무료 프롬프트 옵티마이저를 시도해 보자. 최고의 코딩 도구 분석은 Claude Code vs Codex를 보자.

우리는 매주 이것을 한다. AI 도구, 워크플로우, 진솔한 의견에 대한 심층 분석 — 과대광고도, 채우기도 없다. 우리와 함께하자 →

공개: 이 글의 일부 링크는 제휴 링크다. 우리는 개인적으로 테스트하고 정기적으로 사용하는 도구만 추천한다. 전체 공개 정책을 보자.