๐ŸŽ“ ์˜จ๋ผ์ธ ์ž๊ฒฉ์ฆ ๊ฒ€์ฆ & ๋””์ง€ํ„ธ ๋ฐฐ์ง€ ๋ฐœ๊ธ‰ ํ”Œ๋žซํผ - ๋ธ”๋ก์ฒด์ธ ์Šคํƒ€์ผ ์ธ์ฆ์„œ ๊ด€๋ฆฌ - ๋ฐ”์ด๋ธŒ์ฝ”๋”ฉ ๋ ˆ์‹œํ”ผ
๊ณ ๊ธ‰ PHP Laravel AI: Claude

๐ŸŽ“ ์˜จ๋ผ์ธ ์ž๊ฒฉ์ฆ ๊ฒ€์ฆ & ๋””์ง€ํ„ธ ๋ฐฐ์ง€ ๋ฐœ๊ธ‰ ํ”Œ๋žซํผ - ๋ธ”๋ก์ฒด์ธ ์Šคํƒ€์ผ ์ธ์ฆ์„œ ๊ด€๋ฆฌ

๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ช… digital-badge-certification-platform
์กฐํšŒ์ˆ˜ 95 0๊ฑด ์ œ์ถœ

๋ฌธ์ œ ์„ค๋ช…

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

๊ธฐ์—…๊ณผ ๊ต์œก๊ธฐ๊ด€์ด ์ˆ˜๋ฃŒ์ƒ์—๊ฒŒ ๊ฒ€์ฆ ๊ฐ€๋Šฅํ•œ ๋””์ง€ํ„ธ ์ž๊ฒฉ์ฆ์„ ๋ฐœ๊ธ‰ํ•˜๊ณ , ์ˆ˜๋ฃŒ์ƒ์€ ์ž์‹ ์˜ ์ธ์ฆ์„œ๋ฅผ ํฌํŠธํด๋ฆฌ์˜ค์ฒ˜๋Ÿผ ๊ด€๋ฆฌํ•˜๋ฉฐ ์™ธ๋ถ€์— ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋Š” ํ’€์Šคํƒ ํ”Œ๋žซํผ์ž…๋‹ˆ๋‹ค. Laravel 13.x + Blade + Livewire๋กœ ๊ตฌํ˜„ํ•˜๋ฉฐ, QR ์ฝ”๋“œ ๊ธฐ๋ฐ˜ ์ง„์œ„ ํ™•์ธ, PDF ์ž๋™ ์ƒ์„ฑ, ๊ณต๊ฐœ ํ”„๋กœํ•„ ํŽ˜์ด์ง€, ์ด๋ฉ”์ผ ์ž๋™ ๋ฐœ์†ก ๋“ฑ ์‹ค๋ฌด ์ˆ˜์ค€์˜ ๊ธฐ๋Šฅ์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

ํ•ต์‹ฌ ๊ธฐ๋Šฅ

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

  1. ๊ด€๋ฆฌ์ž ๋Œ€์‹œ๋ณด๋“œ (๋ฐœ๊ธ‰ ๊ธฐ๊ด€์šฉ)

    • ์ž๊ฒฉ์ฆ ํ…œํ”Œ๋ฆฟ ์ƒ์„ฑ (๊ณผ์ •๋ช…, ์„ค๋ช…, ๋””์ž์ธ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•)
    • ์ˆ˜๋ฃŒ์ƒ ์ผ๊ด„ ๋“ฑ๋ก (CSV ์—…๋กœ๋“œ)
    • ๊ฐœ๋ณ„/์ผ๊ด„ ์ž๊ฒฉ์ฆ ๋ฐœ๊ธ‰
    • ๋ฐœ๊ธ‰ ์ด๋ ฅ ์กฐํšŒ ๋ฐ ํ†ต๊ณ„
  2. ์ž๊ฒฉ์ฆ ๋ฐœ๊ธ‰ ์‹œ์Šคํ…œ

    • ๊ณ ์œ  ์ธ์ฆ๋ฒˆํ˜ธ ์ž๋™ ์ƒ์„ฑ (UUID)
    • QR ์ฝ”๋“œ ์ž๋™ ์‚ฝ์ž… (๊ณต๊ฐœ ๊ฒ€์ฆ URL ํฌํ•จ)
    • PDF ์ž๋™ ์ƒ์„ฑ (Laravel DOMPDF/Snappy)
    • ๋ฐœ๊ธ‰ ์ฆ‰์‹œ ์ด๋ฉ”์ผ ์ž๋™ ๋ฐœ์†ก (Queue ์‚ฌ์šฉ)
  3. ์ˆ˜๋ฃŒ์ƒ ๋งˆ์ดํŽ˜์ด์ง€

    • ๋‚ด๊ฐ€ ๋ฐ›์€ ์ž๊ฒฉ์ฆ ๋ชฉ๋ก (์ธ๋„ค์ผ ๊ทธ๋ฆฌ๋“œ)
    • ์ž๊ฒฉ์ฆ ์ƒ์„ธ ๋ณด๊ธฐ (๋ฉ”ํƒ€๋ฐ์ดํ„ฐ: ๋ฐœ๊ธ‰์ผ, ๋ฐœ๊ธ‰๊ธฐ๊ด€, ์œ ํšจ๊ธฐ๊ฐ„)
    • PDF ๋‹ค์šด๋กœ๋“œ
    • ์†Œ์…œ ๋ฏธ๋””์–ด ๊ณต์œ  ๋งํฌ ์ƒ์„ฑ
  4. ๊ณต๊ฐœ ๊ฒ€์ฆ ์‹œ์Šคํ…œ

    • QR ์ฝ”๋“œ ์Šค์บ” โ†’ ๊ณต๊ฐœ ๊ฒ€์ฆ ํŽ˜์ด์ง€ ์ด๋™
    • ์ธ์ฆ๋ฒˆํ˜ธ ์ž…๋ ฅ์œผ๋กœ ์ˆ˜๋™ ๊ฒ€์ฆ
    • ๊ฒ€์ฆ ๊ฒฐ๊ณผ: ๋ฐœ๊ธ‰ ์ •๋ณด, ์ˆ˜๋ฃŒ์ƒ ์ด๋ฆ„, ๊ณผ์ •๋ช…, ๋ฐœ๊ธ‰์ผ, ์ง„์œ„ ์—ฌ๋ถ€
    • ์œ„์กฐ ๋ฐฉ์ง€: DB ํ•ด์‹œ ๋น„๊ต
  5. ๊ณต๊ฐœ ํ”„๋กœํ•„ ํŽ˜์ด์ง€

    • ์ˆ˜๋ฃŒ์ƒ๋ณ„ ๊ณ ์œ  URL (์˜ˆ: /profile/{username})
    • ์ทจ๋“ํ•œ ์ž๊ฒฉ์ฆ ๊ฐค๋Ÿฌ๋ฆฌ
    • ์ž๊ธฐ์†Œ๊ฐœ & ์†Œ์…œ ๋งํฌ
    • ํ”„๋กœํ•„ ๊ณต๊ฐœ/๋น„๊ณต๊ฐœ ์„ค์ •

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

  • ์ž๊ฒฉ์ฆ ๋งŒ๋ฃŒ์ผ ์ž๋™ ์•Œ๋ฆผ (Laravel Scheduler)
  • ๋‹ค๊ตญ์–ด ์ง€์› (ํ•œ/์˜ ์ž๊ฒฉ์ฆ)
  • ์ž๊ฒฉ์ฆ ํ…œํ”Œ๋ฆฟ ๋งˆ์ผ“ํ”Œ๋ ˆ์ด์Šค (๊ด€๋ฆฌ์ž๊ฐ€ ๋‹ค์–‘ํ•œ ๋””์ž์ธ ์„ ํƒ)
  • ๋ฐœ๊ธ‰ ํ†ต๊ณ„ ๋Œ€์‹œ๋ณด๋“œ (Chart.js)
  • ์ž๊ฒฉ์ฆ ์žฌ๋ฐœ๊ธ‰ ์š”์ฒญ ์‹œ์Šคํ…œ
  • OAuth ์†Œ์…œ ๋กœ๊ทธ์ธ (Google/GitHub)

๊ธฐ์ˆ  ์Šคํƒ

  • Backend: Laravel 13.x (Eloquent ORM, Queue, Scheduler, Mail)
  • Frontend: Blade + Livewire 3.x (์‹ค์‹œ๊ฐ„ UI ์—…๋ฐ์ดํŠธ)
  • Styling: Tailwind CSS 4.x
  • Database: MySQL/PostgreSQL (๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ ์„ค๊ณ„)
  • PDF: Laravel Snappy (wkhtmltopdf) ๋˜๋Š” DOMPDF
  • QR Code: SimpleSoftwareIO/simple-qrcode
  • Storage: Laravel Storage (S3/๋กœ์ปฌ)
  • Queue: Redis (์ด๋ฉ”์ผ ๋น„๋™๊ธฐ ๋ฐœ์†ก)

