DocumentFragment — 배치 삽입으로 리플로우 줄이기

DocumentFragment를 사용해 여러 DOM 노드를 메모리에 조립한 뒤 단 한 번의 삽입으로 리플로우를 최소화하는 패턴과, template 요소와 함께 활용하는 방법을 정리합니다.

· 4 min read · PALDYN Team

지난 글에서 classList·dataset으로 요소의 속성을 조작하는 방법을 알아봤습니다. 이번에는 여러 노드를 한꺼번에 삽입해 레이아웃 재계산(리플로우)을 최소화하는 DocumentFragment를 다룹니다.


왜 개별 삽입이 느린가

브라우저는 appendChild, insertBefore 같은 Live DOM 변경이 일어날 때마다 레이아웃 재계산과 리페인트를 예약합니다. 루프 안에서 노드를 10개 넣으면 최대 10번의 리플로우 트리거가 쌓입니다. 실제로 매 프레임마다 플러시되기 때문에 체감이 다를 수 있지만, 레이아웃이 강제 동기화(forced synchronous layout)되는 상황에서는 성능 저하가 직접 나타납니다.

DocumentFragment 개념 — 배치 삽입 원리


DocumentFragment란

DocumentFragment는 실제 문서 트리에 존재하지 않는 경량 컨테이너 노드입니다. parentNode가 항상 null이고, 화면에 렌더링되지 않습니다. 자식 노드를 맘껏 조립한 뒤 Live DOM에 삽입하면, 컨테이너 자체가 아닌 자식 노드만 이전됩니다. 삽입 후 Fragment는 자동으로 비워집니다.

const frag = document.createDocumentFragment();
// 또는: new DocumentFragment()  (최신 브라우저)

['사과', '바나나', '체리'].forEach(name => {
  const li = document.createElement('li');
  li.textContent = name;
  frag.appendChild(li); // DOM 접촉 없음
});

document.querySelector('ul').appendChild(frag); // 리플로우 1회
// 이후 frag.childNodes.length === 0

삽입 이후 frag를 재사용하려면 다시 노드를 채워야 합니다.


template 요소와의 관계

<template> 요소의 .content 프로퍼티가 바로 DocumentFragment입니다. 파싱 시 비활성 상태로 보관되기 때문에 스크립트 실행, 이미지 로딩, 스타일 적용이 일어나지 않습니다. 재사용 가능한 마크업 청사진으로 이상적입니다.

<template id="row-tpl">
  <tr>
    <td class="name"></td>
    <td class="age"></td>
  </tr>
</template>
const tmpl = document.querySelector('#row-tpl');

function renderRow(data) {
  const clone = tmpl.content.cloneNode(true); // 깊은 복사
  clone.querySelector('.name').textContent = data.name;
  clone.querySelector('.age').textContent  = data.age;
  return clone; // DocumentFragment 반환
}

const tbody = document.querySelector('tbody');
const frag  = document.createDocumentFragment();

users.forEach(u => frag.appendChild(renderRow(u)));
tbody.appendChild(frag); // 단 한 번 삽입

cloneNode(true)로 깊은 복사를 해야 원본 template 내용이 유지됩니다.

DocumentFragment 코드 패턴


주의 사항

querySelector 범위: Fragment에 append된 노드는 frag.querySelector() 로 탐색할 수 있습니다. 그러나 document.querySelector()로는 찾을 수 없습니다.

이벤트 위임: Fragment 자체에 이벤트를 붙여도 삽입 후 소멸하므로 의미가 없습니다. 삽입 전에 개별 자식에 리스너를 붙이거나, 삽입 후 공통 조상에 위임 방식으로 처리합니다.

성능 측정: 노드 수가 적을 때는 Fragment 없이 직접 삽입해도 차이가 미미합니다. 수십 개 이상의 노드를 루프에서 삽입할 때 Fragment가 의미 있는 차이를 만듭니다. 항상 performance.now()나 DevTools Performance 탭으로 실측합니다.


정리

항목설명
생성document.createDocumentFragment()
parentNode항상 null
렌더링없음 (메모리 전용)
삽입 시 동작자식만 이전, Fragment 비워짐
template.contentFragment 타입

DocumentFragment는 DOM 조작의 “스테이징 구역”입니다. 여러 노드를 미리 조립한 뒤 한 번에 올리는 습관이 리플로우 비용을 줄이는 가장 간단한 방법입니다.


지난 글: classList · attributes · dataset — 요소 속성 조작 API

다음 글: innerHTML · textContent · innerText 완벽 비교


읽어주셔서 감사합니다. 😊