본문 바로가기
Javascript/성능

[Javascript - 성능] DOM 렌더링 방식에 따른 성능 비교해보기 - 1

by 맨날개발 2025. 6. 22.

✅ DocumentFragment이란?

DocumentFragment란 Dom 노드를 임시로 구성할 수 있는 가벼운 컨테이너이다. Document와는 달리 DocumentFragment는 메모리상에서만 존재하지 때문에 DocumentFragment로 구성된 트리를 변경하더라도 리플로우가 발생하지 않는다.

 

이러한 특징을 가진 DocumentFragment를 활용해서 DocumentFragment의 트리에서 DOM 조작을 완료한 후 한번에 실제 DOM에 적용함으로써 리플로우를 최소하하여 성능을 향상시킬 수 있다고 알고있었다. 

 

그래서 이번에 실제로 DocumentFragment를 활용하는 방법과 일반적인 append 메서드를 활용하는 방법의 속도차이를 알아보려고 한다.

 

💾 예제

DOM을 조작하는 예제는 다음과 같이 간단하게 div를 만들고 text를 추가해준다. 

<div id="app"></div>

<script>
const count = 1000;

function createElement(i) {
  const element = document.createElement('div');
  element.textContent = `${i}번째 div`;
  return element;
}
</script>

 

count 만큼, 반복문을 수행해서 element를 생성하고 DOM을 조작하는데 걸리는 시간을 측정할 예정이다.

 

 

1️⃣ appendChild 사용하기

반복횟수만큼 element를 생성하고 즉시 appendChild 메서를 호출하는 방식을 수행한다.

 

아래와 같이 반복문내에서 element를 추가하고 즉시 #app에 추가하고 있따.

function appendChild() {
  const $app = document.getElementById('app');

  console.time('appendChild 시간 측정');

  for (let i = 0; i < count; i += 1) {
    const element = createElement(i);
    $app.appendChild(element);
  }

  requestAnimationFrame(() => {
    requestAnimationFrame(() => {
      const end = performance.now();
      console.log('appendChild 시간 측정 :', end - start, 'ms');
    });
  });
}

 

다음 결과를 확인해보니 평균 42.266 ms 정도의 시간이 걸렸다.

appendChild 시간 측정 : 37.89 ms
appendChild 시간 측정 : 41.69 ms
appendChild 시간 측정 : 42.40 ms
appendChild 시간 측정 : 39 ms
appendChild 시간 측정 : 45.60 ms
appendChild 시간 측정 : 47.39 ms
appendChild 시간 측정 : 39.69 ms
appendChild 시간 측정 : 43.5 ms
appendChild 시간 측정 : 42.20 ms
appendChild 시간 측정 : 43.30 ms

 

 

2️⃣ append 사용하기

appendChild는 하나의 element 만을 전달받을 수 있지만, append의 경우 한 개 이상의 element를 전달받을 수 있다. 반복문을 실행하는 동안에는 배열에 담았다가 append를 한번 호출했을 때는 어떠한 결과를 보이는지를 측정해본다.

function append() {
  const $app = document.getElementById('app');

  const start = performance.now();

  const list = [];

  for (let i = 0; i < count; i += 1) {
    const element = createElement(i);

    list.push(element);
  }

  $app.append(...list);

  requestAnimationFrame(() => {
    requestAnimationFrame(() => {
      const end = performance.now();
      console.log('append 시간 측정 :', end - start, 'ms');
    });
  });
}

 

다음 결과를 확인해보니 평균 40.028 ms 정도의 시간이 걸렸다. appendChild보다는 빠르다고 할 수 있지만 5프로 정도의 차이이기 때문에 이정도면 동일한 것으로 결론을 내려도 될 것 같다.

append 시간 측정 : 36.80 ms
append 시간 측정 : 41.20 ms
append 시간 측정 : 40.39 ms
append 시간 측정 : 39.5 ms
append 시간 측정 : 37.69 ms
append 시간 측정 : 40 ms
append 시간 측정 : 39 ms
append 시간 측정 : 42.10 ms
append 시간 측정 : 44.10 ms
append 시간 측정 : 39.5 ms

 

 

3️⃣ DocumentFragment 사용하기

DocumentFragment를 생성하고 반복문을 수행하는 동안에는 DocumentFragment의 트리를 조작 후 결과를 실제 DOM 트리에 반영하는 방식으로 진행한다.

function documentFragment() {
  const $app = document.getElementById('app');

  const start = performance.now();

  const fragment = new DocumentFragment();

  for (let i = 0; i < count; i += 1) {
    const element = createElement(i);

    fragment.appendChild(element);
  }

  $app.appendChild(fragment);

  requestAnimationFrame(() => {
    requestAnimationFrame(() => {
      const end = performance.now();
      console.log('DocumentFragment 시간 측정 :', end - start, 'ms');
    });
  });
}

 

다음 결과를 확인해보니 평균 41.166 ms 정도의 시간이 걸렸다. 앞선 두 방법과의 차이가 거의 없다고봐도 무방할 것 같다.

DocumentFragment 시간 측정 : 38.40 ms
DocumentFragment 시간 측정 : 41.60 ms
DocumentFragment 시간 측정 : 44.5 ms
DocumentFragment 시간 측정 : 40.09 ms
DocumentFragment 시간 측정 : 39.90 ms
DocumentFragment 시간 측정 : 39.19 ms
DocumentFragment 시간 측정 : 43.59 ms
DocumentFragment 시간 측정 : 39.39 ms
DocumentFragment 시간 측정 : 41.80 ms
DocumentFragment 시간 측정 : 43.20 ms

 

 

🤔 왜 이런 결과가 나오는 걸까?

3가지 방법을 통해서 DOM 트리가 변경되었을 때 걸리는 시간을 비교해보았다. 3가지 중 어떠한 방법을 사용하더라도 걸리는 시간에는 차이가 없는 것으로 보인다.

 

이를 정확히 확인하기 위해서 Performance 탭을 확인해보았다.

 

아래 이미지는 appendChild를 수행한 결과이다. 결과를 보면 appendChild 작업이 모두 완료 된 후 Layout 작업이 수행되는 것을 확인할 수 있다.

 

 

🎯 결론

최적화가 진행되어 appendChild 즉시 리플로우가 발생하는 것이 아닌 모둔 작업이 완료 된 후에 리플로우 및 리페인트가 되기 때문에 위에 알아본 3가지 방법 중 어떠한 방법을 사용하더라도 비슷한 시간이 걸리게 된다.