웹 컴포넌트(4) — 템플릿 엘리먼트(Template Element)와 HTML Imports

이번 글은 웹 컴포넌트 소개 연재 4번째로 템플릿 엘리먼트(Template Element)와 HTML Imports에 대한 글이다. 서두에 밝히자면, 이 두 표준의 흐름은 웹 컴포넌트 개발에서 제외될 것으로 보인다. 따라서 두 표준을 자세히 알아보기보다는 이것들이 웹 컴포넌트로써 사용되게 된 배경과 한계점, 그리고 Polymer Summit 2017에서 나온 현재의 동향을 이야기로 풀어보았다. 직접 실습할 내용도 없으니 가볍게 이야기 읽듯 따라가 보자.

고전적인 템플릿

자바스크립트에서 HTML 엘리먼트를 만드는 일은 일반적이다. 고민 없이 엘리먼트를 하나 만들어서 body에 붙여보자. document.createElementappnedChild 두 개의 함수 실행으로 간단히 할 수 있다.

그러나 복잡한 DOM을 구성할 때 이런 방식을 적용하면 코드는 너무 길어져 버리고, DOM 구조를 파악하기도 괴롭다. 유지보수 하기도 힘들어지므로 이렇게 쓰고 싶지는 않다. 복잡한 템플릿을 작성할 때에는 innerHTML을 사용하는 것이 편하다. 아래의 코드는 더욱 직관적이면서 유지보수 가능한 형태로 보인다.

그러나 이 방법도 리스트처럼 반복적인 엘리먼트를 생성해야 하거나, 조건에 따라 템플릿이 바뀌어야 하는 경우 등의 다양한 요구사항을 해결하기에는 부족하다. 더불어 내부의 엘리먼트에 접근하기 위해서는 querySelector를 사용해야하는 점도 불편하다.

템플릿 엔진들

문자열의 간단한 조합에서 해결할 수 없는 Loop, If 등의 요구사항들을 해결하기 위해 템플릿 엔진들이 사용되기 시작했다. MustacheHandlebars 등 템플릿 엔진들은 비슷한 목적으로 쓰이면서도 각자의 개성을 가지고 있다. 유명한 Vue.jsAngularPolymer(과거) 들도 카테고리에 속한다 할 수 있다. 이 템플릿 엔진 방식은 여전히 많은 상황에서 유용하게 사용되고 있다.

JSX + Virtual DOM

프론트엔드 개발이 컴포넌트화 되면서, 컴포넌트들이 그리는 여러 개의 템플릿들을 효율적으로 처리해주는 리엑트(React: JSX + Virtual DOM)가 인기를 얻었다. JSX는 ESCAScript의 확장으로 스크립트 내부에 직접 템플릿을 작성할 수 있게 해주며, Virtual DOM은 주어진 상태에 따라 DOM의 일부분만 업데이트 해주는 역할을 한다. 템플릿 관점에서 보자면, 리엑트는 상태에 따라 템플릿을 컴포넌트 단위로 업데이트 해주는 일을 한다고 할 수 있다. JSX + Virtual DOM은 현재 가장 인기있는 템플릿 처리 방식이라 말할 수 있다.

템플릿 엘리먼트

드디어 템플릿 엘리먼트(Template Element)의 이야기 이다. 템플릿 엔진들은 유용하지만 문자열로 처리되기에 어쩔 수 없는 문제점들을 내포한다. XSS 공격에 노출될 위험이 있으며, innerHTML의 사용이 강제된다. DOM API들을 템플릿에 사용할 수 없다는 것들이다. 이러한 문제들을 해결하기 위해 템플릿의 문자열 처리를 지양하고 엘리먼트로 처리하는 방법으로 템플릿 엘리먼트가 표준으로 만들어 졌다.

템플릿 엘리먼트는 자바스크립트 코드로 많은 양의 코드를 적지 않아도 되고, 조건에 따라 DOM의 변경도 가능하다. 이러한 변경은 강력한 DOM API들을 그대로 사용할 수 있어 편리하다. 템플릿 엘리먼트는 DOM에 한 번 정의되면 필요할 때마다 복사하여 붙여넣기 때문에 성능도 훌륭하다.

템플릿 엘리먼트 + 웹 컴포넌트

이러한 장점들으로 템플릿 엘리먼트는 웹 컴포넌트를 구성하는 표준이 되었다. 템플릿 엘리먼트는 스크립트와 스타일도 포함할 수 있다. 스크립트와 스타일은 템플릿에 있을때는 적용되지 않지만, 복사되어 Document에 붙을 때에 적용된다. 쉐도우 돔(Shadow DOM)과 시너지를 일으켜 웹 컴포넌트의 템플릿 기능을 수행하는 데 충분한 장점이라 할 수 있다.

