๐ŸŽฏ g5se ์˜ค๋Š˜์˜ ๋ช…์–ธ ์ƒ์„ฑ๊ธฐ - ๋žœ๋ค ์˜๊ฐ & ๊ณต์œ ํ•˜๊ธฐ - ๋ฐ”์ด๋ธŒ์ฝ”๋”ฉ ๋ ˆ์‹œํ”ผ
์ž…๋ฌธ PHP g5se AI: Claude

๐ŸŽฏ g5se ์˜ค๋Š˜์˜ ๋ช…์–ธ ์ƒ์„ฑ๊ธฐ - ๋žœ๋ค ์˜๊ฐ & ๊ณต์œ ํ•˜๊ธฐ

๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ช… g5se-daily-quote-generator
์กฐํšŒ์ˆ˜ 79 0๊ฑด ์ œ์ถœ

๋ฌธ์ œ ์„ค๋ช…

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

๋งค์ผ ์ƒˆ๋กœ์šด ์˜๊ฐ์„ ์ฃผ๋Š” ๋ช…์–ธ์„ ๋žœ๋ค์œผ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ์›น ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ์ƒˆ๋กœ์šด ๋ช…์–ธ์ด ๋‚˜ํƒ€๋‚˜๊ณ , ๋งˆ์Œ์— ๋“œ๋Š” ๋ช…์–ธ์€ SNS์— ๊ณต์œ ํ•˜๊ฑฐ๋‚˜ ์ด๋ฏธ์ง€๋กœ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ˆ„๋ณด๋“œ5 SE์˜ modern shell์„ ํ™œ์šฉํ•˜์—ฌ ๋‹คํฌ๋ชจ๋“œ๋ฅผ ์ง€์›ํ•˜๊ณ , ๋””์ž์ธ ํ† ํฐ์œผ๋กœ ์ผ๊ด€๋œ UI๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. DB ์—†์ด PHP ๋ฐฐ์—ด๋กœ ๋ช…์–ธ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋ฉฐ, JavaScript๋กœ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒํ•œ UI๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

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

  1. ๋žœ๋ค ๋ช…์–ธ ํ‘œ์‹œ

    • ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ ๋žœ๋ค ๋ช…์–ธ 1๊ฐœ ํ‘œ์‹œ
    • ๋ช…์–ธ ๋ณธ๋ฌธ + ์ €์ž๋ช… ํ‘œ์‹œ
    • ๋ถ€๋“œ๋Ÿฌ์šด fade-in ์• ๋‹ˆ๋ฉ”์ด์…˜
  2. ์ƒˆ ๋ช…์–ธ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

    • "์ƒˆ ๋ช…์–ธ ๋ณด๊ธฐ" ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ๋‹ค๋ฅธ ๋ช…์–ธ์œผ๋กœ ๊ต์ฒด
    • AJAX ์—†์ด JavaScript๋กœ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ๋žœ๋ค ์„ ํƒ
    • ์ด์ „ ๋ช…์–ธ๊ณผ ์ค‘๋ณต๋˜์ง€ ์•Š๋„๋ก ์ฒ˜๋ฆฌ
  3. ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ๋ถ„๋ฅ˜

    • ๋ช…์–ธ ์นดํ…Œ๊ณ ๋ฆฌ ํ‘œ์‹œ (๋™๊ธฐ๋ถ€์—ฌ, ์„ฑ๊ณต, ํ–‰๋ณต, ์ง€ํ˜œ, ๋„์ „ ๋“ฑ)
    • ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ์ƒ‰์ƒ ๋ฐฐ์ง€๋กœ ์‹œ๊ฐ์  ๊ตฌ๋ถ„
  4. ๊ณต์œ  ๊ธฐ๋Šฅ

    • ํŠธ์œ„ํ„ฐ/ํŽ˜์ด์Šค๋ถ ๊ณต์œ  ๋ฒ„ํŠผ
    • ํด๋ฆฝ๋ณด๋“œ ๋ณต์‚ฌ ๋ฒ„ํŠผ (๋ณต์‚ฌ ์™„๋ฃŒ ํ† ์ŠคํŠธ ์•Œ๋ฆผ)
  5. ๋ฐ˜์‘ํ˜• ๋””์ž์ธ

    • ๋ชจ๋ฐ”์ผ/ํƒœ๋ธ”๋ฆฟ/๋ฐ์Šคํฌํ†ฑ ๋Œ€์‘
    • modern shell์˜ ๋””์ž์ธ ํ† ํฐ์œผ๋กœ ๋‹คํฌ๋ชจ๋“œ ์ž๋™ ๋Œ€์‘

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

  • ๋ช…์–ธ ์ฆ๊ฒจ์ฐพ๊ธฐ (localStorage ํ™œ์šฉ)
  • ๋ช…์–ธ์„ ์ด๋ฏธ์ง€ ์นด๋“œ๋กœ ๋‹ค์šด๋กœ๋“œ (html2canvas ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ)
  • ์นดํ…Œ๊ณ ๋ฆฌ ํ•„ํ„ฐ๋ง (ํŠน์ • ์นดํ…Œ๊ณ ๋ฆฌ๋งŒ ๋ณด๊ธฐ)
  • ๋ช…์–ธ ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ
  • ์˜ค๋Š˜์˜ ๋ช…์–ธ ์ž๋™ ์ถ”์ฒœ (๋‚ ์งœ ๊ธฐ๋ฐ˜ ์‹œ๋“œ)

