๐ŸŽŸ๏ธ ๊ณต์—ฐ ํ‹ฐ์ผ“ ์œ„์‹œ๋ฆฌ์ŠคํŠธ - ๋ณด๊ณ  ์‹ถ์€ ๊ณต์—ฐ ๋‹ด์•„๋‘๊ธฐ - ๋ฐ”์ด๋ธŒ์ฝ”๋”ฉ ๋ ˆ์‹œํ”ผ
์ž…๋ฌธ PHP + JavaScript Laravel + React AI: Claude

๐ŸŽŸ๏ธ ๊ณต์—ฐ ํ‹ฐ์ผ“ ์œ„์‹œ๋ฆฌ์ŠคํŠธ - ๋ณด๊ณ  ์‹ถ์€ ๊ณต์—ฐ ๋‹ด์•„๋‘๊ธฐ

๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ช… show-wishlist-tracker
์กฐํšŒ์ˆ˜ 31 0๊ฑด ์ œ์ถœ

๋ฌธ์ œ ์„ค๋ช…

ํ”„๋กœ์ ํŠธ ๊ฐœ์š”

๊ณต์—ฐยท์ „์‹œยท์ฝ˜์„œํŠธ ๋“ฑ ๋ณด๊ณ  ์‹ถ์€ ๋ฌธํ™” ํ–‰์‚ฌ๋ฅผ ์œ„์‹œ๋ฆฌ์ŠคํŠธ์— ๋‹ด์•„๋‘๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๊ฐ„๋‹จํ•œ ํ’€์Šคํƒ ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค. Laravel 13.x๋กœ RESTful API๋ฅผ ๋งŒ๋“ค๊ณ , React 19๋กœ ์‚ฌ์šฉ์ž ์นœํ™”์ ์ธ ํ”„๋ก ํŠธ์—”๋“œ๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

ํ•„์ˆ˜ ๊ธฐ๋Šฅ

  • ๊ณต์—ฐ ์ •๋ณด ์ž…๋ ฅ: ๊ณต์—ฐ๋ช…, ์žฅ์†Œ, ๋‚ ์งœ, ๊ฐ€๊ฒฉ, ์žฅ๋ฅด(๋ฎค์ง€์ปฌ/์ฝ˜์„œํŠธ/์ „์‹œ ๋“ฑ), ์˜ˆ๋งค ๋งํฌ๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ์œ„์‹œ๋ฆฌ์ŠคํŠธ์— ์ถ”๊ฐ€
  • ์œ„์‹œ๋ฆฌ์ŠคํŠธ ๋ชฉ๋ก: ๋“ฑ๋ก๋œ ๊ณต์—ฐ์„ ์นด๋“œ ํ˜•ํƒœ๋กœ ๋‚˜์—ด (๋‚ ์งœ ์ˆœ ์ •๋ ฌ)
  • ์žฅ๋ฅด๋ณ„ ํ•„ํ„ฐ: ์žฅ๋ฅด ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ํ•ด๋‹น ์žฅ๋ฅด ๊ณต์—ฐ๋งŒ ํ‘œ์‹œ
  • ๊ด€๋žŒ ์™„๋ฃŒ ์ฒดํฌ: ๊ณต์—ฐ์„ ๋ณด๊ณ  ๋‚˜๋ฉด '๊ด€๋žŒ ์™„๋ฃŒ' ์ฒดํฌ๋ฐ•์Šค๋กœ ํ‘œ์‹œ, ์™„๋ฃŒ๋œ ํ•ญ๋ชฉ์€ ํšŒ์ƒ‰ ์ฒ˜๋ฆฌ
  • ์‚ญ์ œ ๊ธฐ๋Šฅ: ์œ„์‹œ๋ฆฌ์ŠคํŠธ์—์„œ ํ•ญ๋ชฉ ์ œ๊ฑฐ
  • ๋ฐ˜์‘ํ˜• ๋””์ž์ธ: ๋ชจ๋ฐ”์ผ/๋ฐ์Šคํฌํ†ฑ ๋ชจ๋‘ ๊น”๋”ํ•œ UI