그러나, 템플릿 엘리먼트는 템플릿이 HTML로 작성되어야 한다는 것이 오히려 단점이 되기도 한다. 컴포넌트의 컨트롤러에 해당하는 자바스크립트와 템플릿 뷰에 해당하는 HTML이 분리되어야 한다는 점이다. 컴포넌트와 모듈이 정확히 동의어라고 할 수는 없지만, 모듈로서 분리되어 재사용 될 수 있어야 의미가 있다. 그런데 템플릿 엘리먼트와 커스텀 엘리먼트(Custom Elements)를 컴포넌트로 구성하려면 HTML, JS 두 개의 파일이 필요하다. 이 방식은 웹 컴포넌트를 모듈로 만들기에 큰 걸림돌이다.

구원자 HTML Imports

이제 HTML Imports가 역할을 할 때가 되었다. HTML Imports는 스크립트 기반의 CommonJS, RequireJS와는 달리 HTML 기반의 의존성 해결사 역할을 한다. HTML에서 HTML을 읽어와 붙여주기에 아래처럼 깔끔한 처리도 가능하다. HTML Imports의 문법은 스타일을 연결하는 것과 유사하지만 스크립트, 스타일, HTML 엘리먼트들의 의존성을 한 번에 해결해 주는 강력한 방법이다.

HTML Imports는 HTML과 자바스크립트 두 개의 의존성을 해결해야만 하는 숙명의 웹 컴포넌트의 구원자이다. 이를 적용하여 웹 컴포넌트를 구성하면 아래의 모습이 된다. 이전 시간까지 알아보았던 모든 웹 컴포넌트 표준들(Custom Elements, Shadow DOM, Template Element, HTML Imports)을 모두 조합해보자. 아래 보이는 HTML로 컴포넌트를 구성하는 모습은 모양과 처리 방법은 다르나 Polymer와 Vue.js에서도 비슷한 모습이다.

Firefox는 HTML Imports를 싫어해

구글을 필두로 한 웹 컴포넌트 그룹은 위의 모양으로 커스텀 엘리먼트(Custom Elements), 쉐도우 돔(Shadow DOM), 템플릿 엘리먼트(Template Element), HTML Imports 4개의 표준을 웹 컴포넌트 표준이라 정했다. 이후 템플릿 엘리먼트는 가장 먼저 안정화 되었고 IE를 제외한 모든 브라우저에서 네이티브로 지원한다. 커스텀 엘리먼트와 쉐도우 돔은 구현에 있어 몇 가지 의견을 수렴하면서 v1 스펙이 정의되어, IE를 제외한 브라우저에서 이미 지원되고 있거나 개발 중이다. 그런데 HTML Imports에서 문제가 생겼다. Firefox가 HTML Imports를 지원하지 않겠다고 선언한 것을 시작으로 다른 브라우저들도 구현에 나서지 않고 있기 때문이다.

Mozilla will not ship an implementation of HTML Imports. We expect that once JavaScript modules — a feature derived from JavaScript libraries written by the developer community — is shipped, the way we look at this problem will have changed. We have also learned from Gaia and others, that lack of HTML Imports is not a problem as the functionality can easily be provided for with a polyfill if desired. 

Mozilla and Web Components

웹 컴포넌트 옹호론자들은 꾸준히 HTML Imports를 구현할 것을 요구했지만, 실현되지 않을 것으로 보인다. HTML Imports에 여러가지 문제가 제기되었는데 그 중 몇 가지는 아래와 같다. 브라우저 제작사들은 이들 이유가 타당하다고 판단한 것 같다. — The Problem With Using HTML Imports For Dependency Management

  • 이미 ES Module 표준이 만들어지고 있다. (현재는 이미 모든 브라우저에서 지원되고 있거나 개발 중이다)
  • 브라우저만을 위한 표준화 작업을 또 하고 싶지 않다.
  • 표준이 아니어도 폴리필로 매우 쉽게 구현 가능하다. (현재 폴리필도 1000라인이 안되며, 기능 구현만 고려하면 500여 라인 정도면 충분하다)
  • HTTP/2를 사용하면 여러 개 파일을 빠르게 받을 수 있다 해도, 여전히 번들링한 단일 파일을 받는 것이 빠르다. (정확히 표준에 관한 이야기는 아니지만, 구조상 같이 언급되는 경우가 많다)
  • De-Duping 할 수 없음(CDN 경로를 사용할 경우를 예상할 수 있는데, 동일한 파일들을 파악하여 처리하기 힘들다. ex) code.jquery.com/jquery-2.1.1.min.js vs maxcdn.bootstrapcdn.com/bootstrap/2.3.0/js/bootstrap.min.js)

위에서 제기한 이유들 외에도 실제 프로젝트를 구성해보려면 바로 만나게 되는 큰 장벽이 있다. 웹팩(Webpack)을 사용할 수 없다. 이미 표준 같은 힘을 지닌 웹팩, 롤업과 같이 사용할 수 없는 점은 치명적이다. 현재는 polymer-webpack-loader가 나와있지만 겨우 2달 되었고 직접 사용해본 결과 기본적인 문제들의 해결이 필요해 보인다. 결국, 웹 컴포넌트를 작성하기 위해서는 폴리머(Polymer)등의 지원 프레임워크와 독립적인 도구들을 사용해야 한다는 결론으로 흐르게 된다.

