๐ŸŽŸ๏ธ ๊ณต์—ฐ ํ‹ฐ์ผ“ ๋ฆฌ์…€ ๋งˆ์ผ“ํ”Œ๋ ˆ์ด์Šค - ์–‘๋„/๊ตฌ๋งค ๋งค์นญ ํ”Œ๋žซํผ - ๋ฐ”์ด๋ธŒ์ฝ”๋”ฉ ๋ ˆ์‹œํ”ผ
์ค‘๊ธ‰ PHP + JavaScript Laravel + React + Inertia.js AI: Claude

๐ŸŽŸ๏ธ ๊ณต์—ฐ ํ‹ฐ์ผ“ ๋ฆฌ์…€ ๋งˆ์ผ“ํ”Œ๋ ˆ์ด์Šค - ์–‘๋„/๊ตฌ๋งค ๋งค์นญ ํ”Œ๋žซํผ

๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ช… concert-ticket-resale-marketplace
์กฐํšŒ์ˆ˜ 339 0๊ฑด ์ œ์ถœ

๋ฌธ์ œ ์„ค๋ช…

๐ŸŽŸ๏ธ ๊ณต์—ฐ ํ‹ฐ์ผ“ ๋ฆฌ์…€ ๋งˆ์ผ“ํ”Œ๋ ˆ์ด์Šค

์ฝ˜์„œํŠธ, ๋ฎค์ง€์ปฌ, ์Šคํฌ์ธ  ๊ฒฝ๊ธฐ ๋“ฑ ๊ฐ์ข… ๊ณต์—ฐ ํ‹ฐ์ผ“์„ ์–‘๋„ํ•˜๊ฑฐ๋‚˜ ๊ตฌ๋งคํ•  ์ˆ˜ ์žˆ๋Š” ์ค‘๊ณ  ํ‹ฐ์ผ“ ๊ฑฐ๋ž˜ ํ”Œ๋žซํผ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. Laravel 13.x + Inertia.js + React 19 ํ’€์Šคํƒ ๋ชจ๋…ธ๋ฆฌ์Šค ์•„ํ‚คํ…์ฒ˜๋กœ ๊ตฌํ˜„ํ•˜๋ฉฐ, ์‹ค์ œ ํ‹ฐ์ผ“ ๊ฑฐ๋ž˜ ์„œ๋น„์Šค์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์„ ๊ฒฝํ—˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

ํ‹ฐ์ผ“ ๋“ฑ๋ก & ๊ด€๋ฆฌ

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

๊ฒ€์ƒ‰ & ํ•„ํ„ฐ๋ง

  • ๊ณต์—ฐ๋ช…, ๋‚ ์งœ, ์นดํ…Œ๊ณ ๋ฆฌ, ๊ฐ€๊ฒฉ๋Œ€๋กœ ํ•„ํ„ฐ๋ง
  • ์ตœ์‹ ์ˆœ/๊ฐ€๊ฒฉ ๋‚ฎ์€ ์ˆœ/๋งˆ๊ฐ ์ž„๋ฐ•์ˆœ ์ •๋ ฌ
  • ํŒ๋งค ์™„๋ฃŒ๋œ ํ‹ฐ์ผ“์€ ๋ณ„๋„ ํ‘œ์‹œ

๊ตฌ๋งค ์˜์‚ฌ ํ‘œ์‹œ & ์ƒํƒœ ๊ด€๋ฆฌ

  • ๊ด€์‹ฌ ์žˆ๋Š” ํ‹ฐ์ผ“์— "๊ตฌ๋งค ํฌ๋ง" ๋ฒ„ํŠผ ํด๋ฆญ
  • ํŒ๋งค์ž๊ฐ€ ๊ตฌ๋งค์ž ๋ชฉ๋ก ํ™•์ธ ํ›„ "์˜ˆ์•ฝ์ค‘" ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ
  • ๊ฑฐ๋ž˜ ์™„๋ฃŒ ์‹œ "ํŒ๋งค์™„๋ฃŒ" ์ฒ˜๋ฆฌ

์‚ฌ์šฉ์ž ์ธ์ฆ

  • Laravel Breeze ๊ธฐ๋ณธ ์ธ์ฆ ์‹œ์Šคํ…œ ํ™œ์šฉ
  • ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๋งŒ ํ‹ฐ์ผ“ ๋“ฑ๋ก/๊ตฌ๋งค ํฌ๋ง ๊ฐ€๋Šฅ

๋ฐ˜์‘ํ˜• UI

  • ์นด๋“œ ๊ทธ๋ฆฌ๋“œ ๋ ˆ์ด์•„์›ƒ์œผ๋กœ ํ‹ฐ์ผ“ ๋ชฉ๋ก ํ‘œ์‹œ
  • ๋ชจ๋ฐ”์ผ/ํƒœ๋ธ”๋ฆฟ/๋ฐ์Šคํฌํ†ฑ ๋Œ€์‘
  • Tailwind CSS๋กœ ๊น”๋”ํ•œ ๋””์ž์ธ

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

  • ํ‹ฐ์ผ“ ์ƒ์„ธ ํŽ˜์ด์ง€์—์„œ ํŒ๋งค์ž ํ”„๋กœํ•„ ๋ณด๊ธฐ
  • ์ฐœํ•˜๊ธฐ ๊ธฐ๋Šฅ (์œ„์‹œ๋ฆฌ์ŠคํŠธ)
  • ๊ฑฐ๋ž˜ ํ›„๊ธฐ ์‹œ์Šคํ…œ (๋ณ„์  & ๋Œ“๊ธ€)
  • ํŒ๋งค์ž์—๊ฒŒ ๊ฐ„๋‹จํ•œ ์ชฝ์ง€ ๋ณด๋‚ด๊ธฐ (์‹ค์‹œ๊ฐ„ ์ฑ„ํŒ… ๋Œ€์‹  HTTP ๊ธฐ๋ฐ˜ ๋ฉ”์‹œ์ง€)
  • ๊ณต์—ฐ D-day ์นด์šดํŠธ๋‹ค์šด ํ‘œ์‹œ

