** ๐Ÿ“ŒDOM(Document Object Model) ์ด๋ž€?**

HTML ๋ฌธ์„œ๋ฅผ ๊ฐ์ฒด๋กœ ํ‘œํ˜„ํ•œ ๊ตฌ์กฐ โ†’ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ํŠธ๋ฆฌ

  • HTML์„ ๊ฐ์ฒดํ™” โ†’ JS๋กœ ์ง์ ‘ ์กฐ์ž‘ ๊ฐ€๋Šฅ
  • ํŠธ๋ฆฌ(Tree) ๊ตฌ์กฐ๋กœ ๊ณ„์ธตํ™” โ†’ ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„

2๏ธโƒฃ ๐ŸŒณ DOM ํŠธ๋ฆฌ ๊ตฌ์กฐ (์‹œ๊ฐํ™”)

<body>
  <div id="container">
    <p>Hello</p>
    <ul>
      <li>Item 1</li>
      <li>Item 2</li>
    </ul>
  </div>
</body>
php-template
๋ณต์‚ฌํŽธ์ง‘
Document
 โ””โ”€โ”€ <html>
      โ””โ”€โ”€ <body>
           โ””โ”€โ”€ <div id="container">
                โ”œโ”€โ”€ <p>
                โ””โ”€โ”€ <ul>
                     โ”œโ”€โ”€ <li>
                     โ””โ”€โ”€ <li>

3๏ธโƒฃ ๐Ÿ” DOM ํƒ์ƒ‰ (๊ธฐ์ดˆ + ์‹ฌํ™”)

๐Ÿ“‹ ๊ธฐ๋ณธ ํƒ์ƒ‰ ๋ฉ”์„œ๋“œ

๋ฉ”์„œ๋“œ ์„ค๋ช…
getElementById id๋กœ ํƒ์ƒ‰
getElementsByClassName class๋กœ ํƒ์ƒ‰ (HTMLCollection)
querySelector CSS ์„ ํƒ์ž ๊ธฐ๋ฐ˜ ํƒ์ƒ‰ (์ตœ์ดˆ ํ•˜๋‚˜)
querySelectorAll CSS ์„ ํƒ์ž ๊ธฐ๋ฐ˜ ๋ชจ๋“  ์š”์†Œ (NodeList)

โœ… ์‹ฌํ™” ํƒ์ƒ‰ ๋ฉ”์„œ๋“œ

๋ฉ”์„œ๋“œ ์„ค๋ช…
parentNode / parentElement ๋ถ€๋ชจ ์š”์†Œ ํƒ์ƒ‰
children ์ž์‹ ์š”์†Œ(HTMLCollection)
firstElementChild / lastElementChild ์ฒซ/๋งˆ์ง€๋ง‰ ์ž์‹
nextElementSibling / previousElementSibling ํ˜•์ œ ๋…ธ๋“œ ํƒ์ƒ‰

๐ŸŽฏ ์‹ฌํ™” ์˜ˆ์ œ

javascript
๋ณต์‚ฌํŽธ์ง‘
const container = document.getElementById('container');
console.log(container.children); // ul, p
console.log(container.parentElement); // body
console.log(container.firstElementChild); // <p

4๏ธโƒฃ ๐Ÿ› ๏ธ DOM ์ˆ˜์ •/์ถ”๊ฐ€/์‚ญ์ œ ์‹ฌํ™” ํŒจํ„ด

์ž‘์—… ๋ฉ”์„œ๋“œ
๋‚ด์šฉ ์ˆ˜์ • element.textContent, innerHTML
์†์„ฑ ์ˆ˜์ • setAttribute, classList
์š”์†Œ ์ถ”๊ฐ€ appendChild, insertBefore, insertAdjacentHTML
์š”์†Œ ์‚ญ์ œ removeChild, remove()

๐Ÿš€ ์‹ค๋ฌด ์ตœ์ ํ™” ํŒจํ„ด

// ๊ธฐ์กด ์š”์†Œ๋ฅผ ์ œ๊ฑฐ ํ›„ ์žฌ์‚ฝ์ž… โ†’ ์„ฑ๋Šฅ ์ €ํ•˜
document.body.innerHTML += '<div>New</div>'; // โŒ

