File-based Planning Workflow 파트 1: 스펙과 계획으로 코딩 에이전트와 협업하기
문제: AI는 추측해서 만듭니다
코딩 에이전트에게 "로그인 기능 만들어주세요"라고 요청하면 무슨 일이 벌어질까요?
코딩 에이전트는 추측해서 만듭니다.
- 이메일 로그인인지, 소셜 로그인인지
- 세션 기반인지, JWT인지
- 실패하면 계정 잠금이 있는지 없는지
- 패스워드 재설정은 어떻게 할지
우리가 정하지 않은 것을 AI가 정합니다.
그 결과는 우리가 원하는 것과 다를 확률이 높습니다.
해법: 스펙을 먼저 명시합니다
코딩 에이전트와 작업할 때 가장 먼저 할 일은 무엇을 만들지 명확히 하는 것입니다.
FBPW(File-based Planning Workflow)는 사람과 AI 모두 읽고 쓸 수 있는 파일로 이 문제를 해결합니다.1
specs/login-feature/
├── README.md # 개요
├── spec.md # 요구사항 (스펙)
├── plan.md # 구현 계획
├── tasks.md # 작업 목록
├── findings.md # 발견과 결정
└── progress.md # 진행 기록
1단계: README.md — 배경과 목표
왜 이 기능이 필요한지, 무엇을 달성하려 하는지, 어떻게 동작하는지를 간단히 정리합니다.
프로젝트의 전체 그림을 한눈에 파악할 수 있습니다.
# 사용자 로그인
## Background
현재 인증 없이 모든 API가 열려 있습니다.
사용자별 데이터 보호가 필요합니다.
## Goal
이메일/패스워드 기반 로그인 시스템을 구축하여 사용자 인증을 제공합니다.
## How it works
1. 사용자가 이메일/패스워드 입력
2. 서버가 JWT 발급
3. 클라이언트가 JWT를 헤더에 포함해 API 요청
2단계: spec.md — 요구사항 정의
이 파일이 가장 중요합니다.
사용자 스토리, 인수 시나리오, 기능 요구사항, 제약사항, 성공 기준을 명확히 적습니다.
AI 도구로 초안을 작성하고, 리뷰하고, 수정 요청하는 작업을 반복해서 완성해 나갑니다.
# Feature Specification: 사용자 로그인
## User Scenarios & Testing (mandatory)
### User Story 1: 로그인 성공
- As: 가입한 사용자로서
- I: 이메일/패스워드로 로그인할 수 있습니다
- So: 인증이 필요한 기능을 사용하기 위해
#### Acceptance Scenarios
Scenario 1: **이메일과 패스워드가 올바를 때**
- Given: 이메일 `user@example.com`, 패스워드 `pass123`인 사용자가 있을 때
- When: 해당 이메일/패스워드로 로그인 요청하면
- Then: JWT 토큰이 반환됩니다
### User Story 2: 로그인 실패
Scenario 1: 패스워드가 틀렸을 때
- Given: 가입된 이메일이 있을 때
- When: 잘못된 패스워드로 로그인 시도하면
- Then: 401 에러와 "이메일 또는 패스워드가 올바르지 않습니다" 메시지 반환됩니다
## Functional Requirements (mandatory)
- FR-1: MUST 이메일/패스워드 기반 로그인
- FR-2: MUST JWT 토큰 발급 (유효기간 24시간)
- FR-3: MUST 로그인 5회 실패하면 계정 10분 잠금
- FR-4: SHOULD 패스워드 재설정 (이메일 링크)
## Constraints (mandatory)
- CON-1: MUST Argon2로 패스워드 해싱
- CON-2: MUST 토큰은 httpOnly 쿠키로 전달
## Success Criteria (mandatory)
- SC-1: 올바른 이메일, 패스워드로 로그인하면 JWT 발급
- SC-2: JWT로 보호된 API 접근 가능
- SC-3: 5회 실패하면 10분 잠금 동작 확인
여기까지 스펙을 작성했으면 바로 구현을 요청해도 됩니다.
우리는 개발자니까, 사전 기술 검토를 위해 구현 계획도 세워봅니다.
3단계: plan.md — 구현 계획
spec.md를 바탕으로 어떻게 만들지 계획을 세웁니다.
주요 구현 방식, 주요 파일 목록, 아키텍처, 구현 단계를 작성합니다.
# Implementation Plan: 사용자 로그인
## Summary
Kotlin + Spring Boot로 JWT 기반 인증 시스템을 구현합니다.
Outside-In TDD로 개발합니다 (Controller → Application Service → Domain Model).
## Requirements
1. 이메일/패스워드 로그인
2. JWT 발급 (24시간 유효)
3. 5회 실패하면 10분 잠금
## Critical Files
### New Files
- `src/main/kotlin/.../controller/SessionController.kt` — API 엔드포인트
- `src/main/kotlin/.../application/LoginService.kt` — 로그인 유스케이스 조율
- `src/main/kotlin/.../models/User.kt` — 사용자 모델
- `src/main/kotlin/.../models/JwtService.kt` — JWT 도메인 서비스
### Modified Files
- `build.gradle.kts` — jjwt, spring-security-crypto 의존성 추가
### Reference Files
- `src/main/kotlin/.../config/SecurityConfig.kt` — 기존 보안 설정
## Architecture
### User Flow
```text
POST /api/session
→ SessionController
→ LoginService
→ User.verifyPassword()
→ JwtService.generateToken()
→ JWT 반환
```
## Implementation Steps
### Step 1: SessionController (UI Layer)
**Red:** `SessionControllerTest.kt`
```kotlin
// POST /api/session 요청 → JWT 응답 테스트
```
**Green:** `SessionController.kt`
```kotlin
@RestController
class SessionController(private val loginService: LoginService) {
@PostMapping("/api/session")
@ResponseStatus(HttpStatus.CREATED)
fun create(@RequestBody request: LoginRequest): String
}
```
### Step 2: LoginService (Application Layer)
**Red:** `LoginServiceTest.kt`
```kotlin
// 로그인 성공/실패, 계정 잠금 테스트
```
**Green:** `LoginService.kt`
```kotlin
@Service
class LoginService(private val userRepository: UserRepository) {
fun login(email: String, password: String): String
}
```
### Step 3: User (Domain Layer)
**Red:** `UserTest.kt`
```kotlin
// 패스워드 해싱(Argon2), 검증 테스트
```
**Green:** `User.kt`
```kotlin
@Entity
class User(val email: String) {
fun hashPassword(password: String): String
fun verifyPassword(password: String): Boolean
}
```
## Verification
### Build
```bash
./gradlew build
```
### Test
```bash
./gradlew test
```
### Manual Test
```bash
curl -X POST http://localhost:8080/api/session \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"pass123"}'
```
왜 파일인가요?
데이터베이스도, 클라우드도 아닌 마크다운 파일을 쓰는 이유:
- 코딩 에이전트가 읽고 쓸 수 있습니다 → 추가 도구 불필요
- Git으로 버전 관리합니다 → 변경 이력 추적
- 사람도 쉽게 편집합니다 → IDE에서 바로 수정
- 인프라가 불필요합니다 → 파일 시스템이면 충분
파일 시스템은 사람과 코딩 에이전트가 함께 읽고 쓸 수 있는 가장 단순한 저장소입니다.
정리
파트 1에서 다룬 내용:
- 스펙을 먼저 명시합니다 (
spec.md) → AI가 추측하지 않도록 - 계획을 뽑습니다 (
plan.md) → 구현 전에 방향 설정
구현하면서 맥락을 유지하는 방법(tasks.md, findings.md, progress.md)은 파트
2에서 다룹니다.
템플릿: https://github.com/ahastudio/file-based-planning-workflow
실제 예제: https://github.com/ahastudio/CodingLife/tree/main/20260213/react/specs/calendar
Footnotes
-
File-based Planning Workflow(FBPW)는 코딩 에이전트와의 협업을 위한 파일 기반 패턴입니다. 스펙 명시와 기록을 통해 체계적인 개발을 가능하게 합니다. ↩