๐ŸŽฒ ํ™•๋ฅ  ์‹คํ—˜์‹ค - ์ฃผ์‚ฌ์œ„/๋™์ „/์นด๋“œ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ - ๋ฐ”์ด๋ธŒ์ฝ”๋”ฉ ๋ ˆ์‹œํ”ผ
์ž…๋ฌธ PHP g5se AI: Claude

๐ŸŽฒ ํ™•๋ฅ  ์‹คํ—˜์‹ค - ์ฃผ์‚ฌ์œ„/๋™์ „/์นด๋“œ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ

๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ช… g5se-probability-lab
์กฐํšŒ์ˆ˜ 60 0๊ฑด ์ œ์ถœ

๋ฌธ์ œ ์„ค๋ช…

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

ํ™•๋ฅ ๊ณผ ํ†ต๊ณ„๋ฅผ ์ง๊ด€์ ์œผ๋กœ ์ฒดํ—˜ํ•  ์ˆ˜ ์žˆ๋Š” ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์ž…๋‹ˆ๋‹ค. ์ฃผ์‚ฌ์œ„, ๋™์ „, ์นด๋“œ ๋ฝ‘๊ธฐ ๋“ฑ ๋‹ค์–‘ํ•œ ํ™•๋ฅ  ์‹คํ—˜์„ ๋ฐ˜๋ณต ์‹คํ–‰ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์‹ค์‹œ๊ฐ„ ์ฐจํŠธ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ˆ˜ํ•™ ๊ต์œก, ํ™•๋ฅ  ํ•™์Šต, ๋˜๋Š” ๋‹จ์ˆœํžˆ "์ด ์ฃผ์‚ฌ์œ„ ์ •๋ง ๊ณต์ •ํ•œ๊ฐ€?" ๊ฐ™์€ ๊ถ๊ธˆ์ฆ์„ ํ•ด๊ฒฐํ•˜๋Š” ์žฌ๋ฏธ์žˆ๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

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

  1. 3๊ฐ€์ง€ ์‹คํ—˜ ๋ชจ๋“œ

    • ์ฃผ์‚ฌ์œ„ ๋˜์ง€๊ธฐ (1~6)
    • ๋™์ „ ๋˜์ง€๊ธฐ (์•ž๋ฉด/๋’ท๋ฉด)
    • ์นด๋“œ ๋ฝ‘๊ธฐ (52์žฅ ๋ฑ์—์„œ ๋ฌด์ž‘์œ„)
  2. ์‹คํ—˜ ์„ค์ •

    • ์‹คํ—˜ ํšŸ์ˆ˜ ์ž…๋ ฅ (10ํšŒ ~ 10000ํšŒ)
    • "์‹คํ—˜ ์‹œ์ž‘" ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์ฆ‰์‹œ ์‹คํ–‰
  3. ๊ฒฐ๊ณผ ์‹œ๊ฐํ™”

    • ๊ฐ ๊ฒฐ๊ณผ๋ณ„ ์ถœํ˜„ ํšŸ์ˆ˜ ๋ง‰๋Œ€ ๊ทธ๋ž˜ํ”„ (CSS๋กœ ๊ตฌํ˜„)
    • ์ด๋ก ์  ํ™•๋ฅ  vs ์‹ค์ œ ํ™•๋ฅ  ๋น„๊ต ํ‘œ์‹œ
    • ์ด ์‹คํ—˜ ํšŸ์ˆ˜, ์ตœ๋‹ค ์ถœํ˜„ ๊ฒฐ๊ณผ ํ•˜์ด๋ผ์ดํŠธ
  4. ํžˆ์Šคํ† ๋ฆฌ ๋กœ๊ทธ

    • ์ตœ๊ทผ 20๊ฐœ ์‹คํ—˜ ๊ฒฐ๊ณผ ํƒ€์ž„๋ผ์ธ
    • "๋ชจ๋‘ ์ง€์šฐ๊ธฐ" ๋ฒ„ํŠผ
  5. ๋‹คํฌ๋ชจ๋“œ ๋Œ€์‘

    • modern shell์˜ ๋””์ž์ธ ํ† ํฐ ํ™œ์šฉ
    • ์ฐจํŠธ ์ƒ‰์ƒ๋„ ๋‹คํฌ๋ชจ๋“œ์—์„œ ๊ฐ€๋…์„ฑ ์œ ์ง€

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

  • ์• ๋‹ˆ๋ฉ”์ด์…˜: ์‹คํ—˜ ์‹คํ–‰ ์ค‘ "์ฃผ์‚ฌ์œ„ ๊ตด๋Ÿฌ๊ฐ€๋Š”" ํšจ๊ณผ
  • CSV ๋‹ค์šด๋กœ๋“œ: ์‹คํ—˜ ๊ฒฐ๊ณผ๋ฅผ ํŒŒ์ผ๋กœ ์ €์žฅ
  • ์‚ฌ์šฉ์ž ์ •์˜ ์ฃผ์‚ฌ์œ„: 6๋ฉด ๋Œ€์‹  4๋ฉด/8๋ฉด/20๋ฉด ์„ ํƒ
  • ์—ฐ์† ์‹คํ—˜ ๋น„๊ต: ์—ฌ๋Ÿฌ ์‹คํ—˜์„ ๋‚˜๋ž€ํžˆ ๋น„๊ตํ•˜๋Š” ๋Œ€์‹œ๋ณด๋“œ

