선언적 부분 업데이트

게시일: 2026년 5월 19일, 최종 업데이트: 2026년 9월 8일

웹은 시작된 이후로 정적이고 문서 중심적인 매체에서 벗어난 지 오래입니다. 현대적이고 풍부한 웹 앱은 소통, 구매, 리치 콘텐츠 소비, 복잡한 삶 관리 등 다양한 이유로 모든 사람이 사용합니다.

HTML은 모든 발전에도 불구하고 콘텐츠가 준비되거나 사용자가 콘텐츠를 소비하는 시점을 거의 고려하지 않고 위에서 아래로 순서대로 전송됩니다. CSS를 사용하면 콘텐츠의 순서를 변경할 수 있지만 접근성 측면에서 심각한 부작용이 발생하는 경우가 많습니다. JavaScript를 사용하면 다양한 API를 통해 DOM을 조작하여 이 제한에서 어느 정도 벗어날 수 있지만, 이러한 API에는 장황한 구문이나 HTML에 연결할 DOM 트리 구성이 필요한 경우가 많습니다.

매체의 클라이언트-서버 특성을 고려할 때 웹에서는 성능이 매우 중요합니다. 하지만 HTML의 순서대로라는 특성을 우회하기 위해 최적화되지 않은 선택이 이루어지는 경우가 많으며, 이로 인해 성능이 저하됩니다. 여기에는 전체 페이지가 준비될 때까지 기다리거나 무거운 프레임워크를 사용하여 비동기 방식으로 구성요소를 제공하는 것이 포함됩니다. JavaScript 프레임워크의 인기는 웹 개발자가 웹의 기원인 엄격한 문서 정신 모델보다 구성요소 기반 모델을 선호한다는 것을 보여줍니다.

Chrome팀은 이 문제를 고려하여 선언적 부분 업데이트라는 이름으로 웹 플랫폼에 새로운 기능을 개발해 왔습니다.

처음 두 가지 새로운 API 세트를 사용하면 HTML 문서 자체에서 순서가 바뀌거나 새로운 JavaScript API를 사용하여 기존 문서에 HTML을 동적으로 삽입하는 더 쉬운 방법을 통해 HTML을 덜 선형적인 방식으로 더 쉽게 제공할 수 있습니다. 아직 지원하지 않는 브라우저에서도 이러한 새로운 API를 바로 사용할 수 있는 폴리필도 제공됩니다.

순서가 잘못된 스트리밍

Browser Support

  • Chrome: 150.
  • Edge: 150.
  • Firefox: not supported.
  • Safari: not supported.

Source

첫 번째 변경사항은 처리 명령 자리표시자와 for 속성이 있는 <template> HTML 요소를 사용하는 새로운 순서가 지정되지 않은 스트리밍 API입니다. 예를 들면 다음과 같습니다.

<div>
  <?marker name="placeholder">
</div>

...

<template for="placeholder">
  Here is some <em>HTML content</em>!
</template>

처리 지침은 XML에 오랫동안 존재했지만 HTML에서는 주석으로 처리되어 무시되었습니다. 이 새로운 API는 이를 변경하고 처리 지침을 HTML에 도입합니다. 예를 들어 브라우저가 <?marker name="placeholder"> 처리 명령어를 보면 이전과 마찬가지로 바로 아무것도 하지 않지만 나중에 참조할 수 있습니다.

for 속성이 있는 <template> 요소name 속성이 있는 해당 처리 지침을 조회하고 콘텐츠를 대체합니다. 이 경우 파싱 후 DOM은 다음과 같이 됩니다 (공백 차이는 무시).

<div>
  Here is some <em>HTML content</em>!
</div>

대체를 위한 <?marker> 속성 외에도 템플릿이 처리되기 전에 임시 자리표시자 콘텐츠를 표시할 수 있는 <?start><?end> 범위 마커도 있습니다.

<div>
  <?start name="another-placeholder">
  Loading…
  <?end>
</div>

...

