DOM이란?
DOM(Document Object Model)은 브라우저가 HTML을 읽고 만드는 트리 구조 객체입니다. JavaScript는 이 DOM을 통해 웹 페이지의 내용, 구조, 스타일을 동적으로 변경할 수 있습니다.
HTML 문서의 모든 것 — 태그, 텍스트, 속성 — 이 DOM 노드가 됩니다.
document
└── html
├── head
│ └── title
└── body
├── h1
└── p요소 선택하기
querySelector / querySelectorAll
CSS 선택자로 요소를 찾습니다. 가장 많이 쓰는 방법입니다.
// 하나만 선택 (첫 번째 일치)
const title = document.querySelector('h1');
const btn = document.querySelector('.btn-primary');
const nav = document.querySelector('#main-nav');
// 여러 개 선택 (NodeList 반환)
const cards = document.querySelectorAll('.card');
const links = document.querySelectorAll('nav a');
// NodeList 순회
cards.forEach(card => {
console.log(card.textContent);
});getElementById / getElementsByClassName
const header = document.getElementById('header'); // 하나
const items = document.getElementsByClassName('item'); // HTMLCollection
const paragraphs = document.getElementsByTagName('p'); // HTMLCollection팁: querySelector를 쓰면 통일성 있게 코드를 작성할 수 있어서 추천합니다.
내용 변경하기
textContent vs innerHTML
const title = document.querySelector('.title');
// 텍스트만 변경 (안전 — XSS 방지)
title.textContent = '새로운 제목';
// HTML 포함 변경 (주의 — 사용자 입력 넣지 말 것!)
title.innerHTML = '새로운 <strong>제목</strong>';보안 규칙: 사용자 입력을 innerHTML에 직접 넣으면 XSS(크로스 사이트 스크립팅) 공격에 취약해집니다. 사용자 입력에는 항상 textContent를 사용하세요.
스타일 변경하기
const box = document.querySelector('.box');
// 직접 스타일 변경
box.style.backgroundColor = '#2dd4bf';
box.style.padding = '16px';
box.style.borderRadius = '8px';
// 클래스 토글 (추천)
box.classList.add('active');
box.classList.remove('hidden');
box.classList.toggle('open'); // 있으면 제거, 없으면 추가
box.classList.contains('active'); // true/false베스트 프랙티스: style 속성보다 classList로 클래스를 추가/제거하는 것이 좋습니다. CSS와 JS의 역할이 분리되어 유지보수가 쉬워집니다.
속성 변경하기
const img = document.querySelector('img');
const link = document.querySelector('a');
// 속성 읽기/쓰기
img.src = 'new-image.png';
img.alt = '새 이미지 설명';
link.href = 'https://compit.kr';
// data 속성
const card = document.querySelector('.card');
card.dataset.id; // data-id 값 읽기
card.dataset.status = 'complete'; // data-status 설정
// 임의 속성
card.setAttribute('aria-label', '챌린지 카드');
card.getAttribute('aria-label');
card.removeAttribute('disabled');요소 생성하기
// 새 요소 만들기
const card = document.createElement('div');
card.className = 'card';
card.innerHTML = `
<h3>새 카드</h3>
<p>설명 텍스트</p>
`;
// DOM에 추가
document.querySelector('.card-grid').appendChild(card);
// 특정 위치에 삽입
const container = document.querySelector('.container');
container.insertBefore(card, container.firstChild); // 맨 앞에
// insertAdjacentHTML (편리한 방법)
container.insertAdjacentHTML('beforeend', `
<div class="card">
<h3>또 다른 카드</h3>
</div>
`);insertAdjacentHTML 위치 옵션:
'beforebegin'— 요소 앞'afterbegin'— 요소 내부 맨 앞'beforeend'— 요소 내부 맨 뒤'afterend'— 요소 뒤
요소 삭제하기
const card = document.querySelector('.card');
// 방법 1: remove()
card.remove();
// 방법 2: 부모에서 제거
card.parentElement.removeChild(card);이벤트 처리
addEventListener
const btn = document.querySelector('.btn');
btn.addEventListener('click', function(event) {
console.log('클릭됨!');
console.log(event.target); // 클릭된 요소
});
// 화살표 함수
btn.addEventListener('click', (e) => {
e.preventDefault(); // 기본 동작 방지
// ...
});자주 쓰는 이벤트
| 이벤트 | 설명 |
|---|---|
click | 클릭 |
dblclick | 더블클릭 |
mouseenter / mouseleave | 마우스 진입/이탈 |
keydown / keyup | 키보드 누름/뗌 |
input | 입력 값 변경 |
submit | 폼 제출 |
scroll | 스크롤 |
DOMContentLoaded | DOM 로딩 완료 |
이벤트 위임 (Event Delegation)
자식 요소가 많을 때 각각에 이벤트를 붙이지 않고, 부모에 하나만 붙이는 패턴입니다.
// 비효율: 각 카드마다 이벤트 추가
document.querySelectorAll('.card').forEach(card => {
card.addEventListener('click', handleClick);
});
// 효율: 부모에 하나만
document.querySelector('.card-grid').addEventListener('click', (e) => {
const card = e.target.closest('.card');
if (!card) return;
console.log('카드 클릭:', card.dataset.id);
});closest()는 자기 자신부터 부모를 거슬러 올라가며 선택자와 일치하는 요소를 찾습니다.
실전 예제: 할 일 목록
<div class="todo-app">
<form class="todo-form">
<input type="text" class="todo-input" placeholder="할 일 입력">
<button type="submit">추가</button>
</form>
<ul class="todo-list"></ul>
</div>const form = document.querySelector('.todo-form');
const input = document.querySelector('.todo-input');
const list = document.querySelector('.todo-list');
form.addEventListener('submit', (e) => {
e.preventDefault();
const text = input.value.trim();
if (!text) return;
const li = document.createElement('li');
li.className = 'todo-item';
li.innerHTML = `
<span>${text}</span>
<button class="delete-btn">삭제</button>
`;
list.appendChild(li);
input.value = '';
input.focus();
});
// 이벤트 위임으로 삭제 처리
list.addEventListener('click', (e) => {
if (e.target.classList.contains('delete-btn')) {
e.target.closest('.todo-item').remove();
}
});정리
DOM 조작 핵심:
- 선택:
querySelector/querySelectorAll - 내용 변경:
textContent(안전) /innerHTML(주의) - 스타일:
classList로 클래스 제어 - 속성:
dataset,setAttribute - 생성/삭제:
createElement,remove() - 이벤트:
addEventListener+ 이벤트 위임
React, Vue 같은 프레임워크도 내부적으로 DOM을 조작합니다. 기초를 이해하면 프레임워크 학습도 빨라집니다.