HTML Imports NO! Template Literals YES?

HTML Imports는 브라우저 지원이 안 될 것이라는 것, 웹팩 생태계와 따로 놀고 있다는 점등을 인식하고 결국 방향이 바뀌고 있다. 본인 의견으로도 Polymer가 웹팩 로더만 빠르게 지원했어도 더 나은 결과가 나오지 않았을까 생각한다. 지난주 있었던 Polymer Summit 2017에서는 Polymer 3.0 preview를 발표하며 새로운 방향을 보여주었다. 바로 HTML Imports를 포기하는 것이다. HTML Imports는 ES Modules로 대체되었고, 템플릿 엘리먼트는 템플릿 리터럴(Template literals)로 바뀌었다. 이로써 웹팩 생태계의 시민이 된 것이다. 의존성 문제는 해결되었지만, 템플릿은? 결국, 문자열 기반의 템플릿으로 돌아가면 템플릿 엘리먼트의 장점을 잃어버리게 되는 것 아닌가? 다행히도 ES6 템플릿 리터럴은 단순한 문자열이 아니고 템플릿 역할을 수행할 멋진 가능성을 가지고 있다.

이것 으로는 부족하다 여길 것이다. 다행히도 최근 hyper(HTML)lit-htmlhyperx등의 템플릿 리터럴을 사용한 프로젝트가 인기를 얻고 있다. lit-html의 설명에 따르면 이는 템플릿 리터럴를 받아 템플릿 엘리먼트를 생성한다. 만약 같은 템플릿으로 엘리먼트를 재생성하면, 이미 생성한 템플릿 엘리먼트를 사용해서 효율을 높이는 방향으로 만들어졌다고 한다. 문자열을 사용하되 템플릿 엘리먼트의 장점을 흡수하고자 하는 것이다. 크기도 minified기준으로 2kb가 채 안 된다고 하니, 바닐라 자바스크립트를 지향하는 경우에도 템플릿으로는 템플릿 리터럴를 이용한 이들 프로젝트를 사용하는 것이 좋아 보인다.

종합하면, 모양새는 커스텀 엘리먼트와 쉐도우 돔이 자리를 지키고, HTML Imports의 하차로 인해 템플릿 엘리먼트까지 쓰지 못하게 되었다. 그리고 빈 자리를 표준인 ES Modules와 템플릿 리터럴이 차지할 것으로 보인다. 그리고 템플릿 리터럴을 활용한 프로젝트들을 주시할 필요가 있겠다. Polymer Summit의 발표가 보여준 방향이 웹 컴포넌트의 방향을 정한다 볼 수 없으나, 많은 사람이 고민하고 있던 문제들이 해결되는 모습이다. 무엇보다 웹팩의 시민이 되었으니 앞으로 웹 컴포넌트의 행보가 탄력을 받을 것으로 예상해본다.

마치며

오늘 템플릿 엘리먼트와 HTML Imports에 대해 생각하며, 이미 힘을 잃은 이 두 기술을 어떻게 쓸지 고민이 많아 시작이 쉽지 않았다. 두 기술을 자세히 설명하는 것은 의미 없을 것으로 생각해 프론트엔드에서의 템플릿의 흐름, 의존성과의 연관성, 최근의 흐름에 대해 하나의 이야기로 풀어 마무리한다. 다행인 것은 최근의 흐름이 웹 컴포넌트에 관심이 있던 한 명으로서 웹 컴포넌트가 좋은 방향으로 진행 되는것 같아 기쁘다.

또한 이 글을 재미있게 읽었다면 지난주 Polymer Summit 2017의 lit-html 세션영상을 추천한다. 이 글 역시 해당 세션의 흐름을 따라가며 작성하였고, 이 영상은 템플릿 리터럴에 대한 이해와 리엑트에 대한 이야기, 그리고 이들을 이용해서 lit-html이 어떻게 영리하게 문제를 해결하는지를 담고 있다. 이들 템플릿 리터럴 라이브러리들이 웹 컴포넌트를 리엑트보다 멋진 모습을 만들어 줄 것이라는 기대도 생긴다.

이번을 웹 컴포넌트 마지막 글로 계획했다. 그러나 한 번 더 바뀐 흐름을 따라, 어떻게 웹 컴포넌트 개발을 시작할 수 있는지 튜토리얼 성격의 글을 여러분과 같이 시작해 볼 예정이다.

참고

KyuWoo Choi
Full Stack Engineer(CoE) at GS SHOP(지에스샵)
Full Stack Engineer Skilled in OpenSource and JavaScript. Experienced in Web, Mobile, Game development with Java, C# languages as well. Please check my current works on GitHub.

4 thoughts on “웹 컴포넌트(4) — 템플릿 엘리먼트(Template Element)와 HTML Imports

댓글 남기기

This site uses Akismet to reduce spam. Learn how your comment data is processed.