๐ŸŽจ UI/UX ํฌ์ธํŠธ

  • ์‹คํ—˜ ๋ชจ๋“œ ์„ ํƒ์€ ํฐ ์นด๋“œํ˜• ๋ฒ„ํŠผ (์•„์ด์ฝ˜ + ์ด๋ฆ„)
  • ๊ฒฐ๊ณผ ์ฐจํŠธ๋Š” ์‹ค์‹œ๊ฐ„์œผ๋กœ ์• ๋‹ˆ๋ฉ”์ด์…˜ํ•˜๋ฉฐ ์ฑ„์›Œ์ง
  • ์ด๋ก  ํ™•๋ฅ  ๋Œ€๋น„ ์˜ค์ฐจ๊ฐ€ ํด ๊ฒฝ์šฐ ๊ฒฝ๊ณ  ๋ฐฐ์ง€ ํ‘œ์‹œ
  • ๋ชจ๋ฐ”์ผ์—์„œ๋„ ์ฐจํŠธ๊ฐ€ ๋ช…ํ™•ํžˆ ๋ณด์ด๋„๋ก ๋ฐ˜์‘ํ˜• ๋””์ž์ธ

์ œ์•ฝ ์กฐ๊ฑด

  • โ€ข g5se ํ”„๋ ˆ์ž„์›Œํฌ ์‚ฌ์šฉ (Laravel/Composer/npm ๊ธˆ์ง€)
  • โ€ข ํด๋” ์ž๋™ ๋ผ์šฐํŒ…: app/modules/probability-lab/index.php ์ƒ์„ฑ
  • โ€ข modern shell ์ ์šฉ ํ•„์ˆ˜ (_head.inc.php + m-shell + _nav.inc.php + m-container + _footer.inc.php)
  • โ€ข ๋””์ž์ธ ํ† ํฐ ์‚ฌ์šฉ: var(--m-surface), var(--m-text), var(--m-shadow) ๋“ฑ
  • โ€ข SQLite DB ์‚ฌ์šฉ: G5_DATA_PATH.'/modules/probability-lab.sqlite' (ํžˆ์Šคํ† ๋ฆฌ ์ €์žฅ์šฉ)
  • โ€ข ์ˆœ์ˆ˜ HTML/CSS/JS๋กœ ์ฐจํŠธ ๊ตฌํ˜„ (Chart.js ๊ฐ™์€ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ธˆ์ง€)
  • โ€ข prepared statement๋กœ SQL ์ธ์ ์…˜ ๋ฐฉ์ง€
  • โ€ข ์ถœ๋ ฅ ์ด์Šค์ผ€์ดํ”„: htmlspecialchars() ์‚ฌ์šฉ
  • โ€ข ๋ชจ๋ฐ”์ผ ๋ฐ˜์‘ํ˜• ๋””์ž์ธ (๋ฏธ๋””์–ด ์ฟผ๋ฆฌ ํ™œ์šฉ)

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

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

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