๋ณด๋„ˆ์Šค ๊ธฐ๋Šฅ

  • D-day ์นด์šดํ„ฐ: ๊ณต์—ฐ ๋‚ ์งœ๊นŒ์ง€ ๋‚จ์€ ์ผ์ˆ˜ ํ‘œ์‹œ
  • ๊ฐ€๊ฒฉ ํ•ฉ๊ณ„: ์œ„์‹œ๋ฆฌ์ŠคํŠธ ์ด ์˜ˆ์ƒ ๊ธˆ์•ก ๊ณ„์‚ฐ
  • ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ: ๊ณต์—ฐ๋ช…์œผ๋กœ ๋น ๋ฅด๊ฒŒ ์ฐพ๊ธฐ
  • ๊ณต์—ฐ ํฌ์Šคํ„ฐ ์ด๋ฏธ์ง€ URL ์ถ”๊ฐ€ (์„ ํƒ ์‚ฌํ•ญ)
  • ๋ฉ”๋ชจ ํ•„๋“œ: ํ•จ๊ป˜ ๋ณผ ์‚ฌ๋žŒ, ์ขŒ์„ ์„ ํ˜ธ๋„ ๋“ฑ ๊ฐ„๋‹จํ•œ ๋ฉ”๋ชจ ์ž‘์„ฑ

๊ธฐ์ˆ  ์Šคํƒ

  • ๋ฐฑ์—”๋“œ: Laravel 13.x (RESTful API, SQLite)
  • ํ”„๋ก ํŠธ์—”๋“œ: React 19 (Vite)
  • ์Šคํƒ€์ผ๋ง: Tailwind CSS
  • ํ†ต์‹ : fetch API

ํ•™์Šต ๋ชฉํ‘œ

  • Laravel API ๋ผ์šฐํŠธ ์„ค๊ณ„ ๋ฐ ์ปจํŠธ๋กค๋Ÿฌ ์ž‘์„ฑ
  • React ์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌ ๋ฐ ์ƒํƒœ ๊ด€๋ฆฌ (useState)
  • fetch๋ฅผ ์ด์šฉํ•œ CRUD API ํ˜ธ์ถœ
  • ํ•„ํ„ฐ๋ง ๋ฐ ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง
  • Tailwind CSS ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค ํ™œ์šฉ

์ œ์•ฝ ์กฐ๊ฑด

  • โ€ข Laravel 13.x ์ตœ์‹  ๋ฒ„์ „ ์‚ฌ์šฉ
  • โ€ข React 19 ์ตœ์‹  ๋ฒ„์ „ ์‚ฌ์šฉ
  • โ€ข SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‚ฌ์šฉ (๊ฐ„๋‹จํ•œ ์„ค์ •)
  • โ€ข Laravel์€ API ์ „์šฉ ๋ฐฑ์—”๋“œ, React๋Š” ๋ณ„๋„ Vite ํ”„๋กœ์ ํŠธ๋กœ ๋ถ„๋ฆฌ
  • โ€ข CORS ์„ค์ • ํ•„์ˆ˜ (Laravel์—์„œ React ํ”„๋ก ํŠธ์—”๋“œ ์š”์ฒญ ํ—ˆ์šฉ)
  • โ€ข Tailwind CSS๋กœ ์Šคํƒ€์ผ๋ง
  • โ€ข ๋ณต์žกํ•œ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ ๊ธˆ์ง€ (useState๋งŒ ์‚ฌ์šฉ)
  • โ€ข ์‹ค์‹œ๊ฐ„ ๊ธฐ๋Šฅ(WebSocket) ์‚ฌ์šฉ ๊ธˆ์ง€
  • โ€ข ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ตœ์†Œํ™” (๊ธฐ๋ณธ React + Tailwind + fetch๋งŒ ์‚ฌ์šฉ)

ํ”„๋กฌํ”„ํŠธ ๋ ˆ์‹œํ”ผ

์•„๋ž˜ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ˆœ์„œ๋Œ€๋กœ AI์—๊ฒŒ ๋ณด๋‚ด๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋ณต์‚ฌ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ๋ฐ”๋กœ ์‚ฌ์šฉํ•˜์„ธ์š”.

