팀 스프린트 플래닝 포커 - 실시간 스토리 포인트 투표 - 바이브코딩 레시피
중급 PHP + JavaScript Laravel + React + Inertia.js AI: Claude

팀 스프린트 플래닝 포커 - 실시간 스토리 포인트 투표

리포지토리명 sprint-planning-poker-inertia
조회수 133 2건 제출

문제 설명

프로젝트 개요

애자일 스프린트 플래닝 시 팀원들이 실시간으로 스토리 포인트를 투표하고 결과를 집계하는 플래닝 포커 웹 애플리케이션입니다. Laravel 13.x + Inertia.js + React 19 모노리스 풀스택 아키텍처로 구현하며, 방 생성/참가, 카드 투표, 결과 공개 등의 기능을 제공합니다.

필수 기능

  • 방 생성 및 관리: 방장이 플래닝 포커 세션 방을 생성하고 고유 코드 발급
  • 참가자 입장: 방 코드로 다른 팀원들이 입장 (닉네임 입력)
  • 스토리 카드 관리: 방장이 투표할 스토리(이슈) 추가/수정/삭제
  • 포인트 투표: 각 참가자가 피보나치 수열(1, 2, 3, 5, 8, 13, 21) 또는 커스텀 카드로 투표
  • 투표 현황 표시: 누가 투표했는지 실시간으로 표시 (카드는 뒤집힌 상태)
  • 결과 공개: 방장이 '공개' 버튼을 누르면 모든 카드가 동시에 오픈되고 평균/중앙값 표시
  • 재투표 기능: 논의 후 다시 투표할 수 있도록 리셋
  • 세션 대시보드: 방장이 현재 방 상태, 참가자 목록, 투표 히스토리 확인

보너스 기능

  • 투표 결과 히스토리 저장 및 CSV 내보내기
  • 타이머 기능 (투표 제한 시간 설정)
  • 다크 모드 지원
  • 방 비밀번호 설정 (선택적)
  • 투표 통계 차트 (막대 그래프로 분포 시각화)

기술 스택

  • Backend: Laravel 13.x (라우트, 컨트롤러, Eloquent ORM, SQLite)
  • Frontend: React 19 (컴포넌트, Hooks)
  • Bridge: Inertia.js (Laravel과 React 연결, SPA 경험 제공)
  • Styling: Tailwind CSS
  • Database: SQLite (로컬 개발용)

데이터베이스 구조

  • rooms: id, code(unique), name, created_by, status, created_at
  • participants: id, room_id, nickname, is_host, joined_at
  • stories: id, room_id, title, description, status(pending/voting/revealed), order
  • votes: id, story_id, participant_id, point, created_at

제약 조건

  • • Laravel 13.x + Inertia.js + React 19 모노리스 풀스택 구조 (별도 API 라우트 없음)
  • • Laravel Breeze (React + Inertia) starter kit 사용 권장
  • • SQLite 데이터베이스 사용
  • • Tailwind CSS로 스타일링
  • • Inertia::render()로 Laravel 컨트롤러에서 React 컴포넌트에 데이터 전달
  • • 방 코드는 6자리 영숫자 랜덤 생성 (unique)
  • • 투표 카드는 피보나치 수열 기본 제공 (1, 2, 3, 5, 8, 13, 21, ?, ☕)
  • • 방장만 스토리 추가/삭제 및 결과 공개 권한
  • • 모든 참가자가 투표해야 '모두 투표 완료' 상태 표시
  • • 투표 결과는 평균과 중앙값 모두 표시
  • • React 컴포넌트는 Pages 폴더에 구성 (Inertia 페이지 컴포넌트)
  • • useState, useEffect 등 React Hooks 활용
  • • Laravel 마이그레이션으로 테이블 스키마 정의
  • • Eloquent 관계(hasMany, belongsTo) 적극 활용

프롬프트 레시피

아래 프롬프트를 순서대로 AI에게 보내면 됩니다. 복사 버튼을 눌러 바로 사용하세요.

1
Step 1
mkdir sprint-planning-poker-inertia && cd sprint-planning-poker-inertia

이 폴더에서 Laravel 13.x + Inertia.js + React 19 프로젝트를 시작합니다.