๐ŸŽจ ๋””์ž์ธ ์š”๊ตฌ์‚ฌํ•ญ

  • ๋ช…์–ธ ์นด๋“œ๋Š” ์ค‘์•™ ์ •๋ ฌ, ํฐ ํƒ€์ดํฌ๊ทธ๋ž˜ํ”ผ
  • ์นดํ…Œ๊ณ ๋ฆฌ ๋ฐฐ์ง€๋Š” ๋ช…์–ธ ์ƒ๋‹จ์— ํ‘œ์‹œ
  • ๋ฒ„ํŠผ์€ modern shell์˜ primary ์ƒ‰์ƒ ํ™œ์šฉ
  • ๋ช…์–ธ ๊ต์ฒด ์‹œ fade-out โ†’ fade-in ์• ๋‹ˆ๋ฉ”์ด์…˜
  • ์ €์ž๋ช…์€ ์ดํƒค๋ฆญ์ฒด๋กœ ์šฐ์ธก ํ•˜๋‹จ ์ •๋ ฌ

์ œ์•ฝ ์กฐ๊ฑด

  • โ€ข ๊ทธ๋ˆ„๋ณด๋“œ5 SE ์œ„์—์„œ ๋™์ž‘ (Laravel/Composer/npm ์‚ฌ์šฉ ๊ธˆ์ง€)
  • โ€ข ํด๋” ๊ธฐ๋ฐ˜ ์ž๋™ ๋ผ์šฐํŒ…: app/modules/daily-quote/index.php โ†’ /daily-quote
  • โ€ข 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 ์‚ฌ์šฉ ๊ธˆ์ง€ (PHP ๋ฐฐ์—ด๋กœ ๋ช…์–ธ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ)
  • โ€ข ์™ธ๋ถ€ CDN ์ตœ์†Œํ™” (ํ•„์š”์‹œ html2canvas๋งŒ ํ—ˆ์šฉ)
  • โ€ข ์ˆœ์ˆ˜ JavaScript (jQuery ๊ธˆ์ง€)
  • โ€ข htmlspecialchars()๋กœ XSS ๋ฐฉ์ง€

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

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

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

**Step 1: ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ & modern shell ๊ณจ๊ฒฉ ์ƒ์„ฑ**

1. `app/modules/daily-quote/index.php` ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜์„ธ์š”.

2. ๋‹ค์Œ modern shell ๊ณจ๊ฒฉ์„ ์ž‘์„ฑํ•˜์„ธ์š”:

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