1
Step 1
mkdir show-wishlist-tracker && cd show-wishlist-tracker

์ด ํด๋”์—์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

# Step 1: Laravel 13.x API ๋ฐฑ์—”๋“œ ์ดˆ๊ธฐ ์„ค์ •

Laravel 13.x ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์„ค์ •ํ•ด์ฃผ์„ธ์š”.

## ์š”๊ตฌ์‚ฌํ•ญ

1. `backend` ํด๋”์— Laravel 13.x ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
2. `.env` ํŒŒ์ผ์—์„œ DB_CONNECTION์„ sqlite๋กœ ๋ณ€๊ฒฝ
3. `database/database.sqlite` ๋นˆ ํŒŒ์ผ ์ƒ์„ฑ
4. `shows` ํ…Œ์ด๋ธ” ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ƒ์„ฑ:
   - id (primary key)
   - title (๊ณต์—ฐ๋ช…, string)
   - venue (์žฅ์†Œ, string)
   - date (๊ณต์—ฐ ๋‚ ์งœ, date)
   - price (๊ฐ€๊ฒฉ, integer, nullable)
   - genre (์žฅ๋ฅด, string: ๋ฎค์ง€์ปฌ/์ฝ˜์„œํŠธ/์ „์‹œ/์—ฐ๊ทน ๋“ฑ)
   - booking_url (์˜ˆ๋งค ๋งํฌ, string, nullable)
   - is_watched (๊ด€๋žŒ ์™„๋ฃŒ ์—ฌ๋ถ€, boolean, default false)
   - notes (๋ฉ”๋ชจ, text, nullable)
   - timestamps
5. ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹คํ–‰
6. `Show` ๋ชจ๋ธ ์ƒ์„ฑ (fillable ์„ค์ • ํฌํ•จ)

## ๋ช…๋ น์–ด ์˜ˆ์‹œ

```bash
cd show-wishlist-tracker
composer create-project laravel/laravel:^13.0 backend
cd backend
touch database/database.sqlite
php artisan migrate
```

๋ชจ๋“  ์„ค์ •์ด ์™„๋ฃŒ๋˜๋ฉด "Laravel ๋ฐฑ์—”๋“œ ์ดˆ๊ธฐ ์„ค์ • ์™„๋ฃŒ"๋ผ๊ณ  ์•Œ๋ ค์ฃผ์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: Laravel 13.x ํ”„๋กœ์ ํŠธ๊ฐ€ `backend` ํด๋”์— ์ƒ์„ฑ๋˜๊ณ , SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ • ๋ฐ `shows` ํ…Œ์ด๋ธ” ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค. Show ๋ชจ๋ธ์ด ์ƒ์„ฑ๋˜์–ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.

2
Step 2
# Step 2: Laravel API ๋ผ์šฐํŠธ ๋ฐ ์ปจํŠธ๋กค๋Ÿฌ ๊ตฌํ˜„

๊ณต์—ฐ ์œ„์‹œ๋ฆฌ์ŠคํŠธ CRUD API๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.

## ์š”๊ตฌ์‚ฌํ•ญ

1. `ShowController` ์ƒ์„ฑ (API ๋ฆฌ์†Œ์Šค ์ปจํŠธ๋กค๋Ÿฌ)
2. `routes/api.php`์— ๋‹ค์Œ ๋ผ์šฐํŠธ ๋“ฑ๋ก:
   - GET `/api/shows` - ์ „์ฒด ๊ณต์—ฐ ๋ชฉ๋ก ์กฐํšŒ (๋‚ ์งœ ์ˆœ ์ •๋ ฌ)
   - POST `/api/shows` - ์ƒˆ ๊ณต์—ฐ ์ถ”๊ฐ€
   - PUT `/api/shows/{id}` - ๊ณต์—ฐ ์ •๋ณด ์ˆ˜์ • (๊ด€๋žŒ ์™„๋ฃŒ ์ฒดํฌ ํฌํ•จ)
   - DELETE `/api/shows/{id}` - ๊ณต์—ฐ ์‚ญ์ œ
3. ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ ๊ตฌํ˜„:
   - index(): ๋ชจ๋“  ๊ณต์—ฐ์„ ๋‚ ์งœ ์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ ๋ฐ˜ํ™˜ (JSON)
   - store(): ์š”์ฒญ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒ€์ฆํ•˜๊ณ  ์ƒˆ ๊ณต์—ฐ ์ƒ์„ฑ (title, venue, date, genre ํ•„์ˆ˜)
   - update(): ๊ณต์—ฐ ์ •๋ณด ์—…๋ฐ์ดํŠธ (is_watched ํ† ๊ธ€ ํฌํ•จ)
   - destroy(): ๊ณต์—ฐ ์‚ญ์ œ
4. CORS ์„ค์ •: `config/cors.php`์—์„œ `'paths' => ['api/*']`, `'allowed_origins' => ['http://localhost:5173']` ์„ค์ • (React Vite ๊ธฐ๋ณธ ํฌํŠธ)
5. `.env`์—์„œ `APP_URL=http://localhost:8000` ํ™•์ธ

## ๊ฒ€์ฆ

Postman์ด๋‚˜ curl๋กœ API ํ…Œ์ŠคํŠธ:

```bash
# ๊ณต์—ฐ ์ถ”๊ฐ€
curl -X POST http://localhost:8000/api/shows \
  -H "Content-Type: application/json" \
  -d '{"title":"์˜คํŽ˜๋ผ์˜ ์œ ๋ น","venue":"์ƒฌ๋กฏ๋ฐ์”จ์–ดํ„ฐ","date":"2025-06-15","genre":"๋ฎค์ง€์ปฌ","price":150000}'

# ๊ณต์—ฐ ๋ชฉ๋ก ์กฐํšŒ
curl http://localhost:8000/api/shows
```

๋ชจ๋“  API ์—”๋“œํฌ์ธํŠธ๊ฐ€ ์ •์ƒ ์ž‘๋™ํ•˜๋ฉด "API ๊ตฌํ˜„ ์™„๋ฃŒ"๋ผ๊ณ  ์•Œ๋ ค์ฃผ์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: Laravel API ๋ผ์šฐํŠธ์™€ ShowController๊ฐ€ ์ƒ์„ฑ๋˜์–ด CRUD ๊ธฐ๋Šฅ์ด ์™„์„ฑ๋ฉ๋‹ˆ๋‹ค. CORS ์„ค์ •์œผ๋กœ React ํ”„๋ก ํŠธ์—”๋“œ์—์„œ API ํ˜ธ์ถœ์ด ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค. Postman/curl๋กœ API ํ…Œ์ŠคํŠธ ์„ฑ๊ณต.

3
Step 3
# Step 3: React 19 ํ”„๋ก ํŠธ์—”๋“œ ์ดˆ๊ธฐ ์„ค์ • ๋ฐ ๊ธฐ๋ณธ UI

`frontend` ํด๋”์— React 19 + Vite + Tailwind CSS ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ธฐ๋ณธ ๋ ˆ์ด์•„์›ƒ์„ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.

## ์š”๊ตฌ์‚ฌํ•ญ

1. ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ์—์„œ `frontend` ํด๋” ์ƒ์„ฑ ํ›„ React + Vite ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐํ™”:
   ```bash
   npm create vite@latest frontend -- --template react
   cd frontend
   npm install
   ```
2. Tailwind CSS ์„ค์น˜ ๋ฐ ์„ค์ •:
   ```bash
   npm install -D tailwindcss postcss autoprefixer
   npx tailwindcss init -p
   ```
   - `tailwind.config.js`์—์„œ `content: ["./index.html", "./src/**/*.{js,jsx}"]` ์„ค์ •
   - `src/index.css`์— Tailwind ์ง€์‹œ์–ด ์ถ”๊ฐ€:
     ```css
     @tailwind base;
     @tailwind components;
     @tailwind utilities;
     ```