다음 작업을 수행해주세요:

1. Laravel Breeze (React + Inertia) starter kit으로 프로젝트 초기화
   - `composer create-project laravel/laravel . "^13.0"`
   - `composer require laravel/breeze --dev`
   - `php artisan breeze:install react --inertia`
   - `npm install && npm run build`

2. SQLite 데이터베이스 설정
   - .env 파일에서 DB_CONNECTION=sqlite로 변경
   - database/database.sqlite 파일 생성

3. 필요한 마이그레이션 파일 4개 생성:
   - rooms 테이블: id, code(string, unique), name(string), created_by(string), status(enum: active, closed), timestamps
   - participants 테이블: id, room_id(foreign), nickname(string), is_host(boolean), joined_at(timestamp)
   - stories 테이블: id, room_id(foreign), title(string), description(text, nullable), status(enum: pending, voting, revealed), order(integer), timestamps
   - votes 테이블: id, story_id(foreign), participant_id(foreign), point(string), timestamps

4. 마이그레이션 실행: `php artisan migrate`

5. 기본 레이아웃 확인 및 Tailwind CSS 설정 점검

완료 후 프로젝트 구조와 마이그레이션 파일 내용을 보여주세요.

기대 결과: Laravel 13.x + Breeze + Inertia + React 19 프로젝트가 초기화되고, SQLite 데이터베이스에 rooms, participants, stories, votes 테이블이 생성됩니다. 마이그레이션 파일에 외래키 제약조건과 인덱스가 올바르게 설정되어 있어야 합니다.

2
Step 2
Eloquent 모델 4개와 기본 컨트롤러 2개를 생성해주세요.

1. **모델 생성 (app/Models):**
   - Room 모델: participants, stories 관계 정의 (hasMany)
   - Participant 모델: room, votes 관계 정의 (belongsTo, hasMany)
   - Story 모델: room, votes 관계 정의 (belongsTo, hasMany)
   - Vote 모델: story, participant 관계 정의 (belongsTo)

2. **컨트롤러 생성:**
   - RoomController: 방 생성(store), 방 입장(join), 방 정보 조회(show) 메서드
   - StoryController: 스토리 CRUD 메서드 (index, store, update, destroy)

3. **라우트 설정 (routes/web.php):**
   ```php
   Route::get('/', [RoomController::class, 'index'])->name('home');
   Route::post('/rooms', [RoomController::class, 'store'])->name('rooms.store');
   Route::get('/rooms/{code}', [RoomController::class, 'show'])->name('rooms.show');
   Route::post('/rooms/{code}/join', [RoomController::class, 'join'])->name('rooms.join');
   Route::post('/rooms/{code}/stories', [StoryController::class, 'store'])->name('stories.store');
   Route::patch('/stories/{story}', [StoryController::class, 'update'])->name('stories.update');
   Route::delete('/stories/{story}', [StoryController::class, 'destroy'])->name('stories.destroy');
   ```

4. **RoomController 로직 구현:**
   - index: 홈 페이지 (Inertia::render('Home'))
   - store: 방 생성 로직 (6자리 랜덤 코드 생성, 방장 정보 저장, 방 코드 반환)
   - show: 방 정보 + 참가자 목록 + 스토리 목록 반환 (Inertia::render('Room/Show', [...]))
   - join: 참가자 추가 로직 (닉네임 중복 체크, participant 레코드 생성)

완료 후 모델 관계 설정과 컨트롤러 코드를 보여주세요.

기대 결과: Room, Participant, Story, Vote 모델에 Eloquent 관계가 정의되고, RoomController와 StoryController에 기본 CRUD 로직이 구현됩니다. 방 생성 시 6자리 고유 코드가 자동 생성되며, 방장 정보가 participants 테이블에 저장됩니다.

3
Step 3
React 페이지 컴포넌트 2개를 생성해주세요 (resources/js/Pages).

1. **Home.jsx (홈 페이지):**
   - 방 생성 폼: 방 이름 입력, 방장 닉네임 입력, '방 만들기' 버튼
   - 방 참가 폼: 방 코드 입력, 닉네임 입력, '참가하기' 버튼
   - Inertia의 useForm hook 사용하여 폼 데이터 관리
   - router.post()로 방 생성/참가 요청
   - Tailwind CSS로 깔끔한 카드 레이아웃 (중앙 정렬, 그림자 효과)

