DOM


HTML 파일은 한 번 로드되고, 그 뒤로 페이지는 그대로 머물러 있습니다. 하지만 입력할 때마다 검색 결과를 제안해주는 검색창, 클릭하면 열리는 메뉴, 숫자가 올라가는 카운터 같은 것들은 HTML 파일 안에는 없습니다. 페이지가 로드된 뒤에 화면 위의 내용을 바꾸는 무언가가 있다면, 그것은 바로 JavaScript가 DOM을 통해 페이지에 손을 뻗은 것입니다. 이 장에서는 그 손길에 대해 다룹니다. JavaScript가 페이지의 부분들을 찾아서 바꾸고, 그 결과 독자의 눈앞에서 페이지가 업데이트되는 방식을 살펴봅니다.
DOM이란 무엇인가
브라우저가 페이지를 로드하면 HTML을 읽어서 그로부터 **객체들의 트리**를 만들어냅니다. 페이지 전체를 위한 객체 하나, <body>를 위한 객체 하나, 그리고 그 안의 제목, 문단, 버튼마다 각각 하나씩요. 이 트리가 바로 DOM(Document Object Model, 문서 객체 모델)입니다. JavaScript는 이 트리를 읽고 바꿀 수 있으며, 그렇게 하면 눈앞의 페이지가 곧바로 업데이트됩니다.
여기서 꼭 기억해야 할 점은, DOM이 곧 HTML 파일은 아니라는 것입니다. DOM은 브라우저의 메모리 안에 존재하는, 페이지의 살아있는 버전입니다. .html 파일은 처음 준비한 조리법이고, DOM은 브라우저가 실제로 내놓은 완성된 요리인데, JavaScript는 사람들이 그 요리를 먹고 있는 동안에도 접시 위 내용을 다시 배치할 수 있습니다.
DOM 안의 모든 요소는 객체에서 배운 것과 같은 종류의 객체이며, 읽고 바꿀 수 있는 속성을 가지고 있습니다. 요령은 바로 이것입니다. 페이지가 객체들로 이루어져 있고, 여러분은 이미 객체를 다루는 법을 알고 있으니, 페이지를 바꾸는 것은 곧 객체의 속성을 바꾸는 것입니다.
요소 선택하기
요소를 바꾸기 전에, 먼저 트리 안에서 그것을 찾아야 합니다. CSS와 같은 방식으로 셀렉터를 사용해 요소를 가리키면, 브라우저가 그에 맞는 객체를 돌려줍니다.
가장 많이 쓰는 것은 document.querySelector입니다. CSS 셀렉터, 즉 CSS에서 쓰는 것과 같은 패턴을 넘겨주면 처음으로 일치하는 요소를 반환합니다.
const card = document.querySelector(".card"); // class="card"인 첫 번째 요소
const total = document.querySelector("#total"); // id="total"인 요소.card는 클래스로 매칭하고, #total은 id로 매칭하며, "button"처럼 그냥 단어를 쓰면 태그 이름으로 매칭합니다. querySelector는 첫 번째로 일치하는 요소만 반환하고 거기서 멈춥니다. 일치하는 요소를 모두 원한다면 querySelectorAll을 쓰면 되는데, 이는 순회할 수 있는 목록을 반환합니다.
const cards = document.querySelectorAll(".card"); // class="card"인 모든 요소
console.log(cards.length); // 몇 개를 찾았는지일치하는 것이 없으면 querySelector는 null을 반환하는데, 이는 JavaScript가 "여기에는 아무것도 없다"고 말하는 방식입니다.
document.querySelector에 .card나 #total 같은 CSS 셀렉터를 넘기세요. 그러면 첫 번째로 일치하는 요소를 돌려줍니다. 전부를 목록으로 원한다면 querySelectorAll을 쓰세요. 일치하는 게 없으면 "여기에는 아무것도 없다"는 뜻의 null을 받게 됩니다. 요소 읽고 바꾸기
일단 요소를 손에 넣었다면, 다른 객체를 읽고 바꿀 때와 마찬가지로 그 속성을 통해 읽고 바꿉니다. 여기서부터 페이지가 실제로 움직이기 시작합니다.
가장 많이 쓰게 될 속성은 요소 안의 텍스트를 나타내는 textContent입니다. 읽을 수도, 설정할 수도 있습니다.
const total = document.querySelector("#total");
total.textContent = "42 items"; // 이제 페이지에 "42 items"가 보입니다무언가의 모양을 바꿀 때는 보통 클래스를 추가하거나 제거해서 CSS가 스타일을 처리하게 둡니다. classList에는 그럴 때 쓰는 메서드가 세 가지 있습니다.
const card = document.querySelector(".card");
card.classList.add("selected"); // 클래스 추가
card.classList.remove("selected"); // 클래스 제거
card.classList.toggle("selected"); // 없으면 추가하고, 있으면 제거.style을 통해 단일 스타일을 직접 설정할 수도 있고, setAttribute로 src나 href 같은 속성을 설정할 수도 있습니다.
card.style.color = "crimson"; // 인라인 스타일 하나
const avatar = document.querySelector(".avatar");
avatar.setAttribute("src", "jimin.jpg"); // 이미지 소스 설정classList를 먼저 쓰고, .style은 일회성 조정에만 쓰세요. 스타일링을 CSS에 몰아두면 한곳에서 관리할 수 있습니다.
textContent로 요소 안의 텍스트를 설정하고, classList.add, remove, toggle로 클래스를 추가하거나 제거해서 모양을 바꾸세요. 일회성 조정에는 .style을, 이미지 소스 같은 것에는 setAttribute를 쓰세요. 클래스에 의존하면 스타일링이 CSS 안에 그대로 남습니다. 요소 만들고 지우기
이미 존재하는 요소를 바꾸는 것만으로도 많은 것을 할 수 있지만, 때로는 아직 아무것도 없는 상황도 있습니다. 새로 추가하는 할 일 항목이나 도착하는 검색 결과처럼요. 이럴 때는 새 요소를 만들어 트리에 넣고, 오래된 요소는 빼내야 합니다.
document.createElement로 새 요소를 만들고, 내용을 채운 다음, append로 페이지에 추가합니다.
const list = document.querySelector(".todo-list");
const item = document.createElement("li"); // 새 <li>, 아직 페이지에는 없음
item.textContent = "오트밀크 사기"; // 텍스트 채우기
list.append(item); // 이제 목록 맨 끝에 나타남createElement로 만든 새 요소는 어딘가에 append하기 전까지는 메모리 안에만 존재합니다. 페이지에 나타나게 하는 것은 바로 그 단계입니다. 요소를 페이지에서 없애려면 그 요소에 remove를 호출하면 됩니다.
item.remove(); // 페이지에서 사라짐document.createElement로 새 요소를 만들고, textContent로 텍스트를 채우고, append로 페이지에 추가하세요. 새로 만든 요소는 append하기 전까지 메모리 안에만 있으므로, 그 단계가 바로 화면에 나타나게 만드는 단계입니다. 페이지에서 없애려면 그 요소에 remove를 호출하세요. DOM, 여기서부터는 어디로 가야 할까요
선택하고, 읽고, 바꾸고, 만들고, 지우는 것이 JavaScript가 페이지에 하는 일의 대부분을 차지하며, 그 모든 것은 밑바탕에서 보면 동일한 동작입니다: 트리에서 객체를 찾아 그 속성을 바꾸는 것입니다. DOM이 곧 객체들이기 때문에, 객체에서 배운 모든 것이 여기에도 그대로 적용되며, 이 장에서 다룬 작은 메서드 모음(querySelector, textContent, classList, createElement, append)이 도구상자의 대부분을 차지합니다.
지금까지 빠져 있는 것은 타이밍입니다. 여기 나온 예제는 모두 위에서 아래로 한 번만 실행되지만, 실제 페이지는 독자가 하는 행동, 클릭, 키 입력, 제출된 폼에 반응해서 바뀝니다. DOM 변경을 이런 순간들에 연결하는 것이 다음 장인 이벤트이며, 여기서 addEventListener는 "사용자가 이것을 했다"와 "그래서 페이지가 저것을 한다"를 이어줍니다. 선택하고 바꾸는 것에 이벤트에 반응하는 능력까지 더해지면, 이 안내서가 목표로 하는 인터랙티브한 페이지를 만들 수 있게 됩니다.

