미분류/Tip

왜 Web Component를 사용하는가?

DarrenKwonDev 2020. 11. 14. 16:59

SPA 유행이 말해준 것은 SPA가 중요한 것이 아니라 component drive development가 중요하다는 것이었다. 

SPA의 단점을 극복한 Post-React 시대의 대안 중 주목 받는 것이 Web Component를 기반으로한 기술이다.

 

Web Components

 

HTML이 기본으로 제공하는 엘리먼트는 브라우저와 운영체에제 따라 다르게 보이는 경우도 있고, 발전하는 웹 환경에 대응하기에 한계가 있습니다. 여기서 js 기반 컴포넌트(예를 들어 naver에서 만든 jindo js)가 대안으로 제시되었으나 

JavaScript 컴포넌트를 사용하려면 스크립트 파일과 CSS 파일을 포함하고 특정한 마크업 구조를 사용해야 하며, JavaScript 컴포넌트는 상당한 크기의 JavaScript 덩어리라 성능이 낮은 기기에서는 원활한 작동이 힘들다는 단점도 있다.

 

 

아 너무 모섭다 ㄷㄷ

 

W3C에서는 이러한 문제를 개선하고자 웹 컴포넌트(Web Component)라는 명세(Specification)를 만들었다.

웹 컴포넌트란, 개발자가 자체적으로 HTML 엘리먼트를 만드는 기술이다.

 

웹 컴포넌트는 템플릿(Templates), 데코레이터(Decorators), 커스텀 엘리먼트(Custom Element), 섀도 DOM(Shadow DOM)에 관해 알아보자.

 

 

템플릿(Templates)

<template>
    <div class="slide">
        <ul>
            <content select="li"></content>
        </ul>
    </div>
</template>  

<template> 태그에 있는 엘리먼트는 DOM의 구조를 가지고 있지만 렌더링되지 않으며 이미지와 같은 리소스 파일을 내려받지 않는다. 그리고 <content> 태그의 select 속성에 CSS 선택자(CSS Selector)를 이용해 엘리먼트를 넣을 수 있다. 

 

 

데코레이터(Decorators)

데코레이터는 엘리먼트를 오버라이드(override)해 엘리먼트를 꾸미는 역할을 한다. 비교를 하자면 테코레이터는 리모델링이라고 생각하면 되고, 뒤에 설명할 커스텀 엘리먼트는 새로 만드는 것으로 생각하면 된다.

 

템플릿 엘리먼트에 적당한 엘리먼트 구조를 만들고 스타일을 지정한 다음에 <decorator> 태그를 이용하여 만든다.

 

데코레이터에는 listen 메서드로 이벤트를 할당할 수 있다. this.listen 메서드는 오브젝트를 인자로 받으며 오브젝트에는 선택자(selector), 타입(type), 핸들러(handler)를 등록하여 이벤트를 관리한다.

<decorator id="decorator-event-demo">
    <script>
        function h(event) {
            alert(event.target);
        }
        this.listen({selector: "#b", type: "click", handler: h});
    </script>    
    <template>
        <content></content>
        <button id="b">Bar</button>
    </template>
</decorator>  

 

 

커스텀 엘리먼트(Custom Elements)

테코레이터가 단순히 엘리먼트에 스타일을 적용하는 것이라면 커스텀 엘리먼트는 개발자가 새로운 엘리먼트를 만드는 것과 같다. 그래서 다양한 기능이 있는 커스텀 엘리먼트를 만들 수 있다. 커스텀 엘리먼트는 엘리먼트를 상속하고 있어 createElement 메서드나 생성자로 만들 수 있다.

 

데코레이터와 사용 방법은 유사하다. 다만 <decorate> 태그가 아니라 <element> 태그를 사용한다.

 

<element> 태그의 속성을 살펴보면,

name 속성은 커스텀 엘리먼트의 이름을 지정한다.

extends 속성은 어떤 엘리먼트를 확장했는지 지정한다.

constructor 속성은 커스텀 엘리먼트의 생성자 이름이며 스크립트로 커스텀 엘리먼트를 만들 때 사용한다.