// ๋ช…์–ธ ๋ฐ์ดํ„ฐ ๋ฐฐ์—ด (์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ๋ถ„๋ฅ˜)
$quotes = [
    ['text' => '์„ฑ๊ณต์€ ๋งค์ผ ๋ฐ˜๋ณตํ•œ ์ž‘์€ ๋…ธ๋ ฅ๋“ค์˜ ํ•ฉ์ด๋‹ค.', 'author' => '๋กœ๋ฒ„ํŠธ ์ฝœ๋ฆฌ์–ด', 'category' => '์„ฑ๊ณต'],
    ['text' => 'ํ–‰๋ณต์€ ์Šต๊ด€์ด๋‹ค. ๊ทธ๊ฒƒ์„ ๋ชธ์— ์ง€๋‹ˆ๋ผ.', 'author' => 'ํ—ˆ๋ฒ„๋“œ', 'category' => 'ํ–‰๋ณต'],
    ['text' => '๋‹น์‹ ์ด ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ๋ฏฟ๋“  ํ•  ์ˆ˜ ์—†๋‹ค๊ณ  ๋ฏฟ๋“  ๋ฏฟ๋Š” ๋Œ€๋กœ ๋  ๊ฒƒ์ด๋‹ค.', 'author' => 'ํ—จ๋ฆฌ ํฌ๋“œ', 'category' => '๋™๊ธฐ๋ถ€์—ฌ'],
    ['text' => '์œ„๋Œ€ํ•œ ์ผ์„ ํ•˜๋ ค๋ฉด ํ–‰๋™ํ•˜๋Š” ๊ฒƒ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๊ฟˆ์„ ๊พธ๋Š” ๊ฒƒ๋„ ํ•„์š”ํ•˜๋‹ค.', 'author' => '์•„๋‚˜ํ†จ ํ”„๋ž‘์Šค', 'category' => '๋„์ „'],
    ['text' => '์ง€ํ˜œ๋Š” ๊ฒฝํ—˜์˜ ๋”ธ์ด๋‹ค.', 'author' => '๋ ˆ์˜ค๋‚˜๋ฅด๋„ ๋‹ค ๋นˆ์น˜', 'category' => '์ง€ํ˜œ'],
    ['text' => '์˜ค๋Š˜ ํ•  ์ˆ˜ ์žˆ๋Š” ์ผ์„ ๋‚ด์ผ๋กœ ๋ฏธ๋ฃจ์ง€ ๋งˆ๋ผ.', 'author' => '๋ฒค์ž๋ฏผ ํ”„๋žญํด๋ฆฐ', 'category' => '๋™๊ธฐ๋ถ€์—ฌ'],
    ['text' => '์‹คํŒจ๋Š” ์„ฑ๊ณต์˜ ์–ด๋จธ๋‹ˆ๋‹ค.', 'author' => 'ํ† ๋งˆ์Šค ์—๋””์Šจ', 'category' => '์„ฑ๊ณต'],
    ['text' => 'ํ–‰๋ณต์˜ ๋ฌธ์ด ํ•˜๋‚˜ ๋‹ซํžˆ๋ฉด ๋‹ค๋ฅธ ๋ฌธ์ด ์—ด๋ฆฐ๋‹ค.', 'author' => 'ํ—ฌ๋ Œ ์ผˆ๋Ÿฌ', 'category' => 'ํ–‰๋ณต'],
    ['text' => '๋„์ „์€ ์ธ์ƒ์„ ํฅ๋ฏธ๋กญ๊ฒŒ ๋งŒ๋“ค๋ฉฐ, ๋„์ „์˜ ๊ทน๋ณต์ด ์ธ์ƒ์„ ์˜๋ฏธ์žˆ๊ฒŒ ํ•œ๋‹ค.', 'author' => '์กฐ์Šˆ์•„ ๋งˆ๋ฆฐ', 'category' => '๋„์ „'],
    ['text' => '๋ฐฐ์›€์—๋Š” ๋์ด ์—†๋‹ค.', 'author' => '๊ณต์ž', 'category' => '์ง€ํ˜œ']
];

