BioPlayground

🧬
목록으로

DOM이란 — 문서 객체 모델

DOM(Document Object Model)이 무엇인지 이해하고, JavaScript로 HTML 요소를 선택, 수정, 추가하는 방법을 배웁니다.

입문
|
8
|
검증 완료 (2026-07)
DOM문서 객체 모델querySelector이벤트 리스너노드 트리
진행률0/32 (0%)

DOM이란 — 문서 객체 모델

이 토픽을 마치면

DOM이 무엇인지 설명할 수 있고, JavaScript로 HTML 요소를 선택하고, 내용을 바꾸고, 이벤트를 붙이는 기본 작업을 할 수 있습니다.


HTML은 텍스트, DOM은 객체

브라우저가 HTML 파일을 받으면 텍스트를 그대로 화면에 그리는 것이 아닙니다. HTML 텍스트를 파싱해서 메모리에 트리 구조의 객체를 만듭니다. 이 객체 트리가 DOM(Document Object Model)입니다.

html
<html>
  <body>
    <h1>안녕하세요</h1>
    <p>반갑습니다</p>
  </body>
</html>
text
document
└── html
    └── body
        ├── h1 → "안녕하세요"
        └── p  → "반갑습니다"

모든 HTML 태그, 텍스트, 속성이 이 트리의 노드가 됩니다. JavaScript는 이 트리를 통해 HTML을 읽고, 수정하고, 삭제할 수 있습니다.


요소 선택하기

DOM을 조작하려면 먼저 원하는 요소를 선택해야 합니다.

javascript
// 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로 선택합니다.

javascript
// 조합도 가능
const activeItem = document.querySelector('ul.menu > li.active');

내용 읽기와 수정하기

javascript
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를 사용합니다.


스타일과 속성 변경

javascript
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에 분리하는 원칙입니다.

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"

요소 생성과 추가

javascript
// 새 요소 만들기
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]);  // 특정 위치에 삽입
javascript
// 여러 요소를 한 번에 추가할 때
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 접근은 비용이 큰 작업이므로 횟수를 줄이는 것이 성능에 유리합니다.


이벤트 — 사용자 동작에 반응하기

javascript
const button = document.querySelector('#submit');

button.addEventListener('click', function(event) {
  console.log('버튼이 클릭됨');
  console.log(event.target);  // 클릭된 요소
});

addEventListener는 세 부분으로 구성됩니다:

  1. 이벤트 이름: 'click', 'input', 'submit', 'keydown'
  2. 콜백 함수: 이벤트가 발생하면 실행할 함수
  3. event 객체: 이벤트에 대한 정보 (어떤 키를 눌렀는지, 마우스 좌표 등)
javascript
// 입력 필드 실시간 반응
const input = document.querySelector('#search');
input.addEventListener('input', function(e) {
  console.log('현재 입력:', e.target.value);
});

이벤트 위임

버튼 100개에 각각 이벤트를 거는 대신, 부모에 하나만 걸 수 있습니다:

javascript
// ❌ 비효율적 — 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 보안

javascript
const userInput = '<script>alert("해킹")</script>';

element.innerHTML = userInput;    // ❌ 위험 — XSS 공격 가능
element.textContent = userInput;  // ✅ 안전 — 태그가 텍스트로 표시됨

사용자 입력을 innerHTML로 넣으면 악성 스크립트가 실행될 수 있습니다. 사용자 입력에는 반드시 textContent를 사용합니다. HTML 구조를 동적으로 만들어야 할 때는 createElement로 조립하는 것이 안전합니다.


DOM과 프레임워크

현대 프론트엔드 프레임워크(React, Vue, Svelte)를 쓰면 DOM을 직접 조작하지 않습니다. 프레임워크가 가상 DOM(Virtual DOM)이나 컴파일러를 통해 대신 처리합니다:

text
직접 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이 어떻게 작동하는지 이해하고 있으면 디버깅과 최적화에 큰 도움이 됩니다.