<template for="another-placeholder">
  Here is some <em>HTML content</em>!
</template>

이 경우 <template>이 표시될 때까지 Loading… 문자열이 표시된 후 새 콘텐츠로 대체됩니다.

템플릿에 처리 안내를 포함하여 여러 업데이트를 허용할 수도 있습니다.

<ul id="results">
  <?start name="results">
  Loading…
  <?end>
</ul>

...

<template for="results">
  <li>Result One</li>
  <?marker name="results">
</template>

...

<template for="results">
  <li>Result Two</li>
  <?marker name="results">
</template>

...

파싱되고 처리된 후에는 다음과 같은 HTML이 됩니다.

<ul id="results">
  <li>Result One</li>
  <li>Result Two</li>
  <?marker name="results">
</ul>

나중에 문서에 <template for="results"> 자리표시자가 추가될 경우를 대비해 마지막에 최종 처리 지침이 있습니다.

표준 HTML 요소 대신 처리 명령어를 사용하는 이유는 무엇인가요?

이 API를 처음 사용하는 사용자에게는 <slot> 또는 <template> 요소를 직접 사용하라는 제안과 함께 이 질문이 자주 표시됩니다. 이 제안서의 초기 버전은 표준 HTML 요소와 함께 작동했지만 디자인이 반복되면서 처리 명령어로 전환되었습니다. 처리 명령어를 사용하면 DOM에 영향을 주지 않고 패치를 적용할 수 있습니다. 이를 통해 <head>(예: <title> 업데이트)에서 사용하거나 <table>와 같은 다른 요소 내에서 사용할 수 있습니다(예: 선택적 추가 행 추가).

많은 웹 개발자에게 구문이 익숙하지 않지만 처리 지침은 하위 호환성 문제가 발생할 가능성이 적고 훨씬 더 큰 유연성을 제공합니다. 이 제안 덕분에 XML에서 이미 사용되고 있으며 이제 HTML 표준의 일부가 되었습니다.

데모

이 동영상에서는 스트리밍 HTML을 사용하여 기본 사진 앨범 애플리케이션을 구현합니다.

순서가 지정되지 않은 스트리밍으로 구현된 사진 앨범 데모 (소스)

상태와 사진은 초기 레이아웃 후에 HTML로 스트리밍됩니다.

사용 사례

스트리밍 HTML과 결합된 이 순서가 잘못된 HTML 패치에는 다양한 사용 사례가 있습니다.

  • 아일랜드 아키텍처. Astro와 같은 프레임워크로 널리 알려진 일반적인 패턴인 아일랜드 아키텍처는 구성요소가 정적 HTML 위에 독립적으로 하이드레이션됩니다. <template for> API를 사용하면 HTML에서 직접 유사한 방식으로 정적 콘텐츠를 처리할 수 있습니다. JavaScript 프레임워크는 이를 사용하여 더 상호작용적인 아일랜드를 만들거나 구성요소를 처리할 수도 있습니다.
  • 준비가 되면 콘텐츠를 제공합니다. 이 아일랜드 아키텍처 덕분에 콘텐츠는 추가 처리가 필요한 콘텐츠(예: 데이터베이스 조회)로 인해 지연되지 않고 준비되는 즉시 스트리밍될 수 있습니다. 많은 플랫폼에서 HTML 스트리밍을 허용하지만 HTML의 순서 지정 특성으로 인해 콘텐츠가 지연되거나 복잡한 JavaScript DOM 조작을 사용해야 하는 경우가 많습니다. 이제 기다리는 동안 정적 콘텐츠를 제공하고 HTML 스트림 끝에 더 비싸고 동적인 콘텐츠를 삽입할 수 있습니다.
  • 페이지 로드 성능에 최적화된 순서로 HTML을 제공할 수 있습니다. 한 단계 더 나아가 준비가 완료된 경우에도 순서를 변경할 수 있습니다. 예를 들어 메가 메뉴는 페이지가 상호작용이 가능해질 때까지 사용자가 볼 수 없는 많은 HTML을 포함하는 일반적인 탐색 기능입니다. 이 큰 HTML 청크는 초기 페이지 로드에 필요한 더 중요한 HTML의 우선순위를 지정하기 위해 HTML 문서에서 나중에 제공될 수 있습니다. HTML을 사용하면 순서가 더 이상 장벽이 아닙니다.

