icon안동민 개발노트

DOM 조작과 이벤트 처리 기초


 DOM(Document Object Model)은 HTML 문서의 프로그래밍 인터페이스로, 웹 페이지의 내용, 구조, 스타일을 자바스크립트로 동적으로 변경할 수 있게 해줍니다.

 이 절에서는 DOM 조작과 이벤트 처리의 기본 개념과 기법을 살펴보겠습니다.

DOM의 개념과 구조

 DOM은 문서를 논리 트리로 표현합니다.

 각 노드는 문서의 일부를 나타내며, 이 트리 구조를 통해 노드를 생성, 변경, 삭제할 수 있습니다.

DOM 요소 선택

  1. getElementById : ID로 단일 요소 선택
let element = document.getElementById('myId');
  1. querySelector : CSS 선택자로 첫 번째 일치 요소 선택
let element = document.querySelector('.myClass');
  1. querySelectorAll : CSS 선택자로 일치하는 모든 요소 선택
let elements = document.querySelectorAll('p');

DOM 조작

  1. 요소 생성
let newDiv = document.createElement('div');
  1. 텍스트 노드 생성
let textNode = document.createTextNode('Hello, World!');
newDiv.appendChild(textNode);
  1. 요소 추가
document.body.appendChild(newDiv);
  1. 요소 제거
let oldElement = document.getElementById('oldElement');
oldElement.parentNode.removeChild(oldElement);
  1. 속성 조작
element.setAttribute('class', 'highlight');
let classValue = element.getAttribute('class');
  1. 스타일 변경
element.style.color = 'red';
element.style.backgroundColor = '#f0f0f0';

이벤트 처리

 이벤트는 사용자 동작이나 문서의 상태 변화 등을 감지하는 메커니즘입니다.

 이벤트 리스너 추가

element.addEventListener('click', function(event) {
    console.log('Element clicked!');
});

 이벤트 리스너 제거

function handleClick(event) {
    console.log('Clicked!');
}
 
element.addEventListener('click', handleClick);
// 나중에 제거
element.removeEventListener('click', handleClick);

 주요 이벤트 타입

  • click : 요소 클릭 시
  • submit : 폼 제출 시
  • load : 페이지나 이미지 로드 완료 시
  • keydown, keyup : 키보드 키 누름/뗌
  • mouseover, mouseout : 마우스 진입/이탈

 이벤트 객체

 이벤트 핸들러는 이벤트 객체를 받아 추가 정보를 얻을 수 있습니다.

element.addEventListener('click', function(event) {
    console.log('Clicked at: ' + event.clientX + ', ' + event.clientY);
});

이벤트 전파

 이벤트는 DOM 트리를 따라 전파됩니다.

  1. 캡처링 단계 : 최상위 요소에서 타겟 요소로
  2. 타겟 단계 : 이벤트가 타겟 요소에 도달
  3. 버블링 단계 : 타겟 요소에서 최상위 요소로
element.addEventListener('click', function(event) {
    console.log('Captured!');
}, true); // 캡처링 단계에서 이벤트 감지
 
element.addEventListener('click', function(event) {
    console.log('Bubbled!');
}); // 기본적으로 버블링 단계에서 이벤트 감지

이벤트 위임

 부모 요소에 이벤트 리스너를 추가하여 자식 요소들의 이벤트를 효율적으로 처리할 수 있습니다.

document.getElementById('parent-list').addEventListener('click', function(e) {
    if(e.target && e.target.nodeName == "LI") {
        console.log("List item ", e.target.id, " was clicked!");
    }
});

실제 예제 : 탭 인터페이스 구현

<div id="tab-container">
    <div class="tab" data-tab="tab1">Tab 1</div>
    <div class="tab" data-tab="tab2">Tab 2</div>
    <div class="tab" data-tab="tab3">Tab 3</div>
</div>
<div id="content-container">
    <div id="tab1" class="content">Content 1</div>
    <div id="tab2" class="content">Content 2</div>
    <div id="tab3" class="content">Content 3</div>
</div>
document.getElementById('tab-container').addEventListener('click', function(e) {
    if(e.target && e.target.classList.contains('tab')) {
        // 모든 탭과 콘텐츠 비활성화
        document.querySelectorAll('.tab, .content').forEach(el => el.classList.remove('active'));
        
        // 클릭된 탭과 해당 콘텐츠 활성화
        e.target.classList.add('active');
        document.getElementById(e.target.getAttribute('data-tab')).classList.add('active');
    }
});

 이 예제는 이벤트 위임을 사용하여 탭 인터페이스를 구현합니다.

 각 탭을 클릭하면 해당 콘텐츠가 표시됩니다.

성능 고려사항

  1. DOM 조작 최소화 : DOM 조작은 비용이 많이 듭니다. 가능한 한 조작을 그룹화하고 최소화하세요.
  2. DocumentFragment 사용 : 여러 요소를 추가할 때는 DocumentFragment를 사용하여 한 번에 추가하세요.
let fragment = document.createDocumentFragment();
for(let i = 0; i < 1000; i++) {
    let newElement = document.createElement('div');
    fragment.appendChild(newElement);
}
document.body.appendChild(fragment);
  1. 이벤트 위임 활용 : 가능한 경우 개별 요소 대신 부모 요소에 이벤트 리스너를 추가하세요.
  2. 리플로우 최소화 : 스타일 변경이 리플로우를 트리거할 수 있습니다. 변경사항을 그룹화하고 requestAnimationFrame을 사용하여 최적화하세요.
requestAnimationFrame(() => {
    element.style.width = '100px';
    element.style.height = '100px';
});
  1. 캐싱 : 자주 사용하는 DOM 요소는 변수에 저장하여 재사용하세요.
let myElement = document.getElementById('myElement');
// myElement를 반복해서 사용

 DOM 조작과 이벤트 처리는 동적 웹 페이지 생성의 핵심입니다. 효과적인 DOM 조작을 통해 사용자 인터페이스를 동적으로 업데이트하고, 이벤트 처리를 통해 사용자 상호작용에 응답할 수 있습니다.

 DOM API를 사용할 때는 성능에 주의를 기울여야 합니다. DOM 조작은 비용이 많이 드는 작업이므로, 필요한 경우에만 최소한으로 사용해야 합니다. 가능한 한 조작을 그룹화하고, DocumentFragment를 사용하여 여러 변경사항을 한 번에 적용하는 것이 좋습니다.

 이벤트 처리에서는 이벤트 위임 기법을 활용하면 코드를 더 효율적으로 만들 수 있습니다. 특히 동적으로 생성되는 요소가 많은 경우에 유용합니다.

 마지막으로, 현대 웹 개발에서는 React, Vue, Angular 같은 프레임워크를 사용하여 DOM 조작을 추상화하는 경우가 많습니다. 이러한 프레임워크들은 가상 DOM을 사용하여 성능을 최적화하고 개발자가 더 선언적인 방식으로 UI를 구현할 수 있게 해줍니다. 그러나 이러한 프레임워크의 기반에는 여전히 순수한 DOM 조작과 이벤트 처리 기술이 있으므로, 이에 대한 깊은 이해는 여전히 중요합니다.