๐ŸŽจ ํ”„๋ฆฌ๋žœ์„œ ๊ฒฌ์ ์„œ ์ž๋™ ์ƒ์„ฑ๊ธฐ - ๊ณ ๊ฐ ๋งž์ถค PDF ๊ฒฌ์ ์„œ ๋ฐœ์†ก - ๋ฐ”์ด๋ธŒ์ฝ”๋”ฉ ๋ ˆ์‹œํ”ผ
์ž…๋ฌธ PHP + JavaScript Laravel + React AI: Claude

๐ŸŽจ ํ”„๋ฆฌ๋žœ์„œ ๊ฒฌ์ ์„œ ์ž๋™ ์ƒ์„ฑ๊ธฐ - ๊ณ ๊ฐ ๋งž์ถค PDF ๊ฒฌ์ ์„œ ๋ฐœ์†ก

๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ช… freelancer-quote-pdf-generator
์กฐํšŒ์ˆ˜ 125 0๊ฑด ์ œ์ถœ

๋ฌธ์ œ ์„ค๋ช…

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

ํ”„๋ฆฌ๋žœ์„œ ๋””์ž์ด๋„ˆ, ๊ฐœ๋ฐœ์ž, ์ž‘๊ฐ€ ๋“ฑ์ด ๊ณ ๊ฐ์—๊ฒŒ ์ „๋ฌธ์ ์ธ ๊ฒฌ์ ์„œ๋ฅผ ๋น ๋ฅด๊ฒŒ ์ƒ์„ฑํ•˜๊ณ  PDF๋กœ ๋‹ค์šด๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋Š” ํ’€์Šคํƒ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ž…๋‹ˆ๋‹ค. Laravel API ๋ฐฑ์—”๋“œ์™€ React ํ”„๋ก ํŠธ์—”๋“œ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์œผ๋ฉฐ, ๊ฒฌ์  ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ด์•ก์ด ๊ณ„์‚ฐ๋˜๊ณ  ๊น”๋”ํ•œ PDF ๊ฒฌ์ ์„œ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

  • โœ… ๊ฒฌ์ ์„œ ๊ธฐ๋ณธ ์ •๋ณด ์ž…๋ ฅ (๊ณ ๊ฐ๋ช…, ํ”„๋กœ์ ํŠธ๋ช…, ๋ฐœํ–‰์ผ์ž)
  • โœ… ๊ฒฌ์  ํ•ญ๋ชฉ ์ถ”๊ฐ€/์‚ญ์ œ (์„œ๋น„์Šค๋ช…, ๋‹จ๊ฐ€, ์ˆ˜๋Ÿ‰)
  • โœ… ์‹ค์‹œ๊ฐ„ ์ด์•ก ๊ณ„์‚ฐ (ํ•ญ๋ชฉ๋ณ„ ์†Œ๊ณ„ + ์ „์ฒด ํ•ฉ๊ณ„)
  • โœ… ์ƒ์„ฑ๋œ ๊ฒฌ์ ์„œ ๋ชฉ๋ก ์กฐํšŒ (์นด๋“œ ๋ ˆ์ด์•„์›ƒ)
  • โœ… PDF ๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ (๋ธŒ๋ผ์šฐ์ € ์ธ์‡„ ๊ธฐ๋Šฅ ํ™œ์šฉ)
  • โœ… ๋ฐ˜์‘ํ˜• ๋””์ž์ธ (๋ชจ๋ฐ”์ผ ์ตœ์ ํ™”)

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

  • ๐ŸŽ ๊ฒฌ์ ์„œ ํ…œํ”Œ๋ฆฟ ์ƒ‰์ƒ ํ…Œ๋งˆ ์„ ํƒ (๋ธ”๋ฃจ/๊ทธ๋ฆฐ/ํผํ”Œ)
  • ๐ŸŽ ๋ถ€๊ฐ€์„ธ(VAT) ์ž๋™ ๊ณ„์‚ฐ ์˜ต์…˜
  • ๐ŸŽ ๊ฒฌ์  ์œ ํšจ๊ธฐ๊ฐ„ ์„ค์ •
  • ๐ŸŽ ํšŒ์‚ฌ ๋กœ๊ณ  ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ
  • ๐ŸŽ ๊ฒฌ์ ์„œ ์ƒํƒœ ๊ด€๋ฆฌ (์ž‘์„ฑ์ค‘/๋ฐœ์†ก์™„๋ฃŒ/์Šน์ธ)