๊ธฐ์ˆ  ์Šคํƒ

  • ๋ฐฑ์—”๋“œ: Laravel 13.x (์ปจํŠธ๋กค๋Ÿฌ, Eloquent ORM, ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜, ์ธ์ฆ)
  • ํ”„๋ก ํŠธ์—”๋“œ: React 19 (ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ, Hooks)
  • ํ’€์Šคํƒ ์—ฐ๊ฒฐ: Inertia.js (์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ผ์šฐํŒ… + React ๋ Œ๋”๋ง)
  • ์Šคํƒ€์ผ๋ง: Tailwind CSS
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค: SQLite (๊ฐœ๋ฐœ์šฉ) / MySQL (ํ”„๋กœ๋•์…˜ ์˜ต์…˜)
  • ํŒŒ์ผ ์—…๋กœ๋“œ: Laravel Storage (๋กœ์ปฌ ๋˜๋Š” S3)

์ด ํ”„๋กœ์ ํŠธ๋ฅผ ํ†ตํ•ด ์‹ค์ œ ์ด์ปค๋จธ์Šค/๋งˆ์ผ“ํ”Œ๋ ˆ์ด์Šค ์„œ๋น„์Šค์˜ ํ•ต์‹ฌ ๋กœ์ง(์ƒํ’ˆ ๋“ฑ๋ก, ๊ฒ€์ƒ‰ ํ•„ํ„ฐ๋ง, ์ƒํƒœ ๊ด€๋ฆฌ, ์‚ฌ์šฉ์ž ์ธ์ฆ)์„ ์ตํž ์ˆ˜ ์žˆ์œผ๋ฉฐ, Inertia.js๋ฅผ ํ™œ์šฉํ•œ ๋ชจ๋˜ ํ’€์Šคํƒ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์„ ์Œ“์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ œ์•ฝ ์กฐ๊ฑด

  • โ€ข Laravel 13.x + Inertia.js + React 19 ๋ชจ๋…ธ๋ฆฌ์Šค ๊ตฌ์กฐ (๋ณ„๋„ API ๋ผ์šฐํŠธ ์—†์Œ)
  • โ€ข Laravel Breeze (React + Inertia starter kit) ์‚ฌ์šฉ ๊ถŒ์žฅ
  • โ€ข Inertia::render()๋กœ Laravel ์ปจํŠธ๋กค๋Ÿฌ์—์„œ React ์ปดํฌ๋„ŒํŠธ์— ๋ฐ์ดํ„ฐ ์ „๋‹ฌ
  • โ€ข SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‚ฌ์šฉ (๊ฐ„ํŽธํ•œ ๋กœ์ปฌ ๊ฐœ๋ฐœ)
  • โ€ข Tailwind CSS๋กœ ์Šคํƒ€์ผ๋ง
  • โ€ข ํŒŒ์ผ ์—…๋กœ๋“œ๋Š” Laravel Storage ๊ธฐ๋ณธ ๊ธฐ๋Šฅ ํ™œ์šฉ
  • โ€ข ์‹ค์‹œ๊ฐ„ ์ฑ„ํŒ… ๊ธˆ์ง€ (WebSocket ๋Œ€์‹  HTTP ๊ธฐ๋ฐ˜ ์ชฝ์ง€/๋Œ“๊ธ€ ์‚ฌ์šฉ)
  • โ€ข ๋ฐ˜์‘ํ˜• ๋””์ž์ธ ํ•„์ˆ˜ (๋ชจ๋ฐ”์ผ ํผ์ŠคํŠธ)
  • โ€ข Eloquent ORM์œผ๋กœ DB ์ฟผ๋ฆฌ ์ž‘์„ฑ
  • โ€ข ํผ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋Š” Laravel Request Validation ํ™œ์šฉ

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

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

1
Step 1
mkdir concert-ticket-resale-marketplace && cd concert-ticket-resale-marketplace

์ด ํด๋”์—์„œ Laravel 13.x + Inertia.js + React 19 ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

**์š”๊ตฌ์‚ฌํ•ญ:**
1. Laravel Breeze (React + Inertia stack)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐํ™”
2. ๋‹ค์Œ ๋ช…๋ น์–ด ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰:
   - `composer create-project laravel/laravel .` (Laravel 13.x ์„ค์น˜)
   - `composer require laravel/breeze --dev`
   - `php artisan breeze:install react` (React + Inertia ์Šคํƒ ์„ ํƒ)
   - `npm install && npm run build`
3. `.env` ํŒŒ์ผ์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ SQLite๋กœ ์„ค์ •:
   ```
   DB_CONNECTION=sqlite
   # DB_HOST, DB_PORT, DB_DATABASE ์ฃผ์„ ์ฒ˜๋ฆฌ
   ```
4. `database/database.sqlite` ๋นˆ ํŒŒ์ผ ์ƒ์„ฑ
5. `php artisan migrate` ์‹คํ–‰ํ•˜์—ฌ ๊ธฐ๋ณธ ํ…Œ์ด๋ธ” ์ƒ์„ฑ (users, password_resets ๋“ฑ)
6. `php artisan serve`๋กœ ์„œ๋ฒ„ ์‹คํ–‰ ํ›„ http://localhost:8000 ์ ‘์† ํ™•์ธ
7. Breeze ๊ธฐ๋ณธ ์ธ์ฆ ํŽ˜์ด์ง€(๋กœ๊ทธ์ธ/ํšŒ์›๊ฐ€์ž…) ์ž‘๋™ ํ™•์ธ

