๐Ÿƒ ๋Ÿฌ๋‹ ํฌ๋ฃจ ์ฃผ๊ฐ„ ๋Ÿฌ๋‹ ๊ธฐ๋ก ํŽ˜์ด์ง€ - ํŒ€์›๋ณ„ ๊ฑฐ๋ฆฌ & ํŽ˜์ด์Šค ํ†ต๊ณ„ - ๋ฐ”์ด๋ธŒ์ฝ”๋”ฉ ๋ ˆ์‹œํ”ผ
์ž…๋ฌธ PHP Laravel AI: Claude

๐Ÿƒ ๋Ÿฌ๋‹ ํฌ๋ฃจ ์ฃผ๊ฐ„ ๋Ÿฌ๋‹ ๊ธฐ๋ก ํŽ˜์ด์ง€ - ํŒ€์›๋ณ„ ๊ฑฐ๋ฆฌ & ํŽ˜์ด์Šค ํ†ต๊ณ„

๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ช… running-crew-weekly-log
์กฐํšŒ์ˆ˜ 73 0๊ฑด ์ œ์ถœ

๋ฌธ์ œ ์„ค๋ช…

๐ŸŽฏ ํ”„๋กœ์ ํŠธ ๊ฐœ์š”

๋Ÿฌ๋‹ ํฌ๋ฃจ(๋™ํ˜ธํšŒ)๋ฅผ ์œ„ํ•œ ์ฃผ๊ฐ„ ๋Ÿฌ๋‹ ๊ธฐ๋ก ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ํฌ๋ฃจ ๋ฉค๋ฒ„๋“ค์ด ์ด๋ฒˆ ์ฃผ์— ๋›ด ๊ฑฐ๋ฆฌ, ํŽ˜์ด์Šค, ๋‚ ์งœ๋ฅผ ๊ธฐ๋กํ•˜๊ณ , ์ „์ฒด ํŒ€์›์˜ ํ†ต๊ณ„๋ฅผ ํ•œ๋ˆˆ์— ๋ณผ ์ˆ˜ ์žˆ๋Š” ์‹ฌํ”Œํ•œ ์›น ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค.

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

  1. ๋Ÿฌ๋‹ ๊ธฐ๋ก ๋“ฑ๋ก ํผ

    • ์ด๋ฆ„, ๋‚ ์งœ, ๊ฑฐ๋ฆฌ(km), ํŽ˜์ด์Šค(๋ถ„:์ดˆ/km), ๋ฉ”๋ชจ ์ž…๋ ฅ
    • SQLite DB์— ์ €์žฅ
  2. ์ฃผ๊ฐ„ ๋Ÿฌ๋‹ ๊ธฐ๋ก ๋ชฉ๋ก

    • ์ตœ๊ทผ 1์ฃผ์ผ ๊ธฐ๋ก๋งŒ ํ‘œ์‹œ (์˜ค๋Š˜ ๊ธฐ์ค€ 7์ผ ์ด๋‚ด)
    • ๋‚ ์งœ ์ตœ์‹ ์ˆœ ์ •๋ ฌ
    • ๊ฐ ๊ธฐ๋ก๋งˆ๋‹ค ์ด๋ฆ„, ๋‚ ์งœ, ๊ฑฐ๋ฆฌ, ํŽ˜์ด์Šค ํ‘œ์‹œ
  3. ์ฃผ๊ฐ„ ํ†ต๊ณ„ ์š”์•ฝ

    • ์ „์ฒด ํฌ๋ฃจ์› ์ด ๊ฑฐ๋ฆฌ ํ•ฉ๊ณ„
    • ํ‰๊ท  ํŽ˜์ด์Šค ๊ณ„์‚ฐ
    • ๊ฐ€์žฅ ๋งŽ์ด ๋›ด ํฌ๋ฃจ์› TOP 3 ๋ฐฐ์ง€
  4. ๋ฐ˜์‘ํ˜• UI (Tailwind CSS)

    • ๋ชจ๋ฐ”์ผ์—์„œ๋„ ํŽธํ•˜๊ฒŒ ๊ธฐ๋ก ์ž…๋ ฅ
    • ์นด๋“œํ˜• ๋ ˆ์ด์•„์›ƒ์œผ๋กœ ๊น”๋”ํ•˜๊ฒŒ ์ •๋ฆฌ
    • ๋‹ฌ๋ฆฌ๊ธฐ ๊ด€๋ จ ์•„์ด์ฝ˜/์ƒ‰์ƒ ํ™œ์šฉ (๐Ÿƒโ€โ™‚๏ธ ์ดˆ๋ก/ํŒŒ๋ž‘ ๊ณ„์—ด)

