~/VibeHandbook
$39

챕터 12 · 02

사례 연구 2: Auth + 데이터베이스가 있는 작은 SaaS 도구

아이디어

한 프리랜스 디자이너가 클라이언트별 청구 가능 시간을 기록하고 월간 CSV로 내보내는 비공개 대시보드를 원했다. 화려할 것은 없지만, 계정(그래서 그녀의 데이터가 오롯이 그녀 것이 되도록)과 영속성이 필요했다.

스펙

Auth와 데이터베이스는 판돈을 올리므로, 스펙은 경계에 대해 더 구체적이 됐다:

로그인한 웹 앱으로, 사용자가 다음을 할 수 있다: 이메일로 가입/로그인,
클라이언트 생성, 시간 항목 기록(날짜, 클라이언트, 시간, 메모),
월별로 필터링된 항목 테이블 보기, 그 달을 CSV로 다운로드.
각 사용자는 오직 자기 데이터만 본다. 모바일 친화적으로.

"각 사용자는 오직 자기 데이터만 본다"는 UX 줄처럼 보인다. 그것은 사실 앱 전체의 보안 모델을 아홉 단어로 압축한 것이다. 스펙에서 그것을 명명한 덕에, AI가 표류할 때마다 우리는 거기로 되짚어 가리킬 수 있었다.

스택

1인 빌더에게 이기는 수는 auth와 데이터베이스가 당신이 작성하는 코드가 아니라 매니지드 서비스인 스택이다. 우리는 서버리스 호스트에 배포한 Next.js 앱에, 호스팅된 Postgres 데이터베이스와, 이메일 로그인·세션·비밀번호 재설정을 대신 처리해주는 드롭인 auth 공급자를 선택했다. 잘못될 코드가 적다는 것은 AI가 우리를 대신해 잘못 만들 코드가 적다는 뜻이다. 특히 auth는 AI와 함께 직접 만들고 싶은 마음이 거의 들지 않는 부류다: 실패 양태가 조용하고, 폭발 반경이 모두의 계정이며, 매니지드 공급자는 수백만 번의 로그인으로 엣지 케이스를 두들겨 맞아왔다.

핵심 프롬프트

auth 공급자 자체의 템플릿이 무거운 작업을 하게 두고, 그 위에 도메인 로직을 얹도록 AI를 지시했다:

우리는 [auth 공급자]의 Next.js 스타터를 쓰고 있다. 두 테이블을
가진 Postgres 스키마를 추가하라: clients (id, user_id, name)와
time_entries (id, user_id, client_id, date, hours, note). 모든
쿼리는 반드시 세션의 로그인한 사용자 id로 필터해야 한다.
마이그레이션과 타입이 지정된 데이터 접근 함수를 생성하라.

"반드시 user_id로 필터하라"는 줄이 프로젝트 전체에서 가장 중요한 문장이었다. 우리는 데이터를 건드리는 거의 모든 프롬프트에서 그 제약을 반복했는데, 다중 사용자 앱에서 가장 무서운 단일 버그가 한 사용자가 다른 사용자의 행을 보는 것이기 때문이다. 타이핑할 때는 반복이 군더더기처럼 느껴진다; 바로 그 군더더기가 당신을 구하는데, 모델은 별개의 프롬프트들 사이에서 그 제약이 얼마나 중요한지에 대한 기억이 없기 때문이다.

내보내기에 대해서는:

한 달(YYYY-MM)을 받아, 그 달에 대한 로그인한 사용자의
time_entries를 클라이언트 이름과 조인해 끌어오고, CSV 다운로드를
스트리밍하는 /api/export 라우트를 추가하라. 유효한 세션이
없으면 요청을 거부하라.

장애물

테스트하면서 우리는 계정 두 개를 만들었고, 계정 B가 드롭다운에서 계정 A의 클라이언트를 볼 수 있음을 발견했다. 이것이 바로 우리가 두려워한 버그다. AI에게 "고쳐"라고 요청하기보다, 먼저 문제를 증명하게 만들었다:

계정 B가 계정 A의 클라이언트를 보고 있다. 코드베이스에서
clients 테이블을 읽는 모든 데이터베이스 쿼리를 보여주고, 각각에
대해 세션 user_id로 필터하는지 알려달라. 아직 아무것도 고치지
말고 — 감사만 하라.

감사 결과 쿼리 하나 — 드롭다운 로더 — 가 드러났는데, 그것은 우리가 제약을 추가하기 전에 작성되어 빠져나갔던 것이었다. 우리는 누락된 필터를 추가하게 한 다음, 가드를 요청했다:

모든 읽기가 거쳐 가는 단일 헬퍼를 추가하라. 세션을 받아
user_id 필터를 주입해서, 미래의 어떤 쿼리도 그것을 잊을 수 없게.
기존 쿼리들을 이걸 쓰도록 리팩터링하라.

그것이 일회성 수정을 구조적 보장으로 바꿨다. 그런 다음 우리는 그 보장을 테스트 가능하게 만들었는데, 검증할 수 없는 가드는 그저 희망에 불과하기 때문이다:

사용자 두 명을 만들고, 각자가 클라이언트를 하나씩 생성하게 한 뒤,
사용자 A의 세션이 어떤 데이터 접근 함수를 통해서도 사용자 B의
클라이언트를 절대 읽을 수 없음을 단언하는 테스트를 작성하라.

교훈: AI가 보안 버그를 들여오면, 그 인스턴스만 패치하지 말라 — 그 실수의 부류를 제거하도록 지시한 다음, 누군가 그것을 다시 열면 시끄럽게 실패하는 테스트로 그 부류를 잠가버려라.

출시

우리는 테스트용 한 달치 데이터를 시드하고, CSV를 내보내고, 스프레드시트에서 열어 숫자와 인코딩이 맞는지 확인한 뒤, 강력한 데이터베이스 비밀번호를 설정하고 자격 증명을 어떤 로컬 파일에서도 빼내 교체했다. 서버리스 호스트에 배포하고, 그 대시보드에 프로덕션 환경 변수를 추가하고, 그녀에게 URL을 줬다. 그녀는 진짜 가입으로 스스로 온보딩했다. 전체 빌드는 주말 하나였다.

오프라인으로 보고 싶으세요?

PDF + EPUB + 다운로드형 프롬프트 라이브러리 + 버전 업데이트를 받으세요.

$ PDF 받기 — $39