**์ถ”๊ฐ€ ์„ค์ •:**
- Tailwind CSS๊ฐ€ ์ด๋ฏธ ์„ค์ •๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธ (`tailwind.config.js` ์กด์žฌ)
- `resources/js/app.jsx`์—์„œ Inertia ์ดˆ๊ธฐํ™” ํ™•์ธ
- `resources/js/Pages/` ํด๋”์— ๊ธฐ๋ณธ ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ ํ™•์ธ

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: Laravel 13.x + Breeze (React + Inertia) ํ”„๋กœ์ ํŠธ๊ฐ€ ์ƒ์„ฑ๋˜๊ณ , ๊ธฐ๋ณธ ์ธ์ฆ ์‹œ์Šคํ…œ(๋กœ๊ทธ์ธ/ํšŒ์›๊ฐ€์ž…)์ด ์ž‘๋™ํ•˜๋Š” ์ƒํƒœ. SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์™„๋ฃŒ. http://localhost:8000์—์„œ Welcome ํŽ˜์ด์ง€์™€ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ํ™•์ธ ๊ฐ€๋Šฅ.

2
Step 2
**ํ‹ฐ์ผ“ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„ ๋ฐ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ƒ์„ฑ**

`tickets` ํ…Œ์ด๋ธ”์„ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.

**์š”๊ตฌ์‚ฌํ•ญ:**
1. ๋‹ค์Œ ๋ช…๋ น์–ด๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ƒ์„ฑ:
   ```bash
   php artisan make:migration create_tickets_table
   ```

2. ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํŒŒ์ผ์—์„œ `tickets` ํ…Œ์ด๋ธ” ์Šคํ‚ค๋งˆ ์ •์˜:
   - `id`: ๊ธฐ๋ณธ ํ‚ค
   - `user_id`: ํŒ๋งค์ž (users ํ…Œ์ด๋ธ” ์™ธ๋ž˜ ํ‚ค)
   - `title`: ๊ณต์—ฐ๋ช… (๋ฌธ์ž์—ด, ํ•„์ˆ˜)
   - `category`: ์นดํ…Œ๊ณ ๋ฆฌ (enum: 'concert', 'musical', 'sports', 'exhibition')
   - `event_date`: ๊ณต์—ฐ ๋‚ ์งœ (date)
   - `venue`: ๊ณต์—ฐ ์žฅ์†Œ (๋ฌธ์ž์—ด)
   - `seat_info`: ์ขŒ์„ ์ •๋ณด (๋ฌธ์ž์—ด, nullable)
   - `original_price`: ์›๊ฐ€ (์ •์ˆ˜)
   - `selling_price`: ํŒ๋งค ๊ฐ€๊ฒฉ (์ •์ˆ˜)
   - `status`: ์ƒํƒœ (enum: 'available', 'reserved', 'sold', ๊ธฐ๋ณธ๊ฐ’ 'available')
   - `image_path`: ํ‹ฐ์ผ“ ์ด๋ฏธ์ง€ ๊ฒฝ๋กœ (๋ฌธ์ž์—ด, nullable)
   - `description`: ์ƒ์„ธ ์„ค๋ช… (ํ…์ŠคํŠธ, nullable)
   - `timestamps`: created_at, updated_at

3. ์™ธ๋ž˜ ํ‚ค ์ œ์•ฝ ์กฐ๊ฑด:
   ```php
   $table->foreignId('user_id')->constrained()->onDelete('cascade');
   ```

4. ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹คํ–‰:
   ```bash
   php artisan migrate
   ```

5. Ticket ๋ชจ๋ธ ์ƒ์„ฑ:
   ```bash
   php artisan make:model Ticket
   ```

6. `app/Models/Ticket.php`์—์„œ:
   - `$fillable` ์†์„ฑ์— ๋ชจ๋“  ์ปฌ๋Ÿผ ์ถ”๊ฐ€ (id, timestamps ์ œ์™ธ)
   - User ๊ด€๊ณ„ ์ •์˜: `belongsTo(User::class)`
   - `$casts` ์†์„ฑ์—์„œ `event_date`๋ฅผ `date`๋กœ ์บ์ŠคํŒ…

7. `app/Models/User.php`์— Ticket ๊ด€๊ณ„ ์ถ”๊ฐ€:
   ```php
   public function tickets()
   {
       return $this->hasMany(Ticket::class);
   }
   ```

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: `tickets` ํ…Œ์ด๋ธ”์ด ์ƒ์„ฑ๋˜๊ณ , Ticket ๋ชจ๋ธ๊ณผ User ๋ชจ๋ธ ๊ฐ„ ๊ด€๊ณ„๊ฐ€ ์ •์˜๋จ. ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹คํ–‰ ํ›„ `database/database.sqlite`์— tickets ํ…Œ์ด๋ธ” ํ™•์ธ ๊ฐ€๋Šฅ.

3
Step 3
**ํ‹ฐ์ผ“ ๋ชฉ๋ก ํŽ˜์ด์ง€ ๊ตฌํ˜„ (๋ฉ”์ธ ํ™”๋ฉด)**

Inertia.js๋ฅผ ํ™œ์šฉํ•˜์—ฌ ํ‹ฐ์ผ“ ๋ชฉ๋ก์„ ๋ณด์—ฌ์ฃผ๋Š” ๋ฉ”์ธ ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.

**๋ฐฑ์—”๋“œ (Laravel):**

