๐ŸŽฒ ํ–‰์šด์˜ ์ฃผ์‚ฌ์œ„ - ๊ฒฐ์ • ์žฅ์•  ํ•ด๊ฒฐ์‚ฌ - ๋ฐ”์ด๋ธŒ์ฝ”๋”ฉ ๋ ˆ์‹œํ”ผ
์ž…๋ฌธ PHP g5se AI: Claude

๐ŸŽฒ ํ–‰์šด์˜ ์ฃผ์‚ฌ์œ„ - ๊ฒฐ์ • ์žฅ์•  ํ•ด๊ฒฐ์‚ฌ

๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ช… lucky-dice-decision-maker
์กฐํšŒ์ˆ˜ 75 0๊ฑด ์ œ์ถœ

๋ฌธ์ œ ์„ค๋ช…

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

๊ฒฐ์ • ์žฅ์• ๊ฐ€ ์žˆ๋Š” ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•œ ๊ท€์—ฌ์šด ์ฃผ์‚ฌ์œ„ ๊ตด๋ฆฌ๊ธฐ ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ 2~6๊ฐœ์˜ ์„ ํƒ์ง€๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ์ฃผ์‚ฌ์œ„๋ฅผ ๊ตด๋ ค์„œ ํ•˜๋‚˜๋ฅผ ๋žœ๋ค์œผ๋กœ ์„ ํƒํ•ด์ฃผ๋Š” ๊ฐ„๋‹จํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ˆ„๋ณด๋“œ5 SE์˜ modern shell์„ ํ™œ์šฉํ•˜์—ฌ ์‚ฌ์ดํŠธ ์ „์ฒด์™€ ํ†ต์ผ๋œ ๋””์ž์ธ์„ ์œ ์ง€ํ•˜๋ฉฐ, ๋‹คํฌ๋ชจ๋“œ๋„ ์ž๋™์œผ๋กœ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

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

  1. ์„ ํƒ์ง€ ์ž…๋ ฅ ํผ

    • 2~6๊ฐœ์˜ ํ…์ŠคํŠธ ์ž…๋ ฅ ํ•„๋“œ (๋™์  ์ถ”๊ฐ€/์‚ญ์ œ)
    • "์„ ํƒ์ง€ ์ถ”๊ฐ€" / "์‚ญ์ œ" ๋ฒ„ํŠผ
    • ๋นˆ ์„ ํƒ์ง€ ์ž…๋ ฅ ์‹œ ๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€
  2. ์ฃผ์‚ฌ์œ„ ์• ๋‹ˆ๋ฉ”์ด์…˜

    • "์ฃผ์‚ฌ์œ„ ๊ตด๋ฆฌ๊ธฐ" ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ 3D ํšŒ์ „ ์• ๋‹ˆ๋ฉ”์ด์…˜
    • ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ข…๋ฃŒ ํ›„ ๋‹น์ฒจ๋œ ์„ ํƒ์ง€๋ฅผ ํฌ๊ฒŒ ํ‘œ์‹œ
    • ์ฃผ์‚ฌ์œ„ ๋ฉด์— 1~6 ์ˆซ์ž ๋Œ€์‹  ์„ ํƒ์ง€ ๋ฒˆํ˜ธ ํ‘œ์‹œ
  3. ๊ฒฐ๊ณผ ํ‘œ์‹œ

    • ๋‹น์ฒจ๋œ ์„ ํƒ์ง€๋ฅผ ๊ฐ•์กฐ ํ‘œ์‹œ (ํฐ ๊ธ€์”จ + ๋ฐฐ๊ฒฝ์ƒ‰)
    • "๋‹ค์‹œ ๊ตด๋ฆฌ๊ธฐ" ๋ฒ„ํŠผ์œผ๋กœ ์žฌ์‹œ๋„ ๊ฐ€๋Šฅ
    • ์ด์ „ ๊ฒฐ๊ณผ ํžˆ์Šคํ† ๋ฆฌ (์ตœ๊ทผ 5๊ฐœ) ๋ฆฌ์ŠคํŠธ ํ‘œ์‹œ
  4. ๋ฐ˜์‘ํ˜• ๋””์ž์ธ

    • ๋ชจ๋ฐ”์ผ/๋ฐ์Šคํฌํ†ฑ ๋ชจ๋‘ ์ตœ์ ํ™”
    • ๋‹คํฌ๋ชจ๋“œ ์ž๋™ ๋Œ€์‘ (modern shell ํ† ํฐ ์‚ฌ์šฉ)

