16-1. 돔(DOM)의 기본 개념16-1-1. DOM의 역할과 기본 구조 16-2. 돔 조작과 변형16-2-1. 요소 선택과 변경16-2-2. 동적으로 요소 생성 및 제거16-2-3. DOM 노드 탐색16-2-4. 스타일 변경과 클래스 조작
16-1. 돔(DOM)의 기본 개념
16-1-1. DOM의 역할과 기본 구조
DOM(Document Object Model)은 문서객체모델로 간단히 말하면, 웹 문서(HTML, XML)를 제어하기 위한 인터페이스(API)이다. 자바스크립트 언어로 요소 내의 텍스트를 불러오거나, 스타일(CSS)을 동적으로 변경하는 등 웹 문서를 객체화하는 것을 말한다. 돔의 구성요소는 아래 그림과 같이 트리 형태 구조로 표현된다.

이 구조를 돔 트리, 각각의 요소를 노드라고 한다. html이 노드 트리의 최상위에 있고, root 노드라고 한다. 아래 코드를 통해 보면, 기본 구조를 확인할 수 있다.
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>test</title> </head> <body> <h1>hello world</h1> <p>안녕하세요.</p> </body> </html>
- 문서 노드(Document Node): 문서 전체를 나타내며, 돔 트리의 최상위에 위치하고, 최상위 노드를 root 노드라고 한다. html 태그를 나타낸다. head와 body 자식 노드가 있다.
- 요소 노드(Element Node): HTML 요소를 나타내며, 문서의 구조를 형성한다. head, title, h1, p 등이 요소 노드이다. 다른 요소를 자식으로 가질 수 있다.
- 텍스트 노드(Text Node): HTML 요소 안에 있는 텍스트를 나타낸다. <h1>hello world</h1>, <p>안녕하세요.</p> 요소 노드에 실제 텍스트가 텍스트 노드이다.
- 속성(Attribute): HTML 요소의 속성을 나타내며, 요소의 추가 정보를 제공한다. 예를 들어 img 요소의 src나 alt 속성을 말한다.
즉, 모든 html 요소는 노드라고 생각하면 된다. 모든 노드는 부모 노드와 자식 노드 간의 계층구조를 형성하고, 각 요소와 해당 요소의 속성을 객체로 표현한다.
이 객체는 프로퍼티나 메서드를 통해서 자바스크립트 언어를 사용하여 페이지 동작을 처리할 수 있다. 웹페이지에서 발생하는 이벤트(클릭, 입력, 마우스 이동) 등을 감지하고, 제어할 수 있다.
브라우저에서 제공하는 기능이고, API기 때문에 자바스크립트를 사용해서 제어할 수 있는 대상일 뿐이고, 다른 언어도 라이브러리 갖춰지면 조작할 수 있다. DOM은 이렇게 웹 페이지의 구조를 프로그래밍적으로 접근할 수 있게 해서, 웹 접근성을 개선하고 웹 표준을 준수하는 데 도움을 준다. 스크린 리더와 같은 보조 기술을 사용하는 사용자가 웹 페이지를 더 쉽게 이해하고 상호작용할 수 있도록 하기때문에 돔을 이해하고 잘 다뤄야 한다.
아래 내용부터 돔에 어떻게 접근하고, 처리하는지 핵심 내용을 다뤄본다.
16-2. 돔 조작과 변형
16-2-1. 요소 선택과 변경
DOM에서 각각의 요소를 선택하고, 조작하는 방법은 document 객체에 다양한 메서드, 프로퍼티를 통해서 접근할 수 있다. 각 요소를 선택하면 해당 요소 내용에 속성값 변경, 텍스트 내용 변경, 스타일 지정 및 변경 다양한 조작이 가능하다.
먼저 대표적으로 제일 많이 쓰이는 메서드는 document.querySelector() 가 있다.
<h1>hello</h1> <h2 class='title'>world</h2> <script> const h1 = document.querySelector('h1'); const h2 = document.querySelector('.title'); h1.innerHTML = '<p>bye</p>'; </script>
document.querySelector는 한가지 요소를 불러오고, 변수에 저장한다. 같은 요소가 많은 경우 첫 번째 요소를 불러온다. 요소를 불러올 때 class는 .(온점)을, id는 #(샵)을 요소 앞에 붙여준다. innerHTML 프로퍼티는 html 요소 자체를 선택하고, 추가할 때 주로 사용한다. h1 태그 내에 p 태그가 추가되고, 텍스트 내용이 bye로 변경되는 것이다. 이렇게 메서드와 프로퍼티를 사용하여 돔에 접근하고 속성 변경을 간단하게 조작할 수 있다.
querySelector()가 하나의 선택자(요소)만 불러올 수 있어, 일치하는 요소가 여러 개일 경우 사용하는 메서드는 document.querySelectorAll() 이 있다.
<p>안녕하세요.</p> <p>안녕하세요.</p> <p>안녕하세요.</p> <script> document.querySelectorAll('p').forEach(item => { item.textContent = '안녕히가세요'; }) </script>
querySelectorAll() 은 일치하는 모든 요소를 불러올 수 있다.
querySelectorAll()은 NodeList로 반환되며, 배열이 아닌 유사 배열 객체이다. 따라서 textContent를 직접 사용할 수 없고, forEach() 를 사용하여 querySelectorAll로 선택한 모든 <p> 요소에 대해 반복하고, 각 요소의 textContent를 변경해야 한다. textContent 프로퍼티는 요소의 내용을 변경할 때 사용한다. h1 태그 내에 텍스트를 ‘bye’로 변경한 것이다.
이외에도 id 값을 불러오는 document.getElementById 메서드와 class 값을 불러오는 document.getElementsByClassName 메서드가 있다.
<div id='wrap'></div> <h1 class='logo'></h1> <script> const div = document.getElementById('wrap'); const logo = document.getElementsByClassName('logo'); console.log(div) console.log(logo) // <div id='wrap'></div> // HTMLCollection [h1.logo] </script>
메서드 동작방식에 따른 차이로 결과가 다른 것을 볼 수 있다. getElementById 메서드는 문서 내 고유한 id를 가진 요소를 하나만 불러와서 <div>요소 자체가 출력 되는 것을 볼 수 있다.
반면 getElementsByClassName 메서드는 클래스이름과 일치하는 모든 요소를 선택하여 모든 요소를 배열형태의 HTMLCollection 으로 출력한다.
- 요소의 속성값을 변경,추가하는 메서드 setAttribute()
<input type='text'/> <script> const input = document.querySelector('input'); input.setAttribute('type', 'checkbox'); </script>
setAttribute(name, value) 지정된 요소의 속성값을 변경한다. 기존 요소의 속성이 없다면, 지정된 이름과 값으로 새 속성이 추가된다. type이 text에서 checkbox로 변경된다.
- 요소의 속성값을 가져오는 메서드 getAttribute()
<div id='wrap'></div> <script> const id = document.getElementById('wrap'); id.getAttribute('id'); // 'wrap' </script>
getAttribute('id') 로 쓰면 언뜻 getElementById() 메서드와 비슷해 보이지만, 요소의 속성값을 가져올 때 사용한다. 따라서 속성값 ‘wrap’만 출력하는 것을 볼 수 있다.
getElementById() 는 하나뿐인 id 요소를 직접 선택하는 것이다.
- 요소의 태그명을 불러오는 메서드 getElementsByTagName(tagname)
- 요소의 특정 속성을 변경하는 메서드 element.attribute = value
<img src='logo.jpg' alt='' /> <script> const image = document.getElementByTagName('img'); image.src = 'new_logo.png'; </script>
getElementsByTagName() 메서드는 태그 명으로 요소를 불러온다. 태그 명과 일치하는 모든 요소를 선택하기 때문에 잘 사용되지 않는다. 필요한 요소에 대한 선택자를 불러오려면 id나 class를 불러오는 메서드를 사용하는게 더 정확하고 안전하다.
image.src = 'new_logo.png' 요소의 특정 속성을 변경할 수 있어 해당 코드는 이미지요소의 ‘src’ 속성을 변경한다. 주로 클릭 이벤트, 애니메이션 효과를 만들 때 사용할 수 있다.
16-2-2. 동적으로 요소 생성 및 제거
- 동적으로 요소 생성
새로운 HTML 요소를 동적으로 생성하는 메서드 document.Element(tagName) @
위에 프로퍼티로 동적으로 생성할 수 있지만, 브라우저 호환성 측면에서 대부분 일반적인 경우는 document.Element() 를 사용한다.
const text = document.createElement('p');
생성할 html 요소 태그 명을 넣어주면 된다. 주로 사용자가 버튼을 클릭해서 새로운 아이템을 추가하거나, 입력에 따라 새로운 리스트를 만들 때 많이 사용된다.
- 텍스트 노드 생성
새로운 텍스트 노드를 동적으로 생성하는 메서드 document.createTextNode('추가하는 텍스트')
const text = document.createTextNode('hello world!');
document.createTextNode() 를 사용하면 텍스트 내용을 조작할 수 있어 입력 필드값을 바꾸고, 에러메시지를 만들 때 유용하게 사용할 수 있다.
- 특정 요소 자식으로 추가
부모노드를 통해 자식 노드를 추가하는 메서드 appendChild(newNode)
<ul></ul> <script> const ul = document.querySelector('ul'); const li = document.createElement('li'); ul.appendChild(li) </script>
메서드를 호출할 때 추가하는 노드를 인자로 전달하면, 이 노드가 특정 요소의 자식 노드로 추가된다. 위 예제는 HTML의 ul 요소를 불러오고, 동적으로 li 요소를 생성한후 appendChild() 로 ul의 자식으로 li를 추가한 것을 볼 수 있다. 이처럼 부모 노드의 자식 노드로 추가되고, 하나의 노드만 추가할 수 있다. 여러 요소를 동시에 추가하고 싶으면 append() 메서드를 사용해야 한다.
- 특정 노드를 추가하는 메서드 append()
하나 이상의 노드 또는 텍스트를 특정 요소 노드에 추가한다. 여러 요소를 동시에 추가할 수 있고, 요소의 순서를 지정할 수 있다.
const parent = document.getElementById('wrap'); const child1 = document.createElement('div'); const child2 = document.createElement('h1'); const text = document.createTextNode('hello world!'); parent.append(child1, child2, text);
- 동적으로 요소 제거
부모 노드를 통해 자식 노드를 제거하는 메서드 removeChild(child)
부모 노드에서 메서드를 호출하고, 인자로 자식노드를 지정하면 자식 노드가 제거된다.
<ul> <li></li> </ul> <script> const ul = document.querySelector('ul'); const li = document.querySelector('li'); ul.removeChild(li); </script>
HTML의 ul과 li 요소를 불러오고, removeChild() 로 ul 요소의 자식 요소인 li를 제거한다. 자식 요소를 직접 지정하지 않고, 특정 부모 노드에서 하나의 자식 노드를 제거할 때 사용된다.
- 특정 노드를 제거하는 메서드 remove()
해당 노드 자체에서 메서드를 호출하여 해당 노드를 제거한다. 노드가 여러 개일 경우 각각 remove() 메서드를 호출하면 된다.
const ul = document.querySelector('ul'); ul.remove();
16-2-3. DOM 노드 탐색
- 노드 사이의 관계

