AI 바이브 코딩 프롬프트 - 백엔드편

jiiim_ni·2026년 5월 19일

백엔드 바이브코딩 가이드

CH5 팀 프로젝트때 티켓팅 사이트 구현했는데
그때 튜터님이 알려주셨던 바이브 코딩 가이드 정리!
(내가 나중에 보려고)

백엔드 바이브코딩 프롬프트 가이드(TicketFlow)

SA 문서 10종을 기반으로 Claude Code에게 백엔드 코드를 생성시키는 프롬프트 작성 가이드
기술 스택: Java 17 + Spring Boot 3.x + JPA + MySQL 8 + Redis + Docker


1. 바이브코딩으로 백엔드를 만든다는 것

바이브코딩은 AI에게 "이런 걸 만들어줘"라고 자연어로 지시하면 코드를 생성하는 방식입니다. 여러분은 이미 SA 문서 10종(프로젝트 개요서, ERD, API 명세서, 기능 명세서, 동시성 제어 설계서 등)을 작성했습니다. 이 문서들이 바로 AI에게 전달할 설계도입니다.

비유하자면 이렇습니다. 여러분이 건축주이고, SA 문서가 설계도면이고, Claude Code가 시공업체입니다. 설계도면이 정확할수록 시공 품질이 좋아지는 것처럼, SA 문서를 얼마나 정확하게 전달하느냐가 바이브코딩의 품질을 결정합니다.


2. 클로드코드를 사용할 경우 ? 시작하기 전: CLAUDE.md 세팅

프로젝트 루트에 CLAUDE.md를 만들어두면 Claude Code가 프로젝트 컨텍스트를 매 대화마다 기억합니다. Spring Boot의 application.yml이 프로젝트 설정을 담는 것처럼, CLAUDE.md는 AI를 위한 프로젝트 설정 파일입니다.

# CLAUDE.md — TicketFlow Backend

## 프로젝트 개요
TicketFlow — 공연·스포츠 티켓팅 플랫폼 백엔드 (B2C)
공정한 선착순 좌석 예매 + 선착순 쿠폰 발급 + 검색 캐싱

## 기술 스택
- Java 17 + Spring Boot 3.x
- Spring Data JPA + QueryDSL
- Spring Security + JWT (AccessToken 단독, 1시간 TTL)
- MySQL 8 (InnoDB, 타임존 KST)
- Redis 7 (Lettuce — 분산락 SETNX, Hold TTL, 인기검색어 ZSet)
- Caffeine (로컬 캐시, v2 검색)
- Docker + Docker Compose

## 패키지 구조
com.ticketflow
├── global/          # 공통 설정, 예외 처리, 보안
│   ├── config/      # SecurityConfig, RedisConfig, CacheConfig
│   ├── exception/   # GlobalExceptionHandler, ErrorCode, CustomException
│   └── jwt/         # JwtTokenProvider, JwtAuthenticationFilter
├── domain/
│   ├── user/        # User 엔티티, 회원가입/로그인
│   ├── event/       # Event, Section, Seat, Venue 엔티티 + 검색
│   ├── booking/     # Order, Booking, ActiveBooking + Hold + 결제
│   ├── coupon/      # Coupon, UserCoupon + 선착순 발급
│   └── chat/        # ChatRoom, ChatMessage + WebSocket (도전)
└── infra/
    └── redis/       # RedisLockRepository, RedisCacheConfig

## 각 도메인 패키지 내부 구조 (일관되게 유지)
domain/{도메인}/
├── entity/          # JPA 엔티티
├── repository/      # Spring Data JPA Repository
├── service/         # 비즈니스 로직
├── controller/      # REST API
├── dto/
│   ├── request/     # 요청 DTO
│   └── response/    # 응답 DTO
└── exception/       # 도메인 전용 예외 (선택)

## 코딩 규칙
- 엔티티에 @Setter 사용 금지 → 비즈니스 메서드로 상태 변경
- DTO와 엔티티 분리 필수 — 컨트롤러에서 엔티티 직접 반환 금지
- 응답은 공통 응답 형식 사용: { status, code, message, data }
- 예외는 GlobalExceptionHandler에서 일괄 처리
- 서비스 메서드에 @Transactional(readOnly=true) 기본, 쓰기 메서드만 @Transactional
- 테스트: 단위 테스트(Mockito) + 동시성 테스트(ExecutorService + CyclicBarrier)
- 한국어 주석으로 핵심 비즈니스 로직 설명