// JSON ์ธ์ฝ”๋”ฉ (JavaScript์—์„œ ์‚ฌ์šฉ)
$quotesJson = json_encode($quotes, JSON_UNESCAPED_UNICODE);

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="quote-generator">
            <h1 class="page-title">๐ŸŽฏ ์˜ค๋Š˜์˜ ๋ช…์–ธ</h1>
            <p class="page-subtitle">๋งค์ผ ์ƒˆ๋กœ์šด ์˜๊ฐ์„ ๋ฐ›์•„๋ณด์„ธ์š”</p>
            
            <div class="quote-card" id="quoteCard">
                <div class="quote-category" id="quoteCategory"></div>
                <p class="quote-text" id="quoteText"></p>
                <p class="quote-author" id="quoteAuthor"></p>
            </div>
            
            <div class="action-buttons">
                <button class="btn btn-primary" id="newQuoteBtn">
                    โœจ ์ƒˆ ๋ช…์–ธ ๋ณด๊ธฐ
                </button>
                <button class="btn btn-secondary" id="copyBtn">
                    ๐Ÿ“‹ ๋ณต์‚ฌํ•˜๊ธฐ
                </button>
            </div>
            
            <div class="share-buttons">
                <a href="#" class="share-btn" id="twitterShare" target="_blank">
                    ๐Ÿฆ ํŠธ์œ„ํ„ฐ
                </a>
                <a href="#" class="share-btn" id="facebookShare" target="_blank">
                    ๐Ÿ“˜ ํŽ˜์ด์Šค๋ถ
                </a>
            </div>
        </div>
        
        <div class="toast" id="toast">๋ช…์–ธ์ด ๋ณต์‚ฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!</div>
    </main>
    <?php require G5_THEME_PATH.'/modern/_footer.inc.php'; ?>
</div>

<script>
const quotes = <?php echo $quotesJson; ?>;
let currentQuoteIndex = -1;

// ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ ๋žœ๋ค ๋ช…์–ธ ํ‘œ์‹œ
window.addEventListener('DOMContentLoaded', () => {
    showRandomQuote();
});
</script>

<?php include_once('./tail.sub.php');
```

3. ํŒŒ์ผ์„ ์ €์žฅํ•˜๊ณ  ๋ธŒ๋ผ์šฐ์ €์—์„œ `/daily-quote` ๊ฒฝ๋กœ๋กœ ์ ‘์†ํ•˜์—ฌ ํŽ˜์ด์ง€๊ฐ€ ๋กœ๋“œ๋˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

**์˜ˆ์ƒ ๊ฒฐ๊ณผ:**
- modern shell์ด ์ ์šฉ๋œ ํŽ˜์ด์ง€ ๊ณจ๊ฒฉ์ด ๋ณด์ž„
- ๋ช…์–ธ ์นด๋“œ ์˜์—ญ๊ณผ ๋ฒ„ํŠผ๋“ค์ด ํ‘œ์‹œ๋จ (์•„์ง ์Šคํƒ€์ผ ์—†์Œ)
- ์ฝ˜์†”์— JavaScript ์—๋Ÿฌ ์—†์Œ

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: modern shell์ด ์ ์šฉ๋œ ๊ธฐ๋ณธ ํŽ˜์ด์ง€ ๊ตฌ์กฐ๊ฐ€ ์ƒ์„ฑ๋˜๊ณ , PHP ๋ฐฐ์—ด๋กœ ๋ช…์–ธ ๋ฐ์ดํ„ฐ๊ฐ€ ์ •์˜๋ฉ๋‹ˆ๋‹ค. JavaScript์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก JSON์œผ๋กœ ์ธ์ฝ”๋”ฉ๋œ ๋ช…์–ธ ๋ฐ์ดํ„ฐ๊ฐ€ ์ค€๋น„๋ฉ๋‹ˆ๋‹ค.

2
Step 2
**Step 2: ๋ช…์–ธ ํ‘œ์‹œ & ๋žœ๋ค ์„ ํƒ ๋กœ์ง ๊ตฌํ˜„**

`app/modules/daily-quote/index.php`์˜ `<script>` ๋ธ”๋ก์— ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”:

```javascript
// ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ์ƒ‰์ƒ ๋งคํ•‘
const categoryColors = {
    '๋™๊ธฐ๋ถ€์—ฌ': '#FF6B6B',
    '์„ฑ๊ณต': '#4ECDC4',
    'ํ–‰๋ณต': '#FFD93D',
    '์ง€ํ˜œ': '#95E1D3',
    '๋„์ „': '#A8E6CF'
};

