Daily CS) DOM

goldenGlow_21·2024년 12월 15일
0

Daily CS

목록 보기
21/50

DOM

Document Object Model

웹 개발을 한다면 반드시라고 해도 좋을 정도로 만나게 되는 DOM! 오늘은 이 친구에 대해서 알아보자.


1. DOM의 개요

1.1 DOM의 정의와 역할

DOM(Document Object Model) 은 HTML, XML 문서를 객체로 표현한 계층적 구조를 말하며, 개발자가 문서의 내용과 구조를 동적으로 접근하고 조작할 수 있도록 하는 표준화된 프로그래밍 인터페이스이다. 웹 브라우저는 HTML 문서를 읽고, 이를 기반으로 DOM 트리를 생성하며, DOM은 웹 페이지의 시각적 출력과 사용자 상호작용을 연결하는 중요한 역할을 한다.

DOM의 역할

  1. 구조화된 문서 표현: DOM은 HTML 문서를 트리 구조로 변환하여 각 요소와 그 속성을 프로그래밍적으로 접근 가능하게 한다.
  2. 조작 가능성: DOM을 통해 HTML 요소를 추가, 수정, 삭제하거나 속성을 변경할 수 있다.
  3. 상호작용 관리: DOM은 JavaScript와 같은 언어를 통해 사용자의 입력(클릭, 키 입력 등)과 같은 이벤트를 처리한다.
  4. 표준화된 인터페이스 제공: W3C(World Wide Web Consortium)와 WHATWG(Web Hypertext Application Technology Working Group)에서 DOM 표준을 정의하며, 모든 주요 브라우저가 이를 준수한다.

1.2 DOM의 구조와 구성 요소 (노드, 요소, 속성 등)

DOM은 계층적 트리 구조로 구성되며, 문서의 각 부분은 트리의 노드로 표현된다.

DOM 트리 구성 요소

  1. 노드(Node)

DOM에서 모든 요소(HTML 태그), 텍스트, 속성은 각각의 노드로 표현된다.

  • 문서 노드(Document Node): DOM 트리의 최상위 노드. 전체 문서를 대표한다.
  • 요소 노드(Element Node): HTML 태그를 나타냄.
  • 텍스트 노드(Text Node): HTML 태그 내 텍스트 콘텐츠를 나타냄.
  • 속성 노드(Attribute Node): HTML 태그의 속성을 나타냄.
  1. 요소(Element)

HTML 태그를 나타내는 DOM의 기본 단위다. 각 요소는 자식 노드와 속성을 포함할 수 있다.

<div id="example">Hello, DOM!</div>
  • div: 요소 노드.
  • id="example": 속성 노드.
  • "Hello, DOM!": 텍스트 노드.
  1. 속성(Attribute)

요소가 가진 추가적인 정보를 나타냄. JavaScript를 사용하여 속성을 읽거나 수정할 수 있다.

const element = document.getElementById("example");
console.log(element.getAttribute("id")); // "example"
element.setAttribute("class", "highlight");

DOM 트리 예제

다음의 HTML 문서를 DOM 트리로 표현하면:

<!DOCTYPE html>
<html>
  <head>
    <title>Example</title>
  </head>
  <body>
    <h1>Hello World</h1>
    <p>This is an example paragraph.</p>
  </body>
</html>
Document
├── html
    ├── head
    │   └── title: "Example"
    ├── body
        ├── h1: "Hello World"
        └── p: "This is an example paragraph."

1.3 DOM과 HTML/CSS/JavaScript의 관계

DOM은 HTML, CSS, JavaScript를 상호작용하도록 연결하는 다리 역할을 한다. DOM은 이러한 세 요소를 연결하여 웹 페이지의 동적이고 상호작용적인 특성을 가능하게 한다.

1. HTML과 DOM

HTML 문서는 웹 페이지의 기본 구조와 내용을 정의하며, 브라우저는 이를 기반으로 DOM 트리를 생성한다. DOM은 HTML 문서를 조작할 수 있도록 인터페이스를 제공한다.

  • 예시: HTML의 특정 요소를 DOM을 통해 선택하고 변경