์˜ˆ์ƒ ํ•™์Šต ํฌ์ธํŠธ

  • ๋ณต์žกํ•œ DB ์„ค๊ณ„ (Organizations, Certificates, Templates, Users ๊ด€๊ณ„)
  • Queue๋ฅผ ํ™œ์šฉํ•œ ๋น„๋™๊ธฐ ์ž‘์—… (์ด๋ฉ”์ผ, PDF ์ƒ์„ฑ)
  • ํŒŒ์ผ ์—…๋กœ๋“œ & ์Šคํ† ๋ฆฌ์ง€ ๊ด€๋ฆฌ
  • PDF ๋™์  ์ƒ์„ฑ (Blade ๋ทฐ๋ฅผ PDF๋กœ ๋ณ€ํ™˜)
  • QR ์ฝ”๋“œ ์ƒ์„ฑ ๋ฐ ๊ณต๊ฐœ ๊ฒ€์ฆ ๋กœ์ง
  • Laravel Livewire๋ฅผ ํ™œ์šฉํ•œ SPA ๊ฐ™์€ UX
  • Middleware๋ฅผ ํ†ตํ•œ ๊ถŒํ•œ ๊ด€๋ฆฌ (๋ฐœ๊ธ‰๊ธฐ๊ด€/์ˆ˜๋ฃŒ์ƒ ๋ถ„๋ฆฌ)
  • ์ด๋ฉ”์ผ ํ…œํ”Œ๋ฆฟ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•
  • Scheduler๋ฅผ ํ™œ์šฉํ•œ ์ž๋™ํ™” (๋งŒ๋ฃŒ ์•Œ๋ฆผ)

์ œ์•ฝ ์กฐ๊ฑด

  • โ€ข Laravel 13.x ์ตœ์‹  ๋ฒ„์ „ ์‚ฌ์šฉ ํ•„์ˆ˜
  • โ€ข Blade ํ…œํ”Œ๋ฆฟ ์—”์ง„ ์‚ฌ์šฉ (React/Vue ๊ธˆ์ง€)
  • โ€ข Livewire 3.x๋กœ ๋™์  UI ๊ตฌํ˜„
  • โ€ข Tailwind CSS 4.x๋กœ ์Šคํƒ€์ผ๋ง
  • โ€ข MySQL ๋˜๋Š” PostgreSQL ์‚ฌ์šฉ (SQLite ๊ธˆ์ง€, ํ”„๋กœ๋•์…˜ ์ˆ˜์ค€ DB ํ•„์š”)
  • โ€ข Queue ์‹œ์Šคํ…œ ํ•„์ˆ˜ ๊ตฌํ˜„ (Redis ๊ถŒ์žฅ)
  • โ€ข PDF ์ƒ์„ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ†ตํ•ฉ (Snappy ๋˜๋Š” DOMPDF)
  • โ€ข QR ์ฝ”๋“œ ์ƒ์„ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ†ตํ•ฉ
  • โ€ข ํŒŒ์ผ ์—…๋กœ๋“œ๋Š” ๋ฐ˜๋“œ์‹œ Laravel Storage ์‚ฌ์šฉ
  • โ€ข ์ด๋ฉ”์ผ ๋ฐœ์†ก์€ Queue๋กœ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ
  • โ€ข ๊ณต๊ฐœ ๊ฒ€์ฆ ํŽ˜์ด์ง€๋Š” ์ธ์ฆ ์—†์ด ์ ‘๊ทผ ๊ฐ€๋Šฅํ•ด์•ผ ํ•จ
  • โ€ข ๊ด€๋ฆฌ์ž์™€ ์ˆ˜๋ฃŒ์ƒ ๊ถŒํ•œ ๋ถ„๋ฆฌ (Middleware/Policy ์‚ฌ์šฉ)
  • โ€ข ๋ฐ˜์‘ํ˜• ๋””์ž์ธ ํ•„์ˆ˜ (๋ชจ๋ฐ”์ผ ์นœํ™”์ )
  • โ€ข ๋ณด์•ˆ: CSRF ๋ณดํ˜ธ, SQL Injection ๋ฐฉ์ง€, XSS ๋ฐฉ์ง€
  • โ€ข ํ”„๋กœ๋•์…˜ ๋ ˆ๋””: ์—๋Ÿฌ ํ•ธ๋“ค๋ง, ๋กœ๊น…, ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ด€๋ฆฌ

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

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

1
Step 1
mkdir digital-badge-certification-platform && cd digital-badge-certification-platform

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

# ๋‹จ๊ณ„ 1: Laravel ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐ ์„ค์ • ๋ฐ ์ธ์ฆ ์‹œ์Šคํ…œ ๊ตฌ์ถ•

Laravel 13.x ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ๋‹ค์Œ ์š”๊ตฌ์‚ฌํ•ญ์— ๋งž์ถฐ ์ดˆ๊ธฐ ์„ค์ •์„ ์™„๋ฃŒํ•˜์„ธ์š”:

## 1. ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ ๋ฐ ๊ธฐ๋ณธ ์„ค์ •
- Laravel 13.x ์ตœ์‹  ๋ฒ„์ „์œผ๋กœ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
- `.env` ํŒŒ์ผ ์„ค์ • (MySQL/PostgreSQL DB ์—ฐ๊ฒฐ)
- Tailwind CSS 4.x ์„ค์น˜ ๋ฐ Vite ์„ค์ •
- Livewire 3.x ์„ค์น˜ ๋ฐ ์„ค์ •

## 2. ์ธ์ฆ ์‹œ์Šคํ…œ ๊ตฌ์ถ•
- Laravel Breeze (Blade + Livewire ์Šคํƒ) ์„ค์น˜
- ์‚ฌ์šฉ์ž ์—ญํ•  ์‹œ์Šคํ…œ ์ถ”๊ฐ€:
  - `users` ํ…Œ์ด๋ธ”์— `role` ์ปฌ๋Ÿผ ์ถ”๊ฐ€ (enum: 'admin', 'student')
  - ๊ด€๋ฆฌ์ž๋Š” ์ž๊ฒฉ์ฆ ๋ฐœ๊ธ‰ ๊ธฐ๊ด€ ๋‹ด๋‹น์ž
  - ํ•™์ƒ์€ ์ž๊ฒฉ์ฆ ์ˆ˜๋ฃŒ์ƒ
- ํšŒ์›๊ฐ€์ž… ์‹œ ๊ธฐ๋ณธ ์—ญํ• ์€ 'student'๋กœ ์„ค์ •
- ์—ญํ• ๋ณ„ ์ ‘๊ทผ ์ œ์–ด๋ฅผ ์œ„ํ•œ Middleware ์ƒ์„ฑ:
  - `AdminMiddleware`: ๊ด€๋ฆฌ์ž๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅ
  - `StudentMiddleware`: ํ•™์ƒ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅ

## 3. ๊ธฐ๋ณธ ๋ผ์šฐํŠธ ๊ตฌ์กฐ
- ๊ณต๊ฐœ ๋ผ์šฐํŠธ: ํ™ˆํŽ˜์ด์ง€, ๋กœ๊ทธ์ธ, ํšŒ์›๊ฐ€์ž…, ์ž๊ฒฉ์ฆ ๊ฒ€์ฆ ํŽ˜์ด์ง€
- ๊ด€๋ฆฌ์ž ๋ผ์šฐํŠธ: `/admin/*` (AdminMiddleware ์ ์šฉ)
- ํ•™์ƒ ๋ผ์šฐํŠธ: `/dashboard`, `/my-certificates` (auth ๋ฏธ๋“ค์›จ์–ด)

## 4. ๋ ˆ์ด์•„์›ƒ ๋ฐ ๋„ค๋น„๊ฒŒ์ด์…˜
- Blade ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ ์ƒ์„ฑ (`layouts/app.blade.php`)
- Tailwind CSS๋กœ ๊ธฐ๋ณธ ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฐ” ๊ตฌํ˜„:
  - ๋กœ๊ณ 
  - ๋กœ๊ทธ์ธ/ํšŒ์›๊ฐ€์ž… ๋งํฌ (๋น„๋กœ๊ทธ์ธ ์‹œ)
  - ์—ญํ• ๋ณ„ ๋ฉ”๋‰ด (๋กœ๊ทธ์ธ ์‹œ)
    - ๊ด€๋ฆฌ์ž: ๋Œ€์‹œ๋ณด๋“œ, ํ…œํ”Œ๋ฆฟ ๊ด€๋ฆฌ, ์ž๊ฒฉ์ฆ ๋ฐœ๊ธ‰
    - ํ•™์ƒ: ๋‚ด ์ž๊ฒฉ์ฆ, ํ”„๋กœํ•„
  - ๋กœ๊ทธ์•„์›ƒ ๋ฒ„ํŠผ

## 5. ํ™ˆํŽ˜์ด์ง€ ๊ตฌํ˜„
- ๋žœ๋”ฉ ํŽ˜์ด์ง€ (`welcome.blade.php`) ์ƒ์„ฑ
- ์„œ๋น„์Šค ์†Œ๊ฐœ ์„น์…˜
- ์ฃผ์š” ๊ธฐ๋Šฅ ์นด๋“œ (์ž๊ฒฉ์ฆ ๋ฐœ๊ธ‰, ๊ฒ€์ฆ, ๊ณต๊ฐœ ํ”„๋กœํ•„)
- CTA ๋ฒ„ํŠผ (ํšŒ์›๊ฐ€์ž…, ์ž๊ฒฉ์ฆ ๊ฒ€์ฆํ•˜๊ธฐ)

์™„๋ฃŒ ํ›„ `php artisan serve`๋กœ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๊ณ , ํšŒ์›๊ฐ€์ž…/๋กœ๊ทธ์ธ์ด ์ •์ƒ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: Laravel 13.x ํ”„๋กœ์ ํŠธ๊ฐ€ ์ƒ์„ฑ๋˜๊ณ , Livewire + Tailwind CSS๊ฐ€ ์„ค์น˜๋˜๋ฉฐ, ์—ญํ•  ๊ธฐ๋ฐ˜ ์ธ์ฆ ์‹œ์Šคํ…œ์ด ๊ตฌ์ถ•๋ฉ๋‹ˆ๋‹ค. ๊ด€๋ฆฌ์ž์™€ ํ•™์ƒ์„ ๊ตฌ๋ถ„ํ•˜๋Š” Middleware๊ฐ€ ์ƒ์„ฑ๋˜๊ณ , ๊ธฐ๋ณธ ๋ ˆ์ด์•„์›ƒ๊ณผ ํ™ˆํŽ˜์ด์ง€๊ฐ€ ์™„์„ฑ๋ฉ๋‹ˆ๋‹ค.

