๐ŸŽฐ g5se ์นด์ง€๋…ธ ์Šฌ๋กฏ๋จธ์‹  ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ - ํ–‰์šด์˜ ๋ฆด ๋Œ๋ฆฌ๊ธฐ & ์žญํŒŸ ํžˆ์Šคํ† ๋ฆฌ - ๋ฐ”์ด๋ธŒ์ฝ”๋”ฉ ๋ ˆ์‹œํ”ผ
๊ณ ๊ธ‰ PHP g5se AI: Claude

๐ŸŽฐ g5se ์นด์ง€๋…ธ ์Šฌ๋กฏ๋จธ์‹  ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ - ํ–‰์šด์˜ ๋ฆด ๋Œ๋ฆฌ๊ธฐ & ์žญํŒŸ ํžˆ์Šคํ† ๋ฆฌ

๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ช… g5se-casino-slot-machine
์กฐํšŒ์ˆ˜ 122 0๊ฑด ์ œ์ถœ

๋ฌธ์ œ ์„ค๋ช…

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

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

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

  1. ๋ฆด ์Šคํ•€ ์• ๋‹ˆ๋ฉ”์ด์…˜

    • 3๊ฐœ ๋ฆด์ด ๋…๋ฆฝ์ ์œผ๋กœ ํšŒ์ „ํ•˜๋ฉฐ ์ˆœ์ฐจ์ ์œผ๋กœ ๋ฉˆ์ถค
    • CSS animation + setTimeout์œผ๋กœ ๋ฆด ํšŒ์ „ ์‹œ๊ฐ ํšจ๊ณผ
    • ๊ฐ ๋ฆด์€ 7๊ฐœ ์‹ฌ๋ณผ(๐Ÿ’์ฒด๋ฆฌ, ๐Ÿ‹๋ ˆ๋ชฌ, ๐ŸŠ์˜ค๋ Œ์ง€, ๐Ÿ‰์ˆ˜๋ฐ•, โญ๋ณ„, ๐Ÿ’Ž๋‹ค์ด์•„, 7๏ธโƒฃ์„ธ๋ธ) ์ค‘ ๋žœ๋ค ์„ ํƒ
  2. ๋ฐฐ๋‹น ์‹œ์Šคํ…œ

    • 3๊ฐœ ์‹ฌ๋ณผ ์ผ์น˜: ๋ฐฐ๋‹น ์ง€๊ธ‰ (์ฒด๋ฆฌ 2๋ฐฐ, ๋ ˆ๋ชฌ 3๋ฐฐ, ์˜ค๋ Œ์ง€ 5๋ฐฐ, ์ˆ˜๋ฐ• 10๋ฐฐ, ๋ณ„ 20๋ฐฐ, ๋‹ค์ด์•„ 50๋ฐฐ, ์„ธ๋ธ 100๋ฐฐ)
    • 2๊ฐœ ์ผ์น˜: ๋ฒ ํŒ…์•ก ๋ฐ˜ํ™˜
    • ๋ถˆ์ผ์น˜: ๋ฒ ํŒ…์•ก ์ฐจ๊ฐ
    • ์žญํŒŸ(์„ธ๋ธ 3๊ฐœ): ํŠน์ˆ˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ + ์‚ฌ์šด๋“œ + ํžˆ์Šคํ† ๋ฆฌ ๊ธฐ๋ก
  3. ๊ฐ€์ƒ ์ฝ”์ธ ๊ด€๋ฆฌ

    • ์ดˆ๊ธฐ ์ง€๊ธ‰: 1000์ฝ”์ธ
    • ๋ฒ ํŒ… ๊ธˆ์•ก ์„ ํƒ: 10/50/100/500์ฝ”์ธ
    • ์ฝ”์ธ ์†Œ์ง„ ์‹œ ์ผ์ผ ๋ณด๋„ˆ์Šค๋กœ ์žฌ์ถฉ์ „ ๊ฐ€๋Šฅ (1ํšŒ/์ผ, 500์ฝ”์ธ)
    • ์‚ฌ์šฉ์ž๋ณ„ ์ฝ”์ธ ์ž”์•ก SQLite DB ์ €์žฅ
  4. ๊ฒŒ์ž„ ํžˆ์Šคํ† ๋ฆฌ & ํ†ต๊ณ„

    • ์ตœ๊ทผ 20ํšŒ ์Šคํ•€ ๊ฒฐ๊ณผ ๋กœ๊ทธ (์‹ฌ๋ณผ ์กฐํ•ฉ, ๋ฐฐ๋‹น)
    • ์žญํŒŸ ๋‹ฌ์„ฑ ํžˆ์Šคํ† ๋ฆฌ (๋‹‰๋„ค์ž„, ํƒ€์ž„์Šคํƒฌํ”„, ๋ฐฐ๋‹น์•ก)
    • ๊ฐœ์ธ ํ†ต๊ณ„: ์ด ์Šคํ•€ ํšŸ์ˆ˜, ์ด ๋ฒ ํŒ…์•ก, ์ด ํš๋“์•ก, ์Šน๋ฅ 
  5. ์‚ฌ์šด๋“œ & ์‹œ๊ฐ ํšจ๊ณผ

    • ๋ฆด ํšŒ์ „ ์‚ฌ์šด๋“œ (Web Audio API๋กœ ๊ฐ„๋‹จํ•œ beep ์ƒ์„ฑ)
    • ๋‹น์ฒจ ์‹œ ์ถ•ํ•˜ ํšจ๊ณผ (confetti.js ๊ฐ™์€ ํŒŒํ‹ฐํด ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋˜๋Š” CSS keyframe)
    • ์žญํŒŸ ์‹œ ์ „์ฒด ํ™”๋ฉด ์ถ•ํ•˜ ๋ชจ๋‹ฌ + ํญ์ฃฝ ์• ๋‹ˆ๋ฉ”์ด์…˜
  6. ํšŒ์› ์—ฐ๋™

    • g5se ๋กœ๊ทธ์ธ ํšŒ์›๋งŒ ํ”Œ๋ ˆ์ด ๊ฐ€๋Šฅ ($is_member ์ฒดํฌ)
    • ๋น„ํšŒ์›์€ ๋ฐ๋ชจ ๋ชจ๋“œ(์ฝ”์ธ ์ง€๊ธ‰ ์—†์Œ, ๊ฒฐ๊ณผ๋งŒ ํ‘œ์‹œ)
    • ๋‹‰๋„ค์ž„์œผ๋กœ ์žญํŒŸ ํžˆ์Šคํ† ๋ฆฌ ๊ธฐ๋ก

์„ ํƒ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ (๋ณด๋„ˆ์Šค)

  • ์˜คํ†  ์Šคํ•€: ์ง€์ • ํšŸ์ˆ˜๋งŒํผ ์ž๋™์œผ๋กœ ๋ฆด ํšŒ์ „
  • ๋”๋ธ” ์—… ๊ฒŒ์ž„: ๋‹น์ฒจ ํ›„ ๋ฐฐ๋‹น์„ 2๋ฐฐ๋กœ ๋Š˜๋ฆด ๊ธฐํšŒ ์ œ๊ณต (๋™์ „ ๋˜์ง€๊ธฐ ๋ฏธ๋‹ˆ๊ฒŒ์ž„)
  • ๋ฆฌ๋”๋ณด๋“œ: ๋ˆ„์  ํš๋“ ์ฝ”์ธ TOP 10 ๋žญํ‚น
  • ์ผ์ผ ๋ฏธ์…˜: ํŠน์ • ์‹ฌ๋ณผ ์กฐํ•ฉ ๋‹ฌ์„ฑ ์‹œ ๋ณด๋„ˆ์Šค ์ฝ”์ธ ์ง€๊ธ‰
  • ํ…Œ๋งˆ ์„ ํƒ: ํด๋ž˜์‹/๋„ค์˜จ/๋ ˆํŠธ๋กœ ์Šฌ๋กฏ๋จธ์‹  ์Šคํ‚จ ์ „ํ™˜

๊ธฐ์ˆ  ์Šคํƒ

  • ๋ฐฑ์—”๋“œ: PHP 8.2+ (g5se ์ปจ๋ฒค์…˜)
  • ํ”„๋ก ํŠธ์—”๋“œ: ์ˆœ์ˆ˜ HTML/CSS/JavaScript (CDN ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ตœ์†Œํ™”)
  • DB: SQLite (G5_DATA_PATH/modules/casino.sqlite)
  • ์• ๋‹ˆ๋ฉ”์ด์…˜: CSS @keyframes + transition
  • ์‚ฌ์šด๋“œ: Web Audio API (AudioContext)

์ œ์•ฝ ์กฐ๊ฑด

  • g5se modern shell ํ•„์ˆ˜ ์‚ฌ์šฉ (๋‹คํฌ๋ชจ๋“œ ๋Œ€์‘)
  • ์™ธ๋ถ€ npm/vite/webpack ๋นŒ๋“œ ๋„๊ตฌ ์‚ฌ์šฉ ๊ธˆ์ง€
  • ์‹ค์‹œ๊ฐ„ WebSocket ์‚ฌ์šฉ ๊ธˆ์ง€ (HTTP polling์œผ๋กœ ํžˆ์Šคํ† ๋ฆฌ ๊ฐฑ์‹ )
  • ์‹ค์ œ ๊ธˆ์ „ ๊ฑฐ๋ž˜ ๊ธฐ๋Šฅ ์—†์Œ (๊ฐ€์ƒ ์ฝ”์ธ๋งŒ)
  • ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๋ฐ์ดํ„ฐ ๋ชจ๋‘ htmlspecialchars() ์ด์Šค์ผ€์ดํ”„

์ œ์•ฝ ์กฐ๊ฑด

  • โ€ข g5se ์„ค์น˜๋ณธ ์œ„์— ์ž‘์—… (๊ธฐ์กด ์„ค์น˜ ํ•„์š”)
  • โ€ข ํด๋” ๊ธฐ๋ฐ˜ ์ž๋™ ๋ผ์šฐํŒ… ์‚ฌ์šฉ (app/modules/casino/index.php โ†’ /casino)
  • โ€ข modern shell ํ•„์ˆ˜ ์ ์šฉ (_head.inc.php + m-shell + _nav + _footer)
  • โ€ข ๋””์ž์ธ ํ† ํฐ ์‚ฌ์šฉ (var(--m-surface), var(--m-text) ๋“ฑ)
  • โ€ข ์ˆœ์ˆ˜ PHP + HTML/CSS/JS (์™ธ๋ถ€ ๋นŒ๋“œ ๋„๊ตฌ ๊ธˆ์ง€)
  • โ€ข SQLite DB๋Š” G5_DATA_PATH/modules/casino.sqlite์— ์ €์žฅ
  • โ€ข prepared statement๋กœ SQL ์ธ์ ์…˜ ๋ฐฉ์ง€
  • โ€ข ํšŒ์› ์ „์šฉ ๊ธฐ๋Šฅ ($is_member ์ฒดํฌ)
  • โ€ข Web Audio API๋กœ ์‚ฌ์šด๋“œ ์ƒ์„ฑ (์™ธ๋ถ€ mp3 ํŒŒ์ผ ์‚ฌ์šฉ ๊ธˆ์ง€)
  • โ€ข CSS animation์œผ๋กœ ๋ฆด ํšŒ์ „ ๊ตฌํ˜„
  • โ€ข ์‹ค์‹œ๊ฐ„ WebSocket ์‚ฌ์šฉ ๊ธˆ์ง€ (HTTP ๊ธฐ๋ฐ˜ ํด๋ง)
  • โ€ข CDN ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ตœ์†Œํ™” (๊ฐ€๋Šฅํ•˜๋ฉด ์ˆœ์ˆ˜ JS๋กœ)

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

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