**Step 1: ํ”„๋กœ์ ํŠธ ๊ณจ๊ฒฉ & modern shell ์ ์šฉ**

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

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

// DB ์—ฐ๊ฒฐ (ํžˆ์Šคํ† ๋ฆฌ ์ €์žฅ์šฉ)
$db_dir = G5_DATA_PATH.'/modules';
if (!is_dir($db_dir)) {
    @mkdir($db_dir, 0707, true);
}
$db_path = $db_dir.'/probability-lab.sqlite';
$pdo = new PDO('sqlite:'.$db_path);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// ํ…Œ์ด๋ธ” ์ƒ์„ฑ (์ตœ์ดˆ 1ํšŒ)
$pdo->exec("
    CREATE TABLE IF NOT EXISTS experiments (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        mode TEXT NOT NULL,
        trials INTEGER NOT NULL,
        results TEXT NOT NULL,
        created_at DATETIME DEFAULT CURRENT_TIMESTAMP
    )
");

require_once(G5_THEME_PATH.'/modern/_head.inc.php');
include_once('./head.sub.php');
?>
<style>
.lab-header {
    text-align: center;
    margin-bottom: 32px;
}
.lab-header h1 {
    font-size: 2rem;
    font-weight: 700;
    color: var(--m-text);
    margin-bottom: 8px;
}
.lab-header p {
    color: var(--m-text-muted);
    font-size: 1rem;
}
.mode-selector {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 16px;
    margin-bottom: 32px;
}
.mode-card {
    background: var(--m-surface);
    border: 2px solid transparent;
    border-radius: var(--m-radius-lg);
    padding: 24px;
    text-align: center;
    cursor: pointer;
    transition: all 0.2s;
    box-shadow: var(--m-shadow);
}
.mode-card:hover {
    border-color: var(--m-primary);
    transform: translateY(-4px);
}
.mode-card.active {
    border-color: var(--m-primary);
    background: linear-gradient(135deg, rgba(79, 70, 229, 0.1), rgba(99, 102, 241, 0.05));
}
.mode-icon {
    font-size: 3rem;
    margin-bottom: 12px;
}
.mode-name {
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--m-text);
}
</style>
<div class="m-shell">
    <?php require G5_THEME_PATH.'/modern/_nav.inc.php'; ?>
    <main class="m-container" style="padding: 32px 20px 48px;">
        <div class="lab-header">
            <h1>๐ŸŽฒ ํ™•๋ฅ  ์‹คํ—˜์‹ค</h1>
            <p>์ฃผ์‚ฌ์œ„, ๋™์ „, ์นด๋“œ โ€” ํ™•๋ฅ ์˜ ์„ธ๊ณ„๋ฅผ ์ฒดํ—˜ํ•˜์„ธ์š”</p>
        </div>

        <div class="mode-selector">
            <div class="mode-card active" data-mode="dice">
                <div class="mode-icon">๐ŸŽฒ</div>
                <div class="mode-name">์ฃผ์‚ฌ์œ„</div>
            </div>
            <div class="mode-card" data-mode="coin">
                <div class="mode-icon">๐Ÿช™</div>
                <div class="mode-name">๋™์ „</div>
            </div>
            <div class="mode-card" data-mode="card">
                <div class="mode-icon">๐Ÿƒ</div>
                <div class="mode-name">์นด๋“œ</div>
            </div>
        </div>

        <div style="text-align: center; color: var(--m-text-muted); padding: 40px;">
            ์‹คํ—˜ ์„ค์ • UI๋Š” Step 2์—์„œ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
        </div>
    </main>
    <?php require G5_THEME_PATH.'/modern/_footer.inc.php'; ?>
