동네 공방 클래스 수강후기 플랫폼 - 별점 & 포토 리뷰 공유 - 바이브코딩 레시피
중급 PHP + JavaScript Laravel + React + Inertia.js AI: Claude

동네 공방 클래스 수강후기 플랫폼 - 별점 & 포토 리뷰 공유

리포지토리명 craft-class-review-platform
조회수 54 0건 제출

문제 설명

🎨 동네 공방 클래스 수강후기 플랫폼

도자기, 가죽공예, 캔들 만들기 등 다양한 공방 클래스를 수강한 사람들이 후기를 남기고 공유하는 커뮤니티 플랫폼입니다. 사용자는 공방을 검색하고, 클래스별 리뷰를 확인하며, 자신의 수강 경험을 사진과 함께 공유할 수 있습니다.

필수 기능

  • 공방 목록 페이지: 지역별, 카테고리별(도자기/가죽/캔들/플라워 등) 필터링
  • 공방 상세 페이지: 공방 정보, 운영 클래스 목록, 평균 별점, 리뷰 리스트
  • 리뷰 작성: 별점(1~5), 사진 업로드(최대 3장), 텍스트 후기, 수강한 클래스 선택
  • 리뷰 목록: 최신순/별점순 정렬, 페이지네이션, 사진 썸네일 그리드
  • 좋아요 기능: 리뷰에 공감 표시 (중복 방지)
  • 공방 찜하기: 관심 있는 공방을 북마크하여 마이페이지에서 모아보기
  • 검색 기능: 공방명, 지역, 클래스명으로 검색

