모든 강의를 무료로 볼 수 있어요. 회원가입 없이도 학습 가능합니다.

16강 / 전체 22강

DOM이란

13분 읽기 조회 3

DOM 트리 구조와 노드 개념, querySelector/querySelectorAll로 요소 선택, HTMLElement의 주요 속성과 메서드, NodeList 순회까지 DOM 조작의 첫걸음을 완성합니다.

🎯 학습 목표

이 강을 마치면 다음을 할 수 있습니다.

  • DOM이 무엇인지, 브라우저가 HTML을 어떻게 트리 구조로 표현하는지 이해합니다.
  • getElementById, querySelector, querySelectorAll로 원하는 요소를 선택할 수 있습니다.
  • 선택한 요소의 기본 속성(id, className, tagName, textContent)을 읽을 수 있습니다.
  • NodeList를 배열처럼 순회할 수 있습니다.

🌳 DOM이란 무엇인가

이 섹션에서는 DOM의 정의와 브라우저가 HTML 문서를 어떻게 내부 구조로 표현하는지 살펴보겠습니다.

1강에서 자바스크립트가 웹 페이지에 동작을 불어넣는 언어라고 배웠습니다. 그렇다면 자바스크립트는 실제로 어떻게 웹 페이지를 조작할까요? 바로 DOM(Document Object Model, 문서 객체 모델)을 통해서입니다.

브라우저는 HTML 파일을 받으면 이를 그대로 화면에 그리는 것이 아닙니다. HTML을 파싱(분석)해서 트리(Tree) 구조의 객체로 변환합니다. 이 트리가 DOM입니다. 마치 회사 조직도처럼 부모-자식 관계로 연결된 계층 구조입니다.

// HTML 문서
// <html>
//   <head>
//     <title>내 페이지</title>
//   </head>
//   <body>
//     <h1 id="title">안녕하세요</h1>
//     <ul class="list">
//       <li>항목 1</li>
//       <li>항목 2</li>
//     </ul>
//   </body>
// </html>

// DOM 트리 구조:
// Document
//   └── html
//         ├── head
//         │     └── title ("내 페이지")
//         └── body
//               ├── h1 (id="title") ("안녕하세요")
//               └── ul (class="list")
//                     ├── li ("항목 1")
//                     └── li ("항목 2")

// 브라우저 콘솔에서 document 객체 확인
console.log(document);          // HTMLDocument
console.log(document.title);    // "내 페이지"
console.log(document.body);     // <body> 요소

DOM에서 각 HTML 태그는 노드(Node)라는 객체가 됩니다. 노드는 여러 종류가 있습니다. <div>, <p>처럼 태그에 해당하는 요소 노드(Element Node), 태그 안의 텍스트에 해당하는 텍스트 노드(Text Node), HTML 속성에 해당하는 속성 노드(Attribute Node)가 있습니다. 자바스크립트로 DOM을 조작한다는 것은 이 노드 객체들의 속성을 읽고 바꾸는 것입니다. 자바스크립트가 DOM을 변경하면 브라우저가 즉시 화면을 다시 그립니다.

🔍 요소 선택하기 — querySelector

이 섹션에서는 DOM에서 원하는 요소를 찾아 선택하는 다양한 방법을 살펴보겠습니다.

DOM을 조작하려면 먼저 대상 요소를 선택해야 합니다. 자바스크립트는 CSS 선택자를 그대로 사용하는 강력한 선택 API를 제공합니다.

// HTML:
// <div id="app">
//   <h1 class="title main">제목</h1>
//   <p class="desc">설명</p>
//   <ul>
//     <li data-id="1">항목 1</li>
//     <li data-id="2">항목 2</li>
//     <li data-id="3">항목 3</li>
//   </ul>
// </div>

// 1. getElementById — id로 하나 선택 (가장 빠름)
const app = document.getElementById("app");
console.log(app);  // <div id="app">

// 2. querySelector — CSS 선택자로 첫 번째 요소 선택
const title = document.querySelector(".title");    // 클래스
const desc  = document.querySelector("#app p");   // id 하위 p 태그
const firstLi = document.querySelector("li");     // 첫 번째 li
const dataEl  = document.querySelector("[data-id='2']");  // 속성 선택자

