헤어샵 스타일북 & 예약 플랫폼 - 포트폴리오 갤러리 with 시술 예약
hair-salon-style-booking
문제 설명
헤어샵 스타일북 & 예약 플랫폼
헤어샵을 운영하는 디자이너들이 자신의 시술 포트폴리오를 갤러리 형태로 보여주고, 고객이 마음에 드는 스타일을 선택해 예약할 수 있는 풀스택 플랫폼입니다. Laravel 13.x + Inertia.js + React 19 모노리스 아키텍처로 구현하며, 실제 소규모 헤어샵이나 프리랜서 디자이너가 바로 활용할 수 있는 실용적인 서비스입니다.
필수 기능
- 스타일 포트폴리오 갤러리: 헤어 스타일 이미지 업로드 및 카테고리별 분류 (커트, 펌, 염색, 클리닉 등)
- 스타일 상세 페이지: 시술 시간, 가격, 사용 제품, 시술 과정 설명 표시
- 디자이너 프로필: 경력, 전문 분야, 고객 리뷰 평점 표시
- 예약 시스템: 날짜/시간 선택 캘린더, 원하는 스타일 선택, 고객 정보 입력
- 예약 관리: 예약 목록 조회, 상태 변경 (대기/확정/완료/취소)
- 이미지 업로드: 포트폴리오 사진 등록 및 썸네일 자동 생성
- 필터링 & 검색: 카테고리, 가격대, 시술 시간별 필터링
- 반응형 디자인: 모바일 우선 디자인으로 고객이 스마트폰에서 편하게 예약
보너스 기능
- 고객 리뷰 & 별점 시스템 (시술 완료 후 작성)
- 디자이너별 예약 가능 시간대 자동 표시
- 카카오톡 알림톡 연동 (예약 확정/리마인더)
- 단골 고객 쿠폰 발급 기능
- 비포/애프터 사진 슬라이더
- SNS 공유 버튼 (인스타그램, 페이스북)
이 프로젝트는 실제 헤어샵 비즈니스에서 바로 사용할 수 있는 수준의 UX와 기능을 제공하며, 이커머스 + 예약 플랫폼 + 포트폴리오 쇼케이스를 결합한 트렌디한 서비스입니다.
제약 조건
- • Laravel 13.x + Inertia.js + React 19 모노리스 풀스택 (별도 API 라우트 없음)
- • Laravel Breeze React+Inertia starter kit 또는 `npx create laravel` 사용
- • SQLite 데이터베이스 사용 (개발 환경 간소화)
- • 이미지 업로드는 Laravel Storage 및 public disk 사용
- • Tailwind CSS로 모던하고 반응형 UI 구현
- • Inertia::render()로 Laravel 컨트롤러에서 React 컴포넌트에 데이터 전달
- • React useState/useReducer로 상태 관리 (복잡한 상태관리 라이브러리 금지)
- • Laravel Eloquent ORM으로 DB 조작 (쿼리 빌더 권장)
- • 마이그레이션으로 테이블 스키마 관리
- • 실시간 채팅 기능 금지 (예약 문의는 폼 기반으로 처리)
프롬프트 레시피
아래 프롬프트를 순서대로 AI에게 보내면 됩니다. 복사 버튼을 눌러 바로 사용하세요.
mkdir hair-salon-style-booking && cd hair-salon-style-booking 이 폴더에서 Laravel 13.x + Inertia.js + React 19 프로젝트를 생성합니다. 다음 명령을 실행하여 Laravel Breeze React+Inertia starter kit으로 프로젝트를 초기화하세요: ```bash composer create-project laravel/laravel . composer require laravel/breeze --dev php artisan breeze:install react npm install npm run dev ``` 프로젝트 구조를 확인하고, `resources/js/Pages` 폴더에 React 컴포넌트가 있는지 확인하세요. `.env` 파일에서 DB_CONNECTION을 sqlite로 설정하고, `database/database.sqlite` 파일을 생성하세요. 마이그레이션을 실행하여 기본 테이블을 생성하세요: ```bash php artisan migrate ``` 프로젝트가 정상적으로 실행되는지 확인하세요: ```bash php artisan serve ``` ブラウザ에서 http://localhost:8000 접속 시 Laravel 기본 페이지가 보이면 성공입니다.
기대 결과: Laravel 13.x + Breeze React+Inertia 프로젝트가 생성되고, SQLite DB 연결 및 기본 마이그레이션 완료. 브라우저에서 Laravel 기본 페이지 확인 가능.
헤어샵 스타일북 데이터베이스 스키마를 설계하고 마이그레이션을 생성합니다.
다음 테이블을 생성하세요:
1. **designers** 테이블 (디자이너 정보)
- id (PK)
- name (디자이너 이름)
- bio (자기소개)
- specialty (전문 분야: 커트/펌/염색 등)
- experience_years (경력 년수)
- profile_image (프로필 사진 경로)
- timestamps
2. **styles** 테이블 (헤어 스타일 포트폴리오)
- id (PK)
- designer_id (FK -> designers)
- title (스타일 제목)
- description (시술 설명)
- category (카테고리: 커트/펌/염색/클리닉)
- price (가격)
- duration_minutes (시술 소요 시간)
- image_path (대표 이미지 경로)
- timestamps
3. **bookings** 테이블 (예약 정보)
- id (PK)
- style_id (FK -> styles)
- designer_id (FK -> designers)
- customer_name (고객 이름)
- customer_phone (고객 전화번호)
- customer_email (고객 이메일)
- booking_date (예약 날짜)
- booking_time (예약 시간)
- status (상태: pending/confirmed/completed/cancelled)
- note (요청 사항)
- timestamps
다음 명령으로 마이그레이션 파일을 생성하고 작성하세요:
```bash
php artisan make:migration create_designers_table
php artisan make:migration create_styles_table
php artisan make:migration create_bookings_table
```
각 마이그레이션 파일의 `up()` 메서드에 위 스키마를 반영하세요. 외래키 제약조건도 추가하세요 (onDelete('cascade')).
마이그레이션 실행:
```bash
php artisan migrate
```
Eloquent 모델도 생성하세요:
```bash
php artisan make:model Designer
php artisan make:model Style
php artisan make:model Booking
```
각 모델에 관계를 정의하세요:
- Designer hasMany Styles, hasMany Bookings
- Style belongsTo Designer, hasMany Bookings
- Booking belongsTo Style, belongsTo Designer
기대 결과: designers, styles, bookings 테이블이 SQLite DB에 생성되고, Eloquent 모델에 관계가 정의됨. 마이그레이션 성공 메시지 확인.
시더(Seeder)를 사용하여 샘플 디자이너와 스타일 데이터를 생성합니다.
다음 명령으로 시더 파일을 생성하세요:
```bash
php artisan make:seeder DesignerSeeder
php artisan make:seeder StyleSeeder
```
**DesignerSeeder**에서 3명의 디자이너를 생성하세요 (이름, 자기소개, 전문 분야, 경력, 프로필 이미지 더미 경로).
**StyleSeeder**에서 각 디자이너당 4~5개의 헤어 스타일을 생성하세요 (제목, 설명, 카테고리, 가격, 시술 시간, 이미지 더미 경로). 카테고리는 '커트', '펌', '염색', '클리닉' 중 랜덤하게 배정하세요.
`database/seeders/DatabaseSeeder.php`에서 두 시더를 호출하도록 설정하세요:
```php
public function run()
{
$this->call([
DesignerSeeder::class,
StyleSeeder::class,
]);
}
```
시더 실행:
```bash
php artisan db:seed
```
데이터가 정상적으로 삽입되었는지 확인하세요 (Tinker 또는 DB 브라우저 사용).
기대 결과: designers 테이블에 3명의 디자이너, styles 테이블에 12~15개의 헤어 스타일 샘플 데이터가 생성됨. db:seed 성공 메시지 확인.
헤어 스타일 포트폴리오 갤러리 페이지를 구현합니다.
**라우트 정의** (`routes/web.php`):
```php
use App\Http\Controllers\StyleController;
Route::get('/', [StyleController::class, 'index'])->name('styles.index');
Route::get('/styles/{style}', [StyleController::class, 'show'])->name('styles.show');
```
**컨트롤러 생성**:
```bash
php artisan make:controller StyleController
```
**StyleController**의 `index()` 메서드:
- 모든 스타일을 페이지네이션으로 가져오기 (with 'designer' eager loading)
- 카테고리 필터링 쿼리 파라미터 지원 (?category=커트)
- Inertia::render('Styles/Index', ['styles' => $styles, 'categories' => ['전체', '커트', '펌', '염색', '클리닉']])로 데이터 전달
**StyleController**의 `show()` 메서드:
- 특정 스타일 상세 정보 가져오기 (with 'designer')
- Inertia::render('Styles/Show', ['style' => $style])로 데이터 전달
**React 컴포넌트 생성** (`resources/js/Pages/Styles/Index.jsx`):
- Tailwind CSS로 그리드 레이아웃 (모바일: 1열, 태블릿: 2열, 데스크톱: 3열)
- 각 스타일 카드: 이미지, 제목, 디자이너 이름, 카테고리 배지, 가격, 시술 시간 표시
- 카테고리 필터 버튼 (전체/커트/펌/염색/클리닉)
- 카드 클릭 시 상세 페이지로 이동 (Inertia Link 사용)
**React 컴포넌트 생성** (`resources/js/Pages/Styles/Show.jsx`):
- 스타일 대표 이미지 크게 표시
- 제목, 디자이너 이름, 카테고리, 가격, 시술 시간, 상세 설명 표시
- "예약하기" 버튼 (다음 단계에서 구현)
- "목록으로" 버튼 (Inertia Link로 index 페이지 이동)
더미 이미지는 https://via.placeholder.com/400x500?text=Hair+Style 같은 플레이스홀더 사용.
브라우저에서 http://localhost:8000 접속 시 스타일 갤러리가 보이고, 카테고리 필터링 및 상세 페이지 이동이 작동하는지 확인하세요.
기대 결과: 스타일 갤러리 페이지(Index)와 상세 페이지(Show)가 구현되고, 카테고리 필터링 및 페이지 이동이 정상 작동. Tailwind CSS로 반응형 그리드 레이아웃 확인.
예약 시스템을 구현합니다.
**라우트 추가** (`routes/web.php`):
```php
use App\Http\Controllers\BookingController;
Route::get('/styles/{style}/book', [BookingController::class, 'create'])->name('bookings.create');
Route::post('/bookings', [BookingController::class, 'store'])->name('bookings.store');
Route::get('/bookings', [BookingController::class, 'index'])->name('bookings.index');
```
**컨트롤러 생성**:
```bash
php artisan make:controller BookingController
```
**BookingController**의 `create()` 메서드:
- 특정 스타일 정보 가져오기 (with 'designer')
- 예약 가능한 날짜/시간대 배열 생성 (예: 오늘부터 30일간, 09:00~18:00, 1시간 단위)
- Inertia::render('Bookings/Create', ['style' => $style, 'availableDates' => $dates, 'availableTimes' => $times])로 데이터 전달
**BookingController**의 `store()` 메서드:
- 예약 폼 데이터 검증 (customer_name, customer_phone, customer_email, booking_date, booking_time 필수)
- Booking 모델 생성 (status는 'pending'으로 초기화)
- 성공 메시지와 함께 예약 목록 페이지로 리다이렉트
**BookingController**의 `index()` 메서드:
- 모든 예약 목록 가져오기 (with 'style', 'designer', 최신순 정렬)
- Inertia::render('Bookings/Index', ['bookings' => $bookings])로 데이터 전달
**React 컴포넌트 생성** (`resources/js/Pages/Bookings/Create.jsx`):
- 스타일 정보 요약 표시 (이미지, 제목, 가격, 시술 시간)
- 예약 폼: 고객 이름, 전화번호, 이메일 입력 필드
- 날짜 선택 (드롭다운 또는 date picker)
- 시간대 선택 (버튼 그리드 형태로 시간대 표시)
- 요청 사항 텍스트 영역
- "예약 신청" 버튼 (Inertia useForm 훅 사용하여 POST 요청)
- 에러 메시지 표시 (validation errors)
**React 컴포넌트 생성** (`resources/js/Pages/Bookings/Index.jsx`):
- 예약 목록을 테이블 또는 카드 형태로 표시
- 각 예약: 스타일 제목, 디자이너 이름, 고객 이름, 예약 날짜/시간, 상태 배지 (대기/확정/완료/취소)
- 상태별 색상 구분 (pending: 노란색, confirmed: 파란색, completed: 초록색, cancelled: 회색)
**Styles/Show.jsx 수정**:
- "예약하기" 버튼에 Inertia Link 연결 (href={route('bookings.create', style.id)})
브라우저에서 스타일 상세 페이지 → 예약하기 → 폼 작성 → 예약 목록 확인 플로우가 작동하는지 테스트하세요.
기대 결과: 예약 폼 페이지(Create)와 예약 목록 페이지(Index)가 구현되고, 스타일 상세 페이지에서 예약 플로우가 정상 작동. 예약 데이터가 DB에 저장되고 목록에 표시됨.
디자이너 프로필 페이지와 이미지 업로드 기능을 구현합니다.
**라우트 추가** (`routes/web.php`):
```php
use App\Http\Controllers\DesignerController;
Route::get('/designers/{designer}', [DesignerController::class, 'show'])->name('designers.show');
Route::post('/styles', [StyleController::class, 'store'])->name('styles.store')->middleware('auth');
```
**컨트롤러 생성**:
```bash
php artisan make:controller DesignerController
```
**DesignerController**의 `show()` 메서드:
- 특정 디자이너 정보 가져오기
- 해당 디자이너의 스타일 목록 가져오기 (with 'styles')
- 평균 리뷰 평점 계산 (보너스 기능이므로 일단 더미 값 4.5로 설정)
- Inertia::render('Designers/Show', ['designer' => $designer, 'styles' => $styles])로 데이터 전달
**React 컴포넌트 생성** (`resources/js/Pages/Designers/Show.jsx`):
- 디자이너 프로필 이미지, 이름, 자기소개, 전문 분야, 경력, 평균 평점 표시
- 해당 디자이너의 스타일 포트폴리오 그리드 (Index와 유사한 레이아웃)
- 각 스타일 카드 클릭 시 상세 페이지로 이동
**StyleController**의 `store()` 메서드 (이미지 업로드):
- 인증된 사용자만 접근 가능 (middleware('auth'))
- 스타일 등록 폼 데이터 검증 (title, description, category, price, duration_minutes, image 필수)
- 이미지 업로드 처리:
```php
$imagePath = $request->file('image')->store('styles', 'public');
```
- Style 모델 생성 (designer_id는 현재 로그인한 사용자의 designer_id로 설정, 또는 고정값 사용)
- 성공 메시지와 함께 스타일 목록 페이지로 리다이렉트
**React 컴포넌트 생성** (`resources/js/Pages/Styles/Create.jsx`):
- 스타일 등록 폼: 제목, 설명, 카테고리(드롭다운), 가격, 시술 시간, 이미지 파일 업로드 필드
- Inertia useForm 훅 사용하여 POST 요청 (multipart/form-data)
- "등록하기" 버튼
**라우트 추가**:
```php
Route::get('/styles/create', [StyleController::class, 'create'])->name('styles.create')->middleware('auth');
```
**StyleController**의 `create()` 메서드:
- Inertia::render('Styles/Create')로 빈 폼 페이지 렌더링
**Styles/Index.jsx 수정**:
- 상단에 "스타일 등록" 버튼 추가 (Inertia Link, auth 사용자만 보이도록 조건부 렌더링)
**심볼릭 링크 생성** (업로드된 이미지 접근 가능하도록):
```bash
php artisan storage:link
```
브라우저에서 스타일 등록 → 이미지 업로드 → 갤러리에 새 스타일 표시 → 디자이너 프로필 페이지 확인 플로우를 테스트하세요.
기대 결과: 디자이너 프로필 페이지(Show)와 스타일 등록 폼(Create)이 구현되고, 이미지 업로드 및 저장이 정상 작동. 업로드된 이미지가 갤러리와 프로필 페이지에 표시됨.
제출된 작품 (0)
로그인 후 제출아직 제출된 작품이 없습니다. 첫 번째 도전자가 되어보세요!