티스토리 뷰
다음 글의 번역입니다. [The state of Web Components - Wilson Page]
If writer does not want this article, please contact me.(han41858@gmail.com)
개발자들은 요즘 Web Components 에 대한 소식을 자주 접한다. Fronteers Conference 2011 에서 Alex Russel 가 처음 Web Components를 소개했다. 이 때 발표한 내용은 커뮤니티를 뒤흔들었고 미래 기술에 대해 이야기할 때 빠지지 않는 중요한 주제의 하나가 되었다.
2013년에 Google에서는 Web Component 기반의 프레임워크인 Polymer를 발표했고, 기존의 불편함을 해소하는 새로운 API를 도입하면서 커뮤니티의 피드백을 받아 문법 설탕(역주 : sugar, syntax sugar를 의미하며 같은 로직을 간단하게 구현할 수 있는 문법) 을 추가하고 의견들을 받고 있다.
4년이 지난 현재, Web Components는 많은 곳에 존재하고 있지만, 브라우저에서 Web Components의 '어떤 버전' 을 명시하고 있는 것은 Chrome 뿐이다. 주류 브라우저들이 Web Components를 관심있어 하기 전까지는 커뮤니티들의 환영을 받지는 못한 것 같다.
왜 이렇게 오래 걸렸나?
간단하게 말해서, 벤더들이 동의하지 않았다.
Web Components는 Google과 다른 브라우저들의 협상에 의한 결과물이다. 다른 협상과 마찬가지로, 참여자들의 열정이 없다면, 동의를 이끌어 낼 수 없을 것이다.
Web Components는 어마어마한 제안이었다. 초기 API는 구현하기 복잡했고, 벤더들의 논쟁과 반대만이 있었다.
그래도 Google은 계속해서 이것을 추진했고 피드백을 받으면서 커뮤니티들을 인수하기도 했지만, 다른 벤더들이 도입하기 전까지는 사용성에 문제가 있었다.
Polyfills을 이용하면 Web Components를 도입하지 않은 브라우저에서도 Web Components를 동작하게 했지만 '완성품' 정도는 아니었다.
이런 와중에, Microsoft는 Edge에서 동작하는 새로운 DOM API들을 추가하고 있었고, Apple도 Safari를 위한 것들에 집중하고 있었다.
Custom Elements
Web Components의 모든 기술들 중에 Custom Elements는 가장 논란이 적다. 부분적인 UI의 모양과 동작을 정의하고 이 조각을 브라우저와 프레임워크에 관계없이 배포할 수 있다는 점은 이미 어느정도 지지를 받고 있다.
'업그레이드'
전통적인 HTMLElement를 기존의 방식과 prototype을 이용하여 Custom Element로 만드는 것을 '업그레이드'라고 할 수 있다. 요즘은, elements 가 업그레이드 되면 createdCallback이 호출된다.
var proto = Object.create(HTMLElement.prototype); proto.createdCallback = function() { ... }; document.registerElement('x-foo', { prototype: proto });
여러 벤더들에 의해 5개의 제안이 있었지만, 모두가 동의하기 전까지는 2개가 유력해보인다.
'Dmitry'
좀 더 이후의 버전의 createCallback 패턴은 ES6에서도 잘 동작한다. createdCallback 컨셉은 살아남았지만, 클래스에 대한 내용은 일부 변경되었다.
class MyEl extends HTMLElement { createdCallback() { ... } } document.registerElement("my-el", MyEl);
요즘의 구현방식에서는 custom element는 HTMLUnknownElement로 생성되지만 이 객체는 미리 정의된 객체로 변환되고 createCallback를 호출한다.
그 이후의 진행은 플랫폼이 어떻게 동작하느냐에 따라 다르다. Element는 처음에 'unknown' 이었고, 최종적으로 특정 객체로 변하지만 동작이 같지는 않기 때문에 개발자들에게는 혼란을 줄 수 있다.
동기방식의 생성자
개발자에 의해 등록된 생성자(역주 : class constructor)는 custom element가 생성될때 파서에 의해 실행되고 트리에 들어간다.
class MyEl extends HTMLElement { constructor() { ... } } document.registerElement("my-el", MyEl);
이 방식이 합리적으로 보이지만, 초기에 다운로드 된 문서에 있는 custom element가 registerElement 를 비동기로 호출한다면 업그레이드에 실패할 수 있기도 하다. 비동기 ES6 모듈의 영역에 접근하기 어려운 점이다.
게다가 동기방식의 생성자는 .cloneNode() 와 연관된 플랫폼 이슈가 있다.
2015년 6월에 있는 미팅에서 벤더들에 의해 결정이 될 것으로 보인다.
is=""
is 속성을 이용하면 custom element에 대한 행동을 기본 엘리먼트를 통해 정의할 수 있다.
<input type="text" is="my-text-input">
찬성 의견
- 기본 엘리먼트를 확장할 수 있다. (예를 들면 문자로 접근하는 방식이나 <form> 컨트롤, <template>).
- 이 방식은 엘리먼트에 '점진적인 향상' 의 의미를 부여하기 때문에 JavaScript 없이도 함수형으로 동작한다.
반대 의견
- 문법이 헷갈린다.
- 플랫폼의 접근성(accessibility) 요소를 사용하지 못하게 하는 방법이다.
- 기본 엘리먼트를 '적절하게' 확장하는 방법한다는 것이 무엇인지 모른다.
- 용도가 제한적이다. 곧이어 개발자들은 Shadow DOM을 소개하면서, 모든 내장 접근성 기능을 잃었다.
Shadow DOM 은 벤더들 사이에서 가장 논쟁이 되고 있는 부분이다. 빠른 합의를 위해 이 기능은 'V1'과 'V2' 로 나뉘어져 진행되고 있다.
배포(Distribution)
배포 단계는 한 Shadow DOM 객체가 상위 Shadow DOM 에 들어가 화면에 표시되는 단계이다. 이제 당신이 만든 컴포넌트를 다른 사람의 컴포넌트 안에 넣어서 사용할 수 있다.
현재 API
현재 구현되어 있는 API는 명확하다. Shadow DOM 안에서 <content> 엘리먼트를 사용하여 위치할 부모를 지정할 수 있다.
<content select="header"></content>
그러나 Apple과 Microsoft에서는 복잡함과 성능의 문제로 이러한 방식을 거부했다.
새로운 API
벤더들이 직접 만나는 미팅에서도 API에 대한 명확한 합의가 이루어 지지는 않았고, 어쩔 수 없이 필수적인 것들만 합의하게 되었다.
Microsoft, Google, Apple, Mozilla, 이렇게 4개의 벤더들은 2015년 7월을 목표로 새로운 API를 구체화하는 작업에 착수했다. 그 결과, 3개의 제안이 나왔다. 셋 중에 가장 간단한 것은 이런 방식이다.
var shadow = host.createShadowRoot({ distribute: function(nodes) { var slot = shadow.querySelector('content'); for (var i = 0; i < nodes.length; i++) { slot.add(nodes[i]); } } }); shadow.innerHTML = ''; // 초기 호출 shadow.distribute(); // 이후 MutationObserver를 후킹
이 방식의 큰 위험요소는 타이밍이다. MutationObserver 콜백이 실행되는 시점에, 자식 객체가 변경되어 재배포 된다면 레이아웃 속성을 찾지 못하고 잘못된 결과가 나올 수 있다.
myHost.appendChild(someElement); someElement.offsetTop; //=> 이전 값 // mutation observer 콜백에 의해 재배포 (비동기) someElement.offsetTop; //=> 새로운 값
offsetTop에서 새로운 값을 참조하고 싶겠지만, 중간에 있는 콜백에 의한 레이아웃 재배포는 비동기로 실행되기 때문에 원하는 값을 얻을 수 없을 것이다.
이런 코드가 항상 문제가 되지는 않겠지만, 스크립트나 브라우저는 스크롤과 같은 동작에서 내부적으로 offsetTop과 같은 값의 보정하기 때문에 종종 문제가 될 수 있다.
이런 문제는 명확한 API에 대한 논의가 제대로 수행되기 전까지는 해결할 수 없을 것이다. 하지만 Apple 에서 제안한 "named slots" API 에서는 이 문제를 회피할 수 있다.
새로운 API의 정의 - 'Named Slots'
'named slots' 은 이전에 'content select' API를 좀 더 단순화했으며, 배포하려고 하는 컴포넌트에 대해 명확한 설명을 요구한다.
<x-page>에 대한 Shadow Root에 대해 정의는 방식은 아래와 같다.
some shadow content
<x-page>를 사용하는 방법은 아래와 같다.
header my page title
my page content
이 컴포넌트가 트리에 렌더링되어 사용자가 보는 모습은 아래와 같다.
header my page title
my page content
some shadow content
브라우저는 shadow 부모(myXPage.children)를 직접적인 자식 객체로 보고 <slot> 객체의 속성과 맞는 이름이 있다면 shadowRoot를 연결할 것이다.
같은 속성을 찾았다면 연관된 <slot> 엘리먼트와 연결되어 '배포' 된 것처럼 보일 것이다. 연결 단계에서 같은 속성을 찾지 못한 다른 자식 객체들은 그대로 이름 없는 <slot> 엘리먼트로 대체될 수 있다.
찬성 의견 :
- 배포는 좀 더 명확하고 알아보기 쉽다.
- 브라우저 엔진이 연산하기에 좀 더 간단하다.
반대 의견 :
- <select>와 같은 이미 있는 엘리먼트들은 어떻게 해야 하는지에 대한 내용이 없다.
- slot 속성을 모두 추가하는 것은 사용자에게 부담이 된다.
- 표현방식이 좀 이상하다.
4월에 있었던 미팅에서 이런 '모드' 가 필요하다는 것이 확실해졌지만, 벤더들은 기본값이 '열린' 상태여야 하는지 '닫힌' 상태여야 하는지 합의하지 못했다. 그 결과로, V1 에서는 '모드' 에서 인자를 받기로 합의했고, 기본 값에 대해서는 미리 정할 필요가 없어졌다.
element.createShadowRoot({ mode: 'open' }); element.createShadowRoot({ mode: 'closed' });
-- 내용이 많아서 두 개의 글로 나눕니다 --
- Total
- Today
- Yesterday
- typescript
- Angular 5.0.0
- Angular HttpClientModule
- 커스텀 컴포넌트
- DENO
- 2017 티스토리 결산
- 양방향 바인딩
- Angular
- Angular 7.0.0
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |