Claude Code를 활용한 명세 기반 개발 (Spec-Driven Development): 처음부터 Nuxt 앱 구축하기 — 3부작 중 1부
요약
본 글은 AI가 생성한 코드의 신뢰성 문제(‘거의 맞지만 완전히 정확하지 않은’ 코드)를 지적하며, 이를 해결하기 위한 방법론으로 '명세 기반 개발(Spec-Driven Development, SDD)'을 제안합니다. SDD는 코드를 작성하기 전에 목표, 비목표, 수락 기준 등을 포함한 구조화된 명세를 먼저 작성하고, 이 명세를 기준으로 코드를 생성하는 방식입니다. 3부작 시리즈를 통해 Nuxt 3와 Supabase를 활용하여 실제 앱(Catálogo Guayoyo)을 구축하며 SDD 워크플로우를 적용할 예정입니다.
핵심 포인트
- AI 코드의 한계: 개발자들은 AI가 생성한 코드가 완벽하지 않다는 문제에 직면해 있습니다.
- 명세 기반 개발 (SDD): 명세를 주요 산출물(artifact)로 삼고, 코드는 그 컴파일 대상(compilation target)으로 간주하는 방법론입니다.
- SDD의 장점: 코드 동작 방식에 대한 '계약(contract)'을 먼저 정의하여 패턴 드리프트나 취약점을 방지합니다.
- Vibe Coding vs SDD: 단순한 프롬프트를 사용하는 Vibe Coding과 달리, SDD는 구조화된 명세서를 통해 프로덕션 환경의 신뢰성을 확보합니다.
- 실습 프로젝트: Nuxt 3, Supabase, Tailwind CSS를 사용하여 실제 앱을 구축하는 과정을 통해 SDD 워크플로우를 학습할 수 있습니다.
시리즈: Claude Code를 활용한 명세 기반 개발 (Spec-Driven Development) — 1부 · 2부 · 3부
개발자의 66%는 AI에 대한 가장 큰 불만 사항이 "거의 맞지만, 완전히 정확하지는 않은" 코드라고 말합니다 (Stack Overflow Developer Survey 2025). Cloud Security Alliance의 연구에 따르면 AI가 생성한 코드의 45%가 OWASP Top 10 취약점을 유발하는 것으로 나타났습니다. METR의 무작위 대조 시험(randomized controlled trial)에 따르면, 개발자들은 AI 도구를 사용할 때 속도가 24% 향상될 것이라고 예측했음에도 불구하고 실제로는 19% 더 느려졌습니다.
문제는 모델이 아닙니다. 무엇을 요청해야 할지 정확히 모르는 것이 문제입니다. Claude Code에게 "Supabase를 사용한 로그인 기능을 만들어줘"라고 말하면 작동하는 코드를 얻을 수 있습니다. 하지만 그것이 당신의 프로젝트 패턴을 사용하고 있습니까? 당신이 기대하는 방식으로 에러를 처리합니까? 라우트(routes)를 올바르게 보호합니까? 그 대답이 "거의"입니까?
명세 기반 개발 (Spec-Driven Development, SDD)은 간단한 원칙으로 이 문제를 해결합니다: 명세(specification)가 주요 산출물(artifact)입니다. 코드는 그 컴파일 대상(compilation target)입니다.
명세 기반 개발 (Spec-Driven Development)이란 무엇인가?
SDD는 단 한 줄의 코드를 생성하기 전에 목표, 비목표(non-goals), 수락 기준(acceptance criteria), 제약 조건(constraints)을 포함한 구조화된 명세를 작성하는 방법론입니다. 명세는 코드와 함께 버전 관리되며 저장소(repository)에 존재합니다. 무언가 변경되면, 명세를 업데이트하고 다시 생성합니다.
"먼저 코딩하고 나중에 문서를 작성하는 대신, 명세 기반 개발에서는 명세로 시작합니다. 이것은 당신의 코드가 어떻게 동작해야 하는지에 대한 계약(contract)입니다." — Den Delimarsky, GitHub Blog
이것은 폭포수(waterfall) 모델이 아닙니다. AI 에이전트를 정글에 떨어뜨리기 전에 지도를 주는 것입니다.
SDD vs Vibe Coding:
| 구분 | Vibe Coding | Spec-Driven Development (SDD) |
|---|---|---|
| 주요 산출물 (Primary artifact) | 프롬프트 (The prompt) | 명세서 (The specification) |
| 신뢰의 원천 (Source of truth) | 생성된 코드 (Generated code) | 명세서 (The spec) |
| 적합한 용도 (Best for) | 프로토타입, 탐색 (Prototypes, exploration) | 인증, 결제, 데이터, 프로덕션 (Auth, payments, data, production) |
| 실패 모드 (Failure mode) | 패턴 드리프트, "거의 맞음" (Pattern drift, "almost right") | 과잉 명세 (Over-specification) (완화 가능) |
| 반복 루프 (Iteration loop) | 작동할 때까지 다시 프롬프트 입력 | 명세 수정 → 코드 재생성 |
RemyBuilds가 언급했듯이, "구조가 분위기(vibes)를 이깁니다(Structure beats vibes)."
구축할 내용
이 3부작 시리즈에서는 단순하지만 완전한 앱인 Catálogo Guayoyo를 구축할 것입니다:
✅ 이메일/비밀번호 로그인 (Supabase Auth)
✅ API를 통한 제품 카탈로그
✅ 미들웨어(middleware)를 사용한 보호된 경로
✅ Tailwind CSS UI
✅ 프로덕션 배포
이 모든 과정은 SDD 워크플로우를 따르며, Nuxt 3, Supabase, Tailwind CSS, 그리고 개발 파트너로서의 Claude Code를 사용합니다.
사전 요구 사항
- Node.js 20 이상
- Claude Code CLI 설치 및 인증 완료
- 무료 Supabase 계정
- VS Code (권장 사항, 필수 아님)
- 추측하기를 멈추려는 의지
1단계: Nuxt 프로젝트 초기화
터미널을 엽니다:
npx nuxi@latest init catalogo-guayoyo
cd catalogo-guayoyo
npm install
이 명령은 깨끗한 Nuxt 3 프로젝트를 생성합니다. 아직 종속성(dependencies)을 수동으로 설치하지 않을 것입니다. Claude Code가 명세(spec)를 읽을 때 이를 처리하도록 할 것입니다.
2단계: CLAUDE.md — 프로젝트 헌법
어떤 기능에 대한 명세를 작성하기 전에, CLAUDE.md 파일이 필요합니다. 이 파일은 Claude Code가 매 세션 시작 시 읽는 파일로, 프로젝트의 타협할 수 없는 규칙들을 정의합니다.
프로젝트 루트에 CLAUDE.md를 생성합니다:
CLAUDE.md — Catálogo Guayoyo
Stack (스택)
- Frontend (프론트엔드): Nuxt 3 (Vue 3 Composition API + TypeScript)
- Styling (스타일링): Tailwind CSS
- Backend/Auth (백엔드/인증): Supabase (auth + database + API)
- Hosting (호스팅): Vercel (권장)
Architecture Rules (아키텍처 규칙)
- 항상
<script setup lang="ts">와 함께 Composition API를 사용합니다. - 모든 Supabase 호출은
@supabase/supabase-js클라이언트를 사용합니다. - 환경 변수 (Environment variables)는
.env에 저장합니다 (절대 하드코딩하지 마세요). - 보호된 라우트 (Protected routes)는 Nuxt 미들웨어 (
defineNuxtRouteMiddleware)를 사용합니다. - Supabase 데이터베이스로부터 타입을 생성합니다 (
supabase gen types).
Code Style (코드 스타일)
- 엄격한 TypeScript (tsconfig에서
strict: true설정) - 컴포넌트 이름: PascalCase
- Composables (컴포저블):
use접두사 사용 (예:useAuth,useProducts) any사용 금지 — 타입을 확신할 수 없다면unknown을 사용하고 타입을 좁힙니다 (narrowing).
What We DON'T Do (하지 않는 것들)
- Options API 사용 금지
- axios 사용 금지 (Nuxt의
$fetch또는 Supabase 클라이언트 사용) - 다른 인증 제공자 사용 금지 (Supabase Auth만 사용)
- 별도의 API 서버 구축 금지 — Supabase가 백엔드입니다.
이 파일은 SDD (명세 기반 개발)의 첫 번째 단계입니다. 여러분은 Claude Code에게 무엇을 만들지 말하기 전에 어떻게 일해야 하는지를 알려주고 있는 것입니다. 향후 모든 세션은 이 컨텍스트 (context)를 상속받습니다.
💡 Context Engineering (컨텍스트 엔지니어링): Shopify의 CEO Tobi Lütke와 Andrej Karpathy는 2025년 6월, 이틀 간격으로 이 용어를 만들어냈습니다. 이는 "다음 단계를 위해 컨텍스트 윈도우 (context window)를 딱 적절한 정보로 채우는 섬세한 기술이자 과학"을 의미합니다. CLAUDE.md는 여러분의 첫 번째 컨텍스트 엔지니어링 레이어입니다.
단계 3: 명세 (Specification) 작성하기
이제 우리 앱을 위한 명세를 작성합니다.
specs/catalogo-guayoyo.md 생성:
명세 (Spec): Catálogo Guayoyo v1.0
목표 (Goals)
- 사용자는 이메일과 비밀번호로 회원가입을 할 수 있다
- 사용자는 로그인 및 로그아웃을 할 수 있다
- 인증된 사용자는 제품 카탈로그를 볼 수 있다
- 제품은 API로부터 가져온다
- 세션이 존재하지 않으면 보호된 경로 (Protected routes)는 로그인 페이지로 리다이렉트한다
비목표 (Non-Goals, 범위 외)
- 비밀번호 복구 (v2)
- 소셜 로그인 (Google, GitHub — v2)
- 장바구니 (v2)
- 관리자 대시보드 (v2)
- 사용자 역할 (v2)
사용자 스토리 (User Stories)
US-1: 회원가입 (Registration)
사용자로서 새로운 방문자인 나는
이메일과 비밀번호로 계정을 생성하고 싶다
그 결과로 제품 카탈로그에 접근할 수 있어야 한다
수락 기준 (Acceptance criteria):
- [AC-1.1] 필드 구성: 이메일, 비밀번호, 비밀번호 확인
- [AC-1.2] 유효성 검사 (Validation): 유효한 이메일, 비밀번호 8자 이상, 일치 여부
- [AC-1.3] 회원가입 성공 시, /products로 리다이렉트
- [AC-1.4] 이메일이 이미 존재하는 경우, "이미 등록된 이메일입니다" 메시지 표시
- [AC-1.5] 비밀번호는 UI에 절대 평문 (plain text)으로 표시되지 않는다
US-2: 로그인 (Sign In)
사용자로서 등록된 사용자인 나는
이메일과 비밀번호로 로그인하고 싶다
그 결과로 제품 카탈로그를 볼 수 있어야 한다
수락 기준 (Acceptance criteria):
- [AC-2.1] 필드 구성: 이메일, 비밀번호
- [AC-2.2] 로그인 시, /products로 리다이렉트
- [AC-2.3] 잘못된 자격 증명 (credentials) 입력 시, 일반적인 에러 메시지 표시
- [AC-2.4] 페이지 새로고침 시에도 세션이 유지된다
US-3: 제품 카탈로그 (Product Catalog)
사용자로서 인증된 사용자인 나는
이름, 가격, 이미지가 포함된 제품 목록을 보고 싶다
그 결과로 카탈로그를 둘러볼 수 있어야 한다
수락 기준 (Acceptance criteria):
- [AC-3.1] 반응형 그리드 (모바일 1열, 태블릿 2열, 데스크톱 3열)
- [AC-3.2] 각 제품 표시 항목: 이미지, 이름, 가격, 카테고리
- [AC-3.3] 데이터 페칭 (fetching) 중 로딩 상태 (Loading state) 표시
- [AC-3.4] 제품이 없는 경우 빈 상태 (Empty state) 표시
- [AC-3.5] API 호출 실패 시 에러 상태 (Error state) 표시
US-4: 로그아웃 (Sign Out)
사용자로서 인증된 사용자인 나는
로그아웃을 하고 싶다
그 결과로 내 계정을 보호할 수 있어야 한다
수락 기준 (Acceptance criteria):
- [AC-4.1] 내비게이션에 "로그아웃" 버튼이 표시된다
- [AC-4.2] 로그아웃 시, /login으로 리다이렉트
- [AC-4.3] /products에 접근할 수 없다
세션 없이 ### US-5: 보호된 라우트 (Protected Routes) 사용자로서 나는 인증이 필요한 라우트를 보호하기를 원한다 그 결과 인증된 사용자만이 카탈로그에 접근할 수 있다. 수락 기준 (Acceptance criteria): - [AC-5.1] /products는 인증이 필요함 - [AC-5.2] 이미 인증된 경우 /login 및 /register는 /products로 리다이렉트됨 - [AC-5.3] 재사용 가능한 인증 미들웨어 (페이지별이 아닌 방식) ## 기술적 제약 사항 (Technical Constraints) - 프레임워크: Nuxt 3 (Composition API + 엄격한 TypeScript) - 스타일링: Tailwind CSS - 인증: Supabase Auth (email/password 프로바이더) - 데이터: Supabase의 products 테이블, 필드 구성: id, name, price, category, image_url, created_at - 제품 API: Supabase의 REST 엔드포인트 ( /rest/v1/products ) - 외부 상태 관리 사용 금지 (Pinia 미사용) — composables로 충분함 - 환경 변수: .env 파일 내 SUPABASE_URL, SUPABASE_ANON_KEY ## 데이터 스키마 (Data Schema) ### 테이블: products | 필드 | 타입 | 설명 | |-------|------|-------------| | id | uuid (PK) | 고유 식별자 | | name | text | 제품 이름 | | price | numeric(10,2) | USD 기준 가격 | | category | text | 카테고리 (예: "Electronics") | | image_url | text | 이미지 URL | | created_at | timestamptz | 생성 날짜 | Step 4: Claude Code를 이용한 스캐폴드 (Scaffold) 생성 명세서가 작성되고 CLAUDE.md가 준비되었으므로, Claude Code에게 구조 생성을 요청합니다:
claude
Claude Code 내부 명령:
@specs/catalogo-guayoyo.md 및 @CLAUDE.md를 읽으세요. 명세에 따라 Nuxt 3 프로젝트 스캐폴드를 생성하세요: 의존성(@supabase/supabase-js, @nuxtjs/tailwindcss)을 설치하고, Tailwind를 구성하며, Supabase 환경 변수를 설정하고, 필요한 컴포넌트(components), 페이지(pages), 컴포저블(composables)을 포함한 파일 구조를 생성하세요. 아직 로직을 구현하지 마세요 — 오직 스캐폴드만 생성하세요.
Claude Code는 파일을 읽고 다음을 생성합니다:
catalogo-guayoyo/
├── CLAUDE.md
├── specs/
│ └── catalogo-guayoyo.md
├── .env
├── nuxt.config.ts
├── pages/
│ ├── index.vue # /products 또는 /login으로 리다이렉트
│ ├── login.vue # 로그인 양식
│ ├── registro.vue # 회원가입 양식
│ └── productos.vue # 제품 카탈로그
├── composables/
│ ├── useAuth.ts # 인증 (Authentication) 로직
│ └── useProducts.ts # 제품 페칭 (Product fetching)
├── middleware/
│ └── auth.ts # 경로 보호 미들웨어 (Route protection middleware)
├── components/
│ ├── ProductCard.vue # 개별 제품 카드
│ ├── ProductGrid.vue # 제품 그리드
│ └── AppHeader.vue # 네비게이션 및 로그아웃이 포함된 헤더
└── server/
└── (비어 있음 — Supabase가 백엔드 역할을 수행함)
1부에서 달성한 내용
20분 만에 여러분은 막연한 아이디어("제품이 있는 로그인 기능")에서 다음과 같은 상태로 나아갔습니다:
✅ 엄격한 TypeScript (TypeScript)가 설정된 Nuxt 3 프로젝트
✅ 프로젝트 규칙을 영구적으로 정의하는 CLAUDE.md
✅ 5개의 사용자 스토리 (User stories)와 16개의 수락 기준 (Acceptance criteria)을 포함한 완전한 명세서
✅ Tailwind CSS, Supabase 클라이언트 및 환경 변수 설정 완료
✅ 구현 준비가 된 파일 구조
그리고 여러분은 아직 비즈니스 로직을 단 한 줄도 작성하지 않았습니다.
이것이 바로 SDD(명세 기반 개발)입니다. 먼저 무엇을 어떻게 할지 알고, 그 다음에 코드를 작성하는 것입니다. 그 반대가 아닙니다.
참고: Birgitta Boeckeler는 세 가지 SDD 성숙도 단계인 spec-first(명세 우선), spec-anchored(명세 고정), spec-as-source(명세 중심)를 정의합니다. 우리는 가장 가볍고 대부분의 팀이 시작하는 단계인 spec-first 단계에 있습니다. AI에게 코드를 요청하기 전에 명세를 먼저 작성하세요. 단순하지만 효과적입니다.
2부: 인증 및 데이터(Auth & Data)에서는 Supabase Auth를 구현하고, 제품 테이블을 생성하며, Claude Code가 자동화된 TypeScript 타입을 사용하여 데이터 액세스 계층 (Data access layer)을 생성하도록 할 것입니다.
참고 문헌: Spec-Driven Development: Structure Beats Vibes — RemyBuilds (dev.to) Spec-Driven Development: The Definitive 2026 Guide — BCMS Exploring Generative AI: Spec-Driven Development — Martin Fowler (Boeckeler) Spec-Driven Development with AI: Get Started with Spec Kit — GitHub Blog Spec-Driven Development with Claude Code in Action — Alex Op Stack Overflow 2025 Developer Survey CSA Research Note: AI-Generated Code Vulnerability Surge 2026
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기