๐ฐ g5se ๋๋ค ๋ก๋ ์๋ฎฌ๋ ์ดํฐ - ํ๋ฅ ์ ์ธ๊ณ ์ฒดํํ๊ธฐ
g5se-lotto-probability-sim
๋ฌธ์ ์ค๋ช
๐ฏ ํ๋ก์ ํธ ๊ฐ์
๋ก๋ 1๋ฑ ๋น์ฒจ ํ๋ฅ ์ ์ฝ 814๋ง๋ถ์ 1! ์ด ๋ฏฟ๊ธฐ ํ๋ ํ๋ฅ ์ ์ง์ ์ฒดํํด๋ณด๋ ์๋ฎฌ๋ ์ดํฐ๋ฅผ ๋ง๋ญ๋๋ค. ์ฌ์ฉ์๊ฐ ์ํ๋ ๋ฒํธ๋ฅผ ์ ํํ๊ฑฐ๋ ์๋ ๋ฒํธ๋ฅผ ์์ฑํ ๋ค, ์ค์ ๋ก๋ ์ถ์ฒจ์ฒ๋ผ ๋ฌด์์๋ก ๋น์ฒจ ๋ฒํธ๋ฅผ ๋ฝ์์ ๋น๊ตํฉ๋๋ค. ๋ช ๋ฒ ๋ง์ 1๋ฑ์ ๋น์ฒจ๋ ๊น์? ํน์ 1,000๋ฒ์ ๋ฝ์๋ 5๋ฑ์กฐ์ฐจ ์ ๋ ์๋ ์์ต๋๋ค. ํ๋ฅ ์ ์์ธํจ์ ์๊ฐ์ ์ผ๋ก ํ์ธํ๋ฉฐ ์ฌ๋ฏธ์ ๊ตํ์ ๋์์ ์ป๋ ํ์ด์ง์ ๋๋ค.
โ ํ์ ๊ธฐ๋ฅ
- ๋ฒํธ ์ ํ UI: 1~45 ์ซ์ ๋ฒํผ ๊ทธ๋ฆฌ๋, ํด๋ฆญํ๋ฉด ์ ํ/ํด์ (์ต๋ 6๊ฐ)
- ์๋ ๋ฒํธ ์์ฑ: ๋ฒํผ ํด๋ฆญ ์ ๋๋ค์ผ๋ก 6๊ฐ ์ซ์ ์๋ ์ ํ
- ์ถ์ฒจ ์คํ: ๋น์ฒจ ๋ฒํธ 6๊ฐ + ๋ณด๋์ค ๋ฒํธ 1๊ฐ ๋ฌด์์ ์์ฑ
- ๋ฑ์ ํ์ : ์ฌ์ฉ์ ๋ฒํธ์ ๋น์ฒจ ๋ฒํธ ๋น๊ตํ์ฌ 1~5๋ฑ ๋๋ ๋์ฒจ ํ์
- 1๋ฑ: 6๊ฐ ์ผ์น
- 2๋ฑ: 5๊ฐ ์ผ์น + ๋ณด๋์ค ์ผ์น
- 3๋ฑ: 5๊ฐ ์ผ์น
- 4๋ฑ: 4๊ฐ ์ผ์น
- 5๋ฑ: 3๊ฐ ์ผ์น
- ํต๊ณ ํ์: ์ง๊ธ๊น์ง ๋ฝ์ ์ด ํ์, ๊ฐ ๋ฑ์๋ณ ๋น์ฒจ ํ์ ์ง๊ณ
- ์ฐ์ ์ถ์ฒจ: "100๋ฒ ์๋ ์ถ์ฒจ" ๊ฐ์ ๋ฒํผ์ผ๋ก ๋น ๋ฅด๊ฒ ํ๋ฅ ์ฒดํ
- ์ด๊ธฐํ ๋ฒํผ: ํต๊ณ ๋ฆฌ์
๐ ์ ํ ์ถ๊ฐ ๊ธฐ๋ฅ (๋ณด๋์ค)
- ์ ๋๋ฉ์ด์ ํจ๊ณผ: ์ถ์ฒจ ์ ๋ฒํธ๊ฐ ์ฌ๋กฏ๋จธ์ ์ฒ๋ผ ๊ตด๋ฌ๊ฐ๋ ์ฐ์ถ
- ๋น์ฒจ๊ธ ์๋ฎฌ๋ ์ด์ : ๊ฐ ๋ฑ์๋ณ ์์ ๋น์ฒจ๊ธ ํ์ (๊ณ ์ ๊ฐ ๋๋ ์ค์ ํ์ฐจ ๋ฐ์ดํฐ ์ฐ๋)
- ์ต๊ณ ๊ธฐ๋ก ์ ์ฅ: LocalStorage์ "๊ฐ์ฅ ๋นจ๋ฆฌ 1๋ฑ ๋น์ฒจํ ํ์" ๊ธฐ๋ก
- ๋คํฌ๋ชจ๋ ๋์: modern shell ํ ํฐ์ผ๋ก ์๋ ๋์
- ๊ณต์ ๊ธฐ๋ฅ: "๋๋ X๋ฒ ๋ง์ Y๋ฑ ๋น์ฒจ!" SNS ๊ณต์ ํ ์คํธ ์์ฑ
๐จ ๋์์ธ ํฌ์ธํธ
- ๋ฒํธ ์ ํ ๋ฒํผ์ ํฐ ์ํ(40px ์ด์)์ผ๋ก ํฐ์นํ๊ธฐ ์ฝ๊ฒ
- ์ ํ๋ ๋ฒํธ๋ ๊ธ์ ๋๋ ๊ฐ์กฐ ์ปฌ๋ฌ๋ก ํ์ด๋ผ์ดํธ
- ๋น์ฒจ ๋ฒํธ๋ ๋นจ๊ฐ์ ๊ณต ์ด๋ฏธ์ง ๋๋ ๊ทธ๋ผ๋ฐ์ด์ ๋ฐฐ๊ฒฝ์ผ๋ก ํ๋ คํ๊ฒ
- ๋ฑ์ ํ์ ๊ฒฐ๊ณผ๋ ํฐ ํ ์คํธ + ์ถํ/์๋ก ๋ฉ์์ง๋ก ๊ฐ์ ์๊ทน
- ํต๊ณ๋ ๋ง๋ ๊ทธ๋ํ ๋๋ ์นด์ดํฐ ์ ๋๋ฉ์ด์ ์ผ๋ก ์๊ฐํ
์ ์ฝ ์กฐ๊ฑด
- โข g5se ์ค์น๋ณธ ์์ ์์ (Laravel/Composer/npm ์ฌ์ฉ ๊ธ์ง)
- โข ํด๋ ๊ธฐ๋ฐ ์๋ ๋ผ์ฐํ : app/modules/lotto-sim/index.php โ /lotto-sim
- โข modern shell ํ์ ์ ์ฉ (_head.inc.php, m-shell, _nav.inc.php, m-container, _footer.inc.php)
- โข ๋์์ธ ํ ํฐ ์ฌ์ฉ (--m-surface, --m-text, --m-shadow, --m-radius-lg ๋ฑ)
- โข DB ๋ถํ์ (์์ ํ๋ก ํธ์๋ ๋ก์ง, LocalStorage๋ก ํต๊ณ ์ ์ฅ ๊ฐ๋ฅ)
- โข ์์ PHP + HTML + CSS + Vanilla JS๋ง ์ฌ์ฉ (์ธ๋ถ CDN ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ธ์ง)
- โข ๋ชจ๋ ์ฌ์ฉ์ ์ ๋ ฅ htmlspecialchars()๋ก ์ด์ค์ผ์ดํ (XSS ๋ฐฉ์ง)
- โข ๋ฐ์ํ ๋์์ธ (๋ชจ๋ฐ์ผ์์๋ ๋ฒํธ ์ ํ UI๊ฐ ๊นจ์ง์ง ์๊ฒ)
ํ๋กฌํํธ ๋ ์ํผ
์๋ ํ๋กฌํํธ๋ฅผ ์์๋๋ก AI์๊ฒ ๋ณด๋ด๋ฉด ๋ฉ๋๋ค. ๋ณต์ฌ ๋ฒํผ์ ๋๋ฌ ๋ฐ๋ก ์ฌ์ฉํ์ธ์.
g5se๊ฐ ์ค์น๋ ๋๋ ํฐ๋ฆฌ(๋ฃจํธ์ `index.php`ยท`app/`์ด ์๋ ๊ณณ)์์ ์์
์ ์์ํฉ๋๋ค. (g5se ์ค์น๋ https://github.com/gnuboard/g5se ์ฐธ๊ณ )
๋ค์ ํ์ผ์ ์์ฑํ์ธ์:
**app/modules/lotto-sim/index.php**
```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; max-width: 800px; margin: 0 auto;">
<h1 style="font-size: 2rem; font-weight: 700; margin-bottom: 8px; color: var(--m-text);">๐ฐ ๋ก๋ ํ๋ฅ ์๋ฎฌ๋ ์ดํฐ</h1>
<p style="color: var(--m-text-muted); margin-bottom: 32px;">1๋ฑ ๋น์ฒจ ํ๋ฅ 814๋ง๋ถ์ 1์ ์ง์ ์ฒดํํด๋ณด์ธ์!</p>
<!-- ๋ฒํธ ์ ํ ์์ญ -->
<div style="background: var(--m-surface); padding: 24px; border-radius: var(--m-radius-lg); box-shadow: var(--m-shadow); margin-bottom: 24px;">
<h2 style="font-size: 1.25rem; font-weight: 600; margin-bottom: 16px; color: var(--m-text);">๋ด ๋ฒํธ ์ ํ (6๊ฐ)</h2>
<div id="number-grid" style="display: grid; grid-template-columns: repeat(9, 1fr); gap: 8px; margin-bottom: 16px;">
<!-- 1~45 ๋ฒํผ ๋์ ์์ฑ -->
</div>
<div style="display: flex; gap: 12px; flex-wrap: wrap;">
<button id="auto-pick" style="padding: 10px 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 8px; font-weight: 600; cursor: pointer;">๐ฒ ์๋ ๋ฒํธ</button>
<button id="clear-pick" style="padding: 10px 20px; background: var(--m-surface); color: var(--m-text); border: 1px solid #ddd; border-radius: 8px; cursor: pointer;">์ด๊ธฐํ</button>
</div>
<div id="selected-numbers" style="margin-top: 16px; font-size: 1.5rem; font-weight: 700; color: #f59e0b; min-height: 40px;">
<!-- ์ ํ๋ ๋ฒํธ ํ์ -->
</div>
</div>
<!-- ์ถ์ฒจ ์คํ ์์ญ -->
<div style="background: var(--m-surface); padding: 24px; border-radius: var(--m-radius-lg); box-shadow: var(--m-shadow); margin-bottom: 24px;">
<button id="draw-once" style="width: 100%; padding: 16px; background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; border: none; border-radius: 12px; font-size: 1.25rem; font-weight: 700; cursor: pointer; margin-bottom: 12px;">๐ ์ถ์ฒจ ์์!</button>
<button id="draw-100" style="width: 100%; padding: 12px; background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); color: white; border: none; border-radius: 12px; font-weight: 600; cursor: pointer;">โก 100๋ฒ ์ฐ์ ์ถ์ฒจ</button>
</div>
<!-- ๊ฒฐ๊ณผ ํ์ ์์ญ -->
<div id="result-area" style="background: var(--m-surface); padding: 24px; border-radius: var(--m-radius-lg); box-shadow: var(--m-shadow); margin-bottom: 24px; display: none;">
<h2 style="font-size: 1.25rem; font-weight: 600; margin-bottom: 16px; color: var(--m-text);">๋น์ฒจ ๋ฒํธ</h2>
<div id="winning-numbers" style="font-size: 1.5rem; font-weight: 700; margin-bottom: 16px;">
<!-- ๋น์ฒจ ๋ฒํธ + ๋ณด๋์ค -->
</div>
<div id="result-message" style="font-size: 1.75rem; font-weight: 700; text-align: center; padding: 20px; border-radius: 12px;">
<!-- ๋ฑ์ ํ์ ๋ฉ์์ง -->
</div>
</div>
<!-- ํต๊ณ ์์ญ -->
<div style="background: var(--m-surface); padding: 24px; border-radius: var(--m-radius-lg); box-shadow: var(--m-shadow);">
<h2 style="font-size: 1.25rem; font-weight: 600; margin-bottom: 16px; color: var(--m-text);">๐ ์ถ์ฒจ ํต๊ณ</h2>
<div id="stats" style="font-size: 1rem; line-height: 1.8; color: var(--m-text);">
์ด ์ถ์ฒจ ํ์: <strong>0</strong>ํ<br>
1๋ฑ: <strong>0</strong>ํ<br>
2๋ฑ: <strong>0</strong>ํ<br>
3๋ฑ: <strong>0</strong>ํ<br>
4๋ฑ: <strong>0</strong>ํ<br>
5๋ฑ: <strong>0</strong>ํ<br>
๋์ฒจ: <strong>0</strong>ํ
</div>
<button id="reset-stats" style="margin-top: 16px; padding: 10px 20px; background: #ef4444; color: white; border: none; border-radius: 8px; cursor: pointer;">ํต๊ณ ์ด๊ธฐํ</button>
</div>
</main>
<?php require G5_THEME_PATH.'/modern/_footer.inc.php'; ?>
</div>
<script>
// 1~45 ๋ฒํธ ๋ฒํผ ์์ฑ
const grid = document.getElementById('number-grid');
for (let i = 1; i <= 45; i++) {
const btn = document.createElement('button');
btn.textContent = i;
btn.dataset.num = i;
btn.style.cssText = 'width: 100%; aspect-ratio: 1; border: 2px solid #ddd; border-radius: 50%; background: white; font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.2s;';
btn.addEventListener('click', () => toggleNumber(i, btn));
grid.appendChild(btn);
}
let selectedNumbers = [];
function toggleNumber(num, btn) {
const idx = selectedNumbers.indexOf(num);
if (idx > -1) {
selectedNumbers.splice(idx, 1);
btn.style.background = 'white';
btn.style.borderColor = '#ddd';
btn.style.color = '#000';
} else {
if (selectedNumbers.length >= 6) {
alert('์ต๋ 6๊ฐ๊น์ง ์ ํ ๊ฐ๋ฅํฉ๋๋ค!');
return;
}
selectedNumbers.push(num);
btn.style.background = 'linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%)';
btn.style.borderColor = '#f59e0b';
btn.style.color = 'white';
}
updateSelectedDisplay();
}
function updateSelectedDisplay() {
const display = document.getElementById('selected-numbers');
if (selectedNumbers.length === 0) {
display.textContent = '๋ฒํธ๋ฅผ ์ ํํ์ธ์';
display.style.color = 'var(--m-text-muted)';
} else {
selectedNumbers.sort((a, b) => a - b);
display.textContent = selectedNumbers.join(', ');
display.style.color = '#f59e0b';
}
}
// ์๋ ๋ฒํธ ์์ฑ
document.getElementById('auto-pick').addEventListener('click', () => {
// ๊ธฐ์กด ์ ํ ์ด๊ธฐํ
document.querySelectorAll('#number-grid button').forEach(btn => {
btn.style.background = 'white';
btn.style.borderColor = '#ddd';
btn.style.color = '#000';
});
selectedNumbers = [];
// 6๊ฐ ๋๋ค ์ ํ
while (selectedNumbers.length < 6) {
const num = Math.floor(Math.random() * 45) + 1;
if (!selectedNumbers.includes(num)) selectedNumbers.push(num);
}
selectedNumbers.sort((a, b) => a - b);
// UI ์
๋ฐ์ดํธ
selectedNumbers.forEach(num => {
const btn = document.querySelector(`[data-num="${num}"]`);
btn.style.background = 'linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%)';
btn.style.borderColor = '#f59e0b';
btn.style.color = 'white';
});
updateSelectedDisplay();
});
// ์ ํ ์ด๊ธฐํ
document.getElementById('clear-pick').addEventListener('click', () => {
document.querySelectorAll('#number-grid button').forEach(btn => {
btn.style.background = 'white';
btn.style.borderColor = '#ddd';
btn.style.color = '#000';
});
selectedNumbers = [];
updateSelectedDisplay();
});
// ํต๊ณ (LocalStorage)
let stats = JSON.parse(localStorage.getItem('lotto-stats') || '{"total":0,"rank1":0,"rank2":0,"rank3":0,"rank4":0,"rank5":0,"lose":0}');
function saveStats() {
localStorage.setItem('lotto-stats', JSON.stringify(stats));
updateStatsDisplay();
}
function updateStatsDisplay() {
document.getElementById('stats').innerHTML = `
์ด ์ถ์ฒจ ํ์: <strong>${stats.total}</strong>ํ<br>
1๋ฑ: <strong>${stats.rank1}</strong>ํ<br>
2๋ฑ: <strong>${stats.rank2}</strong>ํ<br>
3๋ฑ: <strong>${stats.rank3}</strong>ํ<br>
4๋ฑ: <strong>${stats.rank4}</strong>ํ<br>
5๋ฑ: <strong>${stats.rank5}</strong>ํ<br>
๋์ฒจ: <strong>${stats.lose}</strong>ํ
`;
}
updateStatsDisplay();
// ์ถ์ฒจ ๋ก์ง
function drawLotto() {
if (selectedNumbers.length !== 6) {
alert('6๊ฐ์ ๋ฒํธ๋ฅผ ์ ํํ์ธ์!');
return null;
}
// ๋น์ฒจ ๋ฒํธ ์์ฑ
const winning = [];
while (winning.length < 6) {
const num = Math.floor(Math.random() * 45) + 1;
if (!winning.includes(num)) winning.push(num);
}
winning.sort((a, b) => a - b);
// ๋ณด๋์ค ๋ฒํธ
let bonus;
do {
bonus = Math.floor(Math.random() * 45) + 1;
} while (winning.includes(bonus));
return { winning, bonus };
}
function checkRank(winning, bonus) {
const matched = selectedNumbers.filter(n => winning.includes(n)).length;
const hasBonus = selectedNumbers.includes(bonus);
if (matched === 6) return 1;
if (matched === 5 && hasBonus) return 2;
if (matched === 5) return 3;
if (matched === 4) return 4;
if (matched === 3) return 5;
return 0; // ๋์ฒจ
}
// 1ํ ์ถ์ฒจ
document.getElementById('draw-once').addEventListener('click', () => {
const result = drawLotto();
if (!result) return;
const { winning, bonus } = result;
const rank = checkRank(winning, bonus);
// ๊ฒฐ๊ณผ ํ์
document.getElementById('result-area').style.display = 'block';
document.getElementById('winning-numbers').innerHTML = `
${winning.map(n => `<span style="display: inline-block; width: 40px; height: 40px; line-height: 40px; text-align: center; background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; border-radius: 50%; margin: 0 4px;">${n}</span>`).join('')}
<span style="color: var(--m-text-muted); margin: 0 8px;">+</span>
<span style="display: inline-block; width: 40px; height: 40px; line-height: 40px; text-align: center; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 50%;">${bonus}</span>
`;
const messages = {
1: { text: '๐ 1๋ฑ ๋น์ฒจ! ์ถํํฉ๋๋ค!', bg: 'linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%)', color: 'white' },
2: { text: '๐ 2๋ฑ ๋น์ฒจ! ๋๋ฐ์
๋๋ค!', bg: 'linear-gradient(135deg, #a78bfa 0%, #8b5cf6 100%)', color: 'white' },
3: { text: '๐ 3๋ฑ ๋น์ฒจ! ์ข์์!', bg: 'linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%)', color: 'white' },
4: { text: '๐ 4๋ฑ ๋น์ฒจ!', bg: 'linear-gradient(135deg, #34d399 0%, #10b981 100%)', color: 'white' },
5: { text: 'โจ 5๋ฑ ๋น์ฒจ!', bg: 'linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%)', color: 'white' },
0: { text: '๐ข ๋์ฒจ์
๋๋ค...', bg: 'var(--m-surface)', color: 'var(--m-text-muted)' }
};
const msg = messages[rank];
const resultMsg = document.getElementById('result-message');
resultMsg.textContent = msg.text;
resultMsg.style.background = msg.bg;
resultMsg.style.color = msg.color;
// ํต๊ณ ์
๋ฐ์ดํธ
stats.total++;
if (rank === 1) stats.rank1++;
else if (rank === 2) stats.rank2++;
else if (rank === 3) stats.rank3++;
else if (rank === 4) stats.rank4++;
else if (rank === 5) stats.rank5++;
else stats.lose++;
saveStats();
});
// 100ํ ์ฐ์ ์ถ์ฒจ
document.getElementById('draw-100').addEventListener('click', () => {
for (let i = 0; i < 100; i++) {
const result = drawLotto();
if (!result) return;
const { winning, bonus } = result;
const rank = checkRank(winning, bonus);
stats.total++;
if (rank === 1) stats.rank1++;
else if (rank === 2) stats.rank2++;
else if (rank === 3) stats.rank3++;
else if (rank === 4) stats.rank4++;
else if (rank === 5) stats.rank5++;
else stats.lose++;
}
saveStats();
alert('100ํ ์ถ์ฒจ ์๋ฃ! ํต๊ณ๋ฅผ ํ์ธํ์ธ์.');
});
// ํต๊ณ ์ด๊ธฐํ
document.getElementById('reset-stats').addEventListener('click', () => {
if (confirm('์ ๋ง ํต๊ณ๋ฅผ ์ด๊ธฐํํ์๊ฒ ์ต๋๊น?')) {
stats = { total: 0, rank1: 0, rank2: 0, rank3: 0, rank4: 0, rank5: 0, lose: 0 };
saveStats();
document.getElementById('result-area').style.display = 'none';
}
});
</script>
<?php include_once('./tail.sub.php'); ?>
```
์ด ์ฝ๋๋ g5se modern shell ๊ณจ๊ฒฉ์ ์ฌ์ฉํ์ฌ ๋ก๋ ์๋ฎฌ๋ ์ดํฐ์ ๊ธฐ๋ณธ UI์ ๋ก์ง์ ๊ตฌํํฉ๋๋ค. 1~45 ๋ฒํธ ์ ํ, ์๋ ๋ฒํธ ์์ฑ, ์ถ์ฒจ ์คํ, ๋ฑ์ ํ์ , ํต๊ณ ๊ธฐ๋ก ๊ธฐ๋ฅ์ด ๋ชจ๋ ํฌํจ๋์ด ์์ต๋๋ค.
๊ธฐ๋ ๊ฒฐ๊ณผ: ๋ธ๋ผ์ฐ์ ์์ `/lotto-sim`์ ์ ์ํ๋ฉด modern shell nav/footer๊ฐ ํฌํจ๋ ํ์ด์ง๊ฐ ํ์๋ฉ๋๋ค. 1~45 ์ซ์ ๋ฒํผ ๊ทธ๋ฆฌ๋๊ฐ ๋ณด์ด๊ณ , ํด๋ฆญํ๋ฉด ๊ธ์์ผ๋ก ํ์ด๋ผ์ดํธ๋๋ฉฐ ์ต๋ 6๊ฐ๊น์ง ์ ํ ๊ฐ๋ฅํฉ๋๋ค. '์๋ ๋ฒํธ' ๋ฒํผ์ผ๋ก ๋๋ค 6๊ฐ ์ ํ, '์ถ์ฒจ ์์!' ๋ฒํผ์ผ๋ก ๋น์ฒจ ๋ฒํธ ์์ฑ ๋ฐ ๋ฑ์ ํ์ ์ด ๋์ํฉ๋๋ค. ํต๊ณ๋ LocalStorage์ ์ ์ฅ๋์ด ํ์ด์ง ์๋ก๊ณ ์นจ ํ์๋ ์ ์ง๋ฉ๋๋ค.
ํ์ฌ ํ์ด์ง์ ๋ค์ ๊ฐ์ ์ฌํญ์ ์ถ๊ฐํ์ธ์: 1. **๋ฒํธ ๋ฒํผ ํธ๋ฒ ํจ๊ณผ**: ๋ง์ฐ์ค ์ค๋ฒ ์ ์ด์ง ํ๋๋๊ณ ๊ทธ๋ฆผ์๊ฐ ์๊ธฐ๋ transition ์ถ๊ฐ 2. **์ถ์ฒจ ์ ๋๋ฉ์ด์ **: '์ถ์ฒจ ์์!' ๋ฒํผ ํด๋ฆญ ์ ๋น์ฒจ ๋ฒํธ๊ฐ ์ฆ์ ๋ํ๋์ง ์๊ณ , 0.5์ด๊ฐ "์ถ์ฒจ ์ค..." ๋ฉ์์ง ํ์ ํ ๊ฒฐ๊ณผ ๋ ธ์ถ (setTimeout ์ฌ์ฉ) 3. **๋ฐ์ํ ๊ทธ๋ฆฌ๋ ๊ฐ์ **: ๋ชจ๋ฐ์ผ(ํ๋ฉด ๋๋น 480px ์ดํ)์์๋ ๋ฒํธ ๊ทธ๋ฆฌ๋๋ฅผ 7์ด๋ก ์ถ์ (๋ฏธ๋์ด ์ฟผ๋ฆฌ ์ฌ์ฉ) 4. **๋น์ฒจ ํ๋ฅ ์๋ด**: ํ์ด์ง ์๋จ์ "1๋ฑ ํ๋ฅ : 1/8,145,060" ๊ฐ์ ํ๋ฅ ์ ๋ณด๋ฅผ ์์ ํ ์คํธ๋ก ์ถ๊ฐ 5. **์ต๊ณ ๊ธฐ๋ก ํ์**: LocalStorage์ "๊ฐ์ฅ ์ ์ ํ์๋ก 1๋ฑ ๋น์ฒจ" ๊ธฐ๋ก์ ์ ์ฅํ๊ณ , ํต๊ณ ์์ญ ํ๋จ์ "์ต๋จ 1๋ฑ ๊ธฐ๋ก: Xํ" ํ์ (1๋ฑ ๋น์ฒจ ์ ๊ฐฑ์ ) **app/modules/lotto-sim/index.php** ํ์ผ์ ์์ ํ์ฌ ์ ๊ธฐ๋ฅ์ ๋ฐ์ํ์ธ์. CSS๋ `<style>` ๋ธ๋ก์ `<main>` ์์ ์ธ๋ผ์ธ์ผ๋ก ์ถ๊ฐํ๊ฑฐ๋ ๊ธฐ์กด ์ธ๋ผ์ธ ์คํ์ผ์ ํ์ฅํ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: ๋ฒํธ ๋ฒํผ์ ๋ง์ฐ์ค๋ฅผ ์ฌ๋ฆฌ๋ฉด ์ด์ง ์ปค์ง๊ณ ๊ทธ๋ฆผ์๊ฐ ์๊น๋๋ค. ์ถ์ฒจ ๋ฒํผ ํด๋ฆญ ์ "์ถ์ฒจ ์ค..." ๋ฉ์์ง๊ฐ 0.5์ด๊ฐ ํ์๋ ํ ๊ฒฐ๊ณผ๊ฐ ๋ํ๋ฉ๋๋ค. ๋ชจ๋ฐ์ผ์์๋ ๋ฒํธ ๊ทธ๋ฆฌ๋๊ฐ 7์ด๋ก ์ค์ด๋ค์ด ํฐ์นํ๊ธฐ ํธํด์ง๋๋ค. ํ์ด์ง ์๋จ์ ํ๋ฅ ์ ๋ณด๊ฐ ํ์๋๊ณ , ํต๊ณ ์์ญ์ "์ต๋จ 1๋ฑ ๊ธฐ๋ก" ํญ๋ชฉ์ด ์ถ๊ฐ๋์ด 1๋ฑ ๋น์ฒจ ์ ์๋ ๊ฐฑ์ ๋ฉ๋๋ค.
๋ง์ง๋ง์ผ๋ก ๋ค์ ๋ณด๋์ค ๊ธฐ๋ฅ์ ์ถ๊ฐํ์ธ์:
1. **๊ณต์ ๊ธฐ๋ฅ**: ์ถ์ฒจ ๊ฒฐ๊ณผ ํ๋จ์ "๊ฒฐ๊ณผ ๊ณต์ ํ๊ธฐ" ๋ฒํผ ์ถ๊ฐ. ํด๋ฆญ ์ ํด๋ฆฝ๋ณด๋์ "๋๋ ๋ก๋ ์๋ฎฌ๋ ์ดํฐ์์ X๋ฒ ๋ง์ Y๋ฑ ๋น์ฒจ! ์ด Zํ ์ถ์ฒจ ์ค 1๋ฑ Aํ ๐ฐ" ๊ฐ์ ํ
์คํธ ๋ณต์ฌ (navigator.clipboard.writeText ์ฌ์ฉ)
2. **๋คํฌ๋ชจ๋ ํ์ธ**: modern shell์ ๋คํฌ๋ชจ๋ ํ ๊ธ ๋ฒํผ์ ๋๋ฌ๋ดค์ ๋ ๋ฐฐ๊ฒฝ/ํ
์คํธ ์์์ด ์์ฐ์ค๋ฝ๊ฒ ์ ํ๋๋์ง ํ์ธ (ํ ํฐ ์ฌ์ฉ ๋๋ถ์ ์๋ ๋์)
3. **์ ๊ทผ์ฑ ๊ฐ์ **: ๋ฒํธ ๋ฒํผ์ `aria-label="์ซ์ {num} ์ ํ"` ์์ฑ ์ถ๊ฐ, ์ถ์ฒจ ๋ฒํผ์ `aria-live="polite"` ์์ญ ์ฐ๊ฒฐํ์ฌ ์คํฌ๋ฆฐ ๋ฆฌ๋ ์ฌ์ฉ์๋ ๊ฒฐ๊ณผ๋ฅผ ๋ค์ ์ ์๊ฒ
4. **์ฑ๋ฅ ์ต์ ํ**: 100ํ ์ฐ์ ์ถ์ฒจ ์ UI๊ฐ ๋ฉ์ถ์ง ์๋๋ก `requestAnimationFrame` ๋๋ `setTimeout(..., 0)` ํจํด์ผ๋ก ๋น๋๊ธฐ ์ฒ๋ฆฌ (ํ์๋ ์๋์ง๋ง ๊ถ์ฅ)
**app/modules/lotto-sim/index.php** ํ์ผ์ ์ต์ข
์์ ํ์ธ์. ์์ฑ ํ `/lotto-sim`์ ์ ์ํ์ฌ ๋ชจ๋ ๊ธฐ๋ฅ์ด ์ ์ ๋์ํ๋์ง ํ
์คํธํ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: ์ถ์ฒจ ๊ฒฐ๊ณผ ์์ญ์ "๊ฒฐ๊ณผ ๊ณต์ ํ๊ธฐ" ๋ฒํผ์ด ๋ํ๋๊ณ , ํด๋ฆญํ๋ฉด ํ์ฌ ํต๊ณ๋ฅผ ํฌํจํ ํ ์คํธ๊ฐ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ๋ฉ๋๋ค (๋ณต์ฌ ์๋ฃ alert ํ์). ๋คํฌ๋ชจ๋ ์ ํ ์ ํ์ด์ง ๋ฐฐ๊ฒฝ๊ณผ ํ ์คํธ๊ฐ ์์ฐ์ค๋ฝ๊ฒ ์ด๋์ด ์์ผ๋ก ๋ฐ๋๋๋ค. ๋ฒํธ ๋ฒํผ์ aria-label์ด ์ถ๊ฐ๋์ด ์ ๊ทผ์ฑ์ด ํฅ์๋ฉ๋๋ค. 100ํ ์ฐ์ ์ถ์ฒจ์ ์คํํด๋ ๋ธ๋ผ์ฐ์ ๊ฐ ๋ฉ์ถ์ง ์๊ณ ๋ถ๋๋ฝ๊ฒ ์ฒ๋ฆฌ๋ฉ๋๋ค. ์ต์ข ์ ์ผ๋ก ๋ก๋ ํ๋ฅ ์ ์ฌ๋ฏธ์๊ฒ ์ฒดํํ ์ ์๋ ์์ฑ๋ ๋์ ํ์ด์ง๊ฐ ์์ฑ๋ฉ๋๋ค!
์ ์ถ๋ ์ํ (0)
๋ก๊ทธ์ธ ํ ์ ์ถ์์ง ์ ์ถ๋ ์ํ์ด ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋์ ์๊ฐ ๋์ด๋ณด์ธ์!