๐ŸŽ ์„ ํƒ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ (๋ณด๋„ˆ์Šค)

  • ํฌ๋ฃจ์›๋ณ„ ํ•„ํ„ฐ๋ง (๋“œ๋กญ๋‹ค์šด์œผ๋กœ ํŠน์ • ๋ฉค๋ฒ„๋งŒ ๋ณด๊ธฐ)
  • ํŽ˜์ด์Šค๋ฅผ ๋ง‰๋Œ€ ๊ทธ๋ž˜ํ”„๋กœ ์‹œ๊ฐํ™” (๋น ๋ฆ„/๋ณดํ†ต/๋А๋ฆผ ์ƒ‰์ƒ ๊ตฌ๋ถ„)
  • ๊ธฐ๋ก ์‚ญ์ œ ๋ฒ„ํŠผ (๋ณธ์ธ ์ด๋ฆ„ ํ™•์ธ ํ›„ ์‚ญ์ œ)
  • "์ด๋ฒˆ ์ฃผ MVP ๋Ÿฌ๋„ˆ" ์ž๋™ ์„ ์ • (๊ฐ€์žฅ ๋งŽ์ด ๋›ด ์‚ฌ๋žŒ)
  • ๋‹คํฌ ๋ชจ๋“œ ํ† ๊ธ€

๐Ÿ› ๏ธ ๊ธฐ์ˆ  ์Šคํƒ

  • Backend: Laravel 13.x + Blade
  • Database: SQLite (๊ฐ„๋‹จํ•œ ๋กœ์ปฌ DB)
  • Frontend: Tailwind CSS (CDN ๋˜๋Š” Vite ํ†ตํ•ฉ)
  • ๊ธฐํƒ€: Carbon (๋‚ ์งœ ๊ณ„์‚ฐ์šฉ)

๐Ÿ“ ํ•™์Šต ํฌ์ธํŠธ

  • Laravel ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์œผ๋กœ ํ…Œ์ด๋ธ” ์ƒ์„ฑ
  • Blade ํ…œํ”Œ๋ฆฟ์œผ๋กœ ํผ๊ณผ ๋ชฉ๋ก ๋ Œ๋”๋ง
  • Eloquent ORM์œผ๋กœ ๋ฐ์ดํ„ฐ ์กฐํšŒ/์ €์žฅ
  • Carbon์œผ๋กœ "์ตœ๊ทผ 7์ผ" ํ•„ํ„ฐ๋ง
  • Tailwind CSS๋กœ ๋น ๋ฅธ UI ๊ตฌ์„ฑ