<p id="demo">Original Text</p>
<script>
  const paragraph = document.getElementById("demo");
  paragraph.textContent = "Modified Text"; // DOM을 통해 텍스트 변경
</script>

2. CSS와 DOM

CSS는 DOM의 요소에 스타일을 적용한다. DOM을 사용하여 CSS 클래스나 인라인 스타일을 동적으로 변경할 수 있다.

  • 예시
<div id="box" style="width:100px; height:100px; background-color:red;"></div>
<script>
  const box = document.getElementById("box");
  box.style.backgroundColor = "blue"; // CSS 스타일 변경
</script>

3. JavaScript와 DOM

JavaScript는 DOM을 조작하는 주요 도구다. JavaScript는 DOM을 통해 페이지의 콘텐츠를 동적으로 수정하거나 이벤트를 처리할 수 있다.

  • 예시: 버튼 클릭 이벤트 처리
<button id="btn">Click Me</button>
<script>
  const button = document.getElementById("btn");
  button.addEventListener("click", () => {
    alert("Button Clicked!");
  });
</script>

정리: DOM과의 관계

  1. HTML: 구조 정의
  2. CSS: 스타일 적용
  3. JavaScript: DOM 조작 및 동작 구현

2. DOM의 작동 원리

2.1 DOM 트리 생성 과정 (HTML 파싱, DOM 트리 빌드)

DOM 트리는 브라우저가 HTML 문서를 읽고 해석한 결과로 생성된다. 이 과정은 웹 페이지가 화면에 렌더링되는 핵심 단계다.

1. HTML 파싱(Parsing)

브라우저는 서버에서 HTML 문서를 수신한 뒤 이를 파싱(Parsing) 하여 DOM 트리를 생성한다.

  • HTML 읽기: 브라우저는 HTML 문서를 위에서 아래로 순차적으로 읽는다.
  • 토큰화(Tokenization): HTML 태그를 개별적인 토큰(예: <html>, <body> 등)으로 분리한다.
  • 노드 생성(Node Creation): 각 토큰을 기반으로 DOM 트리의 노드(문서, 요소, 텍스트 등)를 생성한다.

2. DOM 트리 빌드(Building)

HTML 문서를 기반으로 DOM 트리를 구축한다.

  1. 최상위 노드(Document): 문서를 대표하는 최상위 노드
  2. HTML 요소(Element Node): HTML 태그는 DOM 트리의 요소 노드로 추가
  3. 텍스트 노드(Text Node): 태그 내부의 콘텐츠는 텍스트 노드로 추가

3. 렌더링과 CSSOM 통합

브라우저는 DOM 트리와 CSSOM(CSS Object Model)을 결합하여 렌더 트리를 생성한 후, 이를 화면에 그린다. 이 과정은 브라우저가 웹 페이지의 구조와 콘텐츠를 이해하고 화면에 렌더링할 준비를 완료하는 중요한 단계다.

  • CSSOM: CSS 규칙을 파싱한 트리 구조.
  • 렌더 트리: DOM과 CSSOM을 조합하여 시각적 정보를 나타내는 구조.

예시

<!DOCTYPE html>
<html>
  <head>
    <title>Example</title>
  </head>
  <body>
    <h1>Hello, DOM!</h1>
  </body>
</html>
Document
└── html
    ├── head
    │   └── title: "Example"
    └── body
        └── h1: "Hello, DOM!"

2.2 DOM의 탐색과 조작

DOM은 트리 구조로 이루어져 있으며, JavaScript를 사용해 각 노드를 탐색하고 조작할 수 있다.

1. DOM 탐색

DOM 노드에 접근하기 위해 브라우저는 다양한 메서드를 제공한다.

  • getElementById: 특정 ID를 가진 요소를 선택
const element = document.getElementById("example");
console.log(element.textContent);
  • querySelector / querySelectorAll: CSS 선택자를 사용하여 요소를 선택
const heading = document.querySelector("h1");
const paragraphs = document.querySelectorAll("p");
  • 트리 탐색 속성
    - parentNode: 부모 노드
    - childNodes: 자식 노드 목록
    - nextSibling / previousSibling: 형제 노드