돔 트리의 노드는 root 노드 <html>제외하고, 계층구조로 각 하나의 부모와 여러 개의 자식을 가진다. 같은 부모 노드의 관계를 sibling이라고 한다. DOM 트리의 계층관계를 탐색하는 프로퍼티가 있고, 이 프로퍼티로 DOM 트리 각 노드를 탐색할 수 있다.
- 노드를 탐색하는 프로퍼티
- 단일 노드
프로퍼티 | 설명 |
parentNode | 요소의 부모 노드를 접근한다. |
childNodes | 요소의 모든 자식 노드를 접근한다. NodeList로 객체로 반환된다. |
firstChild | 자식 노드에서 첫번째 자식 노드를 접근한다. |
lastChild | 자식 노드에서 마지막 자식 노드를 접근한다. |
nextSibling | 부모 노드의 모든 자식 노드중 자신 다음에 있는 노드를 접근한다. |
previousSibling | 부모 노드의 모든 자식 노드중 자신 이전에 있는 노드를 접근한다. |
children | 요소의 자식 노드 목록에 접근한다. childNodes 와 다르게 텍스트 노드를 건너뛴다. |
자주 사용되는 parentNode 프로퍼티는 특정 노드의 부모를 찾기 유용하여 부모 요소의 내용을 변경하는 등 다양한 작업을 수행할 수 있다.
<div id='parent'> <div id='child'></div> </div> <script> const child = document.getElementById('child'); const parent = child.parendNode; </script>
DOM 탐색을 통해 웹의 구조를 이해하고, 원하는 요소에 접근하는 게 간편하고 효과적으로 제어할 수 있다.
그러나 DOM 노드 탐색은 성능에 영향을 미치기 때문에, 가능하면 최적화된 쿼리메서드 querySelector 또는 getElementById 를 사용하고, 반복적인 탐색은 주의해야 한다.
16-2-4. 스타일 변경과 클래스 조작
- DOM 요소의 CSS 스타일 프로퍼티를 변경하는 방법 element.style.property = value
‘property’는 스타일 속성의 이름이고, ‘value’는 속성에 적용할 값이다.
const div = document.querySelector('div'); div.style.backgroundColor = 'red';
div 요소를 불러오고, 백그라운드 스타일 프로퍼티를 변경한 것이다. style은 DOM 요소의 스타일 객체이며, ‘backgroundColor’는 스타일 속성의 이름으로 카멜 케이스로 작성해야 하고, ‘red’는 속성에 적용할 값이다.
동적으로 스타일을 변경해야 할 때 예를 들어, 버튼 클릭할 때 스타일을 변경하거나 애니메이션 효과를 주는 등 이벤트리스너 클릭 이벤트와 같이 쓰이고, 자주 사용된다.
- 요소의 새로운 클래스를 추가하는 메서드 element.classList.add(className)
<style> .active { color: red; } </style> <body> <button id='btn'>안녕하세요</button> <script> const btn = document.getElementById('btn'); btn.addEventListener('click', ()=> { btn.classList.add('active'); }); </script> </body>
element.classList 는 엘레먼트의 클래스 목록에 접근하는 간편한 방법으로, 읽기 전용 프로퍼티이다.
버튼 요소를 불러오고, 버튼을 클릭하면 ‘active’클래스가 추가되서 스타일 컬러 색상이 변경된다.
add() 메서드는 클래스를 추가하는 메서드로 클래스 이름을 인수로 받는다. 추가할 클래스가 여러 개라면 btn.classList.add('active','show','toggle') 이런 식으로 한꺼번에 여러 개를 호출하면 된다.
이벤트리스너 addEventListener 등록은 이벤트 목차에서 다룰 예정으로 클릭할 때 스타일 변경 등 DOM 요소의 클래스를 조작하는데 주로 같이 사용된다.
- 요소에서 클래스를 제거하는 메서드 element.classList.remove(className)
const btn = document.querySelector('.btn'); btn.addEventListener('click', ()=> { btn.classList.remove('btn'); });
‘btn’ 클래스 이름을 가진 요소를 불러오고, 요소를 클릭할 때 ‘btn’ 클래스 이름이 삭제된다. add() 메서드와 마찬가지로 삭제할 클래스가 여러 개라면 btn.classList.remove('btn','show','toggle') 한꺼번에 여러 개 호출이 가능하다.
- 요소에 클래스가 없으면 추가, 있으면 제거 하는 메서드 element.classList.toggle(className)
<div id='hello' class='toggle'>Hello</div> <div id='bye'>Bye</div> <script> const hello = document.getElementById('hello'); const bye = document.getElementById('bye'); hello.addEventListener('click', ()=>{ hello.classList.toggle('toggle'); }); bye.addEventListener('click', ()=>{ bye.classList.toggle('toggle'); }); </script>
각각의 div 요소를 불러와서, 요소를 클릭 할때 ‘toggle’클래스 이름이 있다면 삭제되고, 없다면 ‘toggle’클래스가 추가된다. ‘hello’는 이미 있기 때문에 제거되고, ‘bye’는 없으므로 클래스가 추가된다.