## ERD 핵심 테이블
- USER (user_id PK, email UK, password, nickname, role ENUM)
- VENUE (venue_id PK, name, address) — 시드데이터 1개만, venue_id=1 고정
- EVENT (event_id PK, venue_id FK, title, category ENUM, event_date, sale_start_at, sale_end_at, round_number, status ENUM)
- SECTION (section_id PK, event_id FK, section_name, price, total_seats)
- SEAT (seat_id PK, section_id FK, row_name, col_num) — status 컬럼 없음!
- ORDER (order_id PK, user_id FK, user_coupon_id FK nullable, total/discount/final_amount, status ENUM)
- BOOKING (booking_id PK, order_id FK, user_id FK, seat_id FK, event_id FK, original_price, ticket_code UK nullable, status ENUM)
- ACTIVE_BOOKING (seat_id PK, booking_id UK) — 확정된 예약만, 중복 확정 물리적 차단
- PAYMENT (payment_id PK, order_id FK, payment_key UK, method, paid_amount, status ENUM)
- COUPON (coupon_id PK, name, discount_amount, total_quantity, remaining_quantity)
- USER_COUPON (user_coupon_id PK, user_id FK, coupon_id FK, status ENUM, version)
- CHAT_ROOM, CHAT_MESSAGE (도전 기능)

## 좌석 상태 판단 로직 (SEAT에 status 컬럼 없음!)
1. ACTIVE_BOOKING에 seat_id 존재 → CONFIRMED
2. Redis hold:{eventId}:{seatId} 키 존재 → ON_HOLD
3. 그 외 → AVAILABLE

## 동시성 제어
- 좌석 Hold: Lettuce SETNX 분산락 (lock:seat:{eventId}:{seatId}), Fail Fast
- 쿠폰 발급: Redis DECR + Lua Script 원자적 차감 (분산락 미사용)
- 쿠폰 사용: lock:user-coupon-use:{userCouponId} 분산락 + @Version 낙관적 락
- 락 획득 순서: ① 좌석 Hold 락 → ② 쿠폰 사용 락 → ③ DB 트랜잭션 (역순 해제)

## 서버 타임존
KST (UTC+9) — spring.jpa.properties.hibernate.jdbc.time_zone=Asia/Seoul

이 CLAUDE.md를 프로젝트 루트에 먼저 만들어두세요. Claude Code가 이 파일을 자동으로 읽고, 이후 모든 코드 생성에 이 컨텍스트를 반영합니다.


3. SA 문서 → 프롬프트 매핑 전략

여러분이 작성한 SA 문서 10종은 각각 바이브코딩의 특정 단계에서 활용됩니다.

SA 문서바이브코딩 활용 시점어떻게 전달하나
01 프로젝트 개요서CLAUDE.md 작성기술 스택, 핵심 플로우 요약
02 사용자 시나리오통합 테스트 시나리오 작성페르소나별 Happy/Unhappy Path
03 유스케이스 명세서서비스 로직 구현 요청UC별 주요/대체/예외 흐름
04 기능 명세서핵심! 기능 단위 구현 요청FN-ID별로 하나씩 요청
05 ERD엔티티 + Repository 생성ERD Mermaid 또는 테이블 DDL
06 API 명세서Controller + DTO 생성엔드포인트별 요청/응답 스펙
07 화면 설계서프론트엔드 연동 검증API 응답과 화면 매핑 확인
08 인프라 아키텍처Docker Compose + CI/CD 구성다이어그램 + 설정 파일
09 동시성 제어 설계서분산락 + 동시성 테스트 구현시나리오 매트릭스 + 코드 예시
10 ADR아키텍처 결정 사항 반영왜 이렇게 설계했는지 근거

4. 구현 순서와 단계별 프롬프트

Spring Boot에서 개발할 때 보통 Entity → Repository → Service → Controller 순서로 만들죠? 바이브코딩도 마찬가지입니다. 단, 인프라 세팅을 가장 먼저 합니다.

구현 순서:

Phase 0: 프로젝트 초기 세팅 + Docker Compose     ← 인프라 아키텍처 문서
Phase 1: 공통 설정 (Security, Exception, Redis)   ← ADR + 프로젝트 개요서
Phase 2: 엔티티 + Repository                      ← ERD 문서
Phase 3: 도메인별 서비스 + 컨트롤러               ← 기능 명세서 + API 명세서
Phase 4: 동시성 제어 (분산락 + 테스트)             ← 동시성 제어 설계서
Phase 5: 캐싱 (Caffeine → Redis)                  ← 프로젝트 개요서 §6
Phase 6: 통합 테스트 + QA                          ← 사용자 시나리오

Phase 0: 프로젝트 초기 세팅

Spring Boot 3.x + Java 17 프로젝트를 생성해줘.

build.gradle 의존성:
- spring-boot-starter-web
- spring-boot-starter-data-jpa
- spring-boot-starter-security
- spring-boot-starter-data-redis (Lettuce)
- spring-boot-starter-cache
- spring-boot-starter-validation
- spring-boot-starter-websocket (도전 기능용)
- com.github.ben-manes.caffeine:caffeine
- querydsl-jpa (jakarta classifier)
- jjwt-api, jjwt-impl, jjwt-jackson (io.jsonwebtoken 0.12.x)
- mysql-connector-j
- lombok
- spring-boot-starter-test
- h2 (test scope)

application.yml 설정:
- DB: jdbc:mysql://localhost:3306/ticketflow?serverTimezone=Asia/Seoul
- JPA: ddl-auto=validate, show-sql=true, time_zone=Asia/Seoul
- Redis: host=localhost, port=6379
- JWT: secret=${JWT_SECRET:local-dev-secret-key-minimum-256-bits-long}, expiration=3600000
- Server port: 8080

패키지 구조는 CLAUDE.md에 정의한 대로 만들어줘.

Docker Compose도 함께 요청합니다:

인프라 아키텍처 문서를 참고해서 docker-compose.yml을 만들어줘.

서비스 구성:
- mysql (MySQL 8.0, port 3306, DB명 ticketflow, root/root)
- redis (Redis 8.0, port 6379)
- 볼륨: mysql_data

app 서비스는 아직 추가하지 마. 로컬에서 IDE로 직접 실행할 거야.

Phase 1: 공통 설정 (Security + Exception + Redis)

이 단계에서는 ADR 문서와 프로젝트 개요서를 활용합니다.

JWT 인증:

ADR-001 결정 사항을 반영해서 JWT 인증을 구현해줘.

설계:
- AccessToken 단독 (RefreshToken 미구현, ADR-001)
- 알고리즘: HS256
- 만료: 1시간 (3600초)
- Payload: { userId, email, role }
- Spring Security 필터 체인에 JwtAuthenticationFilter 추가

구현할 클래스:
1. JwtTokenProvider
   - createToken(userId, email, role): String
   - validateToken(token): boolean
   - getUserIdFromToken(token): Long
   - getRoleFromToken(token): String