const parent = element.parentNode;
const children = parent.childNodes;

2. DOM 조작

DOM 노드를 동적으로 수정하여 페이지의 콘텐츠와 구조를 변경할 수 있다. DOM 탐색과 조작은 웹 페이지의 동적 콘텐츠를 구성하고 사용자 상호작용을 처리하는 핵심 도구다.

  • 내용 수정
element.textContent = "Updated Text"; // 텍스트 내용 변경
element.innerHTML = "<strong>Bold Text</strong>"; // HTML 내용 변경
  • 노드 추가
const newElement = document.createElement("p");
newElement.textContent = "New Paragraph";
document.body.appendChild(newElement);
  • 노드 삭제
element.remove(); // 요소 삭제

2.3 DOM 이벤트의 작동 방식

DOM 이벤트는 사용자의 행동(클릭, 입력 등)에 응답하기 위한 메커니즘이다. 또한 DOM 이벤트는 웹 페이지에서 사용자와 상호작용하는 핵심 기술로, 적절히 활용하면 동적인 사용자 경험을 구현할 수 있다.

1. 이벤트의 기본 개념

이벤트(Event)는 사용자의 특정 행동(예: 클릭, 키 입력) 또는 시스템 상태의 변화를 나타낸다. DOM에서는 이러한 이벤트가 발생했을 때 이벤트 핸들러(Event Handler) 를 통해 동작을 정의한다.

  • 이벤트 등록
const button = document.getElementById("btn");
button.addEventListener("click", () => {
  alert("Button clicked!");
});

2. 이벤트 흐름(Event Flow)

DOM 이벤트는 특정 요소에서 발생한 후, DOM 트리를 따라 흐른다.

  1. 캡처링 단계(Capturing Phase): 이벤트가 최상위 요소에서 목표 요소까지 내려간다.
  2. 목표 단계(Target Phase): 이벤트가 목표 요소에서 실행된다.
  3. 버블링 단계(Bubbling Phase): 이벤트가 목표 요소에서 최상위 요소로 전파된다.
  • 예시
<div id="parent">
  <button id="child">Click Me</button>
</div>
<script>
  document.getElementById("parent").addEventListener("click", () => {
    console.log("Parent clicked!");
  });

  document.getElementById("child").addEventListener("click", () => {
    console.log("Child clicked!");
  });
</script>
  • 결과
  1. "Child clicked!"
  2. "Parent clicked!" (버블링 단계로 전파)

3. 이벤트 위임(Event Delegation)

이벤트 위임은 부모 요소에 이벤트 핸들러를 등록하여, 자식 요소의 이벤트를 처리하는 기법이다.

  • 장점: 많은 자식 요소에 각각 이벤트를 등록할 필요가 없어 성능이 향상된다.

  • 예시

document.getElementById("parent").addEventListener("click", (event) => {
  if (event.target.tagName === "BUTTON") {
    console.log("Button clicked!");
  }
});

3. DOM 조작의 기본 방법

3.1 JavaScript로 DOM 선택하기 (querySelector, getElementById 등)

DOM 조작의 첫 단계는 문서의 특정 요소를 선택하는 것이다. JavaScript는 다양한 메서드를 제공하여 DOM 요소를 선택할 수 있다. 이 메서드들은 필요에 따라 사용되며, querySelector는 특히 유연성과 간결성 때문에 선호된다고 한다.

1. getElementById

getElementById는 요소의 고유 id를 기반으로 선택한다.

  • 장점: 빠르고 간단하며, id는 문서에서 유일하므로 단일 요소를 정확히 선택할 수 있다.

  • 예시

const element = document.getElementById("example");
console.log(element.textContent); // 해당 요소의 텍스트 출력

2. querySelector / querySelectorAll

CSS 선택자를 사용하여 요소를 선택한다.

  • querySelector: 첫 번째로 일치하는 요소를 반환

  • querySelectorAll: 모든 일치하는 요소를 NodeList로 반환

  • 예시