// DocumentFragment ์‚ฌ์šฉ โ†’ ์„ฑ๋Šฅ ํ–ฅ์ƒ
const frag = document.createDocumentFragment();
const newDiv = document.createElement('div');
newDiv.textContent = 'New';
frag.appendChild(newDiv);
document.body.appendChild(frag); // โœ…

5๏ธโƒฃ ๐ŸŽฏ ์ด๋ฒคํŠธ(Event) ๊ธฐ๋ณธ

์šฉ์–ด ์„ค๋ช…
Event ์‚ฌ์šฉ์ž ์ž…๋ ฅ or ์‹œ์Šคํ…œ ๋ฐœ์ƒ ํ–‰๋™ (ํด๋ฆญ, ์Šคํฌ๋กค ๋“ฑ)
Event Listener ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ ์‹คํ–‰ํ•  ํ•จ์ˆ˜ ๋“ฑ๋ก

โœ… ๊ธฐ์ดˆ ์˜ˆ์ œ

const btn = document.querySelector('button');
btn.addEventListener('click', () => console.log('Button clicked!'));

6๏ธโƒฃ ๐Ÿ”ฅ ์ด๋ฒคํŠธ ๋ฒ„๋ธ”๋ง & ์บก์ฒ˜๋ง ์‹ฌํ™”

๐Ÿ“Œ ์ „ํŒŒ ๊ณผ์ •:

1. Capturing Phase (์ตœ์ƒ์œ„ โ†’ ํƒ€๊ฒŸ)
2. Target Phase (์ด๋ฒคํŠธ ๋ฐœ์ƒ)
3. Bubbling Phase (ํƒ€๊ฒŸ โ†’ ์ตœ์ƒ์œ„)

๐Ÿš€ ๊ฐ€์‹œ์  ๋„์‹

<body>
  <div>
    <button>Click</button>
  </div>
</body>

Click โ†’
1. body (์บก์ฒ˜๋ง)
2. div (์บก์ฒ˜๋ง)
3. button (ํƒ€๊ฒŸ)
4. div (๋ฒ„๋ธ”๋ง)
5. body (๋ฒ„๋ธ”๋ง)

โœ… ์บก์ฒ˜๋ง ์„ค์ •

document.body.addEventListener('click', () => console.log('Capturing'), true);

๋ฉด์ ‘ ํฌ์ธํŠธ:

โ€œ๋ฒ„๋ธ”๋ง๊ณผ ์บก์ฒ˜๋ง์˜ ์ฐจ์ด์™€, ์‹ค๋ฌด์—์„œ ์บก์ฒ˜๋ง์€ ์–ธ์ œ ์‚ฌ์šฉํ•˜๋Š”๊ฐ€?โ€

โ†’ ๊ธฐ๋ณธ์€ ๋ฒ„๋ธ”๋ง, ํŠน์ˆ˜ํ•œ ๊ฒฝ์šฐ ์ „ํŒŒ ์ˆœ์„œ ์ œ์–ด ์œ„ํ•ด ์บก์ฒ˜๋ง


7๏ธโƒฃ ๐Ÿšซ stopPropagation & preventDefault ์‹ฌํ™”

๋ฉ”์„œ๋“œ ์—ญํ• 
stopPropagation ์ด๋ฒคํŠธ ์ „ํŒŒ ์ฐจ๋‹จ (์ƒ์œ„๋กœ ์•ˆ ์˜ฌ๋ผ๊ฐ)
stopImmediatePropagation ํ˜„์žฌ ์š”์†Œ ๋‚ด ๋ชจ๋“  ํ•ธ๋“ค๋Ÿฌ ์‹คํ–‰ ์ค‘๋‹จ
preventDefault ๊ธฐ๋ณธ ๋™์ž‘(๋งํฌ ์ด๋™, ํผ ์ œ์ถœ ๋“ฑ) ์ฐจ๋‹จ

โœ… ์‹ฌํ™” ์˜ˆ์ œ

const link = document.querySelector('a');

link.addEventListener('click', (e) => {
  e.preventDefault();      // ๋งํฌ ์ด๋™ X
  e.stopPropagation();     // ์ƒ์œ„ ์ „ํŒŒ X
});

8๏ธโƒฃ ๐Ÿง  ์ด๋ฒคํŠธ ์œ„์ž„ ์‹ฌํ™” ์‹ค๋ฌด ํŒจํ„ด