// ๋žœ๋ค ๋ช…์–ธ ํ‘œ์‹œ ํ•จ์ˆ˜
function showRandomQuote() {
    let newIndex;
    do {
        newIndex = Math.floor(Math.random() * quotes.length);
    } while (newIndex === currentQuoteIndex && quotes.length > 1);
    
    currentQuoteIndex = newIndex;
    const quote = quotes[currentQuoteIndex];
    
    // ํŽ˜์ด๋“œ ์•„์›ƒ
    const card = document.getElementById('quoteCard');
    card.style.opacity = '0';
    
    setTimeout(() => {
        // ๋‚ด์šฉ ์—…๋ฐ์ดํŠธ
        document.getElementById('quoteCategory').textContent = quote.category;
        document.getElementById('quoteCategory').style.backgroundColor = categoryColors[quote.category] || '#999';
        document.getElementById('quoteText').textContent = `"${quote.text}"`;
        document.getElementById('quoteAuthor').textContent = `โ€” ${quote.author}`;
        
        // SNS ๊ณต์œ  ๋งํฌ ์—…๋ฐ์ดํŠธ
        updateShareLinks(quote);
        
        // ํŽ˜์ด๋“œ ์ธ
        card.style.opacity = '1';
    }, 300);
}

// SNS ๊ณต์œ  ๋งํฌ ์—…๋ฐ์ดํŠธ
function updateShareLinks(quote) {
    const text = encodeURIComponent(`"${quote.text}" โ€” ${quote.author}`);
    const url = encodeURIComponent(window.location.href);
    
    document.getElementById('twitterShare').href = 
        `https://twitter.com/intent/tweet?text=${text}&url=${url}`;
    document.getElementById('facebookShare').href = 
        `https://www.facebook.com/sharer/sharer.php?u=${url}&quote=${text}`;
}

// ์ƒˆ ๋ช…์–ธ ๋ฒ„ํŠผ ํด๋ฆญ
document.getElementById('newQuoteBtn').addEventListener('click', () => {
    showRandomQuote();
});

// ๋ณต์‚ฌ ๋ฒ„ํŠผ ํด๋ฆญ
document.getElementById('copyBtn').addEventListener('click', () => {
    const quote = quotes[currentQuoteIndex];
    const text = `"${quote.text}" โ€” ${quote.author}`;
    
    navigator.clipboard.writeText(text).then(() => {
        showToast();
    }).catch(err => {
        console.error('๋ณต์‚ฌ ์‹คํŒจ:', err);
    });
});

// ํ† ์ŠคํŠธ ์•Œ๋ฆผ ํ‘œ์‹œ
function showToast() {
    const toast = document.getElementById('toast');
    toast.classList.add('show');
    setTimeout(() => {
        toast.classList.remove('show');
    }, 2000);
}
```

**์˜ˆ์ƒ ๊ฒฐ๊ณผ:**
- ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ ๋žœ๋ค ๋ช…์–ธ์ด ํ‘œ์‹œ๋จ
- "์ƒˆ ๋ช…์–ธ ๋ณด๊ธฐ" ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ๋‹ค๋ฅธ ๋ช…์–ธ์œผ๋กœ ๊ต์ฒด๋จ (๋ถ€๋“œ๋Ÿฌ์šด ํŽ˜์ด๋“œ ์• ๋‹ˆ๋ฉ”์ด์…˜)
- ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„๋กœ ๋‹ค๋ฅธ ์ƒ‰์ƒ์˜ ๋ฐฐ์ง€๊ฐ€ ํ‘œ์‹œ๋จ
- ๋ณต์‚ฌ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ๋ช…์–ธ์ด ํด๋ฆฝ๋ณด๋“œ์— ๋ณต์‚ฌ๋˜๊ณ  ํ† ์ŠคํŠธ ์•Œ๋ฆผ์ด ๋œธ
- SNS ๊ณต์œ  ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ํ•ด๋‹น ํ”Œ๋žซํผ ๊ณต์œ  ์ฐฝ์ด ์—ด๋ฆผ

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: JavaScript๋กœ ๋žœ๋ค ๋ช…์–ธ ์„ ํƒ ๋กœ์ง์ด ๊ตฌํ˜„๋˜๊ณ , ํŽ˜์ด๋“œ ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ ํ•จ๊ป˜ ๋ช…์–ธ์ด ๊ต์ฒด๋ฉ๋‹ˆ๋‹ค. ํด๋ฆฝ๋ณด๋“œ ๋ณต์‚ฌ์™€ SNS ๊ณต์œ  ๊ธฐ๋Šฅ์ด ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

3
Step 3
**Step 3: CSS ์Šคํƒ€์ผ๋ง & ๋ฐ˜์‘ํ˜• ๋””์ž์ธ**

`app/modules/daily-quote/index.php`์˜ `</main>` ํƒœ๊ทธ ์ง์ „์— ๋‹ค์Œ `<style>` ๋ธ”๋ก์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”:

```html
<style>
.quote-generator {
    max-width: 700px;
    margin: 0 auto;
    text-align: center;
}