</div>
<?php include_once('./tail.sub.php'); ?>
```

์ด ๋‹จ๊ณ„์—์„œ๋Š”:
- modern shell ๊ณจ๊ฒฉ ์ ์šฉ
- SQLite DB ์ดˆ๊ธฐํ™” (experiments ํ…Œ์ด๋ธ”)
- 3๊ฐ€์ง€ ์‹คํ—˜ ๋ชจ๋“œ ์„ ํƒ ์นด๋“œ UI
- ๋””์ž์ธ ํ† ํฐ์œผ๋กœ ๋‹คํฌ๋ชจ๋“œ ์ž๋™ ๋Œ€์‘

๋ธŒ๋ผ์šฐ์ €์—์„œ `/probability-lab` ์ ‘์† ์‹œ ๋ชจ๋“œ ์„ ํƒ ์นด๋“œ 3๊ฐœ๊ฐ€ ๋ณด์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: `/probability-lab` URL๋กœ ์ ‘์†ํ•˜๋ฉด modern shell์ด ์ ์šฉ๋œ ํŽ˜์ด์ง€๊ฐ€ ๋‚˜ํƒ€๋‚˜๊ณ , ์ฃผ์‚ฌ์œ„/๋™์ „/์นด๋“œ 3๊ฐœ์˜ ๋ชจ๋“œ ์„ ํƒ ์นด๋“œ๊ฐ€ ๊ทธ๋ฆฌ๋“œ๋กœ ๋ฐฐ์น˜๋ฉ๋‹ˆ๋‹ค. ์นด๋“œ ํด๋ฆญ ์‹œ active ํด๋ž˜์Šค๊ฐ€ ํ† ๊ธ€๋˜๋ฉฐ ์„ ํƒ ์ƒํƒœ๊ฐ€ ์‹œ๊ฐ์ ์œผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. SQLite DB ํŒŒ์ผ์ด ์ž๋™ ์ƒ์„ฑ๋˜๊ณ  experiments ํ…Œ์ด๋ธ”์ด ์ค€๋น„๋ฉ๋‹ˆ๋‹ค.

2
Step 2
**Step 2: ์‹คํ—˜ ์„ค์ • ํผ & ์‹คํ–‰ ๋กœ์ง ์ถ”๊ฐ€**

`app/modules/probability-lab/index.php` ํŒŒ์ผ์„ ์ˆ˜์ •ํ•˜์—ฌ ์‹คํ—˜ ์„ค์ • UI์™€ ๋ฐฑ์—”๋“œ ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

**์ˆ˜์ • ๋‚ด์šฉ:**

1. **AJAX ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ** (ํŒŒ์ผ ์ƒ๋‹จ, `$pdo` ์ •์˜ ์งํ›„์— ์ถ”๊ฐ€):
```php
// AJAX ์š”์ฒญ ์ฒ˜๋ฆฌ
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
    header('Content-Type: application/json');
    
    if ($_POST['action'] === 'run_experiment') {
        $mode = $_POST['mode'] ?? 'dice';
        $trials = max(10, min(10000, intval($_POST['trials'] ?? 100)));
        
        $results = [];
        for ($i = 0; $i < $trials; $i++) {
            if ($mode === 'dice') {
                $results[] = rand(1, 6);
            } elseif ($mode === 'coin') {
                $results[] = rand(0, 1) === 0 ? 'heads' : 'tails';
            } else { // card
                $suits = ['โ™ ', 'โ™ฅ', 'โ™ฆ', 'โ™ฃ'];
                $ranks = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
                $results[] = $ranks[rand(0, 12)] . $suits[rand(0, 3)];
            }
        }
        
        // DB ์ €์žฅ
        $stmt = $pdo->prepare("INSERT INTO experiments (mode, trials, results) VALUES (?, ?, ?)");
        $stmt->execute([$mode, $trials, json_encode($results)]);
        
        echo json_encode([
            'success' => true,
            'results' => $results,
            'id' => $pdo->lastInsertId()
        ]);
        exit;
    }
}
```

2. **์‹คํ—˜ ์„ค์ • UI ์ถ”๊ฐ€** (`<div class="mode-selector">` ๋‹ค์Œ์—):
```php
<div class="experiment-control" style="background: var(--m-surface); padding: 24px; border-radius: var(--m-radius-lg); box-shadow: var(--m-shadow); margin-bottom: 32px;">
    <div style="margin-bottom: 16px;">
        <label style="display: block; margin-bottom: 8px; font-weight: 600; color: var(--m-text);">์‹คํ—˜ ํšŸ์ˆ˜</label>
        <input type="number" id="trials" min="10" max="10000" value="100" style="width: 100%; padding: 12px; border: 1px solid var(--m-text-muted); border-radius: 8px; background: var(--m-surface); color: var(--m-text); font-size: 1rem;">
    </div>
    <button id="runBtn" style="width: 100%; padding: 16px; background: linear-gradient(135deg, #4f46e5, #6366f1); color: white; border: none; border-radius: 8px; font-size: 1.1rem; font-weight: 600; cursor: pointer; transition: all 0.2s;">๐Ÿš€ ์‹คํ—˜ ์‹œ์ž‘</button>