2. **Room/Show.jsx (방 페이지):**
   - Props로 room, participants, stories, currentParticipant 데이터 받기
   - 상단: 방 이름, 방 코드 표시, 참가자 목록 (아바타 또는 이름 배지)
   - 중앙: 현재 투표 중인 스토리 제목/설명 표시
   - 하단: 투표 카드 버튼 그리드 (1, 2, 3, 5, 8, 13, 21, ?, ☕) - 피보나치 수열
   - 방장인 경우: '스토리 추가' 버튼, '결과 공개' 버튼 표시
   - 투표 현황: 각 참가자의 투표 여부 표시 (카드 뒤집힌 아이콘 또는 체크 표시)

3. **컴포넌트 스타일링:**
   - 투표 카드는 큰 버튼 형태 (hover 효과, 선택 시 색상 변경)
   - 참가자 배지는 둥근 아바타 스타일
   - 반응형 디자인 (모바일에서도 사용 가능)

완료 후 Home.jsx와 Room/Show.jsx 코드를 보여주세요.

기대 결과: Home.jsx에서 방 생성/참가 폼이 작동하며, Room/Show.jsx에서 방 정보, 참가자 목록, 투표 카드 UI가 표시됩니다. Inertia의 useForm과 router를 활용하여 SPA처럼 페이지 전환 없이 데이터 전송이 이루어집니다.

4
Step 4
투표 기능을 구현해주세요.

1. **VoteController 생성 (app/Http/Controllers):**
   - store 메서드: 투표 저장 로직
     - story_id, participant_id, point 받기
     - 중복 투표 체크 (같은 participant가 같은 story에 이미 투표했는지)
     - 중복이면 기존 투표 업데이트, 아니면 새로 생성
     - 투표 후 방 페이지로 리다이렉트 (Inertia 응답)

2. **라우트 추가 (routes/web.php):**
   ```php
   Route::post('/stories/{story}/vote', [VoteController::class, 'store'])->name('votes.store');
   ```

3. **Room/Show.jsx 업데이트:**
   - 투표 카드 클릭 시 router.post()로 투표 전송
   - 현재 참가자가 투표한 카드는 하이라이트 표시
   - 투표 현황 섹션에 각 참가자의 투표 상태 표시 (투표 완료 시 체크 아이콘)

4. **RoomController show 메서드 업데이트:**
   - 각 스토리에 대한 투표 데이터 포함 (votes 관계 로드)
   - 현재 참가자의 투표 여부 확인 로직 추가
   - Inertia::render('Room/Show', [ 'room' => $room, 'participants' => $participants, 'stories' => $stories, 'currentStory' => $currentStory, 'votes' => $votes, 'currentParticipant' => $currentParticipant ])

완료 후 VoteController 코드와 업데이트된 Room/Show.jsx를 보여주세요.

기대 결과: 투표 카드를 클릭하면 votes 테이블에 투표 데이터가 저장되고, 투표 현황이 실시간으로 업데이트됩니다. 중복 투표 시 기존 투표가 업데이트되며, 모든 참가자의 투표 상태가 UI에 반영됩니다.

5
Step 5
스토리 관리 및 결과 공개 기능을 구현해주세요.

1. **StoryController 업데이트:**
   - store 메서드: 새 스토리 추가 (방장만 가능하도록 검증)
   - update 메서드: 스토리 상태 변경 (pending → voting → revealed)
   - reveal 메서드 추가: 스토리 상태를 'revealed'로 변경하고 투표 결과 집계

2. **라우트 추가:**
   ```php
   Route::post('/stories/{story}/reveal', [StoryController::class, 'reveal'])->name('stories.reveal');
   Route::post('/stories/{story}/reset', [StoryController::class, 'reset'])->name('stories.reset');
   ```