이러한 사용 사례는 일부에 불과하며 개발자가 이 새로운 API를 어떻게 사용할지 기대됩니다.

제한사항 및 미묘한 차이

API에는 몇 가지 제한사항과 미묘한 차이가 있으므로 주의해야 합니다.

  • <template for>는 보안상의 이유로 동일한 상위 요소 내에서만 처리 지침을 업데이트할 수 있습니다. <template for><body> 요소에 직접 추가하면 <head>를 포함한 전체 문서에 액세스할 수 있습니다.
  • <?end> 처리 명령어는 선택사항이며, 누락된 경우 <?start> 요소와 포함된 요소의 끝 사이의 콘텐츠가 대체됩니다.
  • <template for>가 스트리밍을 시작한 후 처리 지침을 이동하면 새 콘텐츠가 이전 위치로 계속 스트리밍되는 등 예기치 않은 결과가 발생할 수도 있습니다.
  • setHTML 또는 innerHTML 속성과 같은 메서드를 사용하여 <template for>를 동적으로 삽입할 때 파싱되는 템플릿의 '상위 요소'는 중간 문서 프래그먼트입니다. 즉, 이러한 메서드로 HTML을 삽입해도 기존 DOM을 수정할 수 없으며 패치는 프래그먼트 내에서 '인플레이스'로 발생합니다. 하지만 streamHTMLUnsafe와 같은 메서드를 사용하여 스트리밍할 때는 중간 프래그먼트가 없으므로 템플릿이 기존 콘텐츠를 대체할 수 있습니다.

표준화 상태

<template for> 속성은 HTML 표준의 일부이지만 아직 모든 브라우저에서 지원되지는 않습니다.

향후 추가될 수 있는 기능

고려 중인 향후 추가 기능은 다음과 같습니다.

  • 클라이언트 측 포함. 예를 들어 <template for="footer" src="/partials/footer.html"> 또는 패치를 적용하지 않은 <template src="/partials/footer.html">입니다. 자세한 내용은 설명 동영상을 참고하세요. 이는 chrome://flags/#enable-experimental-web-platform-features 플래그 뒤에서 사용할 수 있습니다.
  • 변경되지 않는 콘텐츠가 덮어쓰여지지 않도록 방지 콘텐츠 수정 번호 또는 버전 관리를 통해 이를 달성할 수 있습니다. 이렇게 하면 콘텐츠를 재설정하는 대신 경로 변경이나 기타 업데이트 간에 상태를 유지할 수 있습니다.
  • 패치하는 동안 정리 예를 들면 <template for=icon safe><svg id="from-untrusted-source">...</svg></template>입니다.

폴리필

Chrome팀은 이 기능이 다른 브라우저에 적용되기 전에도 사이트에서 바로 이 새로운 기능을 사용할 수 있도록 template-for-polyfill을 출시했으며 npm에서 사용할 수 있습니다.

브라우저의 HTML 파서를 직접 업데이트할 수 없으므로 몇 가지 제한사항이 있지만 가장 일반적인 사용 사례는 지원됩니다. 다른 브라우저에서도 사이트를 테스트해야 합니다.

HTML 삽입 및 스트리밍 방법 갱신

일부 콘텐츠는 HTML로 제공할 수 없습니다. 이 영역에서 Chrome이 수행한 작업의 두 번째 부분은 JavaScript로 콘텐츠를 더 쉽게 업데이트할 수 있도록 하는 데 목적이 있습니다.

JavaScript를 사용하여 기존 문서에 HTML을 동적으로 삽입하는 방법은 이미 여러 가지가 있습니다.

  • setHTML
  • setHTMLUnsafe
  • innerHTMLouterHTML
  • createContextualFragment
  • insertAdjacentHTML