1. `TicketController` ์ƒ์„ฑ:
   ```bash
   php artisan make:controller TicketController
   ```

2. `app/Http/Controllers/TicketController.php`์—์„œ `index` ๋ฉ”์„œ๋“œ ์ž‘์„ฑ:
   ```php
   public function index(Request $request)
   {
       $query = Ticket::with('user');

       // ์นดํ…Œ๊ณ ๋ฆฌ ํ•„ํ„ฐ๋ง
       if ($request->category) {
           $query->where('category', $request->category);
       }

       // ์ƒํƒœ ํ•„ํ„ฐ๋ง (๊ธฐ๋ณธ: ํŒ๋งค์ค‘๋งŒ)
       $status = $request->status ?? 'available';
       if ($status !== 'all') {
           $query->where('status', $status);
       }

       // ์ •๋ ฌ (๊ธฐ๋ณธ: ์ตœ์‹ ์ˆœ)
       $sort = $request->sort ?? 'latest';
       if ($sort === 'price_low') {
           $query->orderBy('selling_price', 'asc');
       } elseif ($sort === 'price_high') {
           $query->orderBy('selling_price', 'desc');
       } elseif ($sort === 'event_date') {
           $query->orderBy('event_date', 'asc');
       } else {
           $query->orderBy('created_at', 'desc');
       }

       $tickets = $query->paginate(12);

       return Inertia::render('Tickets/Index', [
           'tickets' => $tickets,
           'filters' => $request->only(['category', 'status', 'sort'])
       ]);
   }
   ```

3. `routes/web.php`์— ๋ผ์šฐํŠธ ์ถ”๊ฐ€:
   ```php
   use App\Http\Controllers\TicketController;

   Route::get('/', [TicketController::class, 'index'])->name('tickets.index');
   ```

**ํ”„๋ก ํŠธ์—”๋“œ (React):**

4. `resources/js/Pages/Tickets/Index.jsx` ํŒŒ์ผ ์ƒ์„ฑ:
   - Props๋กœ `tickets`, `filters` ๋ฐ›๊ธฐ
   - ์ƒ๋‹จ์— ํ•„ํ„ฐ UI (์นดํ…Œ๊ณ ๋ฆฌ ๋ฒ„ํŠผ, ์ •๋ ฌ ๋“œ๋กญ๋‹ค์šด)
   - ํ‹ฐ์ผ“ ์นด๋“œ ๊ทธ๋ฆฌ๋“œ ๋ ˆ์ด์•„์›ƒ (3์—ด, ๋ชจ๋ฐ”์ผ 1์—ด)
   - ๊ฐ ์นด๋“œ์— ํ‘œ์‹œํ•  ์ •๋ณด:
     - ํ‹ฐ์ผ“ ์ด๋ฏธ์ง€ (์—†์œผ๋ฉด ๊ธฐ๋ณธ ์ด๋ฏธ์ง€)
     - ๊ณต์—ฐ๋ช… (title)
     - ๊ณต์—ฐ ๋‚ ์งœ (event_date)
     - ์žฅ์†Œ (venue)
     - ๊ฐ€๊ฒฉ (selling_price, ์›๊ฐ€ ์ทจ์†Œ์„ )
     - ์ƒํƒœ ๋ฐฐ์ง€ (ํŒ๋งค์ค‘/์˜ˆ์•ฝ์ค‘/ํŒ๋งค์™„๋ฃŒ)
   - Inertia Link๋กœ ์ƒ์„ธ ํŽ˜์ด์ง€ ์—ฐ๊ฒฐ (์•„์ง ๋ฏธ๊ตฌํ˜„)
   - ํŽ˜์ด์ง€๋„ค์ด์…˜ (Inertia ๊ธฐ๋ณธ ์ปดํฌ๋„ŒํŠธ ํ™œ์šฉ)

5. ํ•„ํ„ฐ ๋ณ€๊ฒฝ ์‹œ Inertia.get()์œผ๋กœ ์ฟผ๋ฆฌ์ŠคํŠธ๋ง ์—…๋ฐ์ดํŠธ:
   ```jsx
   const handleFilterChange = (key, value) => {
       Inertia.get(route('tickets.index'), 
           { ...filters, [key]: value },
           { preserveState: true }
       );
   };
   ```

6. Tailwind CSS๋กœ ์Šคํƒ€์ผ๋ง:
   - ์นด๋“œ hover ํšจ๊ณผ
   - ์ƒํƒœ๋ณ„ ๋ฐฐ์ง€ ์ƒ‰์ƒ (available: green, reserved: yellow, sold: gray)
   - ๋ฐ˜์‘ํ˜• ๊ทธ๋ฆฌ๋“œ (grid-cols-1 md:grid-cols-2 lg:grid-cols-3)

**ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ:**
7. Tinker๋กœ ์ƒ˜ํ”Œ ํ‹ฐ์ผ“ 3~5๊ฐœ ์ƒ์„ฑ:
   ```bash
   php artisan tinker
   ```
   ```php
   $user = User::first();
   Ticket::create([
       'user_id' => $user->id,
       'title' => '์•„์ด์œ  ์ฝ˜์„œํŠธ 2025',
       'category' => 'concert',
       'event_date' => '2025-06-15',
       'venue' => '์˜ฌ๋ฆผํ”ฝ๊ณต์› ์ฒด์กฐ๊ฒฝ๊ธฐ์žฅ',
       'seat_info' => 'A๊ตฌ์—ญ 3์—ด 15๋ฒˆ',
       'original_price' => 150000,
       'selling_price' => 120000,
       'status' => 'available'
   ]);
   ```

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ํ‹ฐ์ผ“ ๋ชฉ๋ก ํŽ˜์ด์ง€๊ฐ€ ์ž‘๋™ํ•˜๋ฉฐ, ์นดํ…Œ๊ณ ๋ฆฌ/์ƒํƒœ/์ •๋ ฌ ํ•„ํ„ฐ๊ฐ€ ๋™์ž‘ํ•จ. ์นด๋“œ ๊ทธ๋ฆฌ๋“œ๋กœ ํ‹ฐ์ผ“์ด ํ‘œ์‹œ๋˜๊ณ , ๊ฐ ์นด๋“œ์— ๊ณต์—ฐ ์ •๋ณด์™€ ๊ฐ€๊ฒฉ์ด ๋ณด์ž„. ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ž‘๋™ ํ™•์ธ.