3. **Room/Show.jsx 업데이트:**
   - 방장인 경우 '스토리 추가' 모달 또는 폼 표시
   - 스토리 목록 사이드바 추가 (pending/voting/revealed 상태별 구분)
   - 현재 투표 중인 스토리에 '결과 공개' 버튼 (방장만 표시)
   - 결과 공개 시 모든 투표 카드 오픈 + 평균/중앙값 표시
   - '재투표' 버튼으로 스토리 상태를 다시 'voting'으로 변경하고 투표 초기화

4. **투표 결과 집계 로직:**
   - StoryController reveal 메서드에서 해당 스토리의 모든 투표 집계
   - 숫자 카드만 평균/중앙값 계산 (?, ☕는 제외)
   - 결과를 Inertia 응답에 포함

5. **ResultCard 컴포넌트 생성 (resources/js/Components):**
   - 투표 결과를 카드 형태로 표시 (참가자별 투표 포인트)
   - 평균, 중앙값, 최빈값 표시
   - 막대 그래프로 분포 시각화 (선택)

완료 후 StoryController reveal 메서드와 업데이트된 Room/Show.jsx, ResultCard 컴포넌트를 보여주세요.

기대 결과: 방장이 '결과 공개' 버튼을 누르면 모든 투표 카드가 공개되고, 평균과 중앙값이 계산되어 표시됩니다. 스토리 목록에서 상태별로 스토리를 확인할 수 있으며, 재투표 기능으로 다시 투표할 수 있습니다.

6
Step 6
실시간 업데이트 및 최종 UX 개선을 해주세요.

1. **폴링 또는 자동 새로고침 구현:**
   - Room/Show.jsx에서 useEffect로 5초마다 방 데이터 자동 새로고침
   - Inertia의 router.reload() 또는 router.visit() 활용
   - 투표/결과 공개 시 자동으로 다른 참가자 화면도 업데이트되도록

2. **UI/UX 개선:**
   - 투표 완료 참가자 수 / 전체 참가자 수 표시 (예: "3/5 투표 완료")
   - 모든 참가자가 투표 완료 시 자동으로 알림 표시 또는 효과음 (선택)
   - 로딩 스피너 추가 (투표 제출 중, 결과 공개 중)
   - 토스트 알림 (투표 완료, 스토리 추가, 결과 공개 등)

3. **방 코드 공유 기능:**
   - 방 코드 옆에 '복사' 버튼 추가 (클립보드 복사)
   - 공유 링크 생성 (예: http://localhost:8000/rooms/{code})

4. **히스토리 기능 (보너스):**
   - 완료된 스토리 목록 하단에 표시
   - 각 스토리의 최종 투표 결과 요약 표시

5. **다크 모드 토글 (보너스):**
   - 헤더에 다크 모드 토글 버튼 추가
   - Tailwind CSS의 dark: 클래스 활용
   - localStorage에 테마 설정 저장

6. **테스트 및 최종 검증:**
   - 방 생성 → 참가자 입장 → 스토리 추가 → 투표 → 결과 공개 → 재투표 전체 플로우 테스트
   - 에러 핸들링 (방 코드 없음, 닉네임 중복, 권한 없음 등)

완료 후 최종 Room/Show.jsx 코드와 추가된 기능들을 보여주세요. 전체 애플리케이션이 매끄럽게 작동하는지 확인하고, 스크린샷이나 데모 영상을 제공하면 더 좋습니다.

기대 결과: 방 페이지가 5초마다 자동으로 새로고침되어 다른 참가자의 투표 현황이 실시간으로 반영됩니다. 투표 완료 비율이 표시되고, 방 코드 복사 버튼으로 쉽게 팀원을 초대할 수 있습니다. 최종적으로 완전히 작동하는 플래닝 포커 애플리케이션이 완성됩니다.

제출된 작품 (2)

로그인 후 제출

실시간 협업으로 빠르게 추정하고 합의까지 끝내는 저마찰 플래닝 포커 플랫폼, for 'agile-teams'

조회수 1653 댓글수 0 추천수 1

애자일 팀을 위한 실시간 스토리 포인트 투표 시스템. 원격 팀도 효율적으로 스프린트 계획을 수립할 수 있습니다.

조회수 103 댓글수 0 추천수 1

댓글 (0)

로그인 후 댓글을 남길 수 있습니다.
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!