๊ธฐ์ˆ  ์Šคํƒ

  • ๋ฐฑ์—”๋“œ: Laravel 13.x (RESTful API, SQLite)
  • ํ”„๋ก ํŠธ์—”๋“œ: React 19 (Vite, Tailwind CSS)
  • ๋ฐ์ดํ„ฐ ์—ฐ๋™: Axios
  • PDF ์ƒ์„ฑ: ๋ธŒ๋ผ์šฐ์ € window.print() API

์ œ์•ฝ ์กฐ๊ฑด

  • โ€ข Laravel 13.x ๋ฐฑ์—”๋“œ API์™€ React 19 ํ”„๋ก ํŠธ์—”๋“œ๋ฅผ ๋ณ„๋„ ํด๋”๋กœ ๋ถ„๋ฆฌ
  • โ€ข Laravel์€ RESTful API ์„œ๋ฒ„ ์—ญํ•  (JSON ์‘๋‹ต)
  • โ€ข React๋Š” Vite๋กœ ๋ณ„๋„ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
  • โ€ข ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” SQLite ์‚ฌ์šฉ (๊ฐ„ํŽธํ•œ ๋กœ์ปฌ ๊ฐœ๋ฐœ)
  • โ€ข Tailwind CSS๋กœ ๋ชจ๋˜ํ•œ UI ๊ตฌํ˜„
  • โ€ข PDF ์ƒ์„ฑ์€ ๋ธŒ๋ผ์šฐ์ € ๋‚ด์žฅ ์ธ์‡„ ๊ธฐ๋Šฅ ํ™œ์šฉ (๋ณต์žกํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ถˆํ•„์š”)
  • โ€ข CORS ์„ค์ •์œผ๋กœ ํ”„๋ก ํŠธ์—”๋“œ-๋ฐฑ์—”๋“œ ํ†ต์‹  ํ—ˆ์šฉ
  • โ€ข ๊ฒฌ์  ํ•ญ๋ชฉ์€ ๋™์ ์œผ๋กœ ์ถ”๊ฐ€/์‚ญ์ œ ๊ฐ€๋Šฅ
  • โ€ข ์ด์•ก์€ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ์—์„œ ์‹ค์‹œ๊ฐ„ ๊ณ„์‚ฐ

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

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

1
Step 1
mkdir freelancer-quote-pdf-generator && cd freelancer-quote-pdf-generator

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

# Laravel 13.x API ๋ฐฑ์—”๋“œ ์ƒ์„ฑ

1. `backend` ํด๋”์— Laravel ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜์„ธ์š”:
   ```bash
   composer create-project laravel/laravel:^13.0 backend
   cd backend
   ```

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. ๊ฒฌ์ ์„œ(Quote) ๋ชจ๋ธ๊ณผ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์ƒ์„ฑํ•˜์„ธ์š”:
   ```bash
   php artisan make:model Quote -m
   ```