4
Step 4
**ํ‹ฐ์ผ“ ๋“ฑ๋ก ํผ ๊ตฌํ˜„ (Create)**

๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ์ƒˆ ํ‹ฐ์ผ“์„ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋Š” ํผ์„ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.

**๋ฐฑ์—”๋“œ:**

1. `TicketController`์— `create`, `store` ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€:
   ```php
   public function create()
   {
       return Inertia::render('Tickets/Create');
   }

   public function store(Request $request)
   {
       $validated = $request->validate([
           'title' => 'required|string|max:255',
           'category' => 'required|in:concert,musical,sports,exhibition',
           'event_date' => 'required|date|after:today',
           'venue' => 'required|string|max:255',
           'seat_info' => 'nullable|string|max:255',
           'original_price' => 'required|integer|min:0',
           'selling_price' => 'required|integer|min:0',
           'description' => 'nullable|string',
           'image' => 'nullable|image|max:2048' // 2MB
       ]);

       $ticket = new Ticket($validated);
       $ticket->user_id = auth()->id();

       if ($request->hasFile('image')) {
           $path = $request->file('image')->store('tickets', 'public');
           $ticket->image_path = $path;
       }

       $ticket->save();

       return redirect()->route('tickets.index')
           ->with('success', 'ํ‹ฐ์ผ“์ด ๋“ฑ๋ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.');
   }
   ```

2. `routes/web.php`์— ๋ผ์šฐํŠธ ์ถ”๊ฐ€ (์ธ์ฆ ํ•„์ˆ˜):
   ```php
   Route::middleware('auth')->group(function () {
       Route::get('/tickets/create', [TicketController::class, 'create'])->name('tickets.create');
       Route::post('/tickets', [TicketController::class, 'store'])->name('tickets.store');
   });
   ```

3. ์Šคํ† ๋ฆฌ์ง€ ๋งํฌ ์ƒ์„ฑ:
   ```bash
   php artisan storage:link
   ```

**ํ”„๋ก ํŠธ์—”๋“œ:**

4. `resources/js/Pages/Tickets/Create.jsx` ์ƒ์„ฑ:
   - Inertia `useForm` ํ›… ์‚ฌ์šฉ:
     ```jsx
     import { useForm } from '@inertiajs/react';

     const { data, setData, post, processing, errors } = useForm({
         title: '',
         category: 'concert',
         event_date: '',
         venue: '',
         seat_info: '',
         original_price: '',
         selling_price: '',
         description: '',
         image: null
     });
     ```
   - ํผ ํ•„๋“œ:
     - ๊ณต์—ฐ๋ช… (ํ…์ŠคํŠธ ์ž…๋ ฅ)
     - ์นดํ…Œ๊ณ ๋ฆฌ (๋ผ๋””์˜ค ๋ฒ„ํŠผ ๋˜๋Š” ์…€๋ ‰ํŠธ)
     - ๊ณต์—ฐ ๋‚ ์งœ (date input)
     - ์žฅ์†Œ (ํ…์ŠคํŠธ ์ž…๋ ฅ)
     - ์ขŒ์„ ์ •๋ณด (ํ…์ŠคํŠธ ์ž…๋ ฅ, ์„ ํƒ)
     - ์›๊ฐ€ (์ˆซ์ž ์ž…๋ ฅ)
     - ํŒ๋งค ๊ฐ€๊ฒฉ (์ˆซ์ž ์ž…๋ ฅ)
     - ์ƒ์„ธ ์„ค๋ช… (textarea)
     - ํ‹ฐ์ผ“ ์ด๋ฏธ์ง€ (ํŒŒ์ผ ์—…๋กœ๋“œ, ๋ฏธ๋ฆฌ๋ณด๊ธฐ)
   - ์ด๋ฏธ์ง€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ:
     ```jsx
     const [preview, setPreview] = useState(null);
     const handleImageChange = (e) => {
         const file = e.target.files[0];
         setData('image', file);
         setPreview(URL.createObjectURL(file));
     };
     ```
   - ํผ ์ œ์ถœ:
     ```jsx
     const handleSubmit = (e) => {
         e.preventDefault();
         post(route('tickets.store'));
     };
     ```
   - ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ (`errors` ๊ฐ์ฒด ํ™œ์šฉ)
   - Tailwind๋กœ ๊น”๋”ํ•œ ํผ ๋ ˆ์ด์•„์›ƒ

5. ๋„ค๋น„๊ฒŒ์ด์…˜์— "ํ‹ฐ์ผ“ ๋“ฑ๋ก" ๋ฒ„ํŠผ ์ถ”๊ฐ€ (๋กœ๊ทธ์ธ ์ƒํƒœ์—์„œ๋งŒ ๋ณด์ž„):
   - `resources/js/Layouts/AuthenticatedLayout.jsx` ๋˜๋Š” ํ—ค๋” ์ปดํฌ๋„ŒํŠธ ์ˆ˜์ •

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ํ‹ฐ์ผ“ ๋“ฑ๋ก ํผ์— ์ ‘๊ทผ ๊ฐ€๋Šฅ. ๋ชจ๋“  ํ•„๋“œ ์ž…๋ ฅ ํ›„ ์ œ์ถœ ์‹œ ํ‹ฐ์ผ“์ด DB์— ์ €์žฅ๋˜๊ณ , ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ์‹œ `storage/app/public/tickets/`์— ์ €์žฅ๋จ. ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์—๋Ÿฌ ์‹œ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ. ๋“ฑ๋ก ํ›„ ๋ชฉ๋ก ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ.