보너스 기능

  • 리뷰 작성 시 태그 추가 (#초보자추천 #데이트코스 #친절한강사님)
  • 공방별 평균 별점 차트 (Chart.js)
  • 최근 7일간 인기 공방 TOP 5 위젯
  • 리뷰 신고 기능 (부적절한 내용 신고)
  • 이미지 Lightbox 모달 (리뷰 사진 클릭 시 확대)

제약 조건

  • • Laravel 13.x + Inertia.js + React 19 모노리스 풀스택 구조
  • • Laravel Breeze (React + Inertia) starter kit으로 프로젝트 초기화
  • • SQLite 데이터베이스 사용 (로컬 개발 편의성)
  • • Tailwind CSS로 모던한 UI 스타일링
  • • 이미지 업로드는 Laravel Storage 사용 (public disk)
  • • 별도 REST API 라우트 없이 Inertia::render()로 데이터 전달
  • • Eloquent ORM으로 DB 관계 정의 (공방-클래스-리뷰)
  • • React 컴포넌트는 resources/js/Pages에 작성
  • • Form 처리는 Inertia useForm 훅 활용
  • • 실시간 채팅 기능 금지 (HTTP 기반 댓글/리뷰만 사용)

프롬프트 레시피

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

1
Step 1
mkdir craft-class-review-platform && cd craft-class-review-platform

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

1. Laravel Breeze (React + Inertia) starter kit으로 프로젝트를 생성해주세요.
   - 명령어: `composer create-project laravel/laravel . && composer require laravel/breeze --dev && php artisan breeze:install react`
   - SQLite 데이터베이스를 사용하도록 .env 파일 수정 (DB_CONNECTION=sqlite)
   - `touch database/database.sqlite`로 SQLite 파일 생성

2. 기본 마이그레이션을 실행하여 users 테이블 생성: `php artisan migrate`

3. 다음 데이터베이스 마이그레이션을 생성해주세요:
   - **crafts 테이블**: 공방 정보 (id, name, description, address, region, category, image_path, created_at, updated_at)
   - **classes 테이블**: 클래스 정보 (id, craft_id, name, description, price, duration_minutes, created_at, updated_at)
   - **reviews 테이블**: 리뷰 (id, user_id, craft_id, class_id, rating(1~5), content, created_at, updated_at)
   - **review_images 테이블**: 리뷰 이미지 (id, review_id, image_path, created_at, updated_at)
   - **review_likes 테이블**: 리뷰 좋아요 (id, user_id, review_id, created_at) - user_id와 review_id 조합에 unique 인덱스
   - **craft_bookmarks 테이블**: 공방 찜하기 (id, user_id, craft_id, created_at) - user_id와 craft_id 조합에 unique 인덱스

4. 각 테이블에 맞는 Eloquent 모델을 생성하고, 관계를 정의해주세요:
   - Craft 모델: hasMany(classes), hasMany(reviews), belongsToMany(users, 'craft_bookmarks')
   - Class 모델: belongsTo(craft), hasMany(reviews)
   - Review 모델: belongsTo(user), belongsTo(craft), belongsTo(class), hasMany(reviewImages), belongsToMany(users, 'review_likes')
   - ReviewImage 모델: belongsTo(review)

5. Seeder를 작성하여 더미 데이터를 생성해주세요:
   - 공방 10개 (카테고리: 도자기, 가죽공예, 캔들, 플라워, 베이킹 등)
   - 각 공방마다 클래스 2~4개
   - 리뷰 30개 (랜덤 별점, 더미 텍스트)
   - 리뷰 이미지 20개 (placeholder 이미지 URL)

6. `php artisan db:seed`로 더미 데이터를 삽입하고, `npm install && npm run dev`로 Vite 개발 서버를 실행해주세요.

7. 기본 레이아웃이 작동하는지 확인하기 위해 `php artisan serve`로 서버를 실행하고, 로그인 페이지가 보이는지 확인해주세요.

기대 결과: Laravel Breeze + React + Inertia 프로젝트가 초기화되고, 공방/클래스/리뷰 관련 마이그레이션과 모델이 생성됩니다. 더미 데이터가 삽입되어 개발 준비가 완료됩니다. 로그인 페이지가 정상적으로 렌더링됩니다.

2
Step 2
공방 목록 페이지를 구현해주세요.

1. **라우트 정의** (routes/web.php):
   - `GET /crafts` → CraftController@index

2. **CraftController 생성** (`php artisan make:controller CraftController`):
   - `index()` 메서드: 공방 목록을 조회하고 Inertia::render('Crafts/Index')로 전달
   - 쿼리 파라미터로 필터링 지원:
     - `region` (지역 필터: 서울/경기/부산 등)
     - `category` (카테고리 필터: 도자기/가죽/캔들 등)
     - `search` (공방명 검색)
   - 페이지네이션: 12개씩
   - 각 공방의 평균 별점 계산 (reviews 관계 활용)

3. **React 페이지 컴포넌트** (resources/js/Pages/Crafts/Index.jsx):
   - Authenticated 레이아웃 사용
   - 상단에 필터 UI:
     - 지역 선택 드롭다운
     - 카테고리 버튼 그룹 (전체/도자기/가죽/캔들/플라워/베이킹)
     - 검색 입력창
   - 공방 카드 그리드 (3열, 반응형):
     - 공방 이미지 (썸네일)
     - 공방명, 지역, 카테고리
     - 평균 별점 (별 아이콘 + 숫자)
     - 리뷰 개수
     - "상세보기" 링크 버튼
   - 페이지네이션 컴포넌트 (Inertia Links)
   - Tailwind CSS로 모던한 카드 디자인 (hover 효과, 그림자)

4. 필터 변경 시 Inertia.get()으로 쿼리 파라미터를 업데이트하여 서버에서 필터링된 데이터를 받아오도록 구현해주세요.

5. `/crafts` 경로로 접속하여 공방 목록이 잘 표시되는지 확인해주세요.

기대 결과: 공방 목록 페이지가 완성됩니다. 지역/카테고리 필터와 검색 기능이 작동하며, 공방 카드가 그리드 형태로 깔끔하게 표시됩니다. 페이지네이션이 정상 작동합니다.

3
Step 3
공방 상세 페이지를 구현해주세요.

1. **라우트 정의** (routes/web.php):
   - `GET /crafts/{craft}` → CraftController@show

2. **CraftController@show 메서드**:
   - 공방 정보를 조회하고, 다음 데이터를 함께 로드:
     - 운영 중인 클래스 목록 (classes 관계)
     - 리뷰 목록 (reviews 관계, 최신순 정렬, 페이지네이션 10개)
     - 각 리뷰의 작성자 정보 (user), 리뷰 이미지 (reviewImages), 좋아요 수
     - 평균 별점, 별점별 리뷰 개수 (5점: n개, 4점: m개 등)
     - 현재 로그인한 사용자가 이 공방을 찜했는지 여부
   - Inertia::render('Crafts/Show')로 데이터 전달

3. **React 페이지 컴포넌트** (resources/js/Pages/Crafts/Show.jsx):
   - **공방 정보 섹션**:
     - 공방 이미지 (큰 사이즈)
     - 공방명, 주소, 카테고리
     - 평균 별점 (큰 별 아이콘 + 숫자)
     - 찜하기 버튼 (하트 아이콘, 클릭 시 토글)
   - **별점 분포 차트**:
     - 5점~1점까지 가로 막대 그래프 (각 별점의 비율)
   - **클래스 목록 섹션**:
     - 클래스 카드 (클래스명, 설명, 가격, 소요시간)
   - **리뷰 목록 섹션**:
     - 정렬 옵션 (최신순/별점 높은순/좋아요 많은순)
     - 각 리뷰 카드:
       - 작성자 이름, 작성일
       - 별점 (별 아이콘)
       - 수강한 클래스명
       - 리뷰 텍스트
       - 리뷰 이미지 (썸네일 그리드, 클릭 시 확대 - 일단 새 탭으로 열기)
       - 좋아요 버튼 (하트 아이콘 + 좋아요 수)
     - 페이지네이션
   - "리뷰 작성하기" 버튼 (로그인한 사용자만 표시)

4. **찜하기 기능 구현**:
   - 라우트: `POST /crafts/{craft}/bookmark` → CraftController@toggleBookmark
   - 이미 찜한 경우 삭제, 아니면 추가 (toggle)
   - Inertia.post()로 요청 후 페이지 리로드

5. **리뷰 좋아요 기능 구현**:
   - 라우트: `POST /reviews/{review}/like` → ReviewController@toggleLike
   - 이미 좋아요한 경우 삭제, 아니면 추가 (toggle)
   - Inertia.post()로 요청 후 해당 리뷰만 업데이트 (preserveScroll 옵션)

6. 공방 상세 페이지가 잘 표시되고, 찜하기/좋아요 기능이 작동하는지 확인해주세요.

기대 결과: 공방 상세 페이지가 완성됩니다. 공방 정보, 클래스 목록, 리뷰 목록이 표시되며, 찜하기와 리뷰 좋아요 기능이 정상 작동합니다. 별점 분포 차트가 시각적으로 표시됩니다.

4
Step 4
리뷰 작성 페이지를 구현해주세요.

1. **라우트 정의** (routes/web.php):
   - `GET /crafts/{craft}/reviews/create` → ReviewController@create
   - `POST /reviews` → ReviewController@store

2. **ReviewController 생성** (`php artisan make:controller ReviewController`):
   - `create($craftId)` 메서드:
     - 해당 공방의 클래스 목록을 조회
     - Inertia::render('Reviews/Create')로 공방 정보와 클래스 목록 전달
   - `store(Request $request)` 메서드:
     - 유효성 검사:
       - craft_id (required, exists:crafts)
       - class_id (required, exists:classes)
       - rating (required, integer, 1~5)
       - content (required, string, max:1000)
       - images (optional, array, max:3개, each: image, max:2MB)
     - 리뷰 생성
     - 이미지 업로드: Storage::disk('public')->put('review-images', $image)
     - ReviewImage 레코드 생성
     - 공방 상세 페이지로 리다이렉트 (성공 메시지)

3. **React 페이지 컴포넌트** (resources/js/Pages/Reviews/Create.jsx):
   - Authenticated 레이아웃 사용
   - 공방 정보 표시 (공방명, 이미지)
   - **리뷰 작성 폼**:
     - 수강한 클래스 선택 (드롭다운)
     - 별점 선택 (별 아이콘 5개, 클릭하여 선택)
     - 리뷰 내용 입력 (textarea, placeholder: "수업은 어떠셨나요? 솔직한 후기를 남겨주세요!")
     - 사진 업로드 (최대 3장, 미리보기 표시)
     - "리뷰 등록" 버튼
   - Inertia useForm 훅으로 폼 상태 관리
   - 이미지 미리보기: FileReader API 사용
   - 업로드 진행 중 로딩 스피너 표시

4. 리뷰 작성 후 공방 상세 페이지로 돌아가서 새로 작성된 리뷰가 목록에 표시되는지 확인해주세요.

5. 이미지 업로드가 정상적으로 작동하고, storage/app/public/review-images 폴더에 이미지가 저장되는지 확인해주세요. (`php artisan storage:link`로 심볼릭 링크 생성)

기대 결과: 리뷰 작성 페이지가 완성됩니다. 사용자는 별점, 클래스, 텍스트, 사진을 입력하여 리뷰를 등록할 수 있습니다. 이미지 업로드와 미리보기가 정상 작동하며, 리뷰가 DB에 저장됩니다.

5
Step 5
마이페이지를 구현하여 찜한 공방 목록과 내가 작성한 리뷰를 확인할 수 있도록 해주세요.

1. **라우트 정의** (routes/web.php):
   - `GET /mypage` → UserController@mypage

2. **UserController 생성** (`php artisan make:controller UserController`):
   - `mypage()` 메서드:
     - 현재 로그인한 사용자의 찜한 공방 목록 조회 (craft_bookmarks 관계, 최신순)
     - 내가 작성한 리뷰 목록 조회 (reviews 관계, 최신순, 공방/클래스 정보 포함)
     - Inertia::render('User/Mypage')로 데이터 전달

3. **React 페이지 컴포넌트** (resources/js/Pages/User/Mypage.jsx):
   - Authenticated 레이아웃 사용
   - **탭 UI**:
     - "찜한 공방" 탭
     - "내 리뷰" 탭
   - **찜한 공방 탭**:
     - 공방 카드 그리드 (공방 목록 페이지와 동일한 디자인)
     - 찜하기 해제 버튼 (하트 아이콘)
     - 빈 상태 메시지: "아직 찜한 공방이 없습니다."
   - **내 리뷰 탭**:
     - 리뷰 카드 리스트:
       - 공방명, 클래스명
       - 별점, 작성일
       - 리뷰 텍스트 (앞부분만 표시, "더보기" 링크)
       - 리뷰 이미지 썸네일
       - 좋아요 수
       - "수정" / "삭제" 버튼
     - 빈 상태 메시지: "아직 작성한 리뷰가 없습니다."

4. **리뷰 삭제 기능 구현**:
   - 라우트: `DELETE /reviews/{review}` → ReviewController@destroy
   - 본인이 작성한 리뷰만 삭제 가능하도록 권한 체크 (Policy 사용 권장)
   - 삭제 시 관련 이미지 파일도 함께 삭제 (Storage::delete())
   - Inertia.delete()로 요청 후 마이페이지 리로드

5. 마이페이지에서 찜한 공방과 내 리뷰가 잘 표시되는지, 리뷰 삭제 기능이 작동하는지 확인해주세요.

기대 결과: 마이페이지가 완성됩니다. 찜한 공방 목록과 내가 작성한 리뷰 목록을 탭으로 구분하여 확인할 수 있습니다. 리뷰 삭제 기능이 정상 작동합니다.

6
Step 6
검색 기능과 전체 UI/UX를 개선해주세요.

1. **통합 검색 페이지** (선택 사항, 또는 공방 목록 페이지에 통합):
   - 검색창에 공방명, 지역, 클래스명을 입력하면 관련 공방 목록 표시
   - 검색 결과 하이라이트 (검색어 부분 강조)

2. **UI/UX 개선**:
   - **반응형 디자인**: 모바일/태블릿에서도 잘 보이도록 Tailwind 반응형 클래스 활용
   - **로딩 상태**: Inertia 요청 중 로딩 스피너 또는 스켈레톤 UI 표시
   - **빈 상태 메시지**: 리뷰가 없거나 검색 결과가 없을 때 안내 메시지
   - **토스트 알림**: 리뷰 등록, 찜하기, 좋아요 등 액션 후 성공/실패 토스트 메시지 (react-hot-toast 라이브러리 사용 권장)
   - **이미지 최적화**: 썸네일 이미지 lazy loading (loading="lazy" 속성)
   - **접근성**: 버튼에 적절한 aria-label, 키보드 네비게이션 지원

3. **성능 최적화**:
   - 공방 목록 페이지에서 N+1 쿼리 방지 (Eager Loading: `with(['reviews', 'classes'])`)
   - 이미지 업로드 시 리사이징 (Intervention Image 라이브러리 사용 권장, 최대 1200px)

4. **보너스 기능 추가** (선택 사항):
   - **최근 7일간 인기 공방 TOP 5**: 홈 페이지에 위젯 표시 (리뷰 수 + 평균 별점 기준)
   - **리뷰 신고 기능**: 부적절한 리뷰 신고 버튼 (신고 사유 선택, 관리자 검토용 데이터 저장)
   - **이미지 Lightbox**: 리뷰 사진 클릭 시 Lightbox 모달로 확대 (react-image-lightbox 라이브러리 사용)
   - **태그 기능**: 리뷰 작성 시 태그 추가 (#초보자추천 #데이트코스), 태그별 필터링

5. 전체 플로우를 테스트해주세요:
   - 회원가입 → 공방 검색 → 공방 상세 확인 → 찜하기 → 리뷰 작성 → 마이페이지에서 확인 → 리뷰 삭제
   - 모바일 화면에서도 레이아웃이 깨지지 않는지 확인

6. README.md 파일을 작성하여 프로젝트 설명, 설치 방법, 실행 방법을 문서화해주세요.

기대 결과: 검색 기능이 완성되고, 전체 UI/UX가 개선됩니다. 반응형 디자인, 로딩 상태, 토스트 알림이 적용되어 사용자 경험이 향상됩니다. 보너스 기능이 추가되면 더욱 풍부한 플랫폼이 완성됩니다. README 문서가 작성되어 프로젝트가 완성됩니다.

제출된 작품 (0)

로그인 후 제출

아직 제출된 작품이 없습니다. 첫 번째 도전자가 되어보세요!

댓글 (0)

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