๐ธ ๋ฎค์ง์ ๊ณต์ฐ ์ ๋ฆฌ์คํธ ๊ณต์ ํ์ด์ง - ํฌ๋ค๊ณผ ํจ๊ปํ๋ ๋ฌด๋
musician-setlist-sharing-page
๋ฌธ์ ์ค๋ช
๐ต ํ๋ก์ ํธ ๊ฐ์
๋ฎค์ง์ ์ด๋ ๋ฐด๋๊ฐ ๊ณต์ฐ์์ ์ฐ์ฃผํ ๊ณก ๋ชฉ๋ก(์ ๋ฆฌ์คํธ)์ ๋ฏธ๋ฆฌ ๊ณต๊ฐํ๊ณ , ํฌ๋ค์ด ๋ฃ๊ณ ์ถ์ ๊ณก์ ํฌํํ๊ฑฐ๋ ๋๊ธ์ ๋จ๊ธธ ์ ์๋ ์ธํฐ๋ํฐ๋ธ ํ์ด์ง๋ฅผ ๋ง๋ญ๋๋ค. ๊ณต์ฐ ์ ํฌ๋ค์ ๊ธฐ๋๊ฐ์ ๋์ด๊ณ , ๋ฎค์ง์ ์ ๊ด๊ฐ์ ์ ํธ๋๋ฅผ ํ์ ํ ์ ์๋ ์ค์ฉ์ ์ธ ์๋น์ค์ ๋๋ค.
โจ ํ์ ๊ธฐ๋ฅ
- ์ ๋ฆฌ์คํธ ๋ฑ๋ก: ๊ณต์ฐ ์ ๋ชฉ, ๋ ์ง, ์ฅ์์ ํจ๊ป ์ฐ์ฃผํ ๊ณก ๋ชฉ๋ก ์ ๋ ฅ
- ๊ณก๋ณ ์ข์์: ํฌ๋ค์ด ๋ฃ๊ณ ์ถ์ ๊ณก์ ํํธ ๋ฒํผ ํด๋ฆญ (์ค๋ณต ๋ฐฉ์ง)
- ์ค์๊ฐ ์ธ๊ธฐ ์์: ์ข์์ ์์ ๋ฐ๋ผ ๊ณก ๋ชฉ๋ก ์๋ ์ ๋ ฌ
- ๊ณก ์์ธ ์ ๋ณด: ๊ณก๋ช , ์ํฐ์คํธ, ์จ๋ฒ๋ช , ์์ ์ฐ์ฃผ ์๊ฐ ํ์
- ๋ฐ์ํ ๋์์ธ: ๋ชจ๋ฐ์ผ์์๋ ํธํ๊ฒ ํฌํ ๊ฐ๋ฅ
- ๊ณต์ฐ ์ํ ๊ด๋ฆฌ: ์์ /์งํ์ค/์ข ๋ฃ ์ํ ๋ฐฐ์ง ํ์
๐ ๋ณด๋์ค ๊ธฐ๋ฅ
- ๊ณก๋ณ ๊ฐ๋จํ ๋๊ธ ๋จ๊ธฐ๊ธฐ ("์ด ๊ณก ๊ผญ ๋ค์ด์!" ๊ฐ์ ์์ ๋ฉ์์ง)
- ๊ณต์ฐ ํฌ์คํฐ ์ด๋ฏธ์ง ์ ๋ก๋
- ์ ๋ฆฌ์คํธ ๊ณต์ ํ๊ธฐ (๋งํฌ ๋ณต์ฌ)
- ์ข์์ Top 3 ๊ณก ํ์ด๋ผ์ดํธ ํจ๊ณผ
- ๋คํฌ๋ชจ๋ ์ง์
๐ฏ ํ์ต ๋ชฉํ
Laravel 13.x + Blade ํ ํ๋ฆฟ์ผ๋ก ๊ฐ๋จํ CRUD์ ์ค์๊ฐ ์ธํฐ๋์ ์ ๊ตฌํํ๋ฉฐ, Tailwind CSS๋ก ์์ ๊ณต์ฐ ๋ถ์๊ธฐ์ ์ธ๋ จ๋ UI๋ฅผ ๋ง๋ค์ด๋ด ๋๋ค. SQLite ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก ๊ณต์ฐ ์ ๋ณด์ ์ข์์ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๊ณ , Alpine.js๋ก ํด๋ผ์ด์ธํธ ์ธก ์ธํฐ๋์ ์ ์ถ๊ฐํฉ๋๋ค.
์ ์ฝ ์กฐ๊ฑด
- โข Laravel 13.x ์ฌ์ฉ (์ต์ ๋ฒ์ )
- โข Blade ํ ํ๋ฆฟ ์์ง์ผ๋ก ๋ทฐ ๊ตฌ์ฑ (React/Vue ์ฌ์ฉ ๊ธ์ง)
- โข Tailwind CSS๋ก ์คํ์ผ๋ง
- โข SQLite ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฌ์ฉ
- โข Alpine.js๋ก ๊ฐ๋จํ ์ธํฐ๋์ ๊ตฌํ (์ ํ์ )
- โข ๋ฐ์ํ ๋์์ธ ํ์ (๋ชจ๋ฐ์ผ ์ฐ์ )
- โข ์ข์์ ์ค๋ณต ๋ฐฉ์ง ๋ก์ง ๊ตฌํ (์ธ์ ๋๋ ์ฟ ํค ํ์ฉ)
- โข ์ต์ 3๊ฐ ์ด์์ Blade ์ปดํฌ๋ํธ ์ฌ์ฉ
ํ๋กฌํํธ ๋ ์ํผ
์๋ ํ๋กฌํํธ๋ฅผ ์์๋๋ก AI์๊ฒ ๋ณด๋ด๋ฉด ๋ฉ๋๋ค. ๋ณต์ฌ ๋ฒํผ์ ๋๋ฌ ๋ฐ๋ก ์ฌ์ฉํ์ธ์.
mkdir musician-setlist-sharing-page && cd musician-setlist-sharing-page ์ด ํด๋์์ ํ๋ก์ ํธ๋ฅผ ์์ํฉ๋๋ค. Laravel 13.x ํ๋ก์ ํธ๋ฅผ ์์ฑํ๊ณ ์ด๊ธฐ ์ค์ ์ ํด์ฃผ์ธ์: 1. Laravel ์ต์ ๋ฒ์ (13.x) ํ๋ก์ ํธ ์์ฑ 2. .env ํ์ผ์์ DB_CONNECTION์ sqlite๋ก ๋ณ๊ฒฝ 3. database/database.sqlite ํ์ผ ์์ฑ 4. Tailwind CSS ์ค์น ๋ฐ ์ค์ (Laravel Vite์ ํตํฉ) 5. php artisan migrate ์คํํ์ฌ ๊ธฐ๋ณธ ๋ง์ด๊ทธ๋ ์ด์ ์๋ฃ ์๋ฃ ํ php artisan serve๋ก ์๋ฒ๋ฅผ ์คํํ๊ณ http://localhost:8000 ์์ Laravel ๊ธฐ๋ณธ ํ๋ฉด์ด ๋ณด์ด๋์ง ํ์ธํด์ฃผ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: Laravel 13.x ํ๋ก์ ํธ๊ฐ ์์ฑ๋๊ณ , SQLite ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์ฐ๊ฒฐ๋๋ฉฐ, Tailwind CSS๊ฐ ์ค์ ๋ ์ํ. ๋ธ๋ผ์ฐ์ ์์ Laravel ์ฐ์ปด ํ์ด์ง๋ฅผ ํ์ธํ ์ ์์ด์ผ ํฉ๋๋ค.
์ ๋ฆฌ์คํธ์ ๊ณก ์ ๋ณด๋ฅผ ์ ์ฅํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ตฌ์กฐ๋ฅผ ๋ง๋ค์ด์ฃผ์ธ์: **๋ง์ด๊ทธ๋ ์ด์ 2๊ฐ ์์ฑ:** 1. `concerts` ํ ์ด๋ธ: - id (Primary Key) - title (๊ณต์ฐ ์ ๋ชฉ, string) - date (๊ณต์ฐ ๋ ์ง, date) - venue (๊ณต์ฐ ์ฅ์, string) - status (์ํ: upcoming/ongoing/ended, string, ๊ธฐ๋ณธ๊ฐ 'upcoming') - poster_image (ํฌ์คํฐ ์ด๋ฏธ์ง ๊ฒฝ๋ก, string, nullable) - created_at, updated_at 2. `songs` ํ ์ด๋ธ: - id (Primary Key) - concert_id (Foreign Key โ concerts.id, onDelete cascade) - title (๊ณก๋ช , string) - artist (์ํฐ์คํธ๋ช , string) - album (์จ๋ฒ๋ช , string, nullable) - duration (์์ ์ฐ์ฃผ ์๊ฐ(๋ถ), integer, nullable) - likes_count (์ข์์ ์, integer, ๊ธฐ๋ณธ๊ฐ 0) - order (๊ณก ์์, integer) - created_at, updated_at ๋ง์ด๊ทธ๋ ์ด์ ํ์ผ์ ์์ฑํ๊ณ php artisan migrate๋ฅผ ์คํํ์ฌ ํ ์ด๋ธ์ ์์ฑํด์ฃผ์ธ์. ์ถ๊ฐ๋ก Concert์ Song ๋ชจ๋ธ๋ ์์ฑํ๊ณ , Concert ๋ชจ๋ธ์ songs() ๊ด๊ณ(hasMany)๋ฅผ ์ค์ ํด์ฃผ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: concerts์ songs ํ ์ด๋ธ์ด SQLite ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์์ฑ๋๊ณ , Concert ๋ชจ๋ธ๊ณผ Song ๋ชจ๋ธ์ด ์์ฑ๋๋ฉฐ ๊ด๊ณ๊ฐ ์ค์ ๋ฉ๋๋ค. php artisan migrate ๋ช ๋ น์ด ์ฑ๊ณต์ ์ผ๋ก ์คํ๋์ด์ผ ํฉ๋๋ค.
์
๋ฆฌ์คํธ๋ฅผ ๋ณด์ฌ์ค ๋ฉ์ธ ํ์ด์ง๋ฅผ ๋ง๋ค์ด์ฃผ์ธ์:
**๋ผ์ฐํธ ์ค์ (routes/web.php):**
- GET `/` โ ์ ์ฒด ๊ณต์ฐ ๋ชฉ๋ก ํ์ด์ง
- GET `/concerts/{concert}` โ ํน์ ๊ณต์ฐ์ ์
๋ฆฌ์คํธ ์์ธ ํ์ด์ง
- POST `/songs/{song}/like` โ ๊ณก์ ์ข์์ ์ถ๊ฐ
**์ปจํธ๋กค๋ฌ ์์ฑ (ConcertController):**
- index() ๋ฉ์๋: ๋ชจ๋ ๊ณต์ฐ์ ๋ ์ง ์ญ์์ผ๋ก ๊ฐ์ ธ์ ๋ทฐ์ ์ ๋ฌ
- show($id) ๋ฉ์๋: ํน์ ๊ณต์ฐ๊ณผ ๊ด๋ จ ๊ณก๋ค์ order ์์๋๋ก ๊ฐ์ ธ์ ๋ทฐ์ ์ ๋ฌ
**๋ทฐ ํ์ผ ์์ฑ:**
1. `resources/views/layouts/app.blade.php` (๋ฉ์ธ ๋ ์ด์์):
- Tailwind CSS ํฌํจ
- ์์
ํ
๋ง์ ๊ทธ๋ผ๋์ธํธ ๋ฐฐ๊ฒฝ (๋ณด๋ผ-ํํฌ ๊ณ์ด)
- ํค๋์ "๐ธ Setlist Share" ๋ก๊ณ
- @yield('content') ์์ญ
2. `resources/views/concerts/index.blade.php`:
- ๊ณต์ฐ ๋ชฉ๋ก์ ์นด๋ ํํ๋ก ํ์
- ๊ฐ ์นด๋์ ๊ณต์ฐ ์ ๋ชฉ, ๋ ์ง, ์ฅ์, ์ํ ๋ฐฐ์ง ํ์
- ์นด๋ ํด๋ฆญ ์ ์์ธ ํ์ด์ง๋ก ์ด๋
- ๊ณต์ฐ์ด ์์ผ๋ฉด "์์ง ๋ฑ๋ก๋ ๊ณต์ฐ์ด ์์ต๋๋ค" ๋ฉ์์ง
3. `resources/views/concerts/show.blade.php`:
- ๊ณต์ฐ ์ ๋ณด ํค๋ (์ ๋ชฉ, ๋ ์ง, ์ฅ์, ์ํ)
- ์
๋ฆฌ์คํธ ํ
์ด๋ธ: ์์, ๊ณก๋ช
, ์ํฐ์คํธ, ์จ๋ฒ, ์ฐ์ฃผ์๊ฐ, ์ข์์ ์, ์ข์์ ๋ฒํผ
- ์ข์์ ์์ ๋ฐ๋ผ ๊ณก ๋ชฉ๋ก ์ ๋ ฌ (๋ด๋ฆผ์ฐจ์)
- ๋ฐ์ํ ๋์์ธ (๋ชจ๋ฐ์ผ์์๋ ํ
์ด๋ธ ๋์ ์นด๋ ๋ ์ด์์)
Tailwind CSS๋ก ์์
๊ณต์ฐ ๋ถ์๊ธฐ์ ์ธ๋ จ๋ ๋์์ธ์ ์ ์ฉํด์ฃผ์ธ์. ๋ฒํผ์ ํํธ ์ด๋ชจ์ง(โค๏ธ)๋ฅผ ์ฌ์ฉํ๊ณ , ์ข์์๋ฅผ ๋๋ฅธ ๊ณก์ ํํธ๊ฐ ์ฑ์์ง ์ํ(๐)๋ก ํ์ํด์ฃผ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: ๋ฉ์ธ ํ์ด์ง์์ ๊ณต์ฐ ๋ชฉ๋ก์ ๋ณผ ์ ์๊ณ , ๊ฐ ๊ณต์ฐ์ ํด๋ฆญํ๋ฉด ์ ๋ฆฌ์คํธ ์์ธ ํ์ด์ง๋ก ์ด๋ํฉ๋๋ค. ์ ๋ฆฌ์คํธ์๋ ๊ณก ์ ๋ณด์ ์ข์์ ๋ฒํผ์ด ํ์๋๋ฉฐ, Tailwind CSS๋ก ์คํ์ผ๋ง๋ ์์ ํ ๋ง์ UI๊ฐ ์์ฑ๋ฉ๋๋ค.
์ข์์ ๊ธฐ๋ฅ์ ๊ตฌํํด์ฃผ์ธ์:
**SongController ์์ฑ:**
- like($id) ๋ฉ์๋ ๊ตฌํ:
- ์ธ์
์ ์ฌ์ฉํด ์ด๋ฏธ ์ข์์๋ฅผ ๋๋ฅธ ๊ณก์ธ์ง ํ์ธ (์ธ์
ํค: 'liked_songs', ๋ฐฐ์ด๋ก ๊ณก ID ์ ์ฅ)
- ์ด๋ฏธ ์ข์์๋ฅผ ๋๋ ์ผ๋ฉด "์ด๋ฏธ ์ข์์๋ฅผ ๋๋ฅด์
จ์ต๋๋ค" ๋ฉ์์ง์ ํจ๊ป ๋ฆฌ๋ค์ด๋ ํธ
- ์ฒ์ ๋๋ฅด๋ ๊ฒฝ์ฐ Song ๋ชจ๋ธ์ likes_count๋ฅผ +1 ์ฆ๊ฐ์ํค๊ณ ์ธ์
์ ๊ณก ID ์ถ๊ฐ
- ์ฑ๊ณต ๋ฉ์์ง์ ํจ๊ป ์ด์ ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธ
**๋ผ์ฐํธ ์ถ๊ฐ (routes/web.php):**
- POST `/songs/{song}/like` โ SongController@like
**๋ทฐ ์์ (concerts/show.blade.php):**
- ์ข์์ ๋ฒํผ์ ํผ์ผ๋ก ๊ฐ์ธ๊ธฐ (CSRF ํ ํฐ ํฌํจ)
- ์ธ์
์์ liked_songs ๋ฐฐ์ด์ ํ์ธํ์ฌ ์ด๋ฏธ ์ข์์๋ฅผ ๋๋ฅธ ๊ณก์ ๐(์ฑ์์ง ํํธ), ์ ๋๋ฅธ ๊ณก์ โค๏ธ(๋น ํํธ) ํ์
- ์ข์์ ์๊ฐ ๋์ ๊ณก ์์ผ๋ก ์๋ ์ ๋ ฌ (์ปจํธ๋กค๋ฌ์์ orderBy('likes_count', 'desc') ์ ์ฉ)
- ์ฑ๊ณต/์ค๋ฅ ๋ฉ์์ง๋ฅผ Tailwind CSS ์๋ฆผ ๋ฐ์ค๋ก ํ์
**๋ณด๋์ค:**
- Alpine.js๋ฅผ ์ถ๊ฐํ์ฌ ์ข์์ ๋ฒํผ ํด๋ฆญ ์ ํ์ด์ง ์๋ก๊ณ ์นจ ์์ด ํํธ ์์ด์ฝ์ด ์ฆ์ ๋ณ๊ฒฝ๋๋๋ก ๊ตฌํ (์ ํ ์ฌํญ)
์ข์์ ๊ธฐ๋ฅ์ด ์ ์ ์๋ํ๋์ง ํ
์คํธํด์ฃผ์ธ์. ๊ฐ์ ๊ณก์ ์ฌ๋ฌ ๋ฒ ์ข์์๋ฅผ ๋๋ฅด๋ฉด ์ค๋ณต ๋ฐฉ์ง ๋ฉ์์ง๊ฐ ๋์์ผ ํฉ๋๋ค.
๊ธฐ๋ ๊ฒฐ๊ณผ: ์ข์์ ๋ฒํผ์ ํด๋ฆญํ๋ฉด ์ข์์ ์๊ฐ ์ฆ๊ฐํ๊ณ , ์ธ์ ์ ํตํด ์ค๋ณต ์ข์์๊ฐ ๋ฐฉ์ง๋ฉ๋๋ค. ์ด๋ฏธ ์ข์์๋ฅผ ๋๋ฅธ ๊ณก์ ์ฑ์์ง ํํธ(๐)๋ก ํ์๋๊ณ , ๊ณก ๋ชฉ๋ก์ ์ข์์ ์์ ๋ฐ๋ผ ์๋ ์ ๋ ฌ๋ฉ๋๋ค. ์ฑ๊ณต/์ค๋ฅ ๋ฉ์์ง๊ฐ Tailwind ์๋ฆผ ๋ฐ์ค๋ก ํ์๋ฉ๋๋ค.
์ํ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ๊ณ UI๋ฅผ ๊ฐ์ ํด์ฃผ์ธ์: **Seeder ์์ฑ (DatabaseSeeder.php):** 1. Concert 2๊ฐ ์์ฑ: - Concert 1: "Spring Live 2025", ๋ ์ง: 2025-05-15, ์ฅ์: "Olympic Hall", ์ํ: "upcoming" - Concert 2: "Summer Festival", ๋ ์ง: 2025-07-20, ์ฅ์: "Han River Park", ์ํ: "upcoming" 2. ๊ฐ Concert๋ง๋ค Song 5๊ฐ์ฉ ์์ฑ: - ๋ค์ํ ๊ณก๋ช , ์ํฐ์คํธ, ์จ๋ฒ, ์ฐ์ฃผ ์๊ฐ ์ค์ - order๋ 1~5๋ก ์์ฐจ ๋ถ์ฌ - likes_count๋ ๋๋ค์ผ๋ก 0~50 ์ฌ์ด ๊ฐ ํ ๋น php artisan db:seed๋ฅผ ์คํํ์ฌ ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ถ๊ฐํด์ฃผ์ธ์. **UI ๊ฐ์ :** 1. Blade ์ปดํฌ๋ํธ ์์ฑ: - `resources/views/components/concert-card.blade.php`: ๊ณต์ฐ ์นด๋ ์ปดํฌ๋ํธ (์ ๋ชฉ, ๋ ์ง, ์ฅ์, ์ํ ๋ฐฐ์ง) - `resources/views/components/song-row.blade.php`: ์ ๋ฆฌ์คํธ ๊ณก ํ ์ปดํฌ๋ํธ (์์, ๊ณก ์ ๋ณด, ์ข์์ ๋ฒํผ) - `resources/views/components/status-badge.blade.php`: ์ํ ๋ฐฐ์ง ์ปดํฌ๋ํธ (upcoming=ํ๋์, ongoing=์ด๋ก์, ended=ํ์) 2. ๋ทฐ ํ์ผ์์ ์ปดํฌ๋ํธ ์ฌ์ฉ: - `concerts/index.blade.php`์์ <x-concert-card> ์ฌ์ฉ - `concerts/show.blade.php`์์ <x-song-row>, <x-status-badge> ์ฌ์ฉ 3. ์ถ๊ฐ ์คํ์ผ๋ง: - ์ข์์ Top 3 ๊ณก์ ๊ธ/์/๋ ๋ฉ๋ฌ ์ด๋ชจ์ง(๐ฅ๐ฅ๐ฅ) ํ์ - ํธ๋ฒ ํจ๊ณผ ์ถ๊ฐ (์นด๋, ๋ฒํผ์ transition ์ ์ฉ) - ๊ณต์ฐ ์์ธ ํ์ด์ง ์๋จ์ ๊ณต์ฐ ํฌ์คํฐ ์ด๋ฏธ์ง ์์ญ ์ถ๊ฐ (์ด๋ฏธ์ง๊ฐ ์์ผ๋ฉด ๊ธฐ๋ณธ ๋ฎค์ง ์์ด์ฝ ํ์) ๋ชจ๋ ๊ธฐ๋ฅ์ด ์ ์ ์๋ํ๋์ง ํ ์คํธํ๊ณ , ๋ชจ๋ฐ์ผ ํ๋ฉด์์๋ UI๊ฐ ๊น๋ํ๊ฒ ๋ณด์ด๋์ง ํ์ธํด์ฃผ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: ์ํ ๋ฐ์ดํฐ๊ฐ ์ถ๊ฐ๋์ด ๋ฉ์ธ ํ์ด์ง์ 2๊ฐ์ ๊ณต์ฐ์ด ํ์๋๊ณ , ๊ฐ ๊ณต์ฐ๋ง๋ค 5๊ฐ์ ๊ณก์ด ์ ๋ฆฌ์คํธ์ ๋ํ๋ฉ๋๋ค. Blade ์ปดํฌ๋ํธ๋ฅผ ํ์ฉํ์ฌ ์ฝ๋๊ฐ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ๊ณ ๊น๋ํ๊ฒ ์ ๋ฆฌ๋๋ฉฐ, ์ข์์ Top 3 ๊ณก์ ๋ฉ๋ฌ ์ด๋ชจ์ง๊ฐ ํ์๋๊ณ , ํธ๋ฒ ํจ๊ณผ ๋ฑ ์ธ๋ จ๋ UI๊ฐ ์์ฑ๋ฉ๋๋ค. ๋ชจ๋ฐ์ผ์์๋ ๋ฐ์ํ ๋์์ธ์ด ์ ์๋ํด์ผ ํฉ๋๋ค.
์ ์ถ๋ ์ํ (0)
๋ก๊ทธ์ธ ํ ์ ์ถ์์ง ์ ์ถ๋ ์ํ์ด ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋์ ์๊ฐ ๋์ด๋ณด์ธ์!