하지만 모두 약간 다른 방식으로 작동하며 개발자가 항상 고려하지 않을 수 있는 미묘한 차이점이 있습니다.

  • 새 콘텐츠가 덮어쓰기되는지 아니면 추가되는지
  • 잠재적으로 위험한 HTML을 정리하나요(예: <script> 태그를 이스케이프)?
  • 그렇지 않은 경우 <script>를 실행해야 하나요?
  • 신뢰할 수 있는 유형과 어떻게 작동하나요?

이러한 API를 살펴보고 각 API에 대해 이러한 질문에 자신 있게 답변할 수 있는 개발자는 거의 없습니다.

큰 제한사항은 HTML 스트리밍을 허용하는 호출이 있었을 때 미리 알려진 전체 HTML 집합에만 사용할 수 있다는 것입니다. 실제로 이는 HTML의 강점 중 하나가 콘텐츠를 바로 스트리밍할 수 있다는 점인데도 콘텐츠를 삽입하기 전에 전체 콘텐츠를 다운로드해야 한다는 의미입니다. 페이로드를 분할하거나 document.write와 같은 오래되고 불안정한 방법을 사용하여 제한적으로 해결할 수 있지만 자체 문제가 발생합니다.

새로운 정적 및 스트리밍 API 세트

Browser Support

  • Chrome: behind a flag.
  • Edge: behind a flag.
  • Firefox: not supported.
  • Safari: not supported.

Chrome에서는 이 문제를 해결하고 스트리밍 기능을 도입하기 위해 기존 setHTMLsetHTMLUnsafe를 정리하는 새 API 모음과 확장 프로그램을 개발했습니다.

이 기능은 Chrome 148부터 chrome://flags/#enable-experimental-web-platform-features 플래그를 사용하여 개발자 테스트를 진행할 수 있으며 Chrome 155에서 출시될 예정입니다.

기존 HTML 전후에 콘텐츠를 삽입하는 메서드와 함께 설정하거나 대체하는 메서드가 있습니다. 각 메서드에는 스트림에 상응하는 메서드가 있습니다.

작업 정적 스트리밍
요소의 HTML 콘텐츠 설정 setHTML(html, options); streamHTML(options);
전체 요소를 이 HTML로 바꿉니다. replaceWithHTML(html, options); streamReplaceWithHTML(options);
요소 앞에 HTML 추가 beforeHTML(html, options); streamBeforeHTML(options);
HTML을 요소의 첫 번째 하위 요소로 추가 prependHTML(html, options); streamPrependHTML(options);
HTML을 요소의 마지막 하위 요소로 추가 appendHTML(html, options); streamAppendHTML(options);
요소 뒤에 HTML 추가 afterHTML(html, options); streamAfterHTML(options);
새 삽입 및 스트리밍 메서드

곧 다룰 Unsafe 버전도 있습니다. 특히 Unsafe에 해당하는 항목을 추가하면 많은 것처럼 보일 수 있지만 일관된 이름 지정 규칙을 사용하면 이전에 언급한 관련 없는 메서드와 비교했을 때 각 메서드의 기능을 더 명확하게 알 수 있습니다.

정적 버전은 선택적 옵션과 함께 새 HTML을 DOM 문자열 인수로 사용합니다.

const newHTML = "<p>This is a new paragraph</p>";
const contentElement = document.querySelector('#content-to-update');

contentElement.setHTML(newHTML);

스트리밍 버전은 getWriter()와 같은 Streams API와 함께 작동합니다.

const contentElement = document.querySelector('#content-to-update');
const writer = contentElement.streamHTMLUnsafe().getWriter();

// Example stream of updating content
while (true) {
  await writer.write(`<p>${++i}</p>`);
  await new Promise((resolve) => setTimeout(resolve, 1000));
}

writer.close();