3. `src/App.jsx` ๊ธฐ๋ณธ ๋ ˆ์ด์•„์›ƒ ์ž‘์„ฑ:
   - ํ—ค๋”: "๐ŸŽŸ๏ธ ๊ณต์—ฐ ์œ„์‹œ๋ฆฌ์ŠคํŠธ" ํƒ€์ดํ‹€
   - ๊ณต์—ฐ ์ถ”๊ฐ€ ํผ ์˜์—ญ (title, venue, date, price, genre ์„ ํƒ, booking_url, notes ์ž…๋ ฅ ํ•„๋“œ)
   - ์žฅ๋ฅด ํ•„ํ„ฐ ๋ฒ„ํŠผ (์ „์ฒด/๋ฎค์ง€์ปฌ/์ฝ˜์„œํŠธ/์ „์‹œ/์—ฐ๊ทน)
   - ๊ณต์—ฐ ๋ชฉ๋ก ์นด๋“œ ์˜์—ญ (๋นˆ ์ƒํƒœ ๋ฉ”์‹œ์ง€: "์•„์ง ๋“ฑ๋ก๋œ ๊ณต์—ฐ์ด ์—†์Šต๋‹ˆ๋‹ค")
4. Tailwind CSS๋กœ ์Šคํƒ€์ผ๋ง:
   - ํ—ค๋”: ๊ทธ๋ผ๋ฐ์ด์…˜ ๋ฐฐ๊ฒฝ, ํฐ ํฐํŠธ
   - ํผ: ๊ทธ๋ฆฌ๋“œ ๋ ˆ์ด์•„์›ƒ, ์ธํ’‹ ํ•„๋“œ ์Šคํƒ€์ผ๋ง
   - ์นด๋“œ: ๊ทธ๋ฆผ์ž, ํ˜ธ๋ฒ„ ํšจ๊ณผ