<element name="x-slide" extends="ul" constructor="SlideControl">  
    <template>
        <div class="slide">
            <ul>
                <content select="li"></content>
            </ul>
        </div>
    </template>
    <script>
        SlideControl.prototype = {
            currentNum : function(){},
            lastNum : function(){}
        }
        this.lifecycle({
            created: function(root) {}
        });
    </script>
</element>  

 

이렇게 만든 커스텀 엘리먼트를 HTML 파일로 만들고 다음과 같이 <link> 태그를 이용하여 파일에 적용한다.

<link rel="components" href="컴포넌트를 저장한 경로/파일.html">  

 

그리고 다음 코드와 같이 커스텀 엘리먼트 이름에 is 속성으로 <element> 태그의 name 속성 값을 넣으면 컴포넌트가 적용된다.

<x-slide is="x-slide">  
    <li><img src="http://helloworld.naver.com/img/1.jpeg" alt="1.jpeg" width="500px" height="333px" style=""></li>
    <li><img src="http://helloworld.naver.com/img/2.jpeg" alt="2.jpeg" width="500px" height="333px" style=""></li>
    <li><img src="http://helloworld.naver.com/img/3.jpeg" alt="3.jpeg" width="500px" height="333px" style=""></li>
    <li><img src="http://helloworld.naver.com/img/4.jpeg" alt="4.jpeg" width="500px" height="333px" style=""></li>
</x-slide>  

 

 

섀도 DOM(Shadow DOM)

섀도 DOM은 DOM의 구조를 가지고 있으나 외부에는 노출되지 않은 DOM을 말하며 DOM의 구조를 캡슐화할 때 사용한다. 일반적인 외부의 style은 적용되지 않고 섀도 DOM을 추가하거나 접근하기 위해서는 별도의 방법이 필요하다. 그리고 위에서 설명한 템플릿 엘리먼트를 이용해 만들어진 데코레이터나 커스텀 엘리먼트는 모두 섀도 DOM으로 만들어진다. 다만, 데코레이터에서 만들어진 섀도 DOM은 스크립트로 접근하거나 수정할 수 없다. 그에 비해 커스텀 엘리먼트로 만들어진 섀도 DOM은 스크립트로 수정할 수 있다.

다음 그림을 보면 왼쪽이 일반적인 DOM 트리이며 오른쪽이 섀도 DOM 트리이다.

그림 3 DOM 트리와 섀도 DOM 트리 적용 전

코드로 본다면 다음 코드가 왼쪽에 있는 DOM 트리의 코드다.

<x-slide is="x-slide"> <li><img src="http://helloworld.naver.com/img/1.jpeg" alt="1.jpeg" width="500px" height="333px" style=""></li> <li><img src="http://helloworld.naver.com/img/2.jpeg" alt="2.jpeg" width="500px" height="333px" style=""></li> <li><img src="http://helloworld.naver.com/img/3.jpeg" alt="3.jpeg" width="500px" height="333px" style=""></li> <li><img src="http://helloworld.naver.com/img/4.jpeg" alt="4.jpeg" width="500px" height="333px" style=""></li> </x-slide>

그리고 다음 코드가 섀도 DOM이 된다.

<element name="x-slide" extends="ul" constructor="SlideControl"> <template> <div class="slide"> <ul> <content select="li"></content> </ul> </div> </template> </element>

기존의 컴포넌트는 일반적인 DOM 트리가 렌더링된 후 해당 DOM 트리를 변경하기 때문에 리플로(reflow)와 리페인트(repaint) 등의 큰 비용이 발생한다. 하지만 섀도 DOM을 사용하면 다음 그림과 같이 섀도 호스트(shadow host), 위의 코드로 말하면 x-silde 엘리먼트를 만나는 순간 섀도 DOM이 렌더링된다.

그림 4 DOM 트리와 섀도 DOM 트리 적용 후

즉, 위의 커스텀 엘리먼트가 렌더링된 모습을 섀도 DOM과 같이 보면 다음 그림처럼 표현이 된다.

그림 5 섀도 DOM

실제로 렌더링되어 표현된 모습은 다음과 같다.

