์น ์ปดํฌ๋ํธ์ ํต์ฌ ๊ธฐ์ ์ค ํ๋๋ก, HTML ๋ฌธ์ ๋ด์์ ๋ ๋ฆฝ์ ์ธ DOM ํธ๋ฆฌ๋ฅผ ์์ฑํ ์ ์๊ฒ ํด์ฃผ๋ ๊ธฐ๋ฅ์ด๋ค.
์ด๋ฅผ ํตํด ๊ฐ๋ฐ์๋ ์คํ์ผ๊ณผ ์๋ฐ์คํธ๋ฆฝํธ๊ฐ ์๋ก ์ถฉ๋ํ์ง ์๋๋ก ๊ฒฉ๋ฆฌ๋ ํ๊ฒฝ์ ์ ๊ณตํ ์ ์๋ค.
1. ์บก์ํ: Shadow DOM์ ์ธ๋ถ ์คํ์ผ๊ณผ ์๋ฐ์คํฌ๋ฆฝํธ์ ์ํฅ์ ๋ฐ์ง ์๋ ๋
๋ฆฝ์ ์ธ DOM ๊ตฌ์กฐ๋ฅผ ์์ฑํ๋ค.
์ด๊ฒ์ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ ๋ฌธ์์ CSS์ JavaScript์ ์ถฉ๋์ ๋ฐฉ์งํ๋ค.
2. ๊ตฌ์กฐํ: ๋ณต์กํ UI ์์๋ฅผ ๊ตฌ์กฐ์ ์ผ๋ก ๋๋ ์ ์๋ค. ๊ฐ ์ปดํฌ๋ํธ๋ ์์ฒด์ ์ธ DOM์ ๊ฐ์ง๋ฏ๋ก, ์ฝ๋์ ๊ฐ๋ ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ด ํฅ์๋๋ค.
3. ์คํ์ผ ๊ฒฉ๋ฆฌ: Shadow DOM ๋ด์ ์คํ์ผ์ ํญ์ Shadow DOM์๋ง ์ ์ฉ๋๋ฉฐ, ์ธ๋ถ ์คํ์ผ์ ์ํฅ์ ๋ฏธ์น์ง ์๋๋ค. ๋ฐ๋ผ์, ์คํ์ผ์ ์ถฉ๋์ ๊ฑฑ์ ํ ํ์๊ฐ ์๋ค.
4. ์ฌ์ฌ์ฉ์ฑ: Shadow DOM์ ์ด์ฉํ๋ฉด UI ์ปดํฌ๋ํธ๋ฅผ ์ฝ๊ฒ ์ฌ์ฌ์ฉํ ์ ์๋ค. ์ด ์ปดํฌ๋ํธ๋ ๋ค์ํ ํ๋ก์ ํธ์์ ๋ ๋ฆฝ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๋ค.
Shadow DOM์ ์จ๊ฒจ์ง DOM ํธ๋ฆฌ๊ฐ ํต์์ ์ธ DOM ํธ๋ฆฌ์ ์ํ ์์์ ๋ถ์ฐฉ๋ ์ ์๊ฒ ํ๋ค.
์ด Shadow DOM ํธ๋ฆฌ๋ shadow root์์ ์์๋๋ฉฐ, ์ํ๋ ๋ชจ๋ ์์ ์์ ๋ถ์ฐฉ๋ ์ ์๋ค.
์ด ๊ณผ์ ์ ์ผ๋ฐ DOM์์ ์์๋ฅผ ์ถ๊ฐํ๋ ๋ฐฉ๋ฒ๊ณผ ๋์ผํ๋ค.
๐ก Shadow DOM ์ฉ์ด
- Shadow host: Shadow DOM์ด ๋ถ์ฐฉ๋๋ ํต์์ ์ธ DOM ๋ ธ๋
- Shadow tree: Shadow DOM ๋ด๋ถ์ DOM ํธ๋ฆฌ
- Shadow boundary: Shadow DOM์ด ๋๋๊ณ , ํต์์ ์ธ DOM์ด ์์๋๋ ์ฅ์
- Shadow root: Shadow ํธ๋ฆฌ์ root ๋ ธ๋
Element.attachShadow()
๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ ํ ์์์๋ Shadow root๋ฅผ ๋ถ์ฐฉํ ์ ์๋ค.
์ด ๋ฉ์๋๋ ๋งค๊ฐ๋ณ์๋ก ํ๋์ ๋ชจ๋๋ฅผ ํฌํจํ๋ ๋ชจ๋ ๊ฐ์ฒด๋ฅผ ํ์๋ก ํ๋ค.
์ด ๋ฉ์๋๋ HTML ์์์ ๋ํด Shadow root
๋ฅผ ์์ฑํ๋ค.
๐จ ์ฃผ์ ์ฌํญ
์ฃผ๋ก ์ํธ ์์ฉํ๋ ํน์ ์์๋ค์ Shadow host๊ฐ ๋ ์ ์๊ธฐ ๋๋ฌธ์, ๋จ์ํ<a>
์์๋ฅผ Shadow host๋ก ์ฌ์ฉํ ์ ์๋ค!
// HTML ์์ ์ ํ
const hostElement = document.querySelector('.element');
// Shadow DOM ์์ฑ
const shadowRoot = hostElement.attachShadow({ mode: 'open' }); // or ({ mode: "closed" })
// Shadow DOM์ ์ฝํ
์ธ ์ถ๊ฐ
shadowRoot.innerHTML = `
<style>
p {
color: blue;
}
</style>
<p>Hello, Shadow DOM!</p>
`;
Shadow DOM์ ๋ ๊ฐ์ง ๋ชจ๋๋ฅผ ์ง์ํ๋ค.
Open: Shadow DOM์ ์ ๊ทผํ ์ ์๋ API๊ฐ ์ ๊ณต๋๋ค. ์ธ๋ถ์์ Shadow DOM์ ๋ด์ฉ์ ์ฝ๊ฑฐ๋ ์์ ํ ์ ์๋ค.
Closed: Shadow DOM์ ๋ํ ์ ๊ทผ์ด ์ ํ๋๋ค. ์ธ๋ถ์์ Shadow DOM์ ๋ด์ฉ์ ์ง์ ์ ์ผ๋ก ์ ๊ทผํ ์ ์๋ค.
<body>
<div class="shadow-host">
<a href="https://velog.io/@woogur29/posts"> ๋ธ๋ก๊ทธ ํฌ์คํ
๋ฐ๋ก๊ฐ๊ธฐ</a>
</div>
<script>
const shadowHostElement = document.querySelector(".shadow-host");
// host์ Shadow DOM ๋ถ์ด๊ธฐ์ํด attachShadow() ๋ฉ์๋ ์ฌ์ฉ
const shadowRoot = shadowHostElement.attachShadow({ mode: "open" });
</script>
</body>
์ด ์ฝ๋๋ Shadow host์ ์์ ์์์ธ ๋น Shadow root๋ฅผ ์์ฑํ๋ค. <html>
์์๊ฐ DOM์ ์์์ธ ๊ฒ ์ฒ๋ผ Shadow root๋ Shadow DOM์ ์์์ ์ญํ ์ ํ๋ค.
์ผ๋ฐ HTML ์์ ์์(<a>
)๋ ๊ฐ๋ฐ์ ๋๊ตฌ์์ ํ์ธ๋๊ธด ํ์ง๋ง Shadow root๊ฐ ์ฐจ์งํ๋ฉด์ ๋ ์ด์ ํ๋ฉด์ ๋ณด์ด์ง ์๊ฒ ๋๋ค.
Shadow tree๋ DOM tree์ ๋น์ทํ์ง๋ง ์ผ๋ฐ DOM ๋์ Shadow DOM์ ์ฌ์ฉํ๋ค.
const link = document.createElement("a");
link.href = shadowHostElement.querySelector("a").href;
link.innerHTML = `${shadowHostElement.querySelector("a").textContent}`;
shadowRoot.appendChild(link);
์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ๊ณผ ๋์ผํ๊ฒ appendChild()
๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ Shadow DOM์ ์๋ก์ด ์์๋ฅผ ์ถ๊ฐํ๋ค.
์ด ์์ ์์ ํด๋น ์์๋ ์๋์ ๊ฐ๋ค.
const styles = document.createElement("style");
styles.textContent = `
a {
text-decoration: none;
color: #20c997;
font-weight: 800;
}`;
shadowRoot.appendChild(styles);
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shadow DOM ์์ </title>
<style>
/* ๋ถ๋ชจ ์์์ CSS */
.parent {
background-color: red; /* ๋ถ๋ชจ ๋ฐฐ๊ฒฝ์ */
padding: 20px;
}
/* ๋ถ๋ชจ์ ๋ฒํผ ์คํ์ผ */
.parent button {
color: white;
background-color: black;
}
</style>
</head>
<body>
<div class="parent">
<h1>๋ถ๋ชจ ์์</h1>
<button onclick="alert('๋ถ๋ชจ ๋ฒํผ ํด๋ฆญ!')">๋ถ๋ชจ ๋ฒํผ</button>
<!-- Shadow DOM์ ์ฌ์ฉํ ์์ -->
<div id="shadow-host"></div>
</div>
<script>
// Shadow DOM ์์ฑ
const shadowHost = document.getElementById('shadow-host');
const shadowRoot = shadowHost.attachShadow({ mode: 'open' });
// Shadow DOM ๋ด์ HTML ๋ฐ CSS ์ถ๊ฐ
shadowRoot.innerHTML = `
<style>
/* Shadow DOM ๋ด์ CSS */
.child {
background-color: blue; /* Shadow DOM ๋ด์ ๋ฐฐ๊ฒฝ์ */
color: white;
padding: 10px;
border: 1px solid black;
}
</style>
<div class="child">
<h2>Shadow DOM ์์</h2>
<button id="shadow-button">Shadow DOM ๋ฒํผ</button>
</div>
`;
// Shadow DOM ๋ด์ ๋ฒํผ ํด๋ฆญ ์ด๋ฒคํธ ๋ฆฌ์ค๋
const shadowButton = shadowRoot.getElementById('shadow-button');
shadowButton.addEventListener('click', () => {
alert('Shadow DOM ๋ฒํผ ํด๋ฆญ!');
});
</script>
</body>
</html>
์ผ๋ฐ DOM: ๋ชจ๋ ์คํ์ผ์ด ์ ์ญ์ ์ผ๋ก ์ ์ฉ๋๋ค. ์ฆ, ๋ถ๋ชจ ์์์ ์คํ์ผ์ด ์์ ์์์ ์ํฅ์ ๋ฏธ์น ์ ์๋ค.
Shadow DOM: Shadow DOM ๋ด์์ ์ ์ํ ์คํ์ผ์ ํด๋น DOM์๋ง ์ ์ฉ๋๋ค. ์ธ๋ถ ์คํ์ผ์ด Shadow DOM ๋ด์ ์์์ ์ํฅ์ ๋ฏธ์น์ง ์๋๋ค. ๋ฐ๋ผ์ ์คํ์ผ์ ์ถฉ๋์ ๋ฐฉ์งํ ์ ์๋ค.
์ผ๋ฐ DOM: ๋ชจ๋ ์ด๋ฒคํธ ๋ฆฌ์ค๋์ ์คํฌ๋ฆฝํธ๊ฐ ์ ์ญ์ ์ผ๋ก ์๋ํ๋ค. ์ฌ๋ฌ ์์๊ฐ ๊ฐ์ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ๊ณต์ ํ ๊ฒฝ์ฐ, ์๊ธฐ์น ์์ ๋์์ด ๋ฐ์ํ ์ ์๋ค. ์๋ฅผ ๋ค์ด ๋์ผํ ID๋ฅผ ๊ฐ์ง ์์๊ฐ ์์ ๊ฒฝ์ฐ ์คํฌ๋ฆฝํธ๊ฐ ์ถฉ๋ํ ์ ์๋ค.
Shadow DOM: Shadow DOM ๋ด์ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ ์ธ๋ถ DOM๊ณผ ๋ ๋ฆฝ์ ์ผ๋ก ์๋ํ๋ค. Shadow DOM ๋ด์์ ์ค์ ํ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ ํด๋น DOM์๋ง ์ ์ฉ๋๋ฉด ์ธ๋ถ DOM์ ์์์๋ ๋ณ๊ฐ๋ก ์๋ํ๋ค. ์ด๋ ์ฝ๋์ ์ผ๊ด์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ๋์ฌ์ค๋ค.
์ผ๋ฐ DOM: UI ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฌ์ฉํ๋ ค๋ฉด ์คํ์ผ๊ณผ ์คํฌ๋ฆฝํธ๊ฐ ์ถฉ๋ํ์ง ์๋๋ก ์ ๊ฒฝ์จ์ ๊ด๋ฆฌํด์ผ ํ๋ค. ๊ทธ๋ก ์ธํด ์ฝ๋์ ๋ณต์ก์ฑ์ด ์ฆ๊ฐํ ์ ์๋ค.
Shadow DOM: Shadow DOM์ ์ฌ์ฉํ๋ฉด ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ปดํฌ๋ํธ๋ฅผ ์ฝ๊ฒ ๋ง๋ค ์ ์๋ค. ๊ฐ ์ปดํฌ๋ํธ๋ ๋ ๋ฆฝ์ ์ธ ์คํ์ผ๊ณผ ์คํฌ๋ฆฝํธ๋ฅผ ๊ฐ์ง๋ฏ๋ก, ๋ค๋ฅธ ํ๋ก์ ํธ๋ ์ปจํ ์คํธ์์ ์ฝ๊ฒ ์ฌ์ฌ์ฉ ํ ์ ์๋ค.
UI ๋ผ์ด๋ธ๋ฌ๋ฆฌ: ๋ณต์กํ UI ์์(ex. button
, modal
, dropdown
๋ฑ)๋ฅผ ๋ง๋ค ๋ Shadow DOM์ ์ฌ์ฉํ์ฌ ์คํ์ผ๊ณผ ๋์์ ๋
๋ฆฝ์ ์ผ๋ก ์ ์งํ ์ ์๋ค.
์น ์ปดํฌ๋ํธ: ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์น ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค ๋ Shadow DOM์ ํตํด ๊ฐ ์ปดํฌ๋ํธ์ ์คํ์ผ๊ณผ ๊ตฌ์กฐ๋ฅผ ๊ฒฉ๋ฆฌํ ์ ์๋ค.
Shadow DOM์ ์คํ์ผ๊ณผ ์คํฌ๋ฆฝํธ์ ์ถฉ๋์ ๋ฐฉ์งํ์ฌ ๋์ฑ ๊ฒฌ๊ณ ํ๊ณ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค ์ ์๊ฒ ํด์ค๋ค. ์ผ๋ฐ DOM์์๋ ๋ฒํผ์ ์ถ๊ฐํ๋ ๊ฒ๋ง์ผ๋ก๋ ์ด๋ฌํ ๊ฒฉ๋ฆฌ์ ๋ ๋ฆฝ์ฑ์ ๋ณด์ฅํ ์ ์๊ธฐ ๋๋ฌธ์, ๋ณต์กํ UI๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐ ์์ด Shadow DOM์ด ์ ๋ฆฌํ๋ค.
shadow DOM ์ฌ์ฉํ๊ธฐ - MDN
(๋ฒ์ญ) Shadow DOM์ ๋ฌด์์ผ๊น?
์ฒ์ ๋ค์ด๋ณธ ๊ฐ๋ ์ธ๋ฐ, ์คํ์ผ ๊ฒฉ๋ฆฌ๋๋๊ฒ ๋ ๊ฑฐ์ ์ฝ๋๋ฅผ ๊ฑด๋๋ฆฌ๊ฒ ๋๋ ๋ ์๋ ์ ์ฉํ ๊ฒ ๊ฐ๋ค์.
์ข์ ๊ธ ๊ฐ์ฌํฉ๋๋ค.