또는 파이프 체인을 사용하여 가져오기 응답에서 가져올 수 있습니다.

const contentElement = document.querySelector('#content-to-update');
const response = await fetch('/api/content.html');

response.body
  .pipeThrough(new TextDecoderStream())
  .pipeTo(contentElement.streamHTMLUnsafe());

textStream() 편의 메서드

Browser Support

  • Chrome: 151.
  • Edge: 151.
  • Firefox: not supported.
  • Safari: not supported.

중간 TextDecoderStream() 단계가 필요 없이 직접 스트리밍할 수 있는 textStream 편의 메서드도 추가되었습니다.

const contentElement = document.querySelector('#content-to-update');
const response = await fetch('/api/content.html');

response.textStream().pipeTo(contentElement.streamHTMLUnsafe());

options

options 인수를 사용하면 기본값이 default(기본 소독제 구성)인 맞춤 sanitizer를 지정할 수 있습니다. 다음과 같이 사용됩니다.

const newHTML = '<p>This is a new paragraph</p>';
const contentElement = document.querySelector('#content-to-update');

// Only allows basic formatting
const basicFormattingSanitzer = new Sanitizer({ elements: ['em', 'i', 'b', 'strong'] });

contentElement.setHTML(newHTML, {sanitizer: basicFormattingSanitzer});

'안전하지 않은' 메서드

각 API의 '안전하지 않은' 버전도 있습니다.

작업 정적 스트리밍
요소의 HTML 콘텐츠 설정 setHTMLUnsafe(html,options); streamHTMLUnsafe(options);
전체 요소를 이 HTML로 바꿉니다. replaceWithHTMLUnsafe(html, options); streamReplaceWithHTMLUnsafe(options);
요소 앞에 HTML 추가 beforeHTMLUnsafe(html, options); streamBeforeHTMLUnsafe(options);
HTML을 요소의 첫 번째 하위 요소로 추가 prependHTMLUnsafe(html, options); streamPrependHTMLUnsafe(options);
HTML을 요소의 마지막 하위 요소로 추가 appendHTMLUnsafe(html, options); streamAppendHTMLUnsafe(options);
요소 뒤에 HTML 추가 afterHTMLUnsafe(html, options); streamAfterHTMLUnsafe(options);
'안전하지 않은' 삽입 및 스트리밍 메서드

이러한 '안전하지 않은' 메서드는 기본적으로 새니타이저를 사용 중지하며 원하는 경우 맞춤 새니타이저를 지정할 수 있습니다. 이러한 메서드를 사용하면 스크립트를 선택사항인 runScripts 옵션(기본값은 false)으로 실행할 수도 있습니다.

setHTML와 마찬가지로 setHTMLUnsafe는 기존 메서드이지만 스크립트 실행에 사용할 수 있도록 runScripts 옵션 매개변수가 추가되었습니다.

const newHTML = `<p>This is a new paragraph</p>
                 <script src=script.js></script>`;
const contentElement = document.querySelector('#content-to-update');

contentElement.setHTMLUnsafe(newHTML, {runScripts: true});

메서드의 '안전하지 않음'이라는 문구는 이러한 메서드를 사용하지 말라는 것이 아니라 개발자에게 잠재적인 위험과 스크립트를 정리하거나 제한하는 방법을 알려주기 위한 것입니다.

이것이 얼마나 '안전하지 않은지'는 입력의 신뢰도에 따라 다릅니다. Unsafe 정적 메서드는 모두 DOM 문자열 또는 TrustedHTMLhtml 인수로 사용하며, 소독제를 사용할 수도 있습니다. runScript의 경우 전체 의도는 스크립트를 허용하는 것이므로 기본적으로 소독제가 사용되지 않습니다.

사용 사례

이러한 새로운 API를 사용하면 개발자가 기존 페이지에 HTML을 더 쉽게 추가하여 일관된 이름과 옵션으로 새로운 API를 추가할 수 있습니다. 스트리밍 API를 사용하면 플랫폼에서 모든 새 콘텐츠를 사용할 수 있을 때까지 기다리지 않아도 되는 성능상의 이점이 있습니다.