2
Step 2
# ๋‹จ๊ณ„ 2: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„ ๋ฐ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜

์ž๊ฒฉ์ฆ ๋ฐœ๊ธ‰ ์‹œ์Šคํ…œ์˜ ํ•ต์‹ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์„ค๊ณ„ํ•˜๊ณ  ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์ƒ์„ฑํ•˜์„ธ์š”.

## 1. Organizations ํ…Œ์ด๋ธ” (๋ฐœ๊ธ‰ ๊ธฐ๊ด€)
- `id`: Primary Key
- `name`: ๊ธฐ๊ด€๋ช… (string, ํ•„์ˆ˜)
- `logo`: ๋กœ๊ณ  ์ด๋ฏธ์ง€ ๊ฒฝ๋กœ (string, nullable)
- `description`: ๊ธฐ๊ด€ ์†Œ๊ฐœ (text, nullable)
- `created_at`, `updated_at`

## 2. Certificate_Templates ํ…Œ์ด๋ธ” (์ž๊ฒฉ์ฆ ํ…œํ”Œ๋ฆฟ)
- `id`: Primary Key
- `organization_id`: Foreign Key (organizations)
- `name`: ํ…œํ”Œ๋ฆฟ๋ช… (์˜ˆ: "์›น ๊ฐœ๋ฐœ ๊ธฐ์ดˆ ๊ณผ์ •")
- `description`: ๊ณผ์ • ์„ค๋ช… (text)
- `design_config`: JSON ํ•„๋“œ (์ƒ‰์ƒ, ํฐํŠธ, ๋ ˆ์ด์•„์›ƒ ์„ค์ •)
- `duration_months`: ์œ ํšจ ๊ธฐ๊ฐ„ (integer, nullable, ์˜ˆ: 12๊ฐœ์›”)
- `is_active`: ํ™œ์„ฑํ™” ์—ฌ๋ถ€ (boolean, ๊ธฐ๋ณธ๊ฐ’ true)
- `created_at`, `updated_at`

## 3. Certificates ํ…Œ์ด๋ธ” (๋ฐœ๊ธ‰๋œ ์ž๊ฒฉ์ฆ)
- `id`: Primary Key
- `certificate_number`: ๊ณ ์œ  ์ธ์ฆ๋ฒˆํ˜ธ (UUID, unique, indexed)
- `template_id`: Foreign Key (certificate_templates)
- `user_id`: Foreign Key (users, ์ˆ˜๋ฃŒ์ƒ)
- `issued_at`: ๋ฐœ๊ธ‰์ผ (timestamp)
- `expires_at`: ๋งŒ๋ฃŒ์ผ (timestamp, nullable)
- `pdf_path`: PDF ํŒŒ์ผ ๊ฒฝ๋กœ (string)
- `qr_code_path`: QR ์ฝ”๋“œ ์ด๋ฏธ์ง€ ๊ฒฝ๋กœ (string)
- `verification_hash`: ๊ฒ€์ฆ์šฉ ํ•ด์‹œ (string, ์œ„์กฐ ๋ฐฉ์ง€)
- `status`: ์ƒํƒœ (enum: 'active', 'expired', 'revoked')
- `created_at`, `updated_at`

## 4. User_Profiles ํ…Œ์ด๋ธ” (์ˆ˜๋ฃŒ์ƒ ๊ณต๊ฐœ ํ”„๋กœํ•„)
- `id`: Primary Key
- `user_id`: Foreign Key (users, unique)
- `username`: ๊ณ ์œ  ์‚ฌ์šฉ์ž๋ช… (string, unique, indexed)
- `bio`: ์ž๊ธฐ์†Œ๊ฐœ (text, nullable)
- `avatar`: ํ”„๋กœํ•„ ์‚ฌ์ง„ ๊ฒฝ๋กœ (string, nullable)
- `linkedin_url`, `github_url`, `website_url`: ์†Œ์…œ ๋งํฌ (nullable)
- `is_public`: ํ”„๋กœํ•„ ๊ณต๊ฐœ ์—ฌ๋ถ€ (boolean, ๊ธฐ๋ณธ๊ฐ’ false)
- `created_at`, `updated_at`

## 5. Eloquent ๋ชจ๋ธ ์ƒ์„ฑ
- `Organization`, `CertificateTemplate`, `Certificate`, `UserProfile` ๋ชจ๋ธ ์ƒ์„ฑ
- ๊ด€๊ณ„ ์„ค์ •:
  - Organization hasMany CertificateTemplates
  - CertificateTemplate hasMany Certificates
  - User hasMany Certificates
  - User hasOne UserProfile
  - Certificate belongsTo User, belongsTo CertificateTemplate

## 6. ์‹œ๋” ์ƒ์„ฑ
- ํ…Œ์ŠคํŠธ์šฉ ๊ด€๋ฆฌ์ž ๊ณ„์ • ์ƒ์„ฑ (email: admin@example.com, role: admin)
- ํ…Œ์ŠคํŠธ์šฉ ํ•™์ƒ ๊ณ„์ • ์ƒ์„ฑ (email: student@example.com, role: student)
- ์ƒ˜ํ”Œ Organization 1๊ฐœ ์ƒ์„ฑ
- ์ƒ˜ํ”Œ CertificateTemplate 2๊ฐœ ์ƒ์„ฑ

๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์‹คํ–‰ํ•˜๊ณ  ์‹œ๋”๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: Organizations, CertificateTemplates, Certificates, UserProfiles ํ…Œ์ด๋ธ”์ด ์ƒ์„ฑ๋˜๊ณ , Eloquent ๋ชจ๋ธ๊ณผ ๊ด€๊ณ„๊ฐ€ ์ •์˜๋ฉ๋‹ˆ๋‹ค. ์‹œ๋”๋ฅผ ํ†ตํ•ด ํ…Œ์ŠคํŠธ ๊ณ„์ •๊ณผ ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ๊ฐ€ DB์— ์‚ฝ์ž…๋ฉ๋‹ˆ๋‹ค.

3
Step 3
# ๋‹จ๊ณ„ 3: ๊ด€๋ฆฌ์ž ๋Œ€์‹œ๋ณด๋“œ - ์ž๊ฒฉ์ฆ ํ…œํ”Œ๋ฆฟ ๊ด€๋ฆฌ (Livewire)

๊ด€๋ฆฌ์ž๊ฐ€ ์ž๊ฒฉ์ฆ ํ…œํ”Œ๋ฆฟ์„ ์ƒ์„ฑ, ์ˆ˜์ •, ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋Š” CRUD ์‹œ์Šคํ…œ์„ Livewire๋กœ ๊ตฌํ˜„ํ•˜์„ธ์š”.

## 1. ๊ด€๋ฆฌ์ž ๋Œ€์‹œ๋ณด๋“œ ๋ผ์šฐํŠธ ๋ฐ ์ปจํŠธ๋กค๋Ÿฌ
- `/admin/dashboard`: ๊ด€๋ฆฌ์ž ํ™ˆ (๋ฐœ๊ธ‰ ํ†ต๊ณ„ ์š”์•ฝ)
- `/admin/templates`: ํ…œํ”Œ๋ฆฟ ๋ชฉ๋ก
- `/admin/templates/create`: ํ…œํ”Œ๋ฆฟ ์ƒ์„ฑ ํผ
- `/admin/templates/{id}/edit`: ํ…œํ”Œ๋ฆฟ ์ˆ˜์ • ํผ

## 2. Livewire ์ปดํฌ๋„ŒํŠธ: TemplateList
- ํ…œํ”Œ๋ฆฟ ๋ชฉ๋ก์„ ํ…Œ์ด๋ธ”๋กœ ํ‘œ์‹œ
- ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ (ํ…œํ”Œ๋ฆฟ๋ช…์œผ๋กœ ์‹ค์‹œ๊ฐ„ ํ•„ํ„ฐ๋ง)
- ์ •๋ ฌ ๊ธฐ๋Šฅ (์ƒ์„ฑ์ผ ๊ธฐ์ค€)
- ํ™œ์„ฑํ™”/๋น„ํ™œ์„ฑํ™” ํ† ๊ธ€ ๋ฒ„ํŠผ
- ์ˆ˜์ •/์‚ญ์ œ ๋ฒ„ํŠผ
- ์‚ญ์ œ ์‹œ ํ™•์ธ ๋ชจ๋‹ฌ (Livewire Wire:confirm ์‚ฌ์šฉ)

## 3. Livewire ์ปดํฌ๋„ŒํŠธ: TemplateForm
- ํ…œํ”Œ๋ฆฟ ์ƒ์„ฑ/์ˆ˜์ • ํผ
- ์ž…๋ ฅ ํ•„๋“œ:
  - ํ…œํ”Œ๋ฆฟ๋ช… (required)
  - ๊ณผ์ • ์„ค๋ช… (textarea, required)
  - ์œ ํšจ ๊ธฐ๊ฐ„ (๊ฐœ์›” ์ˆ˜, nullable)
  - ๋””์ž์ธ ์„ค์ • (JSON, ์ผ๋‹จ ํ…์ŠคํŠธ ํ•„๋“œ๋กœ ๊ฐ„๋‹จํžˆ ๊ตฌํ˜„)
- ์‹ค์‹œ๊ฐ„ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ (Livewire ์‹ค์‹œ๊ฐ„ validation)
- ์„ฑ๊ณต ์‹œ ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ

## 4. UI ๋””์ž์ธ
- Tailwind CSS๋กœ ๊น”๋”ํ•œ ๊ด€๋ฆฌ์ž ํŒจ๋„ ๋””์ž์ธ
- ์‚ฌ์ด๋“œ๋ฐ”: ํ…œํ”Œ๋ฆฟ ๊ด€๋ฆฌ, ์ž๊ฒฉ์ฆ ๋ฐœ๊ธ‰, ํ†ต๊ณ„
- ๋ฉ”์ธ ์˜์—ญ: ์ปจํ…์ธ  ํ‘œ์‹œ
- ๋ฐ˜์‘ํ˜• ๋””์ž์ธ (๋ชจ๋ฐ”์ผ์—์„œ๋Š” ์‚ฌ์ด๋“œ๋ฐ” ํ† ๊ธ€)

## 5. ๊ถŒํ•œ ํ™•์ธ
- AdminMiddleware๊ฐ€ ๋ชจ๋“  `/admin/*` ๋ผ์šฐํŠธ์— ์ ์šฉ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธ
- ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž๊ฐ€ ์ ‘๊ทผ ์‹œ 403 ์—๋Ÿฌ ๋˜๋Š” ํ™ˆ์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ

์™„๋ฃŒ ํ›„ ๊ด€๋ฆฌ์ž ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธํ•˜์—ฌ ํ…œํ”Œ๋ฆฟ์„ ์ƒ์„ฑ/์ˆ˜์ •/์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ…Œ์ŠคํŠธํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ๊ด€๋ฆฌ์ž ๋Œ€์‹œ๋ณด๋“œ๊ฐ€ ๊ตฌ์ถ•๋˜๊ณ , Livewire๋ฅผ ํ™œ์šฉํ•œ ํ…œํ”Œ๋ฆฟ CRUD ์‹œ์Šคํ…œ์ด ์™„์„ฑ๋ฉ๋‹ˆ๋‹ค. ์‹ค์‹œ๊ฐ„ ๊ฒ€์ƒ‰, ์ •๋ ฌ, ํ† ๊ธ€, ์‚ญ์ œ ํ™•์ธ ๋ชจ๋‹ฌ์ด ์ž‘๋™ํ•˜๋ฉฐ, ๊น”๋”ํ•œ UI๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

4
Step 4
# ๋‹จ๊ณ„ 4: ์ž๊ฒฉ์ฆ ๋ฐœ๊ธ‰ ์‹œ์Šคํ…œ - PDF ์ƒ์„ฑ ๋ฐ QR ์ฝ”๋“œ ํ†ตํ•ฉ

๊ด€๋ฆฌ์ž๊ฐ€ ์ˆ˜๋ฃŒ์ƒ์—๊ฒŒ ์ž๊ฒฉ์ฆ์„ ๋ฐœ๊ธ‰ํ•˜๊ณ , PDF์™€ QR ์ฝ”๋“œ๋ฅผ ์ž๋™ ์ƒ์„ฑํ•˜๋Š” ์‹œ์Šคํ…œ์„ ๊ตฌํ˜„ํ•˜์„ธ์š”.

## 1. ํŒจํ‚ค์ง€ ์„ค์น˜
- PDF ์ƒ์„ฑ: `composer require barryvdh/laravel-dompdf` ๋˜๋Š” `composer require barryvdh/laravel-snappy` (wkhtmltopdf ์„ค์น˜ ํ•„์š”)
- QR ์ฝ”๋“œ: `composer require simplesoftwareio/simple-qrcode`

## 2. ์ž๊ฒฉ์ฆ ๋ฐœ๊ธ‰ ํผ (Livewire)
- `/admin/certificates/issue`: ๋ฐœ๊ธ‰ ํŽ˜์ด์ง€
- ์ž…๋ ฅ ํ•„๋“œ:
  - ํ…œํ”Œ๋ฆฟ ์„ ํƒ (๋“œ๋กญ๋‹ค์šด)
  - ์ˆ˜๋ฃŒ์ƒ ์ด๋ฉ”์ผ ๋˜๋Š” ์‚ฌ์šฉ์ž ๊ฒ€์ƒ‰ (Livewire ์‹ค์‹œ๊ฐ„ ๊ฒ€์ƒ‰)
  - ๋ฐœ๊ธ‰์ผ (๊ธฐ๋ณธ๊ฐ’: ์˜ค๋Š˜)
  - ๋งŒ๋ฃŒ์ผ (์ž๋™ ๊ณ„์‚ฐ: ๋ฐœ๊ธ‰์ผ + ํ…œํ”Œ๋ฆฟ ์œ ํšจ๊ธฐ๊ฐ„)
- ๋ฐœ๊ธ‰ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ:
  - Certificate ๋ ˆ์ฝ”๋“œ ์ƒ์„ฑ (๊ณ ์œ  ์ธ์ฆ๋ฒˆํ˜ธ UUID ์ƒ์„ฑ)
  - PDF ์ƒ์„ฑ (Blade ๋ทฐ๋ฅผ PDF๋กœ ๋ณ€ํ™˜)
  - QR ์ฝ”๋“œ ์ƒ์„ฑ (๊ฒ€์ฆ URL ํฌํ•จ: `/verify/{certificate_number}`)
  - ํŒŒ์ผ์„ Storage์— ์ €์žฅ
  - ๊ฒ€์ฆ์šฉ ํ•ด์‹œ ์ƒ์„ฑ (certificate_number + secret_key๋ฅผ SHA256 ํ•ด์‹ฑ)

## 3. PDF ํ…œํ”Œ๋ฆฟ Blade ๋ทฐ
- `resources/views/certificates/pdf-template.blade.php` ์ƒ์„ฑ
- ๋””์ž์ธ:
  - ์ƒ๋‹จ: ๋ฐœ๊ธ‰ ๊ธฐ๊ด€ ๋กœ๊ณ  ๋ฐ ์ด๋ฆ„
  - ์ค‘์•™: "Certificate of Completion" ์ œ๋ชฉ
  - ์ˆ˜๋ฃŒ์ƒ ์ด๋ฆ„ (ํฌ๊ณ  ๊ตต๊ฒŒ)
  - ๊ณผ์ •๋ช… ๋ฐ ์„ค๋ช…
  - ๋ฐœ๊ธ‰์ผ, ์ธ์ฆ๋ฒˆํ˜ธ
  - ํ•˜๋‹จ: QR ์ฝ”๋“œ (์šฐ์ธก ํ•˜๋‹จ)
  - ์„œ๋ช…๋ž€ (๋ฐœ๊ธ‰ ๊ธฐ๊ด€ ๋‹ด๋‹น์ž ์ด๋ฆ„)
- Tailwind CSS ์ธ๋ผ์ธ ์Šคํƒ€์ผ ์‚ฌ์šฉ (PDF์—์„œ๋Š” ์™ธ๋ถ€ CSS ์ œํ•œ์ )

## 4. QR ์ฝ”๋“œ ์ƒ์„ฑ
- QR ์ฝ”๋“œ์— ํฌํ•จํ•  URL: `https://yourdomain.com/verify/{certificate_number}`
- QR ์ฝ”๋“œ๋ฅผ PNG ์ด๋ฏธ์ง€๋กœ ์ƒ์„ฑํ•˜์—ฌ Storage์— ์ €์žฅ
- PDF์— QR ์ฝ”๋“œ ์ด๋ฏธ์ง€ ์‚ฝ์ž…

## 5. ๋ฐœ๊ธ‰ ์ด๋ ฅ ํŽ˜์ด์ง€
- `/admin/certificates`: ๋ฐœ๊ธ‰๋œ ์ž๊ฒฉ์ฆ ๋ชฉ๋ก
- ํ…Œ์ด๋ธ”: ์ˆ˜๋ฃŒ์ƒ ์ด๋ฆ„, ํ…œํ”Œ๋ฆฟ๋ช…, ๋ฐœ๊ธ‰์ผ, ์ƒํƒœ, PDF ๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ
- ๊ฒ€์ƒ‰ ๋ฐ ํ•„ํ„ฐ (Livewire)

์™„๋ฃŒ ํ›„ ๊ด€๋ฆฌ์ž ๊ณ„์ •์œผ๋กœ ํ…Œ์ŠคํŠธ ์ž๊ฒฉ์ฆ์„ ๋ฐœ๊ธ‰ํ•˜๊ณ , PDF์™€ QR ์ฝ”๋“œ๊ฐ€ ์ •์ƒ ์ƒ์„ฑ๋˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ๊ด€๋ฆฌ์ž๊ฐ€ ์ž๊ฒฉ์ฆ์„ ๋ฐœ๊ธ‰ํ•˜๋ฉด PDF์™€ QR ์ฝ”๋“œ๊ฐ€ ์ž๋™ ์ƒ์„ฑ๋˜๊ณ  Storage์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. PDF์—๋Š” ์ˆ˜๋ฃŒ์ƒ ์ •๋ณด, ๊ณผ์ •๋ช…, QR ์ฝ”๋“œ๊ฐ€ ํฌํ•จ๋˜๋ฉฐ, ๋ฐœ๊ธ‰ ์ด๋ ฅ ํŽ˜์ด์ง€์—์„œ ํ™•์ธ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

5
Step 5
# ๋‹จ๊ณ„ 5: ๊ณต๊ฐœ ์ž๊ฒฉ์ฆ ๊ฒ€์ฆ ์‹œ์Šคํ…œ

๋ˆ„๊ตฌ๋‚˜ ์ธ์ฆ๋ฒˆํ˜ธ ๋˜๋Š” QR ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์ž๊ฒฉ์ฆ์˜ ์ง„์œ„๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๊ณต๊ฐœ ๊ฒ€์ฆ ํŽ˜์ด์ง€๋ฅผ ๊ตฌํ˜„ํ•˜์„ธ์š”.