const heading = document.querySelector("h1"); // 첫 번째 <h1> 선택
const items = document.querySelectorAll(".item"); // 클래스가 'item'인 모든 요소 선택
items.forEach((item) => console.log(item.textContent)); // 각 요소의 텍스트 출력

3. 기타 DOM 선택 메서드

  • getElementsByClassName: 특정 클래스를 가진 모든 요소를 선택

  • getElementsByTagName: 특정 태그 이름을 가진 모든 요소를 선택

  • 예시

const paragraphs = document.getElementsByTagName("p"); // 모든 <p> 요소 선택
console.log(paragraphs[0].textContent); // 첫 번째 <p> 요소의 텍스트 출력

3.2 DOM 요소 추가, 수정, 삭제

DOM 요소를 동적으로 추가, 수정, 삭제하면 사용자 경험을 개선하는 인터랙티브한 웹 페이지를 구현할 수 있다. DOM 요소 추가, 수정, 삭제는 웹 페이지의 내용을 동적으로 변화시켜, 사용자의 상호작용에 즉각적으로 응답할 수 있게 한다.

1. DOM 요소 추가

  • createElement: 새로운 요소를 생성

  • appendChild / append: 새 요소를 부모 요소에 추가

  • 예시

const newElement = document.createElement("p"); // <p> 요소 생성
newElement.textContent = "This is a new paragraph."; // 텍스트 추가
document.body.appendChild(newElement); // <body>에 추가
  • insertBefore: 특정 위치에 요소를 삽입
const referenceNode = document.querySelector("h1"); // 참조 노드
document.body.insertBefore(newElement, referenceNode); // <h1> 앞에 추가

2. DOM 요소 수정

  • 텍스트와 HTML 수정
    - textContent: 텍스트만 수정
    - innerHTML: HTML 콘텐츠를 수정
const element = document.getElementById("example");
element.textContent = "Updated Text"; // 텍스트 수정
element.innerHTML = "<strong>Bold Text</strong>"; // HTML 수정
  • 속성 수정
    - setAttribute: 속성을 추가하거나 수정
    - removeAttribute: 속성을 제거
element.setAttribute("class", "highlight"); // 클래스 추가
element.removeAttribute("id"); // ID 제거

3. DOM 요소 삭제

  • remove: 요소를 삭제

  • removeChild: 부모 요소에서 특정 자식 요소를 제거

  • 예시

const parent = document.getElementById("parent");
const child = document.getElementById("child");
parent.removeChild(child); // 부모 요소에서 자식 요소 제거

3.3 클래스와 스타일 변경

CSS 클래스와 스타일 속성을 조작하면 DOM 요소의 외형과 레이아웃을 동적으로 변경할 수 있다. CSS 클래스와 스타일 변경은 동적 UI와 사용자 경험 개선의 중요한 요소다. 특히 classList는 반복적으로 사용되는 스타일 변경 작업을 효율적으로 처리할 수 있도록 한다.

1. 클래스 변경

JavaScript는 classList API를 사용해 CSS 클래스를 추가, 제거, 토글할 수 있다.

  • add / remove / toggle / contains
const element = document.getElementById("box");
element.classList.add("highlight"); // 클래스 추가
element.classList.remove("highlight"); // 클래스 제거
element.classList.toggle("active"); // 클래스 추가 또는 제거 (토글)
console.log(element.classList.contains("active")); // 클래스 포함 여부 확인

2. 인라인 스타일 변경

style 속성을 사용하면 CSS 속성을 직접 설정할 수 있다.

  • 예시
const element = document.getElementById("box");
element.style.backgroundColor = "blue"; // 배경색 변경
element.style.border = "2px solid black"; // 테두리 추가

3. CSS 변수 변경

CSS 변수는 JavaScript를 통해 동적으로 조작할 수 있다.

  • 예시
const root = document.documentElement; // :root 선택
root.style.setProperty("--main-color", "green"); // CSS 변수 수정

4. DOM 이벤트 처리

4.1 이벤트 리스너와 핸들러