5. `database/migrations/xxxx_create_quotes_table.php` ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํŒŒ์ผ์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•˜์„ธ์š”:
   - ํ…Œ์ด๋ธ”๋ช…: `quotes`
   - ์ปฌ๋Ÿผ:
     - `id` (bigIncrements)
     - `customer_name` (string) - ๊ณ ๊ฐ๋ช…
     - `project_name` (string) - ํ”„๋กœ์ ํŠธ๋ช…
     - `issue_date` (date) - ๋ฐœํ–‰์ผ์ž
     - `items` (json) - ๊ฒฌ์  ํ•ญ๋ชฉ ๋ฐฐ์—ด (์„œ๋น„์Šค๋ช…, ๋‹จ๊ฐ€, ์ˆ˜๋Ÿ‰ ํฌํ•จ)
     - `total_amount` (decimal, 10, 2) - ์ด์•ก
     - `timestamps`

6. ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์‹คํ–‰ํ•˜์„ธ์š”:
   ```bash
   php artisan migrate
   ```

7. `app/Models/Quote.php` ๋ชจ๋ธ์— `$fillable` ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”:
   ```php
   protected $fillable = ['customer_name', 'project_name', 'issue_date', 'items', 'total_amount'];
   protected $casts = ['items' => 'array', 'issue_date' => 'date'];
   ```

8. API ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ƒ์„ฑํ•˜์„ธ์š”:
   ```bash
   php artisan make:controller Api/QuoteController --api
   ```

9. `app/Http/Controllers/Api/QuoteController.php`์— ๋‹ค์Œ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜์„ธ์š”:
   - `index()`: ๋ชจ๋“  ๊ฒฌ์ ์„œ ๋ชฉ๋ก ๋ฐ˜ํ™˜ (์ตœ์‹ ์ˆœ ์ •๋ ฌ)
   - `store(Request $request)`: ์ƒˆ ๊ฒฌ์ ์„œ ์ƒ์„ฑ
     - ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ: customer_name, project_name, issue_date, items (๋ฐฐ์—ด), total_amount ํ•„์ˆ˜
   - `show($id)`: ํŠน์ • ๊ฒฌ์ ์„œ ์ƒ์„ธ ์กฐํšŒ

10. `routes/api.php`์— API ๋ผ์šฐํŠธ๋ฅผ ๋“ฑ๋กํ•˜์„ธ์š”:
    ```php
    use App\Http\Controllers\Api\QuoteController;
    Route::apiResource('quotes', QuoteController::class)->only(['index', 'store', 'show']);
    ```

11. CORS ์„ค์ •์„ ์œ„ํ•ด `config/cors.php`๋ฅผ ์ˆ˜์ •ํ•˜์„ธ์š”:
    - `'paths' => ['api/*']`
    - `'allowed_origins' => ['http://localhost:5173']` (React Vite ๊ธฐ๋ณธ ํฌํŠธ)
    - `'allowed_methods' => ['*']`

12. Laravel ๊ฐœ๋ฐœ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜์„ธ์š”:
    ```bash
    php artisan serve
    ```
    โ†’ http://localhost:8000 ์—์„œ API ์„œ๋ฒ„ ์‹คํ–‰ ํ™•์ธ

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: Laravel ๋ฐฑ์—”๋“œ API ์„œ๋ฒ„๊ฐ€ ๊ตฌ์ถ•๋˜๊ณ , Quote ๋ชจ๋ธ๊ณผ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด ์ƒ์„ฑ๋˜๋ฉฐ, API ์—”๋“œํฌ์ธํŠธ(GET /api/quotes, POST /api/quotes, GET /api/quotes/{id})๊ฐ€ ์ค€๋น„๋ฉ๋‹ˆ๋‹ค. SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— quotes ํ…Œ์ด๋ธ”์ด ์ƒ์„ฑ๋˜๊ณ , CORS ์„ค์ •์ด ์™„๋ฃŒ๋˜์–ด ํ”„๋ก ํŠธ์—”๋“œ์™€ ํ†ต์‹ ํ•  ์ค€๋น„๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

2
Step 2
# React 19 ํ”„๋ก ํŠธ์—”๋“œ ์ƒ์„ฑ

ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ(`freelancer-quote-pdf-generator`)๋กœ ๋Œ์•„์™€์„œ React ํ”„๋ก ํŠธ์—”๋“œ๋ฅผ ์„ค์ •ํ•˜์„ธ์š”.