## 1. ๊ฒ€์ฆ ํŽ˜์ด์ง€ ๋ผ์šฐํŠธ
- `/verify`: ๊ฒ€์ฆ ํ™ˆํŽ˜์ด์ง€ (์ธ์ฆ๋ฒˆํ˜ธ ์ž…๋ ฅ ํผ)
- `/verify/{certificate_number}`: QR ์ฝ”๋“œ ์Šค์บ” ์‹œ ์ž๋™ ์ด๋™ํ•˜๋Š” ๊ฒ€์ฆ ๊ฒฐ๊ณผ ํŽ˜์ด์ง€

## 2. ๊ฒ€์ฆ ํ™ˆํŽ˜์ด์ง€ (`/verify`)
- Blade ๋ทฐ: `resources/views/verify/index.blade.php`
- ์ธ์ฆ๋ฒˆํ˜ธ ์ž…๋ ฅ ํผ (UUID ํ˜•์‹)
- "๊ฒ€์ฆํ•˜๊ธฐ" ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ `/verify/{certificate_number}`๋กœ ์ด๋™
- ๋””์ž์ธ: ๊น”๋”ํ•˜๊ณ  ์‹ ๋ขฐ๊ฐ ์žˆ๋Š” UI (Tailwind CSS)

## 3. ๊ฒ€์ฆ ๊ฒฐ๊ณผ ํŽ˜์ด์ง€ (`/verify/{certificate_number}`)
- ์ปจํŠธ๋กค๋Ÿฌ: `VerificationController@show`
- ๋กœ์ง:
  - DB์—์„œ certificate_number๋กœ ์ž๊ฒฉ์ฆ ์กฐํšŒ
  - ๊ฒ€์ฆ์šฉ ํ•ด์‹œ ๋น„๊ต (์œ„์กฐ ์—ฌ๋ถ€ ํ™•์ธ)
  - ์ž๊ฒฉ์ฆ์ด ์กด์žฌํ•˜๊ณ  ํ•ด์‹œ๊ฐ€ ์ผ์น˜ํ•˜๋ฉด "์œ ํšจํ•œ ์ž๊ฒฉ์ฆ"
  - ์กด์žฌํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ํ•ด์‹œ ๋ถˆ์ผ์น˜ ์‹œ "์œ ํšจํ•˜์ง€ ์•Š์€ ์ž๊ฒฉ์ฆ" ๊ฒฝ๊ณ 
- ํ‘œ์‹œ ์ •๋ณด:
  - ์ˆ˜๋ฃŒ์ƒ ์ด๋ฆ„
  - ๊ณผ์ •๋ช… ๋ฐ ์„ค๋ช…
  - ๋ฐœ๊ธ‰ ๊ธฐ๊ด€
  - ๋ฐœ๊ธ‰์ผ ๋ฐ ๋งŒ๋ฃŒ์ผ
  - ์ธ์ฆ๋ฒˆํ˜ธ
  - ์ƒํƒœ (์œ ํšจ/๋งŒ๋ฃŒ/์ทจ์†Œ)
  - PDF ๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ (์„ ํƒ์‚ฌํ•ญ)
- ์œ ํšจํ•œ ์ž๊ฒฉ์ฆ: ์ดˆ๋ก์ƒ‰ ๋ฐฐ์ง€ + ์ฒดํฌ ์•„์ด์ฝ˜
- ์œ ํšจํ•˜์ง€ ์•Š์€ ์ž๊ฒฉ์ฆ: ๋นจ๊ฐ„์ƒ‰ ๊ฒฝ๊ณ  + X ์•„์ด์ฝ˜

## 4. QR ์ฝ”๋“œ ์Šค์บ” ํ…Œ์ŠคํŠธ
- ๋ฐœ๊ธ‰๋œ PDF์˜ QR ์ฝ”๋“œ๋ฅผ ์Šค๋งˆํŠธํฐ์œผ๋กœ ์Šค์บ”
- ์ž๋™์œผ๋กœ `/verify/{certificate_number}` ํŽ˜์ด์ง€๋กœ ์ด๋™
- ๊ฒ€์ฆ ๊ฒฐ๊ณผ ํ™•์ธ

## 5. SEO ๋ฐ ๊ณต์œ 
- ๊ฒ€์ฆ ๊ฒฐ๊ณผ ํŽ˜์ด์ง€์— Open Graph ๋ฉ”ํƒ€ ํƒœ๊ทธ ์ถ”๊ฐ€
- ์†Œ์…œ ๋ฏธ๋””์–ด ๊ณต์œ  ์‹œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ตœ์ ํ™”

์™„๋ฃŒ ํ›„ ํ…Œ์ŠคํŠธ ์ž๊ฒฉ์ฆ์˜ QR ์ฝ”๋“œ๋ฅผ ์Šค์บ”ํ•˜๊ฑฐ๋‚˜ ์ธ์ฆ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ๊ฒ€์ฆ์ด ์ •์ƒ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ๊ณต๊ฐœ ๊ฒ€์ฆ ํŽ˜์ด์ง€๊ฐ€ ๊ตฌ์ถ•๋˜๊ณ , ์ธ์ฆ๋ฒˆํ˜ธ ์ž…๋ ฅ ๋˜๋Š” QR ์ฝ”๋“œ ์Šค์บ”์œผ๋กœ ์ž๊ฒฉ์ฆ์˜ ์ง„์œ„๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ ํšจํ•œ ์ž๊ฒฉ์ฆ์€ ์ƒ์„ธ ์ •๋ณด๋ฅผ ํ‘œ์‹œํ•˜๊ณ , ์œ ํšจํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

6
Step 6
# ๋‹จ๊ณ„ 6: ํ•™์ƒ ๋งˆ์ดํŽ˜์ด์ง€ - ๋‚ด ์ž๊ฒฉ์ฆ ๊ด€๋ฆฌ

์ˆ˜๋ฃŒ์ƒ์ด ์ž์‹ ์ด ๋ฐ›์€ ์ž๊ฒฉ์ฆ์„ ํ™•์ธํ•˜๊ณ  ๋‹ค์šด๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋Š” ๋งˆ์ดํŽ˜์ด์ง€๋ฅผ ๊ตฌํ˜„ํ•˜์„ธ์š”.

## 1. ํ•™์ƒ ๋Œ€์‹œ๋ณด๋“œ ๋ผ์šฐํŠธ
- `/dashboard`: ํ•™์ƒ ํ™ˆ (์ž๊ฒฉ์ฆ ๊ฐœ์ˆ˜ ์š”์•ฝ)
- `/my-certificates`: ๋‚ด ์ž๊ฒฉ์ฆ ๋ชฉ๋ก
- `/my-certificates/{id}`: ์ž๊ฒฉ์ฆ ์ƒ์„ธ ๋ณด๊ธฐ

## 2. ์ž๊ฒฉ์ฆ ๋ชฉ๋ก ํŽ˜์ด์ง€ (Livewire)
- Livewire ์ปดํฌ๋„ŒํŠธ: `MyCertificates`
- ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž์˜ ์ž๊ฒฉ์ฆ์„ ๊ทธ๋ฆฌ๋“œ ํ˜•์‹์œผ๋กœ ํ‘œ์‹œ
- ๊ฐ ์นด๋“œ:
  - ๊ณผ์ •๋ช…
  - ๋ฐœ๊ธ‰์ผ
  - ์ƒํƒœ ๋ฐฐ์ง€ (์œ ํšจ/๋งŒ๋ฃŒ)
  - ์ธ๋„ค์ผ (PDF ์ฒซ ํŽ˜์ด์ง€ ์ด๋ฏธ์ง€ ๋˜๋Š” ๊ธฐ๋ณธ ์ด๋ฏธ์ง€)
  - "์ƒ์„ธ ๋ณด๊ธฐ" ๋ฒ„ํŠผ
- ํ•„ํ„ฐ ๊ธฐ๋Šฅ: ์ƒํƒœ๋ณ„ (์ „์ฒด/์œ ํšจ/๋งŒ๋ฃŒ)
- ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ: ๊ณผ์ •๋ช…์œผ๋กœ ์‹ค์‹œ๊ฐ„ ํ•„ํ„ฐ๋ง

## 3. ์ž๊ฒฉ์ฆ ์ƒ์„ธ ํŽ˜์ด์ง€
- Blade ๋ทฐ: `resources/views/certificates/show.blade.php`
- ํ‘œ์‹œ ์ •๋ณด:
  - ๊ณผ์ •๋ช… ๋ฐ ์„ค๋ช…
  - ๋ฐœ๊ธ‰ ๊ธฐ๊ด€
  - ๋ฐœ๊ธ‰์ผ ๋ฐ ๋งŒ๋ฃŒ์ผ
  - ์ธ์ฆ๋ฒˆํ˜ธ
  - QR ์ฝ”๋“œ ์ด๋ฏธ์ง€
- ์•ก์…˜ ๋ฒ„ํŠผ:
  - PDF ๋‹ค์šด๋กœ๋“œ
  - ๊ณต๊ฐœ ๊ฒ€์ฆ ํŽ˜์ด์ง€ ๋งํฌ ๋ณต์‚ฌ
  - ์†Œ์…œ ๋ฏธ๋””์–ด ๊ณต์œ  (LinkedIn, Twitter)

## 4. PDF ๋‹ค์šด๋กœ๋“œ ๊ธฐ๋Šฅ
- ๋ผ์šฐํŠธ: `/certificates/{id}/download`
- ์ปจํŠธ๋กค๋Ÿฌ: Storage์—์„œ PDF ํŒŒ์ผ ์ฝ์–ด์„œ ๋‹ค์šด๋กœ๋“œ ์‘๋‹ต ๋ฐ˜ํ™˜
- ๊ถŒํ•œ ํ™•์ธ: ์ž์‹ ์˜ ์ž๊ฒฉ์ฆ๋งŒ ๋‹ค์šด๋กœ๋“œ ๊ฐ€๋Šฅ (Policy ์‚ฌ์šฉ)