์ œ์•ฝ ์กฐ๊ฑด

  • โ€ข Laravel 13.x + Blade ํ…œํ”Œ๋ฆฟ ์‚ฌ์šฉ (React/Vue ๋“ฑ SPA ํ”„๋ ˆ์ž„์›Œํฌ ์‚ฌ์šฉ ๊ธˆ์ง€)
  • โ€ข SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‚ฌ์šฉ (MySQL ์„ค์ • ๋ถˆํ•„์š”)
  • โ€ข Tailwind CSS๋กœ ์Šคํƒ€์ผ๋ง (CDN ๋˜๋Š” Laravel Mix/Vite ํ†ตํ•ฉ)
  • โ€ข ํŽ˜์ด์ง€๋Š” ๋‹จ์ผ ๋ผ์šฐํŠธ (/) ๋˜๋Š” /running-log๋กœ ๊ตฌ์„ฑ
  • โ€ข ๋Ÿฌ๋‹ ๊ธฐ๋ก ํ…Œ์ด๋ธ”: runs (id, name, date, distance_km, pace_seconds, memo, created_at)
  • โ€ข ํŽ˜์ด์Šค๋Š” '๋ถ„:์ดˆ' ํ˜•์‹์œผ๋กœ ์ž…๋ ฅ๋ฐ›์•„ ์ดˆ(int)๋กœ ์ €์žฅ ํ›„ ํ‘œ์‹œ ์‹œ ๋‹ค์‹œ '๋ถ„:์ดˆ'๋กœ ๋ณ€ํ™˜
  • โ€ข ์ตœ๊ทผ 7์ผ ๊ธฐ๋ก๋งŒ ํ‘œ์‹œ (Carbon::now()->subDays(7) ํ™œ์šฉ)
  • โ€ข ํ†ต๊ณ„๋Š” Eloquent์˜ sum(), avg() ๋“ฑ ์ง‘๊ณ„ ํ•จ์ˆ˜ ์‚ฌ์šฉ
  • โ€ข ํผ ๊ฒ€์ฆ: ์ด๋ฆ„(ํ•„์ˆ˜), ๋‚ ์งœ(ํ•„์ˆ˜, date), ๊ฑฐ๋ฆฌ(ํ•„์ˆ˜, numeric, min:0.1), ํŽ˜์ด์Šค(ํ•„์ˆ˜, ํ˜•์‹: MM:SS)

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

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

1
Step 1
mkdir running-crew-weekly-log && cd running-crew-weekly-log

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

# Laravel ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐํ™”

composer create-project laravel/laravel:^13.0 .

ํ”„๋กœ์ ํŠธ๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด ๋‹ค์Œ์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”:
- Laravel ๋ฒ„์ „์ด 13.x์ธ์ง€ ํ™•์ธ (`php artisan --version`)
- `.env` ํŒŒ์ผ์—์„œ DB_CONNECTION=sqlite๋กœ ์„ค์ •
- `database/database.sqlite` ํŒŒ์ผ ์ƒ์„ฑ (`touch database/database.sqlite`)

๊ทธ ๋‹ค์Œ, ๋Ÿฌ๋‹ ๊ธฐ๋ก ํ…Œ์ด๋ธ”์„ ์œ„ํ•œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํŒŒ์ผ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

```bash
php artisan make:migration create_runs_table
```

๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํŒŒ์ผ(`database/migrations/xxxx_create_runs_table.php`)์— ๋‹ค์Œ ์Šคํ‚ค๋งˆ๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”:

```php
Schema::create('runs', function (Blueprint $table) {
    $table->id();
    $table->string('name'); // ํฌ๋ฃจ์› ์ด๋ฆ„
    $table->date('date'); // ๋Ÿฌ๋‹ ๋‚ ์งœ
    $table->decimal('distance_km', 5, 2); // ๊ฑฐ๋ฆฌ (์˜ˆ: 5.25km)
    $table->integer('pace_seconds'); // ํŽ˜์ด์Šค๋ฅผ ์ดˆ ๋‹จ์œ„๋กœ ์ €์žฅ (์˜ˆ: 5๋ถ„ 30์ดˆ = 330์ดˆ)
    $table->text('memo')->nullable(); // ๋ฉ”๋ชจ
    $table->timestamps();
});
```

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

๋งˆ์ง€๋ง‰์œผ๋กœ Run ๋ชจ๋ธ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค:
```bash
php artisan make:model Run
```