<div class="slide"> <ul> <li><img src="http://helloworld.naver.com/img/1.jpeg" alt="1.jpeg" width="500px" height="333px" style=""></li> <li><img src="http://helloworld.naver.com/img/2.jpeg" alt="2.jpeg" width="500px" height="333px" style=""></li> <li><img src="http://helloworld.naver.com/img/3.jpeg" alt="3.jpeg" width="500px" height="333px" style=""></li> <li><img src="http://helloworld.naver.com/img/4.jpeg" alt="4.jpeg" width="500px" height="333px" style=""></li> </ul> </div>

그리고 탬플릿에 보면 content 엘리먼트가 있는데 이 엘리먼트를 이용하여 섀도 호스트에 있는 엘리먼트를 재배치할 수 있다. content 엘리먼트의 select 속성에는 CSS 선택자가 들어가며 섀도 호스트 밑에 있는 엘리먼트를 재배치할 수 있다.

예를 들면, 아래 그림에서 오른쪽의 insertion point가 content 엘리먼트가 들어가는 자리이다.

그림 6 insertion point 적용 전

DOM 트리가 렌더링되다가 섀도 호스트를 만나면 섀도 DOM 트리가 렌더링되고 섀도 DOM 트리의 content(insertion point)에서 섀도 호스트 밑에 있는 엘리먼트를 재배치하여 다음과 같이 렌더링된다.

그림 7 insertion point 적용 후

이렇게 content을 이용하여 컴포넌트에서 활용할 수 있다.

위와 같은 방법 말고도 다음과 같은 방법으로 스크립트를 이용하여 생성할 수 있다.

var host = document.querySelector("#module"); var root = new WebKitShadowRoot(host); var paragraph = document.createElement('p'); paragraph.textContent = 'in the shadow'; root.appendChild(paragraph);

그리고 섀도 DOM 방식은 SVG와 같이 복잡한 마크업을 단순하게 만들 수 있어 SVG와 같이 복잡한 마크업을 가지는 곳에서 활용하기 좋다.

마치며

현재 웹 컴포넌트 명세는 굉장히 미흡하며, 완벽하게 지원하는 브라우저도 없다. Chrome 21 이상에서만 일부 기능이 동작한다. 또한 템플릿의 기능을 이야기할 때 100개의 댓글이 달릴 정도로 논란도 많은 명세다. 그래서 이 글에서 설명한 내용은 언제든지 변경될 여지가 있다.

그래도 웹컴포넌트가 주는 의미는 크다.

기존의 외부라이브러리를 사용해야만 가능했던 기능이 대부분 HTML5의 기능으로 빠르게 대체되고 있다. 컴포넌트 또한 각 라이브러리끼리 서로 다른 방법으로 컴포넌트를 구현했지만 이제는 사용자가 사용하기 좋은 표준화된 방법으로 기존의 컴포넌트들이 재작성될 것이고 성능도 더 좋아질 것이라고 생각한다.

 

 

웹 컴포넌트는 점점 표준으로 자리잡아가고 있다. 점점 웹 컴포넌트의 위상이 증가하고 있다. Storybook에서도 Web Components를 지원하고 있으니 점차 비중이 높아지지 않을까... 하는 생각이다.

 

참고한 글)

 

 

리액트(react) 잘 쓰고 있는데, 왜 웹 컴포넌트(Web Component)가 필요할까?

최근 몇년간의 웹 생태계는 마치 전쟁터와 같았다. 아니, 지금도 전쟁중이다. 각종 도구들부터 시작해서 웹 프레임워크까지, 한시라도 눈을 떼면 뒤쳐져버리기 일쑤다. 요즘, 국내에서도 프론트

blueshw.github.io

 

웹 컴포넌트

개발자로서 우리 모두는 가능한 한 코드를 재사용하는 것이 좋은 생각이라는 것을 알고 있습니다. 이는 전통적으로 커스텀 마크업 구조에선 쉽지 않았습니다. 커스텀 UI 컨트롤을 렌더링하기위

developer.mozilla.org

d2.naver.com/helloworld/188655