</div>

<div id="resultsContainer" style="display: none;"></div>
```

3. **JavaScript ์ถ”๊ฐ€** (`</main>` ์ง์ „์—):
```php
<script>
let currentMode = 'dice';

// ๋ชจ๋“œ ์„ ํƒ
document.querySelectorAll('.mode-card').forEach(card => {
    card.addEventListener('click', () => {
        document.querySelectorAll('.mode-card').forEach(c => c.classList.remove('active'));
        card.classList.add('active');
        currentMode = card.dataset.mode;
    });
});

// ์‹คํ—˜ ์‹คํ–‰
document.getElementById('runBtn').addEventListener('click', async () => {
    const trials = document.getElementById('trials').value;
    const btn = document.getElementById('runBtn');
    
    btn.disabled = true;
    btn.textContent = 'โณ ์‹คํ—˜ ์ค‘...';
    
    const formData = new FormData();
    formData.append('action', 'run_experiment');
    formData.append('mode', currentMode);
    formData.append('trials', trials);
    
    const response = await fetch(window.location.href, {
        method: 'POST',
        body: formData
    });
    
    const data = await response.json();
    
    if (data.success) {
        displayResults(data.results);
    }
    
    btn.disabled = false;
    btn.textContent = '๐Ÿš€ ์‹คํ—˜ ์‹œ์ž‘';
});

