자바스크립트 DOM — HTML 요소를 골라서 바꾸는 방법

DOM은 브라우저가 HTML 문서를 읽어 만든 객체 형태의 지도입니다. 자바스크립트는 이 지도로 요소를 찾아내고 글자와 모양을 바꿉니다. 아래 실습용 문서를 놓고 요소를 고르는 법부터 새 요소를 붙이는 법까지 손대 봅니다.

<h1 id="pageTitle">오늘의 카드뉴스</h1>
<ul id="noteList">
  <li class="note">변수 정리</li>
  <li class="note done">함수 정리</li>
</ul>
<p id="status"></p>

DOM은 태그를 객체로 바꾼 나무입니다

브라우저는 HTML 파일 글자를 그대로 뿌리지 않습니다. 태그를 하나씩 해석해 부모와 자식 관계를 가진 나무 구조로 만듭니다. 위 문서라면 ul이 부모이고 li 둘이 자식입니다. 이 나무의 마디 하나하나가 자바스크립트로 다룰 수 있는 객체이며, 전체 문서를 가리키는 이름이 document입니다.

DOM을 고쳐도 HTML 파일 자체는 바뀌지 않아, 새로 고치면 원래 문서로 돌아갑니다. 부모·자식 관계가 낯설다면 HTML 문서 구조 편을 먼저 보십시오.

querySelector로 요소를 고릅니다

요소를 고르는 문법은 CSS 선택자와 같습니다. 아이디는 #, 클래스는 .입니다.

const title = document.querySelector("#pageTitle");   // 첫 번째 하나만
const firstNote = document.querySelector(".note");    // li 두 개 중 앞의 것
const allNotes = document.querySelectorAll(".note");  // 조건에 맞는 전부

allNotes.forEach(function (item, index) {
  console.log(index, item.textContent);
});

querySelector첫 번째 요소 하나를, querySelectorAll목록을 돌려줍니다. 여기서 흔한 실수가 나옵니다. 목록에 대고 allNotes.textContent = "..."처럼 쓰면 아무 일도 일어나지 않습니다. 목록은 요소가 아니므로 forEach로 하나씩 꺼내야 합니다. 조건에 맞는 요소가 없으면 null이 나와 “null의 속성을 읽을 수 없다”는 오류가 납니다.

textContent와 innerHTML의 차이

요소 안의 내용을 읽고 쓰는 방법은 두 가지이며, 성격이 완전히 다릅니다.

const title = document.querySelector("#pageTitle");

title.textContent = "이번 주 카드뉴스";        // 글자로만 취급
title.innerHTML = "이번 주 <strong>카드뉴스</strong>";   // 태그로 해석

textContent는 넣은 값을 무조건 글자로 봅니다. <strong>을 넣으면 굵은 글씨가 아니라 그 문자열이 그대로 보입니다. innerHTML은 반대로 문자열을 태그로 해석해 문서에 심습니다.

반드시 지켜야 할 원칙이 있습니다. 사용자가 입력한 값을 innerHTML에 그대로 넣지 마십시오. 입력창에 적어 넣은 태그가 실제 코드로 실행되어 페이지가 조작될 수 있습니다. 이런 공격을 XSS라고 부릅니다.

const userInput = document.querySelector("#nameInput").value;

document.querySelector("#status").innerHTML = userInput;    // 위험합니다
document.querySelector("#status").textContent = userInput;  // 안전합니다

기준은 간단합니다. 글자만 바꿀 때는 언제나 textContent, 내가 직접 작성한 안전한 태그를 넣을 때만 innerHTML입니다.

스타일과 클래스를 바꿉니다

모양을 바꾸는 방법도 두 가지입니다. style로 직접 지정하거나, 미리 만들어 둔 CSS 클래스를 붙였다 떼는 것입니다.

const firstNote = document.querySelector(".note");

// 1. 직접 지정 — CSS 속성 이름은 대시 대신 대문자로
firstNote.style.backgroundColor = "#f5f5f5";   // background-color 아님

// 2. 클래스 조작 — 권장 방식
firstNote.classList.add("done");        // 추가
firstNote.classList.remove("done");     // 제거
firstNote.classList.toggle("done");     // 있으면 빼고 없으면 추가

style 방식은 CSS 속성 이름의 대시를 없애고 다음 글자를 대문자로 바꿔야 합니다. style.background-color는 오류입니다. 실무에서는 색과 여백을 CSS 클래스로 정리해 두고 classList로 붙였다 떼는 편이 관리하기 쉽습니다. 참고로 className에 값을 넣으면 기존 클래스가 통째로 날아가므로 하나만 더할 때는 classList.add를 씁니다.

요소를 만들고 붙이고 지웁니다

목록에 항목을 추가하는 작업은 세 단계로 이루어집니다. 만들고, 내용을 채우고, 부모에 붙이는 것입니다.

const list = document.querySelector("#noteList");

const newNote = document.createElement("li");   // 1. 만들기
newNote.textContent = "DOM 정리";               // 2. 채우기
newNote.classList.add("note");
list.appendChild(newNote);                      // 3. 붙이기

list.prepend(newNote);   // 맨 앞에 넣기
newNote.remove();        // 지우기

createElement로 만든 요소는 아직 화면에 없습니다. appendChild로 부모에 붙이는 순간 나타납니다. 만들기만 하고 붙이지 않아 헤매는 일이 잦으니 세 단계를 세트로 기억해 두십시오.

핵심 정리

  • DOM — HTML을 해석해 만든 객체 나무이며 시작점은 document입니다.
  • 요소 선택querySelector는 하나, querySelectorAll은 목록을 돌려줍니다.
  • 내용 변경 — 글자는 textContent, 태그까지 넣을 때만 innerHTML입니다.
  • 보안 — 사용자 입력을 innerHTML에 넣으면 XSS 위험이 생깁니다.
  • 요소 추가createElement → 내용 채우기 → appendChild 세 단계입니다.

이제 이 조작을 사용자 행동과 연결할 차례입니다. 자바스크립트 이벤트 편에서 이어집니다.