2. JwtAuthenticationFilter extends OncePerRequestFilter
   - Authorization 헤더에서 Bearer 토큰 추출
   - 유효하면 SecurityContext에 Authentication 설정
   - /api/auth/** 경로는 필터 스킵

3. SecurityConfig
   - CSRF 비활성화 (Stateless)
   - /api/auth/signup, /api/auth/login → permitAll
   - /api/admin/** → ADMIN 권한만
   - 나머지 → authenticated
   - SessionCreationPolicy.STATELESS

비고: 로그아웃은 클라이언트에서 토큰 삭제로 처리 (서버 블랙리스트 미구현)

공통 예외 처리:

API 명세서의 공통 에러 응답 형식을 참고해서 예외 처리를 구현해줘.

공통 에러 응답:
{
  "status": 409,
  "code": "SEAT_ALREADY_HELD",
  "message": "이미 선점된 좌석입니다.",
  "timestamp": "2026-04-09T12:00:00"
}

구현:
1. ErrorCode enum — 각 도메인별 에러 코드 정의
   - AUTH: EMAIL_DUPLICATED(409), AUTHENTICATION_FAILED(401), INVALID_CURRENT_PASSWORD(401)
   - EVENT: EVENT_NOT_FOUND(404), INVALID_EVENT_DATE(400)
   - SEAT: SEAT_ALREADY_HELD(409), SEAT_LOCK_FAILED(409), HOLD_EXPIRED(410)
   - BOOKING: BOOKING_NOT_FOUND(404), ALREADY_CANCELLED(400)
   - COUPON: COUPON_SOLD_OUT(409), COUPON_ALREADY_ISSUED(409), COUPON_EXPIRED(400)

2. CustomException extends RuntimeException — ErrorCode 포함
3. GlobalExceptionHandler (@RestControllerAdvice)
   - CustomException 처리
   - MethodArgumentNotValidException (Bean Validation 실패) 처리
   - 기타 예외 → 500 Internal Server Error

Phase 2: 엔티티 + Repository

ERD 문서를 통째로 전달합니다. 이것이 가장 효과적입니다.

아래 ERD를 보고 JPA 엔티티와 Repository를 만들어줘.

[ERD 문서(05.erd.md) 전체 내용 붙여넣기]

엔티티 규칙:
- @Getter만, @Setter 금지
- BaseEntity(createdAt, updatedAt)를 만들고 @MappedSuperclass로 상속
- ENUM은 @Enumerated(EnumType.STRING)
- 연관관계는 지연로딩(LAZY) 기본
- 양방향 매핑은 꼭 필요한 경우만 (단방향 우선)
- ACTIVE_BOOKING의 seat_id는 PK이자 UNIQUE

Repository:
- 각 엔티티별 JpaRepository 생성
- 커스텀 쿼리가 필요한 곳은 주석으로 "TODO: QueryDSL" 표시
- BookingRepository: findByUserIdAndStatus, findBySeatIdAndEventId
- ActiveBookingRepository: existsBySeatId, deleteBySeatId
- UserCouponRepository: existsByUserIdAndCouponId (중복 발급 방지)

Phase 3: 도메인별 서비스 + 컨트롤러

여기서 기능 명세서(FN-ID)와 API 명세서를 함께 전달합니다. 한 번에 하나의 도메인씩 요청하는 것이 핵심입니다.

3-1. 회원/인증 도메인

기능 명세서의 FN-AUTH-01(회원가입), FN-AUTH-02(로그인)을 구현해줘.

[FN-AUTH-01, FN-AUTH-02 내용 붙여넣기]

API 명세:
- POST /api/auth/signup
  Request: { email, password, nickname }
  Response 201: { userId, email, nickname, createdAt }
  Error: 409 EMAIL_DUPLICATED, 400 VALIDATION_FAILED

- POST /api/auth/login
  Request: { email, password }
  Response 200: { accessToken, expiresIn, tokenType }
  Error: 401 AUTHENTICATION_FAILED

구현 순서:
1. SignupRequest, LoginRequest DTO (Bean Validation 포함)
2. AuthService — signup(), login()
3. AuthController — @PostMapping

유효성 규칙:
- email: @Email, 최대 100자
- password: 8자 이상, 영문+숫자 포함 (정규식 커스텀 검증)
- nickname: 2~20자, 특수문자 불가

3-2. 이벤트 도메인

기능 명세서의 FN-EVT-01(이벤트 등록), FN-SRCH-01(이벤트 검색 v1)을 구현해줘.

[FN-EVT-01, FN-SRCH-01 내용 붙여넣기]

API 명세:
- POST /api/admin/events (🔐👑 ADMIN만)
  Request: { title, category, venueId, eventDate, saleStartAt, saleEndAt,
             roundNumber, description, thumbnailUrl, sections[] }
  sections: [{ sectionName, price, rowCount, colCount }]
  Response 201: { eventId, title, totalSeats, sectionsCreated }

  이벤트 등록 시 sections 배열 기반으로 SECTION + SEAT를 자동 생성해줘.
  SEAT의 row_name은 A~Z열, col_num은 1부터 colCount까지.
  예: rowCount=3, colCount=5 → A1~A5, B1~B5, C1~C5 총 15석

- GET /api/events?page=0&size=20&sort=eventDate,asc&category=CONCERT (🔓)
  Response: Page<EventSummary> — 캐시 미적용 (v1)

- GET /api/events/{eventId} (🔓)
  Response: EventDetail (섹션별 잔여좌석 포함)

  잔여좌석 계산: 섹션의 전체 좌석 수 - ACTIVE_BOOKING에 존재하는 좌석 수
  (SEAT 테이블에 status 컬럼이 없으므로 ACTIVE_BOOKING JOIN으로 계산)

3-3. 예매 도메인 (핵심! 동시성 주의)

기능 명세서의 FN-SEAT-02(좌석 임시 점유), FN-BK-01(주문 생성),
FN-BK-02(결제 확정 웹훅)을 구현해줘.

⚠️ 이 기능은 동시성 제어가 핵심이야. 동시성 제어 설계서의 시나리오 A를 참고해줘.

[FN-SEAT-02, FN-BK-01, FN-BK-02 내용 붙여넣기]
[동시성 제어 설계서 §3 시나리오 A 내용 붙여넣기]

좌석 Hold 플로우 (프로젝트 개요서 §4-3 기준):
1. 사용자 → 좌석 선택 요청
2. 서버 → Redis 분산락 획득 (Lettuce SETNX, lock:seat:{eventId}:{seatId}, TTL 3초)
3. 서버 → ACTIVE_BOOKING에 해당 seat_id 존재 여부 확인
4. 서버 → Redis에 Hold 키 SET (hold:{eventId}:{seatId}, 값: userId, TTL 5분)
5. 서버 → holdToken 역조회 키 SET (holdToken:{uuid}, 값: "{eventId}:{seatId}:{userId}", TTL 5분)
6. 서버 → user-hold-count:{userId} INCR (4 이상이면 거부)
7. 서버 → 분산락 해제 (UUID 검증 + Lua Script 원자적 삭제)
8. 200 OK { holdToken, expiresAt } 반환

API:
- POST /api/events/{eventId}/seats/{seatId}/hold (🔐⚠️)
  Response 200: { holdToken, expiresAt }
  Error: 409 SEAT_ALREADY_HELD, 409 SEAT_LOCK_FAILED, 429 HOLD_LIMIT_EXCEEDED

Redis 키 설계:
- 분산락: lock:seat:{eventId}:{seatId} (TTL 3초)
- Hold: hold:{eventId}:{seatId} → userId (TTL 300초)
- holdToken 역조회: holdToken:{uuid} → "{eventId}:{seatId}:{userId}" (TTL 300초)
- Hold 카운터: user-hold-count:{userId} (INCR/DECR, TTL 300초)

분산락 해제는 반드시 Lua Script로 원자적으로 처리해줘:
if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
else
    return 0
end

3-4. 쿠폰 도메인

기능 명세서의 FN-CPN-01(쿠폰 등록), FN-CPN-02(선착순 쿠폰 발급)을 구현해줘.

⚠️ 쿠폰 발급은 Redis DECR + Lua Script로 원자적 수량 차감! (분산락 미사용, ADR/ERD §2-8 참고)

[FN-CPN-01, FN-CPN-02 내용 붙여넣기]

API:
- POST /api/admin/coupons (🔐👑)
  쿠폰 등록 시 Redis에도 초기 재고 SET: coupon:stock:{couponId} = totalQuantity

- POST /api/coupons/{couponId}/issue (🔐⚠️)

쿠폰 발급 Lua Script:
  local stock = redis.call("DECR", KEYS[1])
  if stock < 0 then
      redis.call("INCR", KEYS[1])  -- 복원
      return -1  -- 품절
  end
  return stock

발급 플로우:
1. USER_COUPON에 (userId, couponId) 중복 확인 → 이미 있으면 409
2. Lua Script로 Redis coupon:stock:{couponId} DECR
3. DECR 결과 < 0 → INCR 복원 + 409 COUPON_SOLD_OUT
4. DECR 성공 → MySQL remaining_quantity UPDATE (-1)
5. USER_COUPON INSERT (status=ISSUED)
6. 201 { userCouponId, couponName, discountAmount }

비고: Redis 연결 실패 시 → MySQL SELECT ... FOR UPDATE 폴백 경로

Phase 4: 동시성 제어 테스트

동시성 제어 설계서를 활용하여 테스트를 작성합니다.

동시성 제어 설계서를 참고해서 동시성 테스트를 작성해줘.

테스트 시나리오 1: 좌석 Hold Race Condition
- 100개 Thread가 동시에 같은 좌석(eventId=1, seatId=1)에 Hold 요청
- 기대 결과: 정확히 1명만 Hold 성공, 나머지 99명은 실패
- ExecutorService + CyclicBarrier 사용
- @SpringBootTest + 실제 Redis 연결 (Embedded Redis 또는 Testcontainers)

테스트 시나리오 2: 쿠폰 발급 Race Condition
- 잔여 수량 10개인 쿠폰에 500명이 동시 발급 요청
- 기대 결과: 정확히 10명만 발급 성공, remaining_quantity = 0
- Redis DECR + Lua Script 검증

테스트 시나리오 3: 예매 시 쿠폰 적용 (락 중첩)
- 좌석 Hold + 쿠폰 사용이 동시에 일어나는 상황
- 락 획득 순서(좌석→쿠폰→DB) 준수 확인

각 테스트에서 검증할 것:
- CountDownLatch로 모든 Thread 완료 대기
- 성공 횟수 == 기대값 (AtomicInteger 카운터)
- DB 최종 상태 정합성 (remaining_quantity, ACTIVE_BOOKING 수)

Phase 5: 캐싱 (Caffeine → Redis)

프로젝트 개요서 §6의 3단계 캐싱 진화 흐름을 따릅니다.

프로젝트 개요서의 캐싱 3단계 진화 흐름을 구현해줘.

현재 단계: v1 (캐시 없음) → v2 (Caffeine 로컬 캐시 적용)

v2 Caffeine 적용 대상:
- 이벤트 검색 결과: TTL 5분, maximumSize 1000
- 이벤트 상세 조회: TTL 10분, maximumSize 500
- 인기 검색어: Redis ZSet 사용 (Caffeine 아님)

구현:
1. CacheConfig에 Caffeine CacheManager 설정
2. 검색 서비스에 @Cacheable("eventSearch") 적용
3. 이벤트 등록/수정/삭제 시 @CacheEvict 적용
4. v1/v2 엔드포인트는 동일 — 캐시 적용 여부만 다름

도전 기능 (Redis 캐시 전환) 준비:
- @ConditionalOnProperty(name = "cache.provider", havingValue = "caffeine")
- @ConditionalOnProperty(name = "cache.provider", havingValue = "redis")
- application.yml의 cache.provider 값만 바꾸면 Caffeine ↔ Redis 전환

Phase 6: 인프라 + CI/CD

인프라 아키텍처 문서를 전달합니다.

인프라 아키텍처 문서를 참고해서 다음을 만들어줘:

1. Dockerfile (Multi-stage build)
   - Stage 1: Gradle 빌드 (테스트 포함)
   - Stage 2: Eclipse Temurin JRE 17 기반 실행 이미지
   - 환경변수: DB_URL, REDIS_HOST, JWT_SECRET

2. GitHub Actions Workflow (.github/workflows/deploy.yml)
   - 트리거: main 브랜치 push
   - Job 1: test (./gradlew test)
   - Job 2: build-and-deploy
     - ECR 로그인 + 이미지 빌드/push (태그: $GITHUB_SHA)
     - EC2 SSH 접속 + 컨테이너 교체
   - GitHub Secrets: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY,
     EC2_HOST, EC2_SSH_KEY, DB_URL, REDIS_HOST, JWT_SECRET

3. Health Check 엔드포인트
   - GET /actuator/health → 200 OK (ALB Health Check용)

5. 프롬프트 작성 핵심 원칙

원칙 1: SA 문서를 직접 첨부하거나 복붙하라

나쁜 예시:

좌석 예매 기능 만들어줘

좋은 예시:

아래 기능 명세서(FN-BK-01)와 API 명세서를 보고 주문 생성 기능을 구현해줘.

[기능 명세서 FN-BK-01 전문 붙여넣기]
[API 명세서 POST /api/bookings 전문 붙여넣기]
[동시성 제어 설계서 시나리오 A 붙여넣기]

SA 문서가 곧 프롬프트입니다. 여러분이 공들여 작성한 기능 명세서, API 명세서, ERD가 가장 좋은 프롬프트 재료입니다.

원칙 2: 한 번에 하나의 기능(FN-ID) 단위로 요청하라

나쁜 예시:

예매 도메인 전체 기능 다 만들어줘

좋은 예시:

FN-SEAT-02(좌석 임시 점유)만 먼저 구현해줘.
FN-BK-01(주문 생성)은 다음에 요청할게.

원칙 3: 에러 케이스를 반드시 명시하라

API 명세서에 이미 에러 케이스가 정리되어 있습니다. 반드시 함께 전달하세요.

에러 처리:
- 409 SEAT_ALREADY_HELD: 이미 다른 사용자가 Hold 중
- 409 SEAT_LOCK_FAILED: 분산락 획득 실패 (Fail Fast)
- 429 HOLD_LIMIT_EXCEEDED: 1인 최대 Hold 4석 초과
- 410 HOLD_EXPIRED: Hold TTL 만료 후 결제 시도
- 404 EVENT_NOT_FOUND: 존재하지 않는 이벤트

원칙 4: "왜 이렇게 설계했는지"를 함께 전달하라

ADR 문서의 결정 근거를 함께 전달하면 AI가 설계 의도에 맞는 코드를 생성합니다.

JWT는 AccessToken만 사용해 (RefreshToken 미구현).
이유: 3주 일정에서 블랙리스트 관리 복잡도가 과함.
로그아웃은 클라이언트 토큰 삭제로만 처리해.
(ADR-001 참고)

원칙 5: 기존 코드와의 연결점을 알려줘라

좌석 Hold 서비스를 만들 때:
- 좌석 존재 확인: SeatRepository.findById() (이미 Phase 2에서 만든 것)
- 사용자 인증: SecurityContext에서 userId 추출 (Phase 1에서 만든 JwtAuthenticationFilter)
- Redis 작업: RedisTemplate<String, String> (Phase 1에서 설정한 RedisConfig)
- 에러 처리: CustomException + ErrorCode (Phase 1에서 만든 것)

6. 자주 쓰는 상황별 프롬프트

상황 1: 코드 리뷰 요청

방금 만든 HoldService 코드를 리뷰해줘.
체크포인트:
- 분산락 해제가 finally 블록에서 보장되는지
- Lua Script로 본인 락만 삭제하는지 (UUID 검증)
- Redis 키 TTL이 설계서와 일치하는지
- 예외 발생 시 Hold 카운터(user-hold-count)가 정리되는지

상황 2: 테스트 코드 작성

BookingService.confirmBooking()에 대한 테스트를 작성해줘.

Happy Path:
- Hold 유효 + 결제 성공 → BOOKING CONFIRMED + ACTIVE_BOOKING INSERT

Unhappy Path:
- Hold TTL 만료 후 웹훅 수신 → 예매 실패 처리
- ACTIVE_BOOKING INSERT 시 seat_id 중복 (DuplicateKeyException) → 롤백

동시성 테스트:
- 같은 좌석에 2명이 동시에 confirmBooking 호출 → 1명만 성공

상황 3: 에러 디버깅

아래 에러가 발생했어:

org.springframework.dao.DataIntegrityViolationException:
could not execute statement; SQL [n/a]; constraint [PRIMARY]

상황: Hold 성공한 좌석의 결제 확정(웹훅 수신) 시 발생
코드 위치: BookingService.confirmBooking() → activeBookingRepository.save()

이건 ACTIVE_BOOKING의 seat_id PK 중복 때문인 것 같아.
2차 방어선이 작동한 건데, 왜 1차 방어선(Redis 분산락)을 통과했는지 분석해줘.

상황 4: 시드 데이터 생성

프로젝트 개요서 §6-1의 데이터 볼륨 전략을 참고해서 시드 데이터를 생성해줘.

CommandLineRunner로 구현:
- Venue 1개 (KSPO돔)
- Event 5,000개 (카테고리별 균등 분배: CONCERT, MUSICAL, THEATER, SPORTS)
- 이벤트당 Section 3~6개 (랜덤)
- 섹션당 Seat 100~150석 (랜덤)
- Admin 계정 1개 (admin@ticketflow.io / admin1234)
- 테스트 유저 10명

프로파일: spring.profiles.active=seed 일 때만 실행

7. 팀원별 바이브코딩 가이드

SA 문서의 역할 분담(프로젝트 개요서 §9)에 따라 각 팀원이 요청해야 할 기능 목록입니다.

팀원 A (회원 도메인)

구현 순서:
1. FN-AUTH-01 회원가입 + FN-AUTH-02 로그인 (JWT)
2. FN-AUTH-03 내 정보 조회·수정
3. GET /api/users/me/bookings (내 예매 내역)
4. GET /api/users/me/coupons (내 쿠폰 목록)

함께 전달할 SA 문서: 기능 명세서 §1, API 명세서 §1~2, ERD USER 테이블

팀원 B (이벤트 도메인)

구현 순서:
1. FN-EVT-01 이벤트 등록 (섹션+좌석 자동 생성)
2. FN-SRCH-01 검색 v1 (캐시 없음)
3. FN-SEAT-01 좌석 목록 조회 (상태 판단: ACTIVE_BOOKING + Redis)
4. FN-SRCH-02 검색 v2 (Caffeine 캐시)
5. FN-SRCH-03 인기 검색어 (Redis ZSet)

함께 전달할 SA 문서: 기능 명세서 §2~3, API 명세서 §3~4, ERD EVENT/SECTION/SEAT

팀원 C (예매 도메인)

구현 순서:
1. FN-SEAT-02 좌석 Hold (Lettuce 분산락) ← 동시성 제어 핵심!
2. FN-BK-01 주문 생성 + Mock PG 결제 요청
3. FN-BK-02 웹훅 수신 + 결제 확정 (ACTIVE_BOOKING INSERT)
4. FN-BK-03 예매 취소 (ACTIVE_BOOKING DELETE + 쿠폰 복원)
5. 동시성 테스트 코드 (좌석 Hold 100 Thread 테스트)

함께 전달할 SA 문서: 기능 명세서 §4~5, API 명세서 §5~6, 동시성 제어 설계서 시나리오 A

팀원 D (프로모션 도메인)

구현 순서:
1. FN-CPN-01 쿠폰 등록 (Admin)
2. FN-CPN-02 선착순 쿠폰 발급 (Redis DECR + Lua) ← 동시성 핵심!
3. FN-CPN-03 쿠폰 적용 (예매 플로우 내)
4. 동시성 테스트 (쿠폰 500명 동시 발급)
5. (도전) FN-CHAT-01~03 CS 채팅 WebSocket

함께 전달할 SA 문서: 기능 명세서 §6~7, API 명세서 §7~8, 동시성 제어 설계서 시나리오 B

팀원 E (인프라)

구현 순서:
1. Docker Compose (MySQL + Redis) ← Day 1~2 최우선!
2. Dockerfile (Multi-stage build)
3. GitHub Actions CI/CD 파이프라인
4. AWS 인프라 구성 (EC2, RDS, ElastiCache, ECR, ALB)
5. (도전) DB 인덱스 최적화 (EXPLAIN 분석)

함께 전달할 SA 문서: 인프라 아키텍처 문서 전체, ERD §3 인덱스 설계

8. 프롬프트 체크리스트

기능을 요청하기 전에 아래를 확인하세요.

□ CLAUDE.md가 프로젝트 루트에 있는가?
□ 요청할 기능의 FN-ID를 확인했는가? (기능 명세서)
□ 해당 API의 Request/Response/Error를 정리했는가? (API 명세서)
□ 동시성 민감 기능인가? → 동시성 제어 설계서 해당 시나리오 첨부
□ 이 기능에서 사용하는 Redis 키와 TTL을 명시했는가?
□ 에러 케이스(4xx, 5xx)와 ErrorCode를 명시했는가?
□ 이전에 만든 클래스(엔티티, Repository, 서비스)를 참조하는가? → 연결점 명시
□ 테스트 코드도 함께 요청했는가?

9. 주의사항

하지 말 것:

  • "예매 기능 전체 만들어줘" → 한 번에 너무 많으면 품질이 떨어지고 SA 문서와 불일치 발생
  • SA 문서 없이 "쿠폰 발급 만들어줘" → AI가 임의 설계하면 ERD, API 명세서와 충돌
  • "동시성은 알아서 처리해줘" → Lettuce SETNX인지, Redisson인지, 비관적 락인지 명시 필수
  • SEAT 테이블에 status 컬럼 있다고 가정 → ERD v7.0에서 삭제됨! ACTIVE_BOOKING 기반

꼭 할 것:

  • SA 문서를 프롬프트의 근거 자료로 첨부하기
  • 기능 명세서의 FN-ID 단위로 하나씩 요청하기
  • 에러 케이스를 빠짐없이 전달하기
  • 동시성 민감 기능은 Redis 키 설계 + 락 전략을 명시하기
  • 코드 생성 후 동시성 테스트로 검증하기
  • ERD 변경 사항(SEAT.status 삭제, ACTIVE_BOOKING 신규 등)을 AI에게 반드시 알려주기

0개의 댓글