1. `frontend` ํด๋”์— React + Vite ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜์„ธ์š”:
   ```bash
   npm create vite@latest frontend -- --template react
   cd frontend
   npm install
   ```

2. Tailwind CSS๋ฅผ ์„ค์น˜ํ•˜๊ณ  ์„ค์ •ํ•˜์„ธ์š”:
   ```bash
   npm install -D tailwindcss postcss autoprefixer
   npx tailwindcss init -p
   ```

3. `tailwind.config.js`๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•˜์„ธ์š”:
   ```js
   export default {
     content: ['./index.html', './src/**/*.{js,jsx}'],
     theme: { extend: {} },
     plugins: [],
   }
   ```

4. `src/index.css`์— Tailwind ์ง€์‹œ์–ด๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”:
   ```css
   @tailwind base;
   @tailwind components;
   @tailwind utilities;
   ```

5. Axios๋ฅผ ์„ค์น˜ํ•˜์„ธ์š”:
   ```bash
   npm install axios
   ```

6. `src/App.jsx`๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•˜์„ธ์š”:
   - ์ƒํƒœ ๊ด€๋ฆฌ:
     - `quotes` (๋ฐฐ์—ด): ์ƒ์„ฑ๋œ ๊ฒฌ์ ์„œ ๋ชฉ๋ก
     - `formData` (๊ฐ์ฒด): ์ƒˆ ๊ฒฌ์ ์„œ ์ž…๋ ฅ ํผ ๋ฐ์ดํ„ฐ (customer_name, project_name, issue_date)
     - `items` (๋ฐฐ์—ด): ๊ฒฌ์  ํ•ญ๋ชฉ ๋ชฉ๋ก (๊ฐ ํ•ญ๋ชฉ: {service, price, quantity})
   - ํ•จ์ˆ˜:
     - `fetchQuotes()`: GET /api/quotes ํ˜ธ์ถœํ•˜์—ฌ ๊ฒฌ์ ์„œ ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ
     - `addItem()`: ์ƒˆ ๊ฒฌ์  ํ•ญ๋ชฉ ์ถ”๊ฐ€
     - `removeItem(index)`: ํŠน์ • ํ•ญ๋ชฉ ์‚ญ์ œ
     - `calculateTotal()`: ํ•ญ๋ชฉ๋ณ„ ์†Œ๊ณ„ ๋ฐ ์ „์ฒด ํ•ฉ๊ณ„ ๊ณ„์‚ฐ
     - `handleSubmit()`: POST /api/quotes๋กœ ๊ฒฌ์ ์„œ ์ƒ์„ฑ ํ›„ ๋ชฉ๋ก ์ƒˆ๋กœ๊ณ ์นจ
   - UI ๊ตฌ์„ฑ:
     - ์ƒ๋‹จ: ์ œ๋ชฉ "ํ”„๋ฆฌ๋žœ์„œ ๊ฒฌ์ ์„œ ์ƒ์„ฑ๊ธฐ" (Tailwind๋กœ ์Šคํƒ€์ผ๋ง)
     - ์ค‘์•™: ๊ฒฌ์ ์„œ ์ž‘์„ฑ ํผ (๊ณ ๊ฐ๋ช…, ํ”„๋กœ์ ํŠธ๋ช…, ๋ฐœํ–‰์ผ์ž ์ž…๋ ฅ)
     - ๊ฒฌ์  ํ•ญ๋ชฉ ์ถ”๊ฐ€ ์„น์…˜ (์„œ๋น„์Šค๋ช…, ๋‹จ๊ฐ€, ์ˆ˜๋Ÿ‰ ์ž…๋ ฅ + ์ถ”๊ฐ€ ๋ฒ„ํŠผ)
     - ํ•ญ๋ชฉ ๋ชฉ๋ก ํ…Œ์ด๋ธ” (์„œ๋น„์Šค๋ช…, ๋‹จ๊ฐ€, ์ˆ˜๋Ÿ‰, ์†Œ๊ณ„, ์‚ญ์ œ ๋ฒ„ํŠผ)
     - ์ด์•ก ํ‘œ์‹œ (์‹ค์‹œ๊ฐ„ ๊ณ„์‚ฐ)
     - "๊ฒฌ์ ์„œ ์ƒ์„ฑ" ๋ฒ„ํŠผ
     - ํ•˜๋‹จ: ์ƒ์„ฑ๋œ ๊ฒฌ์ ์„œ ๋ชฉ๋ก (์นด๋“œ ๋ ˆ์ด์•„์›ƒ, ๊ณ ๊ฐ๋ช…/ํ”„๋กœ์ ํŠธ๋ช…/์ด์•ก ํ‘œ์‹œ)