`app/Models/Run.php`์—์„œ fillable ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”:
```php
protected $fillable = ['name', 'date', 'distance_km', 'pace_seconds', 'memo'];
```

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: Laravel 13.x ํ”„๋กœ์ ํŠธ๊ฐ€ ์ดˆ๊ธฐํ™”๋˜๊ณ , SQLite DB๊ฐ€ ์„ค์ •๋˜๋ฉฐ, runs ํ…Œ์ด๋ธ”๊ณผ Run ๋ชจ๋ธ์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. `php artisan migrate` ์‹คํ–‰ ์‹œ runs ํ…Œ์ด๋ธ”์ด database.sqlite์— ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

2
Step 2
# Tailwind CSS ์„ค์ • ๋ฐ ๋ฉ”์ธ ํŽ˜์ด์ง€ ๋ ˆ์ด์•„์›ƒ ์ƒ์„ฑ

Tailwind CSS๋ฅผ Laravel์— ํ†ตํ•ฉํ•ฉ๋‹ˆ๋‹ค. Vite๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น ๋ฅด๊ฒŒ ์„ค์ •ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

1. **Tailwind CSS ์„ค์น˜**

```bash
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```

2. **`tailwind.config.js` ์ˆ˜์ •**

```js
export default {
  content: [
    "./resources/**/*.blade.php",
    "./resources/**/*.js",
  ],
  theme: {
    extend: {
      colors: {
        'runner-green': '#10b981',
        'runner-blue': '#3b82f6',
      },
    },
  },
  plugins: [],
}
```

3. **`resources/css/app.css`์— Tailwind ์ง€์‹œ์–ด ์ถ”๊ฐ€**

```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

4. **Vite ๋นŒ๋“œ**

```bash
npm install
npm run dev
```

(๊ฐœ๋ฐœ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•œ ์ƒํƒœ๋กœ ๋‘์„ธ์š”)

5. **๋ฉ”์ธ ๋ ˆ์ด์•„์›ƒ Blade ํŒŒ์ผ ์ƒ์„ฑ**

`resources/views/layout.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>๐Ÿƒ ๋Ÿฌ๋‹ ํฌ๋ฃจ ์ฃผ๊ฐ„ ๊ธฐ๋ก</title>
    @vite('resources/css/app.css')
</head>
<body class="bg-gradient-to-br from-green-50 to-blue-50 min-h-screen">
    <div class="container mx-auto px-4 py-8 max-w-4xl">
        <header class="text-center mb-8">
            <h1 class="text-4xl font-bold text-runner-green mb-2">๐Ÿƒโ€โ™‚๏ธ ๋Ÿฌ๋‹ ํฌ๋ฃจ ์ฃผ๊ฐ„ ๊ธฐ๋ก</h1>
            <p class="text-gray-600">์ด๋ฒˆ ์ฃผ ์šฐ๋ฆฌ ํฌ๋ฃจ์˜ ๋Ÿฌ๋‹ ๊ธฐ๋ก์„ ๋‚จ๊ฒจ๋ณด์„ธ์š”!</p>
        </header>

        <main>
            @yield('content')
        </main>

        <footer class="text-center mt-12 text-gray-500 text-sm">
            <p>๐Ÿ’ช ํ•จ๊ป˜ ๋‹ฌ๋ฆฌ๋ฉด ๋” ๋ฉ€๋ฆฌ ๊ฐ‘๋‹ˆ๋‹ค</p>
        </footer>
    </div>