๐ŸŽ ๋ณด๋„ˆ์Šค ๊ธฐ๋Šฅ (์„ ํƒ)

  • ์ฃผ์‚ฌ์œ„ ๊ตด๋ฆผ ํšจ๊ณผ์Œ ์ถ”๊ฐ€ (Web Audio API)
  • ๋‹น์ฒจ ํ™•๋ฅ  ์กฐ์ • ์˜ต์…˜ (ํŠน์ • ์„ ํƒ์ง€์— ๊ฐ€์ค‘์น˜ ๋ถ€์—ฌ)
  • ํžˆ์Šคํ† ๋ฆฌ๋ฅผ localStorage์— ์ €์žฅํ•˜์—ฌ ํŽ˜์ด์ง€ ์ƒˆ๋กœ๊ณ ์นจ ํ›„์—๋„ ์œ ์ง€
  • ์„ ํƒ์ง€ ํ…œํ”Œ๋ฆฟ ํ”„๋ฆฌ์…‹ ("์ ์‹ฌ ๋ฉ”๋‰ด", "์˜ํ™” ์žฅ๋ฅด", "์šด๋™ ์ข…๋ฅ˜" ๋“ฑ)
  • ์ฃผ์‚ฌ์œ„ ์ƒ‰์ƒ/ํ…Œ๋งˆ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• (ํŒŒ๋ž€์ƒ‰, ๋นจ๊ฐ„์ƒ‰, ๋ฌด์ง€๊ฐœ ๋“ฑ)

๐Ÿ› ๏ธ ๊ธฐ์ˆ  ์Šคํƒ

  • ๋ฐฑ์—”๋“œ: PHP (๊ทธ๋ˆ„๋ณด๋“œ5 SE ๊ธฐ๋ฐ˜, DB ๋ถˆํ•„์š”)
  • ํ”„๋ก ํŠธ์—”๋“œ: HTML5, CSS3 (modern shell ํ† ํฐ), Vanilla JavaScript
  • ์• ๋‹ˆ๋ฉ”์ด์…˜: CSS3 Transform & Transition
  • ๋ผ์šฐํŒ…: g5se ํด๋” ๊ธฐ๋ฐ˜ ์ž๋™ ๋ผ์šฐํŒ… (app/modules/lucky-dice/index.php)

๐Ÿ“ ๊ตฌํ˜„ ์‹œ ์ฃผ์˜์‚ฌํ•ญ

  • DB๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋Š” ํด๋ผ์ด์–ธํŠธ ์ธก(JavaScript ๋ณ€์ˆ˜ ๋˜๋Š” localStorage)์—์„œ ๊ด€๋ฆฌ
  • modern shell์˜ --m-surface, --m-text, --m-primary ๋“ฑ CSS ๋ณ€์ˆ˜๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋‹คํฌ๋ชจ๋“œ ์ž๋™ ๋Œ€์‘
  • ์ฃผ์‚ฌ์œ„ ์• ๋‹ˆ๋ฉ”์ด์…˜์€ CSS @keyframes์™€ transform: rotate3d()๋ฅผ ์‚ฌ์šฉ
  • ์„ ํƒ์ง€ ๊ฐœ์ˆ˜์— ๋”ฐ๋ผ ์ฃผ์‚ฌ์œ„ ๋ฉด ๊ฐœ์ˆ˜๋ฅผ ๋™์ ์œผ๋กœ ์กฐ์ • (2๊ฐœ๋ฉด ๋™์ „, 3~6๊ฐœ๋ฉด ํ•ด๋‹น ๋ฉด ์ฃผ์‚ฌ์œ„)
  • XSS ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉ์ž ์ž…๋ ฅ์€ htmlspecialchars()๋กœ ์ด์Šค์ผ€์ดํ”„ (์ถœ๋ ฅ ์‹œ)

