← 글 목록

notes

Shadow DOM이란?

Shadow DOM의 캡슐화·스타일 격리·컴포넌트 구조와 사용 메모.

  • Shadow DOM 은 컴포넌트 하나를 바깥 페이지와 분리된 작은 문서처럼 만든다.
  • 밖에서 스타일을 바꾸려면 컴포넌트가 열어 둔 통로만 쓸 수 있다.
  • 요즘은 JS 없이 HTML 만으로도 만들 수 있어서 SSR 에서도 쓸 수 있다.

Shadow DOM 이란

일반 DOM 은 페이지 전체가 하나의 트리다. 어느 CSS 든 어느 요소든 건드릴 수 있다.

Shadow DOM 은 어떤 요소(shadow host) 아래에 별도 트리(shadow root)를 붙인다. 이 안쪽은 바깥 트리와 경계가 있어서, document.querySelector 로도 바로 못 찾고 바깥 CSS 선택자도 안 먹힌다.

<video> 태그의 재생 버튼이나 <input type="range"> 의 슬라이더가 대표적인 예다. 브라우저가 내부를 Shadow DOM 으로 그려서, 우리 CSS 로 내부를 마음대로 못 바꾼다.

웹 컴포넌트를 만들 때 주로 쓴다. Custom Element 와 같이 쓰면 어떤 페이지에 넣어도 스타일이 깨지지 않는 컴포넌트가 된다.

attachShadow 로 만들기
class MyCard extends HTMLElement {
  constructor() {
    super();
    const root = this.attachShadow({ mode: 'open' });
    root.innerHTML = `
      <style>
        :host { display: block; border: 1px solid #ddd; border-radius: 8px; }
        p { color: var(--card-color, #333); }
      </style>
      <p><slot></slot></p>
    `;
  }
}
customElements.define('my-card', MyCard);

안에 쓴 p { ... } 는 이 컴포넌트 안의 p 에만 먹는다. 페이지의 다른 p 는 영향을 안 받는다.


경계를 넘는 것과 못 넘는 것

캡슐화라고 해서 완전히 막힌 건 아니다. 컴포넌트가 열어 둔 통로로만 들어간다.

밖에서 꾸미기 예시
<style>
  my-card { --card-color: tomato; color: navy; }
  my-card::part(title) { font-weight: 700; }
</style>

<my-card>
  <span slot="title">제목</span>
  본문은 기본 slot 으로 들어간다
</my-card>

이벤트도 비슷하다. 안에서 난 클릭은 밖으로 버블링되지만, 밖에서 보면 event.target 이 host 요소로 바뀌어 보인다. 커스텀 이벤트를 밖으로 내보내려면 composed: true 를 줘야 한다.


open, closed 그리고 Declarative Shadow DOM

mode: 'open' 이면 밖에서 element.shadowRoot 로 안쪽에 접근할 수 있다. 'closed' 면 null 이 나온다.

closed 가 보안 기능은 아니다. 개발자 도구에서는 다 보이고, 테스트나 디버깅만 불편해져서 보통 open 을 쓴다.

예전에는 Shadow DOM 을 JS 로만 만들 수 있어서, 서버에서 렌더링한 HTML 에는 넣을 수가 없었다. 지금은 Declarative Shadow DOM 이 Chrome, Edge, Safari, Firefox 에 다 들어와서 HTML 만으로 만들 수 있다.

Declarative Shadow DOM
<my-card>
  <template shadowrootmode="open">
    <style>
      :host { display: block; }
    </style>
    <p><slot></slot></p>
  </template>
  본문
</my-card>

파서가 template 을 보고 바로 shadow root 를 붙이기 때문에, JS 가 로드되기 전에도 스타일이 적용된 채로 보인다. SSR 하는 웹 컴포넌트에서 깜빡임을 없애는 방법이다.