## 5. ์†Œ์…œ ๊ณต์œ  ๋งํฌ ์ƒ์„ฑ
- LinkedIn ๊ณต์œ  URL: `https://www.linkedin.com/sharing/share-offsite/?url={verification_url}`
- Twitter ๊ณต์œ  URL: `https://twitter.com/intent/tweet?text={text}&url={verification_url}`
- ๊ณต์œ  ํ…์ŠคํŠธ: "์ €๋Š” {๊ณผ์ •๋ช…} ์ž๊ฒฉ์ฆ์„ ์ทจ๋“ํ–ˆ์Šต๋‹ˆ๋‹ค! ๐ŸŽ“"

์™„๋ฃŒ ํ›„ ํ•™์ƒ ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธํ•˜์—ฌ ์ž๊ฒฉ์ฆ ๋ชฉ๋ก์„ ํ™•์ธํ•˜๊ณ  PDF๋ฅผ ๋‹ค์šด๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ…Œ์ŠคํŠธํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ํ•™์ƒ ๋งˆ์ดํŽ˜์ด์ง€๊ฐ€ ๊ตฌ์ถ•๋˜๊ณ , ๋ฐ›์€ ์ž๊ฒฉ์ฆ์„ ๊ทธ๋ฆฌ๋“œ ํ˜•์‹์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ƒ์„ธ ํŽ˜์ด์ง€์—์„œ PDF ๋‹ค์šด๋กœ๋“œ, ๊ณต๊ฐœ ๊ฒ€์ฆ ๋งํฌ ๋ณต์‚ฌ, ์†Œ์…œ ๋ฏธ๋””์–ด ๊ณต์œ ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

7
Step 7
# ๋‹จ๊ณ„ 7: ์ˆ˜๋ฃŒ์ƒ ๊ณต๊ฐœ ํ”„๋กœํ•„ ํŽ˜์ด์ง€

์ˆ˜๋ฃŒ์ƒ์ด ์ž์‹ ์˜ ์ž๊ฒฉ์ฆ์„ ์™ธ๋ถ€์— ๊ณต๊ฐœํ•  ์ˆ˜ ์žˆ๋Š” ํฌํŠธํด๋ฆฌ์˜ค ์Šคํƒ€์ผ์˜ ํ”„๋กœํ•„ ํŽ˜์ด์ง€๋ฅผ ๊ตฌํ˜„ํ•˜์„ธ์š”.

## 1. ํ”„๋กœํ•„ ์„ค์ • ํŽ˜์ด์ง€
- `/profile/edit`: ํ”„๋กœํ•„ ํŽธ์ง‘ ํŽ˜์ด์ง€ (Livewire)
- ์ž…๋ ฅ ํ•„๋“œ:
  - ๊ณ ์œ  ์‚ฌ์šฉ์ž๋ช… (username, URL์— ์‚ฌ์šฉ, unique ๊ฒ€์ฆ)
  - ์ž๊ธฐ์†Œ๊ฐœ (bio, textarea)
  - ํ”„๋กœํ•„ ์‚ฌ์ง„ ์—…๋กœ๋“œ (Livewire File Upload)
  - ์†Œ์…œ ๋งํฌ (LinkedIn, GitHub, ์›น์‚ฌ์ดํŠธ)
  - ํ”„๋กœํ•„ ๊ณต๊ฐœ/๋น„๊ณต๊ฐœ ํ† ๊ธ€
- ์‹ค์‹œ๊ฐ„ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ (์‚ฌ์šฉ์ž๋ช… ์ค‘๋ณต ์ฒดํฌ)
- ํ”„๋กœํ•„ ์‚ฌ์ง„์€ Storage์— ์ €์žฅ

## 2. ๊ณต๊ฐœ ํ”„๋กœํ•„ ํŽ˜์ด์ง€
- ๋ผ์šฐํŠธ: `/profile/{username}` (์ธ์ฆ ๋ถˆํ•„์š”, ๋ˆ„๊ตฌ๋‚˜ ์ ‘๊ทผ ๊ฐ€๋Šฅ)
- Blade ๋ทฐ: `resources/views/profiles/show.blade.php`
- ๋ ˆ์ด์•„์›ƒ:
  - ์ƒ๋‹จ: ํ”„๋กœํ•„ ์‚ฌ์ง„, ์ด๋ฆ„, ์ž๊ธฐ์†Œ๊ฐœ
  - ์†Œ์…œ ๋งํฌ ์•„์ด์ฝ˜ (LinkedIn, GitHub, ์›น์‚ฌ์ดํŠธ)
  - ํ•˜๋‹จ: ์ทจ๋“ํ•œ ์ž๊ฒฉ์ฆ ๊ฐค๋Ÿฌ๋ฆฌ (๊ทธ๋ฆฌ๋“œ)
- ์ž๊ฒฉ์ฆ ์นด๋“œ:
  - ๊ณผ์ •๋ช…
  - ๋ฐœ๊ธ‰์ผ
  - ๋ฐœ๊ธ‰ ๊ธฐ๊ด€ ๋กœ๊ณ 
  - "๊ฒ€์ฆํ•˜๊ธฐ" ๋ฒ„ํŠผ (ํด๋ฆญ ์‹œ `/verify/{certificate_number}`๋กœ ์ด๋™)
- ํ”„๋กœํ•„์ด ๋น„๊ณต๊ฐœ์ธ ๊ฒฝ์šฐ 404 ํŽ˜์ด์ง€ ํ‘œ์‹œ

## 3. ํ”„๋กœํ•„ ๊ณต์œ  ๊ธฐ๋Šฅ
- ํ•™์ƒ ๋Œ€์‹œ๋ณด๋“œ์— "๋‚ด ํ”„๋กœํ•„ ๊ณต์œ " ๋ฒ„ํŠผ ์ถ”๊ฐ€
- ํด๋ฆญ ์‹œ ๊ณต๊ฐœ ํ”„๋กœํ•„ URL ๋ณต์‚ฌ (Clipboard API)
- ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€: "ํ”„๋กœํ•„ ๋งํฌ๊ฐ€ ๋ณต์‚ฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!"

## 4. SEO ์ตœ์ ํ™”
- ๊ณต๊ฐœ ํ”„๋กœํ•„ ํŽ˜์ด์ง€์— ๋ฉ”ํƒ€ ํƒœ๊ทธ ์ถ”๊ฐ€
- Open Graph ํƒœ๊ทธ: ํ”„๋กœํ•„ ์‚ฌ์ง„, ์ด๋ฆ„, ์ž๊ธฐ์†Œ๊ฐœ
- Twitter Card ํƒœ๊ทธ

## 5. ๋””์ž์ธ
- ๊น”๋”ํ•˜๊ณ  ์ „๋ฌธ์ ์ธ ํฌํŠธํด๋ฆฌ์˜ค ์Šคํƒ€์ผ
- ๋ฐ˜์‘ํ˜• ๋””์ž์ธ (๋ชจ๋ฐ”์ผ ์นœํ™”์ )
- Tailwind CSS๋กœ ๊ทธ๋ผ๋ฐ์ด์…˜ ๋ฐฐ๊ฒฝ, ์นด๋“œ ๊ทธ๋ฆผ์ž ํšจ๊ณผ ๋“ฑ ํ™œ์šฉ

์™„๋ฃŒ ํ›„ ํ”„๋กœํ•„์„ ๊ณต๊ฐœ๋กœ ์„ค์ •ํ•˜๊ณ  `/profile/{username}` URL์„ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์—ด์–ด ์ •์ƒ ํ‘œ์‹œ๋˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ์ˆ˜๋ฃŒ์ƒ์˜ ๊ณต๊ฐœ ํ”„๋กœํ•„ ํŽ˜์ด์ง€๊ฐ€ ๊ตฌ์ถ•๋˜๊ณ , ์ž๊ฒฉ์ฆ ๊ฐค๋Ÿฌ๋ฆฌ๊ฐ€ ํฌํŠธํด๋ฆฌ์˜ค ์Šคํƒ€์ผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ํ”„๋กœํ•„ ํŽธ์ง‘ ํŽ˜์ด์ง€์—์„œ ์‚ฌ์šฉ์ž๋ช…, ์ž๊ธฐ์†Œ๊ฐœ, ์†Œ์…œ ๋งํฌ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ณต๊ฐœ/๋น„๊ณต๊ฐœ ์„ค์ •์ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

8
Step 8
# ๋‹จ๊ณ„ 8: ์ด๋ฉ”์ผ ์ž๋™ ๋ฐœ์†ก ์‹œ์Šคํ…œ (Queue)

์ž๊ฒฉ์ฆ ๋ฐœ๊ธ‰ ์‹œ ์ˆ˜๋ฃŒ์ƒ์—๊ฒŒ ์ž๋™์œผ๋กœ ์ด๋ฉ”์ผ์„ ๋ฐœ์†กํ•˜๋Š” ๋น„๋™๊ธฐ ์‹œ์Šคํ…œ์„ ๊ตฌํ˜„ํ•˜์„ธ์š”.

## 1. Queue ์„ค์ •
- `.env` ํŒŒ์ผ์—์„œ `QUEUE_CONNECTION=database` ์„ค์ • (๋˜๋Š” Redis ์‚ฌ์šฉ)
- Queue ํ…Œ์ด๋ธ” ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ƒ์„ฑ: `php artisan queue:table`
- ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹คํ–‰

## 2. Mailable ํด๋ž˜์Šค ์ƒ์„ฑ
- `php artisan make:mail CertificateIssued`
- ์ด๋ฉ”์ผ ๋‚ด์šฉ:
  - ์ œ๋ชฉ: "์ถ•ํ•˜ํ•ฉ๋‹ˆ๋‹ค! {๊ณผ์ •๋ช…} ์ž๊ฒฉ์ฆ์ด ๋ฐœ๊ธ‰๋˜์—ˆ์Šต๋‹ˆ๋‹ค."
  - ๋ณธ๋ฌธ:
    - ์ˆ˜๋ฃŒ์ƒ ์ด๋ฆ„ ์ธ์‚ฌ
    - ๊ณผ์ •๋ช… ๋ฐ ๋ฐœ๊ธ‰์ผ
    - ์ธ์ฆ๋ฒˆํ˜ธ
    - PDF ๋‹ค์šด๋กœ๋“œ ๋งํฌ (๋งˆ์ดํŽ˜์ด์ง€)
    - ๊ณต๊ฐœ ๊ฒ€์ฆ ๋งํฌ
    - ๋ฐœ๊ธ‰ ๊ธฐ๊ด€ ์—ฐ๋ฝ์ฒ˜
- Blade ์ด๋ฉ”์ผ ํ…œํ”Œ๋ฆฟ ์ƒ์„ฑ: `resources/views/emails/certificate-issued.blade.php`
- Tailwind CSS ์ธ๋ผ์ธ ์Šคํƒ€์ผ๋กœ ๋””์ž์ธ (์ด๋ฉ”์ผ ํด๋ผ์ด์–ธํŠธ ํ˜ธํ™˜)

## 3. Job ํด๋ž˜์Šค ์ƒ์„ฑ
- `php artisan make:job SendCertificateEmail`
- Job์—์„œ Mailable ์ „์†ก
- PDF ํŒŒ์ผ์„ ์ด๋ฉ”์ผ์— ์ฒจ๋ถ€ (์„ ํƒ์‚ฌํ•ญ)

## 4. ์ž๊ฒฉ์ฆ ๋ฐœ๊ธ‰ ์‹œ Job ๋””์ŠคํŒจ์น˜
- `CertificateController`์˜ ๋ฐœ๊ธ‰ ๋กœ์ง์—์„œ `SendCertificateEmail::dispatch($certificate)` ์ถ”๊ฐ€
- Queue์— Job์ด ์ถ”๊ฐ€๋˜๋Š”์ง€ ํ™•์ธ

## 5. Queue Worker ์‹คํ–‰
- `php artisan queue:work` ๋ช…๋ น์œผ๋กœ Queue Worker ์‹คํ–‰
- Job์ด ์ฒ˜๋ฆฌ๋˜๊ณ  ์ด๋ฉ”์ผ์ด ๋ฐœ์†ก๋˜๋Š”์ง€ ํ™•์ธ
- ๋กœ๊ทธ์—์„œ ์ด๋ฉ”์ผ ๋ฐœ์†ก ํ™•์ธ (๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋Š” Mailtrap ์‚ฌ์šฉ ๊ถŒ์žฅ)

## 6. ์ด๋ฉ”์ผ ๋ฐœ์†ก ์ด๋ ฅ
- `email_logs` ํ…Œ์ด๋ธ” ์ƒ์„ฑ (์„ ํƒ์‚ฌํ•ญ)
- ๋ฐœ์†ก ์„ฑ๊ณต/์‹คํŒจ ๊ธฐ๋ก
- ๊ด€๋ฆฌ์ž ๋Œ€์‹œ๋ณด๋“œ์—์„œ ์ด๋ฉ”์ผ ๋ฐœ์†ก ์ด๋ ฅ ํ™•์ธ

## 7. ์—๋Ÿฌ ํ•ธ๋“ค๋ง
- Job ์‹คํŒจ ์‹œ ์žฌ์‹œ๋„ ์„ค์ • (์ตœ๋Œ€ 3ํšŒ)
- ์‹คํŒจํ•œ Job์€ `failed_jobs` ํ…Œ์ด๋ธ”์— ๊ธฐ๋ก

์™„๋ฃŒ ํ›„ ํ…Œ์ŠคํŠธ ์ž๊ฒฉ์ฆ์„ ๋ฐœ๊ธ‰ํ•˜๊ณ , ์ˆ˜๋ฃŒ์ƒ ์ด๋ฉ”์ผ๋กœ ์ž๋™ ๋ฐœ์†ก๋˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”. Mailtrap ๋˜๋Š” ์‹ค์ œ ์ด๋ฉ”์ผ ๊ณ„์ •์œผ๋กœ ํ…Œ์ŠคํŠธํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ์ž๊ฒฉ์ฆ ๋ฐœ๊ธ‰ ์‹œ Queue๋ฅผ ํ†ตํ•ด ๋น„๋™๊ธฐ๋กœ ์ด๋ฉ”์ผ์ด ๋ฐœ์†ก๋ฉ๋‹ˆ๋‹ค. ์ด๋ฉ”์ผ์—๋Š” ์ถ•ํ•˜ ๋ฉ”์‹œ์ง€, ์ธ์ฆ๋ฒˆํ˜ธ, PDF ๋‹ค์šด๋กœ๋“œ ๋งํฌ, ๊ฒ€์ฆ ๋งํฌ๊ฐ€ ํฌํ•จ๋˜๋ฉฐ, Queue Worker๊ฐ€ ์ •์ƒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

9
Step 9
# ๋‹จ๊ณ„ 9: ์ž๊ฒฉ์ฆ ๋งŒ๋ฃŒ ์•Œ๋ฆผ ๋ฐ ํ†ต๊ณ„ ๋Œ€์‹œ๋ณด๋“œ

์ž๊ฒฉ์ฆ ๋งŒ๋ฃŒ์ผ์ด ๊ฐ€๊นŒ์›Œ์ง„ ์ˆ˜๋ฃŒ์ƒ์—๊ฒŒ ์ž๋™ ์•Œ๋ฆผ์„ ๋ณด๋‚ด๊ณ , ๊ด€๋ฆฌ์ž๊ฐ€ ๋ฐœ๊ธ‰ ํ†ต๊ณ„๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜์„ธ์š”.

## 1. Laravel Scheduler ์„ค์ •
- `app/Console/Kernel.php`์—์„œ ์Šค์ผ€์ค„ ์ •์˜
- ๋งค์ผ ์ž์ •์— ๋งŒ๋ฃŒ ์˜ˆ์ • ์ž๊ฒฉ์ฆ ํ™•์ธ (๋งŒ๋ฃŒ 7์ผ ์ „)
- Artisan Command ์ƒ์„ฑ: `php artisan make:command SendExpirationReminders`
- Command ๋กœ์ง:
  - ๋งŒ๋ฃŒ 7์ผ ์ „์ธ ์ž๊ฒฉ์ฆ ์กฐํšŒ
  - ๊ฐ ์ˆ˜๋ฃŒ์ƒ์—๊ฒŒ ์•Œ๋ฆผ ์ด๋ฉ”์ผ ๋ฐœ์†ก (Queue ์‚ฌ์šฉ)

## 2. ๋งŒ๋ฃŒ ์•Œ๋ฆผ Mailable
- `php artisan make:mail CertificateExpirationReminder`
- ์ด๋ฉ”์ผ ๋‚ด์šฉ:
  - ์ œ๋ชฉ: "์ž๊ฒฉ์ฆ ๋งŒ๋ฃŒ ์•Œ๋ฆผ: {๊ณผ์ •๋ช…} ์ž๊ฒฉ์ฆ์ด 7์ผ ํ›„ ๋งŒ๋ฃŒ๋ฉ๋‹ˆ๋‹ค."
  - ๋ณธ๋ฌธ:
    - ๋งŒ๋ฃŒ์ผ ์•ˆ๋‚ด
    - ์žฌ๋ฐœ๊ธ‰ ๋˜๋Š” ๊ฐฑ์‹  ์•ˆ๋‚ด (ํ•„์š” ์‹œ)
    - ๋ฌธ์˜ ์—ฐ๋ฝ์ฒ˜

## 3. Cron ์„ค์ •
- ์„œ๋ฒ„์—์„œ Laravel Scheduler ์‹คํ–‰: `* * * * * cd /path-to-project && php artisan schedule:run >> /dev/null 2>&1`
- ๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ๋Š” `php artisan schedule:work`๋กœ ํ…Œ์ŠคํŠธ

## 4. ๊ด€๋ฆฌ์ž ํ†ต๊ณ„ ๋Œ€์‹œ๋ณด๋“œ
- `/admin/dashboard`: ํ†ต๊ณ„ ์š”์•ฝ ํŽ˜์ด์ง€
- ํ‘œ์‹œ ์ •๋ณด:
  - ์ด ๋ฐœ๊ธ‰๋œ ์ž๊ฒฉ์ฆ ์ˆ˜
  - ํ™œ์„ฑ ์ž๊ฒฉ์ฆ ์ˆ˜
  - ๋งŒ๋ฃŒ๋œ ์ž๊ฒฉ์ฆ ์ˆ˜
  - ์ตœ๊ทผ 7์ผ๊ฐ„ ๋ฐœ๊ธ‰ ์ถ”์ด (Chart.js ๋ผ์ธ ์ฐจํŠธ)
  - ํ…œํ”Œ๋ฆฟ๋ณ„ ๋ฐœ๊ธ‰ ํ˜„ํ™ฉ (Chart.js ํŒŒ์ด ์ฐจํŠธ)
- Livewire ์ปดํฌ๋„ŒํŠธ๋กœ ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ

## 5. Chart.js ํ†ตํ•ฉ
- `npm install chart.js` ์„ค์น˜
- Blade ๋ทฐ์—์„œ Chart.js ์‚ฌ์šฉ
- ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ํ†ต๊ณ„ ๋ฐ์ดํ„ฐ๋ฅผ JSON์œผ๋กœ ์ „๋‹ฌ
- ๋ฐ˜์‘ํ˜• ์ฐจํŠธ (๋ชจ๋ฐ”์ผ ์ตœ์ ํ™”)

