์น ๊ฐ๋ฐ์ ํ๋ค ๋ณด๋ฉด "DOM"์ด๋ผ๋ ์ฉ์ด๋ฅผ ์์ฃผ ์ ํ๊ฒ ๋ฉ๋๋ค. DOM(Document Object Model)์ ์น ํ์ด์ง์ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ฒด๋ก ํํํ ๋ชจ๋ธ๋ก, ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ์ฌ ์น ํ์ด์ง๋ฅผ ๋์ ์ผ๋ก ์กฐ์ํ ์ ์๊ฒ ํด ์ค๋๋ค. ์๋ฅผ ๋ค์ด, ์ฌ์ฉ์๊ฐ ๋ฒํผ์ ํด๋ฆญํ์ ๋ ์๋ก์ด ๋ด์ฉ์ ์ถ๊ฐํ๊ฑฐ๋, ์ ๋ ฅ๋ ๋ฐ์ดํฐ๋ฅผ ์ค์๊ฐ์ผ๋ก ๊ฒ์ฆํ๋ ๋ฑ์ ์์ ์ด ๊ฐ๋ฅํฉ๋๋ค.
DOM์ ์ดํดํ๊ณ ํ์ฉํ๋ ๊ฒ์ ์น ๊ฐ๋ฐ์ ๊ธฐ๋ณธ์ด์ ํ์ ์์์ ๋๋ค. ์ด๋ฒ ํฌ์คํ ์์๋ ์๋ฐ์คํฌ๋ฆฝํธ DOM์ ๊ธฐ๋ณธ ๊ฐ๋ ๋ถํฐ ์ค์ต ์์ ๊น์ง ๋จ๊ณ๋ณ๋ก ์์ธํ ์ค๋ช ํ์ฌ, ์ฌ๋ฌ๋ถ์ด DOM์ ์์ ์์ฌ๋ก ๋ค๋ฃฐ ์ ์๋๋ก ๋์๋๋ฆฌ๊ฒ ์ต๋๋ค.
โฃ ๋ชฉ์ฐจ
01. ์๋ฐ์คํฌ๋ฆฝํธ DOM์ด๋?๐
DOM(Document Object Model)์ ์น ํ์ด์ง๋ฅผ ๊ตฌ์กฐํํ์ฌ ๊ฐ ์์๋ฅผ ๊ฐ์ฒด๋ก ํํํ ๋ชจ๋ธ์ ๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ๋ฉด DOM์ ๋์ ์ผ๋ก ์กฐ์ํ์ฌ ์น ํ์ด์ง์ ๋ด์ฉ์ ์ค์๊ฐ์ผ๋ก ๋ณ๊ฒฝํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ์ฌ์ฉ์๊ฐ ๋ฒํผ์ ํด๋ฆญํ์ ๋ ํ๋ฉด์ ์๋ก์ด ๋ด์ฉ์ ์ถ๊ฐํ๊ฑฐ๋, ์ ๋ ฅํ ๋ฐ์ดํฐ๋ฅผ ๊ฒ์ฆํ๋ ๋ฑ์ ์์ ์ด ๊ฐ๋ฅํฉ๋๋ค.
02. ๊ธฐ๋ณธ ๊ฐ๋ : DOM ๊ตฌ์กฐ์ ๋ ธ๋๐ณ
DOM์ ํธ๋ฆฌ ๊ตฌ์กฐ๋ก ์ด๋ฃจ์ด์ ธ ์์ผ๋ฉฐ, ๊ฐ ์์๋ฅผ '๋ ธ๋(Node)'๋ผ๊ณ ๋ถ๋ฆ ๋๋ค. ์ฃผ์ ๋ ธ๋ ํ์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
- ์์ ๋ ธ๋(Element Node): HTML ํ๊ทธ๋ฅผ ๋ํ๋ ๋๋ค.
- ํ ์คํธ ๋ ธ๋(Text Node): ํ ์คํธ๋ฅผ ๋ํ๋ ๋๋ค.
- ์ฃผ์ ๋ ธ๋(Comment Node): ์ฃผ์์ ๋ํ๋ ๋๋ค.
DOM ํธ๋ฆฌ์ ์์๋ฅผ ๋ณด๊ฒ ์ต๋๋ค.
<!DOCTYPE html>
<html>
<body>
<h1>์๋
ํ์ธ์</h1>
<p>์ฌ๊ธฐ๋ DOM ํธ๋ฆฌ ์์์
๋๋ค.</p>
</body>
</html>
์์ HTML ๋ฌธ์์ DOM ํธ๋ฆฌ๋ ๋ค์๊ณผ ๊ฐ์ด ํํ๋ฉ๋๋ค.
html
โโโ body
โโโ h1
โ โโโ ํ
์คํธ ๋
ธ๋: "์๋
ํ์ธ์"
โโโ p
โโโ ํ
์คํธ ๋
ธ๋: "์ฌ๊ธฐ๋ DOM ํธ๋ฆฌ ์์์
๋๋ค."
03. DOM ์ ๊ทผํ๊ธฐ๐
์๋ฐ์คํฌ๋ฆฝํธ๋ก DOM์ ์ ๊ทผํ๋ ๋ฐฉ๋ฒ์ ์ฌ๋ฌ ๊ฐ์ง๊ฐ ์์ต๋๋ค. ๋ํ์ ์ธ ๋ฐฉ๋ฒ๋ค์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
- document.getElementById(): id ์์ฑ์ ์ฌ์ฉํ์ฌ ์์๋ฅผ ์ฐพ์ต๋๋ค.
const header = document.getElementById('header');
- document.querySelector()์ document.querySelectorAll(): CSS ์ ํ์๋ฅผ ์ฌ์ฉํ์ฌ ์์๋ฅผ ์ฐพ์ต๋๋ค.
const firstParagraph = document.querySelector('p');
const allDivs = document.querySelectorAll('div');
- getElementsByClassName()์ getElementsByTagName(): ํด๋์ค๋ช ์ด๋ ํ๊ทธ๋ช ์ ์ฌ์ฉํ์ฌ ์์๋ฅผ ์ฐพ์ต๋๋ค.
const items = document.getElementsByClassName('item');
const paragraphs = document.getElementsByTagName('p');
04. DOM ์กฐ์ํ๊ธฐ๐ ๏ธ
DOM์ ์กฐ์ํ๋ ๋ฐฉ๋ฒ๋ ์ฌ๋ฌ ๊ฐ์ง๊ฐ ์์ต๋๋ค. ์๋ก์ด ์์๋ฅผ ์ถ๊ฐํ๊ฑฐ๋, ๊ธฐ์กด ์์๋ฅผ ์ญ์ ํ๊ฑฐ๋, ์์์ ์์ฑ์ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.
- ๋ ธ๋ ์์ฑ๊ณผ ์ถ๊ฐ
const newDiv = document.createElement('div');
newDiv.textContent = '์๋ก์ด ๋ด์ฉ';
document.body.appendChild(newDiv);
- ๋ ธ๋ ์ญ์
const oldElement = document.getElementById('oldElement');
oldElement.remove();
- ์์ฑ ์กฐ์
const link = document.querySelector('a');
link.setAttribute('href', 'https://www.example.com');
const hrefValue = link.getAttribute('href');
link.removeAttribute('href');
05. ์ด๋ฒคํธ ํธ๋ค๋ง๐
์ด๋ฒคํธ ํธ๋ค๋ง์ ์ฌ์ฉ์์ ์ํธ์์ฉ์ ๋ฐ์ํ๋ ์ค์ํ ๊ธฐ๋ฅ์ ๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ๋ก ๋ค์ํ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
- ์ด๋ฒคํธ ๋ฆฌ์ค๋ ์ถ๊ฐ
const button = document.querySelector('button');
button.addEventListener('click', function() {
alert('๋ฒํผ์ด ํด๋ฆญ๋์์ต๋๋ค!');
});
- ์ด๋ฒคํธ ํ์
- click: ํด๋ฆญ ์ด๋ฒคํธ
- load: ํ์ด์ง๊ฐ ๋ก๋๋ ๋ ๋ฐ์
- DOMContentLoaded: ์ด๊ธฐ HTML ๋ฌธ์๊ฐ ์์ ํ ๋ก๋๋๊ณ ๋ถ์๋์์ ๋ ๋ฐ์
- ์ด๋ฒคํธ ๊ฐ์ฒด์ ์ด๋ฒคํธ ๋ฒ๋ธ๋ง
document.body.addEventListener('click', function(event) {
console.log('์ด๋ฒคํธ ํ์
: ' + event.type);
console.log('์ด๋ฒคํธ ํ๊ฒ: ' + event.target);
});
06. DOM ์กฐ์ ์ค์ต ์์ ๐
- ๊ฐ๋จํ Todo ๋ฆฌ์คํธ ๋ง๋ค๊ธฐ
<ul id="todo-list">
<li>์์ ์์ดํ
1</li>
</ul>
<input type="text" id="new-todo" placeholder="์๋ก์ด ์์ดํ
">
<button id="add-todo">์ถ๊ฐ</button>
<script>
const addButton = document.getElementById('add-todo');
addButton.addEventListener('click', function() {
const newTodo = document.getElementById('new-todo').value;
const newLi = document.createElement('li');
newLi.textContent = newTodo;
document.getElementById('todo-list').appendChild(newLi);
});
</script>
- ์ด๋ฏธ์ง ๊ฐค๋ฌ๋ฆฌ ๊ตฌํ
<div id="gallery"></div>
<button id="load-images">์ด๋ฏธ์ง ๋ถ๋ฌ์ค๊ธฐ</button>
<script>
document.getElementById('load-images').addEventListener('click', function() {
const gallery = document.getElementById('gallery');
for (let i = 1; i <= 5; i++) {
const img = document.createElement('img');
img.src = 'image' + i + '.jpg';
gallery.appendChild(img);
}
});
</script>
07. ์ต์ ํ ๋ฐ ์ฑ๋ฅ ํ๐
DOM์ ํจ์จ์ ์ผ๋ก ์กฐ์ํ๋ฉด ์น ํ์ด์ง์ ์ฑ๋ฅ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์ต๋๋ค. ํนํ ๋๊ท๋ชจ ์น ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ์ฑ๋ฅ ์ต์ ํ๊ฐ ๋งค์ฐ ์ค์ํฉ๋๋ค. ์ด๋ฒ ์น์ ์์๋ DocumentFragment์ Reflow, Repaint์ ๋ํด ์์ธํ ์์๋ณด๊ฒ ์ต๋๋ค.
DocumentFragment๋?
DocumentFragment๋ DOM์์ ์ผ์ข ์ ๊ฐ์ ๋ ธ๋ ๊ฐ์ฒด๋ก, ๋ถ๋ชจ๊ฐ ์๋ ์ํ๋ก ์กด์ฌํฉ๋๋ค. DocumentFragment๋ฅผ ์ฌ์ฉํ๋ฉด ์ฌ๋ฌ ์์๋ฅผ DOM์ ์ถ๊ฐํ๊ธฐ ์ ์ ํ ๋ฒ์ ์์ฑํ ์ ์์ด ์ฑ๋ฅ์ ์ต์ ํํ ์ ์์ต๋๋ค.
DocumentFragment์ ์ฅ์
- DOM ์ ๋ฐ์ดํธ ์ต์ํ: DocumentFragment์ ์์๋ฅผ ์ถ๊ฐํ๋ ๋์ DOM์ ์ํฅ์ ์ฃผ์ง ์์ผ๋ฏ๋ก, DOM ์ ๋ฐ์ดํธ ํ์๋ฅผ ์ค์ผ ์ ์์ต๋๋ค.
- ์ฑ๋ฅ ํฅ์: ํ ๋ฒ์ ์ฌ๋ฌ ์์๋ฅผ DOM์ ์ถ๊ฐํ๋ ๊ฒ๋ณด๋ค, DocumentFragment์ ์์๋ฅผ ์ถ๊ฐํ ํ ํ ๋ฒ์ DOM์ ์ฝ์ ํ๋ ๊ฒ์ด ๋ ํจ์จ์ ์ ๋๋ค.
DocumentFragment ์ฌ์ฉ ์์
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const newDiv = document.createElement('div');
newDiv.textContent = '์์ดํ
' + i;
fragment.appendChild(newDiv);
}
document.body.appendChild(fragment);
์ ์ฝ๋์์๋ 1000๊ฐ์ div ์์๋ฅผ ๋จผ์ DocumentFragment์ ์ถ๊ฐํ ํ, ํ ๋ฒ์ DOM์ ์ถ๊ฐํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๋ธ๋ผ์ฐ์ ๊ฐ 1000๋ฒ์ DOM ์ ๋ฐ์ดํธ๋ฅผ ์ํํ๋ ๋์ , ํ ๋ฒ๋ง ์ ๋ฐ์ดํธํ๊ฒ ๋์ด ์ฑ๋ฅ์ด ํฅ์๋ฉ๋๋ค.
Reflow์ Repaint๋?
Reflow์ Repaint๋ ์น ๋ธ๋ผ์ฐ์ ๊ฐ ํ๋ฉด์ ์ฝํ ์ธ ๋ฅผ ๋ ๋๋ง ํ๋ ๊ณผ์ ์์ ๋ฐ์ํ๋ ์ค์ํ ์์ ์ ๋๋ค. ์ด ๋ ์์ ์ ์ฑ๋ฅ์ ํฐ ์ํฅ์ ๋ฏธ์น๋ฏ๋ก, ์ด๋ฅผ ์ต์ํํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
Reflow(์ฌ๋ฐฐ์น)
Reflow๋ ์์์ ๋ ์ด์์์ด ๋ณ๊ฒฝ๋ ๋ ๋ฐ์ํ๋ ๊ณผ์ ์ ๋๋ค. ์๋ฅผ ๋ค์ด, ์์์ ํฌ๊ธฐ, ์์น, ๋๋ ๋ค๋ฅธ ์์์ ๋ํ ์๋์ ์์น๊ฐ ๋ณ๊ฒฝ๋ ๋ Reflow๊ฐ ๋ฐ์ํฉ๋๋ค. Reflow๋ ๋งค์ฐ ๋น์ฉ์ด ๋ง์ด ๋๋ ์์ ์ผ๋ก, ํ์ด์ง์ ๋ชจ๋ ์์๊ฐ ๋ค์ ๊ณ์ฐ๋์ด์ผ ํ ์ ์์ต๋๋ค.
Repaint(์ฌํ์ธํธ)
Repaint๋ ์์์ ์คํ์ผ์ด ๋ณ๊ฒฝ๋์ด ํ๋ฉด์ ๋ค์ ๊ทธ๋ ค์ ธ์ผ ํ ๋ ๋ฐ์ํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ์์์ ์์, ๋ฐฐ๊ฒฝ, ๋๋ ํ ๋๋ฆฌ๊ฐ ๋ณ๊ฒฝ๋ ๋ Repaint๊ฐ ๋ฐ์ํฉ๋๋ค. Repaint๋ Reflow๋ณด๋ค ๋น์ฉ์ด ๋ ๋ค์ง๋ง, ์ฌ์ ํ ์ฑ๋ฅ์ ์ํฅ์ ๋ฏธ์น ์ ์์ต๋๋ค.
Reflow์ Repaint ์ต์ํ ๋ฐฉ๋ฒ:
- ์คํ์ผ ๋ณ๊ฒฝ ์ต์ํ: ํ ๋ฒ์ ์ฌ๋ฌ ์คํ์ผ์ ๋ณ๊ฒฝํ๊ธฐ๋ณด๋ค, ํ ๋ฒ์ ์คํ์ผ์ ๋ณ๊ฒฝํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
// ๋์ ์
element.style.width = '100px';
element.style.height = '100px';
element.style.backgroundColor = 'red';
// ์ข์ ์
element.style.cssText = 'width: 100px; height: 100px; background-color: red;';
- ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ์คํ์ผ ์ ์ฉ: ๊ฐ๋ณ ์คํ์ผ์ ์ค์ ํ๋ ๋์ ํด๋์ค๋ฅผ ์ถ๊ฐํ์ฌ ์คํ์ผ์ ๋ณ๊ฒฝํ๋ ๊ฒ์ด ๋ ํจ์จ์ ์ ๋๋ค.
// ๋์ ์
element.style.display = 'none';
element.style.display = 'block';
// ์ข์ ์
element.classList.add('hidden');
element.classList.remove('hidden');
- ์คํ๋ผ์ธ DOM ์กฐ์: DocumentFragment๋ฅผ ์ฌ์ฉํ๊ฑฐ๋, ์์๋ฅผ DOM์์ ์ ๊ฑฐํ ํ ๋ณ๊ฒฝ์ ์ ์ฉํ๊ณ ๋ค์ ์ถ๊ฐํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const newDiv = document.createElement('div');
newDiv.textContent = '์์ดํ
' + i;
fragment.appendChild(newDiv);
}
document.body.appendChild(fragment);
- ๋ฆฌํ๋ก์ฐ ํธ๋ฆฌ๊ฑฐ ์ต์ํ: ๊ฐ๋ฅํ ํ ๋ ์ด์์ ๋ณ๊ฒฝ์ ์ต์ํํ๊ณ , ๋ ์ด์์ ์ ๋ณด๋ฅผ ์ฝ๊ธฐ ์ ์ ์ฐ๊ธฐ ์์ ์ ์๋ฃํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
// ๋์ ์
element.style.width = '100px';
var width = element.offsetWidth;
element.style.height = '100px';
// ์ข์ ์
element.style.width = '100px';
element.style.height = '100px';
var width = element.offsetWidth;
์ด๋ฌํ ์ต์ ํ ๊ธฐ๋ฒ์ ์ฌ์ฉํ๋ฉด, DOM ์กฐ์์ผ๋ก ์ธํ ์ฑ๋ฅ ์ ํ๋ฅผ ์ค์ด๊ณ , ๋ณด๋ค ๋น ๋ฅด๊ณ ๋ฐ์์ฑ์ด ์ข์ ์น ํ์ด์ง๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.