function displayResults(results) {
    const container = document.getElementById('resultsContainer');
    container.style.display = 'block';
    container.innerHTML = '<div style="text-align: center; padding: 40px; color: var(--m-text);">๊ฒฐ๊ณผ ์ฐจํŠธ๋Š” Step 3์—์„œ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.<br>์ด ' + results.length + 'ํšŒ ์‹คํ—˜ ์™„๋ฃŒ</div>';
}
</script>
```

์ด ๋‹จ๊ณ„์—์„œ๋Š”:
- ์‹คํ—˜ ํšŸ์ˆ˜ ์ž…๋ ฅ ํผ
- "์‹คํ—˜ ์‹œ์ž‘" ๋ฒ„ํŠผ
- AJAX๋กœ ์‹คํ—˜ ์‹คํ–‰ & ๊ฒฐ๊ณผ ์ €์žฅ
- ์‹คํ—˜ ๊ฒฐ๊ณผ ๊ธฐ์ดˆ ํ‘œ์‹œ

๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์‹คํ—˜์ด ์‹คํ–‰๋˜๊ณ  "์ด NํšŒ ์‹คํ—˜ ์™„๋ฃŒ" ๋ฉ”์‹œ์ง€๊ฐ€ ๋‚˜ํƒ€๋‚˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ์‹คํ—˜ ํšŸ์ˆ˜ ์ž…๋ ฅ๋ž€๊ณผ "์‹คํ—˜ ์‹œ์ž‘" ๋ฒ„ํŠผ์ด ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค. ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ AJAX๋กœ ์‹คํ—˜์ด ์‹คํ–‰๋˜๊ณ , ๋ฐฑ์—”๋“œ์—์„œ ๋ฌด์ž‘์œ„ ๊ฒฐ๊ณผ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ DB์— ์ €์žฅํ•œ ๋’ค JSON์œผ๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋ก ํŠธ์—”๋“œ๋Š” ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์•„ "์ด NํšŒ ์‹คํ—˜ ์™„๋ฃŒ" ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. ์‹คํ—˜ ์ค‘์—๋Š” ๋ฒ„ํŠผ์ด ๋น„ํ™œ์„ฑํ™”๋˜๊ณ  "โณ ์‹คํ—˜ ์ค‘..." ํ…์ŠคํŠธ๋กœ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค.

3
Step 3
**Step 3: ๊ฒฐ๊ณผ ์‹œ๊ฐํ™” ์ฐจํŠธ & ํžˆ์Šคํ† ๋ฆฌ ๋กœ๊ทธ**

`app/modules/probability-lab/index.php` ํŒŒ์ผ์„ ์ˆ˜์ •ํ•˜์—ฌ ์‹คํ—˜ ๊ฒฐ๊ณผ๋ฅผ ๋ง‰๋Œ€ ๊ทธ๋ž˜ํ”„๋กœ ์‹œ๊ฐํ™”ํ•˜๊ณ  ํžˆ์Šคํ† ๋ฆฌ ๋กœ๊ทธ๋ฅผ ํ‘œ์‹œํ•˜์„ธ์š”.

**์ˆ˜์ • ๋‚ด์šฉ:**

1. **ํžˆ์Šคํ† ๋ฆฌ ์กฐํšŒ ๋กœ์ง** (AJAX ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ ๋ถ€๋ถ„์— ์ถ”๊ฐ€):
```php
if ($_POST['action'] === 'get_history') {
    $stmt = $pdo->query("SELECT * FROM experiments ORDER BY created_at DESC LIMIT 20");
    $history = $stmt->fetchAll(PDO::FETCH_ASSOC);
    echo json_encode(['success' => true, 'history' => $history]);
    exit;
}

if ($_POST['action'] === 'clear_history') {
    $pdo->exec("DELETE FROM experiments");
    echo json_encode(['success' => true]);
    exit;
}
```

2. **ํžˆ์Šคํ† ๋ฆฌ UI ์ถ”๊ฐ€** (`<div id="resultsContainer">` ๋‹ค์Œ์—):
```php
<div id="historyContainer" style="background: var(--m-surface); padding: 24px; border-radius: var(--m-radius-lg); box-shadow: var(--m-shadow); margin-top: 32px;">
    <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
        <h3 style="margin: 0; color: var(--m-text);">๐Ÿ“Š ์‹คํ—˜ ํžˆ์Šคํ† ๋ฆฌ</h3>
        <button id="clearHistoryBtn" style="padding: 8px 16px; background: #ef4444; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 0.9rem;">๋ชจ๋‘ ์ง€์šฐ๊ธฐ</button>
    </div>
    <div id="historyList" style="color: var(--m-text-muted); text-align: center; padding: 20px;">์‹คํ—˜์„ ์‹คํ–‰ํ•˜๋ฉด ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.</div>
