DOM이란 — 문서 객체 모델
이 토픽을 마치면
DOM이 무엇인지 설명할 수 있고, JavaScript로 HTML 요소를 선택하고, 내용을 바꾸고, 이벤트를 붙이는 기본 작업을 할 수 있습니다.
HTML은 텍스트, DOM은 객체
브라우저가 HTML 파일을 받으면 텍스트를 그대로 화면에 그리는 것이 아닙니다. HTML 텍스트를 파싱해서 메모리에 트리 구조의 객체를 만듭니다. 이 객체 트리가 DOM(Document Object Model)입니다.
<html>
<body>
<h1>안녕하세요</h1>
<p>반갑습니다</p>
</body>
</html>document
└── html
└── body
├── h1 → "안녕하세요"
└── p → "반갑습니다"모든 HTML 태그, 텍스트, 속성이 이 트리의 노드가 됩니다. JavaScript는 이 트리를 통해 HTML을 읽고, 수정하고, 삭제할 수 있습니다.
요소 선택하기
DOM을 조작하려면 먼저 원하는 요소를 선택해야 합니다.
// id로 선택 (하나만 반환)
const title = document.getElementById('main-title');
// CSS 선택자로 선택 (첫 번째 하나)
const firstCard = document.querySelector('.card');
// CSS 선택자로 선택 (전부)
const allCards = document.querySelectorAll('.card');querySelector는 CSS 선택자를 그대로 사용할 수 있어서 가장 범용적입니다. 클래스는 .card, id는 #main-title, 태그는 p로 선택합니다.
// 조합도 가능
const activeItem = document.querySelector('ul.menu > li.active');내용 읽기와 수정하기
const heading = document.querySelector('h1');
// 읽기
console.log(heading.textContent); // "안녕하세요"
console.log(heading.innerHTML); // "안녕하세요" (HTML 태그 포함)
// 수정
heading.textContent = '환영합니다'; // 텍스트만 변경
heading.innerHTML = '<em>환영</em>합니다'; // HTML 삽입 가능textContent는 순수 텍스트, innerHTML은 HTML 마크업을 다룹니다. 사용자 입력을 innerHTML에 넣으면 XSS 공격에 취약하므로, 사용자 데이터는 textContent를 사용합니다.
스타일과 속성 변경
const box = document.querySelector('.box');
// 인라인 스타일 변경
box.style.backgroundColor = '#3498db';
box.style.padding = '20px';
// CSS 클래스 조작 (권장)
box.classList.add('active');
box.classList.remove('hidden');
box.classList.toggle('selected');인라인 스타일을 직접 바꿀 수도 있지만, CSS 클래스를 추가/제거하는 것이 유지보수에 유리합니다. 스타일은 CSS 파일에, 동작은 JavaScript에 분리하는 원칙입니다.
// 속성 변경
const img = document.querySelector('img');
img.setAttribute('src', 'new-image.png');
img.setAttribute('alt', '새 이미지');
// data 속성
const card = document.querySelector('[data-id="42"]');
console.log(card.dataset.id); // "42"요소 생성과 추가
// 새 요소 만들기
const newItem = document.createElement('li');
newItem.textContent = '새 항목';
newItem.classList.add('item');
// 기존 요소에 추가
const list = document.querySelector('ul');
list.appendChild(newItem); // 맨 뒤에 추가
list.prepend(newItem); // 맨 앞에 추가
list.insertBefore(newItem, list.children[1]); // 특정 위치에 삽입// 여러 요소를 한 번에 추가할 때
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const li = document.createElement('li');
li.textContent = `항목 ${i}`;
fragment.appendChild(li);
}
list.appendChild(fragment); // DOM 접근 1회DocumentFragment를 사용하면 반복적인 DOM 수정을 모아서 한 번에 처리할 수 있습니다. DOM 접근은 비용이 큰 작업이므로 횟수를 줄이는 것이 성능에 유리합니다.
이벤트 — 사용자 동작에 반응하기
const button = document.querySelector('#submit');
button.addEventListener('click', function(event) {
console.log('버튼이 클릭됨');
console.log(event.target); // 클릭된 요소
});addEventListener는 세 부분으로 구성됩니다:
- 이벤트 이름:
'click','input','submit','keydown'등 - 콜백 함수: 이벤트가 발생하면 실행할 함수
event객체: 이벤트에 대한 정보 (어떤 키를 눌렀는지, 마우스 좌표 등)
// 입력 필드 실시간 반응
const input = document.querySelector('#search');
input.addEventListener('input', function(e) {
console.log('현재 입력:', e.target.value);
});이벤트 위임
버튼 100개에 각각 이벤트를 거는 대신, 부모에 하나만 걸 수 있습니다:
// ❌ 비효율적 — 100개의 리스너
document.querySelectorAll('.item').forEach(item => {
item.addEventListener('click', handleClick);
});
// ✅ 이벤트 위임 — 1개의 리스너
document.querySelector('#list').addEventListener('click', function(e) {
if (e.target.classList.contains('item')) {
console.log('클릭된 항목:', e.target.textContent);
}
});클릭 이벤트는 자식에서 부모로 버블링(bubbling)됩니다. 부모에서 e.target을 확인하면 실제 클릭된 자식을 알 수 있습니다. 동적으로 추가되는 요소에도 자동으로 적용되는 장점이 있습니다.
textContent vs innerHTML 보안
const userInput = '<script>alert("해킹")</script>';
element.innerHTML = userInput; // ❌ 위험 — XSS 공격 가능
element.textContent = userInput; // ✅ 안전 — 태그가 텍스트로 표시됨사용자 입력을 innerHTML로 넣으면 악성 스크립트가 실행될 수 있습니다. 사용자 입력에는 반드시 textContent를 사용합니다. HTML 구조를 동적으로 만들어야 할 때는 createElement로 조립하는 것이 안전합니다.
DOM과 프레임워크
현대 프론트엔드 프레임워크(React, Vue, Svelte)를 쓰면 DOM을 직접 조작하지 않습니다. 프레임워크가 가상 DOM(Virtual DOM)이나 컴파일러를 통해 대신 처리합니다:
직접 DOM 조작:
개발자 → querySelector → DOM 직접 수정
React 방식:
개발자 → state 변경 → React가 DOM 수정하지만 DOM의 원리를 모르면:
- 프레임워크의 동작을 이해할 수 없습니다
- 성능 문제를 디버깅할 수 없습니다
- 프레임워크 없이 빠른 프로토타입을 만들 수 없습니다
핵심 정리
| 작업 | 메서드 |
|---|---|
| 선택 | querySelector(), querySelectorAll() |
| 읽기/수정 | textContent, innerHTML |
| 스타일 | classList.add/remove/toggle |
| 생성 | createElement(), appendChild() |
| 이벤트 | addEventListener() |
| 이벤트 위임 | 부모에 리스너 + e.target 확인 |
DOM은 JavaScript가 웹 페이지를 동적으로 만드는 핵심 인터페이스입니다. React 같은 프레임워크를 사용하면 DOM을 직접 조작하지 않아도 되지만, DOM이 어떻게 작동하는지 이해하고 있으면 디버깅과 최적화에 큰 도움이 됩니다.