DOM 이벤트는 사용자 상호작용(클릭, 입력, 마우스 이동 등)에 반응하기 위해 사용된다. 이벤트 리스너는 특정 요소에 이벤트를 감지하고 처리하기 위한 코드를 정의하며, 이벤트 핸들러는 이벤트가 발생했을 때 실행되는 함수를 의미한다. 특히 이벤트 리스너는 웹 페이지에서 동적 상호작용을 구현하는 기본 도구이며, 다양한 이벤트 유형에 반응할 수 있도록 유연하게 설계된다.

1. 이벤트 리스너 추가하기

JavaScript에서 addEventListener 메서드를 사용하여 이벤트 리스너를 추가할 수 있다.

  • 구문
element.addEventListener(eventType, handler, options);
  • eventType: 감지할 이벤트 유형(예: "click", "keydown")

  • handler: 이벤트가 발생했을 때 실행될 함수

  • options: 이벤트 동작을 제어하는 옵션 객체(예: capture: true)

  • 예시: 버튼 클릭 이벤트 처리

const button = document.getElementById("myButton");
button.addEventListener("click", () => {
  alert("Button was clicked!");
});

2. 이벤트 리스너 제거하기

removeEventListener를 사용하여 이벤트 리스너를 제거할 수 있다.

  • 예시
function handleClick() {
  console.log("Button clicked!");
}
button.addEventListener("click", handleClick);
button.removeEventListener("click", handleClick); // 리스너 제거

3. 한 번만 실행되는 이벤트 리스너

once: true 옵션을 사용하면 이벤트가 한 번만 실행되도록 설정할 수 있다.

  • 예시
button.addEventListener(
  "click",
  () => {
    console.log("This will only run once.");
  },
  { once: true }
);

4.2 이벤트 버블링과 캡처링

DOM 이벤트는 특정 요소에서 발생한 뒤 트리 구조를 따라 전파된다. 이 과정을 이해하면 이벤트 처리의 효율성을 높일 수 있다.

1. 이벤트 흐름(Event Flow)

이벤트 흐름은 세 가지 단계로 나뉜다:

  1. 캡처링 단계(Capturing Phase): 이벤트가 최상위 노드에서 시작하여 목표 요소로 내려간다.
  2. 목표 단계(Target Phase): 이벤트가 목표 요소에서 실행된다.
  3. 버블링 단계(Bubbling Phase): 이벤트가 목표 요소에서 최상위 노드로 전파된다.
  • 예시 구조
<div id="parent">
  <button id="child">Click Me</button>
</div>

2. 이벤트 버블링(Event Bubbling)

이벤트가 하위 요소에서 발생한 후 상위 요소로 전파되는 과정이다.

  • 예시
document.getElementById("parent").addEventListener("click", () => {
  console.log("Parent clicked!");
});

document.getElementById("child").addEventListener("click", () => {
  console.log("Child clicked!");
});
  • 출력 결과
  1. "Child clicked!"
  2. "Parent clicked!"

3. 이벤트 캡처링(Event Capturing)

이벤트가 최상위 요소에서 시작하여 하위 요소로 전파되는 과정이다. 캡처링은 기본적으로 비활성화되어 있지만, addEventListenercapture: true 옵션으로 활성화할 수 있다.

  • 예시
document.getElementById("parent").addEventListener(
  "click",
  () => {
    console.log("Parent captured!");
  },
  { capture: true }
);

4. 이벤트 전파 중단

stopPropagation을 사용하여 이벤트가 더 이상 상위 노드로 전파되지 않도록 할 수 있다.

  • 예시
document.getElementById("child").addEventListener("click", (event) => {
  event.stopPropagation();
  console.log("Child clicked, no propagation!");
});

4.3 이벤트 위임의 활용

이벤트 위임(Event Delegation) 은 이벤트 핸들러를 부모 요소에 등록하여 자식 요소의 이벤트를 처리하는 기법이다. 동적으로 생성된 요소에도 적용되므로 효율적인 이벤트 처리가 가능하다.

1. 이벤트 위임의 원리

이벤트 버블링을 활용하여, 부모 요소에서 자식 요소의 이벤트를 감지한다.

  • 장점:
    1. 동적으로 추가된 요소에도 이벤트 핸들러가 자동으로 적용된다.
    2. 여러 자식 요소에 각각 이벤트를 등록하지 않아도 된다.

