๐ฐ ๋ฒ ์ด์ปค๋ฆฌ ์ ์ํ ์ฌ์ ์์ฝ ํ๋ซํผ - ํ์ ์๋ ์์ฝ & ํฝ์ ์๊ฐ ์ ํ
bakery-preorder-pickup-platform
๋ฌธ์ ์ค๋ช
ํ๋ก์ ํธ ๊ฐ์
๋ฒ ์ด์ปค๋ฆฌ๋ ๋์ ํธ์ต์์ ์ ์ํ ์ถ์ ์ ์ฌ์ ์์ฝ์ ๋ฐ๊ณ , ๊ณ ๊ฐ์ด ํฝ์ ์๊ฐ์ ์ ํํ ์ ์๋ ํ๋ซํผ์ ๋ง๋ญ๋๋ค. ํ์ ์๋ ์ ํ์ ์ฌ๊ณ ๋ฅผ ์ค์๊ฐ์ผ๋ก ๋ณด์ฌ์ฃผ๊ณ , ์์ฝ์ด ์๋ฃ๋๋ฉด ํ์ธ ํ์ด์ง๋ฅผ ์ ๊ณตํฉ๋๋ค.
ํต์ฌ ๊ธฐ๋ฅ
- ์ ์ํ ๋ชฉ๋ก ํ์ด์ง: ์ฌ์ง, ์ด๋ฆ, ๊ฐ๊ฒฉ, ๋จ์ ์๋ ํ์
- ์ํ ์์ธ ํ์ด์ง: ์์ธ ์ค๋ช , ์ฌ๋ฃ ์ ๋ณด, ์๋ฌ์ง ์๋ด
- ์์ฝ ํผ: ๊ณ ๊ฐ ์ ๋ณด(์ด๋ฆ, ์ฐ๋ฝ์ฒ), ์๋ ์ ํ, ํฝ์ ์๊ฐ๋ ์ ํ
- ์ฌ๊ณ ๊ด๋ฆฌ: ์์ฝ ์ ์๋์ผ๋ก ์ฌ๊ณ ์ฐจ๊ฐ (ํ์ ์ ์์ฝ ๋ถ๊ฐ)
- ์์ฝ ํ์ธ ํ์ด์ง: ์์ฝ ๋ฒํธ, ํฝ์ ์ ๋ณด, ์ฃผ์์ฌํญ ์๋ด
- ์์ฝ ๋ด์ญ ์กฐํ: ์์ฝ ๋ฒํธ๋ก ๋ด ์์ฝ ํ์ธํ๊ธฐ
์ ํ ์ถ๊ฐ ๊ธฐ๋ฅ (๋ณด๋์ค)
- ํฝ์ ์๊ฐ๋๋ณ ์์ฝ ์ธ์ ์ ํ (์๊ฐ๋๋น ์ต๋ X๋ช )
- ์์ฝ ์ทจ์ ๊ธฐ๋ฅ (ํฝ์ 24์๊ฐ ์ ๊น์ง)
- ์ด๋ฉ์ผ/SMS ์๋ฆผ (์์ฝ ํ์ธ & ํฝ์ 1์๊ฐ ์ ๋ฆฌ๋ง์ธ๋)
- ์ํ๋ณ ์ข์์/์ฐํ๊ธฐ ๊ธฐ๋ฅ
- ๊ด๋ฆฌ์ ํ์ด์ง: ์ค๋์ ์์ฝ ํํฉ & ํฝ์ ์๋ฃ ์ฒดํฌ
๊ธฐ์ ์คํ
- Laravel 13.x
- Blade ํ ํ๋ฆฟ ์์ง
- Tailwind CSS
- SQLite ๋ฐ์ดํฐ๋ฒ ์ด์ค
- Alpine.js (๊ฐ๋จํ ์ธํฐ๋์ )
ํ์ต ๋ชฉํ
- Laravel ๋ผ์ฐํ ๊ณผ ์ปจํธ๋กค๋ฌ ๊ธฐ๋ณธ ๊ตฌ์กฐ ์ดํด
- Blade ์ปดํฌ๋ํธ์ ๋ ์ด์์ ํ์ฉ
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ง์ด๊ทธ๋ ์ด์ ๊ณผ Eloquent ๋ชจ๋ธ CRUD
- ํผ ์ ํจ์ฑ ๊ฒ์ฌ์ ์๋ฌ ํธ๋ค๋ง
- ์กฐ๊ฑด๋ถ ๋ ๋๋ง (์ฌ๊ณ ์์/ํ์ )
- ์ธ์ ๊ณผ ๋ฆฌ๋ค์ด๋ ํธ ํ์ฉ
์ ์ฝ ์กฐ๊ฑด
- โข Laravel 13.x ์ต์ ๋ฒ์ ์ฌ์ฉ (๊ตฌ๋ฒ์ ๊ธ์ง)
- โข Blade ํ ํ๋ฆฟ ์์ง ์ฌ์ฉ (React, Vue ๋ฑ SPA ํ๋ ์์ํฌ ์ฌ์ฉ ๊ธ์ง)
- โข Tailwind CSS๋ก UI ๊ตฌ์ฑ
- โข SQLite ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฌ์ฉ (๊ฐ๋จํ ๋ก์ปฌ DB)
- โข Alpine.js๋ก ๊ฐ๋จํ ํด๋ผ์ด์ธํธ ์ธํฐ๋์ ๊ตฌํ (๋๋กญ๋ค์ด, ๋ชจ๋ฌ ๋ฑ)
- โข ๋ฐ์ํ ๋์์ธ (๋ชจ๋ฐ์ผ ์นํ์ )
- โข ๋ช ํํ ์๋ฌ ๋ฉ์์ง์ ์ฌ์ฉ์ ํผ๋๋ฐฑ ์ ๊ณต
- โข ์ฝ๋ ์ฃผ์์ผ๋ก ์ด๋ณด์๋ ์ดํด ๊ฐ๋ฅํ๋๋ก ์์ฑ
ํ๋กฌํํธ ๋ ์ํผ
์๋ ํ๋กฌํํธ๋ฅผ ์์๋๋ก AI์๊ฒ ๋ณด๋ด๋ฉด ๋ฉ๋๋ค. ๋ณต์ฌ ๋ฒํผ์ ๋๋ฌ ๋ฐ๋ก ์ฌ์ฉํ์ธ์.
mkdir bakery-preorder-pickup-platform && cd bakery-preorder-pickup-platform
์ด ํด๋์์ Laravel 13.x ํ๋ก์ ํธ๋ฅผ ์์ํฉ๋๋ค.
Laravel 13.x ํ๋ก์ ํธ๋ฅผ ์์ฑํ๊ณ ๊ธฐ๋ณธ ์ค์ ์ ์๋ฃํด์ฃผ์ธ์.
1. Laravel 13.x ์ค์น:
composer create-project laravel/laravel . "13.*"
2. .env ํ์ผ ์ค์ :
- DB_CONNECTION=sqlite
- DB_DATABASE=/absolute/path/to/database.sqlite (์ ๋ ๊ฒฝ๋ก๋ก)
- APP_NAME="๋ฒ ์ด์ปค๋ฆฌ ์ฌ์ ์์ฝ"
3. database/database.sqlite ํ์ผ ์์ฑ:
touch database/database.sqlite
4. Tailwind CSS ์ค์น:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
5. tailwind.config.js ์ค์ :
content: [
"./resources/**/*.blade.php",
"./resources/**/*.js",
]
6. resources/css/app.css์ Tailwind ๋๋ ํฐ๋ธ ์ถ๊ฐ:
@tailwind base;
@tailwind components;
@tailwind utilities;
7. Alpine.js CDN์ resources/views/layouts/app.blade.php์ ์ถ๊ฐ:
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
8. npm run dev ์คํ ํ Laravel ์๋ฒ ์์:
php artisan serve
๋ชจ๋ ์ค์ ์ด ์๋ฃ๋๋ฉด http://localhost:8000 ์ ์ ์ Laravel ๊ธฐ๋ณธ ํ๋ฉด์ด ๋์์ผ ํฉ๋๋ค.
๊ธฐ๋ ๊ฒฐ๊ณผ: Laravel 13.x ํ๋ก์ ํธ๊ฐ ์์ฑ๋๊ณ , Tailwind CSS์ Alpine.js๊ฐ ์ค์ ๋๋ฉฐ, SQLite ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์ฐ๊ฒฐ๋ฉ๋๋ค. ๊ธฐ๋ณธ ํ๋ฉด์ด ์ ์์ ์ผ๋ก ํ์๋ฉ๋๋ค.
๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ง์ด๊ทธ๋ ์ด์ ๊ณผ ๋ชจ๋ธ์ ์์ฑํฉ๋๋ค. 1. Product ๋ชจ๋ธ๊ณผ ๋ง์ด๊ทธ๋ ์ด์ ์์ฑ: php artisan make:model Product -m 2. database/migrations/xxxx_create_products_table.php ์์ : - name (string): ์ํ๋ช - description (text): ์์ธ ์ค๋ช - price (decimal, 8, 2): ๊ฐ๊ฒฉ - stock (integer): ์ฌ๊ณ ์๋ - image_url (string, nullable): ์ํ ์ด๋ฏธ์ง URL - pickup_start_time (time): ํฝ์ ์์ ์๊ฐ - pickup_end_time (time): ํฝ์ ์ข ๋ฃ ์๊ฐ - timestamps 3. Reservation ๋ชจ๋ธ๊ณผ ๋ง์ด๊ทธ๋ ์ด์ ์์ฑ: php artisan make:model Reservation -m 4. database/migrations/xxxx_create_reservations_table.php ์์ : - product_id (foreignId): ์ํ ID (products ํ ์ด๋ธ ์ฐธ์กฐ) - customer_name (string): ๊ณ ๊ฐ ์ด๋ฆ - customer_phone (string): ์ฐ๋ฝ์ฒ - quantity (integer): ์์ฝ ์๋ - pickup_time (datetime): ํฝ์ ์๊ฐ - reservation_number (string, unique): ์์ฝ ๋ฒํธ (์๋ ์์ฑ) - status (enum: pending, completed, cancelled): ์์ฝ ์ํ - timestamps 5. ๋ง์ด๊ทธ๋ ์ด์ ์คํ: php artisan migrate 6. app/Models/Product.php์ ๊ด๊ณ ์ค์ : - reservations() hasMany ๊ด๊ณ - $fillable ์ค์ 7. app/Models/Reservation.php์ ๊ด๊ณ ์ค์ : - product() belongsTo ๊ด๊ณ - $fillable ์ค์ - ์์ฝ ๋ฒํธ ์๋ ์์ฑ ๋ก์ง (boot ๋ฉ์๋์์ creating ์ด๋ฒคํธ ํ์ฉ) ๋ชจ๋ธ ๊ด๊ณ๊ฐ ๋ช ํํ ์ค์ ๋์ด์ผ ํฉ๋๋ค.
๊ธฐ๋ ๊ฒฐ๊ณผ: products์ reservations ํ ์ด๋ธ์ด ์์ฑ๋๊ณ , Eloquent ๋ชจ๋ธ ๊ฐ ๊ด๊ณ๊ฐ ์ค์ ๋ฉ๋๋ค. ์์ฝ ๋ฒํธ๊ฐ ์๋์ผ๋ก ์์ฑ๋๋ ๋ก์ง์ด ํฌํจ๋ฉ๋๋ค.
๋๋ฏธ ๋ฐ์ดํฐ(์๋)๋ฅผ ์์ฑํ๊ณ ๋ฉ์ธ ๋ ์ด์์์ ๊ตฌ์ฑํฉ๋๋ค.
1. ProductSeeder ์์ฑ:
php artisan make:seeder ProductSeeder
2. database/seeders/ProductSeeder.php ์์ฑ:
- 5~7๊ฐ์ ๋ฒ ์ด์ปค๋ฆฌ ์ํ ์์ฑ (์: ํฌ๋ฃจ์์, ๋ฐ๊ฒํธ, ์ด์ฝ ๋จธํ, ์น์ฆ ์ผ์ดํฌ, ๋ง์นด๋กฑ ๋ฑ)
- ๊ฐ ์ํ์ ์ด๋ฆ, ์ค๋ช
, ๊ฐ๊ฒฉ(3000~15000์), ์ฌ๊ณ (5~20๊ฐ), ํฝ์
์๊ฐ(09:00~18:00) ์ค์
- ์ด๋ฏธ์ง๋ Unsplash API๋ placeholder ์ด๋ฏธ์ง ์ฌ์ฉ (https://via.placeholder.com/400x300)
3. database/seeders/DatabaseSeeder.php์์ ProductSeeder ํธ์ถ
4. ์๋ ์คํ:
php artisan db:seed
5. resources/views/layouts/app.blade.php ์์ฑ:
- HTML5 ๊ธฐ๋ณธ ๊ตฌ์กฐ
- Tailwind CSS ๋งํฌ (@vite(['resources/css/app.css', 'resources/js/app.js']))
- Alpine.js CDN
- ํค๋: ๋ก๊ณ , ๋ค๋น๊ฒ์ด์
(ํ, ์ํ ๋ชฉ๋ก, ์์ฝ ์กฐํ)
- ํธํฐ: ์นดํผ๋ผ์ดํธ, SNS ๋งํฌ
- @yield('content') ์์ญ
๋ ์ด์์์ด ๊น๋ํ๊ณ ๋ชจ๋ฐ์ผ์์๋ ์ ๋ณด์ฌ์ผ ํฉ๋๋ค.
๊ธฐ๋ ๊ฒฐ๊ณผ: ๋๋ฏธ ์ํ ๋ฐ์ดํฐ๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฝ์ ๋๊ณ , Blade ๋ ์ด์์ ํ์ผ์ด ์์ฑ๋์ด ๋ชจ๋ ํ์ด์ง์์ ๊ณตํต์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ํ ๋ชฉ๋ก ํ์ด์ง์ ์์ธ ํ์ด์ง๋ฅผ ๊ตฌํํฉ๋๋ค.
1. ProductController ์์ฑ:
php artisan make:controller ProductController
2. app/Http/Controllers/ProductController.php ์์ฑ:
- index() ๋ฉ์๋: ๋ชจ๋ ์ํ ์กฐํ (์ฌ๊ณ ์๋ ๊ฒ ์ฐ์ ์ ๋ ฌ)
- show($id) ๋ฉ์๋: ํน์ ์ํ ์์ธ ์กฐํ
3. routes/web.php ์ค์ :
- GET / โ ProductController@index (์ํ ๋ชฉ๋ก)
- GET /products/{id} โ ProductController@show (์ํ ์์ธ)
4. resources/views/products/index.blade.php ์์ฑ:
- @extends('layouts.app')
- ์ํ ์นด๋ ๊ทธ๋ฆฌ๋ ๋ ์ด์์ (Tailwind grid)
- ๊ฐ ์นด๋: ์ด๋ฏธ์ง, ์ํ๋ช
, ๊ฐ๊ฒฉ, ๋จ์ ์ฌ๊ณ ํ์
- ์ฌ๊ณ 0์ธ ๊ฒฝ์ฐ "ํ์ " ๋ฐฐ์ง ํ์
- ์ํ ํด๋ฆญ ์ ์์ธ ํ์ด์ง๋ก ์ด๋
5. resources/views/products/show.blade.php ์์ฑ:
- ์ํ ์ด๋ฏธ์ง (ํฌ๊ฒ)
- ์ํ๋ช
, ๊ฐ๊ฒฉ, ์์ธ ์ค๋ช
- ์ฌ๊ณ ์ํ ํ์ ("๋จ์ ์๋: X๊ฐ" ๋๋ "ํ์ ")
- ํฝ์
๊ฐ๋ฅ ์๊ฐ ์๋ด
- "์์ฝํ๊ธฐ" ๋ฒํผ (์ฌ๊ณ ์์ ๋๋ง ํ์ฑํ)
- ๋ค๋ก๊ฐ๊ธฐ ๋ฒํผ
Tailwind๋ก ๊น๋ํ ์นด๋ ๋์์ธ๊ณผ ๋ฐ์ํ ๋ ์ด์์์ ๊ตฌํํ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: ์ํ ๋ชฉ๋ก ํ์ด์ง์์ ๋ชจ๋ ์ํ์ ์นด๋ ํํ๋ก ๋ณผ ์ ์๊ณ , ํด๋ฆญ ์ ์์ธ ํ์ด์ง๋ก ์ด๋ํฉ๋๋ค. ์ฌ๊ณ ์ํ๊ฐ ์๊ฐ์ ์ผ๋ก ๋ช ํํ ํ์๋ฉ๋๋ค.
์์ฝ ํผ๊ณผ ์์ฝ ์ฒ๋ฆฌ ๋ก์ง์ ๊ตฌํํฉ๋๋ค.
1. ReservationController ์์ฑ:
php artisan make:controller ReservationController
2. app/Http/Controllers/ReservationController.php ์์ฑ:
- create($product_id) ๋ฉ์๋: ์์ฝ ํผ ํ์
- store(Request $request) ๋ฉ์๋:
- ํผ ์ ํจ์ฑ ๊ฒ์ฌ (์ด๋ฆ, ์ฐ๋ฝ์ฒ, ์๋, ํฝ์
์๊ฐ)
- ์ฌ๊ณ ํ์ธ (์์ฒญ ์๋ โค ํ์ฌ ์ฌ๊ณ )
- ์ฌ๊ณ ์ฐจ๊ฐ ๋ฐ ์์ฝ ์์ฑ
- ์์ฝ ๋ฒํธ ์๋ ์์ฑ
- ์์ฝ ํ์ธ ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธ
- show($reservation_number) ๋ฉ์๋: ์์ฝ ์์ธ ์กฐํ
3. routes/web.php ์ถ๊ฐ:
- GET /reservations/create/{product_id} โ ReservationController@create
- POST /reservations โ ReservationController@store
- GET /reservations/{reservation_number} โ ReservationController@show
4. resources/views/reservations/create.blade.php ์์ฑ:
- ์ํ ์ ๋ณด ์์ฝ (์ด๋ฆ, ๊ฐ๊ฒฉ)
- ์์ฝ ํผ:
- ์ด๋ฆ (required)
- ์ฐ๋ฝ์ฒ (required, 010-1234-5678 ํ์)
- ์๋ (required, min 1, max ์ฌ๊ณ ์๋)
- ํฝ์
์๊ฐ (required, datetime-local input, ํฝ์
๊ฐ๋ฅ ์๊ฐ ๋ฒ์ ๋ด)
- ์ด ๊ธ์ก ๊ณ์ฐ (์๋ ร ๊ฐ๊ฒฉ, Alpine.js๋ก ์ค์๊ฐ ๊ณ์ฐ)
- ์ ์ถ ๋ฒํผ
- ์๋ฌ ๋ฉ์์ง ํ์ ์์ญ (@error ๋ธ๋ ์ด๋ ๋๋ ํฐ๋ธ)
5. resources/views/reservations/show.blade.php ์์ฑ:
- ์์ฝ ์๋ฃ ๋ฉ์์ง
- ์์ฝ ๋ฒํธ (ํฌ๊ฒ ๊ฐ์กฐ)
- ์ํ๋ช
, ์๋, ์ด ๊ธ์ก
- ํฝ์
์๊ฐ, ๊ณ ๊ฐ ์ ๋ณด
- ์ฃผ์์ฌํญ (์: ํฝ์
์๊ฐ ์์, ์์ฝ ๋ฒํธ ์ง์ฐธ)
- ํ์ผ๋ก ๋์๊ฐ๊ธฐ ๋ฒํผ
ํผ ์ ํจ์ฑ ๊ฒ์ฌ์ ์๋ฌ ์ฒ๋ฆฌ๊ฐ ๋ช
ํํด์ผ ํฉ๋๋ค.
๊ธฐ๋ ๊ฒฐ๊ณผ: ์์ฝ ํผ์์ ๊ณ ๊ฐ ์ ๋ณด๋ฅผ ์ ๋ ฅํ๊ณ ์ ์ถํ๋ฉด, ์ฌ๊ณ ๊ฐ ์ฐจ๊ฐ๋๊ณ ์์ฝ์ด ์์ฑ๋ฉ๋๋ค. ์์ฝ ํ์ธ ํ์ด์ง์์ ์์ฝ ๋ฒํธ์ ์์ธ ์ ๋ณด๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
์์ฝ ์กฐํ ๊ธฐ๋ฅ๊ณผ ์ต์ข UI ๊ฐ์ ์ ์๋ฃํฉ๋๋ค. 1. resources/views/reservations/search.blade.php ์์ฑ: - ์์ฝ ๋ฒํธ ์ ๋ ฅ ํผ - ์กฐํ ๋ฒํผ - Alpine.js๋ก ๊ฐ๋จํ ๋ก๋ฉ ์ํ ํ์ 2. ReservationController์ search() ๋ฉ์๋ ์ถ๊ฐ: - GET /reservations/search โ ๊ฒ์ ํผ ํ์ - POST /reservations/search โ ์์ฝ ๋ฒํธ๋ก ์กฐํ ํ show ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธ - ์์ฝ์ด ์์ผ๋ฉด ์๋ฌ ๋ฉ์์ง 3. routes/web.php ์ถ๊ฐ: - GET /reservations/search - POST /reservations/search 4. ๋ค๋น๊ฒ์ด์ ์ "์์ฝ ์กฐํ" ๋งํฌ ์ถ๊ฐ (layouts/app.blade.php) 5. UI ๊ฐ์ : - ์ฌ๊ณ ๋ถ์กฑ ์ "ํ์ " ๋ฐฐ์ง๋ฅผ ๋นจ๊ฐ์์ผ๋ก ๊ฐ์กฐ - ์์ฝ ๊ฐ๋ฅํ ์ํ์ "์์ฝ ๊ฐ๋ฅ" ์ด๋ก์ ๋ฐฐ์ง - ํฝ์ ์๊ฐ ์ ํ ์ ์๊ฐ๋๋ณ ์๋ด ๋ฌธ๊ตฌ (์: "์ค์ ํฝ์ ์ 09:00~12:00") - Alpine.js๋ก ์ด ๊ธ์ก ์๋ ๊ณ์ฐ (์๋ ๋ณ๊ฒฝ ์) - ๋ฐ์ํ ๋์์ธ ์ ๊ฒ (๋ชจ๋ฐ์ผ, ํ๋ธ๋ฆฟ, ๋ฐ์คํฌํฑ) 6. ํ ์คํธ: - ์ํ ๋ชฉ๋ก โ ์์ธ ํ์ด์ง โ ์์ฝ โ ์์ฝ ํ์ธ ์ ์ฒด ํ๋ก์ฐ ํ์ธ - ์ฌ๊ณ 0์ธ ์ํ ์์ฝ ์๋ โ ์๋ฌ ์ฒ๋ฆฌ ํ์ธ - ์์ฝ ์กฐํ ๊ธฐ๋ฅ ์ ์ ์๋ ํ์ธ ๋ชจ๋ ๊ธฐ๋ฅ์ด ์ ์ ์๋ํ๊ณ UI๊ฐ ์ง๊ด์ ์ด์ด์ผ ํฉ๋๋ค.
๊ธฐ๋ ๊ฒฐ๊ณผ: ์์ฝ ๋ฒํธ๋ก ์์ฝ ๋ด์ญ์ ์กฐํํ ์ ์๊ณ , ์ ์ฒด ์ฌ์ฉ์ ํ๋ก์ฐ๊ฐ ๋งค๋๋ฝ๊ฒ ์๋ํฉ๋๋ค. UI๊ฐ ๊น๋ํ๊ณ ๋ฐ์ํ์ด๋ฉฐ, ์๋ฌ ์ฒ๋ฆฌ๊ฐ ๋ช ํํฉ๋๋ค.
์ ์ถ๋ ์ํ (0)
๋ก๊ทธ์ธ ํ ์ ์ถ์์ง ์ ์ถ๋ ์ํ์ด ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋์ ์๊ฐ ๋์ด๋ณด์ธ์!