.page-title {
    font-size: 2.5rem;
    font-weight: 700;
    color: var(--m-text);
    margin-bottom: 8px;
}

.page-subtitle {
    font-size: 1.1rem;
    color: var(--m-text-muted);
    margin-bottom: 40px;
}

.quote-card {
    background: var(--m-surface);
    border-radius: var(--m-radius-lg);
    box-shadow: var(--m-shadow);
    padding: 48px 32px;
    margin-bottom: 32px;
    transition: opacity 0.3s ease;
    min-height: 280px;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.quote-category {
    display: inline-block;
    padding: 6px 16px;
    border-radius: 20px;
    font-size: 0.85rem;
    font-weight: 600;
    color: white;
    margin-bottom: 24px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.quote-text {
    font-size: 1.6rem;
    line-height: 1.6;
    color: var(--m-text);
    margin-bottom: 20px;
    font-weight: 500;
}

.quote-author {
    font-size: 1.1rem;
    color: var(--m-text-muted);
    font-style: italic;
    text-align: right;
}

.action-buttons {
    display: flex;
    gap: 12px;
    justify-content: center;
    margin-bottom: 20px;
}

.btn {
    padding: 12px 28px;
    font-size: 1rem;
    font-weight: 600;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    transition: all 0.2s;
}

.btn-primary {
    background: var(--m-primary);
    color: white;
}

.btn-primary:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

.btn-secondary {
    background: var(--m-surface);
    color: var(--m-text);
    border: 2px solid var(--m-border, #e0e0e0);
}

.btn-secondary:hover {
    background: var(--m-hover, #f5f5f5);
}

.share-buttons {
    display: flex;
    gap: 12px;
    justify-content: center;
}

.share-btn {
    padding: 10px 20px;
    background: var(--m-surface);
    color: var(--m-text);
    text-decoration: none;
    border-radius: 8px;
    font-size: 0.95rem;
    font-weight: 500;
    transition: all 0.2s;
    border: 1px solid var(--m-border, #e0e0e0);
}

.share-btn:hover {
    background: var(--m-hover, #f5f5f5);
    transform: translateY(-2px);
}

.toast {
    position: fixed;
    bottom: 30px;
    left: 50%;
    transform: translateX(-50%) translateY(100px);
    background: #333;
    color: white;
    padding: 14px 24px;
    border-radius: 8px;
    font-size: 0.95rem;
    opacity: 0;
    transition: all 0.3s;
    z-index: 1000;
}

.toast.show {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}

@media (max-width: 768px) {
    .page-title {
        font-size: 2rem;
    }
    
    .quote-card {
        padding: 32px 20px;
        min-height: 240px;
    }
    
    .quote-text {
        font-size: 1.3rem;
    }
    
    .action-buttons {
        flex-direction: column;
    }
    
    .btn {
        width: 100%;
    }
    
    .share-buttons {
        flex-direction: column;
    }
    
    .share-btn {
        width: 100%;
    }
}
</style>
```

**์˜ˆ์ƒ ๊ฒฐ๊ณผ:**
- ๋ช…์–ธ ์นด๋“œ๊ฐ€ ์ค‘์•™ ์ •๋ ฌ๋˜๊ณ  ํฐ ํƒ€์ดํฌ๊ทธ๋ž˜ํ”ผ๋กœ ํ‘œ์‹œ๋จ
- ์นดํ…Œ๊ณ ๋ฆฌ ๋ฐฐ์ง€๊ฐ€ ๋ช…์–ธ ์ƒ๋‹จ์— ์ƒ‰์ƒ๋ณ„๋กœ ํ‘œ์‹œ๋จ
- ๋ฒ„ํŠผ๋“ค์ด ๊น”๋”ํ•˜๊ฒŒ ์ •๋ ฌ๋˜๊ณ  ํ˜ธ๋ฒ„ ํšจ๊ณผ๊ฐ€ ์ž‘๋™ํ•จ
- ๋ชจ๋ฐ”์ผ์—์„œ ๋ฒ„ํŠผ๋“ค์ด ์„ธ๋กœ๋กœ ๋ฐฐ์น˜๋จ
- ๋‹คํฌ๋ชจ๋“œ ์ „ํ™˜ ์‹œ ์ƒ‰์ƒ์ด ์ž๋™์œผ๋กœ ๋ณ€๊ฒฝ๋จ (๋””์ž์ธ ํ† ํฐ ๋•๋ถ„)
- ํ† ์ŠคํŠธ ์•Œ๋ฆผ์ด ํ•˜๋‹จ์—์„œ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๋‚˜ํƒ€๋‚จ

**์ตœ์ข… ํ™•์ธ ์‚ฌํ•ญ:**
1. `/daily-quote` ๊ฒฝ๋กœ๋กœ ์ ‘์†
2. ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ ๋žœ๋ค ๋ช…์–ธ ํ‘œ์‹œ ํ™•์ธ
3. "์ƒˆ ๋ช…์–ธ ๋ณด๊ธฐ" ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ๋‹ค๋ฅธ ๋ช…์–ธ์œผ๋กœ ๊ต์ฒด๋˜๋Š”์ง€ ํ™•์ธ
4. ๋ณต์‚ฌ ๋ฒ„ํŠผ ํด๋ฆญ ํ›„ Ctrl+V๋กœ ๋ถ™์—ฌ๋„ฃ๊ธฐ ํ…Œ์ŠคํŠธ
5. SNS ๊ณต์œ  ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์ƒˆ ์ฐฝ์—์„œ ๊ณต์œ  ํŽ˜์ด์ง€๊ฐ€ ์—ด๋ฆฌ๋Š”์ง€ ํ™•์ธ
6. ๋‹คํฌ๋ชจ๋“œ ํ† ๊ธ€ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์ƒ‰์ƒ์ด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ณ€๊ฒฝ๋˜๋Š”์ง€ ํ™•์ธ
7. ๋ชจ๋ฐ”์ผ ํ™”๋ฉด ํฌ๊ธฐ๋กœ ์ค„์˜€์„ ๋•Œ ๋ ˆ์ด์•„์›ƒ์ด ๋ฐ˜์‘ํ˜•์œผ๋กœ ๋ณ€๊ฒฝ๋˜๋Š”์ง€ ํ™•์ธ

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ์™„์„ฑ๋œ ๋ช…์–ธ ์ƒ์„ฑ๊ธฐ ํŽ˜์ด์ง€๊ฐ€ modern shell๊ณผ ํ†ตํ•ฉ๋˜์–ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ๋””์ž์ธ ํ† ํฐ ๋•๋ถ„์— ๋‹คํฌ๋ชจ๋“œ๊ฐ€ ์ž๋™์œผ๋กœ ๋Œ€์‘๋˜๊ณ , ๋ฐ˜์‘ํ˜• ๋ ˆ์ด์•„์›ƒ์ด ๋ชจ๋ฐ”์ผ์—์„œ๋„ ์ž˜ ๋ณด์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ์ธํ„ฐ๋ž™์…˜(๋žœ๋ค ์„ ํƒ, ๋ณต์‚ฌ, ๊ณต์œ )์ด ๋งค๋„๋Ÿฝ๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

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

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

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

๋Œ“๊ธ€ (0)

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