๐ฏ ์ค๋์ ๋ ๋ณ ๊ถํฉ ์ด์ธ - ๋์ 12๋ ๊ถํฉ ํ ์คํธ
g5se-zodiac-compatibility-fortune
๋ฌธ์ ์ค๋ช
ํ๋ก์ ํธ ๊ฐ์
๋์ 12๋ (์ฅ, ์, ํธ๋์ด, ํ ๋ผ, ์ฉ, ๋ฑ, ๋ง, ์, ์์ญ์ด, ๋ญ, ๊ฐ, ๋ผ์ง)๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ค๋์ ๊ถํฉ ์ด์ธ๋ฅผ ์ ๊ณตํ๋ ์ธํฐ๋ํฐ๋ธ ํ์ด์ง์ ๋๋ค. ์ฌ์ฉ์๊ฐ ์์ ์ ๋ ์ ์๋๋ฐฉ์ ๋ ๋ฅผ ์ ํํ๋ฉด, ๊ถํฉ ์ ์์ ์ฌ๋ฏธ์๋ ์ด์ธ ๋ฉ์์ง๋ฅผ ํ์ธํ ์ ์์ต๋๋ค. ์นด๋ ์ ํ ์ ๋๋ฉ์ด์ ๊ณผ ๊ฒฐ๊ณผ ํ๋ฉด ์ ํ์ผ๋ก ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐํํฉ๋๋ค.
ํ์ ๊ธฐ๋ฅ
- 12๋ ์นด๋ ์ ํ UI: ๊ฐ ๋ ๋ฅผ ๋ํํ๋ ์ด๋ชจ์ง(๐ญ๐ฎ๐ฏ๐ฐ๐ฒ๐๐ด๐๐ต๐๐ถ๐ท)์ ํ๊ธ ์ด๋ฆ์ด ๋ด๊ธด ์นด๋๋ฅผ ๊ทธ๋ฆฌ๋๋ก ๋ฐฐ์น. ํธ๋ฒ ์ ์ด์ง ํ๋๋๋ ์ ๋๋ฉ์ด์
- 2๋จ๊ณ ์ ํ ํ๋ก์ธ์ค: "๋ด ๋ ์ ํ" โ "์๋๋ฐฉ ๋ ์ ํ" ์์๋ก ์งํ. ์ ํํ ์นด๋๋ ๊ฐ์กฐ ํ์
- ๊ถํฉ ์ ์ ๊ณ์ฐ: ๋ ์กฐํฉ์ ๋ฐ๋ผ 0~100์ ์ฌ์ด์ ๊ถํฉ ์ ์ ์ฐ์ถ (๋ฏธ๋ฆฌ ์ ์๋ ๊ถํฉ ๋ฐ์ดํฐ ๊ธฐ๋ฐ). ์: ์ฅ-์ฉ 95์ (์ต๊ณ ๊ถํฉ), ์ฅ-๋ง 40์ (์๊ทน)
- ์ด์ธ ๋ฉ์์ง ์์ฑ: ๊ถํฉ ์ ์ ๊ตฌ๊ฐ(90
100/7089/50~69/50 ๋ฏธ๋ง)์ ๋ฐ๋ผ ๋ค๋ฅธ ๋ฉ์์ง ํ์. ์: "์ฒ์์ฐ๋ถ์ด์์! ๐", "๋ ธ๋ ฅํ๋ฉด ์ข์ ๊ด๊ณ๋ฅผ ๋ง๋ค ์ ์์ด์ ๐ช" - ๊ฒฐ๊ณผ ํ๋ฉด: ๋ ๋ ์ ์ด๋ชจ์ง, ๊ถํฉ ์ ์(ํ๋ก๊ทธ๋ ์ค ๋ฐ๋ก ์๊ฐํ), ์ด์ธ ๋ฉ์์ง, ์ค๋์ ์กฐ์ธ(๋๋ค) ํ์
- ๋ค์ ํ๊ธฐ ๋ฒํผ: ๊ฒฐ๊ณผ ํ๋ฉด์์ ํด๋ฆญ ์ ์ด๊ธฐ ํ๋ฉด์ผ๋ก ๋์๊ฐ๊ธฐ
- SQLite ๊ธฐ๋ก: ์กฐํ๋ ๋ ์กฐํฉ๊ณผ ์ ์๋ฅผ
G5_DATA_PATH/modules/zodiac_compatibility.sqlite์ ์ ์ฅ (์ธ๊ธฐ ์กฐํฉ ํต๊ณ์ฉ)
์ ํ ์ถ๊ฐ ๊ธฐ๋ฅ (๋ณด๋์ค)
- ์ธ๊ธฐ ์กฐํฉ TOP 3: ํ์ด์ง ํ๋จ์ ๊ฐ์ฅ ๋ง์ด ์กฐํ๋ ๋ ์กฐํฉ ์์ ํ์ (DB ์ง๊ณ)
- ๊ณต์ ํ๊ธฐ ๋ฒํผ: ๊ฒฐ๊ณผ๋ฅผ ์ด๋ฏธ์ง๋ก ์บก์ฒํ๊ฑฐ๋ ํ ์คํธ๋ก ๋ณต์ฌํ๋ ๊ธฐ๋ฅ
- ์๋ ์ ๋ ฅ ๋ชจ๋: ๋ ๋ฅผ ์ง์ ์ ํํ๋ ๋์ ํ์ด๋ ํด๋ฅผ ์ ๋ ฅํ๋ฉด ์๋์ผ๋ก ๋ ๊ณ์ฐ (์: 1990๋ โ ๋ง๋ )
- ์ ๋๋ฉ์ด์ ๊ฐํ: ๊ฒฐ๊ณผ ํ๋ฉด ์ง์ ์ ์ ์๊ฐ 0์์ ์ต์ข ์ ์๊น์ง ์นด์ดํธ์ ๋๋ ํจ๊ณผ
- ๋คํฌ๋ชจ๋ ํ ๋ง: ์๊ฐ๋์ ๋ฐ๋ผ ์๋์ผ๋ก ๋ฐ์/์ด๋์ด ๋ฐฐ๊ฒฝ ์ ํ (modern shell ํ ํฐ ํ์ฉ)
๊ธฐ์ ์คํ
- ํ๋ ์์ํฌ: ๊ทธ๋๋ณด๋5 SE (PHP 8.x)
- DB: SQLite (๋ชจ๋ ์ ์ฉ ํ์ผ)
- ํ๋ก ํธ์๋: ์์ HTML/CSS/JS (CDN ์์ด ์ธ๋ผ์ธ ๊ตฌํ)
- ๋์์ธ: g5se modern shell ํ ํฐ ๊ธฐ๋ฐ ๋คํฌ๋ชจ๋ ๋์
ํ์ต ๋ชฉํ
- g5se์ ํด๋ ๊ธฐ๋ฐ ์๋ ๋ผ์ฐํ ์ดํด
- modern shell ์ ์ฉ์ผ๋ก ์ฌ์ดํธ ๋์์ธ ์ผ๊ด์ฑ ์ ์ง
- SQLite ํ์ผ DB ์์ฑ ๋ฐ CRUD ์ฐ์ต
- ์ฌ์ฉ์ ์ธํฐ๋์ (์นด๋ ์ ํ, ํ๋ฉด ์ ํ) ๊ตฌํ
- CSS ๋ณ์๋ฅผ ํ์ฉํ ๋คํฌ๋ชจ๋ ๋์ ๋์์ธ
์ ์ฝ ์กฐ๊ฑด
- โข ๊ทธ๋๋ณด๋5 SE(g5se) ํ๋ ์์ํฌ ์ ์ฉ โ Laravel/React/Vue ์ฌ์ฉ ๊ธ์ง
- โข ํด๋ ๊ธฐ๋ฐ ์๋ ๋ผ์ฐํ ์ฌ์ฉ: app/modules/zodiac-compat/index.php โ /zodiac-compat
- โข 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/zodiac_compatibility.sqlite (g5se ๋ฉ์ธ DB ํ ์ด๋ธ ์ถ๊ฐ ๊ธ์ง)
- โข ์ธ๋ถ CDN/๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ ๊ธ์ง โ ์์ HTML/CSS/JS๋ก ๊ตฌํ
- โข prepared statement๋ก SQL ์ธ์ ์ ๋ฐฉ์ง
- โข ์ฌ์ฉ์ ์ ๋ ฅ์ htmlspecialchars()๋ก ์ด์ค์ผ์ดํ
- โข ๋ฐ์ํ ๋์์ธ: ๋ชจ๋ฐ์ผ/ํ๋ธ๋ฆฟ/๋ฐ์คํฌํฑ ๋ชจ๋ ์ง์
ํ๋กฌํํธ ๋ ์ํผ
์๋ ํ๋กฌํํธ๋ฅผ ์์๋๋ก AI์๊ฒ ๋ณด๋ด๋ฉด ๋ฉ๋๋ค. ๋ณต์ฌ ๋ฒํผ์ ๋๋ฌ ๋ฐ๋ก ์ฌ์ฉํ์ธ์.
g5se๊ฐ ์ค์น๋ ๋๋ ํฐ๋ฆฌ(๋ฃจํธ์ `index.php`ยท`app/`์ด ์๋ ๊ณณ)์์ ์์
์ ์์ํฉ๋๋ค. (g5se ์ค์น๋ https://github.com/gnuboard/g5se ์ฐธ๊ณ )
**๋ชฉํ**: ๋ ๋ณ ๊ถํฉ ์ด์ธ ํ์ด์ง์ ๊ธฐ๋ณธ ๊ณจ๊ฒฉ์ ๋ง๋ค๊ณ , 12๋ ์นด๋ ์ ํ UI๋ฅผ ๊ตฌํํฉ๋๋ค.
**์์
๋ด์ฉ**:
1. `app/modules/zodiac-compat/` ํด๋๋ฅผ ์์ฑํ๊ณ ๊ทธ ์์ `index.php` ํ์ผ์ ๋ง๋ญ๋๋ค.
2. `index.php`์ ๋ค์ ๋ด์ฉ์ ์์ฑํฉ๋๋ค:
- `_common.php` ์ธํด๋ฃจ๋ (g5se ์ง์
์ปจํ
์คํธ)
- 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;">
<!-- ํ์ด์ง ๋ณธ๋ฌธ -->
</main>
<?php require G5_THEME_PATH.'/modern/_footer.inc.php'; ?>
</div>
<?php include_once('./tail.sub.php'); ?>
```
3. `<main>` ์์ ๋ค์ HTML ๊ตฌ์กฐ๋ฅผ ์์ฑ:
- **ํค๋ ์น์
**: ํ์ด์ง ์ ๋ชฉ "๐ฏ ์ค๋์ ๋ ๋ณ ๊ถํฉ ์ด์ธ", ๋ถ์ "๋ ์ฌ๋์ ๋ ๋ฅผ ์ ํํด๋ณด์ธ์"
- **์ ํ ๋จ๊ณ ํ์**: "1๋จ๊ณ: ๋ด ๋ ์ ํ" ํ
์คํธ (๋์ค์ ๋์ ์ผ๋ก ๋ณ๊ฒฝ๋ ์์ญ)
- **12๋ ์นด๋ ๊ทธ๋ฆฌ๋**: 4์ด ๊ทธ๋ฆฌ๋๋ก 12๊ฐ์ ์นด๋ ๋ฐฐ์น. ๊ฐ ์นด๋๋ `<button>` ํ๊ทธ๋ก ๋ง๋ค๊ณ , ๋ค์ ์ ๋ณด ํฌํจ:
- ๋ ์ด๋ชจ์ง (๐ญ์ฅ, ๐ฎ์, ๐ฏํธ๋์ด, ๐ฐํ ๋ผ, ๐ฒ์ฉ, ๐๋ฑ, ๐ด๋ง, ๐์, ๐ต์์ญ์ด, ๐๋ญ, ๐ถ๊ฐ, ๐ท๋ผ์ง)
- ๋ ์ด๋ฆ (ํ๊ธ)
- `data-zodiac` ์์ฑ์ ์๋ฌธ ๋ ์ด๋ฆ ์ ์ฅ (์: `data-zodiac="rat"`)
- **๊ฒฐ๊ณผ ํ๋ฉด ์์ญ**: ์ด๊ธฐ์๋ `display: none`์ผ๋ก ์จ๊น. ๋์ค์ JavaScript๋ก ํ์
4. CSS ์คํ์ผ ์์ฑ (`<style>` ๋ธ๋ก์ `<main>` ์์ ์ธ๋ผ์ธ์ผ๋ก ์ถ๊ฐ):
- ํค๋: ์ค์ ์ ๋ ฌ, ํฐ ์ ๋ชฉ ํฐํธ ํฌ๊ธฐ, `color: var(--m-text)`
- ์นด๋ ๊ทธ๋ฆฌ๋: `display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px;` (๋ชจ๋ฐ์ผ์ 2์ด๋ก ๋ฐ์ํ ์กฐ์ )
- ๊ฐ ์นด๋:
- `background: var(--m-surface);`
- `border: 2px solid transparent;`
- `border-radius: var(--m-radius-lg);`
- `box-shadow: var(--m-shadow);`
- `padding: 20px;`
- `cursor: pointer;`
- ํธ๋ฒ ์: `transform: scale(1.05); border-color: #ff6b6b;` (๋นจ๊ฐ ๊ฐ์กฐ์)
- ์ ํ ์: `border-color: #ff6b6b; background: rgba(255, 107, 107, 0.1);`
- ์ด๋ชจ์ง: `font-size: 48px;`
- ๋ ์ด๋ฆ: `color: var(--m-text); font-weight: 600;`
5. JavaScript ๊ธฐ๋ณธ ๊ตฌ์กฐ ์์ฑ (`<script>` ๋ธ๋ก์ `</main>` ์ง์ ์ ์ถ๊ฐ):
- ๋ณ์ ์ ์ธ: `let myZodiac = null; let partnerZodiac = null; let step = 1;`
- ์นด๋ ํด๋ฆญ ์ด๋ฒคํธ ๋ฆฌ์ค๋:
- ํด๋ฆญ ์ `data-zodiac` ๊ฐ์ ๊ฐ์ ธ์์ `step`์ ๋ฐ๋ผ `myZodiac` ๋๋ `partnerZodiac`์ ์ ์ฅ
- ์ ํ๋ ์นด๋์ `selected` ํด๋์ค ์ถ๊ฐ (CSS๋ก ์คํ์ผ ๋ณ๊ฒฝ)
- `step`์ด 1์ด๋ฉด "2๋จ๊ณ: ์๋๋ฐฉ ๋ ์ ํ"์ผ๋ก ํ
์คํธ ๋ณ๊ฒฝํ๊ณ `step`์ 2๋ก ์ฆ๊ฐ
- `step`์ด 2์ด๋ฉด ๊ฒฐ๊ณผ ํ๋ฉด์ผ๋ก ์ ํํ๋ ํจ์ ํธ์ถ (๋ค์ ๋จ๊ณ์์ ๊ตฌํ)
**์์ ๊ฒฐ๊ณผ**: `/zodiac-compat` URL๋ก ์ ์ํ๋ฉด 12๋ ์นด๋๊ฐ 4์ด ๊ทธ๋ฆฌ๋๋ก ํ์๋๊ณ , ์นด๋ ํธ๋ฒ ์ ํ๋ ์ ๋๋ฉ์ด์
์ด ์๋ํฉ๋๋ค. ์ฒซ ๋ฒ์งธ ์นด๋๋ฅผ ํด๋ฆญํ๋ฉด "2๋จ๊ณ: ์๋๋ฐฉ ๋ ์ ํ" ์๋ด๊ฐ ๋ํ๋๊ณ , ๋ ๋ฒ์งธ ์นด๋๋ฅผ ํด๋ฆญํ๋ฉด ์ฝ์์ ์ ํ๋ ๋ ์ ๋ณด๊ฐ ์ถ๋ ฅ๋ฉ๋๋ค (์์ง ๊ฒฐ๊ณผ ํ๋ฉด์ ์์).
๊ธฐ๋ ๊ฒฐ๊ณผ: ํด๋ ๊ธฐ๋ฐ ๋ผ์ฐํ ์ผ๋ก `/zodiac-compat` ํ์ด์ง๊ฐ ์๋ ์์ฑ๋๊ณ , modern shell์ด ์ ์ฉ๋์ด ์ฌ์ดํธ ๋ค๋น๊ฒ์ด์ /ํธํฐ๊ฐ ํ์๋ฉ๋๋ค. 12๋ ์นด๋ ๊ทธ๋ฆฌ๋๊ฐ ๋ฐ์ํ์ผ๋ก ๋ฐฐ์น๋๋ฉฐ, ํธ๋ฒ ์ ์นด๋๊ฐ ํ๋๋๋ ์ ๋๋ฉ์ด์ ์ด ์๋ํฉ๋๋ค. ์นด๋ ํด๋ฆญ ์ ์ ํ ์ํ๊ฐ ์๊ฐ์ ์ผ๋ก ํ์๋๊ณ , ๋จ๊ณ๋ณ ์๋ด ํ ์คํธ๊ฐ ๋ณ๊ฒฝ๋ฉ๋๋ค.
**๋ชฉํ**: ๋ ์กฐํฉ์ ๋ฐ๋ฅธ ๊ถํฉ ์ ์ ๊ณ์ฐ ๋ก์ง์ ๊ตฌํํ๊ณ , ๊ฒฐ๊ณผ ํ๋ฉด์ ๋ง๋ญ๋๋ค.
**์์
๋ด์ฉ**:
1. JavaScript์ ๊ถํฉ ๋ฐ์ดํฐ ๊ฐ์ฒด ์ถ๊ฐ (`<script>` ๋ธ๋ก ์ต์๋จ):
```javascript
const compatibilityData = {
// ํค ํ์: "๋ 1_๋ 2" (์ํ๋ฒณ ์์๋ก ์ ๋ ฌ)
"rat_dragon": { score: 95, message: "์ฒ์์ฐ๋ถ์ด์์! ์๋ก๋ฅผ ์๋ฒฝํ๊ฒ ์ดํดํ๋ ์ต๊ณ ์ ์กฐํฉ์
๋๋ค. ๐" },
"rat_monkey": { score: 90, message: "ํ์์ ์ผ๋ฏธ! ํจ๊ป ์์ผ๋ฉด ์ฆ๊ฑฐ์ด ์๊ฐ์ด ๊ฐ๋ํฉ๋๋ค. ๐" },
"rat_horse": { score: 40, message: "์๊ทน ๊ด๊ณ์์. ์๋ก ๋ค๋ฅธ ์ ์ ์ธ์ ํ๋ ๋
ธ๋ ฅ์ด ํ์ํฉ๋๋ค. ๐ช" },
// ... (๋ชจ๋ ์กฐํฉ 66๊ฐ ์ถ๊ฐ โ 12C2 = 66)
// ์ ์ ๋ถํฌ: 90~100์ (์ต๊ณ ๊ถํฉ) 10๊ฐ, 70~89์ (์ข์ ๊ถํฉ) 20๊ฐ, 50~69์ (๋ณดํต) 20๊ฐ, 50์ ๋ฏธ๋ง(์๊ทน) 16๊ฐ
};
```
โป AI์๊ฒ "12๋ ์ ์ ํต์ ์ธ ๊ถํฉ ์ด๋ก ์ ์ฐธ๊ณ ํ์ฌ 66๊ฐ ์กฐํฉ์ ์ ์์ ๋ฉ์์ง๋ฅผ ์์ฑํด์ฃผ์ธ์. ๊ฐ์ ๋ ๋ผ๋ฆฌ๋ 75์ ์ ๋๋ก ์ค์ ํ๊ณ , ์ผํฉ(ไธๅ)ยท์กํฉ(ๅ
ญๅ) ๊ด๊ณ๋ 90์ ์ด์, ์ก์ถฉ(ๅ
ญๆฒ) ๊ด๊ณ๋ 40์ ์ดํ๋ก ํด์ฃผ์ธ์."๋ผ๊ณ ์์ฒญ
2. ๊ถํฉ ์ ์ ๊ณ์ฐ ํจ์ ์์ฑ:
```javascript
function getCompatibility(zodiac1, zodiac2) {
// ์ํ๋ฒณ ์์๋ก ์ ๋ ฌํ์ฌ ํค ์์ฑ
const key = [zodiac1, zodiac2].sort().join('_');
return compatibilityData[key] || { score: 60, message: "ํ๋ฒํ ๊ถํฉ์ด์์. ๋
ธ๋ ฅํ๋ฉด ์ข์ ๊ด๊ณ๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค." };
}
```
3. ๊ฒฐ๊ณผ ํ๋ฉด HTML ๊ตฌ์กฐ ์ถ๊ฐ (`<main>` ์, ์นด๋ ๊ทธ๋ฆฌ๋ ์๋):
```html
<div id="result-screen" style="display: none;">
<div class="result-header">
<h2>๊ถํฉ ๊ฒฐ๊ณผ</h2>
</div>
<div class="result-zodiacs">
<div class="result-zodiac" id="my-zodiac-result">
<div class="zodiac-emoji">๐ญ</div>
<div class="zodiac-name">์ฅ๋ </div>
</div>
<div class="result-heart">๐</div>
<div class="result-zodiac" id="partner-zodiac-result">
<div class="zodiac-emoji">๐ฒ</div>
<div class="zodiac-name">์ฉ๋ </div>
</div>
</div>
<div class="result-score">
<div class="score-label">๊ถํฉ ์ ์</div>
<div class="score-value" id="score-value">95</div>
<div class="score-bar">
<div class="score-fill" id="score-fill" style="width: 95%;"></div>
</div>
</div>
<div class="result-message" id="result-message">
์ฒ์์ฐ๋ถ์ด์์! ์๋ก๋ฅผ ์๋ฒฝํ๊ฒ ์ดํดํ๋ ์ต๊ณ ์ ์กฐํฉ์
๋๋ค. ๐
</div>
<button id="retry-btn" class="retry-button">๋ค์ ํ๊ธฐ</button>
</div>
```
4. ๊ฒฐ๊ณผ ํ๋ฉด CSS ์ถ๊ฐ:
- `#result-screen`: `text-align: center; animation: fadeIn 0.5s;`
- `.result-zodiacs`: ๊ฐ๋ก ๋ฐฐ์น (`display: flex; justify-content: center; align-items: center; gap: 20px;`)
- `.result-zodiac`: ์ธ๋ก ๋ฐฐ์น, ์ด๋ชจ์ง ํฌ๊ธฐ `72px`, ์ด๋ฆ `font-size: 18px;`
- `.result-heart`: `font-size: 48px;`
- `.score-value`: `font-size: 64px; font-weight: bold; color: #ff6b6b;`
- `.score-bar`: `width: 100%; max-width: 400px; height: 20px; background: var(--m-surface); border-radius: 10px; margin: 20px auto; overflow: hidden;`
- `.score-fill`: `height: 100%; background: linear-gradient(90deg, #ff6b6b, #ff8787); transition: width 1s ease;`
- `.retry-button`: `padding: 12px 32px; background: #ff6b6b; color: white; border: none; border-radius: var(--m-radius-lg); cursor: pointer; font-size: 16px;`
- `@keyframes fadeIn`: `from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); }`
5. ๊ฒฐ๊ณผ ํ๋ฉด ํ์ ํจ์ ์์ฑ:
```javascript
function showResult() {
const compat = getCompatibility(myZodiac, partnerZodiac);
// ๋ ์ ๋ณด ์
๋ฐ์ดํธ
document.querySelector('#my-zodiac-result .zodiac-emoji').textContent = getZodiacEmoji(myZodiac);
document.querySelector('#my-zodiac-result .zodiac-name').textContent = getZodiacName(myZodiac);
document.querySelector('#partner-zodiac-result .zodiac-emoji').textContent = getZodiacEmoji(partnerZodiac);
document.querySelector('#partner-zodiac-result .zodiac-name').textContent = getZodiacName(partnerZodiac);
// ์ ์ ๋ฐ ๋ฉ์์ง ์
๋ฐ์ดํธ
document.getElementById('score-value').textContent = compat.score;
document.getElementById('score-fill').style.width = compat.score + '%';
document.getElementById('result-message').textContent = compat.message;
// ํ๋ฉด ์ ํ
document.querySelector('.zodiac-cards').style.display = 'none';
document.getElementById('result-screen').style.display = 'block';
}
// ํฌํผ ํจ์
function getZodiacEmoji(zodiac) {
const emojis = { rat: '๐ญ', ox: '๐ฎ', tiger: '๐ฏ', rabbit: '๐ฐ', dragon: '๐ฒ', snake: '๐', horse: '๐ด', goat: '๐', monkey: '๐ต', rooster: '๐', dog: '๐ถ', pig: '๐ท' };
return emojis[zodiac];
}
function getZodiacName(zodiac) {
const names = { rat: '์ฅ๋ ', ox: '์๋ ', tiger: 'ํธ๋์ด๋ ', rabbit: 'ํ ๋ผ๋ ', dragon: '์ฉ๋ ', snake: '๋ฑ๋ ', horse: '๋ง๋ ', goat: '์๋ ', monkey: '์์ญ์ด๋ ', rooster: '๋ญ๋ ', dog: '๊ฐ๋ ', pig: '๋ผ์ง๋ ' };
return names[zodiac];
}
```
6. "๋ค์ ํ๊ธฐ" ๋ฒํผ ์ด๋ฒคํธ ์ถ๊ฐ:
```javascript
document.getElementById('retry-btn').addEventListener('click', function() {
myZodiac = null;
partnerZodiac = null;
step = 1;
document.querySelectorAll('.zodiac-card').forEach(card => card.classList.remove('selected'));
document.querySelector('.step-indicator').textContent = '1๋จ๊ณ: ๋ด ๋ ์ ํ';
document.querySelector('.zodiac-cards').style.display = 'grid';
document.getElementById('result-screen').style.display = 'none';
});
```
**์์ ๊ฒฐ๊ณผ**: ๋ ๊ฐ์ ๋ ๋ฅผ ์ ํํ๋ฉด ์นด๋ ๊ทธ๋ฆฌ๋๊ฐ ์ฌ๋ผ์ง๊ณ ๊ฒฐ๊ณผ ํ๋ฉด์ด ํ์ด๋์ธ ์ ๋๋ฉ์ด์
๊ณผ ํจ๊ป ๋ํ๋ฉ๋๋ค. ์ ํํ ๋ ๋ ์ ์ด๋ชจ์ง์ ์ด๋ฆ, ๊ถํฉ ์ ์(ํ๋ก๊ทธ๋ ์ค ๋ฐ๋ก ์๊ฐํ), ๋ฉ์์ง๊ฐ ํ์๋ฉ๋๋ค. "๋ค์ ํ๊ธฐ" ๋ฒํผ์ ํด๋ฆญํ๋ฉด ์ด๊ธฐ ํ๋ฉด์ผ๋ก ๋์๊ฐ๋๋ค.
๊ธฐ๋ ๊ฒฐ๊ณผ: ๋ ์กฐํฉ์ ๋ฐ๋ผ ์ ํํ ๊ถํฉ ์ ์์ ๋ฉ์์ง๊ฐ ๊ณ์ฐ๋์ด ๊ฒฐ๊ณผ ํ๋ฉด์ ํ์๋ฉ๋๋ค. ํ๋ก๊ทธ๋ ์ค ๋ฐ๊ฐ ์ ์์ ๋ง๊ฒ ์ฑ์์ง๊ณ , ํ์ด๋์ธ ์ ๋๋ฉ์ด์ ์ด ๋ถ๋๋ฝ๊ฒ ์๋ํฉ๋๋ค. "๋ค์ ํ๊ธฐ" ๋ฒํผ์ผ๋ก ์ด๊ธฐ ํ๋ฉด ๋ณต๊ท๊ฐ ์ ์ ์๋ํฉ๋๋ค.
**๋ชฉํ**: SQLite ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฐ๋ํ์ฌ ์กฐํ๋ ๋ ์กฐํฉ์ ๊ธฐ๋กํ๊ณ , ํ์ด์ง ํ๋จ์ ์ธ๊ธฐ ์กฐํฉ TOP 3๋ฅผ ํ์ํฉ๋๋ค.
**์์
๋ด์ฉ**:
1. PHP ์ฝ๋ ์๋จ์ DB ์ฐ๊ฒฐ ๋ฐ ํ
์ด๋ธ ์์ฑ ๋ก์ง ์ถ๊ฐ (modern shell ์ธํด๋ฃจ๋ ์ ):
```php
<?php
include_once('./_common.php');
// SQLite DB ์ค์
$db_dir = G5_DATA_PATH.'/modules';
@mkdir($db_dir, 0707, true); // ๋๋ ํฐ๋ฆฌ๊ฐ ์์ผ๋ฉด ์์ฑ
$db_path = $db_dir.'/zodiac_compatibility.sqlite';
try {
$pdo = new PDO('sqlite:'.$db_path);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// ํ
์ด๋ธ ์์ฑ (์ฒซ ์ ๊ทผ ์ ์๋ ์์ฑ)
$pdo->exec("CREATE TABLE IF NOT EXISTS compatibility_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
zodiac1 TEXT NOT NULL,
zodiac2 TEXT NOT NULL,
score INTEGER NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)");
// ์ธ๋ฑ์ค ์์ฑ (์กฐํ ์ฑ๋ฅ ํฅ์)
$pdo->exec("CREATE INDEX IF NOT EXISTS idx_zodiacs ON compatibility_logs(zodiac1, zodiac2)");
} catch (PDOException $e) {
die('DB ์ฐ๊ฒฐ ์คํจ: ' . htmlspecialchars($e->getMessage()));
}
// AJAX ์์ฒญ ์ฒ๋ฆฌ
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
header('Content-Type: application/json');
if ($_POST['action'] === 'save_result') {
$zodiac1 = $_POST['zodiac1'] ?? '';
$zodiac2 = $_POST['zodiac2'] ?? '';
$score = (int)($_POST['score'] ?? 0);
// ์ํ๋ฒณ ์์๋ก ์ ๋ ฌ
$zodiacs = [$zodiac1, $zodiac2];
sort($zodiacs);
$stmt = $pdo->prepare("INSERT INTO compatibility_logs (zodiac1, zodiac2, score) VALUES (?, ?, ?)");
$stmt->execute([$zodiacs[0], $zodiacs[1], $score]);
echo json_encode(['success' => true]);
exit;
}
if ($_POST['action'] === 'get_popular') {
$stmt = $pdo->query("
SELECT zodiac1, zodiac2, COUNT(*) as count, AVG(score) as avg_score
FROM compatibility_logs
GROUP BY zodiac1, zodiac2
ORDER BY count DESC
LIMIT 3
");
$popular = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode($popular);
exit;
}
}
require_once(G5_THEME_PATH.'/modern/_head.inc.php');
include_once('./head.sub.php');
?>
```
2. JavaScript์ ๊ฒฐ๊ณผ ์ ์ฅ ํจ์ ์ถ๊ฐ (`showResult()` ํจ์ ๋์):
```javascript
function showResult() {
const compat = getCompatibility(myZodiac, partnerZodiac);
// ... (๊ธฐ์กด ์ฝ๋)
// DB์ ๊ฒฐ๊ณผ ์ ์ฅ
fetch(window.location.href, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
action: 'save_result',
zodiac1: myZodiac,
zodiac2: partnerZodiac,
score: compat.score
})
});
// ํ๋ฉด ์ ํ
document.querySelector('.zodiac-cards').style.display = 'none';
document.getElementById('result-screen').style.display = 'block';
}
```
3. ์ธ๊ธฐ ์กฐํฉ ์น์
HTML ์ถ๊ฐ (`<main>` ๋, `#result-screen` ์๋):
```html
<div class="popular-section">
<h3>๐ฅ ์ธ๊ธฐ ์กฐํฉ TOP 3</h3>
<div id="popular-list" class="popular-list">
<div class="loading">๋ฐ์ดํฐ ๋ก๋ฉ ์ค...</div>
</div>
</div>
```
4. ์ธ๊ธฐ ์กฐํฉ CSS ์ถ๊ฐ:
- `.popular-section`: `margin-top: 60px; padding-top: 40px; border-top: 1px solid rgba(128,128,128,0.2);`
- `.popular-list`: `display: grid; gap: 16px; margin-top: 20px;`
- `.popular-item`: `background: var(--m-surface); border-radius: var(--m-radius-lg); padding: 20px; box-shadow: var(--m-shadow); display: flex; justify-content: space-between; align-items: center;`
- `.popular-zodiacs`: `display: flex; gap: 12px; align-items: center; font-size: 24px;`
- `.popular-stats`: `text-align: right; color: var(--m-text-muted); font-size: 14px;`
5. ์ธ๊ธฐ ์กฐํฉ ๋ก๋ฉ ํจ์ ์์ฑ (ํ์ด์ง ๋ก๋ ์ ์คํ):
```javascript
function loadPopularCombinations() {
fetch(window.location.href, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ action: 'get_popular' })
})
.then(res => res.json())
.then(data => {
const container = document.getElementById('popular-list');
if (data.length === 0) {
container.innerHTML = '<div class="no-data">์์ง ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ์กฐํ์๊ฐ ๋์ด๋ณด์ธ์!</div>';
return;
}
container.innerHTML = data.map((item, index) => `
<div class="popular-item">
<div class="popular-rank">${index + 1}์</div>
<div class="popular-zodiacs">
${getZodiacEmoji(item.zodiac1)} ร ${getZodiacEmoji(item.zodiac2)}
<span>${getZodiacName(item.zodiac1)} & ${getZodiacName(item.zodiac2)}</span>
</div>
<div class="popular-stats">
<div>์กฐํ์: ${item.count}ํ</div>
<div>ํ๊ท ์ ์: ${Math.round(item.avg_score)}์ </div>
</div>
</div>
`).join('');
})
.catch(err => {
console.error('์ธ๊ธฐ ์กฐํฉ ๋ก๋ฉ ์คํจ:', err);
document.getElementById('popular-list').innerHTML = '<div class="error">๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ฌ ์ ์์ต๋๋ค.</div>';
});
}
// ํ์ด์ง ๋ก๋ ์ ์คํ
document.addEventListener('DOMContentLoaded', loadPopularCombinations);
```
6. "๋ค์ ํ๊ธฐ" ๋ฒํผ ํด๋ฆญ ์ ์ธ๊ธฐ ์กฐํฉ ์๋ก๊ณ ์นจ ์ถ๊ฐ:
```javascript
document.getElementById('retry-btn').addEventListener('click', function() {
// ... (๊ธฐ์กด ์ฝ๋)
loadPopularCombinations(); // ์ถ๊ฐ
});
```
**์์ ๊ฒฐ๊ณผ**: ๋ ์กฐํฉ์ ์กฐํํ ๋๋ง๋ค SQLite DB์ ๊ธฐ๋ก์ด ์ ์ฅ๋๊ณ , ํ์ด์ง ํ๋จ์ ๊ฐ์ฅ ๋ง์ด ์กฐํ๋ ์กฐํฉ TOP 3๊ฐ ์ค์๊ฐ์ผ๋ก ํ์๋ฉ๋๋ค. ์ธ๊ธฐ ์กฐํฉ ์น์
์๋ ์์, ๋ ์ด๋ชจ์ง, ์กฐํ์, ํ๊ท ์ ์๊ฐ ํ์๋ฉ๋๋ค. DB ํ์ผ์ `G5_DATA_PATH/modules/zodiac_compatibility.sqlite` ๊ฒฝ๋ก์ ์๋ ์์ฑ๋ฉ๋๋ค.
๊ธฐ๋ ๊ฒฐ๊ณผ: SQLite DB ์ฐ๋์ด ์ ์ ์๋ํ์ฌ ์กฐํ ๊ธฐ๋ก์ด ์ ์ฅ๋๊ณ , ์ธ๊ธฐ ์กฐํฉ TOP 3๊ฐ ํ์ด์ง ํ๋จ์ ๋์ ์ผ๋ก ํ์๋ฉ๋๋ค. AJAX ํต์ ์ผ๋ก ํ์ด์ง ์๋ก๊ณ ์นจ ์์ด ๋ฐ์ดํฐ๊ฐ ์ ์ฅ/์กฐํ๋ฉ๋๋ค. prepared statement๋ก SQL ์ธ์ ์ ์ด ๋ฐฉ์ง๋๊ณ , ์๋ฌ ํธ๋ค๋ง์ด ์ ์ ํ ๊ตฌํ๋์ด ์์ต๋๋ค. ๋คํฌ๋ชจ๋ ํ ํฐ์ด ์ ์ฉ๋์ด ๋ฐ์/์ด๋์ด ํ ๋ง ๋ชจ๋์์ ๊ฐ๋ ์ฑ์ด ๋ณด์ฅ๋ฉ๋๋ค.
์ ์ถ๋ ์ํ (0)
๋ก๊ทธ์ธ ํ ์ ์ถ์์ง ์ ์ถ๋ ์ํ์ด ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋์ ์๊ฐ ๋์ด๋ณด์ธ์!