</div>
```

3. **JavaScript ์ˆ˜์ •** (๊ธฐ์กด `displayResults` ํ•จ์ˆ˜๋ฅผ ๋‹ค์Œ์œผ๋กœ ๊ต์ฒด):
```php
<script>
// ... (๊ธฐ์กด ๋ชจ๋“œ ์„ ํƒ, ์‹คํ—˜ ์‹คํ–‰ ์ฝ”๋“œ ์œ ์ง€)

function displayResults(results) {
    const container = document.getElementById('resultsContainer');
    container.style.display = 'block';
    
    // ๊ฒฐ๊ณผ ์ง‘๊ณ„
    const counts = {};
    results.forEach(r => {
        const key = String(r);
        counts[key] = (counts[key] || 0) + 1;
    });
    
    // ์ •๋ ฌ (์ฃผ์‚ฌ์œ„๋Š” ์ˆซ์ž ์ˆœ, ๋‚˜๋จธ์ง€๋Š” ์ถœํ˜„ ๋นˆ๋„ ์ˆœ)
    let sortedKeys;
    if (currentMode === 'dice') {
        sortedKeys = Object.keys(counts).sort((a, b) => parseInt(a) - parseInt(b));
    } else {
        sortedKeys = Object.keys(counts).sort((a, b) => counts[b] - counts[a]);
    }
    
    // ์ฐจํŠธ HTML ์ƒ์„ฑ
    let html = '<div style="background: var(--m-surface); padding: 24px; border-radius: var(--m-radius-lg); box-shadow: var(--m-shadow);">';
    html += '<h3 style="margin: 0 0 16px 0; color: var(--m-text);">๐Ÿ“ˆ ์‹คํ—˜ ๊ฒฐ๊ณผ</h3>';
    html += '<div style="margin-bottom: 20px; color: var(--m-text-muted);">์ด ' + results.length + 'ํšŒ ์‹คํ—˜</div>';
    
    const maxCount = Math.max(...Object.values(counts));
    
    sortedKeys.forEach(key => {
        const count = counts[key];
        const percentage = ((count / results.length) * 100).toFixed(1);
        const barWidth = (count / maxCount) * 100;
        
        html += '<div style="margin-bottom: 12px;">';
        html += '<div style="display: flex; justify-content: space-between; margin-bottom: 4px; color: var(--m-text); font-size: 0.9rem;">';
        html += '<span>' + key + '</span>';
        html += '<span>' + count + 'ํšŒ (' + percentage + '%)</span>';
        html += '</div>';
        html += '<div style="background: rgba(79, 70, 229, 0.1); border-radius: 4px; height: 24px; overflow: hidden;">';
        html += '<div style="background: linear-gradient(135deg, #4f46e5, #6366f1); width: ' + barWidth + '%; height: 100%; transition: width 0.5s;"></div>';
        html += '</div>';
        html += '</div>';
    });
    
    html += '</div>';
    container.innerHTML = html;
    
    // ํžˆ์Šคํ† ๋ฆฌ ์ƒˆ๋กœ๊ณ ์นจ
    loadHistory();
}

async function loadHistory() {
    const formData = new FormData();
    formData.append('action', 'get_history');
    
    const response = await fetch(window.location.href, {
        method: 'POST',
        body: formData
    });
    
    const data = await response.json();
    
    if (data.success && data.history.length > 0) {
        let html = '';
        data.history.forEach(exp => {
            const date = new Date(exp.created_at);
            const modeIcon = exp.mode === 'dice' ? '๐ŸŽฒ' : exp.mode === 'coin' ? '๐Ÿช™' : '๐Ÿƒ';
            html += '<div style="padding: 12px; border-bottom: 1px solid rgba(128,128,128,0.1); color: var(--m-text); font-size: 0.9rem;">';
            html += modeIcon + ' ' + exp.mode + ' โ€” ' + exp.trials + 'ํšŒ ์‹คํ—˜ | ' + date.toLocaleString('ko-KR');
            html += '</div>';
        });
        document.getElementById('historyList').innerHTML = html;
    } else {
        document.getElementById('historyList').innerHTML = '<div style="text-align: center; padding: 20px; color: var(--m-text-muted);">์‹คํ—˜ ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.</div>';
    }
}