2. 이벤트 위임 예시

  • HTML 구조
<ul id="list">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
</ul>
  • JavaScript 코드
const list = document.getElementById("list");

list.addEventListener("click", (event) => {
  if (event.target.tagName === "LI") {
    console.log(`You clicked on ${event.target.textContent}`);
  }
});
  • 결과: 클릭한 <li> 요소의 텍스트 출력

3. 이벤트 위임의 활용 사례

  1. 동적 콘텐츠 관리: 동적으로 추가된 요소에도 이벤트를 적용 가능
  2. 리소스 절약: 다수의 이벤트 리스너를 등록하는 대신 부모 요소 하나에만 등록
  3. 유지보수 용이: 코드의 간결성과 관리 효율성 증대

5. DOM과 성능 최적화

5.1 DOM 조작이 성능에 미치는 영향

DOM 조작은 웹 애플리케이션의 성능에 큰 영향을 미친다. 브라우저는 DOM 트리를 기반으로 페이지를 렌더링하며, DOM을 수정하거나 조작하면 브라우저는 렌더링 과정을 다시 실행해야 한다. 특히, 과도한 DOM 조작은 페이지 로드 속도를 느리게 하고 사용자 경험을 저하시킬 수 있다. 배치 업데이트와 효율적인 DOM 조작 기법을 사용하면 성능 저하를 줄이고, 브라우저의 Reflow와 Repaint를 최소화할 수 있을 것이다.

1. DOM 조작의 기본 영향

  • DOM 수정은 브라우저가 Reflow(레이아웃 계산)Repaint(화면 다시 그리기) 를 실행하도록 한다.
  • 여러 DOM 조작이 연속적으로 발생하면 브라우저는 각 단계마다 렌더링을 다시 수행하여 성능이 저하될 수 있다.

2. 과도한 DOM 조작의 예

  • 반복적으로 DOM 노드를 추가하거나 삭제할 때, 개별 작업마다 브라우저가 Reflow를 수행
for (let i = 0; i < 100; i++) {
  const div = document.createElement("div");
  div.textContent = `Item ${i}`;
  document.body.appendChild(div);
}

3. 배치 업데이트(Batched Updates)

DOM 조작 작업을 그룹화하여, 브라우저가 효율적으로 렌더링하도록 한다.

  • 예시

DOM 조작을 메모리상의 문서 조각(Document Fragment)에서 먼저 수행한 뒤, 최종적으로 DOM에 추가

const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
  const div = document.createElement("div");
  div.textContent = `Item ${i}`;
  fragment.appendChild(div);
}
document.body.appendChild(fragment);

5.2 Virtual DOM의 등장과 활용

Virtual DOM은 실제 DOM 조작의 비용을 줄이기 위해 등장한 개념으로, React, Vue와 같은 최신 프레임워크에서 채택하고 있다. Virtual DOM은 성능과 개발 효율성을 동시에 추구하는 현대 웹 개발의 핵심 기술이다.

1. Virtual DOM이란?

Virtual DOM은 브라우저의 실제 DOM 트리를 메모리에 복제하여, DOM 조작을 가상 환경에서 먼저 수행한 뒤, 변경 사항만 실제 DOM에 반영하는 기술이다.

  • 과정:
    1. Virtual DOM에서 상태 변경을 반영.
    2. Virtual DOM과 실제 DOM을 비교(Diffing).
    3. 변경된 부분만 실제 DOM에 업데이트(Patching).

2. Virtual DOM의 장점

  • 성능 최적화: 변경된 부분만 반영하므로 전체 DOM을 다시 렌더링할 필요가 없다.
  • 개발 효율성: 개발자는 전체 상태를 다시 렌더링하도록 코드를 작성해도, Virtual DOM이 최적화를 수행.

3. Virtual DOM 예시 (React)

function App() {
  const [count, setCount] = React.useState(0);

  return (
    <div>
      <h1>Counter: {count}</h1>
      <button onClick={() => setCount(count + 1)}>Increase</button>
    </div>
  );
}