사용 사례는 다음과 같습니다.

  • 단일 페이지 앱에서 대규모 콘텐츠 업데이트의 동적 스트리밍 앞서 언급했듯이 현재 SPA 아키텍트의 큰 단점은 초기 HTML 로드의 스트리밍 특성을 활용하지 못한다는 것입니다. 하지만 이제는 다릅니다.
  • HTML 바닥글과 같은 일반적인 콘텐츠 삽입 JavaScript API를 사용하면 전송되는 모든 페이지에서 반복하는 대신 캐싱의 이점을 누리면서 파트를 가져와 페이지에 삽입할 수 있습니다. 하지만 실행하려면 JavaScript가 필요하므로 초기 로드 시 표시되지 않는 콘텐츠에만 사용해야 합니다.

이는 몇 가지 예일 뿐이며, 여러분이 어떤 아이디어를 내실지 기대됩니다.

제한사항 및 미묘한 차이

이러한 새로운 API에는 몇 가지 제한사항과 미묘한 차이도 포함되어 있습니다.

  • 신뢰할 수 있는 유형 API를 사용한 스트리밍 통합에는 새 createParserOptions 메서드를 사용해야 합니다. 이 메서드를 사용하면 새니타이저를 모든 HTML 설정 작업에 삽입할 수 있습니다. 신뢰할 수 있는 유형 통합에 관한 자세한 내용은 설명 동영상 참고
  • <template for>와 마찬가지로 스트리밍되는 요소를 이동하면 예기치 않은 결과나 스트림 오류가 발생할 수 있습니다.
  • streamHTMLUnsafe<template for> 명령어를 기본 문서에 추가될 때 처리하고 defer 스크립트를 스트림 끝까지 지연하는 등 여러 면에서 기본 파서와 유사하게 작동합니다.

표준화 상태

최신 삽입 및 스트리밍 메서드는 HTML 표준에 추가되는 중이지만 아직 모든 브라우저에서 지원되지는 않습니다.

폴리필

Chrome팀은 이 기능이 다른 브라우저에 적용되기 전에도 사이트에서 바로 이 새로운 기능을 사용할 수 있도록 html-setters-polyfill을 출시했으며 npm에서 사용할 수 있습니다.

이 polyfill은 스트리밍되지 않고 완료되면 버퍼링되고 적용됩니다. 기능보다는 API 모양을 위한 폴리필에 가깝습니다.

또한 안전한 콘텐츠 설정은 Safari에서 지원되지 않는 setHTMLSanitizer API에 따라 달라집니다.

둘 다 함께 사용

이 두 API는 서로 별개이지만, 이를 결합할 때 진정한 효과가 발휘됩니다. 새 <template for> 요소를 HTML로 스트리밍하면 DOM에 대한 별도의 JavaScript 참조로 각 요소를 직접 타겟팅하지 않고도 콘텐츠의 여러 부분을 동적으로 업데이트할 수 있습니다.

기본 SPA 스타일 페이지 로드는 처리 지침이 포함된 개요 페이지를 로드한 다음 각 새 페이지의 템플릿을 HTML 하단으로 스트리밍하여 이러한 처리 지침에 삽입하는 방식으로 구현할 수 있습니다.

이 두 API에는 더 많은 잠재력과 사용 사례가 있으니 Google의 (제한된!) 상상력에 얽매이지 마세요. 부분 업데이트를 더 쉽게 관리할 수 있게 되면 상용구 코드를 줄이고, 업데이트를 더 쉽게 진행하고, 웹의 새로운 가능성을 열 수 있습니다.

Chrome에서는 선언적 부분 업데이트 프로젝트에 따라 몇 가지 API를 더 개발하고 있지만, 우선 이 두 가지 API를 제공해 드릴 수 있게 되어 기쁩니다. 이 공간에 더 많은 기능이 제공되면 알려드리겠습니다.