๐จ DIY ๊ตฟ์ฆ ์ ์ ์ฃผ๋ฌธ ํ๋ซํผ - ์ปค์คํ ๋์์ธ ์ ๋ก๋ & ๊ฒฌ์ ์๋ ์ฐ์ถ
custom-merch-order-platform
๋ฌธ์ ์ค๋ช
๐ฏ ํ๋ก์ ํธ ๊ฐ์
๊ฐ์ธ ํฌ๋ฆฌ์์ดํฐ๋ ์๊ท๋ชจ ๋ธ๋๋๊ฐ ๊ตฟ์ฆ(ํฐ์ ์ธ , ์คํฐ์ปค, ์์ฝ๋ฐฑ ๋ฑ)๋ฅผ ์ ์ ์๋ขฐํ ์ ์๋ ์ฃผ๋ฌธ ํ๋ซํผ์ ๋๋ค. ๊ณ ๊ฐ์ด ์ง์ ๋์์ธ ํ์ผ์ ์ ๋ก๋ํ๊ณ , ์ํ ์ข ๋ฅ์ ์๋์ ์ ํํ๋ฉด ์ค์๊ฐ์ผ๋ก ๊ฒฌ์ ์ด ๊ณ์ฐ๋ฉ๋๋ค. Laravel + Inertia.js + React ๋ชจ๋ ธ๋ฆฌ์ค ํ์คํ์ผ๋ก ๊ตฌํํ์ฌ ๋ณ๋ API ์์ด ํตํฉ๋ ๊ฒฝํ์ ์ ๊ณตํฉ๋๋ค.
โจ ํต์ฌ ๊ธฐ๋ฅ
ํ์ ๊ธฐ๋ฅ
- ์ํ ์นดํ๋ก๊ทธ: ํฐ์ ์ธ , ํ๋, ์คํฐ์ปค, ์์ฝ๋ฐฑ ๋ฑ ๊ตฟ์ฆ ์ข ๋ฅ๋ณ ์ํ ๋ชฉ๋ก (DB ๊ด๋ฆฌ)
- ์ฃผ๋ฌธ์ ์์ฑ ํผ: ์ํ ์ ํ, ์๋ ์ ๋ ฅ, ๋์์ธ ํ์ผ ์ ๋ก๋ (์ด๋ฏธ์ง ํ์ผ ์ ์ฅ)
- ์ค์๊ฐ ๊ฒฌ์ ๊ณ์ฐ: ์๋๋ณ ๋จ๊ฐ ์๋ ๊ณ์ฐ (JavaScript ๋๋ Laravel ์ปจํธ๋กค๋ฌ)
- ์ฃผ๋ฌธ ๋ชฉ๋ก ์กฐํ: ๋ด๊ฐ ์ ์ฒญํ ์ฃผ๋ฌธ ํ์คํ ๋ฆฌ (Inertia ํ์ด์ง๋ค์ด์ )
- ์ฃผ๋ฌธ ์ํ ๊ด๋ฆฌ: ๋๊ธฐ์ค/๊ฒํ ์ค/์ ์์ค/์๋ฃ ์ํ ํ์ (Enum ๋๋ status ์ปฌ๋ผ)
- ๋ฐ์ํ UI: ๋ชจ๋ฐ์ผ/๋ฐ์คํฌํฑ ์ต์ ํ (Tailwind CSS)
๋ณด๋์ค ๊ธฐ๋ฅ
- ๋์์ธ ํ์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ (์ด๋ฏธ์ง ์ธ๋ค์ผ ์์ฑ)
- ์ฃผ๋ฌธ ํํฐ๋ง/๊ฒ์ (์ํ ์ข ๋ฅ, ์ํ๋ณ)
- ๊ด๋ฆฌ์ ์ฃผ๋ฌธ ์น์ธ/๊ฑฐ๋ถ ๊ธฐ๋ฅ (Policy/Gate ํ์ฉ)
- ์ด๋ฉ์ผ ์๋ฆผ (์ฃผ๋ฌธ ์ ์ ํ์ธ, ์ํ ๋ณ๊ฒฝ ์ Laravel Mail)
- ๊ฒฌ์ ์ PDF ๋ค์ด๋ก๋ (Laravel Snappy ๋๋ DomPDF)
๐ ๊ธฐ์ ์คํ
- ๋ฐฑ์๋: Laravel 13.x (Eloquent ORM, ํ์ผ ์คํ ๋ฆฌ์ง)
- ํ๋ก ํธ์๋: React 19 + Inertia.js (๋ชจ๋ ธ๋ฆฌ์ค SPA)
- ์คํ์ผ๋ง: Tailwind CSS
- DB: SQLite (๊ฐ๋ฐ์ฉ) ๋๋ MySQL
- ํ์ผ ์ ๋ก๋: Laravel Storage (local/public disk)
๐ ์ ์ฝ ์กฐ๊ฑด
- Inertia.js ๊ธฐ๋ฐ ๋ชจ๋ ธ๋ฆฌ์ค ๊ตฌ์กฐ (๋ณ๋ REST API ์์)
- ํ์ผ ์ ๋ก๋๋ ์ด๋ฏธ์ง๋ง ํ์ฉ (jpg, png, pdf, ์ต๋ 5MB)
- ์ฃผ๋ฌธ์ ์ ๋ณด๋ ๊ฐ๋จํ ์ด๋ฆ/์ด๋ฉ์ผ/์ฐ๋ฝ์ฒ๋ง ์์ง
- ๊ฒฐ์ ์ฐ๋ ์์ (๊ฒฌ์ ํ์ธ ํ ๋ณ๋ ์ฐ๋ฝ)
- ์ค์๊ฐ ์ฑํ ์์ (์ฃผ๋ฌธ ๋ฌธ์๋ ๋ฉ๋ชจ ํ๋๋ก ๋์ฒด)
์ ์ฝ ์กฐ๊ฑด
- โข Laravel 13.x + Inertia.js + React 19 ๋ชจ๋ ธ๋ฆฌ์ค ํ์คํ ๊ตฌ์กฐ
- โข npx create laravel ๋๋ Laravel Breeze (React + Inertia) starter kit ์ฌ์ฉ
- โข Tailwind CSS๋ก ์คํ์ผ๋ง
- โข SQLite ๋๋ MySQL ๋ฐ์ดํฐ๋ฒ ์ด์ค
- โข ํ์ผ ์ ๋ก๋๋ Laravel Storage ํ์ฉ (์ด๋ฏธ์ง ํ์ผ๋ง, ์ต๋ 5MB)
- โข Inertia::render()๋ก Laravel ์ปจํธ๋กค๋ฌ์์ React ์ปดํฌ๋ํธ์ ๋ฐ์ดํฐ ์ ๋ฌ
- โข ์ค์๊ฐ ์ฑํ ๊ธฐ๋ฅ ์์ (HTTP ๊ธฐ๋ฐ ์ฃผ๋ฌธ ๋ฉ๋ชจ๋ก ๋์ฒด)
- โข ๊ฒฐ์ ์ฐ๋ ์์ (๊ฒฌ์ ํ์ธ ํ ๋ณ๋ ํ์)
ํ๋กฌํํธ ๋ ์ํผ
์๋ ํ๋กฌํํธ๋ฅผ ์์๋๋ก AI์๊ฒ ๋ณด๋ด๋ฉด ๋ฉ๋๋ค. ๋ณต์ฌ ๋ฒํผ์ ๋๋ฌ ๋ฐ๋ก ์ฌ์ฉํ์ธ์.
mkdir custom-merch-order-platform && cd custom-merch-order-platform ์ด ํด๋์์ Laravel 13.x + Inertia.js + React 19 ํ๋ก์ ํธ๋ฅผ ์์ฑํฉ๋๋ค. ๋ค์ ๋ช ๋ น์ด๋ก Laravel Breeze (React + Inertia) starter kit์ ์ค์นํ์ธ์: ```bash composer create-project laravel/laravel . composer require laravel/breeze --dev php artisan breeze:install react npm install npm run dev ``` ๊ทธ๋ฆฌ๊ณ ๋ค์ ๋ง์ด๊ทธ๋ ์ด์ ํ์ผ๋ค์ ์์ฑํ์ธ์: 1. **products ํ ์ด๋ธ** (๊ตฟ์ฆ ์ํ ์นดํ๋ก๊ทธ): - id (primary key) - name (string, ์ํ๋ช : ํฐ์ ์ธ , ํ๋, ์คํฐ์ปค ๋ฑ) - category (string, ์นดํ ๊ณ ๋ฆฌ: ์๋ฅ/๋ฌธ๊ตฌ/๊ธฐํ) - base_price (decimal, ๊ธฐ๋ณธ ๋จ๊ฐ) - description (text, nullable, ์ํ ์ค๋ช ) - image_url (string, nullable, ์ํ ์ด๋ฏธ์ง URL) - timestamps 2. **orders ํ ์ด๋ธ** (์ฃผ๋ฌธ ์ ๋ณด): - id (primary key) - product_id (foreign key to products) - customer_name (string, ์ฃผ๋ฌธ์ ์ด๋ฆ) - customer_email (string, ์ด๋ฉ์ผ) - customer_phone (string, ์ฐ๋ฝ์ฒ) - quantity (integer, ์ฃผ๋ฌธ ์๋) - design_file_path (string, ์ ๋ก๋๋ ๋์์ธ ํ์ผ ๊ฒฝ๋ก) - total_price (decimal, ์ด ๊ฒฌ์ ๊ธ์ก) - notes (text, nullable, ์ฃผ๋ฌธ ๋ฉ๋ชจ/์์ฒญ์ฌํญ) - status (enum: pending, reviewing, producing, completed, ๊ธฐ๋ณธ๊ฐ pending) - timestamps ๋ง์ด๊ทธ๋ ์ด์ ์ ์คํํ๊ณ , Product ๋ชจ๋ธ๊ณผ Order ๋ชจ๋ธ์ ์์ฑํ์ธ์. Product ๋ชจ๋ธ์๋ `orders()` hasMany ๊ด๊ณ๋ฅผ, Order ๋ชจ๋ธ์๋ `product()` belongsTo ๊ด๊ณ๋ฅผ ์ ์ํ์ธ์. ์๋๋ฅผ ๋ง๋ค์ด ์ํ ์ํ ๋ฐ์ดํฐ๋ฅผ 5๊ฐ ์ ๋ ์ถ๊ฐํ์ธ์ (ํฐ์ ์ธ , ํ๋, ์์ฝ๋ฐฑ, ์คํฐ์ปค, ๋จธ๊ทธ์ปต ๋ฑ). `.env` ํ์ผ์์ DB_CONNECTION=sqlite๋ก ์ค์ ํ๊ณ `database/database.sqlite` ํ์ผ์ ์์ฑํ ํ ๋ง์ด๊ทธ๋ ์ด์ ์ ์คํํ์ธ์. ๋ง์ง๋ง์ผ๋ก Laravel Storage์ public ๋์คํฌ๋ฅผ ์ฌ๋ณผ๋ฆญ ๋งํฌ๋ก ์ฐ๊ฒฐํ์ธ์: ```bash php artisan storage:link ``` ์๋ฃ๋๋ฉด `php artisan serve`๋ก ์๋ฒ๋ฅผ ์คํํ๊ณ http://localhost:8000 ์์ Laravel Breeze ๊ธฐ๋ณธ ํ๋ฉด์ด ๋จ๋์ง ํ์ธํ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: Laravel + Inertia + React ํ๋ก์ ํธ ์์ฑ ์๋ฃ. products, orders ํ ์ด๋ธ ๋ง์ด๊ทธ๋ ์ด์ ๋ฐ ๋ชจ๋ธ ์์ฑ. ์ํ ์ํ ๋ฐ์ดํฐ ์๋ ์ถ๊ฐ. Storage ์ฌ๋ณผ๋ฆญ ๋งํฌ ์ค์ ์๋ฃ. ๊ฐ๋ฐ ์๋ฒ ์คํ ๊ฐ๋ฅ.
์ํ ๋ชฉ๋ก ํ์ด์ง๋ฅผ ๋ง๋ญ๋๋ค.
**๋ฐฑ์๋ (Laravel)**:
- `app/Http/Controllers/ProductController.php`๋ฅผ ์์ฑํ๊ณ `index()` ๋ฉ์๋๋ฅผ ์์ฑํ์ธ์.
- `Product::all()`๋ก ๋ชจ๋ ์ํ์ ์กฐํํ๊ณ `Inertia::render('Products/Index', ['products' => $products])`๋ก React ์ปดํฌ๋ํธ์ ์ ๋ฌํ์ธ์.
- `routes/web.php`์ `Route::get('/products', [ProductController::class, 'index'])->name('products.index');` ๋ผ์ฐํธ๋ฅผ ์ถ๊ฐํ์ธ์.
**ํ๋ก ํธ์๋ (React + Inertia)**:
- `resources/js/Pages/Products/Index.jsx` ํ์ผ์ ์์ฑํ์ธ์.
- Inertia์ `usePage()` ํ
์ผ๋ก `products` props๋ฅผ ๋ฐ์์ค์ธ์.
- ์ํ ๋ชฉ๋ก์ ๊ทธ๋ฆฌ๋ ๋ ์ด์์(Tailwind grid)์ผ๋ก ํ์ํ์ธ์. ๊ฐ ์ํ ์นด๋์๋ ๋ค์ ์ ๋ณด๋ฅผ ํฌํจ:
- ์ํ ์ด๋ฏธ์ง (image_url์ด ์์ผ๋ฉด ํ์, ์์ผ๋ฉด placeholder)
- ์ํ๋ช
(name)
- ์นดํ
๊ณ ๋ฆฌ (category) ๋ฐฐ์ง
- ๊ธฐ๋ณธ ๋จ๊ฐ (base_price) - "โฉ10,000๋ถํฐ" ํ์์ผ๋ก ํ์
- "์ฃผ๋ฌธํ๊ธฐ" ๋ฒํผ (ํด๋ฆญ ์ `/orders/create?product_id={id}` ํ์ด์ง๋ก ์ด๋)
- ๋ฐ์ํ ๋์์ธ (๋ชจ๋ฐ์ผ: 1์ด, ํ๋ธ๋ฆฟ: 2์ด, ๋ฐ์คํฌํฑ: 3์ด)
- Tailwind CSS๋ก ๊น๋ํ ์นด๋ ๋์์ธ (hover ํจ๊ณผ, shadow ๋ฑ)
์๋จ ๋ค๋น๊ฒ์ด์
์ "์ํ ๋ชฉ๋ก" ๋งํฌ๋ฅผ ์ถ๊ฐํ๊ณ , ํ์ด์ง ์ ๋ชฉ์ "DIY ๊ตฟ์ฆ ์ ์ ์ฃผ๋ฌธ"์ผ๋ก ํ์ํ์ธ์.
์๋ฃ ํ http://localhost:8000/products ์์ ์ํ ๋ชฉ๋ก์ด ์ ํ์๋๋์ง ํ์ธํ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: ์ํ ๋ชฉ๋ก ํ์ด์ง ์์ฑ. Laravel ์ปจํธ๋กค๋ฌ์์ Inertia๋ก ๋ฐ์ดํฐ ์ ๋ฌ. React ์ปดํฌ๋ํธ์์ ๊ทธ๋ฆฌ๋ ๋ ์ด์์์ผ๋ก ์ํ ์นด๋ ํ์. "์ฃผ๋ฌธํ๊ธฐ" ๋ฒํผ์ผ๋ก ์ฃผ๋ฌธ ํ์ด์ง ์ด๋ ๊ฐ๋ฅ.
์ฃผ๋ฌธ์ ์์ฑ ํ์ด์ง๋ฅผ ๋ง๋ญ๋๋ค.
**๋ฐฑ์๋ (Laravel)**:
- `app/Http/Controllers/OrderController.php`๋ฅผ ์์ฑํ๊ณ `create()` ๋ฉ์๋๋ฅผ ์์ฑํ์ธ์.
- URL ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๋ก `product_id`๋ฅผ ๋ฐ์ ํด๋น ์ํ์ ์กฐํํ์ธ์: `$product = Product::findOrFail(request('product_id'));`
- `Inertia::render('Orders/Create', ['product' => $product])`๋ก React ์ปดํฌ๋ํธ์ ์ ๋ฌํ์ธ์.
- `routes/web.php`์ `Route::get('/orders/create', [OrderController::class, 'create'])->name('orders.create');` ๋ผ์ฐํธ๋ฅผ ์ถ๊ฐํ์ธ์.
**ํ๋ก ํธ์๋ (React + Inertia)**:
- `resources/js/Pages/Orders/Create.jsx` ํ์ผ์ ์์ฑํ์ธ์.
- ์ฃผ๋ฌธ์ ํผ์ ๋ง๋์ธ์. ๋ค์ ํ๋๋ฅผ ํฌํจ:
- ์ ํ๋ ์ํ ์ ๋ณด ํ์ (์ฝ๊ธฐ ์ ์ฉ, ์ํ๋ช
/์ด๋ฏธ์ง/๊ธฐ๋ณธ ๋จ๊ฐ)
- ์ฃผ๋ฌธ์ ์ด๋ฆ (input text, required)
- ์ด๋ฉ์ผ (input email, required)
- ์ฐ๋ฝ์ฒ (input tel, required)
- ์ฃผ๋ฌธ ์๋ (input number, min=1, max=1000, ๊ธฐ๋ณธ๊ฐ 10)
- ๋์์ธ ํ์ผ ์
๋ก๋ (input file, accept="image/png,image/jpeg,application/pdf", required)
- ์ฃผ๋ฌธ ๋ฉ๋ชจ/์์ฒญ์ฌํญ (textarea, optional)
- **์ค์๊ฐ ๊ฒฌ์ ๊ณ์ฐ ํ์**: ์๋์ด ๋ณ๊ฒฝ๋ ๋๋ง๋ค `์ด ๊ฒฌ์ = base_price * quantity * (์๋ ํ ์ธ์จ)` ๊ณ์ฐ
- ์: 10๊ฐ ๋ฏธ๋ง 100%, 10~49๊ฐ 95%, 50~99๊ฐ 90%, 100๊ฐ ์ด์ 85% ๋จ๊ฐ ์ ์ฉ
- ๊ณ์ฐ๋ ์ด์ก์ ํฐ ๊ธ์จ๋ก ํ์ ("์์ ๊ฒฌ์ : โฉ50,000")
- useState๋ก ํผ ์ํ ๊ด๋ฆฌ (name, email, phone, quantity, file, notes)
- ์๋ ๋ณ๊ฒฝ ์ ์ค์๊ฐ์ผ๋ก ๊ฒฌ์ ์ฌ๊ณ์ฐ (useEffect ๋๋ onChange ํธ๋ค๋ฌ)
- "์ฃผ๋ฌธ ์ ์ฒญํ๊ธฐ" ๋ฒํผ (์์ง ์ ์ถ ๊ธฐ๋ฅ์ ์์, ๋ค์ ๋จ๊ณ์์ ๊ตฌํ)
- Tailwind CSS๋ก ๊น๋ํ ํผ ๋ ์ด์์ (label, input ์คํ์ผ๋ง)
์๋ฃ ํ http://localhost:8000/orders/create?product_id=1 ์์ ์ฃผ๋ฌธ ํผ์ด ์ ํ์๋๊ณ , ์๋ ๋ณ๊ฒฝ ์ ๊ฒฌ์ ์ด ์ค์๊ฐ์ผ๋ก ์
๋ฐ์ดํธ๋๋์ง ํ์ธํ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: ์ฃผ๋ฌธ์ ์์ฑ ํ์ด์ง ์์ฑ. ์ํ ์ ๋ณด ํ์, ์ฃผ๋ฌธ์ ์ ๋ณด ์ ๋ ฅ ํ๋, ๋์์ธ ํ์ผ ์ ๋ก๋, ์ค์๊ฐ ๊ฒฌ์ ๊ณ์ฐ ๊ธฐ๋ฅ ๊ตฌํ. ์๋ ๋ณ๊ฒฝ ์ ์ด์ก ์๋ ์ ๋ฐ์ดํธ.
์ฃผ๋ฌธ ์ ์ถ ๊ธฐ๋ฅ์ ๊ตฌํํฉ๋๋ค.
**๋ฐฑ์๋ (Laravel)**:
- `OrderController`์ `store()` ๋ฉ์๋๋ฅผ ์ถ๊ฐํ์ธ์.
- ํผ ๋ฐ์ดํฐ ๊ฒ์ฆ (Validation):
```php
$validated = $request->validate([
'product_id' => 'required|exists:products,id',
'customer_name' => 'required|string|max:100',
'customer_email' => 'required|email',
'customer_phone' => 'required|string|max:20',
'quantity' => 'required|integer|min:1|max:1000',
'design_file' => 'required|file|mimes:jpg,jpeg,png,pdf|max:5120',
'notes' => 'nullable|string|max:1000',
]);
```
- ๋์์ธ ํ์ผ ์
๋ก๋ ์ฒ๋ฆฌ:
- `$request->file('design_file')->store('designs', 'public')`๋ก ํ์ผ์ `storage/app/public/designs` ํด๋์ ์ ์ฅ
- ์ ์ฅ๋ ๊ฒฝ๋ก๋ฅผ `$filePath` ๋ณ์์ ์ ์ฅ
- ์ด ๊ฒฌ์ ๊ธ์ก ๊ณ์ฐ (๋ฐฑ์๋์์๋ ๋์ผํ ๋ก์ง ์ ์ฉ):
```php
$product = Product::findOrFail($validated['product_id']);
$quantity = $validated['quantity'];
$discount = 1.0;
if ($quantity >= 100) $discount = 0.85;
elseif ($quantity >= 50) $discount = 0.90;
elseif ($quantity >= 10) $discount = 0.95;
$totalPrice = $product->base_price * $quantity * $discount;
```
- Order ๋ ์ฝ๋ ์์ฑ:
```php
Order::create([
'product_id' => $validated['product_id'],
'customer_name' => $validated['customer_name'],
'customer_email' => $validated['customer_email'],
'customer_phone' => $validated['customer_phone'],
'quantity' => $quantity,
'design_file_path' => $filePath,
'total_price' => $totalPrice,
'notes' => $validated['notes'],
'status' => 'pending',
]);
```
- ์ฑ๊ณต ์ ์ฃผ๋ฌธ ๋ชฉ๋ก ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธ: `return redirect()->route('orders.index')->with('success', '์ฃผ๋ฌธ์ด ์ ์๋์์ต๋๋ค!');`
- `routes/web.php`์ `Route::post('/orders', [OrderController::class, 'store'])->name('orders.store');` ๋ผ์ฐํธ ์ถ๊ฐ
**ํ๋ก ํธ์๋ (React + Inertia)**:
- `Orders/Create.jsx`์์ Inertia์ `useForm()` ํ
์ ์ฌ์ฉํ์ฌ ํผ ์ ์ถ ์ฒ๋ฆฌ:
```jsx
import { useForm } from '@inertiajs/react';
const { data, setData, post, processing, errors } = useForm({
product_id: product.id,
customer_name: '',
customer_email: '',
customer_phone: '',
quantity: 10,
design_file: null,
notes: '',
});
```
- ๊ฐ input์ onChange์์ `setData()` ํธ์ถ
- ํ์ผ input์ onChange์์ `setData('design_file', e.target.files[0])`
- "์ฃผ๋ฌธ ์ ์ฒญํ๊ธฐ" ๋ฒํผ ํด๋ฆญ ์ `post(route('orders.store'))` ํธ์ถ
- ์๋ฌ ๋ฉ์์ง ํ์ (errors ๊ฐ์ฒด ํ์ฉ)
- ์ ์ถ ์ค์ผ ๋ ๋ฒํผ ๋นํ์ฑํ (processing ์ํ ํ์ฉ)
์๋ฃ ํ ์ฃผ๋ฌธ์ ์ ์ถํ๋ฉด DB์ ์ ์ฅ๋๊ณ ์ฃผ๋ฌธ ๋ชฉ๋ก ํ์ด์ง๋ก ์ด๋ํ๋์ง ํ์ธํ์ธ์. `storage/app/public/designs` ํด๋์ ํ์ผ์ด ์
๋ก๋๋์๋์ง๋ ํ์ธํ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: ์ฃผ๋ฌธ ์ ์ถ ๊ธฐ๋ฅ ์์ฑ. ํผ ๊ฒ์ฆ, ํ์ผ ์ ๋ก๋, ๊ฒฌ์ ๊ณ์ฐ, DB ์ ์ฅ ๋ชจ๋ ๋์. Inertia useForm์ผ๋ก ํผ ์ ์ถ ์ฒ๋ฆฌ. ์๋ฌ ์ฒ๋ฆฌ ๋ฐ ์ฑ๊ณต ์ ๋ฆฌ๋ค์ด๋ ํธ ๊ตฌํ.
์ฃผ๋ฌธ ๋ชฉ๋ก ํ์ด์ง๋ฅผ ๋ง๋ญ๋๋ค.
**๋ฐฑ์๋ (Laravel)**:
- `OrderController`์ `index()` ๋ฉ์๋๋ฅผ ์ถ๊ฐํ์ธ์.
- ๋ชจ๋ ์ฃผ๋ฌธ์ ์ต์ ์์ผ๋ก ์กฐํํ๊ณ ์ํ ์ ๋ณด๋ ํจ๊ป ๋ก๋:
```php
$orders = Order::with('product')
->orderBy('created_at', 'desc')
->paginate(10);
```
- `Inertia::render('Orders/Index', ['orders' => $orders])`๋ก ์ ๋ฌ
- `routes/web.php`์ `Route::get('/orders', [OrderController::class, 'index'])->name('orders.index');` ๋ผ์ฐํธ ์ถ๊ฐ
**ํ๋ก ํธ์๋ (React + Inertia)**:
- `resources/js/Pages/Orders/Index.jsx` ํ์ผ์ ์์ฑํ์ธ์.
- ์ฃผ๋ฌธ ๋ชฉ๋ก์ ํ
์ด๋ธ ๋๋ ์นด๋ ํํ๋ก ํ์ํ์ธ์. ๊ฐ ์ฃผ๋ฌธ์ ๋ค์ ์ ๋ณด ํฌํจ:
- ์ฃผ๋ฌธ ๋ฒํธ (id)
- ์ํ๋ช
(product.name)
- ์ฃผ๋ฌธ์ ์ด๋ฆ (customer_name)
- ์๋ (quantity)
- ์ด ๊ฒฌ์ ๊ธ์ก (total_price) - "โฉ50,000" ํ์
- ์ฃผ๋ฌธ ์ํ (status) - ๋ฐฐ์ง๋ก ํ์ (pending: ๋
ธ๋์, reviewing: ํ๋์, producing: ์ฃผํฉ์, completed: ์ด๋ก์)
- ์ฃผ๋ฌธ ๋ ์ง (created_at) - "2025-01-15" ํ์
- "์์ธ๋ณด๊ธฐ" ๋ฒํผ (ํด๋ฆญ ์ `/orders/{id}` ํ์ด์ง๋ก ์ด๋)
- Inertia์ ํ์ด์ง๋ค์ด์
์ปดํฌ๋ํธ ํ์ฉ:
```jsx
import { Link } from '@inertiajs/react';
// ํ์ด์ง๋ค์ด์
๋งํฌ ๋ ๋๋ง
orders.links.map(link => <Link href={link.url}>...</Link>)
```
- ์ฃผ๋ฌธ์ด ์์ ๋ "์์ง ์ฃผ๋ฌธ์ด ์์ต๋๋ค" ๋ฉ์์ง ํ์
- "์ ์ฃผ๋ฌธ ์ ์ฒญ" ๋ฒํผ ์ถ๊ฐ (์ํ ๋ชฉ๋ก ํ์ด์ง๋ก ์ด๋)
- Tailwind CSS๋ก ๊น๋ํ ํ
์ด๋ธ/์นด๋ ๋์์ธ
๋ค๋น๊ฒ์ด์
์ "์ฃผ๋ฌธ ๋ชฉ๋ก" ๋งํฌ๋ฅผ ์ถ๊ฐํ์ธ์.
์๋ฃ ํ http://localhost:8000/orders ์์ ์ฃผ๋ฌธ ๋ชฉ๋ก์ด ํ์๋๊ณ , ํ์ด์ง๋ค์ด์
์ด ๋์ํ๋์ง ํ์ธํ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: ์ฃผ๋ฌธ ๋ชฉ๋ก ํ์ด์ง ์์ฑ. ๋ชจ๋ ์ฃผ๋ฌธ์ ํ ์ด๋ธ/์นด๋ ํํ๋ก ํ์. ์ํ๋ณ ๋ฐฐ์ง, ํ์ด์ง๋ค์ด์ ๊ตฌํ. "์์ธ๋ณด๊ธฐ" ๋ฒํผ์ผ๋ก ์ฃผ๋ฌธ ์์ธ ํ์ด์ง ์ด๋ ๊ฐ๋ฅ.
์ฃผ๋ฌธ ์์ธ ํ์ด์ง๋ฅผ ๋ง๋ญ๋๋ค.
**๋ฐฑ์๋ (Laravel)**:
- `OrderController`์ `show($id)` ๋ฉ์๋๋ฅผ ์ถ๊ฐํ์ธ์.
- ํน์ ์ฃผ๋ฌธ์ ์ํ ์ ๋ณด์ ํจ๊ป ์กฐํ:
```php
$order = Order::with('product')->findOrFail($id);
```
- `Inertia::render('Orders/Show', ['order' => $order])`๋ก ์ ๋ฌ
- `routes/web.php`์ `Route::get('/orders/{id}', [OrderController::class, 'show'])->name('orders.show');` ๋ผ์ฐํธ ์ถ๊ฐ
**ํ๋ก ํธ์๋ (React + Inertia)**:
- `resources/js/Pages/Orders/Show.jsx` ํ์ผ์ ์์ฑํ์ธ์.
- ์ฃผ๋ฌธ ์์ธ ์ ๋ณด๋ฅผ 2๋จ ๋ ์ด์์์ผ๋ก ํ์:
- **์ผ์ชฝ**: ์ฃผ๋ฌธ ์ ๋ณด
- ์ฃผ๋ฌธ ๋ฒํธ
- ์ฃผ๋ฌธ ๋ ์ง
- ์ฃผ๋ฌธ ์ํ (ํฐ ๋ฐฐ์ง๋ก ํ์)
- ์ฃผ๋ฌธ์ ์ ๋ณด (์ด๋ฆ, ์ด๋ฉ์ผ, ์ฐ๋ฝ์ฒ)
- ์ฃผ๋ฌธ ๋ฉ๋ชจ/์์ฒญ์ฌํญ (์์ ๊ฒฝ์ฐ)
- **์ค๋ฅธ์ชฝ**: ์ํ ๋ฐ ๊ฒฌ์ ์ ๋ณด
- ์ํ ์ด๋ฏธ์ง (product.image_url)
- ์ํ๋ช
(product.name)
- ์นดํ
๊ณ ๋ฆฌ (product.category)
- ๊ธฐ๋ณธ ๋จ๊ฐ (product.base_price)
- ์ฃผ๋ฌธ ์๋ (order.quantity)
- ์ด ๊ฒฌ์ ๊ธ์ก (order.total_price) - ํฐ ๊ธ์จ๋ก ๊ฐ์กฐ
- **๋์์ธ ํ์ผ ๋ค์ด๋ก๋** ์น์
:
- ์
๋ก๋๋ ๋์์ธ ํ์ผ ์ธ๋ค์ผ ํ์ (์ด๋ฏธ์ง์ธ ๊ฒฝ์ฐ)
- "๋์์ธ ํ์ผ ๋ค์ด๋ก๋" ๋งํฌ (href="/storage/{order.design_file_path}" download)
- "๋ชฉ๋ก์ผ๋ก ๋์๊ฐ๊ธฐ" ๋ฒํผ (์ฃผ๋ฌธ ๋ชฉ๋ก ํ์ด์ง๋ก ์ด๋)
- Tailwind CSS๋ก ๊น๋ํ 2๋จ ๋ ์ด์์ (grid ๋๋ flex)
์๋ฃ ํ ์ฃผ๋ฌธ ๋ชฉ๋ก์์ "์์ธ๋ณด๊ธฐ"๋ฅผ ํด๋ฆญํ๋ฉด ์ฃผ๋ฌธ ์์ธ ํ์ด์ง๊ฐ ์ ํ์๋๋์ง ํ์ธํ์ธ์. ๋์์ธ ํ์ผ ๋ค์ด๋ก๋ ๋งํฌ๊ฐ ๋์ํ๋์ง๋ ํ์ธํ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: ์ฃผ๋ฌธ ์์ธ ํ์ด์ง ์์ฑ. ์ฃผ๋ฌธ ์ ๋ณด, ์ํ ์ ๋ณด, ๊ฒฌ์ ์ ๋ณด๋ฅผ 2๋จ ๋ ์ด์์์ผ๋ก ํ์. ๋์์ธ ํ์ผ ์ธ๋ค์ผ ๋ฐ ๋ค์ด๋ก๋ ๋งํฌ ๊ตฌํ. ๋ชฉ๋ก์ผ๋ก ๋์๊ฐ๊ธฐ ๋ฒํผ ์ถ๊ฐ.
์ ์ถ๋ ์ํ (0)
๋ก๊ทธ์ธ ํ ์ ์ถ์์ง ์ ์ถ๋ ์ํ์ด ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋์ ์๊ฐ ๋์ด๋ณด์ธ์!