5
Step 5
**ํ‹ฐ์ผ“ ์ƒ์„ธ ํŽ˜์ด์ง€ ๋ฐ ๊ตฌ๋งค ํฌ๋ง ๊ธฐ๋Šฅ ๊ตฌํ˜„**

๊ฐœ๋ณ„ ํ‹ฐ์ผ“์˜ ์ƒ์„ธ ์ •๋ณด๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ , ๊ด€์‹ฌ ์žˆ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ "๊ตฌ๋งค ํฌ๋ง" ์˜์‚ฌ๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.

**๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค:**

1. `ticket_interests` ํ…Œ์ด๋ธ” ์ƒ์„ฑ (๊ตฌ๋งค ํฌ๋ง์ž ๊ธฐ๋ก):
   ```bash
   php artisan make:migration create_ticket_interests_table
   ```
   - ์ปฌ๋Ÿผ: `id`, `ticket_id` (์™ธ๋ž˜ ํ‚ค), `user_id` (์™ธ๋ž˜ ํ‚ค), `message` (์„ ํƒ์  ๋ฉ”์‹œ์ง€), `timestamps`
   - ์œ ๋‹ˆํฌ ์ œ์•ฝ: ํ•œ ์‚ฌ์šฉ์ž๋Š” ๊ฐ™์€ ํ‹ฐ์ผ“์— ํ•œ ๋ฒˆ๋งŒ ๊ตฌ๋งค ํฌ๋ง ๊ฐ€๋Šฅ
   ```php
   $table->unique(['ticket_id', 'user_id']);
   ```

2. TicketInterest ๋ชจ๋ธ ์ƒ์„ฑ:
   ```bash
   php artisan make:model TicketInterest
   ```
   - Ticket, User ๊ด€๊ณ„ ์ •์˜

3. Ticket ๋ชจ๋ธ์— interests ๊ด€๊ณ„ ์ถ”๊ฐ€:
   ```php
   public function interests()
   {
       return $this->hasMany(TicketInterest::class);
   }
   ```

**๋ฐฑ์—”๋“œ:**

4. `TicketController`์— `show` ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€:
   ```php
   public function show(Ticket $ticket)
   {
       $ticket->load(['user', 'interests.user']);
       
       // ํ˜„์žฌ ์‚ฌ์šฉ์ž๊ฐ€ ๊ตฌ๋งค ํฌ๋ง ํ‘œ์‹œํ–ˆ๋Š”์ง€ ํ™•์ธ
       $hasInterest = auth()->check() 
           ? $ticket->interests()->where('user_id', auth()->id())->exists()
           : false;

       return Inertia::render('Tickets/Show', [
           'ticket' => $ticket,
           'hasInterest' => $hasInterest,
           'isOwner' => auth()->id() === $ticket->user_id
       ]);
   }
   ```

5. ๊ตฌ๋งค ํฌ๋ง ๋“ฑ๋ก ๋ฉ”์„œ๋“œ:
   ```php
   public function addInterest(Request $request, Ticket $ticket)
   {
       if ($ticket->user_id === auth()->id()) {
           return back()->withErrors(['error' => '๋ณธ์ธ ํ‹ฐ์ผ“์—๋Š” ๊ตฌ๋งค ํฌ๋ง์„ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.']);
       }

       if ($ticket->status !== 'available') {
           return back()->withErrors(['error' => 'ํŒ๋งค์ค‘์ธ ํ‹ฐ์ผ“๋งŒ ๊ตฌ๋งค ํฌ๋ง ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.']);
       }

       $validated = $request->validate([
           'message' => 'nullable|string|max:500'
       ]);

       TicketInterest::updateOrCreate(
           ['ticket_id' => $ticket->id, 'user_id' => auth()->id()],
           ['message' => $validated['message'] ?? null]
       );

       return back()->with('success', '๊ตฌ๋งค ํฌ๋ง์ด ๋“ฑ๋ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.');
   }
   ```

6. ๋ผ์šฐํŠธ ์ถ”๊ฐ€:
   ```php
   Route::get('/tickets/{ticket}', [TicketController::class, 'show'])->name('tickets.show');
   Route::post('/tickets/{ticket}/interest', [TicketController::class, 'addInterest'])
       ->middleware('auth')
       ->name('tickets.interest');
   ```

**ํ”„๋ก ํŠธ์—”๋“œ:**