console.log(title);    // <h1 class="title main">
console.log(firstLi);  // <li data-id="1">

// 없으면 null 반환
const missing = document.querySelector(".nonexistent");
console.log(missing);  // null

// 3. querySelectorAll — CSS 선택자에 맞는 모든 요소
const allLis = document.querySelectorAll("li");
console.log(allLis);         // NodeList(3) [li, li, li]
console.log(allLis.length);  // 3

// 특정 요소 내에서만 검색 (범위 제한)
const ul = document.querySelector("ul");
const lisInUl = ul.querySelectorAll("li");  // ul 하위에서만
console.log(lisInUl.length);  // 3

querySelector와 querySelectorAll은 CSS에서 사용하는 선택자 문법을 그대로 사용합니다. 클래스는 .className, ID는 #id, 태그는 tagName, 복합 선택자는 .parent .child처럼 작성합니다. CSS를 이미 안다면 DOM 선택도 바로 익숙하게 사용할 수 있습니다. 선택 범위를 제한할 때는 document 대신 특정 요소에서 querySelector를 호출하면 해당 요소의 하위에서만 검색합니다.

📄 요소의 주요 속성

이 섹션에서는 선택한 요소 객체가 가진 주요 속성들을 살펴보겠습니다.

선택한 DOM 요소는 HTMLElement 객체입니다. 이 객체는 HTML 태그의 구조를 자바스크립트로 표현한 것으로, 태그 이름, 클래스, ID, 텍스트 내용 등 다양한 속성을 가집니다.

// HTML:
// <h1 id="main-title" class="title highlight" data-level="1">
//   환영합니다!
// </h1>

const el = document.querySelector("#main-title");

// 기본 식별 속성
console.log(el.tagName);    // "H1" (대문자)
console.log(el.id);         // "main-title"
console.log(el.className);  // "title highlight" (전체 클래스 문자열)

// 클래스 목록 — classList
console.log(el.classList);              // DOMTokenList ["title", "highlight"]
console.log(el.classList.contains("highlight"));  // true

// 텍스트 내용
console.log(el.textContent);  // "\n  환영합니다!\n" (공백 포함)
console.log(el.textContent.trim());  // "환영합니다!"

// HTML 속성 접근
console.log(el.getAttribute("data-level"));  // "1"

// 부모·자식 관계
console.log(el.parentElement);   // 부모 요소
console.log(el.children);        // 자식 요소 목록 (HTML 요소만)
console.log(el.nextElementSibling);   // 다음 형제 요소
console.log(el.previousElementSibling); // 이전 형제 요소

자주 헷갈리는 속성이 있습니다. textContent는 요소 안의 모든 텍스트(하위 요소 포함)를 순수 텍스트로 반환합니다. 17강에서 배울 innerHTML은 HTML 마크업 포함한 문자열을 반환합니다. className은 전체 클래스를 하나의 문자열로 반환하는 반면, classList는 클래스 목록을 배열처럼 다룰 수 있는 DOMTokenList를 반환합니다. classList에는 add, remove, toggle, contains 메서드가 있어 클래스를 더 편리하게 조작할 수 있습니다. 이 내용은 17강에서 자세히 다룹니다.

📋 NodeList 순회

이 섹션에서는 querySelectorAll이 반환하는 NodeList를 다루는 방법을 살펴보겠습니다.

querySelectorAll은 배열처럼 생겼지만 진짜 배열이 아닌 NodeList를 반환합니다. forEach는 사용할 수 있지만, map·filter·reduce 같은 배열 메서드는 없습니다. 필요하면 배열로 변환합니다.

const lis = document.querySelectorAll("li");

// forEach는 NodeList에서 직접 사용 가능
lis.forEach((li, index) => {
  console.log(`${index + 1}번째: ${li.textContent}`);
});

// for...of도 사용 가능
for (const li of lis) {
  console.log(li.textContent);
}

// 배열로 변환 — map·filter·reduce 사용 가능
const arr = Array.from(lis);
const texts = arr.map(li => li.textContent.trim());
console.log(texts);  // ["항목 1", "항목 2", "항목 3"]

// 또는 스프레드 연산자
const arr2 = [...lis];
const filtered = arr2.filter(li => li.getAttribute("data-id") > 1);
console.log(filtered.length);  // 2

