사전 렌더링은 서버에서 커스텀 요소의 마크업(섀도 트리 포함)을 전송하는 것을 의미하므로 JavaScript가 실행되기 전에 구성요소의 스타일과 구조가 지정됩니다. 이를 위한 기본 플랫폼 프리미티브는 선언적 Shadow DOM (DSD)입니다. 파서는 HTML에서만 섀도우 루트를 연결하고, 요소는 나중에 등록된 경우 클래스에 의해 업그레이드 ('하이드레이션')됩니다. 이 패턴은 서버 측 렌더링, 스트리밍, 스타일이 지정되지 않은 콘텐츠의 깜박임 (FOUC) 방지에 사용합니다.
선언적 섀도우 루트 작성
shadowrootmode 속성이 있는 <template>를 호스트의 첫 번째 하위 요소로 중첩합니다. 파서는 콘텐츠를 shadow root로 연결하고 템플릿을 삭제합니다. 스크립트는 필요하지 않습니다.
<my-card>
<template shadowrootmode="open">
<style>
.card { border: 1px solid; border-radius: 8px; padding: 1rem; }
</style>
<div class="card">
<slot name="header"></slot>
<div class="body"><slot></slot></div>
</div>
</template>
<!-- Light DOM children are projected into the slots above. -->
<h2 slot="header">Server-rendered title</h2>
<p>Visible and styled before JS loads.</p>
</my-card>
수분 섭취 시점
일치하는 클래스가 등록되면 브라우저가 호스트를 업그레이드합니다. 섀도우 루트가 이미 존재하므로 생성자는 두 번째 루트를 무작정 만들어서는 안 됩니다.
class MyCard extends HTMLElement {
constructor() {
super();
// Reuse the server-rendered shadow root if present; only create one if absent.
if (!this.shadowRoot) {
this.attachShadow({ mode: 'open' });
this.shadowRoot.append(template.content.cloneNode(true));
}
// Hydrate in place: attach listeners once against existing markup (not unguarded
// in connectedCallback, which re-runs if the element moves in the DOM), and do
// NOT re-render; that would discard the server's DOM and flash.
this.shadowRoot.getElementById('toggle')
?.addEventListener('click', () => this.toggleAttribute('open'));
}
}
거의 모든 경우에 closed가 아닌 shadowrootmode="open"를 설정하여 스크립트와 도구가 this.shadowRoot에 도달하여 하이드레이션할 수 있도록 합니다. 서버 또는 테스트에서 기존 shadow root를 HTML로 다시 직렬화하려면 getHTML({ serializableShadowRoots: true })를 사용하고 serializable: true를 사용하여 루트를 연결합니다.
FOUC 방지
DSD는 스타일이 선언적 템플릿 내에 제공되는 경우에만 첫 번째 페인트에서 올바르게 렌더링됩니다. 이를 유지하려면 다음 단계를 따르세요.
- 구성요소 CSS를 별도의 스크립트 로드 시트가 아닌
<template shadowrootmode>(<style>태그 또는<link>) 내부에 배치합니다. - JS를 기다려야 하는 스타일(일반적으로 SSR 패스를 놓친 클라이언트 렌더링 인스턴스의 페이드 아웃과 같은 페이지 수준 가드 규칙)에만
my-card:not(:defined) { … }를 사용하세요. 섀도우 루트가 아직 존재하지 않고:host선택기가 외부에서 적용되지 않으므로, 이들은 light-DOM (페이지) 스타일시트에 있습니다. DSD 템플릿 내의 구조적 스타일에는 이러한 가드가 필요하지 않습니다. - 하이드레이션 시 섀도 트리를 다시 렌더링하지 않습니다. 서버 마크업을 신뢰할 수 있는 것으로 취급하고 여기에 동작을 연결합니다.
대체 전략
shadowrootmode를 파싱하지 않는 엔진에서는 템플릿이 비활성 상태이며 콘텐츠가 shadow root로 연결되지 않습니다. 이를 감지하고 (Object.hasOwn(HTMLTemplateElement.prototype, 'shadowRootMode')) 없는 경우 업그레이드 중에 동일한 마크업에서 shadow root를 반드시 연결하여 구성요소가 계속 렌더링되도록 합니다.