๐ŸŽฌ ์˜ํ™”๊ด€ ์ƒ์˜ ์‹œ๊ฐ„ํ‘œ & ์˜ˆ๊ณ ํŽธ ๊ฐค๋Ÿฌ๋ฆฌ - ๋ฐ”์ด๋ธŒ์ฝ”๋”ฉ ๋ ˆ์‹œํ”ผ
์ž…๋ฌธ PHP Laravel AI: Claude

๐ŸŽฌ ์˜ํ™”๊ด€ ์ƒ์˜ ์‹œ๊ฐ„ํ‘œ & ์˜ˆ๊ณ ํŽธ ๊ฐค๋Ÿฌ๋ฆฌ

๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ช… cinema-schedule-trailer-gallery
์กฐํšŒ์ˆ˜ 75 0๊ฑด ์ œ์ถœ

๋ฌธ์ œ ์„ค๋ช…

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

์˜ํ™”๊ด€ ์›น์‚ฌ์ดํŠธ์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋Š” Laravel 13.x + Blade ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค. ์‹ค์ œ ์˜ํ™”๊ด€์ฒ˜๋Ÿผ ํ˜„์žฌ ์ƒ์˜ ์ค‘์ธ ์˜ํ™” ๋ชฉ๋ก, ์ƒ์˜ ์‹œ๊ฐ„ํ‘œ, ์˜ˆ๊ณ ํŽธ ์˜์ƒ์„ ํ•œ๋ˆˆ์— ๋ณผ ์ˆ˜ ์žˆ๋Š” ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๊ด€๋ฆฌ์ž๊ฐ€ ์˜ํ™” ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋ฐฉ๋ฌธ์ž๋Š” ๊น”๋”ํ•œ ์นด๋“œ UI๋กœ ์˜ํ™”๋ฅผ ํƒ์ƒ‰ํ•˜๊ณ , YouTube ์˜ˆ๊ณ ํŽธ์„ ๋ฐ”๋กœ ์‹œ์ฒญํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

  • ์˜ํ™” ๋ชฉ๋ก ํŽ˜์ด์ง€: ํ˜„์žฌ ์ƒ์˜ ์ค‘์ธ ์˜ํ™”๋ฅผ ์นด๋“œ ๊ทธ๋ฆฌ๋“œ ํ˜•ํƒœ๋กœ ํ‘œ์‹œ (ํฌ์Šคํ„ฐ ์ด๋ฏธ์ง€, ์ œ๋ชฉ, ์žฅ๋ฅด, ๋Ÿฌ๋‹ํƒ€์ž„)
  • ์˜ํ™” ์ƒ์„ธ ํŽ˜์ด์ง€: ์ค„๊ฑฐ๋ฆฌ, ๊ฐ๋…, ์ถœ์—ฐ์ง„, ๊ด€๋žŒ๋“ฑ๊ธ‰, YouTube ์˜ˆ๊ณ ํŽธ ์ž„๋ฒ ๋“œ ์˜์ƒ ํ‘œ์‹œ
  • ์ƒ์˜ ์‹œ๊ฐ„ํ‘œ: ์˜ํ™”๋ณ„๋กœ ์˜ค๋Š˜/๋‚ด์ผ/๋ชจ๋ ˆ ์ƒ์˜ ์‹œ๊ฐ„ ๋ชฉ๋ก (์˜ˆ: 10:30, 13:00, 15:30, 18:00, 20:30)
  • ์˜ํ™” ๋“ฑ๋ก ํผ: ๊ฐ„๋‹จํ•œ ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€์—์„œ ์˜ํ™” ์ •๋ณด ์ž…๋ ฅ (์ œ๋ชฉ, ํฌ์Šคํ„ฐ URL, ์žฅ๋ฅด, ๋Ÿฌ๋‹ํƒ€์ž„, ์ค„๊ฑฐ๋ฆฌ, ์˜ˆ๊ณ ํŽธ YouTube URL, ์ƒ์˜ ์‹œ๊ฐ„)
  • ๋ฐ˜์‘ํ˜• ๋””์ž์ธ: Tailwind CSS๋กœ ๋ชจ๋ฐ”์ผ/ํƒœ๋ธ”๋ฆฟ/๋ฐ์Šคํฌํ†ฑ ๋Œ€์‘
  • SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค: movies ํ…Œ์ด๋ธ”๊ณผ showtimes ํ…Œ์ด๋ธ” (1:N ๊ด€๊ณ„)

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

  • ์žฅ๋ฅด๋ณ„ ํ•„ํ„ฐ๋ง (์•ก์…˜, ๋“œ๋ผ๋งˆ, ์ฝ”๋ฏธ๋””, ๊ณตํฌ, SF ๋“ฑ)
  • ์ƒ์˜ ์‹œ๊ฐ„ ์˜ˆ์•ฝ ๋ฒ„ํŠผ (ํด๋ฆญ ์‹œ ๊ฐ„๋‹จํ•œ ์˜ˆ์•ฝ ์™„๋ฃŒ ๋ฉ”์‹œ์ง€)
  • ์˜ํ™” ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ (์ œ๋ชฉ์œผ๋กœ ๊ฒ€์ƒ‰)
  • ๋ณ„์  ํ‘œ์‹œ (๋”๋ฏธ ๋ฐ์ดํ„ฐ๋กœ ํ‰์  ์‹œ๊ฐํ™”)
  • ๋‹คํฌ ๋ชจ๋“œ ํ† ๊ธ€

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

  • Laravel 13.x ๋ผ์šฐํŒ…, ์ปจํŠธ๋กค๋Ÿฌ, Blade ํ…œํ”Œ๋ฆฟ ๊ธฐ์ดˆ
  • Eloquent ORM์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ด€๊ณ„ ์„ค์ • (hasMany, belongsTo)
  • Blade ์ปดํฌ๋„ŒํŠธ ์žฌ์‚ฌ์šฉ (์˜ํ™” ์นด๋“œ, ์‹œ๊ฐ„ํ‘œ ๋ฆฌ์ŠคํŠธ)
  • Tailwind CSS๋กœ ๋น ๋ฅธ UI ๊ตฌ์„ฑ
  • YouTube iframe ์ž„๋ฒ ๋“œ ๋ฐฉ๋ฒ•
  • ํผ ๊ฒ€์ฆ (Form Request Validation)