7. `resources/js/Pages/Tickets/Show.jsx` ์ƒ์„ฑ:
   - ํ‹ฐ์ผ“ ์ •๋ณด ํ‘œ์‹œ:
     - ํฐ ์ด๋ฏธ์ง€ (์—†์œผ๋ฉด ๊ธฐ๋ณธ ์ด๋ฏธ์ง€)
     - ๊ณต์—ฐ๋ช…, ์นดํ…Œ๊ณ ๋ฆฌ, ๋‚ ์งœ, ์žฅ์†Œ, ์ขŒ์„
     - ๊ฐ€๊ฒฉ ์ •๋ณด (์›๊ฐ€ vs ํŒ๋งค๊ฐ€)
     - ์ƒ์„ธ ์„ค๋ช…
     - ํŒ๋งค์ž ์ •๋ณด (์ด๋ฆ„, ๊ฐ€์ž…์ผ)
     - ์ƒํƒœ ๋ฐฐ์ง€
   - ๊ตฌ๋งค ํฌ๋ง ๋ฒ„ํŠผ:
     - ๋กœ๊ทธ์ธ ์•ˆ ํ–ˆ์œผ๋ฉด "๋กœ๊ทธ์ธ ํ›„ ์ด์šฉ ๊ฐ€๋Šฅ" ์•ˆ๋‚ด
     - ๋ณธ์ธ ํ‹ฐ์ผ“์ด๋ฉด ๋ฒ„ํŠผ ์ˆจ๊น€
     - ์ด๋ฏธ ๊ตฌ๋งค ํฌ๋ง ํ–ˆ์œผ๋ฉด "๊ตฌ๋งค ํฌ๋ง ์ทจ์†Œ" ๋ฒ„ํŠผ
     - ํŒ๋งค ์™„๋ฃŒ ์ƒํƒœ๋ฉด ๋ฒ„ํŠผ ๋น„ํ™œ์„ฑํ™”
   - ๊ฐ„๋‹จํ•œ ๋ฉ”์‹œ์ง€ ์ž…๋ ฅ๋ž€ (์„ ํƒ)
   - ํŒ๋งค์ž์ธ ๊ฒฝ์šฐ:
     - ๊ตฌ๋งค ํฌ๋ง์ž ๋ชฉ๋ก ํ‘œ์‹œ (์ด๋ฆ„, ๋ฉ”์‹œ์ง€, ๋“ฑ๋ก ์‹œ๊ฐ„)
     - "์˜ˆ์•ฝ์ค‘์œผ๋กœ ๋ณ€๊ฒฝ" ๋ฒ„ํŠผ

8. ๋ชฉ๋ก ํŽ˜์ด์ง€ ์นด๋“œ์—์„œ ์ƒ์„ธ ํŽ˜์ด์ง€ ๋งํฌ ์—ฐ๊ฒฐ:
   ```jsx
   <Link href={route('tickets.show', ticket.id)}>...</Link>
   ```

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ํ‹ฐ์ผ“ ์ƒ์„ธ ํŽ˜์ด์ง€์—์„œ ๋ชจ๋“  ์ •๋ณด๊ฐ€ ํ‘œ์‹œ๋จ. ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ๊ตฌ๋งค ํฌ๋ง ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ DB์— ๊ธฐ๋ก๋˜๊ณ , ํŒ๋งค์ž๋Š” ๊ตฌ๋งค ํฌ๋ง์ž ๋ชฉ๋ก์„ ํ™•์ธ ๊ฐ€๋Šฅ. ๋ณธ์ธ ํ‹ฐ์ผ“์ด๋‚˜ ์ด๋ฏธ ๊ตฌ๋งค ํฌ๋งํ•œ ํ‹ฐ์ผ“์—๋Š” ์ ์ ˆํ•œ UI ํ‘œ์‹œ.

6
Step 6
**ํ‹ฐ์ผ“ ์ˆ˜์ •/์‚ญ์ œ ๋ฐ ์ƒํƒœ ๊ด€๋ฆฌ ๊ธฐ๋Šฅ ๊ตฌํ˜„**

ํŒ๋งค์ž๊ฐ€ ์ž์‹ ์˜ ํ‹ฐ์ผ“์„ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•˜๊ณ , ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์™„์„ฑํ•ด์ฃผ์„ธ์š”.

**๋ฐฑ์—”๋“œ:**

1. `TicketController`์— `edit`, `update`, `destroy`, `updateStatus` ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€:
   ```php
   public function edit(Ticket $ticket)
   {
       $this->authorize('update', $ticket); // Policy ์‚ฌ์šฉ
       return Inertia::render('Tickets/Edit', ['ticket' => $ticket]);
   }

   public function update(Request $request, Ticket $ticket)
   {
       $this->authorize('update', $ticket);

       $validated = $request->validate([
           'title' => 'required|string|max:255',
           'category' => 'required|in:concert,musical,sports,exhibition',
           'event_date' => 'required|date',
           'venue' => 'required|string|max:255',
           'seat_info' => 'nullable|string|max:255',
           'original_price' => 'required|integer|min:0',
           'selling_price' => 'required|integer|min:0',
           'description' => 'nullable|string',
           'image' => 'nullable|image|max:2048'
       ]);

       $ticket->fill($validated);

       if ($request->hasFile('image')) {
           // ๊ธฐ์กด ์ด๋ฏธ์ง€ ์‚ญ์ œ
           if ($ticket->image_path) {
               Storage::disk('public')->delete($ticket->image_path);
           }
           $path = $request->file('image')->store('tickets', 'public');
           $ticket->image_path = $path;
       }

       $ticket->save();

       return redirect()->route('tickets.show', $ticket)
           ->with('success', 'ํ‹ฐ์ผ“ ์ •๋ณด๊ฐ€ ์ˆ˜์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค.');
   }

   public function destroy(Ticket $ticket)
   {
       $this->authorize('delete', $ticket);

       if ($ticket->image_path) {
           Storage::disk('public')->delete($ticket->image_path);
       }

       $ticket->delete();

       return redirect()->route('tickets.index')
           ->with('success', 'ํ‹ฐ์ผ“์ด ์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.');
   }

   public function updateStatus(Request $request, Ticket $ticket)
   {
       $this->authorize('update', $ticket);

       $validated = $request->validate([
           'status' => 'required|in:available,reserved,sold'
       ]);

       $ticket->update($validated);

       return back()->with('success', 'ํ‹ฐ์ผ“ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.');
   }
   ```

