๐ฒ ์ค๋์ ์ด์ธ ๋ฝ๊ธฐ - ๋๋ค ํฌ์ถ ์ฟ ํค ์์ฑ๊ธฐ
fortune-cookie-generator
๋ฌธ์ ์ค๋ช
๐ฏ ํ๋ก์ ํธ ๊ฐ์
์ฌ์ฉ์๊ฐ ๋ฒํผ์ ๋๋ฅด๋ฉด ๋๋ค์ผ๋ก ์ค๋์ ์ด์ธ ๋ฉ์์ง๋ฅผ ๋ณด์ฌ์ฃผ๋ ๊ท์ฌ์ด ํฌ์ถ ์ฟ ํค ์์ฑ๊ธฐ๋ฅผ ๋ง๋ญ๋๋ค. g5se์ modern shell์ ํ์ฉํ์ฌ ์ฌ์ดํธ ์ ์ฒด์ ์ผ๊ด๋ ๋์์ธ์ ์ ์งํ๋ฉด์, ๋คํฌ๋ชจ๋์์๋ ์๋ฒฝํ๊ฒ ๋์ํ๋ ์ธํฐ๋ํฐ๋ธํ ๋จ์ผ ํ์ด์ง ์ฑ์ ๋๋ค.
โจ ํ์ ๊ธฐ๋ฅ
- ๋๋ค ์ด์ธ ์์ฑ: ๋ฒํผ ํด๋ฆญ ์ ๋ฏธ๋ฆฌ ์ค๋น๋ ์ด์ธ ๋ฉ์์ง ์ค ํ๋๋ฅผ ๋๋ค์ผ๋ก ํ์
- ์๊ฐ์ ํผ๋๋ฐฑ: ์ด์ธ๋ฅผ ๋ฝ์ ๋ ์ฟ ํค๊ฐ ๊นจ์ง๋ ๋ฏํ ์ ๋๋ฉ์ด์ ํจ๊ณผ
- ์ด์ธ ์นดํ ๊ณ ๋ฆฌ: ์ฐ์ ์ด, ์ฌ๋ฌผ์ด, ๊ฑด๊ฐ์ด, ํ์ ์ด ๋ฑ ๋ค์ํ ์นดํ ๊ณ ๋ฆฌ์ ๋ฉ์์ง (์ต์ 20๊ฐ ์ด์)
- ๊ณต์ ๊ธฐ๋ฅ: ๋ฝ์ ์ด์ธ๋ฅผ SNS์ ๊ณต์ ํ ์ ์๋ ํ ์คํธ ๋ณต์ฌ ๋ฒํผ
- ๋คํฌ๋ชจ๋ ๋์: modern shell์ ๋์์ธ ํ ํฐ์ ํ์ฉํ์ฌ ๋ผ์ดํธ/๋คํฌ ๋ชจ๋ ์๋ ์ ํ
- ๋ฐ์ํ ๋์์ธ: ๋ชจ๋ฐ์ผ/ํ๋ธ๋ฆฟ/๋ฐ์คํฌํฑ ๋ชจ๋ ํ๋ฉด์์ ์ต์ ํ๋ ๋ ์ด์์
๐ ๋ณด๋์ค ๊ธฐ๋ฅ
- ์ค๋ ์ด๋ฏธ ์ด์ธ๋ฅผ ๋ฝ์๋ค๋ฉด ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ์ฃผ๊ธฐ (localStorage ํ์ฉ)
- ์ด์ธ๋ณ ํ์ด์ ์์/์ซ์ ์ถ๊ฐ ํ์
- ๋ฝ์ ์ด์ธ ํ์คํ ๋ฆฌ (์ต๊ทผ 5๊ฐ) ํ๋จ์ ํ์
- ๋ถ๋๋ฌ์ด fade-in/slide-up ์ ๋๋ฉ์ด์
- ๋ฐฐ๊ฒฝ์ ๋ ๋ค๋๋ ๋ณ/๋ฐ์ง์ ํจ๊ณผ (CSS ์ ๋๋ฉ์ด์ )
๐จ ๋์์ธ ๊ฐ์ด๋
- ํฌ์ถ ์ฟ ํค ์์ด์ฝ/์ผ๋ฌ์คํธ (์ด๋ชจ์ง ๋๋ SVG)
- ์นด๋ ํํ์ ์ด์ธ ๋ฉ์์ง ๋ฐ์ค (var(--m-surface) ๋ฐฐ๊ฒฝ, var(--m-shadow) ๊ทธ๋ฆผ์)
- ํฐ ๋ฒํผ (var(--m-radius-lg) ๋ผ์ด๋, hover ์ scale ํจ๊ณผ)
- ์ด์ธ ํ ์คํธ๋ ์ค์ ์ ๋ ฌ, ํฐ ํฐํธ ์ฌ์ด์ฆ
- ์นดํ ๊ณ ๋ฆฌ๋ณ ์์ด์ฝ (๐ ์ฐ์ , ๐ฐ ์ฌ๋ฌผ, ๐ ๊ฑด๊ฐ, ๐ ํ์ )
๐ ๏ธ ๊ธฐ์ ์คํ
- PHP (์์ PHP, g5se ์ปจ๋ฒค์ )
- HTML/CSS (modern shell ๋์์ธ ํ ํฐ)
- Vanilla JavaScript (์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์)
- SQLite ๋ถํ์ (์ด์ธ ๋ฐ์ดํฐ๋ PHP ๋ฐฐ์ด๋ก ํ๋์ฝ๋ฉ)
๐ ํ์ต ํฌ์ธํธ
- g5se ํด๋ ๊ธฐ๋ฐ ์๋ ๋ผ์ฐํ ์ดํด
- modern shell ํตํฉ์ผ๋ก ์ผ๊ด๋ UI ๊ตฌํ
- CSS ๋ณ์๋ฅผ ํ์ฉํ ๋คํฌ๋ชจ๋ ๋์
- JavaScript๋ก ๋๋ค ์ ํ ๋ก์ง ๊ตฌํ
- localStorage๋ฅผ ํ์ฉํ ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ์ํ ๊ด๋ฆฌ
- CSS transition/animation์ผ๋ก ๋ถ๋๋ฌ์ด UX ๋ง๋ค๊ธฐ
์ ์ฝ ์กฐ๊ฑด
- โข g5se ์ค์น๋ณธ ์์ ์์ (์ ํด๋ ์์ฑ ๊ธ์ง)
- โข app/modules/fortune-cookie-generator/index.php ๋จ์ผ ํ์ผ๋ก ๊ตฌํ
- โข modern shell (_head.inc.php, _nav.inc.php, _footer.inc.php) ํ์ ์ฌ์ฉ
- โข ๋์์ธ ํ ํฐ (var(--m-surface), var(--m-text), var(--m-shadow) ๋ฑ) ํ์ฉ
- โข ๋ผ์ฐํธ ๋ฑ๋ก ๋ถํ์ (ํด๋ ์๋ ๋งคํ)
- โข ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ/CDN ์ฌ์ฉ ๊ธ์ง (์์ HTML/CSS/JS)
- โข DB ์ฌ์ฉ ๊ธ์ง (์ด์ธ ๋ฐ์ดํฐ๋ PHP ๋ฐฐ์ด๋ก ๊ด๋ฆฌ)
- โข ๋ชจ๋ ์ฌ์ฉ์ ์ ๋ ฅ์ htmlspecialchars()๋ก ์ด์ค์ผ์ดํ
- โข ๋ฐ์ํ ๋์์ธ (๋ชจ๋ฐ์ผ ์ฐ์ )
- โข ๋คํฌ๋ชจ๋ ์๋ ๋์
ํ๋กฌํํธ ๋ ์ํผ
์๋ ํ๋กฌํํธ๋ฅผ ์์๋๋ก AI์๊ฒ ๋ณด๋ด๋ฉด ๋ฉ๋๋ค. ๋ณต์ฌ ๋ฒํผ์ ๋๋ฌ ๋ฐ๋ก ์ฌ์ฉํ์ธ์.
g5se๊ฐ ์ค์น๋ ๋๋ ํฐ๋ฆฌ(๋ฃจํธ์ `index.php`ยท`app/`์ด ์๋ ๊ณณ)์์ ์์
์ ์์ํฉ๋๋ค. (g5se ์ค์น๋ https://github.com/gnuboard/g5se ์ฐธ๊ณ )
์ด์ ํฌ์ถ ์ฟ ํค ์์ฑ๊ธฐ ํ์ด์ง๋ฅผ ๋ง๋ค์ด๋ด
์๋ค.
**1๋จ๊ณ: ๊ธฐ๋ณธ ํ์ด์ง ๊ตฌ์กฐ + modern shell ์ ์ฉ**
`app/modules/fortune-cookie-generator/index.php` ํ์ผ์ ์์ฑํ๊ณ ๋ค์ ๋ด์ฉ์ ์์ฑํ์ธ์:
```php
<?php
include_once('./_common.php');
// ์ด์ธ ๋ฐ์ดํฐ ๋ฐฐ์ด (์นดํ
๊ณ ๋ฆฌ๋ณ๋ก ์ ๋ฆฌ)
$fortunes = [
'love' => [
'์ค๋์ ํน๋ณํ ์ธ์ฐ์ ๋ง๋ ์ ์๋ ๋ ์
๋๋ค. ๐',
'๋ง์์ ์ด๋ฉด ์ฌ๋์ด ์ฐพ์์ต๋๋ค. ์ฉ๊ธฐ๋ฅผ ๋ด๋ณด์ธ์!',
'์์คํ ์ฌ๋์๊ฒ ๋จผ์ ์ฐ๋ฝํด๋ณด๋ ๊ฑด ์ด๋จ๊น์?',
'์ค๋์ ๋น์ ์ ๋งค๋ ฅ์ด ๋์นฉ๋๋ค. ์์ ๊ฐ์ ๊ฐ์ง์ธ์!',
'์ง์ฌ์ ์ธ์ ๊ฐ ํตํ๊ฒ ๋์ด์์ต๋๋ค. ์กฐ๊ธํดํ์ง ๋ง์ธ์.',
],
'money' => [
'์์ ํ์ด์ด ๋น์ ์ ์ฐพ์์ฌ ์์ ์
๋๋ค. ๐ฐ',
'์ค๋์ ์ ์ถ๋ณด๋ค ์๊ธฐ๊ณ๋ฐ์ ํฌ์ํ๋ ๋ ์
๋๋ค.',
'์์์น ๋ชปํ ๊ณณ์์ ๊ธฐํ๊ฐ ์ฐพ์์ฌ ์ ์์ต๋๋ค.',
'ํ๋ช
ํ ์๋น๊ฐ ๋ฏธ๋์ ๋ถ๋ฅผ ๋ง๋ญ๋๋ค.',
'๊ฐ์ฌํ๋ ๋ง์์ด ๋ ๋ง์ ํ์๋ฅผ ๋ถ๋ฌ์ต๋๋ค.',
],
'health' => [
'์ค๋์ ๋ชธ์ด ๊ฐ๋ฒผ์์ง๋ ๋ ์
๋๋ค. ๊ฐ๋ณ๊ฒ ์ฐ์ฑ
ํด๋ณด์ธ์! ๐',
'์ถฉ๋ถํ ํด์์ด ๋ด์ผ์ ์๋์ง๊ฐ ๋ฉ๋๋ค.',
'๋ฌผ์ ๋ง์ด ๋ง์๊ณ ์คํธ๋ ์นญ์ ํด๋ณด์ธ์.',
'๊ฑด๊ฐํ ์๋จ์ด ํ๋ฃจ๋ฅผ ๋ฐ๊ฟ๋๋ค.',
'๊ท์น์ ์ธ ์ํ์ด ์ต๊ณ ์ ๋ณด์ฝ์
๋๋ค.',
],
'study' => [
'์ค๋ ๋ฐฐ์ด ๊ฒ์ด ๋ฏธ๋์ ์์ฐ์ด ๋ฉ๋๋ค. ๐',
'์์ ๋
ธ๋ ฅ์ด ๋ชจ์ฌ ํฐ ์ฑ๊ณผ๋ฅผ ๋ง๋ญ๋๋ค.',
'์๋ก์ด ๊ฒ์ ๋ฐฐ์ฐ๊ธฐ์ ์๋ฒฝํ ๋ ์
๋๋ค.',
'๊พธ์คํจ์ด ์ฌ๋ฅ์ ์ด๊น๋๋ค. ํฌ๊ธฐํ์ง ๋ง์ธ์!',
'์ค๋์ ์ค์๋ ๋ด์ผ์ ์ฑ์ฅ์
๋๋ค.',
],
];
// ๋ชจ๋ ์ด์ธ๋ฅผ ํ๋์ ๋ฐฐ์ด๋ก ํฉ์น๊ธฐ
$all_fortunes = [];
foreach ($fortunes as $category => $messages) {
foreach ($messages as $message) {
$all_fortunes[] = [
'category' => $category,
'message' => $message,
];
}
}
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;">
<div class="fortune-wrapper">
<h1 class="fortune-title">๐ฒ ์ค๋์ ์ด์ธ</h1>
<p class="fortune-subtitle">ํฌ์ถ ์ฟ ํค๋ฅผ ๊นจ์ ์ค๋์ ์ด์ธ๋ฅผ ํ์ธํด๋ณด์ธ์!</p>
<div class="fortune-cookie-container">
<div class="fortune-cookie" id="fortuneCookie">
<span class="cookie-icon">๐ฅ </span>
</div>
</div>
<button class="fortune-button" id="drawFortuneBtn">
์ด์ธ ๋ฝ๊ธฐ
</button>
<div class="fortune-result" id="fortuneResult" style="display: none;">
<div class="fortune-card">
<div class="fortune-category" id="fortuneCategory"></div>
<div class="fortune-message" id="fortuneMessage"></div>
<button class="copy-button" id="copyBtn">๐ ์ด์ธ ๋ณต์ฌํ๊ธฐ</button>
</div>
</div>
</div>
</main>
<?php require G5_THEME_PATH.'/modern/_footer.inc.php'; ?>
</div>
<style>
.fortune-wrapper {
max-width: 600px;
margin: 0 auto;
text-align: center;
}
.fortune-title {
font-size: 2.5rem;
font-weight: 700;
color: var(--m-text);
margin-bottom: 0.5rem;
}
.fortune-subtitle {
font-size: 1.1rem;
color: var(--m-text-muted);
margin-bottom: 3rem;
}
.fortune-cookie-container {
margin: 3rem 0;
perspective: 1000px;
}
.fortune-cookie {
display: inline-block;
transition: transform 0.3s ease;
}
.fortune-cookie:hover {
transform: scale(1.1) rotate(5deg);
}
.fortune-cookie.crack {
animation: crack 0.5s ease-out;
}
@keyframes crack {
0% { transform: scale(1) rotate(0deg); }
25% { transform: scale(1.2) rotate(-10deg); }
50% { transform: scale(0.9) rotate(10deg); }
75% { transform: scale(1.1) rotate(-5deg); }
100% { transform: scale(1) rotate(0deg); }
}
.cookie-icon {
font-size: 8rem;
display: block;
cursor: pointer;
}
.fortune-button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 1rem 3rem;
font-size: 1.2rem;
font-weight: 600;
border-radius: var(--m-radius-lg);
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
}
.fortune-button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
}
.fortune-button:active {
transform: translateY(0);
}
.fortune-result {
margin-top: 3rem;
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fortune-card {
background: var(--m-surface);
border-radius: var(--m-radius-lg);
padding: 2rem;
box-shadow: var(--m-shadow);
}
.fortune-category {
font-size: 1rem;
color: var(--m-text-muted);
margin-bottom: 1rem;
text-transform: uppercase;
letter-spacing: 1px;
}
.fortune-message {
font-size: 1.5rem;
color: var(--m-text);
line-height: 1.6;
margin-bottom: 1.5rem;
font-weight: 500;
}
.copy-button {
background: var(--m-surface);
color: var(--m-text);
border: 2px solid var(--m-text-muted);
padding: 0.75rem 1.5rem;
border-radius: var(--m-radius-lg);
cursor: pointer;
font-size: 1rem;
transition: all 0.3s ease;
}
.copy-button:hover {
background: var(--m-text-muted);
color: var(--m-surface);
transform: translateY(-2px);
}
@media (max-width: 768px) {
.fortune-title {
font-size: 2rem;
}
.cookie-icon {
font-size: 6rem;
}
.fortune-button {
padding: 0.875rem 2rem;
font-size: 1rem;
}
.fortune-message {
font-size: 1.25rem;
}
}
</style>
<script>
// ์ด์ธ ๋ฐ์ดํฐ๋ฅผ JavaScript๋ก ์ ๋ฌ
const fortuneData = <?php echo json_encode($all_fortunes, JSON_UNESCAPED_UNICODE); ?>;
const cookieEl = document.getElementById('fortuneCookie');
const drawBtn = document.getElementById('drawFortuneBtn');
const resultEl = document.getElementById('fortuneResult');
const categoryEl = document.getElementById('fortuneCategory');
const messageEl = document.getElementById('fortuneMessage');
const copyBtn = document.getElementById('copyBtn');
// ์นดํ
๊ณ ๋ฆฌ ํ๊ธ ์ด๋ฆ ๋งคํ
const categoryNames = {
'love': '๐ ์ฐ์ ์ด',
'money': '๐ฐ ์ฌ๋ฌผ์ด',
'health': '๐ ๊ฑด๊ฐ์ด',
'study': '๐ ํ์
์ด'
};
// ์ด์ธ ๋ฝ๊ธฐ ๋ฒํผ ํด๋ฆญ
drawBtn.addEventListener('click', function() {
// ์ฟ ํค ๊นจ์ง๋ ์ ๋๋ฉ์ด์
cookieEl.classList.add('crack');
setTimeout(() => {
cookieEl.classList.remove('crack');
// ๋๋ค ์ด์ธ ์ ํ
const randomIndex = Math.floor(Math.random() * fortuneData.length);
const fortune = fortuneData[randomIndex];
// ๊ฒฐ๊ณผ ํ์
categoryEl.textContent = categoryNames[fortune.category];
messageEl.textContent = fortune.message;
resultEl.style.display = 'block';
// ๊ฒฐ๊ณผ ์์ญ์ผ๋ก ๋ถ๋๋ฝ๊ฒ ์คํฌ๋กค
setTimeout(() => {
resultEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}, 100);
}, 500);
});
// ์ด์ธ ๋ณต์ฌ ๋ฒํผ ํด๋ฆญ
copyBtn.addEventListener('click', function() {
const text = `${categoryEl.textContent}\n${messageEl.textContent}`;
navigator.clipboard.writeText(text).then(() => {
const originalText = copyBtn.textContent;
copyBtn.textContent = 'โ
๋ณต์ฌ ์๋ฃ!';
setTimeout(() => {
copyBtn.textContent = originalText;
}, 2000);
}).catch(() => {
alert('๋ณต์ฌ์ ์คํจํ์ต๋๋ค. ๋ค์ ์๋ํด์ฃผ์ธ์.');
});
});
</script>
<?php include_once('./tail.sub.php'); ?>
```
์ด์ ๋ธ๋ผ์ฐ์ ์์ `/fortune-cookie-generator`๋ก ์ ์ํ๋ฉด ํฌ์ถ ์ฟ ํค ์์ฑ๊ธฐ๊ฐ ์๋ํฉ๋๋ค!
๊ธฐ๋ ๊ฒฐ๊ณผ: - `/fortune-cookie-generator` URL๋ก ์ ์ ๊ฐ๋ฅ - modern shell์ด ์ ์ฉ๋ ์ผ๊ด๋ ๋์์ธ - ํฐ ์ฟ ํค ์ด๋ชจ์ง(๐ฅ )๊ฐ ์ค์์ ํ์๋จ - '์ด์ธ ๋ฝ๊ธฐ' ๋ฒํผ์ด ๋ณด๋ผ์ ๊ทธ๋ผ๋ฐ์ด์ ์ผ๋ก ํ์๋จ - ๋ฒํผ ํด๋ฆญ ์ ์ฟ ํค๊ฐ ํ๋ค๋ฆฌ๋ ์ ๋๋ฉ์ด์ ์ฌ์ - ๋๋ค ์ด์ธ ๋ฉ์์ง๊ฐ ์นด๋ ํํ๋ก ๋ํ๋จ - ์นดํ ๊ณ ๋ฆฌ ์์ด์ฝ๊ณผ ํจ๊ป ์ด์ธ ํ์ - '์ด์ธ ๋ณต์ฌํ๊ธฐ' ๋ฒํผ ๋์ - ๋คํฌ๋ชจ๋ ์ ํ ์ ์๋์ผ๋ก ์์ ๋ณ๊ฒฝ - ๋ชจ๋ฐ์ผ์์๋ ๋ ์ด์์์ด ๊นจ์ง์ง ์์
**2๋จ๊ณ: localStorage๋ก ์ค๋์ ์ด์ธ ์ ์ฅ + ํ์คํ ๋ฆฌ ๊ธฐ๋ฅ ์ถ๊ฐ**
๊ฐ์ ๋ ์ฌ๋ฌ ๋ฒ ์ ์ํด๋ ๊ฐ์ ์ด์ธ๋ฅผ ๋ณด์ฌ์ฃผ๊ณ , ์ต๊ทผ 5๊ฐ์ ์ด์ธ ํ์คํ ๋ฆฌ๋ฅผ ํ๋จ์ ํ์ํ๋ ๊ธฐ๋ฅ์ ์ถ๊ฐํด๋ด
์๋ค.
`app/modules/fortune-cookie-generator/index.php` ํ์ผ์์ `<script>` ๋ธ๋ก์ ๋ค์๊ณผ ๊ฐ์ด ์์ ํ์ธ์:
```javascript
<script>
// ์ด์ธ ๋ฐ์ดํฐ๋ฅผ JavaScript๋ก ์ ๋ฌ
const fortuneData = <?php echo json_encode($all_fortunes, JSON_UNESCAPED_UNICODE); ?>;
const cookieEl = document.getElementById('fortuneCookie');
const drawBtn = document.getElementById('drawFortuneBtn');
const resultEl = document.getElementById('fortuneResult');
const categoryEl = document.getElementById('fortuneCategory');
const messageEl = document.getElementById('fortuneMessage');
const copyBtn = document.getElementById('copyBtn');
// ์นดํ
๊ณ ๋ฆฌ ํ๊ธ ์ด๋ฆ ๋งคํ
const categoryNames = {
'love': '๐ ์ฐ์ ์ด',
'money': '๐ฐ ์ฌ๋ฌผ์ด',
'health': '๐ ๊ฑด๊ฐ์ด',
'study': '๐ ํ์
์ด'
};
// ์ค๋ ๋ ์ง ๊ตฌํ๊ธฐ (YYYY-MM-DD ํ์)
function getTodayDate() {
const today = new Date();
return today.toISOString().split('T')[0];
}
// ์ค๋์ ์ด์ธ ๋ถ๋ฌ์ค๊ธฐ
function getTodayFortune() {
const saved = localStorage.getItem('todayFortune');
if (saved) {
const data = JSON.parse(saved);
if (data.date === getTodayDate()) {
return data.fortune;
}
}
return null;
}
// ์ค๋์ ์ด์ธ ์ ์ฅํ๊ธฐ
function saveTodayFortune(fortune) {
const data = {
date: getTodayDate(),
fortune: fortune,
timestamp: new Date().getTime()
};
localStorage.setItem('todayFortune', JSON.stringify(data));
// ํ์คํ ๋ฆฌ์๋ ์ถ๊ฐ
addToHistory(fortune);
}
// ํ์คํ ๋ฆฌ์ ์ถ๊ฐ
function addToHistory(fortune) {
let history = JSON.parse(localStorage.getItem('fortuneHistory') || '[]');
// ์ค๋ณต ๋ฐฉ์ง (๊ฐ์ ๋ ์ง์ ์ด์ธ๋ ํ๋๋ง)
history = history.filter(item => item.date !== getTodayDate());
// ์ ์ด์ธ๋ฅผ ๋งจ ์์ ์ถ๊ฐ
history.unshift({
date: getTodayDate(),
...fortune
});
// ์ต๋ 5๊ฐ๊น์ง๋ง ์ ์ง
if (history.length > 5) {
history = history.slice(0, 5);
}
localStorage.setItem('fortuneHistory', JSON.stringify(history));
displayHistory();
}
// ํ์คํ ๋ฆฌ ํ์
function displayHistory() {
const history = JSON.parse(localStorage.getItem('fortuneHistory') || '[]');
const historyEl = document.getElementById('fortuneHistory');
if (history.length === 0) {
historyEl.style.display = 'none';
return;
}
historyEl.style.display = 'block';
const listEl = document.getElementById('historyList');
listEl.innerHTML = '';
history.forEach(item => {
const li = document.createElement('div');
li.className = 'history-item';
li.innerHTML = `
<div class="history-date">${item.date}</div>
<div class="history-category">${categoryNames[item.category]}</div>
<div class="history-message">${item.message}</div>
`;
listEl.appendChild(li);
});
}
// ์ด์ธ ํ์ ํจ์
function displayFortune(fortune) {
categoryEl.textContent = categoryNames[fortune.category];
messageEl.textContent = fortune.message;
resultEl.style.display = 'block';
setTimeout(() => {
resultEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}, 100);
}
// ํ์ด์ง ๋ก๋ ์ ์ค๋์ ์ด์ธ๊ฐ ์์ผ๋ฉด ๋ฐ๋ก ํ์
window.addEventListener('DOMContentLoaded', function() {
const todayFortune = getTodayFortune();
if (todayFortune) {
displayFortune(todayFortune);
drawBtn.textContent = '๋ค์ ๋ณด๊ธฐ';
}
// ํ์คํ ๋ฆฌ ํ์
displayHistory();
});
// ์ด์ธ ๋ฝ๊ธฐ ๋ฒํผ ํด๋ฆญ
drawBtn.addEventListener('click', function() {
// ์ด๋ฏธ ์ค๋์ ์ด์ธ๊ฐ ์์ผ๋ฉด ๋ค์ ํ์๋ง
const existingFortune = getTodayFortune();
if (existingFortune) {
displayFortune(existingFortune);
return;
}
// ์ฟ ํค ๊นจ์ง๋ ์ ๋๋ฉ์ด์
cookieEl.classList.add('crack');
setTimeout(() => {
cookieEl.classList.remove('crack');
// ๋๋ค ์ด์ธ ์ ํ
const randomIndex = Math.floor(Math.random() * fortuneData.length);
const fortune = fortuneData[randomIndex];
// ์ค๋์ ์ด์ธ๋ก ์ ์ฅ
saveTodayFortune(fortune);
// ๊ฒฐ๊ณผ ํ์
displayFortune(fortune);
// ๋ฒํผ ํ
์คํธ ๋ณ๊ฒฝ
drawBtn.textContent = '๋ค์ ๋ณด๊ธฐ';
}, 500);
});
// ์ด์ธ ๋ณต์ฌ ๋ฒํผ ํด๋ฆญ
copyBtn.addEventListener('click', function() {
const text = `${categoryEl.textContent}\n${messageEl.textContent}`;
navigator.clipboard.writeText(text).then(() => {
const originalText = copyBtn.textContent;
copyBtn.textContent = 'โ
๋ณต์ฌ ์๋ฃ!';
setTimeout(() => {
copyBtn.textContent = originalText;
}, 2000);
}).catch(() => {
alert('๋ณต์ฌ์ ์คํจํ์ต๋๋ค. ๋ค์ ์๋ํด์ฃผ์ธ์.');
});
});
</script>
```
๊ทธ๋ฆฌ๊ณ `</main>` ํ๊ทธ ๋ฐ๋ก ์์ ํ์คํ ๋ฆฌ ์น์
์ ์ถ๊ฐํ์ธ์:
```php
<div class="fortune-history" id="fortuneHistory" style="display: none;">
<h2 class="history-title">๐ ์ต๊ทผ ์ด์ธ ๊ธฐ๋ก</h2>
<div id="historyList" class="history-list"></div>
</div>
</div>
</main>
```
๋ง์ง๋ง์ผ๋ก `<style>` ๋ธ๋ก์ ํ์คํ ๋ฆฌ ์คํ์ผ์ ์ถ๊ฐํ์ธ์:
```css
.fortune-history {
margin-top: 4rem;
padding-top: 3rem;
border-top: 1px solid var(--m-text-muted);
}
.history-title {
font-size: 1.5rem;
color: var(--m-text);
margin-bottom: 1.5rem;
}
.history-list {
display: flex;
flex-direction: column;
gap: 1rem;
}
.history-item {
background: var(--m-surface);
border-radius: var(--m-radius-lg);
padding: 1rem 1.5rem;
box-shadow: var(--m-shadow);
text-align: left;
transition: transform 0.2s ease;
}
.history-item:hover {
transform: translateX(5px);
}
.history-date {
font-size: 0.875rem;
color: var(--m-text-muted);
margin-bottom: 0.5rem;
}
.history-category {
font-size: 0.875rem;
color: var(--m-text-muted);
margin-bottom: 0.5rem;
}
.history-message {
font-size: 1rem;
color: var(--m-text);
line-height: 1.5;
}
```
๊ธฐ๋ ๊ฒฐ๊ณผ: - ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจํด๋ ์ค๋ ๋ฝ์ ์ด์ธ๊ฐ ๊ทธ๋๋ก ํ์๋จ - ๊ฐ์ ๋ ์ฌ๋ฌ ๋ฒ '์ด์ธ ๋ฝ๊ธฐ'๋ฅผ ๋๋ฌ๋ ๊ฐ์ ๊ฒฐ๊ณผ๊ฐ ๋์ด - ๋ฒํผ ํ ์คํธ๊ฐ '๋ค์ ๋ณด๊ธฐ'๋ก ๋ณ๊ฒฝ๋จ - ํ๋จ์ '์ต๊ทผ ์ด์ธ ๊ธฐ๋ก' ์น์ ์ด ๋ํ๋จ - ์ต๊ทผ 5๊ฐ์ ์ด์ธ๊ฐ ๋ ์ง์ ํจ๊ป ํ์๋จ - ๋ค์ ๋ ์ ์ํ๋ฉด ์๋ก์ด ์ด์ธ๋ฅผ ๋ฝ์ ์ ์์ - ํ์คํ ๋ฆฌ ํญ๋ชฉ์ ๋ง์ฐ์ค๋ฅผ ์ฌ๋ฆฌ๋ฉด ์ด์ง ์ค๋ฅธ์ชฝ์ผ๋ก ์ด๋ํ๋ ํจ๊ณผ
**3๋จ๊ณ: ํ์ด์ ์ซ์/์์ ์ถ๊ฐ + ๋ฐฐ๊ฒฝ ์ ๋๋ฉ์ด์
ํจ๊ณผ**
์ด์ธ์ ํจ๊ป ํ์ด์ ์ซ์์ ์์์ ๋ณด์ฌ์ฃผ๊ณ , ๋ฐฐ๊ฒฝ์ ๋ ๋ค๋๋ ๋ณ ํจ๊ณผ๋ฅผ ์ถ๊ฐํด๋ด
์๋ค.
๋จผ์ PHP ๋ถ๋ถ์์ ์ด์ธ ๋ฐ์ดํฐ์ ํ์ด์ ์ซ์์ ์์์ ์ถ๊ฐํ์ธ์. `$all_fortunes` ๋ฐฐ์ด์ ๋ง๋๋ ๋ถ๋ถ์ ๋ค์๊ณผ ๊ฐ์ด ์์ :
```php
// ๋ชจ๋ ์ด์ธ๋ฅผ ํ๋์ ๋ฐฐ์ด๋ก ํฉ์น๊ธฐ (ํ์ด์ ์ซ์/์์ ์ถ๊ฐ)
$all_fortunes = [];
$lucky_colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E2'];
foreach ($fortunes as $category => $messages) {
foreach ($messages as $message) {
$all_fortunes[] = [
'category' => $category,
'message' => $message,
'luckyNumber' => rand(1, 99),
'luckyColor' => $lucky_colors[array_rand($lucky_colors)],
];
}
}
```
๊ทธ๋ฆฌ๊ณ ์ด์ธ ์นด๋ HTML์ ํ์ด์ ์ ๋ณด๋ฅผ ํ์ํ ์์ญ์ ์ถ๊ฐํ์ธ์. `fortune-card` ์์ชฝ์ ์์ :
```php
<div class="fortune-result" id="fortuneResult" style="display: none;">
<div class="fortune-card">
<div class="fortune-category" id="fortuneCategory"></div>
<div class="fortune-message" id="fortuneMessage"></div>
<div class="lucky-info">
<div class="lucky-item">
<span class="lucky-label">๐ ํ์ด์ ์ซ์</span>
<span class="lucky-value" id="luckyNumber"></span>
</div>
<div class="lucky-item">
<span class="lucky-label">๐จ ํ์ด์ ์์</span>
<span class="lucky-color-box" id="luckyColorBox"></span>
</div>
</div>
<button class="copy-button" id="copyBtn">๐ ์ด์ธ ๋ณต์ฌํ๊ธฐ</button>
</div>
</div>
```
์คํ์ผ์ ํ์ด์ ์ ๋ณด์ ๋ฐฐ๊ฒฝ ์ ๋๋ฉ์ด์
์ ์ถ๊ฐํ์ธ์:
```css
/* ๋ฐฐ๊ฒฝ ๋ณ ์ ๋๋ฉ์ด์
*/
.fortune-wrapper {
max-width: 600px;
margin: 0 auto;
text-align: center;
position: relative;
}
.fortune-wrapper::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
radial-gradient(2px 2px at 20% 30%, white, transparent),
radial-gradient(2px 2px at 60% 70%, white, transparent),
radial-gradient(1px 1px at 50% 50%, white, transparent),
radial-gradient(1px 1px at 80% 10%, white, transparent),
radial-gradient(2px 2px at 90% 60%, white, transparent),
radial-gradient(1px 1px at 33% 90%, white, transparent);
background-size: 200% 200%;
animation: stars 20s ease-in-out infinite;
opacity: 0.3;
pointer-events: none;
z-index: -1;
}
@keyframes stars {
0%, 100% { background-position: 0% 0%; }
50% { background-position: 100% 100%; }
}
/* ํ์ด์ ์ ๋ณด ์คํ์ผ */
.lucky-info {
display: flex;
gap: 1rem;
justify-content: center;
margin: 1.5rem 0;
padding: 1rem;
background: rgba(102, 126, 234, 0.1);
border-radius: var(--m-radius-lg);
}
.lucky-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
}
.lucky-label {
font-size: 0.875rem;
color: var(--m-text-muted);
}
.lucky-value {
font-size: 1.5rem;
font-weight: 700;
color: var(--m-text);
}
.lucky-color-box {
width: 40px;
height: 40px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
border: 2px solid var(--m-surface);
}
@media (max-width: 768px) {
.lucky-info {
flex-direction: column;
gap: 1rem;
}
}
```
JavaScript์์ ํ์ด์ ์ ๋ณด๋ฅผ ํ์ํ๋๋ก ์์ ํ์ธ์. `displayFortune` ํจ์๋ฅผ ์์ :
```javascript
// ์ด์ธ ํ์ ํจ์
function displayFortune(fortune) {
categoryEl.textContent = categoryNames[fortune.category];
messageEl.textContent = fortune.message;
// ํ์ด์ ์ซ์์ ์์ ํ์
document.getElementById('luckyNumber').textContent = fortune.luckyNumber;
const colorBox = document.getElementById('luckyColorBox');
colorBox.style.backgroundColor = fortune.luckyColor;
resultEl.style.display = 'block';
setTimeout(() => {
resultEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}, 100);
}
```
๋ณต์ฌ ๋ฒํผ ํด๋ฆญ ์ ํ์ด์ ์ ๋ณด๋ ํจ๊ป ๋ณต์ฌ๋๋๋ก ์์ :
```javascript
// ์ด์ธ ๋ณต์ฌ ๋ฒํผ ํด๋ฆญ
copyBtn.addEventListener('click', function() {
const luckyNumber = document.getElementById('luckyNumber').textContent;
const text = `${categoryEl.textContent}\n${messageEl.textContent}\n\n๐ ํ์ด์ ์ซ์: ${luckyNumber}`;
navigator.clipboard.writeText(text).then(() => {
const originalText = copyBtn.textContent;
copyBtn.textContent = 'โ
๋ณต์ฌ ์๋ฃ!';
setTimeout(() => {
copyBtn.textContent = originalText;
}, 2000);
}).catch(() => {
alert('๋ณต์ฌ์ ์คํจํ์ต๋๋ค. ๋ค์ ์๋ํด์ฃผ์ธ์.');
});
});
```
๊ธฐ๋ ๊ฒฐ๊ณผ: - ์ด์ธ ๊ฒฐ๊ณผ์ ํ์ด์ ์ซ์๊ฐ ํฐ ๊ธ์จ๋ก ํ์๋จ - ํ์ด์ ์์์ด ์ปฌ๋ฌ ๋ฐ์ค๋ก ์๊ฐ์ ์ผ๋ก ํ์๋จ - ๋ฐฐ๊ฒฝ์ ์์ํ๊ฒ ๋ ๋ค๋๋ ๋ณ ํจ๊ณผ๊ฐ ๋ํ๋จ - ๋ณ๋ค์ด ์ฒ์ฒํ ์์ง์ด๋ ์ ๋๋ฉ์ด์ ์ฌ์ - ์ด์ธ ๋ณต์ฌ ์ ํ์ด์ ์ซ์๋ ํจ๊ป ๋ณต์ฌ๋จ - ๋ชจ๋ฐ์ผ์์๋ ํ์ด์ ์ ๋ณด๊ฐ ์ธ๋ก๋ก ๋ฐฐ์น๋จ - ๋คํฌ๋ชจ๋์์๋ ๋ณ ํจ๊ณผ๊ฐ ๋ณด์ (ํฌ๋ช ๋๋ก ์กฐ์ ) - ์ ์ฒด์ ์ผ๋ก ๋ ํ์ฑํ๊ณ ์ฌ๋ฏธ์๋ UI ์์ฑ
์ ์ถ๋ ์ํ (0)
๋ก๊ทธ์ธ ํ ์ ์ถ์์ง ์ ์ถ๋ ์ํ์ด ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋์ ์๊ฐ ๋์ด๋ณด์ธ์!