์ œ์•ฝ ์กฐ๊ฑด

  • โ€ข ๊ทธ๋ˆ„๋ณด๋“œ5 SE ์„ค์น˜๋ณธ ์œ„์— ์ž‘์—… (https://github.com/gnuboard/g5se main ๋ธŒ๋žœ์น˜)
  • โ€ข ํด๋” ๊ธฐ๋ฐ˜ ์ž๋™ ๋ผ์šฐํŒ… ์‚ฌ์šฉ (`app/modules/lucky-dice/index.php` โ†’ `/lucky-dice`)
  • โ€ข modern shell ํ•„์ˆ˜ ์ ์šฉ (`_head.inc.php` + `m-shell` + `_nav.inc.php` + `_footer.inc.php`)
  • โ€ข ๋””์ž์ธ ํ† ํฐ ์‚ฌ์šฉ (`--m-surface`, `--m-text`, `--m-primary` ๋“ฑ) โ€” ๋‹คํฌ๋ชจ๋“œ ์ž๋™ ๋Œ€์‘
  • โ€ข DB ์‚ฌ์šฉ ๊ธˆ์ง€ (๋ชจ๋“  ์ƒํƒœ๋Š” ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ ๊ด€๋ฆฌ)
  • โ€ข ์™ธ๋ถ€ ๋นŒ๋“œ ๋„๊ตฌ(npm/vite) ์‚ฌ์šฉ ๊ธˆ์ง€, CDN ์˜์กด ์ตœ์†Œํ™”
  • โ€ข ์‚ฌ์šฉ์ž ์ž…๋ ฅ์€ `htmlspecialchars()`๋กœ ์ด์Šค์ผ€์ดํ”„
  • โ€ข Laravel/Composer/React/Vue ์ ˆ๋Œ€ ์‚ฌ์šฉ ๊ธˆ์ง€ โ€” ์ˆœ์ˆ˜ PHP + g5se๋งŒ ์‚ฌ์šฉ

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

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

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

## Step 1: ํ”„๋กœ์ ํŠธ ๊ธฐ๋ณธ ๊ณจ๊ฒฉ ์ƒ์„ฑ

`app/modules/lucky-dice/index.php` ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ  ๋‹ค์Œ ๋‚ด์šฉ์„ ์ž‘์„ฑํ•˜์„ธ์š”.

**์š”๊ตฌ์‚ฌํ•ญ:**
1. g5se modern shell ๊ณจ๊ฒฉ ์ ์šฉ (์•„๋ž˜ ๊ตฌ์กฐ ํ•„์ˆ˜)
2. ํŽ˜์ด์ง€ ์ œ๋ชฉ: "๐ŸŽฒ ํ–‰์šด์˜ ์ฃผ์‚ฌ์œ„ - ๊ฒฐ์ • ์žฅ์•  ํ•ด๊ฒฐ์‚ฌ"
3. ๊ฐ„๋‹จํ•œ ์„ค๋ช… ๋ฌธ๊ตฌ์™€ "์ฃผ์‚ฌ์œ„ ๊ตด๋ฆฌ๊ธฐ ์‹œ์ž‘" ๋ฒ„ํŠผ ๋ฐฐ์น˜
4. ๋‹คํฌ๋ชจ๋“œ ์ž๋™ ๋Œ€์‘์„ ์œ„ํ•œ CSS ๋ณ€์ˆ˜ ์‚ฌ์šฉ (`--m-surface`, `--m-text` ๋“ฑ)

**modern shell ๊ณจ๊ฒฉ (๋ฐ˜๋“œ์‹œ ์ด ๊ตฌ์กฐ๋ฅผ ๋”ฐ๋ฅผ ๊ฒƒ):**

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

// ํŽ˜์ด์ง€ ๋กœ์ง์€ ์—ฌ๊ธฐ์—

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: 12px;">๐ŸŽฒ ํ–‰์šด์˜ ์ฃผ์‚ฌ์œ„</h1>
        <p style="color: var(--m-text-muted); margin-bottom: 32px;">๊ฒฐ์ • ์žฅ์• ๊ฐ€ ์žˆ์œผ์‹ ๊ฐ€์š”? ์ฃผ์‚ฌ์œ„๊ฐ€ ๋Œ€์‹  ์„ ํƒํ•ด๋“œ๋ฆฝ๋‹ˆ๋‹ค!</p>
        
        <div style="background: var(--m-surface); padding: 24px; border-radius: var(--m-radius-lg); box-shadow: var(--m-shadow);">
            <p style="margin-bottom: 16px;">์•„๋ž˜์— 2~6๊ฐœ์˜ ์„ ํƒ์ง€๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ์ฃผ์‚ฌ์œ„๋ฅผ ๊ตด๋ ค๋ณด์„ธ์š”.</p>
            <button id="startBtn" style="background: var(--m-primary); color: white; padding: 12px 24px; border: none; border-radius: 8px; font-size: 1rem; cursor: pointer;">
                ์‹œ์ž‘ํ•˜๊ธฐ
            </button>
        </div>
    </main>
    <?php require G5_THEME_PATH.'/modern/_footer.inc.php'; ?>
</div>
<?php include_once('./tail.sub.php'); ?>

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

2
Step 2
## Step 2: ์„ ํƒ์ง€ ์ž…๋ ฅ ํผ ๊ตฌํ˜„

`app/modules/lucky-dice/index.php`๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ ๋‹ค์Œ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

**์š”๊ตฌ์‚ฌํ•ญ:**
1. ์„ ํƒ์ง€ ์ž…๋ ฅ ํ•„๋“œ 2๊ฐœ๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ํ‘œ์‹œ (์ตœ๋Œ€ 6๊ฐœ๊นŒ์ง€ ์ถ”๊ฐ€ ๊ฐ€๋Šฅ)
2. "+ ์„ ํƒ์ง€ ์ถ”๊ฐ€" ๋ฒ„ํŠผ (์ตœ๋Œ€ 6๊ฐœ ์ œํ•œ)
3. ๊ฐ ์ž…๋ ฅ ํ•„๋“œ ์˜†์— "์‚ญ์ œ" ๋ฒ„ํŠผ (์ตœ์†Œ 2๊ฐœ๋Š” ์œ ์ง€)
4. "์ฃผ์‚ฌ์œ„ ๊ตด๋ฆฌ๊ธฐ" ๋ฒ„ํŠผ (์„ ํƒ์ง€ 2๊ฐœ ๋ฏธ๋งŒ์ด๋ฉด ๋น„ํ™œ์„ฑํ™”)
5. ๋นˆ ์„ ํƒ์ง€๊ฐ€ ์žˆ์œผ๋ฉด ๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ

**JavaScript ๋กœ์ง:**
- `addChoice()`: ์„ ํƒ์ง€ ์ž…๋ ฅ ํ•„๋“œ ์ถ”๊ฐ€ (์ตœ๋Œ€ 6๊ฐœ)
- `removeChoice(index)`: ์„ ํƒ์ง€ ์‚ญ์ œ (์ตœ์†Œ 2๊ฐœ ์œ ์ง€)
- `rollDice()`: ์ฃผ์‚ฌ์œ„ ๊ตด๋ฆฌ๊ธฐ (๋นˆ ์„ ํƒ์ง€ ๊ฒ€์ฆ)

**HTML ๊ตฌ์กฐ ์˜ˆ์‹œ:**
```html
<div id="choiceInputs">
    <div class="choice-item" style="display: flex; gap: 8px; margin-bottom: 12px;">
        <input type="text" class="choice-input" placeholder="์„ ํƒ์ง€ 1" style="flex: 1; padding: 10px; border: 1px solid var(--m-text-muted); border-radius: 6px; background: var(--m-surface); color: var(--m-text);">
        <button onclick="removeChoice(0)" style="padding: 10px 16px; background: #f44336; color: white; border: none; border-radius: 6px; cursor: pointer;">์‚ญ์ œ</button>
    </div>
</div>
<button onclick="addChoice()" style="margin-top: 12px; padding: 10px 20px; background: var(--m-primary); color: white; border: none; border-radius: 6px; cursor: pointer;">+ ์„ ํƒ์ง€ ์ถ”๊ฐ€</button>
<button id="rollBtn" onclick="rollDice()" style="margin-top: 20px; width: 100%; padding: 16px; background: #4CAF50; color: white; border: none; border-radius: 8px; font-size: 1.1rem; font-weight: 700; cursor: pointer;">๐ŸŽฒ ์ฃผ์‚ฌ์œ„ ๊ตด๋ฆฌ๊ธฐ</button>
```

**JavaScript ํ•จ์ˆ˜ ๊ตฌํ˜„:**
```javascript
let choiceCount = 2;

function addChoice() {
    if (choiceCount >= 6) {
        alert('์ตœ๋Œ€ 6๊ฐœ๊นŒ์ง€๋งŒ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.');
        return;
    }
    choiceCount++;
    const container = document.getElementById('choiceInputs');
    const div = document.createElement('div');
    div.className = 'choice-item';
    div.style.cssText = 'display: flex; gap: 8px; margin-bottom: 12px;';
    div.innerHTML = `
        <input type="text" class="choice-input" placeholder="์„ ํƒ์ง€ ${choiceCount}" style="flex: 1; padding: 10px; border: 1px solid var(--m-text-muted); border-radius: 6px; background: var(--m-surface); color: var(--m-text);">
        <button onclick="removeChoice(${choiceCount - 1})" style="padding: 10px 16px; background: #f44336; color: white; border: none; border-radius: 6px; cursor: pointer;">์‚ญ์ œ</button>
    `;
    container.appendChild(div);
}

function removeChoice(index) {
    if (choiceCount <= 2) {
        alert('์ตœ์†Œ 2๊ฐœ์˜ ์„ ํƒ์ง€๋Š” ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.');
        return;
    }
    const items = document.querySelectorAll('.choice-item');
    items[index].remove();
    choiceCount--;
}

function rollDice() {
    const inputs = document.querySelectorAll('.choice-input');
    const choices = Array.from(inputs).map(input => input.value.trim()).filter(v => v !== '');
    
    if (choices.length < 2) {
        alert('์ตœ์†Œ 2๊ฐœ์˜ ์„ ํƒ์ง€๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.');
        return;
    }
    
    // ๋‹ค์Œ ๋‹จ๊ณ„์—์„œ ์ฃผ์‚ฌ์œ„ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ตฌํ˜„ ์˜ˆ์ •
    alert('์„ ํƒ์ง€: ' + choices.join(', '));
}
```

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

3
Step 3
## Step 3: ์ฃผ์‚ฌ์œ„ ์• ๋‹ˆ๋ฉ”์ด์…˜ & ๊ฒฐ๊ณผ ํ‘œ์‹œ

`app/modules/lucky-dice/index.php`๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ ์ฃผ์‚ฌ์œ„ ๊ตด๋ฆผ ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ ๊ฒฐ๊ณผ ํ‘œ์‹œ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

**์š”๊ตฌ์‚ฌํ•ญ:**
1. ์ฃผ์‚ฌ์œ„ ๊ตด๋ฆผ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ 3D ํšŒ์ „ ์• ๋‹ˆ๋ฉ”์ด์…˜ (2์ดˆ ๋™์•ˆ)
2. ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ข…๋ฃŒ ํ›„ ๋žœ๋ค์œผ๋กœ ์„ ํƒ๋œ ๊ฒฐ๊ณผ๋ฅผ ํฐ ์นด๋“œ๋กœ ํ‘œ์‹œ
3. ๊ฒฐ๊ณผ ์นด๋“œ์— "๐ŸŽ‰ ๋‹น์ฒจ!" ๋ฌธ๊ตฌ์™€ ์„ ํƒ๋œ ํ•ญ๋ชฉ ํ‘œ์‹œ
4. "๋‹ค์‹œ ๊ตด๋ฆฌ๊ธฐ" ๋ฒ„ํŠผ์œผ๋กœ ์žฌ์‹œ๋„ ๊ฐ€๋Šฅ
5. ์ตœ๊ทผ 5๊ฐœ ๊ฒฐ๊ณผ ํžˆ์Šคํ† ๋ฆฌ ๋ฆฌ์ŠคํŠธ ํ‘œ์‹œ

**CSS ์• ๋‹ˆ๋ฉ”์ด์…˜:**
```css
@keyframes diceRoll {
    0% { transform: rotate3d(1, 1, 1, 0deg); }
    25% { transform: rotate3d(1, 1, 1, 180deg) scale(1.2); }
    50% { transform: rotate3d(1, 1, 1, 360deg) scale(1); }
    75% { transform: rotate3d(1, 1, 1, 540deg) scale(1.2); }
    100% { transform: rotate3d(1, 1, 1, 720deg) scale(1); }
}

.dice-rolling {
    animation: diceRoll 2s cubic-bezier(0.68, -0.55, 0.27, 1.55);
}
```

**JavaScript ๋กœ์ง ์ˆ˜์ •:**
```javascript
let history = [];

function rollDice() {
    const inputs = document.querySelectorAll('.choice-input');
    const choices = Array.from(inputs).map(input => input.value.trim()).filter(v => v !== '');
    
    if (choices.length < 2) {
        alert('์ตœ์†Œ 2๊ฐœ์˜ ์„ ํƒ์ง€๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.');
        return;
    }
    
    // ์ฃผ์‚ฌ์œ„ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์ž‘
    const diceElement = document.getElementById('diceAnimation');
    diceElement.classList.add('dice-rolling');
    document.getElementById('rollBtn').disabled = true;
    
    // 2์ดˆ ํ›„ ๊ฒฐ๊ณผ ํ‘œ์‹œ
    setTimeout(() => {
        diceElement.classList.remove('dice-rolling');
        document.getElementById('rollBtn').disabled = false;
        
        const winner = choices[Math.floor(Math.random() * choices.length)];
        showResult(winner);
        addToHistory(winner);
    }, 2000);
}

function showResult(winner) {
    const resultDiv = document.getElementById('result');
    resultDiv.innerHTML = `
        <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 32px; border-radius: var(--m-radius-lg); text-align: center; margin-top: 24px; color: white;">
            <div style="font-size: 3rem; margin-bottom: 8px;">๐ŸŽ‰</div>
            <h2 style="font-size: 1.5rem; font-weight: 700; margin-bottom: 8px;">๋‹น์ฒจ!</h2>
            <p style="font-size: 2rem; font-weight: 700;">${winner}</p>
        </div>
    `;
    resultDiv.style.display = 'block';
}

function addToHistory(item) {
    history.unshift(item);
    if (history.length > 5) history.pop();
    
    const historyDiv = document.getElementById('history');
    historyDiv.innerHTML = '<h3 style="font-size: 1.2rem; font-weight: 700; margin-bottom: 12px;">์ตœ๊ทผ ๊ฒฐ๊ณผ</h3>' +
        history.map((h, i) => `<div style="padding: 8px 12px; background: var(--m-surface); border-radius: 6px; margin-bottom: 8px;">${i + 1}. ${h}</div>`).join('');
    historyDiv.style.display = 'block';
}
```

**HTML ์ถ”๊ฐ€:**
```html
<div id="diceAnimation" style="width: 100px; height: 100px; margin: 24px auto; background: var(--m-primary); border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 3rem; color: white;">
    ๐ŸŽฒ
</div>
<div id="result" style="display: none;"></div>
<div id="history" style="display: none; margin-top: 24px;"></div>
```

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ์ฃผ์‚ฌ์œ„ ๊ตด๋ฆฌ๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์ฃผ์‚ฌ์œ„ ์ด๋ชจ์ง€(๐ŸŽฒ)๊ฐ€ 2์ดˆ ๋™์•ˆ 3D ํšŒ์ „ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ๋๋‚˜๋ฉด ๋žœ๋ค์œผ๋กœ ์„ ํƒ๋œ ํ•ญ๋ชฉ์ด ๋ณด๋ผ์ƒ‰ ๊ทธ๋ผ๋ฐ์ด์…˜ ์นด๋“œ์— ํฌ๊ฒŒ ํ‘œ์‹œ๋˜๊ณ , ํ•˜๋‹จ์— ์ตœ๊ทผ 5๊ฐœ ๊ฒฐ๊ณผ ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ๋ฆฌ์ŠคํŠธ๋กœ ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค. ๋‹คํฌ๋ชจ๋“œ์—์„œ๋„ ์ƒ‰์ƒ์ด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

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

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

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

๋Œ“๊ธ€ (0)

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