- 공유 링크 만들기
- X
- 이메일
- 기타 앱
Featured Post
작성자:
Iros
- 공유 링크 만들기
- X
- 이메일
- 기타 앱

요즘 퇴근하고 집에 와서 노트북을 열면, 예전처럼 혼자 묵묵히 코드를 짜기보다는 AI 코딩 도우미랑 같이 이것저것 만들어보는 시간이 많아졌어요. 특히 Supabase로 백엔드를 붙일 때는 꽤 재미가 있더라고요. 인증도 있고, PostgreSQL도 있고, Edge Functions도 있고. 작은 서비스를 빨리 만들어보기엔 참 좋은 조합이죠.
그런데 말이죠. 처음엔 저도 좀 만만하게 봤어요. “AI한테 시키면 알아서 잘하겠지” 싶었거든요. 20년 넘게 개발했는데도, AI 앞에서는 또 다른 방식으로 삽질을 하게 되더라고요. 프롬프트를 애매하게 던지면 이상한 SQL이 나오고, RLS 정책은 보안 구멍이 생기고, Edge Functions는 갑자기 Node.js 코드처럼 튀어나오고요. 아, 이거 그냥 쓰면 안 되겠구나 싶었습니다.
그래서 한동안 제 나름대로 Supabase AI 바이브 코딩을 할 때 어떤 식으로 질문해야 토큰도 덜 쓰고, 결과도 괜찮게 나오는지 계속 정리해봤어요. 오늘은 그걸 조금 편하게 풀어보려고 합니다. 뭔가 대단한 이론이라기보다는, 실제로 제가 프로젝트 하면서 “이건 진짜 해두면 편하네” 싶었던 것들이에요.
Supabase 프롬프트는 짧게 쓰되, AI가 헷갈리지 않게 써야 하더라고요
처음에 제가 제일 많이 했던 실수가 이거였어요. AI에게 대충 이렇게 물어보는 거죠.
“users 테이블에 대한 RLS 정책 만들어줘. 로그인한 사용자는 자기 데이터만 보고, admin은 전체를 보게 해줘.”
사람끼리 이야기할 때는 이 정도면 통할 것 같잖아요. 그런데 AI 입장에서는 모르는 게 너무 많아요. users 테이블의 기본 키가 뭔지, 사용자 식별 컬럼이 id인지 user_id인지, admin 여부를 어디서 확인하는지, role 컬럼이 실제로 있는지, 아니면 JWT custom claim을 쓰는지 전부 추측해야 하거든요.
그러면 결과가 어떻게 되냐면요. 그럴듯한 SQL은 나오는데, 막상 Supabase SQL Editor에 넣으면 에러가 나요. 아니면 에러는 안 나는데 보안 정책이 이상하게 열려버립니다. 이게 더 무섭죠.
제가 정착한 방식은 간단합니다. 스키마는 필요한 만큼만 주고, 조건은 아주 또렷하게 적기. 이게 핵심이에요. 전체 테이블 구조를 다 붙여 넣을 필요도 없고, 반대로 너무 생략해도 안 됩니다. 딱 AI가 판단하는 데 필요한 컬럼과 조건만 주면 돼요.
| 구분 | 프롬프트 예시 | 제가 겪은 결과 |
|---|---|---|
| 아쉬운 방식 | “users 테이블에 role 컬럼이 있고, 각 행은 user_id로 구분해. 로그인한 사람은 본인 데이터만 보고, admin은 다 보게 해줘. RLS 정책 만들어줘.” |
AI가 조건을 중간에 섞어버림. auth.uid() 비교 대상도 흔들리고, 수정 질문만 4~5번 더 하게 됨.
|
| 괜찮았던 방식 |
“테이블: users 컬럼: id uuid, email text, role text, user_id uuid RLS 조건: 로그인 사용자는 users.user_id = auth.uid() 인 행만 SELECT 가능 관리자 조건: users.role = 'admin' 인 사용자는 모든 행 SELECT 가능 PostgreSQL CREATE POLICY 문으로 작성해줘.” |
처음 나온 SQL을 거의 그대로 테스트 가능. 토큰도 덜 쓰고, 재질문 횟수가 확 줄었음. |
재미있는 건, 프롬프트가 무조건 길다고 좋은 게 아니라는 거예요. 오히려 길고 두루뭉술하면 AI가 중간에서 자기 마음대로 해석해요. 사람도 회의에서 장황하게 말하면 핵심을 놓치잖아요. AI도 비슷하더라고요.
제가 자주 쓰는 RLS 프롬프트 틀
저는 이제 Supabase RLS 정책을 만들 때 거의 아래 틀을 씁니다. 별거 아닌데, 이걸 메모장에 저장해두고 복사해서 쓰니까 진짜 편해요.
Supabase RLS 정책 작성 요청
테이블: {테이블명}
핵심 컬럼: {id uuid, user_id uuid, role text 처럼 필요한 컬럼만}
작업: {SELECT / INSERT / UPDATE / DELETE}
조건:
- 로그인 사용자는 {컬럼명} = auth.uid() 인 행만 접근 가능
- 관리자는 {관리자 판단 기준} 일 때 전체 접근 가능
프로젝트에 이미 정의된 함수: {예: public.is_admin(), public.is_owner(uuid)}
PostgreSQL RLS CREATE POLICY 문으로 작성해줘.
불필요한 설명은 줄이고, 실행 가능한 SQL 위주로 답변해줘.
여기서 제가 특히 좋아하는 방식은 공통 헬퍼 함수를 만들어두는 거예요. 예를 들어 관리자 여부를 매번 role = 'admin'으로 직접 쓰게 하는 대신, public.is_admin() 같은 함수를 만들어둡니다. 그러면 프롬프트도 짧아지고, AI가 매번 다른 방식으로 관리자 조건을 만드는 일도 줄어요.
처음에는 “그냥 AI한테 매번 시키면 되지, 굳이 함수까지 만들어야 하나?” 싶었는데요. 여러 테이블에 RLS 정책을 붙이다 보면 생각이 바뀝니다. 같은 로직이 여러 군데 흩어지면 나중에 수정할 때 피곤해져요. 이건 AI를 쓰든 안 쓰든 똑같더라고요.
Supabase 설정을 미리 해두면 프롬프트가 짧아지고 결과가 안정돼요
오래 개발하다 보니 몸에 밴 습관이 하나 있어요. “반복되는 건 설정으로 빼자.” 젊을 때는 그냥 빠르게 코드로 때우는 게 멋있어 보였는데, 이제는 아니에요. 나중에 덜 고생하는 게 제일 멋있습니다. 솔직히 허리도 아프고요.
Supabase도 마찬가지예요. AI에게 매번 긴 설명을 하지 않으려면, 프로젝트 안에 기준이 되는 함수나 트리거, 타입 파일을 미리 만들어두는 게 좋습니다. 그러면 프롬프트가 짧아지고, AI가 내 프로젝트의 방식에 맞춰 코드를 만들 가능성이 높아져요.
회원가입 후 프로필 생성은 트리거로 빼두는 게 마음 편합니다
예를 들어 사용자가 회원가입하면 profiles 테이블에 기본 데이터를 넣는 작업이 있잖아요. 이걸 매번 애플리케이션 코드에서 처리하려고 하면 생각보다 귀찮습니다. 클라이언트에서 놓칠 수도 있고, 서버 API에서 예외가 생길 수도 있고요.
저는 이런 건 Supabase SQL Editor에서 함수와 트리거로 만들어두는 편이에요. 그러면 AI에게도 짧게 말할 수 있습니다. “회원가입 후 프로필 생성 로직 만들어줘”가 아니라, “이미 있는 handle_new_user 함수를 트리거로 연결해줘”라고 말하면 되니까요.
-- Supabase SQL Editor에서 한 번 만들어두면 이후 프롬프트가 훨씬 짧아집니다.
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO public.profiles (id, email, nickname)
VALUES (NEW.id, NEW.email, NEW.raw_user_meta_data ->> 'full_name');
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
이 함수를 만들어둔 뒤에는 프롬프트가 이렇게 바뀝니다.
“Supabase에서 auth.users에 신규 사용자가 생성될 때 public.handle_new_user()를 실행하는 트리거 SQL을 작성해줘.”
훨씬 짧죠. 토큰도 아끼고, AI가 엉뚱한 프로필 생성 코드를 새로 짜는 일도 줄어듭니다. 저는 이런 식으로 자주 쓰는 로직은 데이터베이스 함수로 고정해두는 걸 좋아해요. AI에게 일을 맡기더라도, 기준점은 사람이 잡아줘야 한다고 봅니다.
TypeScript 타입은 Supabase CLI로 뽑아두면 든든해요
이건 진짜 추천하고 싶어요. Supabase CLI로 데이터베이스 타입을 생성해두면, AI에게 TypeScript 코드를 부탁할 때 훨씬 안정적으로 나옵니다.
supabase gen types typescript --linked > src/types/database.types.ts
이렇게 만들어진 database.types.ts 파일을 프로젝트에 넣어두면, AI에게 “이 타입을 기준으로 API route 만들어줘”라고 요청할 수 있어요. 물론 전체 파일을 통째로 프롬프트에 붙여 넣으면 토큰이 터집니다. 그건 좀 아깝죠.
제가 쓰는 방식은 필요한 테이블 타입만 잘라서 넣는 거예요. 예를 들어 posts, comments만 필요한 작업이면 그 부분만 복사합니다. 그러면 AI가 any를 남발하지 않고, 실제 컬럼명에 맞춰서 코드를 만들어줘요.
| 방식 | 장점 | 주의할 점 |
|---|---|---|
| 스키마 설명만 직접 작성 | 빠르고 간단함 | 컬럼명을 잘못 적으면 AI도 그대로 틀림 |
| 생성된 TypeScript 타입 일부 제공 | 타입 기반 코드 정확도가 좋아짐 | 필요한 부분만 잘라 넣어야 토큰 낭비가 적음 |
| 전체 타입 파일 붙여넣기 | 맥락은 많이 전달됨 | 토큰 사용량이 너무 커지고, 답변 품질이 오히려 흐려질 때가 있음 |
여기서 제 기준은 단순합니다. AI가 지금 작업하는 데 꼭 필요한 정보만 준다. 괜히 “혹시 모르니까 다 넣자”는 마음이 들 때가 있는데, 그럴수록 답변이 산으로 가는 경우가 많았어요.
Edge Functions는 Deno 환경이라고 꼭 못 박아야 합니다
Supabase Edge Functions를 처음 AI에게 맡겼을 때 제일 당황했던 게 이 부분이에요. 분명 Supabase Edge Functions를 만들어달라고 했는데, AI가 Node.js 스타일 코드를 내놓더라고요. require()가 나오고, process.env가 나오고, 어떤 때는 axios를 설치하라는 식으로 답하기도 했습니다.
문제는 그 코드가 얼핏 보면 그럴듯하다는 거예요. 그래서 바쁜 날에는 그냥 붙여 넣고 배포했다가, 그제야 오류를 봅니다. 그 순간 살짝 허탈하죠. “아, 내가 Deno라고 안 말했구나.”
이후로는 Edge Functions 관련 프롬프트 맨 앞에 거의 주문처럼 넣습니다.
“이 코드는 Supabase Edge Functions에서 실행됩니다. 런타임은 Deno입니다. Node.js 전용 API는 사용하지 마세요.”
이 한 문장만 넣어도 결과가 꽤 달라져요. 여기에 CORS, 환경변수, 응답 형식까지 같이 적어두면 더 좋고요.
- ☑ 이 코드는 Supabase Edge Functions에서 실행됩니다.
- ☑ 런타임은 Deno입니다. Node.js 전용 API는 사용하지 않습니다.
- ☑ 외부 API 호출은 기본
fetch를 사용합니다. - ☑
SUPABASE_URL,SUPABASE_ANON_KEY환경변수를 사용합니다. - ☑ CORS 헤더를 명시적으로 포함합니다.
- ☑ 응답은 표준
Response객체로 반환합니다.
저는 이 체크리스트를 아예 Edge Functions 프롬프트 템플릿으로 저장해뒀어요. 그러면 매번 같은 실수를 막을 수 있습니다. 사람도 피곤하면 빠뜨리잖아요. AI도 피곤한 건 아닌데, 제가 애매하게 말하면 비슷하게 빠뜨립니다.
제가 쓰는 Edge Functions 요청 예시
Supabase Edge Functions용 코드를 작성해줘.
런타임은 Deno입니다. Node.js API, require, process.env는 사용하지 마세요.
환경변수는 Deno.env.get()으로 읽어주세요.
외부 API 호출은 fetch만 사용하세요.
CORS preflight 요청을 처리해주세요.
입력: request body에 user_id, message가 들어옵니다.
처리: messages 테이블에 저장한 뒤 저장된 row를 JSON으로 반환합니다.
응답: new Response(JSON.stringify(...), { headers }) 형태로 작성해주세요.
이렇게 요청하면 AI가 훨씬 덜 헤맵니다. 특히 Deno.env.get()을 명시하는 게 은근히 중요해요. 안 그러면 습관처럼 process.env.SUPABASE_URL을 꺼내는 답변이 나올 때가 있거든요.
복잡한 SQL은 “쿼리만” 부탁하면 위험할 때가 있어요
AI가 SQL을 참 잘 짜는 것처럼 보일 때가 있습니다. 그런데 데이터가 조금만 많아지면 이야기가 달라져요. 저도 한 번 크게 데인 적이 있어요.
최근 7일간 작성된 글과 댓글 수를 조회하는 기능이었는데, 처음엔 별생각 없이 AI에게 이렇게 물어봤습니다.
“posts 테이블과 comments 테이블을 사용해서 최근 7일간 작성된 글과 댓글 수를 조회하는 SQL을 만들어줘.”
답은 바로 나왔어요. LEFT JOIN에 GROUP BY를 쓰는 흔한 쿼리였고, 작은 데이터에서는 잘 돌아갔습니다. 문제는 실제 데이터가 어느 정도 쌓인 환경에서였어요. 조회가 10초 넘게 걸렸습니다. 그 순간 등골이 살짝 서늘하죠. 개발자는 알잖아요. 10초짜리 조회는 그냥 느린 게 아니라, 어딘가 잘못된 겁니다.
EXPLAIN ANALYZE를 돌려보니 풀 테이블 스캔이 나오고 있었어요. 인덱스도 제대로 못 타고 있었고요. 그 뒤로는 SQL을 요청할 때 꼭 성능 관련 정보를 같이 넣습니다.
| 구분 | 프롬프트 | 결과 |
|---|---|---|
| 삽질했던 요청 | “최근 7일간 작성된 글과 댓글 수를 조회하는 SQL을 만들어줘. posts와 comments 테이블 사용.” | 쿼리는 동작했지만 느렸음. 데이터가 늘자 실행 시간이 12초까지 증가. |
| 개선한 요청 |
“posts 컬럼: id, title, created_at comments 컬럼: id, post_id, created_at 최근 7일간 posts만 조회합니다. posts.created_at에는 btree 인덱스가 있습니다. comments.post_id에는 인덱스가 있습니다. PostgreSQL 기준으로 작성하고, EXPLAIN ANALYZE로 확인할 포인트도 함께 알려줘.” |
AI가 인덱스 사용을 고려한 쿼리를 제안함. 실제 실행 시간이 0.3초 수준으로 줄어듦. |
여기서 느낀 게 있어요. AI에게 스키마만 주면 기능 중심으로 답하고, 인덱스 정보까지 주면 운영 관점에 가까운 답을 한다는 점입니다. 물론 AI가 항상 완벽하진 않아요. 그래도 질문에 인덱스, 데이터 범위, 예상 row 수 같은 정보를 넣으면 답변의 결이 달라집니다.
제가 SQL 요청할 때 자주 넣는 정보는 이 정도예요.
- 테이블별 핵심 컬럼: 전체 컬럼 말고 JOIN, WHERE, ORDER BY에 쓰는 컬럼 위주
- 인덱스 정보: 예를 들어
posts.created_at btree index,comments.post_id index - 데이터 범위: 최근 7일, 특정 사용자, 공개 글만 조회 같은 조건
- 성능 요청:
EXPLAIN ANALYZE에서 확인할 부분도 알려달라고 요청 - Supabase 기준: PostgreSQL 문법 기준으로 작성해달라고 명시
토큰을 무조건 적게 쓰는 것만이 답은 아니더라고요. 필요한 정보를 조금 더 써서 재작업을 줄이는 게 진짜 절약일 때가 많습니다. 이건 개발 일정에도 똑같이 적용되는 말 같아요. 초반에 조금 귀찮게 잡아두면 뒤에서 덜 무너집니다.
AI가 만든 코드는 믿되, 검증은 꼭 사람이 해야 합니다
AI 코딩을 하다 보면 묘하게 자신감이 붙어요. 답변도 빠르고, 코드도 그럴듯하고, 가끔은 제가 생각 못 한 방향까지 제안하니까요. 그런데 그럴수록 한 발짝 물러서서 봐야 합니다. 특히 Supabase RLS처럼 보안과 직결되는 부분은 더더욱 그래요.
예전에 AI가 만들어준 RLS 정책 중에 이런 게 있었어요. 로그인한 사용자가 자기 데이터만 수정할 수 있어야 했는데, USING 조건은 맞게 만들고 WITH CHECK 조건을 빼먹은 겁니다. SELECT나 UPDATE 조건만 보고 “어, 맞네?” 하고 넘어갔으면 꽤 위험했을 거예요.
그래서 저는 AI가 생성한 Supabase 관련 코드는 보통 아래 순서로 봅니다.
- Supabase SQL Editor에서 먼저 실행해보기
문법 오류가 있는지, 기존 정책과 충돌하지 않는지 확인합니다. - 테스트 계정으로 권한 확인하기
일반 사용자, 다른 사용자, 관리자 계정을 나눠서 SELECT, INSERT, UPDATE, DELETE를 직접 확인합니다. - RLS 정책 이름과 조건 읽어보기
AI가 정책 이름을 그럴듯하게 짓는데, 실제 조건과 이름이 안 맞을 때가 있어요. - 실패한 프롬프트는 따로 적어두기
“column user_id does not exist” 같은 에러는 다음 프롬프트를 고치는 좋은 힌트가 됩니다.
여기서 귀찮다고 테스트를 건너뛰면 나중에 더 귀찮아집니다. 이건 제가 정말 여러 번 당해봐서 하는 말이에요. 특히 RLS는 “안 되는 것처럼 보이는 버그”보다 “너무 잘 되는 보안 문제”가 더 무섭습니다. 다른 사람 데이터가 보이는데 에러는 안 나니까요.
토큰을 아끼려면 나만의 프롬프트 메모장이 있어야 해요
AI 코딩을 오래 쓰다 보니, 결국 생산성 차이는 도구보다 내가 반복 작업을 얼마나 잘 정리해두느냐에서 나더라고요. 저는 지금 프로젝트마다 작은 프롬프트 메모장을 하나씩 둡니다. 거창한 문서가 아니라, 자주 쓰는 요청 문장과 프로젝트 규칙을 적어둔 파일이에요.
예를 들면 이런 식입니다.
프로젝트 규칙
- Supabase 사용
- PostgreSQL 기준 SQL 작성
- RLS는 기본적으로 활성화
- 관리자 판별 함수: public.is_admin()
- 소유자 판별은 user_id = auth.uid() 기준
- Edge Functions는 Deno 런타임
- TypeScript에서는 Database 타입 사용
- 불필요한 설명보다 실행 가능한 코드 우선
이걸 매번 전부 붙여 넣는 건 아니고요. 작업에 필요한 부분만 가져다 씁니다. 그래도 이렇게 기준 문장이 있으니까 질문이 빨라져요. 그리고 이상하게도, 프롬프트 메모장을 만들면 제 생각도 같이 정리됩니다. “내가 뭘 만들려고 했더라?” 하는 순간이 줄어들어요.
제가 실제로 정착한 하루 작업 흐름
거창한 루틴은 아닙니다. 그냥 퇴근 후에 1~2시간 정도 사이드 프로젝트 만질 때 쓰는 방식이에요. 그런데 이 흐름으로 하니까 AI와 주고받는 횟수가 확 줄었습니다.
- Supabase Studio에서 테이블 구조를 먼저 확인합니다.
AI에게 맡기기 전에 제가 먼저 컬럼명, 관계, RLS 활성화 여부를 봅니다. - 작업에 필요한 스키마만 복사합니다.
전체 덤프를 넣지 않고, 지금 필요한 테이블과 컬럼만 가져옵니다. - 프롬프트 템플릿에 맞춰 요청합니다.
RLS, Edge Functions, SQL 최적화 요청을 각각 다른 틀로 씁니다. - AI 답변은 바로 믿지 않고 실행해봅니다.
SQL Editor, 로컬 테스트, 테스트 계정으로 꼭 확인합니다. - 에러 로그를 다음 프롬프트에 반영합니다.
에러 메시지를 숨기지 않고 그대로 넣으면 AI가 훨씬 잘 고칩니다.
처음 2주 정도는 오히려 더 느린 느낌도 있었어요. 템플릿 만들고, 실패 사례 적고, 테스트 계정 나누는 게 번거로웠거든요. 그런데 조금 지나니까 확실히 편해졌습니다. 반복 SQL이나 기본 API 코드는 AI가 꽤 많이 도와주고, 저는 구조와 보안, 성능 쪽에 시간을 더 쓰게 됐어요.
이게 제가 생각하는 AI 바이브 코딩의 좋은 방향입니다. AI가 다 해주는 게 아니라, AI가 반복 작업을 줄여주고 사람은 판단에 집중하는 것. 이 정도 거리감이 딱 좋더라고요.
이런 분들이 읽으면 꽤 도움이 될 거예요
Supabase를 막 도입하려는 분, AI 코딩 도구로 사이드 프로젝트를 빠르게 만들고 싶은 분, RLS 정책 때문에 한 번쯤 머리 싸매본 분이라면 이 글이 꽤 현실적인 도움이 될 거라고 생각해요. 특히 “AI가 코드는 만들어주는데, 왜 자꾸 한 번에 안 되지?” 하고 답답했던 분들에게요.
제 경험상 핵심은 단순합니다. AI에게 모든 걸 길게 설명하려고 하지 말고, 필요한 맥락을 짧고 정확하게 주는 것. 그리고 프로젝트 안에 공통 함수, 트리거, 타입 파일 같은 기준점을 만들어두는 것. 이 두 가지만 해도 토큰 사용량이 줄고, 답변 품질도 꽤 좋아집니다.
20년 넘게 개발을 해도 새로운 도구 앞에서는 또 배울 게 생깁니다. 예전에는 공식 문서와 Stack Overflow를 뒤지며 하나씩 맞춰갔다면, 이제는 AI와 대화하면서 빠르게 초안을 만들고 제가 검증하는 방식으로 바뀌고 있어요. 나쁘지 않습니다. 오히려 꽤 즐거워요.
다만, AI가 만든 코드를 그대로 믿고 배포하는 건 아직 제 취향은 아닙니다. 특히 데이터베이스, 인증, 권한, 비용과 관련된 부분은 사람이 봐야 해요. AI는 좋은 동료지만, 책임자는 아니니까요.
이 글을 읽는 분들도 자기 프로젝트에 맞는 Supabase 프롬프트 템플릿을 하나씩 만들어보면 좋겠습니다. 처음엔 조금 귀찮아도, 나중엔 정말 든든해요. 뭐랄까, 자주 가는 동네 식당에서 내 입맛 아는 사장님 만난 느낌이랄까요. AI도 그렇게 길들여가면 훨씬 편해집니다.
이 글은 실제 Supabase 프로젝트 작업과 ChatGPT, GitHub Copilot을 함께 사용하면서 겪은 경험을 바탕으로 정리했습니다.
댓글
댓글 쓰기