</body>
</html>
```

์ด์ œ ๊ธฐ๋ณธ ๋ ˆ์ด์•„์›ƒ์ด ์™„์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ๋‹จ๊ณ„์—์„œ ๋Ÿฌ๋‹ ๊ธฐ๋ก ํผ๊ณผ ๋ชฉ๋ก์„ ์ถ”๊ฐ€ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: Tailwind CSS๊ฐ€ Laravel ํ”„๋กœ์ ํŠธ์— ํ†ตํ•ฉ๋˜๊ณ , Vite๋กœ ๋นŒ๋“œ๋ฉ๋‹ˆ๋‹ค. `resources/views/layout.blade.php` ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์ด ์ƒ์„ฑ๋˜์–ด ๋Ÿฌ๋‹ ํฌ๋ฃจ ํ…Œ๋งˆ์˜ ํ—ค๋”์™€ ํ‘ธํ„ฐ๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

3
Step 3
# ๋Ÿฌ๋‹ ๊ธฐ๋ก ์ž…๋ ฅ ํผ ๋ฐ ๋ชฉ๋ก ํŽ˜์ด์ง€ ๊ตฌํ˜„

์ด์ œ ๋ฉ”์ธ ํŽ˜์ด์ง€์— ๋Ÿฌ๋‹ ๊ธฐ๋ก ํผ๊ณผ ์ตœ๊ทผ 7์ผ ๊ธฐ๋ก ๋ชฉ๋ก์„ ๋งŒ๋“ค๊ฒ ์Šต๋‹ˆ๋‹ค.

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

```bash
php artisan make:controller RunController
```

2. **`app/Http/Controllers/RunController.php` ์ž‘์„ฑ**

```php
namespace App\Http\Controllers;

use App\Models\Run;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;

class RunController extends Controller
{
    public function index()
    {
        // ์ตœ๊ทผ 7์ผ ๊ธฐ๋ก๋งŒ ๊ฐ€์ ธ์˜ค๊ธฐ
        $runs = Run::where('date', '>=', Carbon::now()->subDays(7))
            ->orderBy('date', 'desc')
            ->orderBy('created_at', 'desc')
            ->get();

        // ์ฃผ๊ฐ„ ํ†ต๊ณ„
        $totalDistance = $runs->sum('distance_km');
        $avgPaceSeconds = $runs->avg('pace_seconds');
        
        // TOP 3 ๋Ÿฌ๋„ˆ (๊ฑฐ๋ฆฌ ํ•ฉ์‚ฐ)
        $topRunners = Run::where('date', '>=', Carbon::now()->subDays(7))
            ->selectRaw('name, SUM(distance_km) as total_distance')
            ->groupBy('name')
            ->orderBy('total_distance', 'desc')
            ->limit(3)
            ->get();

        return view('runs.index', compact('runs', 'totalDistance', 'avgPaceSeconds', 'topRunners'));
    }

    public function store(Request $request)
    {
        $validated = $request->validate([
            'name' => 'required|string|max:50',
            'date' => 'required|date',
            'distance_km' => 'required|numeric|min:0.1',
            'pace' => 'required|regex:/^\d{1,2}:\d{2}$/', // MM:SS ํ˜•์‹
            'memo' => 'nullable|string|max:200',
        ]);

        // ํŽ˜์ด์Šค๋ฅผ ์ดˆ๋กœ ๋ณ€ํ™˜ (์˜ˆ: "5:30" -> 330์ดˆ)
        [$minutes, $seconds] = explode(':', $validated['pace']);
        $paceSeconds = ($minutes * 60) + $seconds;

        Run::create([
            'name' => $validated['name'],
            'date' => $validated['date'],
            'distance_km' => $validated['distance_km'],
            'pace_seconds' => $paceSeconds,
            'memo' => $validated['memo'],
        ]);

        return redirect()->route('runs.index')->with('success', '๋Ÿฌ๋‹ ๊ธฐ๋ก์ด ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค! ๐ŸŽ‰');
    }
}
```

3. **๋ผ์šฐํŠธ ์„ค์ • (`routes/web.php`)**

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

Route::get('/', [RunController::class, 'index'])->name('runs.index');
Route::post('/runs', [RunController::class, 'store'])->name('runs.store');
```

4. **๋ทฐ ํŒŒ์ผ ์ƒ์„ฑ (`resources/views/runs/index.blade.php`)**