1
Step 1
g5se๊ฐ€ ์„ค์น˜๋œ ๋””๋ ‰ํ„ฐ๋ฆฌ(๋ฃจํŠธ์— `index.php`ยท`app/`์ด ์žˆ๋Š” ๊ณณ)์—์„œ ์ž‘์—…์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. (g5se ์„ค์น˜๋Š” https://github.com/gnuboard/g5se ์ฐธ๊ณ )

**Step 1: ํ”„๋กœ์ ํŠธ ํด๋” ์ƒ์„ฑ & modern shell ๊ณจ๊ฒฉ ๊ตฌ์ถ•**

๋‹ค์Œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜์„ธ์š”:

1. `app/modules/casino/index.php` ํŒŒ์ผ ์ƒ์„ฑ
2. modern shell ๊ณจ๊ฒฉ ์ ์šฉ:
   - `_common.php` ์ธํด๋ฃจ๋“œ (g5se ์ง„์ž… ์ปจํ…์ŠคํŠธ)
   - `_head.inc.php` ์ธํด๋ฃจ๋“œ (HTML head + ๋‹คํฌ๋ชจ๋“œ ํ† ํฐ)
   - `m-shell` ๋ž˜ํผ๋กœ ์ „์ฒด ๊ฐ์‹ธ๊ธฐ
   - `_nav.inc.php` ์ธํด๋ฃจ๋“œ (์ƒ๋‹จ ๋„ค๋น„๊ฒŒ์ด์…˜)
   - `<main class="m-container">` ์•ˆ์— ํŽ˜์ด์ง€ ๋ณธ๋ฌธ
   - `_footer.inc.php` ์ธํด๋ฃจ๋“œ (ํ•˜๋‹จ ํ‘ธํ„ฐ)
   - `tail.sub.php` ์ธํด๋ฃจ๋“œ (์Šคํฌ๋ฆฝํŠธ ์ข…๋ฃŒ)

3. ํŽ˜์ด์ง€ ์ œ๋ชฉ: "๐ŸŽฐ ์นด์ง€๋…ธ ์Šฌ๋กฏ๋จธ์‹ "
4. ๋ณธ๋ฌธ์— ์ž„์‹œ ํ…์ŠคํŠธ: "์Šฌ๋กฏ๋จธ์‹  ๊ฒŒ์ž„ ์ค€๋น„ ์ค‘..."

**์ฝ”๋“œ ๊ณจ๊ฒฉ ์˜ˆ์‹œ:**

```php
<?php
include_once('./_common.php');

// ํšŒ์› ์ „์šฉ ์ฒดํฌ
if (!$is_member) {
    alert_close('ํšŒ์›๋งŒ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.');
}

require_once(G5_THEME_PATH.'/modern/_head.inc.php');
include_once('./head.sub.php');
?>
<div class="m-shell">
    <?php require G5_THEME_PATH.'/modern/_nav.inc.php'; ?>
    <main class="m-container" style="padding: 32px 20px 48px;">
        <h1 style="font-size: 2rem; font-weight: 700; margin-bottom: 24px;">๐ŸŽฐ ์นด์ง€๋…ธ ์Šฌ๋กฏ๋จธ์‹ </h1>
        <p style="color: var(--m-text-muted);">์Šฌ๋กฏ๋จธ์‹  ๊ฒŒ์ž„ ์ค€๋น„ ์ค‘...</p>
    </main>
    <?php require G5_THEME_PATH.'/modern/_footer.inc.php'; ?>
</div>
<?php include_once('./tail.sub.php'); ?>
```

๋ธŒ๋ผ์šฐ์ €์—์„œ `/casino` ์ ‘์† ์‹œ ํŽ˜์ด์ง€๊ฐ€ ๋กœ๋“œ๋˜๊ณ  modern shell์ด ์ ์šฉ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: app/modules/casino/index.php ํŒŒ์ผ์ด ์ƒ์„ฑ๋˜๊ณ , /casino ๊ฒฝ๋กœ๋กœ ์ ‘์† ์‹œ modern shell์ด ์ ์šฉ๋œ ๋นˆ ํŽ˜์ด์ง€๊ฐ€ ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค. ์ƒ๋‹จ ๋„ค๋น„๊ฒŒ์ด์…˜, ํ•˜๋‹จ ํ‘ธํ„ฐ, ๋‹คํฌ๋ชจ๋“œ ํ† ๊ธ€์ด ์ •์ƒ ์ž‘๋™ํ•˜๋ฉฐ '์Šฌ๋กฏ๋จธ์‹  ๊ฒŒ์ž„ ์ค€๋น„ ์ค‘...' ํ…์ŠคํŠธ๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

2
Step 2
**Step 2: SQLite DB ์Šคํ‚ค๋งˆ ์„ค๊ณ„ & ์ดˆ๊ธฐํ™”**

์Šฌ๋กฏ๋จธ์‹  ๊ฒŒ์ž„ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  SQLite DB๋ฅผ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๋‹ค.

**DB ํŒŒ์ผ ์œ„์น˜**: `G5_DATA_PATH/modules/casino.sqlite`

**ํ…Œ์ด๋ธ” ์„ค๊ณ„**:

1. `user_coins` ํ…Œ์ด๋ธ”:
   - `mb_id` TEXT PRIMARY KEY (ํšŒ์› ์•„์ด๋””)
   - `coins` INTEGER NOT NULL DEFAULT 1000 (๋ณด์œ  ์ฝ”์ธ)
   - `last_bonus_date` TEXT (๋งˆ์ง€๋ง‰ ์ผ์ผ ๋ณด๋„ˆ์Šค ์ˆ˜๋ น ๋‚ ์งœ, YYYY-MM-DD)
   - `created_at` TEXT (๊ณ„์ • ์ƒ์„ฑ ์ผ์‹œ)

2. `spin_history` ํ…Œ์ด๋ธ”:
   - `id` INTEGER PRIMARY KEY AUTOINCREMENT
   - `mb_id` TEXT NOT NULL (ํšŒ์› ์•„์ด๋””)
   - `bet_amount` INTEGER NOT NULL (๋ฒ ํŒ…์•ก)
   - `symbols` TEXT NOT NULL (๋ฆด ๊ฒฐ๊ณผ, ์˜ˆ: "๐Ÿ’,๐Ÿ’,๐Ÿ’")
   - `payout` INTEGER NOT NULL (๋ฐฐ๋‹น์•ก, 0์ด๋ฉด ๊ฝ)
   - `is_jackpot` INTEGER DEFAULT 0 (์žญํŒŸ ์—ฌ๋ถ€, 1=์žญํŒŸ)
   - `created_at` TEXT (์Šคํ•€ ์ผ์‹œ)

3. `statistics` ํ…Œ์ด๋ธ”:
   - `mb_id` TEXT PRIMARY KEY
   - `total_spins` INTEGER DEFAULT 0 (์ด ์Šคํ•€ ํšŸ์ˆ˜)
   - `total_bet` INTEGER DEFAULT 0 (์ด ๋ฒ ํŒ…์•ก)
   - `total_win` INTEGER DEFAULT 0 (์ด ํš๋“์•ก)
   - `jackpot_count` INTEGER DEFAULT 0 (์žญํŒŸ ๋‹ฌ์„ฑ ํšŸ์ˆ˜)

**PHP ์ฝ”๋“œ ์ถ”๊ฐ€**:

`app/modules/casino/index.php` ์ƒ๋‹จ ๋กœ์ง ๋ถ€๋ถ„์— ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”:

```php
// DB ์ดˆ๊ธฐํ™”
@mkdir(G5_DATA_PATH.'/modules', 0707, true);
$db = new PDO('sqlite:'.G5_DATA_PATH.'/modules/casino.sqlite');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// ํ…Œ์ด๋ธ” ์ƒ์„ฑ (์ฒซ ์ ‘๊ทผ ์‹œ)
$db->exec("
    CREATE TABLE IF NOT EXISTS user_coins (
        mb_id TEXT PRIMARY KEY,
        coins INTEGER NOT NULL DEFAULT 1000,
        last_bonus_date TEXT,
        created_at TEXT DEFAULT (datetime('now'))
    )
");

$db->exec("
    CREATE TABLE IF NOT EXISTS spin_history (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        mb_id TEXT NOT NULL,
        bet_amount INTEGER NOT NULL,
        symbols TEXT NOT NULL,
        payout INTEGER NOT NULL,
        is_jackpot INTEGER DEFAULT 0,
        created_at TEXT DEFAULT (datetime('now'))
    )
");

$db->exec("
    CREATE TABLE IF NOT EXISTS statistics (
        mb_id TEXT PRIMARY KEY,
        total_spins INTEGER DEFAULT 0,
        total_bet INTEGER DEFAULT 0,
        total_win INTEGER DEFAULT 0,
        jackpot_count INTEGER DEFAULT 0
    )
");

// ํ˜„์žฌ ์‚ฌ์šฉ์ž ์ฝ”์ธ ์กฐํšŒ (์—†์œผ๋ฉด ์ดˆ๊ธฐ 1000์ฝ”์ธ ์ง€๊ธ‰)
$stmt = $db->prepare('SELECT coins, last_bonus_date FROM user_coins WHERE mb_id = ?');
$stmt->execute([$member['mb_id']]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);

if (!$user) {
    $db->prepare('INSERT INTO user_coins (mb_id, coins) VALUES (?, 1000)')->execute([$member['mb_id']]);
    $user = ['coins' => 1000, 'last_bonus_date' => null];
}

$coins = (int)$user['coins'];
$last_bonus_date = $user['last_bonus_date'];
```

**๋ณธ๋ฌธ์— ์ฝ”์ธ ์ž”์•ก ํ‘œ์‹œ**:

`<main>` ํƒœ๊ทธ ์•ˆ์— ๋‹ค์Œ HTML์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”:

```html
<div style="background: var(--m-surface); padding: 20px; border-radius: var(--m-radius-lg); box-shadow: var(--m-shadow); margin-bottom: 24px;">
    <h2 style="font-size: 1.5rem; font-weight: 700; margin-bottom: 12px;">๐Ÿ’ฐ ๋ณด์œ  ์ฝ”์ธ</h2>
    <p style="font-size: 2.5rem; font-weight: 900; color: #FFD700;"><?php echo number_format($coins); ?> ์ฝ”์ธ</p>
</div>
```

๋ธŒ๋ผ์šฐ์ € ์ƒˆ๋กœ๊ณ ์นจ ํ›„ ์ฝ”์ธ ์ž”์•ก์ด ํ‘œ์‹œ๋˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: SQLite DB๊ฐ€ G5_DATA_PATH/modules/casino.sqlite ๊ฒฝ๋กœ์— ์ƒ์„ฑ๋˜๊ณ , user_coins/spin_history/statistics ํ…Œ์ด๋ธ”์ด ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด์ง‘๋‹ˆ๋‹ค. ํŽ˜์ด์ง€ ์ ‘์† ์‹œ ํ˜„์žฌ ์‚ฌ์šฉ์ž์˜ ์ฝ”์ธ ์ž”์•ก(์ดˆ๊ธฐ 1000์ฝ”์ธ)์ด ํ™”๋ฉด์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

3
Step 3
**Step 3: ์Šฌ๋กฏ๋จธ์‹  UI & ๋ฆด ์ปดํฌ๋„ŒํŠธ ๊ตฌ์ถ•**

์Šฌ๋กฏ๋จธ์‹ ์˜ ์‹œ๊ฐ์  ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

**์š”๊ตฌ์‚ฌํ•ญ**:

1. 3๊ฐœ์˜ ๋ฆด(์„ธ๋กœ ์ปฌ๋Ÿผ)์„ ๊ฐ€๋กœ๋กœ ๋ฐฐ์น˜
2. ๊ฐ ๋ฆด์€ ์นด๋“œ ์Šคํƒ€์ผ ๋ฐ•์Šค ์•ˆ์— ์‹ฌ๋ณผ ์ด๋ชจ์ง€ ํ‘œ์‹œ
3. ์‹ฌ๋ณผ 7์ข…: ๐Ÿ’(์ฒด๋ฆฌ), ๐Ÿ‹(๋ ˆ๋ชฌ), ๐ŸŠ(์˜ค๋ Œ์ง€), ๐Ÿ‰(์ˆ˜๋ฐ•), โญ(๋ณ„), ๐Ÿ’Ž(๋‹ค์ด์•„), 7๏ธโƒฃ(์„ธ๋ธ)
4. ๋ฒ ํŒ… ๊ธˆ์•ก ์„ ํƒ ๋ฒ„ํŠผ: 10/50/100/500์ฝ”์ธ
5. SPIN ๋ฒ„ํŠผ (๋ฆด ํšŒ์ „ ํŠธ๋ฆฌ๊ฑฐ)
6. ๋ฐฐ๋‹นํ‘œ ์•ˆ๋‚ด (๊ฐ ์‹ฌ๋ณผ๋ณ„ ๋ฐฐ๋‹น๋ฅ )

**HTML ์ฝ”๋“œ ์ถ”๊ฐ€** (`<main>` ํƒœ๊ทธ ์•ˆ):

```html
<!-- ์Šฌ๋กฏ๋จธ์‹  ๋ณธ์ฒด -->
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 40px; border-radius: var(--m-radius-lg); box-shadow: 0 20px 60px rgba(0,0,0,0.3); margin-bottom: 32px;">
    <!-- ๋ฆด ์ปจํ…Œ์ด๋„ˆ -->
    <div id="reels" style="display: flex; gap: 16px; justify-content: center; margin-bottom: 32px;">
        <div class="reel" data-reel="0" style="background: #fff; width: 120px; height: 160px; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 5rem; box-shadow: inset 0 4px 8px rgba(0,0,0,0.1);">
            <span class="symbol">๐Ÿ’</span>
        </div>
        <div class="reel" data-reel="1" style="background: #fff; width: 120px; height: 160px; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 5rem; box-shadow: inset 0 4px 8px rgba(0,0,0,0.1);">
            <span class="symbol">๐Ÿ’</span>
        </div>
        <div class="reel" data-reel="2" style="background: #fff; width: 120px; height: 160px; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 5rem; box-shadow: inset 0 4px 8px rgba(0,0,0,0.1);">
            <span class="symbol">๐Ÿ’</span>
        </div>
    </div>

    <!-- ๋ฒ ํŒ… ๊ธˆ์•ก ์„ ํƒ -->
    <div style="display: flex; gap: 12px; justify-content: center; margin-bottom: 20px;">
        <button class="bet-btn" data-amount="10" style="background: #fff; color: #667eea; border: none; padding: 12px 24px; border-radius: 8px; font-weight: 700; cursor: pointer; transition: transform 0.2s;">10 ์ฝ”์ธ</button>
        <button class="bet-btn" data-amount="50" style="background: #fff; color: #667eea; border: none; padding: 12px 24px; border-radius: 8px; font-weight: 700; cursor: pointer; transition: transform 0.2s;">50 ์ฝ”์ธ</button>
        <button class="bet-btn active" data-amount="100" style="background: #FFD700; color: #333; border: none; padding: 12px 24px; border-radius: 8px; font-weight: 700; cursor: pointer; transition: transform 0.2s;">100 ์ฝ”์ธ</button>
        <button class="bet-btn" data-amount="500" style="background: #fff; color: #667eea; border: none; padding: 12px 24px; border-radius: 8px; font-weight: 700; cursor: pointer; transition: transform 0.2s;">500 ์ฝ”์ธ</button>
    </div>

    <!-- SPIN ๋ฒ„ํŠผ -->
    <div style="text-align: center;">
        <button id="spinBtn" style="background: #FFD700; color: #333; border: none; padding: 20px 60px; border-radius: 50px; font-size: 1.5rem; font-weight: 900; cursor: pointer; box-shadow: 0 8px 20px rgba(255,215,0,0.4); transition: all 0.3s;">๐ŸŽฐ SPIN</button>
    </div>
</div>

<!-- ๋ฐฐ๋‹นํ‘œ -->
<div style="background: var(--m-surface); padding: 24px; border-radius: var(--m-radius-lg); box-shadow: var(--m-shadow); margin-bottom: 32px;">
    <h3 style="font-size: 1.25rem; font-weight: 700; margin-bottom: 16px;">๐Ÿ’Ž ๋ฐฐ๋‹นํ‘œ</h3>
    <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px;">
        <div style="padding: 12px; background: rgba(255,215,0,0.1); border-radius: 8px;">7๏ธโƒฃ ์„ธ๋ธ x3 โ†’ <strong>100๋ฐฐ</strong></div>
        <div style="padding: 12px; background: rgba(138,43,226,0.1); border-radius: 8px;">๐Ÿ’Ž ๋‹ค์ด์•„ x3 โ†’ <strong>50๋ฐฐ</strong></div>
        <div style="padding: 12px; background: rgba(255,215,0,0.1); border-radius: 8px;">โญ ๋ณ„ x3 โ†’ <strong>20๋ฐฐ</strong></div>
        <div style="padding: 12px; background: rgba(255,99,71,0.1); border-radius: 8px;">๐Ÿ‰ ์ˆ˜๋ฐ• x3 โ†’ <strong>10๋ฐฐ</strong></div>
        <div style="padding: 12px; background: rgba(255,165,0,0.1); border-radius: 8px;">๐ŸŠ ์˜ค๋ Œ์ง€ x3 โ†’ <strong>5๋ฐฐ</strong></div>
        <div style="padding: 12px; background: rgba(255,255,0,0.1); border-radius: 8px;">๐Ÿ‹ ๋ ˆ๋ชฌ x3 โ†’ <strong>3๋ฐฐ</strong></div>
        <div style="padding: 12px; background: rgba(220,20,60,0.1); border-radius: 8px;">๐Ÿ’ ์ฒด๋ฆฌ x3 โ†’ <strong>2๋ฐฐ</strong></div>
    </div>
    <p style="margin-top: 12px; color: var(--m-text-muted); font-size: 0.875rem;">* 2๊ฐœ ์ผ์น˜ ์‹œ ๋ฒ ํŒ…์•ก ๋ฐ˜ํ™˜, ๋ถˆ์ผ์น˜ ์‹œ ๋ฒ ํŒ…์•ก ์ฐจ๊ฐ</p>
</div>
```

**JavaScript ์ฝ”๋“œ ์ถ”๊ฐ€** (ํŽ˜์ด์ง€ ํ•˜๋‹จ, `</main>` ๋‹ค์Œ):

```html
<script>
let selectedBet = 100; // ๊ธฐ๋ณธ ๋ฒ ํŒ…์•ก

// ๋ฒ ํŒ… ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
document.querySelectorAll('.bet-btn').forEach(btn => {
    btn.addEventListener('click', () => {
        document.querySelectorAll('.bet-btn').forEach(b => {
            b.style.background = '#fff';
            b.style.color = '#667eea';
            b.classList.remove('active');
        });
        btn.style.background = '#FFD700';
        btn.style.color = '#333';
        btn.classList.add('active');
        selectedBet = parseInt(btn.dataset.amount);
    });
});

// SPIN ๋ฒ„ํŠผ ํด๋ฆญ (์ž„์‹œ - ๋‹ค์Œ ๋‹จ๊ณ„์—์„œ ๊ตฌํ˜„)
document.getElementById('spinBtn').addEventListener('click', () => {
    alert('SPIN ๋ฒ„ํŠผ ํด๋ฆญ! ๋ฒ ํŒ…์•ก: ' + selectedBet + '์ฝ”์ธ');
});
</script>
```

๋ธŒ๋ผ์šฐ์ € ์ƒˆ๋กœ๊ณ ์นจ ํ›„ ์Šฌ๋กฏ๋จธ์‹  UI๊ฐ€ ํ‘œ์‹œ๋˜๊ณ , ๋ฒ ํŒ… ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์Šคํƒ€์ผ์ด ๋ณ€๊ฒฝ๋˜๋ฉฐ, SPIN ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ alert๊ฐ€ ๋œจ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ์Šฌ๋กฏ๋จธ์‹  UI๊ฐ€ ํ™”๋ฉด์— ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค. 3๊ฐœ์˜ ๋ฆด์ด ๊ฐ€๋กœ๋กœ ๋ฐฐ์น˜๋˜๊ณ  ๊ฐ ๋ฆด์— ์‹ฌ๋ณผ ์ด๋ชจ์ง€๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ๋ฒ ํŒ… ๊ธˆ์•ก ์„ ํƒ ๋ฒ„ํŠผ(10/50/100/500)์ด ํ‘œ์‹œ๋˜๋ฉฐ ํด๋ฆญ ์‹œ ํ™œ์„ฑ ์Šคํƒ€์ผ๋กœ ์ „ํ™˜๋ฉ๋‹ˆ๋‹ค. SPIN ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ alert๋กœ ์„ ํƒ๋œ ๋ฒ ํŒ…์•ก์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ๋ฐฐ๋‹นํ‘œ๊ฐ€ ํ•˜๋‹จ์— ๊น”๋”ํ•˜๊ฒŒ ์ •๋ฆฌ๋˜์–ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

4
Step 4
**Step 4: ๋ฆด ์Šคํ•€ ์• ๋‹ˆ๋ฉ”์ด์…˜ & ๋žœ๋ค ์‹ฌ๋ณผ ์ƒ์„ฑ**

๋ฆด์ด ํšŒ์ „ํ•˜๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜์„ CSS๋กœ ๊ตฌํ˜„ํ•˜๊ณ , JavaScript๋กœ ๋žœ๋ค ์‹ฌ๋ณผ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

**์š”๊ตฌ์‚ฌํ•ญ**:

1. SPIN ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ 3๊ฐœ ๋ฆด์ด ์ˆœ์ฐจ์ ์œผ๋กœ ํšŒ์ „ ์‹œ์ž‘
2. ๊ฐ ๋ฆด์€ 0.5์ดˆ ๊ฐ„๊ฒฉ์œผ๋กœ ๋ฉˆ์ถค (์ฒซ ๋ฒˆ์งธ โ†’ ๋‘ ๋ฒˆ์งธ โ†’ ์„ธ ๋ฒˆ์งธ)
3. ํšŒ์ „ ์ค‘ ์‹ฌ๋ณผ์ด ๋ธ”๋Ÿฌ ํšจ๊ณผ๋กœ ํ๋ฆฟํ•˜๊ฒŒ ๋ณด์ž„
4. ๋ฉˆ์ถœ ๋•Œ ์ตœ์ข… ์‹ฌ๋ณผ์ด ์„ ๋ช…ํ•˜๊ฒŒ ํ‘œ์‹œ
5. 7์ข… ์‹ฌ๋ณผ ์ค‘ ๋žœ๋ค ์„ ํƒ (๊ฐ€์ค‘์น˜ ์—†์ด ๊ท ๋“ฑ ํ™•๋ฅ )

**CSS ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ถ”๊ฐ€** (`<style>` ํƒœ๊ทธ๋ฅผ `<main>` ์œ„์— ์‚ฝ์ž…):

```html
<style>
@keyframes spin {
    0% { transform: translateY(0); filter: blur(0); }
    50% { transform: translateY(-20px); filter: blur(8px); }
    100% { transform: translateY(0); filter: blur(0); }
}

.reel.spinning .symbol {
    animation: spin 0.1s linear infinite;
}

.reel {
    transition: all 0.3s ease;
}

.reel.stopped .symbol {
    animation: none;
    filter: blur(0);
}

#spinBtn:hover {
    transform: scale(1.05);
    box-shadow: 0 12px 30px rgba(255,215,0,0.6);
}

#spinBtn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.bet-btn:hover {
    transform: scale(1.05);
}
</style>
```

**JavaScript ์Šคํ•€ ๋กœ์ง ๊ตฌํ˜„** (๊ธฐ์กด `<script>` ํƒœ๊ทธ ๋‚ด์šฉ์„ ๋‹ค์Œ์œผ๋กœ ๊ต์ฒด):

```javascript
const symbols = ['๐Ÿ’', '๐Ÿ‹', '๐ŸŠ', '๐Ÿ‰', 'โญ', '๐Ÿ’Ž', '7๏ธโƒฃ'];
let selectedBet = 100;
let isSpinning = false;

// ๋ฒ ํŒ… ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
document.querySelectorAll('.bet-btn').forEach(btn => {
    btn.addEventListener('click', () => {
        document.querySelectorAll('.bet-btn').forEach(b => {
            b.style.background = '#fff';
            b.style.color = '#667eea';
            b.classList.remove('active');
        });
        btn.style.background = '#FFD700';
        btn.style.color = '#333';
        btn.classList.add('active');
        selectedBet = parseInt(btn.dataset.amount);
    });
});

// ๋žœ๋ค ์‹ฌ๋ณผ ์„ ํƒ
function getRandomSymbol() {
    return symbols[Math.floor(Math.random() * symbols.length)];
}

// ๋ฆด ์Šคํ•€ ์• ๋‹ˆ๋ฉ”์ด์…˜
function spinReel(reelIndex, finalSymbol, delay) {
    return new Promise(resolve => {
        const reel = document.querySelector(`.reel[data-reel="${reelIndex}"]`);
        const symbolEl = reel.querySelector('.symbol');
        
        setTimeout(() => {
            reel.classList.add('spinning');
            
            // 0.1์ดˆ๋งˆ๋‹ค ๋žœ๋ค ์‹ฌ๋ณผ ๋ณ€๊ฒฝ (ํšŒ์ „ ํšจ๊ณผ)
            const interval = setInterval(() => {
                symbolEl.textContent = getRandomSymbol();
            }, 100);
            
            // 2์ดˆ ํ›„ ๋ฉˆ์ถค
            setTimeout(() => {
                clearInterval(interval);
                symbolEl.textContent = finalSymbol;
                reel.classList.remove('spinning');
                reel.classList.add('stopped');
                setTimeout(() => reel.classList.remove('stopped'), 300);
                resolve();
            }, 2000);
        }, delay);
    });
}

// SPIN ๋ฒ„ํŠผ ํด๋ฆญ
document.getElementById('spinBtn').addEventListener('click', async () => {
    if (isSpinning) return;
    
    const currentCoins = parseInt(document.querySelector('[style*="font-size: 2.5rem"]').textContent.replace(/[^0-9]/g, ''));
    
    if (currentCoins < selectedBet) {
        alert('์ฝ”์ธ์ด ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค!');
        return;
    }
    
    isSpinning = true;
    document.getElementById('spinBtn').disabled = true;
    
    // ๋žœ๋ค ์‹ฌ๋ณผ 3๊ฐœ ์ƒ์„ฑ
    const results = [getRandomSymbol(), getRandomSymbol(), getRandomSymbol()];
    
    // ๋ฆด ์ˆœ์ฐจ ํšŒ์ „
    await spinReel(0, results[0], 0);
    await spinReel(1, results[1], 0);
    await spinReel(2, results[2], 0);
    
    // ๊ฒฐ๊ณผ ์ฒ˜๋ฆฌ (๋‹ค์Œ ๋‹จ๊ณ„์—์„œ ๊ตฌํ˜„)
    console.log('๊ฒฐ๊ณผ:', results);
    alert('๊ฒฐ๊ณผ: ' + results.join(' '));
    
    isSpinning = false;
    document.getElementById('spinBtn').disabled = false;
});
```

๋ธŒ๋ผ์šฐ์ € ์ƒˆ๋กœ๊ณ ์นจ ํ›„ SPIN ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”. ๋ฆด์ด ์ˆœ์ฐจ์ ์œผ๋กœ ํšŒ์ „ํ•˜๋ฉฐ ๋ธ”๋Ÿฌ ํšจ๊ณผ๊ฐ€ ์ ์šฉ๋˜๊ณ , 2์ดˆ ํ›„ ๋žœ๋ค ์‹ฌ๋ณผ์ด ํ‘œ์‹œ๋˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: SPIN ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ 3๊ฐœ์˜ ๋ฆด์ด ์ˆœ์ฐจ์ ์œผ๋กœ ํšŒ์ „ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ํšŒ์ „ ์ค‘ ์‹ฌ๋ณผ์ด ๋น ๋ฅด๊ฒŒ ๋ณ€๊ฒฝ๋˜๋ฉฐ ๋ธ”๋Ÿฌ ํšจ๊ณผ๊ฐ€ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๊ฐ ๋ฆด์€ 2์ดˆ ํ›„ ๋ฉˆ์ถ”๊ณ  ์ตœ์ข… ๋žœ๋ค ์‹ฌ๋ณผ์ด ์„ ๋ช…ํ•˜๊ฒŒ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ์Šคํ•€ ์ค‘์—๋Š” SPIN ๋ฒ„ํŠผ์ด ๋น„ํ™œ์„ฑํ™”๋˜์–ด ์ค‘๋ณต ํด๋ฆญ์ด ๋ฐฉ์ง€๋ฉ๋‹ˆ๋‹ค. ์ฝ˜์†”๊ณผ alert๋กœ ์ตœ์ข… ๊ฒฐ๊ณผ๊ฐ€ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

5
Step 5
**Step 5: ๋ฐฐ๋‹น ๊ณ„์‚ฐ & ์ฝ”์ธ ์—…๋ฐ์ดํŠธ (์„œ๋ฒ„ ๋กœ์ง)**

์Šคํ•€ ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ๋ฐฐ๋‹น์„ ๊ณ„์‚ฐํ•˜๊ณ , ์‚ฌ์šฉ์ž ์ฝ”์ธ์„ ์—…๋ฐ์ดํŠธํ•˜๋Š” ์„œ๋ฒ„ ๋กœ์ง์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

**์š”๊ตฌ์‚ฌํ•ญ**:

1. AJAX๋กœ ์Šคํ•€ ๊ฒฐ๊ณผ๋ฅผ ์„œ๋ฒ„์— ์ „์†ก
2. ์„œ๋ฒ„์—์„œ ๋ฐฐ๋‹น ๊ณ„์‚ฐ:
   - 3๊ฐœ ์ผ์น˜: ์‹ฌ๋ณผ๋ณ„ ๋ฐฐ๋‹น ์ง€๊ธ‰
   - 2๊ฐœ ์ผ์น˜: ๋ฒ ํŒ…์•ก ๋ฐ˜ํ™˜
   - ๋ถˆ์ผ์น˜: ๋ฒ ํŒ…์•ก ์ฐจ๊ฐ
3. ์ฝ”์ธ ์ž”์•ก ์—…๋ฐ์ดํŠธ (DB ์ €์žฅ)
4. ์Šคํ•€ ํžˆ์Šคํ† ๋ฆฌ ๊ธฐ๋ก
5. ํ†ต๊ณ„ ์—…๋ฐ์ดํŠธ (์ด ์Šคํ•€, ์ด ๋ฒ ํŒ…, ์ด ํš๋“)

**PHP ์„œ๋ฒ„ ๋กœ์ง ์ถ”๊ฐ€** (`app/modules/casino/index.php` ์ƒ๋‹จ, DB ์ดˆ๊ธฐํ™” ๋‹ค์Œ):

```php
// AJAX ์š”์ฒญ ์ฒ˜๋ฆฌ
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'spin') {
    header('Content-Type: application/json');
    
    $bet_amount = (int)$_POST['bet_amount'];
    $symbols = json_decode($_POST['symbols'], true);
    
    // ์ฝ”์ธ ์ž”์•ก ํ™•์ธ
    $stmt = $db->prepare('SELECT coins FROM user_coins WHERE mb_id = ?');
    $stmt->execute([$member['mb_id']]);
    $current_coins = (int)$stmt->fetchColumn();
    
    if ($current_coins < $bet_amount) {
        echo json_encode(['success' => false, 'message' => '์ฝ”์ธ์ด ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค.']);
        exit;
    }
    
    // ๋ฐฐ๋‹น ๊ณ„์‚ฐ
    $symbol_payouts = [
        '๐Ÿ’' => 2,
        '๐Ÿ‹' => 3,
        '๐ŸŠ' => 5,
        '๐Ÿ‰' => 10,
        'โญ' => 20,
        '๐Ÿ’Ž' => 50,
        '7๏ธโƒฃ' => 100
    ];
    
    $unique = array_unique($symbols);
    $counts = array_count_values($symbols);
    
    $payout = 0;
    $is_jackpot = 0;
    $result_message = '';
    
    if (count($unique) === 1) {
        // 3๊ฐœ ์ผ์น˜
        $symbol = $symbols[0];
        $multiplier = $symbol_payouts[$symbol];
        $payout = $bet_amount * $multiplier;
        $result_message = "๐ŸŽ‰ {$symbol} 3๊ฐœ ์ผ์น˜! {$multiplier}๋ฐฐ ๋‹น์ฒจ!";
        
        if ($symbol === '7๏ธโƒฃ') {
            $is_jackpot = 1;
            $result_message = "๐ŸŽฐ JACKPOT! ์„ธ๋ธ 3๊ฐœ! {$multiplier}๋ฐฐ ๋Œ€๋ฐ•!";
        }
    } elseif (max($counts) === 2) {
        // 2๊ฐœ ์ผ์น˜ - ๋ฒ ํŒ…์•ก ๋ฐ˜ํ™˜
        $payout = $bet_amount;
        $result_message = "๐Ÿ˜Š 2๊ฐœ ์ผ์น˜! ๋ฒ ํŒ…์•ก ๋ฐ˜ํ™˜";
    } else {
        // ๋ถˆ์ผ์น˜
        $payout = 0;
        $result_message = "๐Ÿ˜ข ๊ฝ! ๋‹ค์Œ ๊ธฐํšŒ์—...";
    }
    
    // ์ฝ”์ธ ์—…๋ฐ์ดํŠธ
    $new_coins = $current_coins - $bet_amount + $payout;
    $db->prepare('UPDATE user_coins SET coins = ? WHERE mb_id = ?')->execute([$new_coins, $member['mb_id']]);
    
    // ์Šคํ•€ ํžˆ์Šคํ† ๋ฆฌ ๊ธฐ๋ก
    $symbols_str = implode(',', $symbols);
    $db->prepare('INSERT INTO spin_history (mb_id, bet_amount, symbols, payout, is_jackpot) VALUES (?, ?, ?, ?, ?)')->execute([
        $member['mb_id'],
        $bet_amount,
        $symbols_str,
        $payout,
        $is_jackpot
    ]);
    
    // ํ†ต๊ณ„ ์—…๋ฐ์ดํŠธ
    $db->exec("
        INSERT INTO statistics (mb_id, total_spins, total_bet, total_win, jackpot_count)
        VALUES ('{$member['mb_id']}', 1, {$bet_amount}, {$payout}, {$is_jackpot})
        ON CONFLICT(mb_id) DO UPDATE SET
            total_spins = total_spins + 1,
            total_bet = total_bet + {$bet_amount},
            total_win = total_win + {$payout},
            jackpot_count = jackpot_count + {$is_jackpot}
    ");
    
    echo json_encode([
        'success' => true,
        'payout' => $payout,
        'new_coins' => $new_coins,
        'message' => $result_message,
        'is_jackpot' => $is_jackpot
    ]);
    exit;
}
```

**JavaScript AJAX ์š”์ฒญ ์ถ”๊ฐ€** (๊ธฐ์กด SPIN ๋ฒ„ํŠผ ํด๋ฆญ ๋กœ์ง ์ˆ˜์ •):

```javascript
// SPIN ๋ฒ„ํŠผ ํด๋ฆญ (๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ๋‹ค์Œ์œผ๋กœ ๊ต์ฒด)
document.getElementById('spinBtn').addEventListener('click', async () => {
    if (isSpinning) return;
    
    const currentCoins = parseInt(document.querySelector('[style*="font-size: 2.5rem"]').textContent.replace(/[^0-9]/g, ''));
    
    if (currentCoins < selectedBet) {
        alert('์ฝ”์ธ์ด ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค!');
        return;
    }
    
    isSpinning = true;
    document.getElementById('spinBtn').disabled = true;
    
    // ๋žœ๋ค ์‹ฌ๋ณผ 3๊ฐœ ์ƒ์„ฑ
    const results = [getRandomSymbol(), getRandomSymbol(), getRandomSymbol()];
    
    // ๋ฆด ์ˆœ์ฐจ ํšŒ์ „
    await spinReel(0, results[0], 0);
    await spinReel(1, results[1], 0);
    await spinReel(2, results[2], 0);
    
    // ์„œ๋ฒ„์— ๊ฒฐ๊ณผ ์ „์†ก
    const formData = new FormData();
    formData.append('action', 'spin');
    formData.append('bet_amount', selectedBet);
    formData.append('symbols', JSON.stringify(results));
    
    const response = await fetch(window.location.href, {
        method: 'POST',
        body: formData
    });
    
    const data = await response.json();
    
    if (data.success) {
        // ์ฝ”์ธ ์ž”์•ก ์—…๋ฐ์ดํŠธ
        document.querySelector('[style*="font-size: 2.5rem"]').textContent = data.new_coins.toLocaleString() + ' ์ฝ”์ธ';
        
        // ๊ฒฐ๊ณผ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ
        alert(data.message + '\n\nํš๋“: ' + data.payout.toLocaleString() + ' ์ฝ”์ธ\n์ž”์•ก: ' + data.new_coins.toLocaleString() + ' ์ฝ”์ธ');
        
        // ์žญํŒŸ ์• ๋‹ˆ๋ฉ”์ด์…˜ (๋‹ค์Œ ๋‹จ๊ณ„์—์„œ ๊ตฌํ˜„)
        if (data.is_jackpot) {
            console.log('๐ŸŽฐ JACKPOT!');
        }
    } else {
        alert(data.message);
    }
    
    isSpinning = false;
    document.getElementById('spinBtn').disabled = false;
});
```

๋ธŒ๋ผ์šฐ์ € ์ƒˆ๋กœ๊ณ ์นจ ํ›„ SPIN์„ ์—ฌ๋Ÿฌ ๋ฒˆ ํด๋ฆญํ•˜์„ธ์š”. ์ฝ”์ธ ์ž”์•ก์ด ์‹ค์‹œ๊ฐ„์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜๊ณ , ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ๋ฐฐ๋‹น์ด ์ง€๊ธ‰๋˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: SPIN ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ๋ฆด์ด ํšŒ์ „ํ•˜๊ณ , ์„œ๋ฒ„๋กœ ๊ฒฐ๊ณผ๊ฐ€ ์ „์†ก๋ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„์—์„œ ๋ฐฐ๋‹น์„ ๊ณ„์‚ฐํ•˜์—ฌ ์ฝ”์ธ ์ž”์•ก์ด ์‹ค์‹œ๊ฐ„์œผ๋กœ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค. 3๊ฐœ ์ผ์น˜ ์‹œ ๋ฐฐ๋‹น ์ง€๊ธ‰, 2๊ฐœ ์ผ์น˜ ์‹œ ๋ฒ ํŒ…์•ก ๋ฐ˜ํ™˜, ๋ถˆ์ผ์น˜ ์‹œ ์ฐจ๊ฐ์ด ์ •์ƒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ์Šคํ•€ ํžˆ์Šคํ† ๋ฆฌ์™€ ํ†ต๊ณ„๊ฐ€ DB์— ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค. alert๋กœ ๊ฒฐ๊ณผ ๋ฉ”์‹œ์ง€์™€ ํš๋“ ์ฝ”์ธ, ์ž”์•ก์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

6
Step 6
**Step 6: ์žญํŒŸ ์ถ•ํ•˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ & ์‚ฌ์šด๋“œ ํšจ๊ณผ**

์„ธ๋ธ 3๊ฐœ ์ผ์น˜(์žญํŒŸ) ์‹œ ํŠน์ˆ˜ ํšจ๊ณผ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

**์š”๊ตฌ์‚ฌํ•ญ**:

1. ์žญํŒŸ ์‹œ ์ „์ฒด ํ™”๋ฉด ์ถ•ํ•˜ ๋ชจ๋‹ฌ ํ‘œ์‹œ
2. CSS ํŒŒํ‹ฐํด ์• ๋‹ˆ๋ฉ”์ด์…˜ (๋–จ์–ด์ง€๋Š” ๊ธˆํ™” ํšจ๊ณผ)
3. Web Audio API๋กœ ์Šน๋ฆฌ ์‚ฌ์šด๋“œ ์žฌ์ƒ
4. ๋ชจ๋‹ฌ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ

**HTML ๋ชจ๋‹ฌ ์ถ”๊ฐ€** (`<main>` ํƒœ๊ทธ ๋‹ค์Œ์— ์‚ฝ์ž…):

```html
<!-- ์žญํŒŸ ๋ชจ๋‹ฌ -->
<div id="jackpotModal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.9); z-index: 9999; justify-content: center; align-items: center;">
    <div style="text-align: center; position: relative;">
        <div id="confetti" style="position: absolute; top: 0; left: 50%; transform: translateX(-50%); width: 100%; height: 100%; pointer-events: none;"></div>
        <div style="background: linear-gradient(135deg, #FFD700, #FFA500); padding: 60px; border-radius: 24px; box-shadow: 0 30px 80px rgba(255,215,0,0.6);">
            <h1 style="font-size: 4rem; font-weight: 900; color: #fff; text-shadow: 0 4px 8px rgba(0,0,0,0.3); margin-bottom: 20px;">๐ŸŽฐ JACKPOT! ๐ŸŽฐ</h1>
            <p style="font-size: 2rem; color: #fff; margin-bottom: 12px;">์„ธ๋ธ 3๊ฐœ ์ผ์น˜!</p>
            <p id="jackpotAmount" style="font-size: 3rem; font-weight: 900; color: #fff; text-shadow: 0 4px 8px rgba(0,0,0,0.3);"></p>
            <button onclick="closeJackpotModal()" style="margin-top: 32px; background: #fff; color: #FFA500; border: none; padding: 16px 48px; border-radius: 50px; font-size: 1.25rem; font-weight: 700; cursor: pointer; box-shadow: 0 8px 20px rgba(0,0,0,0.2);">ํ™•์ธ</button>
        </div>
    </div>
</div>
```

**CSS ํŒŒํ‹ฐํด ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ถ”๊ฐ€** (๊ธฐ์กด `<style>` ํƒœ๊ทธ ์•ˆ์— ์ถ”๊ฐ€):

```css
@keyframes fall {
    0% { transform: translateY(-100vh) rotate(0deg); opacity: 1; }
    100% { transform: translateY(100vh) rotate(360deg); opacity: 0; }
}

.coin {
    position: absolute;
    font-size: 2rem;
    animation: fall linear infinite;
}

#jackpotModal {
    animation: fadeIn 0.3s ease;
}

@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}
```

**JavaScript ์žญํŒŸ ๋กœ์ง ์ถ”๊ฐ€** (๊ธฐ์กด `<script>` ํƒœ๊ทธ ์•ˆ์— ์ถ”๊ฐ€):

```javascript
// Web Audio API ์‚ฌ์šด๋“œ ์ƒ์„ฑ
const audioContext = new (window.AudioContext || window.webkitAudioContext)();

function playWinSound() {
    const oscillator = audioContext.createOscillator();
    const gainNode = audioContext.createGain();
    
    oscillator.connect(gainNode);
    gainNode.connect(audioContext.destination);
    
    oscillator.frequency.setValueAtTime(523.25, audioContext.currentTime); // C5
    oscillator.frequency.setValueAtTime(659.25, audioContext.currentTime + 0.1); // E5
    oscillator.frequency.setValueAtTime(783.99, audioContext.currentTime + 0.2); // G5
    oscillator.frequency.setValueAtTime(1046.5, audioContext.currentTime + 0.3); // C6
    
    gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
    gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5);
    
    oscillator.start(audioContext.currentTime);
    oscillator.stop(audioContext.currentTime + 0.5);
}

function createConfetti() {
    const confettiContainer = document.getElementById('confetti');
    confettiContainer.innerHTML = '';
    
    for (let i = 0; i < 50; i++) {
        const coin = document.createElement('div');
        coin.className = 'coin';
        coin.textContent = '๐Ÿ’ฐ';
        coin.style.left = Math.random() * 100 + '%';
        coin.style.animationDuration = (Math.random() * 2 + 2) + 's';
        coin.style.animationDelay = Math.random() * 2 + 's';
        confettiContainer.appendChild(coin);
    }
}

function showJackpotModal(amount) {
    document.getElementById('jackpotAmount').textContent = amount.toLocaleString() + ' ์ฝ”์ธ ํš๋“!';
    document.getElementById('jackpotModal').style.display = 'flex';
    createConfetti();
    playWinSound();
}

function closeJackpotModal() {
    document.getElementById('jackpotModal').style.display = 'none';
}

// ๊ธฐ์กด SPIN ๋กœ์ง์˜ ์žญํŒŸ ๋ถ€๋ถ„ ์ˆ˜์ •
// (๊ธฐ์กด ์ฝ”๋“œ์—์„œ "if (data.is_jackpot)" ๋ถ€๋ถ„์„ ๋‹ค์Œ์œผ๋กœ ๊ต์ฒด)
if (data.is_jackpot) {
    setTimeout(() => showJackpotModal(data.payout), 500);
}
```

๋ธŒ๋ผ์šฐ์ € ์ƒˆ๋กœ๊ณ ์นจ ํ›„ ์„ธ๋ธ 3๊ฐœ๊ฐ€ ๋‚˜์˜ฌ ๋•Œ๊นŒ์ง€ SPIN์„ ๋ฐ˜๋ณตํ•˜์„ธ์š” (ํ™•๋ฅ ์ด ๋‚ฎ์œผ๋ฏ€๋กœ ํ…Œ์ŠคํŠธ์šฉ์œผ๋กœ ์„œ๋ฒ„ ๋กœ์ง์—์„œ ์ž„์‹œ๋กœ ์žญํŒŸ ํ™•๋ฅ ์„ ๋†’์ด๊ฑฐ๋‚˜, ์ฝ˜์†”์—์„œ `showJackpotModal(10000)` ํ•จ์ˆ˜๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜์—ฌ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ). ์žญํŒŸ ๋ชจ๋‹ฌ์ด ํ‘œ์‹œ๋˜๊ณ  ๊ธˆํ™” ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ ์‚ฌ์šด๋“œ๊ฐ€ ์žฌ์ƒ๋˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ์„ธ๋ธ 3๊ฐœ ์ผ์น˜(์žญํŒŸ) ์‹œ ์ „์ฒด ํ™”๋ฉด ์ถ•ํ•˜ ๋ชจ๋‹ฌ์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ๋ชจ๋‹ฌ ๋ฐฐ๊ฒฝ์— ๊ธˆํ™” ์ด๋ชจ์ง€๊ฐ€ ์œ„์—์„œ ์•„๋ž˜๋กœ ๋–จ์–ด์ง€๋Š” ํŒŒํ‹ฐํด ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์žฌ์ƒ๋ฉ๋‹ˆ๋‹ค. Web Audio API๋กœ ์ƒ์„ฑ๋œ ์Šน๋ฆฌ ๋ฉœ๋กœ๋””๊ฐ€ ์žฌ์ƒ๋ฉ๋‹ˆ๋‹ค. ๋ชจ๋‹ฌ์—๋Š” ํš๋“ ์ฝ”์ธ์ด ํ‘œ์‹œ๋˜๊ณ , ํ™•์ธ ๋ฒ„ํŠผ์œผ๋กœ ๋‹ซ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

7
Step 7
**Step 7: ์Šคํ•€ ํžˆ์Šคํ† ๋ฆฌ & ์žญํŒŸ ๋ช…์˜ˆ์˜ ์ „๋‹น**

์ตœ๊ทผ ์Šคํ•€ ๊ธฐ๋ก๊ณผ ์žญํŒŸ ๋‹ฌ์„ฑ์ž ๋ชฉ๋ก์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

**์š”๊ตฌ์‚ฌํ•ญ**:

1. ์ตœ๊ทผ 20ํšŒ ์Šคํ•€ ๊ฒฐ๊ณผ ํ…Œ์ด๋ธ” (์‹ฌ๋ณผ, ๋ฒ ํŒ…์•ก, ๋ฐฐ๋‹น)
2. ์žญํŒŸ ๋ช…์˜ˆ์˜ ์ „๋‹น (๋‹‰๋„ค์ž„, ๋‚ ์งœ, ๋ฐฐ๋‹น์•ก)
3. ๊ฐœ์ธ ํ†ต๊ณ„ ์นด๋“œ (์ด ์Šคํ•€, ์ด ๋ฒ ํŒ…, ์ด ํš๋“, ์Šน๋ฅ )

**PHP ๋ฐ์ดํ„ฐ ์กฐํšŒ ์ถ”๊ฐ€** (`app/modules/casino/index.php` ์ƒ๋‹จ, DB ์ดˆ๊ธฐํ™” ๋‹ค์Œ):

```php
// ์ตœ๊ทผ ์Šคํ•€ ํžˆ์Šคํ† ๋ฆฌ ์กฐํšŒ
$stmt = $db->prepare('SELECT * FROM spin_history WHERE mb_id = ? ORDER BY created_at DESC LIMIT 20');
$stmt->execute([$member['mb_id']]);
$recent_spins = $stmt->fetchAll(PDO::FETCH_ASSOC);

// ์žญํŒŸ ๋ช…์˜ˆ์˜ ์ „๋‹น
$stmt = $db->query('SELECT sh.*, uc.mb_id FROM spin_history sh JOIN user_coins uc ON sh.mb_id = uc.mb_id WHERE sh.is_jackpot = 1 ORDER BY sh.created_at DESC LIMIT 10');
$jackpot_hall = $stmt->fetchAll(PDO::FETCH_ASSOC);

// ๊ฐœ์ธ ํ†ต๊ณ„
$stmt = $db->prepare('SELECT * FROM statistics WHERE mb_id = ?');
$stmt->execute([$member['mb_id']]);
$stats = $stmt->fetch(PDO::FETCH_ASSOC);

if (!$stats) {
    $stats = ['total_spins' => 0, 'total_bet' => 0, 'total_win' => 0, 'jackpot_count' => 0];
}

$win_rate = $stats['total_spins'] > 0 ? round(($stats['total_win'] / $stats['total_bet']) * 100, 1) : 0;
```

**HTML ํžˆ์Šคํ† ๋ฆฌ ์„น์…˜ ์ถ”๊ฐ€** (๋ฐฐ๋‹นํ‘œ ๋‹ค์Œ์— ์‚ฝ์ž…):

```html
<!-- ๊ฐœ์ธ ํ†ต๊ณ„ -->
<div style="background: var(--m-surface); padding: 24px; border-radius: var(--m-radius-lg); box-shadow: var(--m-shadow); margin-bottom: 32px;">
    <h3 style="font-size: 1.25rem; font-weight: 700; margin-bottom: 16px;">๐Ÿ“Š ๋‚˜์˜ ํ†ต๊ณ„</h3>
    <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 16px;">
        <div style="text-align: center; padding: 16px; background: rgba(103,126,234,0.1); border-radius: 8px;">
            <div style="font-size: 0.875rem; color: var(--m-text-muted); margin-bottom: 8px;">์ด ์Šคํ•€</div>
            <div style="font-size: 1.5rem; font-weight: 700;"><?php echo number_format($stats['total_spins']); ?>ํšŒ</div>
        </div>
        <div style="text-align: center; padding: 16px; background: rgba(255,99,71,0.1); border-radius: 8px;">
            <div style="font-size: 0.875rem; color: var(--m-text-muted); margin-bottom: 8px;">์ด ๋ฒ ํŒ…</div>
            <div style="font-size: 1.5rem; font-weight: 700;"><?php echo number_format($stats['total_bet']); ?></div>
        </div>
        <div style="text-align: center; padding: 16px; background: rgba(50,205,50,0.1); border-radius: 8px;">
            <div style="font-size: 0.875rem; color: var(--m-text-muted); margin-bottom: 8px;">์ด ํš๋“</div>
            <div style="font-size: 1.5rem; font-weight: 700;"><?php echo number_format($stats['total_win']); ?></div>
        </div>
        <div style="text-align: center; padding: 16px; background: rgba(255,215,0,0.1); border-radius: 8px;">
            <div style="font-size: 0.875rem; color: var(--m-text-muted); margin-bottom: 8px;">์ˆ˜์ต๋ฅ </div>
            <div style="font-size: 1.5rem; font-weight: 700;"><?php echo $win_rate; ?>%</div>
        </div>
    </div>
</div>

<!-- ์ตœ๊ทผ ์Šคํ•€ ํžˆ์Šคํ† ๋ฆฌ -->
<div style="background: var(--m-surface); padding: 24px; border-radius: var(--m-radius-lg); box-shadow: var(--m-shadow); margin-bottom: 32px;">
    <h3 style="font-size: 1.25rem; font-weight: 700; margin-bottom: 16px;">๐Ÿ“œ ์ตœ๊ทผ ์Šคํ•€ ๊ธฐ๋ก</h3>
    <?php if (empty($recent_spins)): ?>
        <p style="color: var(--m-text-muted); text-align: center; padding: 32px;">์•„์ง ์Šคํ•€ ๊ธฐ๋ก์ด ์—†์Šต๋‹ˆ๋‹ค.</p>
    <?php else: ?>
        <div style="overflow-x: auto;">
            <table style="width: 100%; border-collapse: collapse;">
                <thead>
                    <tr style="border-bottom: 2px solid var(--m-text-muted);">
                        <th style="padding: 12px; text-align: left;">์‹ฌ๋ณผ</th>
                        <th style="padding: 12px; text-align: right;">๋ฒ ํŒ…</th>
                        <th style="padding: 12px; text-align: right;">๋ฐฐ๋‹น</th>
                        <th style="padding: 12px; text-align: center;">๊ฒฐ๊ณผ</th>
                        <th style="padding: 12px; text-align: left;">์‹œ๊ฐ„</th>
                    </tr>
                </thead>
                <tbody>
                    <?php foreach ($recent_spins as $spin): ?>
                        <tr style="border-bottom: 1px solid rgba(128,128,128,0.2);">
                            <td style="padding: 12px; font-size: 1.5rem;"><?php echo htmlspecialchars($spin['symbols']); ?></td>
                            <td style="padding: 12px; text-align: right;"><?php echo number_format($spin['bet_amount']); ?></td>
                            <td style="padding: 12px; text-align: right; font-weight: 700; color: <?php echo $spin['payout'] > 0 ? '#50C878' : '#FF6B6B'; ?>;"><?php echo number_format($spin['payout']); ?></td>
                            <td style="padding: 12px; text-align: center;">
                                <?php if ($spin['is_jackpot']): ?>
                                    <span style="background: #FFD700; color: #333; padding: 4px 12px; border-radius: 12px; font-size: 0.75rem; font-weight: 700;">JACKPOT</span>
                                <?php elseif ($spin['payout'] > $spin['bet_amount']): ?>
                                    <span style="color: #50C878;">โœ… ์Šน</span>
                                <?php elseif ($spin['payout'] == $spin['bet_amount']): ?>
                                    <span style="color: var(--m-text-muted);">โ†”๏ธ ๋ฌด</span>
                                <?php else: ?>
                                    <span style="color: #FF6B6B;">โŒ ํŒจ</span>
                                <?php endif; ?>
                            </td>
                            <td style="padding: 12px; font-size: 0.875rem; color: var(--m-text-muted);"><?php echo date('m/d H:i', strtotime($spin['created_at'])); ?></td>
                        </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
        </div>
    <?php endif; ?>
</div>

<!-- ์žญํŒŸ ๋ช…์˜ˆ์˜ ์ „๋‹น -->
<div style="background: linear-gradient(135deg, #FFD700, #FFA500); padding: 24px; border-radius: var(--m-radius-lg); box-shadow: var(--m-shadow);">
    <h3 style="font-size: 1.25rem; font-weight: 700; margin-bottom: 16px; color: #fff;">๐Ÿ† ์žญํŒŸ ๋ช…์˜ˆ์˜ ์ „๋‹น</h3>
    <?php if (empty($jackpot_hall)): ?>
        <p style="color: #fff; text-align: center; padding: 32px; opacity: 0.8;">์•„์ง ์žญํŒŸ ๋‹ฌ์„ฑ์ž๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.</p>
    <?php else: ?>
        <div style="display: grid; gap: 12px;">
            <?php foreach ($jackpot_hall as $idx => $jackpot): ?>
                <div style="background: rgba(255,255,255,0.2); padding: 16px; border-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
                    <div style="display: flex; align-items: center; gap: 12px;">
                        <div style="background: #fff; color: #FFA500; width: 32px; height: 32px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 700;"><?php echo $idx + 1; ?></div>
                        <div>
                            <div style="color: #fff; font-weight: 700;"><?php echo htmlspecialchars($jackpot['mb_id']); ?></div>
                            <div style="color: #fff; opacity: 0.8; font-size: 0.875rem;"><?php echo date('Y-m-d H:i', strtotime($jackpot['created_at'])); ?></div>
                        </div>
                    </div>
                    <div style="color: #fff; font-size: 1.25rem; font-weight: 700;"><?php echo number_format($jackpot['payout']); ?> ์ฝ”์ธ</div>
                </div>
            <?php endforeach; ?>
        </div>
    <?php endif; ?>
</div>
```

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

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ํŽ˜์ด์ง€ ํ•˜๋‹จ์— ๊ฐœ์ธ ํ†ต๊ณ„ ์นด๋“œ๊ฐ€ ํ‘œ์‹œ๋˜๋ฉฐ ์ด ์Šคํ•€ ํšŸ์ˆ˜, ์ด ๋ฒ ํŒ…์•ก, ์ด ํš๋“์•ก, ์ˆ˜์ต๋ฅ ์ด ์‹ค์‹œ๊ฐ„์œผ๋กœ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค. ์ตœ๊ทผ 20ํšŒ ์Šคํ•€ ๊ฒฐ๊ณผ๊ฐ€ ํ…Œ์ด๋ธ”๋กœ ํ‘œ์‹œ๋˜๋ฉฐ ์‹ฌ๋ณผ, ๋ฒ ํŒ…์•ก, ๋ฐฐ๋‹น, ๊ฒฐ๊ณผ(์Šน/๋ฌด/ํŒจ), ์‹œ๊ฐ„์ด ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค. ์žญํŒŸ ๋ช…์˜ˆ์˜ ์ „๋‹น์—๋Š” ์žญํŒŸ ๋‹ฌ์„ฑ์ž ๋ชฉ๋ก์ด ์ˆœ์œ„๋ณ„๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

8
Step 8
**Step 8: ์ผ์ผ ๋ณด๋„ˆ์Šค ์‹œ์Šคํ…œ**

์ฝ”์ธ์ด ๋ถ€์กฑํ•œ ์‚ฌ์šฉ์ž๋ฅผ ์œ„ํ•ด ํ•˜๋ฃจ 1ํšŒ ๋ณด๋„ˆ์Šค ์ฝ”์ธ์„ ์ง€๊ธ‰ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

**์š”๊ตฌ์‚ฌํ•ญ**:

1. ์ผ์ผ ๋ณด๋„ˆ์Šค ๋ฒ„ํŠผ (500์ฝ”์ธ ์ง€๊ธ‰)
2. ๋งˆ์ง€๋ง‰ ์ˆ˜๋ น์ผ ๊ธฐ์ค€ 24์‹œ๊ฐ„ ํ›„ ์žฌ์ˆ˜๋ น ๊ฐ€๋Šฅ
3. ๋‚จ์€ ์‹œ๊ฐ„ ์นด์šดํŠธ๋‹ค์šด ํ‘œ์‹œ
4. ์ˆ˜๋ น ์‹œ DB ์—…๋ฐ์ดํŠธ

**PHP ๋ณด๋„ˆ์Šค ๋กœ์ง ์ถ”๊ฐ€** (`app/modules/casino/index.php` ์ƒ๋‹จ, AJAX ์š”์ฒญ ์ฒ˜๋ฆฌ ๋ถ€๋ถ„์— ์ถ”๊ฐ€):

```php
// ์ผ์ผ ๋ณด๋„ˆ์Šค ์ˆ˜๋ น
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'claim_bonus') {
    header('Content-Type: application/json');
    
    $stmt = $db->prepare('SELECT coins, last_bonus_date FROM user_coins WHERE mb_id = ?');
    $stmt->execute([$member['mb_id']]);
    $user = $stmt->fetch(PDO::FETCH_ASSOC);
    
    $today = date('Y-m-d');
    
    if ($user['last_bonus_date'] === $today) {
        echo json_encode(['success' => false, 'message' => '์˜ค๋Š˜ ์ด๋ฏธ ๋ณด๋„ˆ์Šค๋ฅผ ์ˆ˜๋ นํ–ˆ์Šต๋‹ˆ๋‹ค.']);
        exit;
    }
    
    $new_coins = $user['coins'] + 500;
    $db->prepare('UPDATE user_coins SET coins = ?, last_bonus_date = ? WHERE mb_id = ?')->execute([$new_coins, $today, $member['mb_id']]);
    
    echo json_encode([
        'success' => true,
        'new_coins' => $new_coins,
        'message' => '์ผ์ผ ๋ณด๋„ˆ์Šค 500์ฝ”์ธ์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค!'
    ]);
    exit;
}
```

**PHP ๋ณด๋„ˆ์Šค ์ƒํƒœ ํ™•์ธ** (๊ธฐ์กด ์ฝ”์ธ ์กฐํšŒ ๋‹ค์Œ์— ์ถ”๊ฐ€):

```php
// ๋ณด๋„ˆ์Šค ์ˆ˜๋ น ๊ฐ€๋Šฅ ์—ฌ๋ถ€
$today = date('Y-m-d');
$can_claim_bonus = ($last_bonus_date !== $today);

// ๋‹ค์Œ ๋ณด๋„ˆ์Šค๊นŒ์ง€ ๋‚จ์€ ์‹œ๊ฐ„ (์ดˆ ๋‹จ์œ„)
if (!$can_claim_bonus) {
    $next_bonus_time = strtotime($last_bonus_date . ' +1 day');
    $remaining_seconds = $next_bonus_time - time();
} else {
    $remaining_seconds = 0;
}
```

**HTML ๋ณด๋„ˆ์Šค ๋ฒ„ํŠผ ์ถ”๊ฐ€** (์ฝ”์ธ ์ž”์•ก ์นด๋“œ ์•ˆ์— ์ถ”๊ฐ€):

```html
<div style="background: var(--m-surface); padding: 20px; border-radius: var(--m-radius-lg); box-shadow: var(--m-shadow); margin-bottom: 24px;">
    <h2 style="font-size: 1.5rem; font-weight: 700; margin-bottom: 12px;">๐Ÿ’ฐ ๋ณด์œ  ์ฝ”์ธ</h2>
    <p style="font-size: 2.5rem; font-weight: 900; color: #FFD700;"><?php echo number_format($coins); ?> ์ฝ”์ธ</p>
    
    <!-- ์ผ์ผ ๋ณด๋„ˆ์Šค ๋ฒ„ํŠผ -->
    <div style="margin-top: 16px;">
        <?php if ($can_claim_bonus): ?>
            <button id="bonusBtn" style="background: linear-gradient(135deg, #50C878, #32CD32); color: #fff; border: none; padding: 12px 32px; border-radius: 50px; font-weight: 700; cursor: pointer; box-shadow: 0 4px 12px rgba(50,205,50,0.4); transition: all 0.3s;">๐ŸŽ ์ผ์ผ ๋ณด๋„ˆ์Šค ๋ฐ›๊ธฐ (500์ฝ”์ธ)</button>
        <?php else: ?>
            <button id="bonusBtn" disabled style="background: #ccc; color: #666; border: none; padding: 12px 32px; border-radius: 50px; font-weight: 700; cursor: not-allowed;">๐ŸŽ ์ผ์ผ ๋ณด๋„ˆ์Šค (๋ฐ›์Œ)</button>
            <p id="bonusTimer" style="margin-top: 8px; font-size: 0.875rem; color: var(--m-text-muted);">๋‹ค์Œ ๋ณด๋„ˆ์Šค: <span id="countdown"></span></p>
        <?php endif; ?>
    </div>
</div>

<script>
const canClaimBonus = <?php echo $can_claim_bonus ? 'true' : 'false'; ?>;
let remainingSeconds = <?php echo $remaining_seconds; ?>;

// ๋ณด๋„ˆ์Šค ์ˆ˜๋ น
if (canClaimBonus) {
    document.getElementById('bonusBtn').addEventListener('click', async () => {
        const formData = new FormData();
        formData.append('action', 'claim_bonus');
        
        const response = await fetch(window.location.href, {
            method: 'POST',
            body: formData
        });
        
        const data = await response.json();
        
        if (data.success) {
            document.querySelector('[style*="font-size: 2.5rem"]').textContent = data.new_coins.toLocaleString() + ' ์ฝ”์ธ';
            alert(data.message);
            location.reload();
        } else {
            alert(data.message);
        }
    });
}

// ์นด์šดํŠธ๋‹ค์šด ํƒ€์ด๋จธ
if (!canClaimBonus && remainingSeconds > 0) {
    const countdownEl = document.getElementById('countdown');
    
    function updateCountdown() {
        if (remainingSeconds <= 0) {
            location.reload();
            return;
        }
        
        const hours = Math.floor(remainingSeconds / 3600);
        const minutes = Math.floor((remainingSeconds % 3600) / 60);
        const seconds = remainingSeconds % 60;
        
        countdownEl.textContent = `${hours}์‹œ๊ฐ„ ${minutes}๋ถ„ ${seconds}์ดˆ`;
        remainingSeconds--;
    }
    
    updateCountdown();
    setInterval(updateCountdown, 1000);
}
</script>
```

๋ธŒ๋ผ์šฐ์ € ์ƒˆ๋กœ๊ณ ์นจ ํ›„ ์ผ์ผ ๋ณด๋„ˆ์Šค ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”. ์ฝ”์ธ์ด 500 ์ฆ๊ฐ€ํ•˜๊ณ , ๋ฒ„ํŠผ์ด ๋น„ํ™œ์„ฑํ™”๋˜๋ฉฐ ์นด์šดํŠธ๋‹ค์šด์ด ์‹œ์ž‘๋˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ์ฝ”์ธ ์ž”์•ก ์นด๋“œ์— ์ผ์ผ ๋ณด๋„ˆ์Šค ๋ฒ„ํŠผ์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ 500์ฝ”์ธ์ด ์ง€๊ธ‰๋˜๊ณ  DB์— ์ˆ˜๋ น ๋‚ ์งœ๊ฐ€ ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ ์ˆ˜๋ นํ•œ ๊ฒฝ์šฐ ๋ฒ„ํŠผ์ด ๋น„ํ™œ์„ฑํ™”๋˜๊ณ  ๋‹ค์Œ ๋ณด๋„ˆ์Šค๊นŒ์ง€ ๋‚จ์€ ์‹œ๊ฐ„์ด ์นด์šดํŠธ๋‹ค์šด์œผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. 24์‹œ๊ฐ„ ๊ฒฝ๊ณผ ํ›„ ํŽ˜์ด์ง€ ์ƒˆ๋กœ๊ณ ์นจ ์‹œ ๋ฒ„ํŠผ์ด ๋‹ค์‹œ ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค.

9
Step 9
**Step 9: ๋ฐ˜์‘ํ˜• ๋””์ž์ธ & ๋ชจ๋ฐ”์ผ ์ตœ์ ํ™”**

๋‹ค์–‘ํ•œ ํ™”๋ฉด ํฌ๊ธฐ์—์„œ ์Šฌ๋กฏ๋จธ์‹ ์ด ์ •์ƒ ์ž‘๋™ํ•˜๋„๋ก ๋ฐ˜์‘ํ˜• ์Šคํƒ€์ผ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

**์š”๊ตฌ์‚ฌํ•ญ**:

1. ๋ชจ๋ฐ”์ผ(~768px): ๋ฆด ํฌ๊ธฐ ์ถ•์†Œ, ๋ฒ„ํŠผ ์„ธ๋กœ ๋ฐฐ์น˜
2. ํƒœ๋ธ”๋ฆฟ(768px~1024px): ์ค‘๊ฐ„ ํฌ๊ธฐ ๋ ˆ์ด์•„์›ƒ
3. ํ„ฐ์น˜ ์ด๋ฒคํŠธ ์ง€์›
4. ๊ฐ€๋กœ ์Šคํฌ๋กค ๋ฐฉ์ง€

**CSS ๋ฏธ๋””์–ด ์ฟผ๋ฆฌ ์ถ”๊ฐ€** (๊ธฐ์กด `<style>` ํƒœ๊ทธ ์•ˆ์— ์ถ”๊ฐ€):

```css
/* ๋ชจ๋ฐ”์ผ ์ตœ์ ํ™” */
@media (max-width: 768px) {
    .reel {
        width: 80px !important;
        height: 120px !important;
        font-size: 3.5rem !important;
    }
    
    #reels {
        gap: 8px !important;
    }
    
    .bet-btn {
        padding: 10px 16px !important;
        font-size: 0.875rem !important;
    }
    
    #spinBtn {
        padding: 16px 40px !important;
        font-size: 1.25rem !important;
    }
    
    h1 {
        font-size: 1.5rem !important;
    }
    
    .m-container {
        padding: 16px 12px 32px !important;
    }
    
    /* ํ…Œ์ด๋ธ” ๊ฐ€๋กœ ์Šคํฌ๋กค */
    table {
        font-size: 0.875rem;
    }
    
    table th,
    table td {
        padding: 8px !important;
    }
}

@media (max-width: 480px) {
    .reel {
        width: 70px !important;
        height: 100px !important;
        font-size: 3rem !important;
    }
    
    .bet-btn {
        padding: 8px 12px !important;
        font-size: 0.75rem !important;
    }
    
    #spinBtn {
        padding: 14px 32px !important;
        font-size: 1rem !important;
    }
}

/* ํ„ฐ์น˜ ํ”ผ๋“œ๋ฐฑ */
button:active {
    transform: scale(0.95);
}

/* ๊ฐ€๋กœ ์Šคํฌ๋กค ๋ฐฉ์ง€ */
body {
    overflow-x: hidden;
}

.m-container {
    max-width: 100%;
    overflow-x: hidden;
}
```

**JavaScript ํ„ฐ์น˜ ์ด๋ฒคํŠธ ์ถ”๊ฐ€** (๊ธฐ์กด `<script>` ํƒœ๊ทธ ์•ˆ์— ์ถ”๊ฐ€):

```javascript
// ํ„ฐ์น˜ ์ด๋ฒคํŠธ ์ง€์›
if ('ontouchstart' in window) {
    document.getElementById('spinBtn').addEventListener('touchstart', (e) => {
        e.preventDefault();
        document.getElementById('spinBtn').click();
    });
    
    document.querySelectorAll('.bet-btn').forEach(btn => {
        btn.addEventListener('touchstart', (e) => {
            e.preventDefault();
            btn.click();
        });
    });
}

// ํ™”๋ฉด ํšŒ์ „ ๊ฐ์ง€
window.addEventListener('orientationchange', () => {
    setTimeout(() => {
        window.scrollTo(0, 0);
    }, 100);
});
```

๋ธŒ๋ผ์šฐ์ € ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์—์„œ ๋ชจ๋ฐ”์ผ ๋ทฐ๋กœ ์ „ํ™˜ํ•˜๊ฑฐ๋‚˜ ์‹ค์ œ ๋ชจ๋ฐ”์ผ ๊ธฐ๊ธฐ๋กœ ์ ‘์†ํ•˜์—ฌ ๋ ˆ์ด์•„์›ƒ์ด ์ •์ƒ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ๋ชจ๋ฐ”์ผ ํ™”๋ฉด(~768px)์—์„œ ๋ฆด ํฌ๊ธฐ๊ฐ€ ์ถ•์†Œ๋˜๊ณ  ๋ฒ„ํŠผ์ด ์ ์ ˆํžˆ ๋ฐฐ์น˜๋ฉ๋‹ˆ๋‹ค. ํ„ฐ์น˜ ์ด๋ฒคํŠธ๊ฐ€ ์ •์ƒ ์ž‘๋™ํ•˜๋ฉฐ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์‹œ๊ฐ์  ํ”ผ๋“œ๋ฐฑ์ด ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ๊ฐ€๋กœ ์Šคํฌ๋กค์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š์œผ๋ฉฐ ๋ชจ๋“  ์ฝ˜ํ…์ธ ๊ฐ€ ํ™”๋ฉด ์•ˆ์— ์ •์ƒ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ํ™”๋ฉด ํšŒ์ „ ์‹œ ๋ ˆ์ด์•„์›ƒ์ด ์ž๋™ ์กฐ์ •๋ฉ๋‹ˆ๋‹ค.

10
Step 10
**Step 10: ์ตœ์ข… ๋งˆ๋ฌด๋ฆฌ & ๋ณด์•ˆ ๊ฐ•ํ™”**

ํ”„๋กœ์ ํŠธ๋ฅผ ์™„์„ฑํ•˜๊ณ  ๋ณด์•ˆ ์ทจ์•ฝ์ ์„ ์ ๊ฒ€ํ•ฉ๋‹ˆ๋‹ค.

**์š”๊ตฌ์‚ฌํ•ญ**:

1. XSS ๋ฐฉ์–ด: ๋ชจ๋“  ์‚ฌ์šฉ์ž ์ž…๋ ฅ ์ด์Šค์ผ€์ดํ”„
2. CSRF ๋ฐฉ์–ด: g5se ํ† ํฐ ๊ฒ€์ฆ
3. SQL ์ธ์ ์…˜ ๋ฐฉ์–ด: prepared statement ํ™•์ธ
4. ์—๋Ÿฌ ํ•ธ๋“ค๋ง ๊ฐœ์„ 
5. ์ฝ”๋“œ ์ •๋ฆฌ & ์ฃผ์„ ์ถ”๊ฐ€

**๋ณด์•ˆ ๊ฐ•ํ™” ์ฝ”๋“œ ์ถ”๊ฐ€**:

**1. CSRF ํ† ํฐ ์ถ”๊ฐ€** (PHP ์ƒ๋‹จ):

```php
// CSRF ํ† ํฐ ์ƒ์„ฑ
if (!isset($_SESSION['casino_token'])) {
    $_SESSION['casino_token'] = bin2hex(random_bytes(32));
}
$csrf_token = $_SESSION['casino_token'];

// AJAX ์š”์ฒญ ์‹œ ํ† ํฐ ๊ฒ€์ฆ
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['casino_token']) {
        header('Content-Type: application/json');
        echo json_encode(['success' => false, 'message' => '์ž˜๋ชป๋œ ์š”์ฒญ์ž…๋‹ˆ๋‹ค.']);
        exit;
    }
}
```

**2. JavaScript CSRF ํ† ํฐ ์ „์†ก** (๋ชจ๋“  fetch ์š”์ฒญ์— ์ถ”๊ฐ€):

```javascript
const csrfToken = '<?php echo $csrf_token; ?>';

// SPIN ๋ฒ„ํŠผ ํด๋ฆญ (๊ธฐ์กด fetch ๋ถ€๋ถ„ ์ˆ˜์ •)
const formData = new FormData();
formData.append('action', 'spin');
formData.append('bet_amount', selectedBet);
formData.append('symbols', JSON.stringify(results));
formData.append('csrf_token', csrfToken); // CSRF ํ† ํฐ ์ถ”๊ฐ€

// ๋ณด๋„ˆ์Šค ๋ฒ„ํŠผ ํด๋ฆญ (๊ธฐ์กด fetch ๋ถ€๋ถ„ ์ˆ˜์ •)
const formData = new FormData();
formData.append('action', 'claim_bonus');
formData.append('csrf_token', csrfToken); // CSRF ํ† ํฐ ์ถ”๊ฐ€
```

**3. ์—๋Ÿฌ ํ•ธ๋“ค๋ง ๊ฐœ์„ ** (PHP):

```php
try {
    // ๊ธฐ์กด DB ์ž‘์—… ์ฝ”๋“œ๋ฅผ try-catch๋กœ ๊ฐ์‹ธ๊ธฐ
    @mkdir(G5_DATA_PATH.'/modules', 0707, true);
    $db = new PDO('sqlite:'.G5_DATA_PATH.'/modules/casino.sqlite');
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
    // ... ๋‚˜๋จธ์ง€ ์ฝ”๋“œ ...
    
} catch (PDOException $e) {
    error_log('Casino DB Error: ' . $e->getMessage());
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        header('Content-Type: application/json');
        echo json_encode(['success' => false, 'message' => '์ผ์‹œ์ ์ธ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.']);
        exit;
    }
}
```

**4. ์ž…๋ ฅ ๊ฒ€์ฆ ๊ฐ•ํ™”** (PHP AJAX ์ฒ˜๋ฆฌ ๋ถ€๋ถ„):

```php
// ์Šคํ•€ ์š”์ฒญ ๊ฒ€์ฆ
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'spin') {
    header('Content-Type: application/json');
    
    // ์ž…๋ ฅ ๊ฒ€์ฆ
    $bet_amount = filter_var($_POST['bet_amount'], FILTER_VALIDATE_INT);
    if ($bet_amount === false || !in_array($bet_amount, [10, 50, 100, 500])) {
        echo json_encode(['success' => false, 'message' => '์ž˜๋ชป๋œ ๋ฒ ํŒ…์•ก์ž…๋‹ˆ๋‹ค.']);
        exit;
    }
    
    $symbols = json_decode($_POST['symbols'], true);
    if (!is_array($symbols) || count($symbols) !== 3) {
        echo json_encode(['success' => false, 'message' => '์ž˜๋ชป๋œ ์š”์ฒญ์ž…๋‹ˆ๋‹ค.']);
        exit;
    }
    
    $valid_symbols = ['๐Ÿ’', '๐Ÿ‹', '๐ŸŠ', '๐Ÿ‰', 'โญ', '๐Ÿ’Ž', '7๏ธโƒฃ'];
    foreach ($symbols as $symbol) {
        if (!in_array($symbol, $valid_symbols)) {
            echo json_encode(['success' => false, 'message' => '์ž˜๋ชป๋œ ์‹ฌ๋ณผ์ž…๋‹ˆ๋‹ค.']);
            exit;
        }
    }
    
    // ... ๋‚˜๋จธ์ง€ ์ฝ”๋“œ ...
}
```

**5. ์‚ฌ์šฉ์ž ์•ˆ๋‚ด ๋ฉ”์‹œ์ง€ ์ถ”๊ฐ€** (HTML):

```html
<!-- ๊ฒŒ์ž„ ์•ˆ๋‚ด -->
<div style="background: rgba(103,126,234,0.1); padding: 16px; border-radius: 8px; margin-bottom: 24px; border-left: 4px solid #667eea;">
    <h4 style="font-size: 1rem; font-weight: 700; margin-bottom: 8px;">โ„น๏ธ ๊ฒŒ์ž„ ์•ˆ๋‚ด</h4>
    <ul style="margin: 0; padding-left: 20px; color: var(--m-text-muted); font-size: 0.875rem; line-height: 1.6;">
        <li>๋ฒ ํŒ… ๊ธˆ์•ก์„ ์„ ํƒํ•˜๊ณ  SPIN ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ๋ฆด์„ ํšŒ์ „์‹œํ‚ค์„ธ์š”.</li>
        <li>3๊ฐœ ์‹ฌ๋ณผ์ด ๋ชจ๋‘ ์ผ์น˜ํ•˜๋ฉด ๋ฐฐ๋‹น์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.</li>
        <li>2๊ฐœ ์ผ์น˜ ์‹œ ๋ฒ ํŒ…์•ก์ด ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.</li>
        <li>์„ธ๋ธ 3๊ฐœ ์ผ์น˜ ์‹œ ์žญํŒŸ ๋‹ฌ์„ฑ! 100๋ฐฐ ๋ฐฐ๋‹น!</li>
        <li>์ฝ”์ธ์ด ๋ถ€์กฑํ•˜๋ฉด ์ผ์ผ ๋ณด๋„ˆ์Šค(500์ฝ”์ธ)๋ฅผ ๋ฐ›์œผ์„ธ์š”.</li>
        <li>์ด ๊ฒŒ์ž„์€ ๊ฐ€์ƒ ์ฝ”์ธ์œผ๋กœ ์ง„ํ–‰๋˜๋ฉฐ ์‹ค์ œ ๊ธˆ์ „๊ณผ ๋ฌด๊ด€ํ•ฉ๋‹ˆ๋‹ค.</li>
    </ul>
</div>
```

**์ตœ์ข… ํ…Œ์ŠคํŠธ ์ฒดํฌ๋ฆฌ์ŠคํŠธ**:

1. โœ… ํšŒ์› ์ „์šฉ ํŽ˜์ด์ง€ (๋น„ํšŒ์› ์ ‘๊ทผ ์ฐจ๋‹จ)
2. โœ… ๋ฆด ์Šคํ•€ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ •์ƒ ์ž‘๋™
3. โœ… ๋ฐฐ๋‹น ๊ณ„์‚ฐ ์ •ํ™•์„ฑ
4. โœ… ์ฝ”์ธ ์ž”์•ก ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ
5. โœ… ์žญํŒŸ ๋ชจ๋‹ฌ & ์‚ฌ์šด๋“œ ์žฌ์ƒ
6. โœ… ์Šคํ•€ ํžˆ์Šคํ† ๋ฆฌ ๊ธฐ๋ก
7. โœ… ์ผ์ผ ๋ณด๋„ˆ์Šค ์‹œ์Šคํ…œ
8. โœ… ๋ฐ˜์‘ํ˜• ๋””์ž์ธ (๋ชจ๋ฐ”์ผ/ํƒœ๋ธ”๋ฆฟ)
9. โœ… CSRF ํ† ํฐ ๊ฒ€์ฆ
10. โœ… SQL ์ธ์ ์…˜ ๋ฐฉ์–ด
11. โœ… XSS ๋ฐฉ์–ด (htmlspecialchars)
12. โœ… ์—๋Ÿฌ ํ•ธ๋“ค๋ง

๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•˜๊ณ , ๊ฐœ๋ฐœ์ž ๋„๊ตฌ ์ฝ˜์†”์— ์—๋Ÿฌ๊ฐ€ ์—†๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”. ๋ชจ๋ฐ”์ผ ๋ทฐ์—์„œ๋„ ์ •์ƒ ์ž‘๋™ํ•˜๋Š”์ง€ ํ…Œ์ŠคํŠธํ•˜์„ธ์š”.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ํ”„๋กœ์ ํŠธ๊ฐ€ ์™„์„ฑ๋˜๊ณ  ๋ชจ๋“  ๋ณด์•ˆ ์กฐ์น˜๊ฐ€ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. CSRF ํ† ํฐ์ด ๋ชจ๋“  POST ์š”์ฒญ์— ํฌํ•จ๋˜์–ด ๊ฒ€์ฆ๋ฉ๋‹ˆ๋‹ค. ์ž…๋ ฅ๊ฐ’์ด ์„œ๋ฒ„์—์„œ ์—„๊ฒฉํžˆ ๊ฒ€์ฆ๋˜๋ฉฐ SQL ์ธ์ ์…˜/XSS ๊ณต๊ฒฉ์ด ์ฐจ๋‹จ๋ฉ๋‹ˆ๋‹ค. ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ์‚ฌ์šฉ์ž ์นœํ™”์ ์ธ ๋ฉ”์‹œ์ง€๊ฐ€ ํ‘œ์‹œ๋˜๊ณ  ๋กœ๊ทธ์— ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค. ๊ฒŒ์ž„ ์•ˆ๋‚ด ๋ฉ”์‹œ์ง€๊ฐ€ ํ‘œ์‹œ๋˜์–ด ์‚ฌ์šฉ์ž๊ฐ€ ๊ทœ์น™์„ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ธฐ๋Šฅ์ด ์•ˆ์ •์ ์œผ๋กœ ์ž‘๋™ํ•˜๋ฉฐ ๋‹ค์–‘ํ•œ ํ™”๋ฉด ํฌ๊ธฐ์—์„œ ์ •์ƒ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

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

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

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

๋Œ“๊ธ€ (0)

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