2. Policy ์ƒ์„ฑ:
   ```bash
   php artisan make:policy TicketPolicy --model=Ticket
   ```
   - `app/Policies/TicketPolicy.php`์—์„œ `update`, `delete` ๋ฉ”์„œ๋“œ ์ •์˜:
   ```php
   public function update(User $user, Ticket $ticket)
   {
       return $user->id === $ticket->user_id;
   }

   public function delete(User $user, Ticket $ticket)
   {
       return $user->id === $ticket->user_id;
   }
   ```

3. ๋ผ์šฐํŠธ ์ถ”๊ฐ€:
   ```php
   Route::middleware('auth')->group(function () {
       Route::get('/tickets/{ticket}/edit', [TicketController::class, 'edit'])->name('tickets.edit');
       Route::put('/tickets/{ticket}', [TicketController::class, 'update'])->name('tickets.update');
       Route::delete('/tickets/{ticket}', [TicketController::class, 'destroy'])->name('tickets.destroy');
       Route::patch('/tickets/{ticket}/status', [TicketController::class, 'updateStatus'])->name('tickets.updateStatus');
   });
   ```

**ํ”„๋ก ํŠธ์—”๋“œ:**

4. `resources/js/Pages/Tickets/Edit.jsx` ์ƒ์„ฑ:
   - Create.jsx์™€ ์œ ์‚ฌํ•˜์ง€๋งŒ ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋กœ ํผ ์ดˆ๊ธฐํ™”
   - `useForm` ํ›…์— `ticket` prop ๋ฐ์ดํ„ฐ ์ „๋‹ฌ
   - `put` ๋ฉ”์„œ๋“œ๋กœ ์ œ์ถœ:
     ```jsx
     put(route('tickets.update', ticket.id))
     ```

5. ์ƒ์„ธ ํŽ˜์ด์ง€(Show.jsx)์—์„œ ํŒ๋งค์ž ์ „์šฉ ๋ฒ„ํŠผ ์ถ”๊ฐ€:
   - "์ˆ˜์ •" ๋ฒ„ํŠผ (Edit ํŽ˜์ด์ง€๋กœ ์ด๋™)
   - "์‚ญ์ œ" ๋ฒ„ํŠผ (ํ™•์ธ ํ›„ DELETE ์š”์ฒญ)
   - ์ƒํƒœ ๋ณ€๊ฒฝ ๋“œ๋กญ๋‹ค์šด (available/reserved/sold)
   - ์‚ญ์ œ ํ™•์ธ ๋ชจ๋‹ฌ:
     ```jsx
     const handleDelete = () => {
         if (confirm('์ •๋ง ์‚ญ์ œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?')) {
             Inertia.delete(route('tickets.destroy', ticket.id));
         }
     };
     ```

6. Flash ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ:
   - `resources/js/Layouts/AuthenticatedLayout.jsx`์—์„œ ์„ฑ๊ณต/์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ํ† ์ŠคํŠธ ์ถ”๊ฐ€
   - Inertia `usePage` ํ›…์œผ๋กœ `flash` ๋ฐ์ดํ„ฐ ์ ‘๊ทผ:
     ```jsx
     const { flash } = usePage().props;
     ```

**์ตœ์ข… ํ…Œ์ŠคํŠธ:**
7. ๋‹ค์–‘ํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค ํ…Œ์ŠคํŠธ:
   - ํ‹ฐ์ผ“ ๋“ฑ๋ก โ†’ ์ˆ˜์ • โ†’ ์ƒํƒœ ๋ณ€๊ฒฝ โ†’ ์‚ญ์ œ
   - ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๊ฐ€ ๊ตฌ๋งค ํฌ๋ง โ†’ ํŒ๋งค์ž๊ฐ€ ์˜ˆ์•ฝ์ค‘์œผ๋กœ ๋ณ€๊ฒฝ
   - ํ•„ํ„ฐ๋ง/์ •๋ ฌ/ํŽ˜์ด์ง€๋„ค์ด์…˜ ๋™์ž‘ ํ™•์ธ
   - ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ/๊ต์ฒด/์‚ญ์ œ ํ™•์ธ
   - ๊ถŒํ•œ ์—†๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ˆ˜์ •/์‚ญ์ œ ์‹œ๋„ ์‹œ ์—๋Ÿฌ ์ฒ˜๋ฆฌ

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ํŒ๋งค์ž๊ฐ€ ์ž์‹ ์˜ ํ‹ฐ์ผ“์„ ์ˆ˜์ •/์‚ญ์ œ/์ƒํƒœ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ. Policy๋กœ ๊ถŒํ•œ ๊ฒ€์ฆ๋˜๋ฉฐ, ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๋Š” ์ ‘๊ทผ ๋ถˆ๊ฐ€. ์ด๋ฏธ์ง€ ๊ต์ฒด ์‹œ ๊ธฐ์กด ํŒŒ์ผ ์‚ญ์ œ๋จ. Flash ๋ฉ”์‹œ์ง€๋กœ ์‚ฌ์šฉ์ž ํ”ผ๋“œ๋ฐฑ ์ œ๊ณต. ์ „์ฒด CRUD ๊ธฐ๋Šฅ๊ณผ ํ•„ํ„ฐ๋ง/์ƒํƒœ ๊ด€๋ฆฌ๊ฐ€ ์™„์ „ํžˆ ์ž‘๋™ํ•˜๋Š” ํ‹ฐ์ผ“ ๋ฆฌ์…€ ๋งˆ์ผ“ํ”Œ๋ ˆ์ด์Šค ์™„์„ฑ.

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

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

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

๋Œ“๊ธ€ (0)

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