๋ถ€๋ชจ ์š”์†Œ์— ์ด๋ฒคํŠธ๋ฅผ ๊ฑธ๊ณ , ์ž์‹์˜ ์ด๋ฒคํŠธ๋ฅผ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌ โ†’ ์„ฑ๋Šฅ ์ตœ์ ํ™”


โœ… ๊ธฐ๋ณธ ์˜ˆ์ œ

document.querySelector('#list').addEventListener('click', (e) => {
  if (e.target.tagName === 'LI') {
    e.target.classList.toggle('active');
  }
});

๐Ÿš€ ์‹ฌํ™”: ๋™์  ์š”์†Œ ๊ด€๋ฆฌ + ์œ„์ž„ ํ™œ์šฉ

const list = document.querySelector('#list');

// ๋™์ ์œผ๋กœ ์š”์†Œ ์ถ”๊ฐ€
const newItem = document.createElement('li');
newItem.textContent = 'New Item';
list.appendChild(newItem);

// ์ด๋ฏธ ๋ถ€๋ชจ์— ๋“ฑ๋ก๋œ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋กœ ์ฒ˜๋ฆฌ OK!

9๏ธโƒฃ โšก ์‹ค๋ฌด ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒจํ„ด

์ƒํ™ฉ ์ตœ์ ํ™” ๋ฐฉ๋ฒ•
๋งŽ์€ ์š”์†Œ์— ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๋ถ€๋ชจ์— ์ด๋ฒคํŠธ ์œ„์ž„ ์ ์šฉ
DOM ์กฐ์ž‘ ๋งŽ์„ ๋•Œ DocumentFragment ์‚ฌ์šฉ โ†’ ๋ฆฌํ”Œ๋กœ์šฐ ์ตœ์†Œํ™”
๊ณ ๋นˆ๋„ ์ด๋ฒคํŠธ (scroll, resize) Throttle/Debounce ์ ์šฉ
๋™์  ์š”์†Œ ๋งŽ์„ ๋•Œ MutationObserver + ์ด๋ฒคํŠธ ์œ„์ž„ ์กฐํ•ฉ ํ™œ์šฉ

๐Ÿ”Ÿ ๐Ÿง  ๊ธฐ์ˆ  ๋ฉด์ ‘ ๋Œ€๋น„ ํ•ต์‹ฌ ์ •๋ฆฌ

์งˆ๋ฌธ ํ•ต์‹ฌ ๋‹ต๋ณ€
DOM์ด๋ž€? HTML ๋ฌธ์„œ๋ฅผ ๊ฐ์ฒด๋กœ ํ‘œํ˜„ํ•œ ํŠธ๋ฆฌ ๊ตฌ์กฐ
DOM ํƒ์ƒ‰ ๋ฐฉ๋ฒ•? getElementById, querySelector, parentNode ๋“ฑ
๋ฒ„๋ธ”๋ง vs ์บก์ฒ˜๋ง ์ฐจ์ด? ์ „ํŒŒ ๋ฐฉํ–ฅ: ํ•˜์œ„โ†’์ƒ์œ„ vs ์ƒ์œ„โ†’ํ•˜์œ„
stopPropagation๊ณผ preventDefault ์ฐจ์ด? ์ „ํŒŒ ์ค‘๋‹จ vs ๊ธฐ๋ณธ ๋™์ž‘ ์ทจ์†Œ
์ด๋ฒคํŠธ ์œ„์ž„ ์žฅ์ ? ๋ฆฌ์Šค๋„ˆ ๋ถ€ํ•˜ โ†“, ๋™์  ์š”์†Œ ๊ด€๋ฆฌ ์‰ฌ์›€
์‹ค๋ฌด์—์„œ DOM ์กฐ์ž‘ ์„ฑ๋Šฅ ๊ฐœ์„  ๋ฐฉ๋ฒ•? Fragment, ์ด๋ฒคํŠธ ์œ„์ž„, Throttle, MutationObserver

๐Ÿš€ ์ข…ํ•ฉ์ฝ”๋“œ ์˜ˆ์ œ

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript ๋น„๋™๊ธฐ & DOM ์กฐ์ž‘ ์‹ฌํ™” ์‹ค์Šต</title>
    <style>
        .area {
            background: lightgray;
            border: 1px solid black;
            width: 600px;
            margin-bottom: 10px;
            padding: 10px;
            white-space: pre-wrap;
            font-family: monospace;
        }
        .big { height: 600px; overflow-y: scroll; }
    </style>
