[JavaScript] 바닐라 자바스크립트로 웹 컴포넌트 만들기

자바스크립트만 사용해서(제이쿼리, 리액트, 뷰 등을 사용하지 않고) 웹 컴포넌트를 만드는 방법에 대해 알아보겠습니다.
바닐라 자바스크립트란?
프레임워크나 라이브러리가 포함되지 않은 순수한 자바스크립트를 의미합니다.
웹 컴포넌트
HTML 엘리먼트를 위해 만들어진 재사용 가능한 자바스크립트 코드를 의미합니다. 재사용이 가능하다는 것은 코드가 함수 등으로 작성되어 언제든 임의의 HTML 엘리먼트에 적용할 수 있다는 것을 의미합니다.
TLDR;
- 함수를 이용해 만드는 방법은 쉽고 빠르게 작성할 수 있어 웹 컴포넌트를 구현할 때 가장 많이 쓰이는 방식
- MutationOvserver는 IE11 이하 버전을 지원하지 않으며, 모든 엘리먼트의 추가 / 제거를 감시해 성능 문제를 일으킬 수 있음
- 커스텀 엘리먼트를 만드는 방법은 깔끔하지만, 커스텀 엘리먼트를 중첩해서 사용할 수 없음.
함수를 이용한 방법
함수를 실행해 엘리먼트에 자바스크립트를 적용하는 방법입니다.
<span class="custom">Click me</span>
function customComponent(elements) { for (const element of elements) { element.onClick = function() { element.textContent = "Already clicked"; }; } } customComponent(document.querySelectorAll(".custom"));
위 방법은 동적으로 자바스크립트에서 HTML을 생성하는 경우, 함수를 실행시켜주는 것을 잊어버린다면 적용이 되지 않는다는 단점이 있습니다. 하지만, 간단하게 구현할 수 있고 호환성 문제와 성능 저하 문제 없이 구현할 수 있다는 장점이 있어서 웹 컴포넌트를 구현할 때 많이 쓰이는 방법입니다.
MutationObserver를 이용한 방법
MutationOvserver
를 이용해 class
나 dataset
과 같은 HTML 속성 값의 변화를 감지하는 방법이 있습니다.
MutationObserver이란?
함수를 이용해서 엘리먼트에 자바스크립트를 적용하는 방법은 동적으로 추가된 엘리먼트를 놓칠 수 있다는 문제가 있습니다. 이러한 문제를 피하기 위해 DOM의 변화를 감지하는 MutationObserver
를 사용할 수 있습니다.
기본적인 사용 방법은 다음과 같습니다.
// 변화를 감지할 노드를 선택 const targetNode = document.querySelector(".target"); // 변화 감지에 대한 옵션 설정 const config = { attributes: true, childList: true, subtree: true }; // 변화가 감지될 때 실행할 콜백 함수 const callback = function (mutationList, observer) { for (const mutation of mutations) { if (mutation.type === "attributes") { console.log(mutation.attributeName + ' 속성이 수정되었습니다.'); } else if (mutation.type === "childList") { console.log('자식 노드가 추가되거나 제거되었습니다.'); } } }; // 실행 const observer = new MutationObserver(callback); observer.observe(targetNode, config);
disconnect
함수를 이용해 관찰을 중지할 수 있습니다.
observer.disconnect();
option 설정
사용하는 옵션에 따라 mutation의 실행 방법이 달라지므로 정확한 설정 값이 필요합니다.
속성 | 값 | 설명 |
childList | true / false | 대상 노드의 하위 요소가 추가되거나 제거되는 것을 감지합니다. |
attributes | true / false | 대상 노드의 속성 변화를 감지합니다. |
characterData | true / false | 대상 노드의 데이터 변화를 감지합니다. |
subtree | true / false | 대상의 하위의 하위의 요소들까지의 변화를 감지합니다. |
attributeOldValue | true / false | 변화 이전의 속성 값을 기록합니다. |
characterDataOldValue | true / false | 변화 이전의 데이터 값을 기록합니다. |
attributeFilter | ["...", "..."] | 모든 속성의 변화를 감지할 필요가 없는 경우 속성을 배열로 설정합니다. |
위 표는 모든 옵션에 대한 설명입니다.
구현 방법
<span class="custom">Click me</span>
for (const element of document.querySelectorAll(".custom")) { element.onClick = function() { element.textContent = "Already clicked"; }; } const config = { attributeFilter: ["class"], childList: true, subtree: true }; const callback = function (mutations) { for (const mutation of mutations) { if (mutation.type === "attributes") { if (mutation.target.classList.contains("custom")) { mutation.target.onclick = function() { element.textContent = "Already clicked"; }; } else { mutation.target.onClick = null; } } else if (mutation.type === "childList") { for (const addedNode of mutation.addNodes) { if (addedNode instanceof Element) { if (addNode.classList.contains("custom")) { addedNode.onclick = function() { element.textContent = "Already clicked"; }; } } } } } }; new MutationOvserver(callback) .observe(document, config);
함수와 비교했을 때 코드가 많이 복잡하고 한 눈에 보기 어렵다는 단점이 있습니다. 엘리먼트의 속성 값의 변화와 자식 엘리먼트의 변화에 따라 다루는 방법이 달라 코드가 매우 길어집니다.
또한, 위 방법은 모든 엘리먼트의 추가 / 제거를 감지해서 반복적으로 엘리먼트가 추가되거나 제거되는 상황에서는 성능 문제를 일으킬 수 있으며, IE11 이하 버전에서는 동작하지 않습니다.
customElements를 이용한 방법
customElements
를 이용해 웹 컴포넌트를 구현할 수 있습니다.
customElements란?
Window
인터페이스 내 customElements
속성은 요소를 등록하거나 이전에 등록한 요소에 대한 정보를 받아올 수 있는 CustomElementRegistry
객체의 참조를 반환할 수 있습니다.
예제
customElements
를 사용하는 가장 흔한 예시는 새로운 요소를 등록하기 위해 CustomElementRegistry.define()
메소드에 적용하는 것입니다.
const customElementRegistry = window.customElements; customElementRegistry.define("my-custom-element", MyCustomElement);
다음과 같이 줄여서 사용할 수 있습니다.
customElements.define( "element-details", class extends HTMLElement { constructor() { super(); const template = document .getElementById("element-details-template") .content; const shadowRoot = this.attachShadow({mode: "open"}) .appendChild(template.cloneNode(true)); } } );
구현
<custom-element>Click me</custom-element>
customElements.define( "custom-element", class extends HTMLElement { constructor() { super(); this.onclick = function() { element.textContent = "Already clicked"; }; } } );
customElements
를 이용한 방법은 코드의 가독성도 좋고 깔끔하다는 장점이 있지만, 여러 엘리먼트에 동일한 커스텀 엘리먼트를 중첩해서 적용할 수 없다는 단점이 있습니다.
'Advanced > JavaScript' 카테고리의 다른 글
[JavaScript] 함수를 콜백으로 전달할 때 (0) | 2021.02.12 |
---|---|
[JavaScript] Virtual DOM 만들기 (2) | 2021.01.26 |
[JavaScript] Element를 저장 및 불러오기 (0) | 2021.01.04 |
[JavaScript] Selection과 Range (0) | 2020.12.29 |
[JavaScript] 캘린더 만들기 - 한 주 구하기 (0) | 2020.12.20 |
댓글
이 글 공유하기
다른 글
-
[JavaScript] 함수를 콜백으로 전달할 때
[JavaScript] 함수를 콜백으로 전달할 때
2021.02.12메일로 한 통의 미디엄 글을 받았습니다. 해당 글의 경우 parseInt를 map에 전달했을 때 이상한 값이 나온다는 내용이었는데 코드는 다음과 같습니다. ['1', '7', '11'].map(parseInt); // [1, NaN, 3] ['1', '7', '11'].map(Number); // [1, 7, 11] 위와 같이 나오는 이유에 대한 글인데, parseInt와 Number의 매개변수는 각각 다음과 같습니다. parseInt: string, radix Number: value Number에서는 잘 동작하지만 parseInt에서는 값의 결과가 깨지는 이유는 array.map의 콜백으로 parseInt를 사용하는 것이 함수 설계에 맞지 않기 때문입니다. 무엇이 문제일까? const numList … -
[JavaScript] Virtual DOM 만들기
[JavaScript] Virtual DOM 만들기
2021.01.26 -
[JavaScript] Element를 저장 및 불러오기
[JavaScript] Element를 저장 및 불러오기
2021.01.04 -
[JavaScript] Selection과 Range
[JavaScript] Selection과 Range
2020.12.29
댓글을 사용할 수 없습니다.