웹 컴포넌트란?
본인이 원하는 대로 태그의 이름과 스타일을 미리 설정하고
이를 바탕으로 편하게 복제를 하며 재사용이 가능하다
즉, 컴포넌트의 역할을 수행하는 것이다
그렇다면 사용하는 이유는 무엇일까?
우선 재사용이 가능하다는 점과 캡슐화를 통해 원하는 속성을 통제할 수 있다는 장점이 있다
class MyElement extends HTMLElement {
constructor() {
super();
// 엘리먼트가 생성됨
}
connectedCallback() {
// 엘리먼트가 문서에 추가될 때 브라우저가 이 메서드를 호출합니다.
// (엘리먼트가 반복적으로 추가/제거되면 여러 번 호출될 수 있음)
}
disconnectedCallback() {
// 엘리먼트가 문서에서 제거될 때 브라우저가 이 메서드를 호출합니다.
// (엘리먼트가 반복적으로 추가/제거되면 여러 번 호출될 수 있음)
}
static get observedAttributes() {
return [/* 변경 사항을 모니터링할 속성 이름의 배열 */];
}
attributeChangedCallback(name, oldValue, newValue) {
// 위에 나열된 속성 중 하나가 수정될 때 호출됩니다.
}
adoptedCallback() {
// 엘리먼트가 새 문서로 이동될 때 호출됩니다.
// (document.adoptNode에서 사용되며 매우 드물게 사용됨)
}
// 다른 엘리먼트 메서드와 속성이 있을 수 있습니다.
}
사용하기 위해선 class 문법을 사용해 생성한다
extends HTMLElement를 기재하여 HTMLElement의 속성을 부여받는다
기본 설정 값으로는 다음 4가지가 있다
하나하나 살펴보자
connectedCallback()
이는 선언한 Element가 HTML에 사용될때 실행하는 함수이다
disconnectedCallback()
선언한 Element가 HTML에 사용되었다가 다시 사라질때 실행되는 함수이다(종료)
attributeChangedCallback()
만약 선언한 Element에 변화가 생긴다면 그 값을 가지고 원하는 동작을 실행시킬 수 있는 함수이다
이를 잘 활용한다면 시계를 나타내는 것도 가능하다
(초가 계속해서 바뀌므로 이를 인식해서 화면에 렌더링한다)
adoptedCallback()
선언한 Element가 새로운 document에 사용되었다면 호출되는 함수이다
물론 이를 제외하고 원하는 함수를 class의 내부에 입력해도 정상적으로 사용이 가능하다
이 과정에서 이벤트를 할당하는 것도 역시 가능하다
이를 통해 알 수 있는 점은 web componenet api는 life cycle이 존재한다는 것이다
connectedCallback으로 시작을 알리고 disconnectedCallback으로 마지막을 알린다
그렇다면 custom Element를 만드는 과정을 살펴보자
customElements.define("my-element", MyElement);
customElements.define으로 첫 번째 인수에는 생성할 태그의 이름을, 두 번째 인수에는 어떤 class를 이용해서 만들 것인지를 정할 수 있다
참고로 첫 번째 인수의 이름에는 반드시 하이픈(-)이 포함되어야 한다
shadow DOM은 강력한 캡슐 기술이다
이는 일반적인 JS호출이나 셀렉터로 접근할 수 없다
DOM에는 shadow DOM과 light DOM이 있다
우선 light DOM은 일반적인 HTML에 사용되는 태그들로 구성된다
여기에는 접근하기 용이하고 조작하기도 수월하다
그에 반해 shadow DOM은 표면에 나타나지 않고 숨어있는 녀석이다
만약 light DOM과 shadow DOM이 동시에 선언되었다면 우선 순위는 shadow DOM이 높아 light DOM은 가려진다(shadowing)
그렇다면 shadow DOM에는 어떻게 접근할까?
방법은 아래와 같다
class MyElement extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
const shadow = this.attachShadow({mode:'open'});
console.log(shadow);
}
}
customElements.define('my-div' , MyElement);
const myDiv = document.createElement('my-div');
document.body.appent(myDiv);