7. Axios ๊ธฐ๋ณธ URL ์„ค์ •:
   ```js
   import axios from 'axios';
   axios.defaults.baseURL = 'http://localhost:8000/api';
   ```

8. React ๊ฐœ๋ฐœ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜์„ธ์š”:
   ```bash
   npm run dev
   ```
   โ†’ http://localhost:5173 ์—์„œ ํ”„๋ก ํŠธ์—”๋“œ ์‹คํ–‰ ํ™•์ธ

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: React ํ”„๋ก ํŠธ์—”๋“œ๊ฐ€ ๊ตฌ์ถ•๋˜๊ณ , Tailwind CSS๋กœ ์Šคํƒ€์ผ๋ง๋œ ๊ฒฌ์ ์„œ ์ž‘์„ฑ ํผ๊ณผ ํ•ญ๋ชฉ ์ถ”๊ฐ€/์‚ญ์ œ ๊ธฐ๋Šฅ์ด ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. Axios๋กœ Laravel API์™€ ํ†ต์‹ ํ•˜์—ฌ ๊ฒฌ์ ์„œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๋ชฉ๋ก์„ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์•ก์ด ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ณ„์‚ฐ๋˜์–ด ํ™”๋ฉด์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

3
Step 3
# PDF ๋‹ค์šด๋กœ๋“œ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ๋ฐ UI ๊ฐœ์„ 

1. `src/components/QuoteDetail.jsx` ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ƒ์„ฑํ•˜์„ธ์š”:
   - Props: `quote` (๊ฐ์ฒด)
   - UI:
     - ๊ฒฌ์ ์„œ ํ—ค๋” (๊ณ ๊ฐ๋ช…, ํ”„๋กœ์ ํŠธ๋ช…, ๋ฐœํ–‰์ผ์ž)
     - ํ•ญ๋ชฉ ํ…Œ์ด๋ธ” (์„œ๋น„์Šค๋ช…, ๋‹จ๊ฐ€, ์ˆ˜๋Ÿ‰, ์†Œ๊ณ„)
     - ์ด์•ก (๊ฐ•์กฐ ํ‘œ์‹œ)
     - "PDF ๋‹ค์šด๋กœ๋“œ" ๋ฒ„ํŠผ (ํด๋ฆญ ์‹œ `window.print()` ํ˜ธ์ถœ)
   - ์ธ์‡„์šฉ CSS:
     - `@media print` ์ฟผ๋ฆฌ๋กœ ๋ฒ„ํŠผ ์ˆจ๊น€ ์ฒ˜๋ฆฌ
     - ํŽ˜์ด์ง€ ์—ฌ๋ฐฑ ์ตœ์ ํ™”

