https://github.com/yamoo9/webcomponent
HTML 웹 컴포넌트
https://github.com/yamoo9/webcomponent
korean translation web-component webcomponent
Last synced: 12 months ago
JSON representation
HTML 웹 컴포넌트
- Host: GitHub
- URL: https://github.com/yamoo9/webcomponent
- Owner: yamoo9
- Created: 2017-11-18T22:33:32.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2017-11-18T22:39:50.000Z (over 8 years ago)
- Last Synced: 2025-04-12T02:05:59.671Z (12 months ago)
- Topics: korean, translation, web-component, webcomponent
- Language: JavaScript
- Size: 59.6 KB
- Stars: 16
- Watchers: 3
- Forks: 5
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
###### HTML Web Component
# HTML 웹 컴포넌트
웹 컴포넌트에 대한 필요성과 기술 표준에 대한 이야기는 몇 년에 걸쳐 거론되어 왔지만 Chrome, Opera를 제외한 주요 브라우저에서는 아직까지 일부만 지원하고 있는 실정입니다.
하지만 [웹 컴포넌트 폴리필](https://www.webcomponents.org/polyfills)을 사용하면 바로 적용해 사용할 수 있습니다.
## 목차
- [소개](#소개)
- [웹 컴포넌트를 구성하는 4가지 요소](#웹-컴포넌트를-구성하는-4가지-요소)
- [커스텀 HTML 요소 만들기](#커스텀-html-요소-만들기)
- [바인딩 할 데이터 API](#바인딩-할-데이터-api)
- [HTML 템플릿 구성하기](#html-템플릿-구성하기)
- [컴포넌트 스타일링](#컴포넌트-스타일링)
- [라이프 사이클 메서드](#라이프-사이클-메서드)
- [connectedCallback](#connectedcallback)
- [Shadow DOM이란?](#shadow-dom이란)
- [데이터 렌더링](#데이터-렌더링)
- [크로스 브라우징](#크로스-브라우징)
- [테스트 서버](#테스트-서버)
- [테스트 서버 개발 모듈](#테스트-서버-개발-모듈)
- [웹 컴포넌트 & Shadow DOM](#웹-컴포넌트--shadow-dom)
- [웹 컴포넌트를 사용할 때 알아두어야 할 점!](#웹-컴포넌트를-사용할-때-알아두어야-할-점ii)
- [컴포넌트 이름 작성 규칙](#컴포넌트-이름-작성-규칙)
- [컴포넌트 확장](#컴포넌트-확장)
- [커스텀 요소는 컴포넌트 클래스 인스턴스](#커스텀-요소는-컴포넌트-클래스-인스턴스)
- [비공개(Private) 메서드](#비공개private-메서드)
- [컴포넌트 클래스 프리징(Freezing)](#컴포넌트-클래스-프리징freezing)
- [결론](#결론)
- [최신 정보](#최신-정보)
- [참고](#참고)
## 소개
웹 컴포넌트는 웹 애플리케이션 제작 시에 사용 가능한 부품(Component)을 말하며, 재사용을 목적으로 캡슐화 된 커스텀 HTML 요소를 만들 수 있는 웹 플랫폼 API 세트입니다.
표준 HTML, DOM 기술 사양에 추가된 웹 컴포넌트 기술을 사용하면 HTML/CSS/JavaScript를 사용하여 재사용 가능한 컴포넌트를 제작할 수 있습니다.
예를 들어, ID로 식별 가능한 데이터를 서버에서 가져와 데이터 바인딩하는 컴포넌트를 아래와 같은 HTML 코드로 사용할 수 있습니다.
```html
```
[⇪ 목차로 이동](#목차)
## 웹 컴포넌트를 구성하는 4가지 요소
HTML, DOM 표준 기술 사양은 웹 컴포넌트를 구성하기 위한 4가지 API를 제공하고 있습니다.
1. __[customElements](https://www.w3.org/TR/custom-elements/)__
커스텀 엘리먼트를 사용하면 새로운 HTML 요소를 만들거나 기존 HTML 요소를 확장할 수 있습니다.
1. __[HTML Template](https://www.html5rocks.com/ko/tutorials/webcomponents/template/#toc-using)__
클라이언트 측 템플릿을 위한 표준 DOM 기반 접근 방식으로 새로운 요소를 정의하기 위해 `` 요소를 사용할 수 있습니다.
1. __[HTML Import](https://www.html5rocks.com/ko/tutorials/webcomponents/imports/)__
HTML 템플릿(``)을 사용하면 새 템플릿을 만들 수 있지만 HTML 임포트(``)를 사용하면 다른 HTML 파일에서 이러한 템플릿으로 가져올 수 있습니다.
이를 통해 컴포넌트와 HTML 템플릿 파일을 분리 관리할 수 있습니다.
1. __[Shadow DOM](https://dom.spec.whatwg.org/#shadow-trees)__
Shadow DOM은 컴포넌트 기반 애플리케이션을 작성하기 위한 도구로 설계 되었습니다.
컴포넌트 스코프(Scope)를 DOM에서 분리하고 CSS 등을 단순화 할 수 있습니다.
[⇪ 목차로 이동](#목차)
## 커스텀 HTML 요소 만들기
컴포넌트를 구성할 디렉토리(Directory)를 만들고, 디렉토리 안에 커스텀 요소를 정의할 파일을 생성합니다.
```sh
.
└── Y9Card
└── customElement.js ⬅︎
```
커스텀 HTML 요소를 생성하려면 먼저 요소를 정의하는 클래스(Class)를 선언해야 합니다.
새롭게 정의하는 클래스는 기존 HTML 요소의 능력을 그대로 물려 받아 사용하기 위해 브라우저 네이티브 클래스 HTMLElement 클래스를 상속 합니다.
클래스를 정의하고 상속하는 구문은 다음과 같습니다.
```js
/* Y9Card 컴포넌트 클래스 정의 */
class Y9Card extends HTMLElement {
constructor() {
// 상위 클래스로부터 상속된 후 생성자를 정의할 경우, 반드시 super() 호출이 요구됨.
super();
// 요소 click 이벤트 바인딩: toggleCard() 메서드 실행
this.addEventListener('click', e => this.toggleCard());
}
// 메서드
toggleCard() { console.log('카드 토글(Toggle)'); }
}
```
이어서 선언한 클래스를 커스텀 HTML 요소로 사용할 수 있도록 등록합니다.
이 과정을 마치면 HTML 문서에서 `` 요소를 사용할 수 있게 됩니다.
```js
/* 커스텀 HTML 요소 등록 */
window.customElements.define('y9-card', Y9Card);
```
[⇪ 목차로 이동](#목차)
## 바인딩 할 데이터 API
[JSONPlaceholder](https://jsonplaceholder.typicode.com/) API를 사용해 등록된 커스텀 요소에 데이터를 바인딩 할 것입니다. 바인딩 될 데이터 구조는 다음과 같습니다.
```js
{
id: 1,
name: "Leanne Graham",
username: "Bret",
email: "Sincere@april.biz",
address: {
street: "Kulas Light",
suite: "Apt. 556",
city: "Gwenborough",
zipcode: "92998-3874",
geo: {
lat: "-37.3159",
lng: "81.1496"
}
},
phone: "1-770-736-8031 x56442",
website: "hildegard.org"
}
```
[⇪ 목차로 이동](#목차)
## HTML 템플릿 구성하기
API 데이터를 바인딩 할 템플릿을 만들어 봅시다. 템플릿으로 사용할 HTML 파일을 디렉토리 안에 만듭니다.
```sh
.
└── Y9Card
├── customElement.js
└── template.html ⬅︎
```
카드 컴포넌트 템플릿은 다음과 같이 HTML 코드로 구성합니다. 템플릿을 식별하기 위한 `id` 및 컴포넌트를 구성하는 각 파트를 식별하기 위한 `class` 속성을 설정할 때는
고유하게 식별 가능한 접두사(예제에서는 `y9-card`)를 사용해 기존 HTML 요소와 충돌나지 않도록 구성해야 합니다.
```html
```
마무리로 템플릿 정의 코드 아래 컴포넌트 커스텀 요소를 정의한 스크립트 파일을 호출합니다.
```html
```
[⇪ 목차로 이동](#목차)
## 컴포넌트 스타일링
컴포넌트를 스타일링할 CSS 파일을 컴포넌트 디렉토리 내에 생성합니다.
```sh
.
└── Y9Card
├── customElement.js
├── template.html
└── style.css ⬅︎
```
생성된 CSS 파일에 카드 스타일링 코드를 작성합니다.
```css
.y9-card__template {
display: inline-block;
width: 30%;
margin: 3px;
border: 1px solid grey;
font-family: Helvetica;
text-align: center;
border-radius: 5px;
}
.y9-card__:hover {
box-shadow: 3px 3px 3px;
}
.y9-card__content {
display: none;
}
.y9-card__details-button {
margin-bottom: 8px;
padding: 6px;
background-color: #dedede;
}
```
이어서 템플릿 `` 시작 부분에 컴포넌트 스타일 파일을 호출하는 구문을 추가합니다.
```html
...
```
[⇪ 목차로 이동](#목차)
## 라이프 사이클 메서드
커스텀 요소의 라이프 사이클(Life Cycle) 메소드는 이벤트에 따라 콜백(callback)하여 사용할 수 있습니다.
메서드 이름 | 설명
--- | ---
connectedCallback | 커스텀 요소가 __DOM에 삽입 될 때마다 호출__ 됩니다.
disconnectedCallback | 커스텀 요소가 __DOM에서 제거 될 때마다 호출__ 됩니다.
attributeChangedCallback | 커스텀 요소의 __속성이 추가, 제거, 업데이트 또는 교체 될 때마다 호출__ 됩니다.
자 그럼 이어서 `Y9Card/customElement.js` 파일에 라이프 사이클 메서드를 추가해봅시다.
### connectedCallback
우리가 정의한 커스텀 요소를 DOM에 삽입할 때 호출될 일련의 과정을 정의하려면 `connectedCallback` 메서드를 사용합니다.
__참고__
> `constructor`와 `connectedCallback` 메소드는 비슷해보이지만 용도가 다릅니다.
>
> `constructor`는 커스텀 요소 인스턴스가 생성되었을 때 실행되는 반면, `connectedCallback` 메서드는 DOM에 커스텀 요소가 삽입될 때마다 실행됩니다.
>
> __`connectedCallback` 메서드는 임포트(`import`)하거나 렌더링(`rendering`) 같은 설정 코드를 실행할 때 유용__ 합니다.
먼저 `Y9Card/customElement.js` 파일에 `currentDocument` 변수를 생성해야 합니다. 해당 변수에는 커스텀 컴포넌트 스크립트 파일을 호출한 HTML 파일의 DOM에 접근하기 위함입니다.
```js
const currentDocument = window.document.currentScript.ownerDocument;
```
이어서 `connectedCallback` 메서드를 클래스 구문 내에 정의합니다.
```js
/* 라이프사이클 메서드 */
// DOM에 커스텀 요소가 삽입될 때 호출
connectedCallback() {
// #1. Shadow DOM
const shadowRoot = this.attachShadow({mode: 'open'});
// #2. 템플릿을 선택하고 복제합니다.
const template = currentDocument.querySelector('#y9-card__template');
const instance = tempalte.content.cloneNode(true);
// #3. 복제된 노드를 ShadowRoot에 삽입합니다.
shadowRoot.appendChild(instance);
// #4. 커스텀 HTML 요소로 부터 data-id 속성 값을 가져옵니다.
// e.g)
const id = this.getAttribute('data-id');
// #5. API에서 해당 사용자 ID에 대한 데이터를 가져와 인스턴스 렌더링 메서드를 호출합니다.
fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
.then( response => response.text() )
// #6. JSON 데이터 ➜ 객체 변경 후, render() 메서드에 전달하여 실행합니다.
.then( responseText => this.render(JSON.parse(responseText)) )
.catch( error => console.error(error) );
}
```
1. `.attachShadow()` 메서드를 사용해 [Shadow DOM]()을 커스텀 요소에 연결합니다.
1. `currentDocument` 변수를 통해 템플릿 식별자를 쿼리하여 템플릿을 참조한 후, 템플릿 노드를 복제합니다.
1. 복제된 템플릿 노드를 `shadowRoot` 변수에 참조된 객체의 `.appendChild()` 메서드를 통해 삽입합니다.
1. 커스텀 요소의 `data-id` 속성 값을 가져와 변수 `id`에 복사합니다.
1. `fetch()` 함수를 사용해 API 데이터를 서버로부터 호출합니다.
1. 응답 받은 JSON 텍스트 데이터를 객체화 하고 커스텀 요소의 `render()` 메서드에 전달하여 실행합니다.
#### Shadow DOM이란?
[Shadow DOM](https://developer.mozilla.org/ko/docs/Web/Web_Components/Shadow_DOM)은 웹 컴포넌트 내에 존재하는
템플릿, 스타일, 스크립트 코드를 캡슐화 하는 기능을 가지고 있어 HTML 문서의 DOM에서 분리할 수 있습니다.
웹 컴포넌트에 연결하여 사용하는 방법이 아니더라도 Shadow DOM 만 따로 사용하는 것도 가능합니다.

> shadow host는 요소, Shadow DOM Subtrees는 Shadow DOM을 말합니다.
Shadow DOM은 반드시 이미 존재하는 요소(HTML 파일 내에 사용된 요소)에 추가해야 합니다.
요소는 `
`와 같은 네이티브 요소일 수도 있고, ``와 같은 커스텀 요소일 수도 있습니다.
위 예제에서 사용했던 [`.attachShadow()`](https://developer.mozilla.org/ko/docs/Web/API/Element/attachShadow) 메서드를 사용해
Shadow DOM을 요소에 추가할 수 있습니다. 구문은 다음과 같습니다.
```js
elementNode.attachShadow(shadowRootInit);
```
`shadowRootInit`은 옵션 객체로 `mode` 속성을 설정할 수 있습니다.
`mode` 속성은 Shadow DOM Tree의 캡슐화 모드를 설정합니다.
모드 값 | 설명
--- | ---
open | shadowRoot를 추가한 요소들이 `.shadowRoot` 속성을 사용해 HTML DOM으로부터 접근 가능하게 설정합니다.
close | shadowRoot를 추가한 요소들이 `.shadowRoot` 속성을 사용해 HTML DOM으로부터 접근 불가능하게 설정합니다.
```js
element.attachShadow({
mode: 'open' // 'open' | 'close'
});
```
[⇪ 목차로 이동](#목차)
## 데이터 렌더링
서버로부터 전달 받아 객체화 한 데이터는 `render()` 메서드의 인자로 전달됩니다.
전달 받은 데이터의 각 속성을 템플릿에 바인딩하는 코드를 `render()` 메서드 내에 작성합니다.
```js
/* 렌더링 메서드 */
render(data) {
// #1. ShadowRoot를 참조합니다.
const shadowRoot = this.shadowRoot;
// #2. 데이터 바인딩 할 템플릿 내부 요소를 참조합니다.
let header = shadowRoot.querySelector('.y9-card__header');
let website = shadowRoot.querySelector('.y9-card__website');
let address = shadowRoot.querySelector('.y9-card__address');
// #3. 참조한 템플릿 요소마다 각각 데이터를 바인딩 합니다.
header.innerHTML = data.username;
website.innerHTML = data.website;
let data_address = data.address;
address.innerHTML = `
주소
${data_address.suite},
${data_address.street},
${data_address.city},
우편번호: ${data_address.zipcode}
`;
}
```
1. ShadowRoot를 참조합니다.
1. 데이터 바인딩 할 템플릿 내부 요소(헤더, 웹사이트, 주소)를 참조합니다.
1. 참조한 템플릿 요소마다 각각 데이터를 바인딩 합니다.
마무리로 `toggle()` 메서드에 다음 코드를 추가합니다.
```js
toggle() {
// #1. ShadowRoot를 참조합니다.
const shadowRoot = this.shadowRoot;
// #2. ShadowRoot를 통해 요소(콘텐츠, 버튼)를 참조합니다.
let content = shadowRoot.querySelector('.y9-card__content');
let button = shadowRoot.querySelector('.y9-card__detail-button');
// #3. 콘텐츠의 display 상태가 화면에 표시되는지 여부를 is_content_visible 변수에 복사합니다.
let is_content_visible = content.style.display === 'none';
// #4. 참조한 버튼 텍스트 값을 is_content_visible 조건 값에 따라 변경(토글)합니다.
button.innerHTML = is_content_visible ? '자세한 정보 보기' : '펼쳐진 정보 감추기';
// #5. 참조한 콘텐츠의 display 상태 값을 is_content_visible 조건 값에 따라 변경(토글)합니다.
content.style.display = is_content_visible ? 'block' : 'none';
}
```
1. ShadowRoot를 참조합니다.
1. ShadowRoot를 통해 요소(콘텐츠, 버튼)를 참조합니다.
1. 콘텐츠의 `display` 상태가 화면에 표시되는지 여부를 `is_content_visible` 변수에 복사합니다.
1. 참조한 버튼 텍스트 값을 `is_content_visible` 조건 값에 따라 변경(토글)합니다.
1. 참조한 콘텐츠의 `display` 상태 값을 `is_content_visible` 조건 값에 따라 변경(토글)합니다.
커스텀 컴포넌트를 생성하고 사용하기 위한 준비 과정이 마무리 되었으니 프로젝트에 해당 컴포넌트를 사용할 수 있습니다.
`index.html` 파일을 생성한 후 컴포넌트를 사용해봅시다.
```sh
.
├── components
│ └── Y9Card
│ ├── customElement.js
│ ├── style.css
│ └── template.html
└── index.html ⬅︎
```
아래는 `index.html` 전문입니다.
```html
웹 컴포넌트 DEMO
```
1. `` 구문을 추가한 다음 `href="./components/Y9Card/template.html"' 속성을 추가합니다.
1. `` 커스텀 요소를 body 요소 내부에 추가합니다.
#### 크로스 브라우징
모든 브라우저가 웹 컴포넌트를 지원하는 것은 아니기 때문에 브라우저 호환성을 고려하려면 [webcomponents.js](https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/1.0.2/webcomponents-lite.js) 파일을 문서에 추가해야 합니다.
`