</head>
<body>
    <h2>๐ŸŸข JavaScript ๋น„๋™๊ธฐ & DOM ์กฐ์ž‘ ์‹ฌํ™” ์‹ค์Šต</h2>
    <div class="area big" id="outputArea"></div>
    
    <script>
        function runExamples() {
            const output = document.getElementById("outputArea");
            function log(text) { output.innerHTML += text + "\n"; }
            
            // โœ… DOM ์กฐ์ž‘ ๊ธฐ๋ณธ
            log("โœ… DOM ์กฐ์ž‘ ๊ธฐ๋ณธ");
            document.body.innerHTML += '<div id="container"><p>Hello</p><ul><li>Item 1</li><li>Item 2</li></ul></div>';
            
            // โœ… DOM ํƒ์ƒ‰ ์˜ˆ์ œ
            log("\nโœ… DOM ํƒ์ƒ‰ ์˜ˆ์ œ");
            const container = document.getElementById('container');
            log("children: " + container.children.length); // ์ž์‹ ์š”์†Œ ๊ฐœ์ˆ˜
            log("parent: " + container.parentElement.tagName); // ๋ถ€๋ชจ ์š”์†Œ ํƒœ๊ทธ๋ช…
            log("first child: " + container.firstElementChild.tagName); // ์ฒซ ๋ฒˆ์งธ ์ž์‹ ์š”์†Œ ํƒœ๊ทธ๋ช…
            
            // โœ… DOM ์ˆ˜์ •/์ถ”๊ฐ€/์‚ญ์ œ ์˜ˆ์ œ
            log("\nโœ… DOM ์ˆ˜์ •/์ถ”๊ฐ€/์‚ญ์ œ ์˜ˆ์ œ");
            const newItem = document.createElement('li');
            newItem.textContent = 'New Item';
            container.querySelector('ul').appendChild(newItem);
            log("์ƒˆ ์•„์ดํ…œ ์ถ”๊ฐ€๋จ: " + newItem.textContent);
            
            // โœ… ์ด๋ฒคํŠธ ํ•ธ๋“ค๋ง ๊ธฐ๋ณธ
            log("\nโœ… ์ด๋ฒคํŠธ ํ•ธ๋“ค๋ง ๊ธฐ๋ณธ");
            const btn = document.createElement('button');
            btn.textContent = 'ํด๋ฆญํ•˜์„ธ์š”';
            document.body.appendChild(btn);
            btn.addEventListener('click', () => log('๋ฒ„ํŠผ ํด๋ฆญ๋จ!'));
            
            // โœ… ์ด๋ฒคํŠธ ๋ฒ„๋ธ”๋ง & ์บก์ฒ˜๋ง
            log("\nโœ… ์ด๋ฒคํŠธ ๋ฒ„๋ธ”๋ง & ์บก์ฒ˜๋ง");
            document.body.addEventListener('click', () => log('๐Ÿ“Œ ์บก์ฒ˜๋ง ๋‹จ๊ณ„'), true);
            document.body.addEventListener('click', () => log('๐Ÿ“Œ ๋ฒ„๋ธ”๋ง ๋‹จ๊ณ„'));
            
            // โœ… stopPropagation & preventDefault ์˜ˆ์ œ
            log("\nโœ… stopPropagation & preventDefault ์˜ˆ์ œ");
            const link = document.createElement('a');
            link.href = "#";
            link.textContent = "ํด๋ฆญ ๊ธˆ์ง€ ๋งํฌ";
            document.body.appendChild(link);
            link.addEventListener('click', (e) => {
                e.preventDefault(); // ๊ธฐ๋ณธ ๋™์ž‘(์ด๋™) ์ฐจ๋‹จ
                e.stopPropagation(); // ์ด๋ฒคํŠธ ์ „ํŒŒ ์ฐจ๋‹จ
                log("โŒ ๋งํฌ ํด๋ฆญ ์ฐจ๋‹จ๋จ");
            });
            
            // โœ… ์ด๋ฒคํŠธ ์œ„์ž„ ํŒจํ„ด
            log("\nโœ… ์ด๋ฒคํŠธ ์œ„์ž„ ํŒจํ„ด");
            const list = document.createElement('ul');
            list.innerHTML = '<li>Item A</li><li>Item B</li>';
            document.body.appendChild(list);
            list.addEventListener('click', (e) => {
                if (e.target.tagName === 'LI') {
                    e.target.classList.toggle('active'); // ํด๋ž˜์Šค ํ† ๊ธ€
                    log("๐Ÿ“Œ " + e.target.textContent + " ์„ ํƒ๋จ");
                }
            });
            
            // โœ… ์‹ค๋ฌด ์„ฑ๋Šฅ ์ตœ์ ํ™”
            log("\nโœ… ์‹ค๋ฌด ์„ฑ๋Šฅ ์ตœ์ ํ™”");
            const frag = document.createDocumentFragment();
            for (let i = 0; i < 5; i++) {
                const div = document.createElement('div');
                div.textContent = 'Batch ' + i;
                frag.appendChild(div); // DocumentFragment์— ์ถ”๊ฐ€
            }
            document.body.appendChild(frag); // ํ•œ ๋ฒˆ์— ์ถ”๊ฐ€ (๋ฆฌํ”Œ๋กœ์šฐ ์ตœ์†Œํ™”)
            log("๐Ÿš€ DocumentFragment๋กœ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ์™„๋ฃŒ");

            // โœ… MutationObserver๋ฅผ ํ™œ์šฉํ•œ ๋™์  ์š”์†Œ ๊ฐ์ง€
            log("\nโœ… MutationObserver ํ™œ์šฉ ์˜ˆ์ œ");
            const observerTarget = document.createElement('div');
            observerTarget.id = 'observerTarget';
            document.body.appendChild(observerTarget);

            const observer = new MutationObserver(mutations => {
                mutations.forEach(mutation => {
                    log("โšก ๋ณ€๊ฒฝ ๊ฐ์ง€๋จ: " + mutation.type);
                });
            });
            observer.observe(observerTarget, { childList: true, subtree: true });

            setTimeout(() => {
                const newElement = document.createElement('p');
                newElement.textContent = '๐Ÿ‘€ ๊ฐ์ง€๋œ ์š”์†Œ';
                observerTarget.appendChild(newElement);
            }, 2000);
        }
        
        runExamples();
    </script>