๊ธฐ์ˆ  ์Šคํƒ

  • Laravel 13.x
  • Blade ํ…œํ”Œ๋ฆฟ ์—”์ง„
  • Tailwind CSS 4.x
  • SQLite
  • Alpine.js (๊ฐ„๋‹จํ•œ ์ธํ„ฐ๋ž™์…˜์šฉ)

์ œ์•ฝ ์กฐ๊ฑด

  • โ€ข Laravel 13.x ์ตœ์‹  ๋ฒ„์ „ ์‚ฌ์šฉ (composer create-project laravel/laravel ๋ช…๋ น)
  • โ€ข Blade ํ…œํ”Œ๋ฆฟ๋งŒ ์‚ฌ์šฉ (React, Vue ๋“ฑ SPA ํ”„๋ ˆ์ž„์›Œํฌ ์‚ฌ์šฉ ๊ธˆ์ง€)
  • โ€ข Tailwind CSS 4.x๋กœ ์Šคํƒ€์ผ๋ง (CDN ๋˜๋Š” Vite ๋นŒ๋“œ)
  • โ€ข SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‚ฌ์šฉ (.env ํŒŒ์ผ์—์„œ DB_CONNECTION=sqlite ์„ค์ •)
  • โ€ข YouTube ์˜ˆ๊ณ ํŽธ URL์€ ์ž„๋ฒ ๋“œ ๊ฐ€๋Šฅํ•œ ํ˜•์‹์œผ๋กœ ์ €์žฅ (https://www.youtube.com/embed/VIDEO_ID)
  • โ€ข ๋ฐ˜์‘ํ˜• ๋””์ž์ธ ํ•„์ˆ˜ (๋ชจ๋ฐ”์ผ ์šฐ์„  ์ ‘๊ทผ)
  • โ€ข Blade ์ปดํฌ๋„ŒํŠธ๋กœ UI ์žฌ์‚ฌ์šฉ์„ฑ ๋†’์ด๊ธฐ
  • โ€ข ์‹ค์ œ ์˜ํ™” ๋ฐ์ดํ„ฐ ์˜ˆ์‹œ 5๊ฐœ ์ด์ƒ ์‹œ๋“œ ๋ฐ์ดํ„ฐ๋กœ ์ œ๊ณต
  • โ€ข ์ƒ์˜ ์‹œ๊ฐ„์€ showtimes ํ…Œ์ด๋ธ”์— ๋ณ„๋„ ์ €์žฅ (movie_id ์™ธ๋ž˜ํ‚ค)
  • โ€ข ๊ด€๋ฆฌ์ž ์ธ์ฆ์€ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์Œ (๊ฐ„๋‹จํ•œ /admin ๊ฒฝ๋กœ๋กœ ์ ‘๊ทผ)

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

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

1
Step 1
mkdir cinema-schedule-trailer-gallery && cd cinema-schedule-trailer-gallery

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

# Laravel 13.x ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ ๋ฐ ์ดˆ๊ธฐ ์„ค์ •

๋‹ค์Œ ์ž‘์—…์„ ์ˆœ์„œ๋Œ€๋กœ ์ง„ํ–‰ํ•ด์ฃผ์„ธ์š”:

1. Laravel 13.x ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ:
   ```bash
   composer create-project laravel/laravel .
   ```

2. .env ํŒŒ์ผ ์ˆ˜์ • (SQLite ์‚ฌ์šฉ):
   ```
   DB_CONNECTION=sqlite
   # DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD ์ฃผ์„ ์ฒ˜๋ฆฌ
   ```

3. database/database.sqlite ํŒŒ์ผ ์ƒ์„ฑ:
   ```bash
   touch database/database.sqlite
   ```

4. Tailwind CSS ์„ค์น˜ ๋ฐ ์„ค์ •:
   - `npm install -D tailwindcss postcss autoprefixer`
   - `npx tailwindcss init -p`
   - tailwind.config.js์—์„œ content ๊ฒฝ๋กœ ์„ค์ •:
     ```js
     content: [
       "./resources/**/*.blade.php",
       "./resources/**/*.js",
     ],
     ```
   - resources/css/app.css์— Tailwind ๋””๋ ‰ํ‹ฐ๋ธŒ ์ถ”๊ฐ€:
     ```css
     @tailwind base;
     @tailwind components;
     @tailwind utilities;
     ```

5. Vite ๋นŒ๋“œ ๋ฐ ๊ฐœ๋ฐœ ์„œ๋ฒ„ ์‹คํ–‰:
   ```bash
   npm install
   npm run dev
   ```

6. ๋ณ„๋„ ํ„ฐ๋ฏธ๋„์—์„œ Laravel ์„œ๋ฒ„ ์‹คํ–‰:
   ```bash
   php artisan serve
   ```

๋ชจ๋“  ์„ค์ •์ด ์™„๋ฃŒ๋˜๋ฉด http://localhost:8000 ์—์„œ Laravel ๊ธฐ๋ณธ ํŽ˜์ด์ง€๊ฐ€ ๋ณด์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: Laravel 13.x ํ”„๋กœ์ ํŠธ๊ฐ€ ์ƒ์„ฑ๋˜๊ณ , SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ • ์™„๋ฃŒ, Tailwind CSS๊ฐ€ ํ†ตํ•ฉ๋˜์–ด ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์ด ์ค€๋น„๋ฉ๋‹ˆ๋‹ค. npm run dev์™€ php artisan serve๊ฐ€ ๋™์‹œ์— ์‹คํ–‰ ์ค‘์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

2
Step 2
# ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ฐ ๋ชจ๋ธ ์ƒ์„ฑ

์˜ํ™” ์ •๋ณด์™€ ์ƒ์˜ ์‹œ๊ฐ„ํ‘œ๋ฅผ ์ €์žฅํ•  ํ…Œ์ด๋ธ”์„ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.

## 1. Movie ๋ชจ๋ธ ๋ฐ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ƒ์„ฑ

```bash
php artisan make:model Movie -m
```

`database/migrations/xxxx_create_movies_table.php` ํŒŒ์ผ์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •:

```php
public function up()
{
    Schema::create('movies', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->string('poster_url');
        $table->string('genre');
        $table->integer('runtime'); // ๋ถ„ ๋‹จ์œ„
        $table->text('synopsis');
        $table->string('director');
        $table->string('cast'); // ์‰ผํ‘œ๋กœ ๊ตฌ๋ถ„๋œ ๋ฐฐ์šฐ ์ด๋ฆ„
        $table->string('rating'); // ์ „์ฒด๊ด€๋žŒ๊ฐ€, 12์„ธ, 15์„ธ, ์ฒญ๋ถˆ ๋“ฑ
        $table->string('trailer_url')->nullable(); // YouTube embed URL
        $table->timestamps();
    });
}
```

## 2. Showtime ๋ชจ๋ธ ๋ฐ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ƒ์„ฑ

```bash
php artisan make:model Showtime -m
```

`database/migrations/xxxx_create_showtimes_table.php` ํŒŒ์ผ์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •:

```php
public function up()
{
    Schema::create('showtimes', function (Blueprint $table) {
        $table->id();
        $table->foreignId('movie_id')->constrained()->onDelete('cascade');
        $table->date('show_date');
        $table->time('show_time');
        $table->timestamps();
    });
}
```

## 3. ๋ชจ๋ธ ๊ด€๊ณ„ ์„ค์ •

`app/Models/Movie.php`์— ๊ด€๊ณ„ ์ถ”๊ฐ€:

```php
public function showtimes()
{
    return $this->hasMany(Showtime::class);
}
```

`app/Models/Showtime.php`์— ๊ด€๊ณ„ ์ถ”๊ฐ€:

```php
public function movie()
{
    return $this->belongsTo(Movie::class);
}

protected $casts = [
    'show_date' => 'date',
    'show_time' => 'datetime:H:i',
];
```

## 4. ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹คํ–‰

```bash
php artisan migrate
```

์™„๋ฃŒ๋˜๋ฉด SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— movies, showtimes ํ…Œ์ด๋ธ”์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: movies์™€ showtimes ํ…Œ์ด๋ธ”์ด ์ƒ์„ฑ๋˜๊ณ , Movie์™€ Showtime ๋ชจ๋ธ ๊ฐ„ 1:N ๊ด€๊ณ„๊ฐ€ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. php artisan migrate ์‹คํ–‰ ํ›„ ์—๋Ÿฌ ์—†์ด ์™„๋ฃŒ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

3
Step 3
# ์‹œ๋“œ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ ๋ฐ ๋”๋ฏธ ์˜ํ™” ์ถ”๊ฐ€

์‹ค์ œ ์˜ํ™”๊ด€์ฒ˜๋Ÿผ ๋ณด์ด๋„๋ก 5๊ฐœ ์ด์ƒ์˜ ์˜ํ™” ๋ฐ์ดํ„ฐ์™€ ๊ฐ ์˜ํ™”๋ณ„ ์ƒ์˜ ์‹œ๊ฐ„ํ‘œ๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”.

## 1. Seeder ์ƒ์„ฑ

```bash
php artisan make:seeder MovieSeeder
```

## 2. `database/seeders/MovieSeeder.php` ์ž‘์„ฑ

```php
use App\Models\Movie;
use App\Models\Showtime;
use Carbon\Carbon;

public function run()
{
    $movies = [
        [
            'title' => '์˜คํŽœํ•˜์ด๋จธ',
            'poster_url' => 'https://image.tmdb.org/t/p/w500/8Gxv8gSFCU0XGDykEGv7zR1n2ua.jpg',
            'genre' => '๋“œ๋ผ๋งˆ',
            'runtime' => 180,
            'synopsis' => '์›์žํญํƒ„์˜ ์•„๋ฒ„์ง€ ๋กœ๋ฒ„ํŠธ ์˜คํŽœํ•˜์ด๋จธ์˜ ์‚ถ๊ณผ ๋งจํ•ดํŠผ ํ”„๋กœ์ ํŠธ๋ฅผ ๋‹ค๋ฃฌ ๋Œ€์„œ์‚ฌ์‹œ.',
            'director' => 'ํฌ๋ฆฌ์Šคํ† ํผ ๋†€๋ž€',
            'cast' => 'ํ‚ฌ๋ฆฌ์–ธ ๋จธํ”ผ, ์—๋ฐ€๋ฆฌ ๋ธ”๋ŸฐํŠธ, ๋กœ๋ฒ„ํŠธ ๋‹ค์šฐ๋‹ˆ ์ฃผ๋‹ˆ์–ด',
            'rating' => '15์„ธ ๊ด€๋žŒ๊ฐ€',
            'trailer_url' => 'https://www.youtube.com/embed/uYPbbksJxIg',
        ],
        [
            'title' => '๋“„: ํŒŒํŠธ 2',
            'poster_url' => 'https://image.tmdb.org/t/p/w500/1pdfLvkbY9ohJlCjQH2CZjjYVvJ.jpg',
            'genre' => 'SF',
            'runtime' => 166,
            'synopsis' => 'ํด ์•„ํŠธ๋ ˆ์ด๋ฐ์Šค๊ฐ€ ํ”„๋ ˆ๋ฉ˜์กฑ๊ณผ ํ•จ๊ป˜ ํ™ฉ์ œ์— ๋งž์„œ ์‹ธ์šฐ๋Š” ์žฅ๋Œ€ํ•œ SF ๋Œ€์ž‘.',
            'director' => '๋“œ๋‹ˆ ๋นŒ๋‡Œ๋ธŒ',
            'cast' => 'ํ‹ฐ๋ชจ์‹œ ์ƒฌ๋ผ๋ฉ”, ์  ๋ฐ์ด์•„, ๋ ˆ๋ฒ ์นด ํผ๊ฑฐ์Šจ',
            'rating' => '12์„ธ ๊ด€๋žŒ๊ฐ€',
            'trailer_url' => 'https://www.youtube.com/embed/Way9Dexny3w',
        ],
        [
            'title' => 'ํŒŒ๋ฌ˜',
            'poster_url' => 'https://image.tmdb.org/t/p/w500/7JsHbxT03xShsvRy4R4TmD5tBTy.jpg',
            'genre' => '๊ณตํฌ',
            'runtime' => 134,
            'synopsis' => '๋ฏธ๊ตญ LA์—์„œ ์˜๋ฌธ์˜ ๋ณ‘์ด ๋ฐœ์ƒํ•˜๊ณ , ๋ฌด์†์ธ๋“ค์ด ๊ธฐ์ดํ•œ ์‚ฌ๊ฑด์˜ ์‹ค์ฒด๋ฅผ ํŒŒํ—ค์น˜๋Š” ์˜ค์ปฌํŠธ ๋ฏธ์Šคํ„ฐ๋ฆฌ.',
            'director' => '์žฅ์žฌํ˜„',
            'cast' => '์ตœ๋ฏผ์‹, ๊น€๊ณ ์€, ์œ ํ•ด์ง„, ์ด๋„ํ˜„',
            'rating' => '15์„ธ ๊ด€๋žŒ๊ฐ€',
            'trailer_url' => 'https://www.youtube.com/embed/HFvPeDSWKTI',
        ],
        [
            'title' => '๋ฒ”์ฃ„๋„์‹œ 4',
            'poster_url' => 'https://image.tmdb.org/t/p/w500/jRyGHNkHSe2vVEcTbNqNvbtHPmO.jpg',
            'genre' => '์•ก์…˜',
            'runtime' => 109,
            'synopsis' => '๋งˆ์„๋„์™€ ๊ด‘์ˆ˜๋Œ€๊ฐ€ IT ๊ธฐ๋ฐ˜ ์‹ ์ข… ๋ฒ”์ฃ„ ์กฐ์ง๊ณผ ๋งž์„œ๋Š” ํ†ต์พŒํ•œ ์•ก์…˜ ๋ธ”๋ก๋ฒ„์Šคํ„ฐ.',
            'director' => 'ํ—ˆ๋ช…ํ–‰',
            'cast' => '๋งˆ๋™์„, ๊น€๋ฌด์—ด, ๋ฐ•์ง€ํ™˜',
            'rating' => '15์„ธ ๊ด€๋žŒ๊ฐ€',
            'trailer_url' => 'https://www.youtube.com/embed/r6XI0xx1LIU',
        ],
        [
            'title' => '์œ„์‹œ',
            'poster_url' => 'https://image.tmdb.org/t/p/w500/AcoVfiv1rrWOmAdpnAMnM56ki19.jpg',
            'genre' => '์• ๋‹ˆ๋ฉ”์ด์…˜',
            'runtime' => 95,
            'synopsis' => '๋””์ฆˆ๋‹ˆ 100์ฃผ๋…„ ๊ธฐ๋…์ž‘. ์†Œ์›์„ ์ด๋ฃจ์–ด์ฃผ๋Š” ์™•๊ตญ์—์„œ ํŽผ์ณ์ง€๋Š” ๋งˆ๋ฒ• ๊ฐ™์€ ๋ชจํ—˜.',
            'director' => 'ํฌ๋ฆฌ์Šค ๋ฒ…, ํŒŒ์šด ๋น„๋ผ์ˆ˜ํ‹ฐ์ฝ˜',
            'cast' => '์•„๋ฆฌ์•„๋‚˜ ๋“œ๋ณด์ฆˆ, ํฌ๋ฆฌ์Šค ํŒŒ์ธ (๋ชฉ์†Œ๋ฆฌ ์ถœ์—ฐ)',
            'rating' => '์ „์ฒด ๊ด€๋žŒ๊ฐ€',
            'trailer_url' => 'https://www.youtube.com/embed/oyKWDB63gJM',
        ],
    ];

    foreach ($movies as $movieData) {
        $movie = Movie::create($movieData);

        // ๊ฐ ์˜ํ™”๋ณ„๋กœ ์˜ค๋Š˜, ๋‚ด์ผ, ๋ชจ๋ ˆ ์ƒ์˜ ์‹œ๊ฐ„ ์ƒ์„ฑ
        $times = ['10:30', '13:00', '15:30', '18:00', '20:30'];
        for ($day = 0; $day < 3; $day++) {
            foreach ($times as $time) {
                Showtime::create([
                    'movie_id' => $movie->id,
                    'show_date' => Carbon::today()->addDays($day),
                    'show_time' => $time,
                ]);
            }
        }
    }
}
```

## 3. DatabaseSeeder์— ๋“ฑ๋ก

`database/seeders/DatabaseSeeder.php`:

```php
public function run()
{
    $this->call([
        MovieSeeder::class,
    ]);
}
```

## 4. ์‹œ๋“œ ์‹คํ–‰

```bash
php artisan db:seed
```

์™„๋ฃŒ๋˜๋ฉด 5๊ฐœ ์˜ํ™”์™€ ๊ฐ๊ฐ 15๊ฐœ์˜ ์ƒ์˜ ์‹œ๊ฐ„(3์ผ ร— 5ํšŒ)์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: MovieSeeder๊ฐ€ ์‹คํ–‰๋˜์–ด 5๊ฐœ์˜ ์˜ํ™”์™€ ์ด 75๊ฐœ์˜ ์ƒ์˜ ์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ๊ฐ€ SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. php artisan db:seed ์‹คํ–‰ ํ›„ ์—๋Ÿฌ ์—†์ด ์™„๋ฃŒ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

4
Step 4
# ์˜ํ™” ๋ชฉ๋ก ํŽ˜์ด์ง€ ๋ฐ ์ƒ์„ธ ํŽ˜์ด์ง€ ๊ตฌํ˜„

๋ฉ”์ธ ํŽ˜์ด์ง€์—์„œ ์˜ํ™” ์นด๋“œ ๊ทธ๋ฆฌ๋“œ๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ , ํด๋ฆญํ•˜๋ฉด ์ƒ์„ธ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๋„๋ก ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.

## 1. ์ปจํŠธ๋กค๋Ÿฌ ์ƒ์„ฑ

```bash
php artisan make:controller MovieController
```

`app/Http/Controllers/MovieController.php`:

```php
namespace App\Http\Controllers;

use App\Models\Movie;
use Illuminate\Http\Request;

class MovieController extends Controller
{
    public function index()
    {
        $movies = Movie::all();
        return view('movies.index', compact('movies'));
    }

    public function show(Movie $movie)
    {
        $movie->load('showtimes');
        return view('movies.show', compact('movie'));
    }
}
```

## 2. ๋ผ์šฐํŠธ ์„ค์ •

`routes/web.php`:

```php
use App\Http\Controllers\MovieController;

Route::get('/', [MovieController::class, 'index'])->name('movies.index');
Route::get('/movies/{movie}', [MovieController::class, 'show'])->name('movies.show');
```

## 3. ๋ ˆ์ด์•„์›ƒ ํ…œํ”Œ๋ฆฟ ์ƒ์„ฑ

`resources/views/layouts/app.blade.php`:

```blade
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@yield('title', '์˜ํ™”๊ด€ ์ƒ์˜ ์‹œ๊ฐ„ํ‘œ')</title>
    @vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body class="bg-gray-900 text-white">
    <nav class="bg-gray-800 shadow-lg">
        <div class="container mx-auto px-4 py-4">
            <a href="{{ route('movies.index') }}" class="text-2xl font-bold text-red-500">๐ŸŽฌ Cinema</a>
        </div>
    </nav>

    <main class="container mx-auto px-4 py-8">
        @yield('content')
    </main>

    <footer class="bg-gray-800 mt-16 py-6 text-center text-gray-400">
        <p>&copy; 2024 Cinema. All rights reserved.</p>
    </footer>
</body>
</html>
```

## 4. ์˜ํ™” ๋ชฉ๋ก ํŽ˜์ด์ง€

`resources/views/movies/index.blade.php`:

```blade
@extends('layouts.app')

@section('title', 'ํ˜„์žฌ ์ƒ์˜์ž‘ - Cinema')

@section('content')
<div class="mb-8">
    <h1 class="text-4xl font-bold mb-2">ํ˜„์žฌ ์ƒ์˜ ์ค‘</h1>
    <p class="text-gray-400">์ง€๊ธˆ ๊ทน์žฅ์—์„œ ๋งŒ๋‚˜๋ณด์„ธ์š”</p>
</div>

<div class="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-6">
    @foreach($movies as $movie)
        <a href="{{ route('movies.show', $movie) }}" class="group">
            <div class="bg-gray-800 rounded-lg overflow-hidden shadow-lg hover:shadow-2xl transition-all duration-300 transform hover:-translate-y-2">
                <img src="{{ $movie->poster_url }}" alt="{{ $movie->title }}" class="w-full h-96 object-cover">
                <div class="p-4">
                    <h3 class="text-lg font-bold mb-2 group-hover:text-red-500 transition">{{ $movie->title }}</h3>
                    <div class="flex items-center justify-between text-sm text-gray-400">
                        <span class="bg-gray-700 px-2 py-1 rounded">{{ $movie->genre }}</span>
                        <span>{{ $movie->runtime }}๋ถ„</span>
                    </div>
                </div>
            </div>
        </a>
    @endforeach
</div>
@endsection
```

## 5. ์˜ํ™” ์ƒ์„ธ ํŽ˜์ด์ง€

`resources/views/movies/show.blade.php`:

```blade
@extends('layouts.app')

@section('title', $movie->title . ' - Cinema')

@section('content')
<div class="max-w-6xl mx-auto">
    <div class="grid md:grid-cols-3 gap-8">
        <div class="md:col-span-1">
            <img src="{{ $movie->poster_url }}" alt="{{ $movie->title }}" class="w-full rounded-lg shadow-2xl">
        </div>
        <div class="md:col-span-2">
            <h1 class="text-4xl font-bold mb-4">{{ $movie->title }}</h1>
            <div class="flex flex-wrap gap-4 mb-6 text-sm">
                <span class="bg-red-600 px-3 py-1 rounded">{{ $movie->rating }}</span>
                <span class="bg-gray-700 px-3 py-1 rounded">{{ $movie->genre }}</span>
                <span class="bg-gray-700 px-3 py-1 rounded">{{ $movie->runtime }}๋ถ„</span>
            </div>
            <p class="text-gray-300 mb-6 leading-relaxed">{{ $movie->synopsis }}</p>
            <div class="mb-4">
                <p class="text-gray-400"><span class="font-semibold text-white">๊ฐ๋…:</span> {{ $movie->director }}</p>
                <p class="text-gray-400"><span class="font-semibold text-white">์ถœ์—ฐ:</span> {{ $movie->cast }}</p>
            </div>

            @if($movie->trailer_url)
            <div class="mb-8">
                <h2 class="text-2xl font-bold mb-4">์˜ˆ๊ณ ํŽธ</h2>
                <div class="aspect-video">
                    <iframe src="{{ $movie->trailer_url }}" class="w-full h-full rounded-lg" allowfullscreen></iframe>
                </div>
            </div>
            @endif

            <div>
                <h2 class="text-2xl font-bold mb-4">์ƒ์˜ ์‹œ๊ฐ„ํ‘œ</h2>
                @php
                    $groupedShowtimes = $movie->showtimes->groupBy(function($showtime) {
                        return $showtime->show_date->format('Y-m-d');
                    });
                @endphp
                @foreach($groupedShowtimes as $date => $times)
                    <div class="mb-4">
                        <h3 class="font-semibold mb-2">{{ \Carbon\Carbon::parse($date)->isoFormat('M์›” D์ผ (ddd)') }}</h3>
                        <div class="flex flex-wrap gap-2">
                            @foreach($times as $showtime)
                                <button class="bg-gray-700 hover:bg-red-600 px-4 py-2 rounded transition">
                                    {{ $showtime->show_time->format('H:i') }}
                                </button>
                            @endforeach
                        </div>
                    </div>
                @endforeach
            </div>
        </div>
    </div>
</div>
@endsection
```

์™„๋ฃŒ๋˜๋ฉด http://localhost:8000 ์—์„œ ์˜ํ™” ๋ชฉ๋ก์„ ๋ณผ ์ˆ˜ ์žˆ๊ณ , ๊ฐ ์˜ํ™”๋ฅผ ํด๋ฆญํ•˜๋ฉด ์ƒ์„ธ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ์˜ํ™” ๋ชฉ๋ก ํŽ˜์ด์ง€์— 5๊ฐœ์˜ ์˜ํ™”๊ฐ€ ์นด๋“œ ํ˜•ํƒœ๋กœ ํ‘œ์‹œ๋˜๊ณ , ํด๋ฆญํ•˜๋ฉด ์ƒ์„ธ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜์—ฌ ํฌ์Šคํ„ฐ, ์ค„๊ฑฐ๋ฆฌ, ์˜ˆ๊ณ ํŽธ, ์ƒ์˜ ์‹œ๊ฐ„ํ‘œ๊ฐ€ ๋ณด์ž…๋‹ˆ๋‹ค. Tailwind CSS๋กœ ์Šคํƒ€์ผ๋ง๋œ ๋ฐ˜์‘ํ˜• UI๊ฐ€ ์™„์„ฑ๋ฉ๋‹ˆ๋‹ค.

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

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

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

๋Œ“๊ธ€ (0)

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