## 6. ํ†ต๊ณ„ ๋ฐ์ดํ„ฐ ์บ์‹ฑ
- ํ†ต๊ณ„ ๋ฐ์ดํ„ฐ๋ฅผ Redis์— ์บ์‹ฑ (1์‹œ๊ฐ„)
- ์บ์‹œ ๋ฌดํšจํ™”: ์ž๊ฒฉ์ฆ ๋ฐœ๊ธ‰ ์‹œ

์™„๋ฃŒ ํ›„ ๊ด€๋ฆฌ์ž ๋Œ€์‹œ๋ณด๋“œ์—์„œ ํ†ต๊ณ„ ์ฐจํŠธ๊ฐ€ ์ •์ƒ ํ‘œ์‹œ๋˜๋Š”์ง€ ํ™•์ธํ•˜๊ณ , Scheduler ๋ช…๋ น์„ ์ˆ˜๋™ ์‹คํ–‰ํ•˜์—ฌ ๋งŒ๋ฃŒ ์•Œ๋ฆผ์ด ๋ฐœ์†ก๋˜๋Š”์ง€ ํ…Œ์ŠคํŠธํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: Laravel Scheduler๋ฅผ ํ†ตํ•ด ๋งค์ผ ์ž๋™์œผ๋กœ ๋งŒ๋ฃŒ ์˜ˆ์ • ์ž๊ฒฉ์ฆ์„ ํ™•์ธํ•˜๊ณ  ์•Œ๋ฆผ ์ด๋ฉ”์ผ์„ ๋ฐœ์†กํ•ฉ๋‹ˆ๋‹ค. ๊ด€๋ฆฌ์ž ๋Œ€์‹œ๋ณด๋“œ์—๋Š” ๋ฐœ๊ธ‰ ํ†ต๊ณ„๊ฐ€ Chart.js๋กœ ์‹œ๊ฐํ™”๋˜๋ฉฐ, ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค.

10
Step 10
# ๋‹จ๊ณ„ 10: ๋ณด์•ˆ ๊ฐ•ํ™”, ํ…Œ์ŠคํŠธ, ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ ์ค€๋น„

ํ”„๋กœ์ ํŠธ์˜ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜๊ณ , ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๋ฉฐ, ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์— ๋ฐฐํฌํ•  ์ค€๋น„๋ฅผ ์™„๋ฃŒํ•˜์„ธ์š”.

## 1. ๋ณด์•ˆ ๊ฐ•ํ™”
- **CSRF ๋ณดํ˜ธ**: ๋ชจ๋“  ํผ์— `@csrf` ํ† ํฐ ํ™•์ธ
- **XSS ๋ฐฉ์ง€**: Blade์—์„œ `{{ }}` ์‚ฌ์šฉ (์ž๋™ ์ด์Šค์ผ€์ดํ”„)
- **SQL Injection ๋ฐฉ์ง€**: Eloquent ORM ์‚ฌ์šฉ (Raw Query ๊ธˆ์ง€)
- **ํŒŒ์ผ ์—…๋กœ๋“œ ๊ฒ€์ฆ**:
  - ํ—ˆ์šฉ๋œ MIME ํƒ€์ž…๋งŒ ์—…๋กœ๋“œ (์ด๋ฏธ์ง€: jpg, png)
  - ํŒŒ์ผ ํฌ๊ธฐ ์ œํ•œ (์ตœ๋Œ€ 2MB)
- **Rate Limiting**:
  - ๊ฒ€์ฆ ํŽ˜์ด์ง€์— Rate Limit ์ ์šฉ (1๋ถ„์— 10ํšŒ)
  - API ๋ผ์šฐํŠธ์— Throttle ๋ฏธ๋“ค์›จ์–ด ์ ์šฉ
- **ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๊ด€๋ฆฌ**:
  - `.env` ํŒŒ์ผ์„ `.gitignore`์— ์ถ”๊ฐ€
  - ๋ฏผ๊ฐํ•œ ์ •๋ณด (API ํ‚ค, DB ๋น„๋ฐ€๋ฒˆํ˜ธ) ์•”ํ˜ธํ™”

## 2. Policy ๋ฐ Authorization
- `CertificatePolicy` ์ƒ์„ฑ
- `view`, `download` ๋ฉ”์„œ๋“œ ์ •์˜ (์ž์‹ ์˜ ์ž๊ฒฉ์ฆ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅ)
- ์ปจํŠธ๋กค๋Ÿฌ์—์„œ `$this->authorize('view', $certificate)` ์‚ฌ์šฉ

## 3. ์—๋Ÿฌ ํ•ธ๋“ค๋ง ๋ฐ ๋กœ๊น…
- 404, 403, 500 ์—๋Ÿฌ ํŽ˜์ด์ง€ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•
- `app/Exceptions/Handler.php`์—์„œ ์—๋Ÿฌ ๋กœ๊น… ์„ค์ •
- Sentry ๋˜๋Š” Bugsnag ์—ฐ๋™ (์„ ํƒ์‚ฌํ•ญ)

## 4. ํ…Œ์ŠคํŠธ ์ž‘์„ฑ
- Feature ํ…Œ์ŠคํŠธ:
  - ์ž๊ฒฉ์ฆ ๋ฐœ๊ธ‰ ํ…Œ์ŠคํŠธ
  - ๊ฒ€์ฆ ํŽ˜์ด์ง€ ํ…Œ์ŠคํŠธ
  - ํ”„๋กœํ•„ ๊ณต๊ฐœ/๋น„๊ณต๊ฐœ ํ…Œ์ŠคํŠธ
- Unit ํ…Œ์ŠคํŠธ:
  - ๊ฒ€์ฆ ํ•ด์‹œ ์ƒ์„ฑ ๋กœ์ง ํ…Œ์ŠคํŠธ
  - UUID ์ƒ์„ฑ ํ…Œ์ŠคํŠธ
- `php artisan test` ์‹คํ–‰ํ•˜์—ฌ ๋ชจ๋“  ํ…Œ์ŠคํŠธ ํ†ต๊ณผ ํ™•์ธ

## 5. ์„ฑ๋Šฅ ์ตœ์ ํ™”
- **Eager Loading**: N+1 ์ฟผ๋ฆฌ ๋ฌธ์ œ ํ•ด๊ฒฐ (`with()` ์‚ฌ์šฉ)
- **์ธ๋ฑ์Šค ์ถ”๊ฐ€**: `certificate_number`, `username` ์ปฌ๋Ÿผ์— ์ธ๋ฑ์Šค
- **์บ์‹ฑ**: ํ†ต๊ณ„ ๋ฐ์ดํ„ฐ, ํ…œํ”Œ๋ฆฟ ๋ชฉ๋ก ์บ์‹ฑ
- **์ด๋ฏธ์ง€ ์ตœ์ ํ™”**: ์—…๋กœ๋“œ๋œ ์ด๋ฏธ์ง€ ์ž๋™ ๋ฆฌ์‚ฌ์ด์ง• (Intervention Image)

## 6. ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ ์ค€๋น„
- `php artisan config:cache` (์„ค์ • ์บ์‹ฑ)
- `php artisan route:cache` (๋ผ์šฐํŠธ ์บ์‹ฑ)
- `php artisan view:cache` (๋ทฐ ์บ์‹ฑ)
- `.env` ํŒŒ์ผ์—์„œ `APP_DEBUG=false`, `APP_ENV=production` ์„ค์ •
- HTTPS ๊ฐ•์ œ ์„ค์ • (AppServiceProvider์—์„œ URL::forceScheme('https'))
- Queue Worker๋ฅผ Supervisor๋กœ ๊ด€๋ฆฌ

## 7. ๋ฌธ์„œํ™”
- `README.md` ์ž‘์„ฑ:
  - ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ
  - ์„ค์น˜ ๋ฐฉ๋ฒ•
  - ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ •
  - ์‹คํ–‰ ๋ช…๋ น์–ด
  - ์Šคํฌ๋ฆฐ์ƒท
- API ๋ฌธ์„œ ์ž‘์„ฑ (ํ•„์š” ์‹œ)

์™„๋ฃŒ ํ›„ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ์ตœ์ข… ํ…Œ์ŠคํŠธํ•˜๊ณ , ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์— ๋ฐฐํฌํ•  ์ค€๋น„๊ฐ€ ์™„๋ฃŒ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ๋ณด์•ˆ์ด ๊ฐ•ํ™”๋˜๊ณ , Policy๋ฅผ ํ†ตํ•œ ๊ถŒํ•œ ๊ด€๋ฆฌ๊ฐ€ ์ ์šฉ๋˜๋ฉฐ, ํ…Œ์ŠคํŠธ๊ฐ€ ์ž‘์„ฑ๋˜์–ด ๋ชจ๋“  ํ…Œ์ŠคํŠธ๊ฐ€ ํ†ต๊ณผํ•ฉ๋‹ˆ๋‹ค. ์„ฑ๋Šฅ ์ตœ์ ํ™”๊ฐ€ ์™„๋ฃŒ๋˜๊ณ , ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ๋ฅผ ์œ„ํ•œ ์บ์‹ฑ ๋ฐ ํ™˜๊ฒฝ ์„ค์ •์ด ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค. README.md ๋ฌธ์„œ๊ฐ€ ์ž‘์„ฑ๋˜์–ด ํ”„๋กœ์ ํŠธ๊ฐ€ ์™„์„ฑ๋ฉ๋‹ˆ๋‹ค.

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

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

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

๋Œ“๊ธ€ (0)

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