React는 상태 변경(setCount)이 발생할 때 Virtual DOM에서 변경된 노드만 비교하고 업데이트하여 성능을 최적화한다.

5.3 DOM 최적화 전략 (Reflow와 Repaint 최소화)

브라우저 렌더링 과정에서 ReflowRepaint는 성능에 중요한 영향을 미친다. 이를 최소화하면 DOM 조작의 효율성을 높일 수 있다. Reflow와 Repaint를 최소화하면 페이지의 렌더링 성능이 크게 향상되며, 사용자 경험을 개선할 수 있다.

1. Reflow와 Repaint의 정의

  • Reflow: DOM 요소의 레이아웃이 변경될 때 발생
    • 예: 요소 크기, 위치, 화면 표시 여부 변경
  • Repaint: 레이아웃은 변경되지 않고, 요소의 스타일(색상, 배경 등)이 변경될 때 발생

2. Reflow와 Repaint를 유발하는 작업

  • DOM 노드 추가, 삭제, 이동
  • 요소 크기 변경(width, height 등)
  • 브라우저의 레이아웃 정보 요청(예: offsetWidth, getBoundingClientRect)

3. 최적화 전략

  • 스타일 변경의 배치 처리

여러 스타일 변경 작업을 한 번에 처리하여 Reflow 발생 횟수를 줄인다.

const element = document.getElementById("box");
element.style.width = "100px";
element.style.height = "100px";
element.style.backgroundColor = "blue"; // 개별 처리 → 비효율
  • 개선 방법
element.style.cssText = "width: 100px; height: 100px; background-color: blue;";
  • Document Fragment 활용

DOM 조작을 Document Fragment에서 먼저 실행하여, Reflow를 최소화

const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
  const div = document.createElement("div");
  div.textContent = `Item ${i}`;
  fragment.appendChild(div);
}
document.body.appendChild(fragment);
  • CSS 클래스 사용

인라인 스타일 변경 대신, CSS 클래스를 추가/제거하여 스타일을 변경

element.classList.add("new-style"); // CSS 클래스 적용
  • Throttle과 Debounce 사용

이벤트 핸들러(스크롤, 리사이즈 등)가 빈번하게 호출되지 않도록 제한

function throttle(func, limit) {
  let inThrottle;
  return function () {
    if (!inThrottle) {
      func.apply(this, arguments);
      inThrottle = true;
      setTimeout(() => (inThrottle = false), limit);
    }
  };
}

6. DOM의 한계와 대안 기술

6.1 DOM의 한계 (대규모 트리의 비효율성)

DOM은 웹 애플리케이션 개발에서 필수적이지만, 대규모 애플리케이션에서는 몇 가지 한계가 있다.

1. DOM 조작의 비용

DOM은 트리 구조로 이루어져 있어, 하나의 노드를 조작할 때도 브라우저가 트리 전체를 탐색하거나 렌더링 과정을 다시 실행해야 한다.

  • 대규모 트리의 문제점:
    • 요소가 많아질수록 DOM 탐색 및 조작 시간이 증가.
    • 복잡한 트리 구조는 Reflow와 Repaint의 빈도를 높여 성능 저하.

2. 상태와 렌더링의 분리 부족

DOM은 상태 관리와 UI 렌더링 사이에 명확한 분리가 없으므로, 복잡한 상태를 가진 애플리케이션에서는 유지보수가 어렵다.

3. 동시성 문제

DOM 조작은 브라우저의 단일 스레드(메인 스레드)에서 실행되므로, 대규모 작업이 UI 반응성을 떨어뜨릴 수 있다.

  • 예: 사용자 입력 처리와 DOM 업데이트가 동시에 이루어질 경우 지연 발생.

6.2 Shadow DOM과 Web Components

Shadow DOMWeb Components는 DOM의 복잡성을 줄이고, 구성 요소 기반 개발을 가능하게 하는 대안 기술이다. 그 중에서도 Web Components는 재사용성과 캡슐화를 높이며, 대규모 애플리케이션 개발에 적합하다.

1. Shadow DOM이란?