우선 코드의 흐름을 먼저 살펴보면 class는 태그를 어떤 방식으로 생성할지를 정한다
그 후 customElements.define을 사용해 'my-div'라는 태그를 MyElement를 이용해 생성한다
이를 body에 붙여주는 것이다
마지막 과정에서 body에 attach 즉, 탄생했으므로 connectedCallback()이 실행되고 shadow를 정상적으로 출력하는 모습이다
원래였으면 접근을 못하지만 mode:'open'으로 인해 접근이 가능해진 모습이다
하지만 이러한 방식을 사용해도 img 태그와 같은 곳은 shado DOM에 접근이 불가능하다
이러한 설정이 완료되었다면 document.querySelector와 같이 shadow에도 같은 방식으로 사용이 가능하다
connectedCallback() {
this.attachShadow({mode:'open'});
console.log(this.shadowRoot.querySelector('div'));
}
와 같은 방식으로 사용이 가능하다
지금은 tag를 생성할때 내부에 생성한 태그가 없으므로 값은 반환이 되진 않는다
주의점
shadow DOM은 강력한 캡슐 기능이라고 소개했다
그렇기에 외부에서 접근하는 것은 불가능하다
따라서 CSS속성을 문서로 부여하는 것은 불가능하다
만약 CSS속성을 주고 싶다면 인라인 방식 혹은, style 태그를 내부에 생성하여 CSS를 적용시킬 수 있다
이는 뒤에 나올 shadow DOM styling에서 더 자세히 보자
HTML문서에 template 태그를 사용하여 감싸면
휘발되어 렌더링이 되지 않는다
이는 component처럼 사용이 가능한 태그이다
미리 생성해놓고 원하는 곳에 append를 통해 렌더링이 가능하도록 한다
앞서 말했듯 light DOM 과 shadow DOM을 동시에 사용하게 되면 shadow DOM의 우선순위가 높아 light DOM이 무시된다
이를 해결하고자 슬롯을 사용하는 것이다
우선 사용법은 다음과 같다
this.shadowRoot.innerHTML = `
<div>Name:
<slot name="username"></slot>
</div>
<div>Birthday:
<slot name="birthday"></slot>
</div>
`;
<user-card>
<span slot="username">John Smith</span>
<span slot="birthday">01.01.2001</span>
</user-card>
만약 이 경우 원래대로였으면 user-card내부에 있는 span태그들은 shadow DOM의 선언으로 인해 무시되었어야 한다
하지만 slot을 사용하여 HTML에 선언된 span태그의 slot 속성의 값을 확인하여
shadow DOM에서 선언된 slot태그의 name속성의 값과 일치한다면 렌더링을 한다
이때 span태그는 light DOM에 포함되고 문서에서 선언한 CSS도 적용이 가능하다
그리고 동일한 이름을 가진 슬롯의 경우 순서대로 추가된다
첫 번째 슬롯의 이름이 지정되지 않았다면 모든 값을 포함하게 된다
또한 slot은 함수의 매개변수에 default value를 설정하듯이 slot fallback을 설정할 수 있다
이는 단순히 태그에 값을 기재하는 것으로 작성할 수 있다
:host를 사용하게 되면 본인을 생성한, 즉 렌더링한 대상을 지정하여 style을 부여할 수 있게된다
이것이 필요한 이유는 shadow DOM에는 정상적인 방법으로 접근이 어렵고, 생성되기 전에 미리 접근하는 방법은 없다
따라서 이러한 방법을 이용하여 CSS를 적용하는 것이다
하지만 host는 light DOM에 있으므로 문서 CSS 규칙의 영향을 받는다
또한 host의 selector(::after 등)도 역시 설정이 가능하다
정말 편리한 방식은 내부에 style 태그에 미리 생성해둔 CSS문서를 @import('문서 경로')를 사용하여 설정도 가능하다
주의해야할 점은 문서의 경로는 항상 내보내는 문서를 기준으로 작성해야한다