// children은 HTMLCollection — 역시 배열 변환 후 사용
const ul = document.querySelector("ul");
console.log(ul.children);               // HTMLCollection
console.log([...ul.children].length);  // 3

NodeList와 HTMLCollection을 배열로 변환할 때 Array.from()이나 스프레드 연산자 [...nodelist]를 사용합니다. 이 두 방법은 동등하며, 변환 후에는 12~13강에서 배운 모든 배열 메서드를 사용할 수 있습니다. 한 가지 주의사항이 있습니다. NodeList는 정적(static)과 실시간(live) 두 가지가 있습니다. querySelectorAll은 정적 NodeList를 반환해 이후 DOM 변경이 반영되지 않습니다. getElementsByClassName 같은 구형 API는 실시간 HTMLCollection을 반환합니다.

⚠️ DOM 접근 전 주의사항

이 섹션에서는 DOM 스크립트 작성 시 반드시 알아야 할 타이밍 문제를 살펴보겠습니다.

자바스크립트로 DOM에 접근하려면 HTML 파싱이 완료된 이후여야 합니다. <head>에서 스크립트를 실행하면 아직 <body>가 파싱되기 전이라 querySelector가 null을 반환하는 문제가 생깁니다.

// 방법 1: <script> 태그를 </body> 바로 위에 배치 (전통적 방법)
// HTML 전체가 파싱된 후 스크립트 실행됨

// 방법 2: defer 속성 (권장)
// <script src="app.js" defer></script>
// HTML 파싱 완료 후, DOMContentLoaded 이후 실행

// 방법 3: DOMContentLoaded 이벤트
document.addEventListener("DOMContentLoaded", () => {
  // 이 안에서 DOM 접근 — HTML 완전히 파싱된 후 실행
  const title = document.querySelector("h1");
  console.log(title.textContent);
});

// 실수 예시
// <head> 안에서 실행하면
const el = document.querySelector("h1");  // null!
// null.textContent  → TypeError!

현대 개발에서는 <script> 태그에 defer 속성을 붙이는 것이 표준 방식입니다. defer는 HTML 파싱을 막지 않고 병렬로 스크립트를 다운로드한 뒤, 파싱 완료 후 실행합니다. React, Vue 같은 프레임워크는 이 문제를 빌드 도구가 자동으로 처리합니다. 순수 자바스크립트 학습 중에는 스크립트를 </body> 바로 위에 배치하거나 DOMContentLoaded 이벤트를 활용하세요.

📝 핵심 요약

16강에서 배운 내용을 정리해 보겠습니다.

개념설명
DOM브라우저가 HTML을 파싱해 만든 트리 구조의 객체 모델
documentDOM의 진입점, HTML 문서 전체를 나타내는 객체
getElementById(id)ID로 하나 선택 — 가장 빠름
querySelector(css)CSS 선택자로 첫 번째 요소 반환 (없으면 null)
querySelectorAll(css)CSS 선택자에 맞는 모든 요소 NodeList로 반환
NodeList 순회forEach/for...of 직접 사용, 배열 메서드 필요하면 Array.from()
타이밍스크립트를 </body> 위 또는 defer로 HTML 파싱 후 실행

다음 강인 17강 — DOM 내용·스타일 변경에서는 선택한 요소를 실제로 수정하는 방법을 배웁니다. textContent·innerHTML으로 내용을 바꾸고, style 속성과 classList로 스타일을 동적으로 변경합니다. 16강에서 요소를 선택하는 법을 배웠다면, 17강에서 그 요소를 원하는 대로 변환하는 법을 배웁니다!

관련 주제

  • DOM 트리 구조
  • querySelector
  • querySelectorAll
  • NodeList 순회
  • textContent className
  • 개발·프로그래밍
  • 개발·프로그래밍 강의
  • 자바스크립트 기초 30강 — 웹을 움직이는 언어
  • 무료강의
  • 무료 온라인 강의
  • NUGUNA
  • 누구나

📚 시리즈 전체 공유

자바스크립트 기초 30강 — 웹을 움직이는 언어

이 강의가 속한 시리즈는 총 22강, 모두 무료입니다. 처음부터 배우려는 동료에게 시리즈 전체를 알려 주세요.

댓글

0/1000

불러오는 중...