Shadow DOM은 DOM의 일부를 캡슐화하여, 외부 CSS나 JavaScript의 영향을 받지 않도록 설계된 기술이다. 이를 통해 모듈화된 구성 요소를 쉽게 관리할 수 있다.

  • 특징

    • 캡슐화: Shadow DOM 내부의 스타일과 구조는 외부로부터 격리된다.
    • 모듈화: 독립적인 구성 요소를 생성 가능
  • 예시: Shadow DOM 생성

const shadowHost = document.getElementById("host");
const shadowRoot = shadowHost.attachShadow({ mode: "open" });
shadowRoot.innerHTML = `
  <style>
    p { color: red; }
  </style>
  <p>Shadow DOM Content</p>
`;

2. Web Components

Web Components는 Shadow DOM을 포함하여 재사용 가능한 커스텀 HTML 요소를 만들기 위한 표준이다.

  • 구성 요소
    - Shadow DOM: 스타일과 구조의 캡슐화

    • Custom Elements: 사용자 정의 HTML 태그
    • HTML Templates: 미리 정의된 HTML 구조
  • Custom Elements 예시

class MyElement extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: "open" });
    shadowRoot.innerHTML = `
      <style>
        p { color: blue; }
      </style>
      <p>Hello from Custom Element!</p>
    `;
  }
}
customElements.define("my-element", MyElement);

6.3 최신 프레임워크에서의 DOM 관리 (React, Vue 등)

React와 Vue 같은 최신 프레임워크는 DOM 관리의 효율성을 개선하기 위해 다양한 기술을 사용한다.

1. React의 Virtual DOM

React는 Virtual DOM을 통해 실제 DOM의 조작을 최소화한다. 결과적으로 보면, React와 Vue는 DOM의 단점을 보완하여 현대적인 웹 애플리케이션 개발에 적합한 대안을 제공한다.

  • 작동 원리:
    1. 상태가 변경되면 Virtual DOM에서 변경 사항을 계산(diffing)
    2. 변경된 부분만 실제 DOM에 반영(patching)
  • 장점:
    • 전체 DOM을 다시 렌더링하지 않으므로 성능 최적화
    • 상태 관리와 렌더링을 컴포넌트 기반으로 분리

2. Vue의 Reactive DOM

Vue는 반응형(Reactive) 데이터 바인딩을 사용하여 DOM 업데이트를 자동화한다.

  • 작동 원리:
    • Vue의 데이터 객체는 반응성을 가지며, 데이터 변경 시 자동으로 DOM이 업데이트된다.
  • 예시
const app = Vue.createApp({
  data() {
    return { message: "Hello, Vue!" };
  },
});
app.mount("#app");

3. 비교

프레임워크DOM 관리 방식장점
ReactVirtual DOM성능 최적화, 상태 관리 용이
VueReactive DOM 바인딩단순한 데이터 기반 DOM 업데이트

4. DOM 관리 개선의 효과

  • 복잡한 애플리케이션에서 DOM 조작의 효율성 향상
  • 구성 요소 기반 개발로 유지보수성 증대
  • 대규모 애플리케이션에서의 성능 최적화

7. 마무리

웹 개발 경험이 그리 많지도 않고, 그나마도 백엔드 파트만 맡았다 보니 오늘 조사는 상당히 새로운 느낌으로 진행하게 되었다. 처음에는 DOM이라는 용어 자체가 익숙하지 않고, 제대로 다뤄 본 적이 없어 뭔지만 알아보자는 느낌으로 들어왔는데, 꼬리를 물며 검색하다 보니 너무 많이 들어왔나 싶기도 하다.

하지만 지금 진행 중인 프로젝트에서도 "알아둬서 나쁠 건 없다"라는 걸 여러 번 느꼈던 만큼, 당장에 쓸 일 없어 보인다고 거르고 싶지는 않다. 예를 들면 url상의 번호를 바꾸며 페이지를 전환하는 대신, AJAX 등을 이용해 동적으로 페이지를 구현하는 사이트를 접했을 때 그걸 알아볼 수 있느냐의 문제랄까...?

profile
안드로이드는 리눅스의 꿈을 꾸는가

0개의 댓글