// ํžˆ์Šคํ† ๋ฆฌ ์‚ญ์ œ
document.getElementById('clearHistoryBtn').addEventListener('click', async () => {
    if (!confirm('๋ชจ๋“  ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์‚ญ์ œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?')) return;
    
    const formData = new FormData();
    formData.append('action', 'clear_history');
    
    await fetch(window.location.href, {
        method: 'POST',
        body: formData
    });
    
    loadHistory();
});

// ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ ํžˆ์Šคํ† ๋ฆฌ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
loadHistory();
</script>
```

์ด ๋‹จ๊ณ„์—์„œ๋Š”:
- ๊ฒฐ๊ณผ๋ฅผ ์ง‘๊ณ„ํ•˜์—ฌ ๋ง‰๋Œ€ ๊ทธ๋ž˜ํ”„๋กœ ์‹œ๊ฐํ™”
- ๊ฐ ๊ฒฐ๊ณผ๋ณ„ ์ถœํ˜„ ํšŸ์ˆ˜์™€ ๋ฐฑ๋ถ„์œจ ํ‘œ์‹œ
- ์‹คํ—˜ ํžˆ์Šคํ† ๋ฆฌ ์ตœ๊ทผ 20๊ฐœ ํ‘œ์‹œ
- "๋ชจ๋‘ ์ง€์šฐ๊ธฐ" ๋ฒ„ํŠผ์œผ๋กœ ํžˆ์Šคํ† ๋ฆฌ ์‚ญ์ œ

์‹คํ—˜ ์‹คํ–‰ ํ›„ ๋ง‰๋Œ€ ๊ทธ๋ž˜ํ”„์™€ ํžˆ์Šคํ† ๋ฆฌ ๋กœ๊ทธ๊ฐ€ ๋‚˜ํƒ€๋‚˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ์‹คํ—˜ ์‹คํ–‰ ์‹œ ๊ฒฐ๊ณผ๊ฐ€ ๋ง‰๋Œ€ ๊ทธ๋ž˜ํ”„๋กœ ์‹œ๊ฐํ™”๋ฉ๋‹ˆ๋‹ค. ๊ฐ ๊ฒฐ๊ณผ(์ฃผ์‚ฌ์œ„ 1~6, ๋™์ „ ์•ž/๋’ค, ์นด๋“œ ๋žญํฌ+๋ฌด๋Šฌ)๋ณ„๋กœ ์ถœํ˜„ ํšŸ์ˆ˜์™€ ๋ฐฑ๋ถ„์œจ์ด ํ‘œ์‹œ๋˜๊ณ , ๋ง‰๋Œ€์˜ ๊ธธ์ด๊ฐ€ ๋น„์œจ์— ๋”ฐ๋ผ ์กฐ์ •๋ฉ๋‹ˆ๋‹ค. ํ•˜๋‹จ์— ์‹คํ—˜ ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ์ตœ๊ทผ 20๊ฐœ๊นŒ์ง€ ํ‘œ์‹œ๋˜๋ฉฐ, "๋ชจ๋‘ ์ง€์šฐ๊ธฐ" ๋ฒ„ํŠผ์œผ๋กœ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ ๊ธฐ์กด ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ์ž๋™์œผ๋กœ ๋ถˆ๋Ÿฌ์™€์ง‘๋‹ˆ๋‹ค.

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

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

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

๋Œ“๊ธ€ (0)

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