2. `src/App.jsx`๋ฅผ ์ˆ˜์ •ํ•˜์„ธ์š”:
   - ๊ฒฌ์ ์„œ ๋ชฉ๋ก์—์„œ ํŠน์ • ๊ฒฌ์  ํด๋ฆญ ์‹œ `QuoteDetail` ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ชจ๋‹ฌ๋กœ ํ‘œ์‹œ
   - ๋ชจ๋‹ฌ ๋ฐฐ๊ฒฝ ํด๋ฆญ ์‹œ ๋‹ซ๊ธฐ
   - ์ƒํƒœ ์ถ”๊ฐ€: `selectedQuote` (์„ ํƒ๋œ ๊ฒฌ์ ์„œ)

3. UI ๊ฐœ์„ :
   - ๊ฒฌ์ ์„œ ์ž‘์„ฑ ํผ์„ ์นด๋“œ ํ˜•ํƒœ๋กœ ๊ฐ์‹ธ๊ธฐ (shadow, rounded, padding)
   - ํ•ญ๋ชฉ ์ถ”๊ฐ€ ๋ฒ„ํŠผ์— ์•„์ด์ฝ˜ ์ถ”๊ฐ€ (+ ๊ธฐํ˜ธ)
   - ์ด์•ก์„ ํฐ ํฐํŠธ์™€ ์ƒ‰์ƒ์œผ๋กœ ๊ฐ•์กฐ
   - ๊ฒฌ์ ์„œ ๋ชฉ๋ก์„ ๊ทธ๋ฆฌ๋“œ ๋ ˆ์ด์•„์›ƒ์œผ๋กœ ๋ณ€๊ฒฝ (3์—ด)
   - ๊ฐ ๊ฒฌ์ ์„œ ์นด๋“œ์— hover ํšจ๊ณผ ์ถ”๊ฐ€

4. ํผ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์ถ”๊ฐ€:
   - ๊ณ ๊ฐ๋ช…, ํ”„๋กœ์ ํŠธ๋ช…, ๋ฐœํ–‰์ผ์ž ํ•„์ˆ˜ ์ž…๋ ฅ
   - ์ตœ์†Œ 1๊ฐœ ์ด์ƒ์˜ ๊ฒฌ์  ํ•ญ๋ชฉ ํ•„์š”
   - ๋‹จ๊ฐ€์™€ ์ˆ˜๋Ÿ‰์€ ์–‘์ˆ˜๋งŒ ํ—ˆ์šฉ
   - ์ž…๋ ฅ ์˜ค๋ฅ˜ ์‹œ ์•Œ๋ฆผ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ

5. ํ…Œ์ŠคํŠธ:
   - ๊ฒฌ์ ์„œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๋ชฉ๋ก์—์„œ ํด๋ฆญํ•˜์—ฌ ์ƒ์„ธ ๋ณด๊ธฐ
   - PDF ๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ๋ธŒ๋ผ์šฐ์ € ์ธ์‡„ ๋Œ€ํ™”์ƒ์ž ํ™•์ธ
   - ์ธ์‡„ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์—์„œ ๋ฒ„ํŠผ์ด ์ˆจ๊ฒจ์ง€๋Š”์ง€ ํ™•์ธ

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ๊ฒฌ์ ์„œ ์ƒ์„ธ ๋ณด๊ธฐ ๋ชจ๋‹ฌ์ด ์ถ”๊ฐ€๋˜๊ณ , PDF ๋‹ค์šด๋กœ๋“œ ๊ธฐ๋Šฅ์ด ๋ธŒ๋ผ์šฐ์ € ์ธ์‡„ API๋กœ ๊ตฌํ˜„๋ฉ๋‹ˆ๋‹ค. UI๊ฐ€ Tailwind CSS๋กœ ์„ธ๋ จ๋˜๊ฒŒ ๊ฐœ์„ ๋˜๋ฉฐ, ํผ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋กœ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค. ์ธ์‡„ ์‹œ ๊น”๋”ํ•œ ๊ฒฌ์ ์„œ ๋ ˆ์ด์•„์›ƒ์ด ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

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

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

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

๋Œ“๊ธ€ (0)

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