</body>
</html>

๐Ÿš€ ์ด๋ฒคํŠธ ์ฝ”๋“œ ์˜ˆ์ œ

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>JavaScript ์ด๋ฒคํŠธ ์™„์ „ ์ •๋ณต ๐Ÿš€</title>
  <style>
    .area {
      width: 100%;
      height: 50px;
      border: 1px solid #333;
      margin-top: 10px;
      padding: 5px;
      background-color: #f9f9f9;
    }
    .highlight {
      background-color: yellow;
    }
  </style>
</head>
<body>

  <h1>๐Ÿ“š ์ด๋ฒคํŠธ(Event) ์™„๋ฒฝ ๊ฐ€์ด๋“œ</h1>

  <!-- 1๏ธโƒฃ ์ธ๋ผ์ธ ์ด๋ฒคํŠธ -->
  <h3>1๏ธโƒฃ ์ธ๋ผ์ธ ์ด๋ฒคํŠธ ๋ฐฉ์‹</h3>
  <button onclick="inlineEvent()">๐Ÿ”ฅ ์ธ๋ผ์ธ ์‹คํ–‰ํ™•์ธ</button>

  <!-- 2๏ธโƒฃ ์†์„ฑ์œผ๋กœ ํ•ธ๋“ค๋Ÿฌ ์—ฐ๊ฒฐ -->
  <h3>2๏ธโƒฃ ์†์„ฑ ๋ฐฉ์‹ (DOM ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹)</h3>
  <button id="btn1">โœ… ์†์„ฑ ๋ฐฉ์‹ ์‹คํ–‰</button>
  <div id="area1" class="area"></div>

  <!-- 3๏ธโƒฃ addEventListener -->
  <h3>3๏ธโƒฃ addEventListener ๋ฐฉ์‹ (ํ‘œ์ค€ ์ด๋ฒคํŠธ ๋ชจ๋ธ)</h3>
  <button id="btn2">๐ŸŽฏ addEventListener ์‹คํ–‰</button>
  <div id="area2" class="area"></div>

  <!-- 4๏ธโƒฃ ์ด๋ฒคํŠธ ๊ฐ์ฒด + this -->
  <h3>4๏ธโƒฃ ์ด๋ฒคํŠธ ๊ฐ์ฒด & this ์ดํ•ด</h3>
  <button id="btn3">๐Ÿ” ์ด๋ฒคํŠธ ๊ฐ์ฒด ํ™•์ธ</button>
  <div id="area3" class="area"></div>

  <!-- 5๏ธโƒฃ ์ด๋ฒคํŠธ ์œ„์ž„ -->
  <h3>5๏ธโƒฃ ์ด๋ฒคํŠธ ์œ„์ž„ (Event Delegation)</h3>
  <div id="parentArea" class="area">
    <button class="child-btn">๋™์ ๋ฒ„ํŠผ1</button>
    <button class="child-btn">๋™์ ๋ฒ„ํŠผ2</button>
  </div>
  <button id="addBtn">โž• ๋™์  ๋ฒ„ํŠผ ์ถ”๊ฐ€</button>

  <!-- 6๏ธโƒฃ ๊ณ ๊ธ‰ - ์ด๋ฒคํŠธ ์ œ๊ฑฐ & once ์˜ต์…˜ -->
  <h3>6๏ธโƒฃ ๊ณ ๊ธ‰ - removeEventListener & once ์˜ต์…˜</h3>
  <button id="btn4">๐Ÿ—‘๏ธ ํ•ธ๋“ค๋Ÿฌ ํ•œ ๋ฒˆ ์‹คํ–‰ ํ›„ ์ œ๊ฑฐ</button>
  <div id="area4" class="area"></div>

  <!-- 7๏ธโƒฃ preventDefault & stopPropagation -->
  <h3>7๏ธโƒฃ ๊ธฐ๋ณธ ๋™์ž‘ ๋ฐฉ์ง€ & ์ด๋ฒคํŠธ ์ „ํŒŒ ์ค‘๋‹จ</h3>
  <form id="myForm">
    <input type="text" placeholder="์•„๋ฌด๊ฑฐ๋‚˜ ์ž…๋ ฅ ํ›„ ์—”ํ„ฐ" />
    <button type="submit">๐Ÿšซ ์ œ์ถœ</button>
  </form>
  <div id="area5" class="area"></div>

  <!-- 8๏ธโƒฃ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ -->
  <h3>8๏ธโƒฃ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ์ƒ์„ฑ & ์‚ฌ์šฉ</h3>
  <button id="btn5">โœจ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ์‹คํ–‰</button>
  <div id="area6" class="area"></div>

  <script>
    /***********************************
    1๏ธโƒฃ ์ธ๋ผ์ธ ์ด๋ฒคํŠธ ๋ฐฉ์‹ - HTML์— ์ง์ ‘ ์ง€์ •
    ************************************/
    function inlineEvent() {
      alert("๐Ÿ”ฅ ์ธ๋ผ์ธ ์ด๋ฒคํŠธ ๋ฐœ์ƒ!");
    }

    /***********************************
    2๏ธโƒฃ ์†์„ฑ ๋ฐฉ์‹ (DOM ํ”„๋กœํผํ‹ฐ์— ํ•จ์ˆ˜ ํ• ๋‹น)
    ************************************/
    const btn1 = document.getElementById("btn1");
    const area1 = document.getElementById("area1");

    btn1.onclick = function() {
      area1.textContent = "โœ… ์†์„ฑ ๋ฐฉ์‹ ์ด๋ฒคํŠธ ์‹คํ–‰!";
      area1.style.backgroundColor = "#d4edda";
    };

    /***********************************
    3๏ธโƒฃ addEventListener ๋ฐฉ์‹ (๋‹ค์ค‘ ํ•ธ๋“ค๋Ÿฌ ๊ฐ€๋Šฅ)
    ************************************/
    const btn2 = document.getElementById("btn2");
    const area2 = document.getElementById("area2");

    btn2.addEventListener("click", function() {
      area2.textContent = "๐ŸŽฏ ์ฒซ ๋ฒˆ์งธ ํ•ธ๋“ค๋Ÿฌ ์‹คํ–‰!";
      area2.style.backgroundColor = "#cce5ff";
    });

    btn2.addEventListener("click", function() {
      console.log("โœ… ๋‘ ๋ฒˆ์งธ ํ•ธ๋“ค๋Ÿฌ๋„ ์‹คํ–‰๋จ!");
    });

    /***********************************
    4๏ธโƒฃ ์ด๋ฒคํŠธ ๊ฐ์ฒด(event) & this ์ดํ•ด
    ************************************/
    const btn3 = document.getElementById("btn3");
    const area3 = document.getElementById("area3");

    btn3.addEventListener("click", function(event) {
      console.log("๐Ÿ‘‰ ์ด๋ฒคํŠธ ํƒ€์ž…:", event.type); // ํด๋ฆญ ํƒ€์ž…
      console.log("๐Ÿ‘‰ ํด๋ฆญํ•œ ์š”์†Œ:", event.target); // ํด๋ฆญ ๋Œ€์ƒ
      this.style.backgroundColor = "pink"; // this๋Š” btn3 ๊ฐ€๋ฆฌํ‚ด

      area3.innerHTML = `
        <strong>์ด๋ฒคํŠธ ํƒ€์ž…:</strong> ${event.type}<br/>
        <strong>ํด๋ฆญํ•œ ์š”์†Œ:</strong> ${event.target.textContent}
      `;
    });

    /***********************************
    5๏ธโƒฃ ์ด๋ฒคํŠธ ์œ„์ž„ (๋ถ€๋ชจ์—๊ฒŒ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ)
    ************************************/
    const parentArea = document.getElementById("parentArea");
    const addBtn = document.getElementById("addBtn");

    parentArea.addEventListener("click", function(event) {
      if (event.target.classList.contains("child-btn")) {
        event.target.classList.toggle("highlight"); // ๊ฐ•์กฐ ํ† ๊ธ€
        alert(`๐Ÿ”ฝ ${event.target.textContent} ํด๋ฆญ๋จ!`);
      }
    });

    addBtn.addEventListener("click", function() {
      const newBtn = document.createElement("button");
      newBtn.textContent = `๋™์ ๋ฒ„ํŠผ${parentArea.children.length + 1}`;
      newBtn.classList.add("child-btn");
      parentArea.appendChild(newBtn);
    });

    /***********************************
    6๏ธโƒฃ ์ด๋ฒคํŠธ ์ œ๊ฑฐ & once ์˜ต์…˜
    ************************************/
    const btn4 = document.getElementById("btn4");
    const area4 = document.getElementById("area4");

    // ๋ฐฉ๋ฒ•1 - once ์˜ต์…˜: ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰ ํ›„ ์ž๋™ ์ œ๊ฑฐ
    btn4.addEventListener("click", function() {
      area4.textContent = "๐Ÿ—‘๏ธ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰ ํ›„ ์ œ๊ฑฐ๋จ!";
      area4.style.backgroundColor = "#ffeeba";
    }, { once: true });

    // ๋ฐฉ๋ฒ•2 - removeEventListener (์„ค๋ช…์šฉ, ๋”ฐ๋กœ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ํ•ธ๋“ค๋Ÿฌ ๋ณ€์ˆ˜ํ™” ํ•„์š”)

    /***********************************
    7๏ธโƒฃ preventDefault & stopPropagation
    ************************************/
    const myForm = document.getElementById("myForm");
    const area5 = document.getElementById("area5");

    myForm.addEventListener("submit", function(event) {
      event.preventDefault(); // ํผ ๊ธฐ๋ณธ ์ œ์ถœ ๋ง‰๊ธฐ ๐Ÿšซ
      area5.textContent = "๐Ÿšซ ๊ธฐ๋ณธ ์ œ์ถœ ๋ฐฉ์ง€๋จ!";
      area5.style.backgroundColor = "#f8d7da";
    });

    /***********************************
    8๏ธโƒฃ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๋งŒ๋“ค๊ธฐ
    ************************************/
    const btn5 = document.getElementById("btn5");
    const area6 = document.getElementById("area6");

    // ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ์ •์˜
    const customEvent = new CustomEvent("shine", {
      detail: { message: "โœจ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๋ฐœ์ƒ!" }
    });

    // ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ
    btn5.addEventListener("shine", function(e) {
      console.log(e.detail.message);
      area6.textContent = e.detail.message;
      area6.style.backgroundColor = "#e2e3e5";
    });

    // ํด๋ฆญ ์‹œ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๋ฐœ์ƒ
    btn5.addEventListener("click", function() {
      btn5.dispatchEvent(customEvent); // ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ์‹คํ–‰
    });

  </script>

</body>
</html>