```blade
@extends('layout')

@section('content')
    {{-- ์„ฑ๊ณต ๋ฉ”์‹œ์ง€ --}}
    @if(session('success'))
        <div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded mb-6">
            {{ session('success') }}
        </div>
    @endif

    {{-- ๋Ÿฌ๋‹ ๊ธฐ๋ก ์ž…๋ ฅ ํผ --}}
    <div class="bg-white rounded-lg shadow-md p-6 mb-8">
        <h2 class="text-2xl font-bold text-runner-blue mb-4">๐Ÿ“ ์˜ค๋Š˜ ๋Ÿฌ๋‹ ๊ธฐ๋กํ•˜๊ธฐ</h2>
        <form action="{{ route('runs.store') }}" method="POST" class="space-y-4">
            @csrf
            <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-1">์ด๋ฆ„</label>
                    <input type="text" name="name" required 
                           class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-runner-green focus:border-runner-green"
                           placeholder="ํ™๊ธธ๋™">
                    @error('name') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
                </div>
                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-1">๋‚ ์งœ</label>
                    <input type="date" name="date" required value="{{ date('Y-m-d') }}"
                           class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-runner-green focus:border-runner-green">
                    @error('date') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
                </div>
                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-1">๊ฑฐ๋ฆฌ (km)</label>
                    <input type="number" step="0.01" name="distance_km" required 
                           class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-runner-green focus:border-runner-green"
                           placeholder="5.5">
                    @error('distance_km') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
                </div>
                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-1">ํŽ˜์ด์Šค (๋ถ„:์ดˆ/km)</label>
                    <input type="text" name="pace" required pattern="\d{1,2}:\d{2}"
                           class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-runner-green focus:border-runner-green"
                           placeholder="5:30">
                    @error('pace') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
                </div>
            </div>
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">๋ฉ”๋ชจ (์„ ํƒ)</label>
                <textarea name="memo" rows="2" 
                          class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-runner-green focus:border-runner-green"
                          placeholder="์˜ค๋Š˜ ์ปจ๋””์…˜์ด ์ข‹์•˜์–ด์š”!"></textarea>
            </div>
            <button type="submit" 
                    class="w-full bg-runner-green hover:bg-green-600 text-white font-bold py-3 rounded-md transition">
                ๐Ÿƒ ๊ธฐ๋ก ์ถ”๊ฐ€ํ•˜๊ธฐ
            </button>
        </form>
    </div>

    {{-- ์ฃผ๊ฐ„ ํ†ต๊ณ„ --}}
    <div class="bg-white rounded-lg shadow-md p-6 mb-8">
        <h2 class="text-2xl font-bold text-runner-blue mb-4">๐Ÿ“Š ์ด๋ฒˆ ์ฃผ ํ†ต๊ณ„</h2>
        <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
            <div class="text-center">
                <p class="text-gray-600 text-sm">์ด ๊ฑฐ๋ฆฌ</p>
                <p class="text-3xl font-bold text-runner-green">{{ number_format($totalDistance, 1) }} km</p>
            </div>
            <div class="text-center">
                <p class="text-gray-600 text-sm">ํ‰๊ท  ํŽ˜์ด์Šค</p>
                <p class="text-3xl font-bold text-runner-blue">
                    @if($avgPaceSeconds)
                        {{ floor($avgPaceSeconds / 60) }}:{{ str_pad($avgPaceSeconds % 60, 2, '0', STR_PAD_LEFT) }}
                    @else
                        -:--
                    @endif
                </p>
            </div>
            <div class="text-center">
                <p class="text-gray-600 text-sm">์ „์ฒด ๊ธฐ๋ก ์ˆ˜</p>
                <p class="text-3xl font-bold text-gray-700">{{ $runs->count() }}๊ฐœ</p>
            </div>
        </div>

        {{-- TOP 3 ๋Ÿฌ๋„ˆ --}}
        @if($topRunners->count() > 0)
            <div class="mt-6">
                <h3 class="text-lg font-semibold text-gray-800 mb-3">๐Ÿ† ์ด๋ฒˆ ์ฃผ TOP ๋Ÿฌ๋„ˆ</h3>
                <div class="space-y-2">
                    @foreach($topRunners as $index => $runner)
                        <div class="flex items-center justify-between bg-gradient-to-r from-yellow-50 to-orange-50 px-4 py-2 rounded">
                            <span class="font-medium">
                                @if($index === 0) ๐Ÿฅ‡
                                @elseif($index === 1) ๐Ÿฅˆ
                                @else ๐Ÿฅ‰
                                @endif
                                {{ $runner->name }}
                            </span>
                            <span class="text-runner-green font-bold">{{ number_format($runner->total_distance, 1) }} km</span>
                        </div>
                    @endforeach
                </div>
            </div>
        @endif
    </div>

    {{-- ๋Ÿฌ๋‹ ๊ธฐ๋ก ๋ชฉ๋ก --}}
    <div class="bg-white rounded-lg shadow-md p-6">
        <h2 class="text-2xl font-bold text-runner-blue mb-4">๐Ÿ“… ์ตœ๊ทผ 7์ผ ๊ธฐ๋ก</h2>
        @if($runs->count() > 0)
            <div class="space-y-3">
                @foreach($runs as $run)
                    <div class="border border-gray-200 rounded-lg p-4 hover:shadow-md transition">
                        <div class="flex justify-between items-start">
                            <div>
                                <p class="font-bold text-lg text-gray-800">{{ $run->name }}</p>
                                <p class="text-sm text-gray-500">{{ Carbon\Carbon::parse($run->date)->format('Y๋…„ m์›” d์ผ (D)') }}</p>
                            </div>
                            <div class="text-right">
                                <p class="text-2xl font-bold text-runner-green">{{ number_format($run->distance_km, 1) }} km</p>
                                <p class="text-sm text-gray-600">
                                    โฑ๏ธ {{ floor($run->pace_seconds / 60) }}:{{ str_pad($run->pace_seconds % 60, 2, '0', STR_PAD_LEFT) }}/km
                                </p>
                            </div>
                        </div>
                        @if($run->memo)
                            <p class="mt-2 text-gray-600 text-sm">๐Ÿ’ฌ {{ $run->memo }}</p>
                        @endif
                    </div>
                @endforeach
            </div>
        @else
            <p class="text-gray-500 text-center py-8">์•„์ง ์ด๋ฒˆ ์ฃผ ๋Ÿฌ๋‹ ๊ธฐ๋ก์ด ์—†์Šต๋‹ˆ๋‹ค. ์ฒซ ๊ธฐ๋ก์„ ๋‚จ๊ฒจ๋ณด์„ธ์š”! ๐Ÿƒโ€โ™€๏ธ</p>
        @endif
    </div>
@endsection
```

์ด์ œ `php artisan serve`๋กœ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๋ธŒ๋ผ์šฐ์ €์—์„œ ํ™•์ธํ•ด๋ณด์„ธ์š”!

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ๋Ÿฌ๋‹ ๊ธฐ๋ก ์ž…๋ ฅ ํผ์ด ํ‘œ์‹œ๋˜๊ณ , ํผ์„ ์ œ์ถœํ•˜๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ SQLite DB์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์ตœ๊ทผ 7์ผ ๊ธฐ๋ก์ด ์นด๋“œ ํ˜•ํƒœ๋กœ ํ‘œ์‹œ๋˜๋ฉฐ, ์ฃผ๊ฐ„ ํ†ต๊ณ„(์ด ๊ฑฐ๋ฆฌ, ํ‰๊ท  ํŽ˜์ด์Šค, TOP 3 ๋Ÿฌ๋„ˆ)๊ฐ€ ๊ณ„์‚ฐ๋˜์–ด ๋ณด์—ฌ์ง‘๋‹ˆ๋‹ค. Tailwind CSS๋กœ ์Šคํƒ€์ผ๋ง๋œ ๋ฐ˜์‘ํ˜• UI๊ฐ€ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

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

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

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

๋Œ“๊ธ€ (0)

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