5. ๊ฐœ๋ฐœ ์„œ๋ฒ„ ์‹คํ–‰: `npm run dev` (http://localhost:5173)

## ๊ธฐ๋Œ€ ๊ฒฐ๊ณผ

๋ธŒ๋ผ์šฐ์ €์—์„œ ํ—ค๋”์™€ ํผ, ๋นˆ ๋ชฉ๋ก ์˜์—ญ์ด ๋ณด์ด๋Š” ๊ธฐ๋ณธ UI๊ฐ€ ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค. ์•„์ง API ์—ฐ๋™์€ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์™„๋ฃŒ๋˜๋ฉด "React ํ”„๋ก ํŠธ์—”๋“œ ๊ธฐ๋ณธ UI ์™„์„ฑ"์ด๋ผ๊ณ  ์•Œ๋ ค์ฃผ์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: React 19 + Vite + Tailwind CSS ํ”„๋กœ์ ํŠธ๊ฐ€ `frontend` ํด๋”์— ์ƒ์„ฑ๋˜๊ณ , ๊ณต์—ฐ ์œ„์‹œ๋ฆฌ์ŠคํŠธ์˜ ๊ธฐ๋ณธ UI(ํ—ค๋”, ํผ, ํ•„ํ„ฐ ๋ฒ„ํŠผ, ๋นˆ ๋ชฉ๋ก)๊ฐ€ ํ™”๋ฉด์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ์•„์ง ๋ฐ์ดํ„ฐ๋Š” ์—†์ง€๋งŒ ๋ ˆ์ด์•„์›ƒ์€ ์™„์„ฑ๋œ ์ƒํƒœ์ž…๋‹ˆ๋‹ค.

4
Step 4
# Step 4: React์—์„œ Laravel API ์—ฐ๋™ - ๊ณต์—ฐ ๋ชฉ๋ก ์กฐํšŒ ๋ฐ ์ถ”๊ฐ€

`App.jsx`์—์„œ useState์™€ fetch๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Laravel API์™€ ์—ฐ๋™ํ•˜๊ณ , ๊ณต์—ฐ ๋ชฉ๋ก์„ ๋ถˆ๋Ÿฌ์˜ค๊ณ  ์ƒˆ ๊ณต์—ฐ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด์ฃผ์„ธ์š”.

## ์š”๊ตฌ์‚ฌํ•ญ

1. `useState`๋กœ ์ƒํƒœ ๊ด€๋ฆฌ:
   - `shows`: ๊ณต์—ฐ ๋ชฉ๋ก ๋ฐฐ์—ด
   - `formData`: ํผ ์ž…๋ ฅ ๋ฐ์ดํ„ฐ (title, venue, date, price, genre, booking_url, notes)
   - `filter`: ํ˜„์žฌ ์„ ํƒ๋œ ์žฅ๋ฅด ํ•„ํ„ฐ (๊ธฐ๋ณธ๊ฐ’ '์ „์ฒด')
2. `useEffect`๋กœ ์ปดํฌ๋„ŒํŠธ ๋งˆ์šดํŠธ ์‹œ API์—์„œ ๊ณต์—ฐ ๋ชฉ๋ก fetch:
   ```javascript
   useEffect(() => {
     fetch('http://localhost:8000/api/shows')
       .then(res => res.json())
       .then(data => setShows(data));
   }, []);
   ```
3. ํผ ์ œ์ถœ ํ•ธ๋“ค๋Ÿฌ (`handleSubmit`):
   - POST ์š”์ฒญ์œผ๋กœ `http://localhost:8000/api/shows`์— formData ์ „์†ก
   - ์„ฑ๊ณต ์‹œ ์ƒˆ ๊ณต์—ฐ์„ `shows` ๋ฐฐ์—ด์— ์ถ”๊ฐ€ํ•˜๊ณ  ํผ ์ดˆ๊ธฐํ™”
4. ์žฅ๋ฅด ํ•„ํ„ฐ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ `filter` ์ƒํƒœ ๋ณ€๊ฒฝ
5. ๊ณต์—ฐ ๋ชฉ๋ก ๋ Œ๋”๋ง:
   - `filter`๊ฐ€ '์ „์ฒด'๋ฉด ๋ชจ๋“  ๊ณต์—ฐ ํ‘œ์‹œ, ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ํ•ด๋‹น ์žฅ๋ฅด๋งŒ ํ•„ํ„ฐ๋ง
   - ๊ฐ ๊ณต์—ฐ์„ ์นด๋“œ ํ˜•ํƒœ๋กœ ํ‘œ์‹œ (์ œ๋ชฉ, ์žฅ์†Œ, ๋‚ ์งœ, ๊ฐ€๊ฒฉ, ์žฅ๋ฅด, ์˜ˆ๋งค ๋งํฌ ๋ฒ„ํŠผ)
   - ๊ด€๋žŒ ์™„๋ฃŒ๋œ ๊ณต์—ฐ์€ ํšŒ์ƒ‰ ๋ฐฐ๊ฒฝ + ์ฒดํฌ ์•„์ด์ฝ˜ ํ‘œ์‹œ
6. ๋นˆ ์ƒํƒœ ์ฒ˜๋ฆฌ: ๊ณต์—ฐ์ด ์—†์œผ๋ฉด "์•„์ง ๋“ฑ๋ก๋œ ๊ณต์—ฐ์ด ์—†์Šต๋‹ˆ๋‹ค" ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ

## ๊ธฐ๋Œ€ ๊ฒฐ๊ณผ

- ํผ์—์„œ ๊ณต์—ฐ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•˜๊ณ  "์ถ”๊ฐ€" ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์ฆ‰์‹œ ๋ชฉ๋ก์— ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.
- ์žฅ๋ฅด ํ•„ํ„ฐ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ํ•ด๋‹น ์žฅ๋ฅด ๊ณต์—ฐ๋งŒ ๋ณด์ž…๋‹ˆ๋‹ค.
- Laravel ๋ฐฑ์—”๋“œ(`php artisan serve`)์™€ React ํ”„๋ก ํŠธ์—”๋“œ(`npm run dev`)๊ฐ€ ๋ชจ๋‘ ์‹คํ–‰ ์ค‘์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์™„๋ฃŒ๋˜๋ฉด "๊ณต์—ฐ ๋ชฉ๋ก ์กฐํšŒ ๋ฐ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ์™„์„ฑ"์ด๋ผ๊ณ  ์•Œ๋ ค์ฃผ์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: React ํ”„๋ก ํŠธ์—”๋“œ์—์„œ Laravel API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๊ณต์—ฐ ๋ชฉ๋ก์„ ๋ถˆ๋Ÿฌ์˜ค๊ณ , ์ƒˆ ๊ณต์—ฐ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์žฅ๋ฅด ํ•„ํ„ฐ ๋ฒ„ํŠผ์œผ๋กœ ๊ณต์—ฐ์„ ํ•„ํ„ฐ๋งํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์นด๋“œ ํ˜•ํƒœ๋กœ ๊ณต์—ฐ ์ •๋ณด๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

5
Step 5
# Step 5: ๊ด€๋žŒ ์™„๋ฃŒ ์ฒดํฌ ๋ฐ ์‚ญ์ œ ๊ธฐ๋Šฅ ๊ตฌํ˜„

๊ฐ ๊ณต์—ฐ ์นด๋“œ์— "๊ด€๋žŒ ์™„๋ฃŒ" ์ฒดํฌ๋ฐ•์Šค์™€ "์‚ญ์ œ" ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ•˜๊ณ , API์™€ ์—ฐ๋™ํ•˜์—ฌ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ํ•ญ๋ชฉ์„ ์‚ญ์ œํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์™„์„ฑํ•ด์ฃผ์„ธ์š”.

## ์š”๊ตฌ์‚ฌํ•ญ

1. **๊ด€๋žŒ ์™„๋ฃŒ ์ฒดํฌ๋ฐ•์Šค**:
   - ๊ฐ ๊ณต์—ฐ ์นด๋“œ์— ์ฒดํฌ๋ฐ•์Šค ์ถ”๊ฐ€ (checked ์†์„ฑ์€ `show.is_watched`)
   - ์ฒดํฌ๋ฐ•์Šค ํด๋ฆญ ์‹œ `handleToggleWatched(id)` ํ•จ์ˆ˜ ํ˜ธ์ถœ
   - `handleToggleWatched` ํ•จ์ˆ˜:
     - PUT ์š”์ฒญ์œผ๋กœ `http://localhost:8000/api/shows/{id}` ํ˜ธ์ถœ
     - ์š”์ฒญ body์— `{is_watched: !currentWatchedStatus}` ์ „์†ก
     - ์„ฑ๊ณต ์‹œ `shows` ๋ฐฐ์—ด์—์„œ ํ•ด๋‹น ๊ณต์—ฐ์˜ `is_watched` ๊ฐ’ ํ† ๊ธ€
   - ๊ด€๋žŒ ์™„๋ฃŒ๋œ ๊ณต์—ฐ์€ ์นด๋“œ ๋ฐฐ๊ฒฝ์„ ํšŒ์ƒ‰(`bg-gray-100`)์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ , ์ œ๋ชฉ์— ์ทจ์†Œ์„  ์ถ”๊ฐ€
2. **์‚ญ์ œ ๋ฒ„ํŠผ**:
   - ๊ฐ ๊ณต์—ฐ ์นด๋“œ์— "์‚ญ์ œ" ๋ฒ„ํŠผ ์ถ”๊ฐ€ (๋นจ๊ฐ„์ƒ‰ ๋ฐฐ๊ฒฝ)
   - ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ `handleDelete(id)` ํ•จ์ˆ˜ ํ˜ธ์ถœ
   - `handleDelete` ํ•จ์ˆ˜:
     - DELETE ์š”์ฒญ์œผ๋กœ `http://localhost:8000/api/shows/{id}` ํ˜ธ์ถœ
     - ์„ฑ๊ณต ์‹œ `shows` ๋ฐฐ์—ด์—์„œ ํ•ด๋‹น ๊ณต์—ฐ ์ œ๊ฑฐ (filter ์‚ฌ์šฉ)
3. **UI ๊ฐœ์„ **:
   - ๊ด€๋žŒ ์™„๋ฃŒ ์ฒดํฌ๋ฐ•์Šค ์˜†์— "โœ… ๊ด€๋žŒ ์™„๋ฃŒ" ๋ผ๋ฒจ ํ‘œ์‹œ
   - ์‚ญ์ œ ๋ฒ„ํŠผ์€ ์นด๋“œ ์šฐ์ธก ์ƒ๋‹จ์— ๋ฐฐ์น˜ (ํ˜ธ๋ฒ„ ์‹œ ๋” ์ง„ํ•œ ๋นจ๊ฐ„์ƒ‰)
4. **์—๋Ÿฌ ํ•ธ๋“ค๋ง**:
   - fetch ์‹คํŒจ ์‹œ console.error๋กœ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ
   - ์‚ฌ์šฉ์ž์—๊ฒŒ ๊ฐ„๋‹จํ•œ alert๋กœ "์ž‘์—… ์‹คํŒจ" ์•Œ๋ฆผ (์„ ํƒ ์‚ฌํ•ญ)

## ๊ธฐ๋Œ€ ๊ฒฐ๊ณผ

- ์ฒดํฌ๋ฐ•์Šค๋ฅผ ํด๋ฆญํ•˜๋ฉด ๊ณต์—ฐ์ด ๊ด€๋žŒ ์™„๋ฃŒ ์ƒํƒœ๋กœ ์ „ํ™˜๋˜๊ณ , ์นด๋“œ ์Šคํƒ€์ผ์ด ํšŒ์ƒ‰์œผ๋กœ ๋ณ€ํ•ฉ๋‹ˆ๋‹ค.
- ์‚ญ์ œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ํ•ด๋‹น ๊ณต์—ฐ์ด ๋ชฉ๋ก์—์„œ ์ฆ‰์‹œ ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค.
- ๋ชจ๋“  ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด Laravel ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค.

์™„๋ฃŒ๋˜๋ฉด "๊ด€๋žŒ ์™„๋ฃŒ ์ฒดํฌ ๋ฐ ์‚ญ์ œ ๊ธฐ๋Šฅ ์™„์„ฑ, ํ”„๋กœ์ ํŠธ ์™„๋ฃŒ!"๋ผ๊ณ  ์•Œ๋ ค์ฃผ์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ๊ฐ ๊ณต์—ฐ ์นด๋“œ์— ๊ด€๋žŒ ์™„๋ฃŒ ์ฒดํฌ๋ฐ•์Šค์™€ ์‚ญ์ œ ๋ฒ„ํŠผ์ด ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค. ์ฒดํฌ๋ฐ•์Šค๋ฅผ ํด๋ฆญํ•˜๋ฉด ๊ณต์—ฐ์ด ๊ด€๋žŒ ์™„๋ฃŒ ์ƒํƒœ๋กœ ์ „ํ™˜๋˜๊ณ  ์นด๋“œ ์Šคํƒ€์ผ์ด ๋ณ€๊ฒฝ๋˜๋ฉฐ, ์‚ญ์ œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ๊ณต์—ฐ์ด ๋ชฉ๋ก์—์„œ ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  CRUD ๊ธฐ๋Šฅ์ด ์™„์„ฑ๋˜์–ด ํ’€์Šคํƒ ๊ณต์—ฐ ์œ„์‹œ๋ฆฌ์ŠคํŠธ ์•ฑ์ด ์™„์„ฑ๋ฉ๋‹ˆ๋‹ค.

์ œ์ถœ๋œ ์ž‘ํ’ˆ (0)

๋กœ๊ทธ์ธ ํ›„ ์ œ์ถœ

์•„์ง ์ œ์ถœ๋œ ์ž‘ํ’ˆ์ด ์—†์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ๋„์ „์ž๊ฐ€ ๋˜์–ด๋ณด์„ธ์š”!

๋Œ“๊ธ€ (0)

๋กœ๊ทธ์ธ ํ›„ ๋Œ“๊ธ€์„ ๋‚จ๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ๋Œ“๊ธ€์„ ๋‚จ๊ฒจ๋ณด์„ธ์š”!