커스텀 엘리먼트(Custom Elements)는 HTML 표준에서 제공하는 기능으로, 개발자가 직접 새로운 HTML 태그(컴포넌트)를 만들 수 있게 해주는 기술입니다.
예시: <site-modal>
, <site-scroll-reveal>
등 기존에 없던 태그를 직접 정의해서 사용할 수 있습니다.
💡 더 자세한 내용은 MDN 공식 문서를 참고하세요.
이 프로젝트는 브랜드별로 디자인이 달라도, 반복적으로 사용되는 핵심 기능(모달, 토스트, 스크롤 효과 등)을 쉽게 재사용할 수 있도록 커스텀 엘리먼트 방식을 채택했습니다.
// 커스텀 엘리먼트 기본 구조 (초보자용 설명)
class MyElement extends HTMLElement {
constructor() {
super();
// 생성자: 태그가 처음 만들어질 때 딱 1번 실행
// 예: 초기 변수 설정, 이벤트 리스너 준비 등
}
connectedCallback() {
// 연결됨: 태그가 HTML 페이지에 추가될 때마다 실행
// 예: HTML 내용 생성, 스타일 적용, 애니메이션 시작 등
// 가장 많이 사용되는 메서드!
}
disconnectedCallback() {
// 연결 해제: 태그가 HTML 페이지에서 제거될 때 실행
// 예: 타이머 정리, 이벤트 리스너 제거 등
}
static get observedAttributes() {
// 감시할 속성들: 변경을 감지하고 싶은 속성 이름들
// 예: data-color, data-size 등이 바뀌면 알려달라고 요청
return ['data-color', 'data-size'];
}
attributeChangedCallback(name, oldValue, newValue) {
// 속성 변경됨: 위에서 지정한 속성이 바뀔 때마다 실행
// 예: data-color가 "red"에서 "blue"로 바뀌면 스타일 업데이트
}
adoptedCallback() {
// 이동됨: 다른 문서로 이동할 때 실행 (거의 사용 안함)
}
// 내가 원하는 기능들을 여기에 추가할 수 있어요!
show() {
// 예: 모달 열기, 애니메이션 시작 등
}
hide() {
// 예: 모달 닫기, 애니메이션 정지 등
}
}
// 등록: 브라우저에게 "my-element"라는 태그를 사용하겠다고 알려줌
// 이제 HTML에서 <my-element></my-element> 사용 가능!
customElements.define('my-element', MyElement);
HTMLElement
를 상속받아 클래스로 정의합니다.connectedCallback()
: 요소가 DOM에 추가될 때 자동 실행disconnectedCallback()
: 요소가 DOM에서 제거될 때 자동 실행attributeChangedCallback(name, oldVal, newVal)
: 지정한 속성이 변경될 때 실행static get observedAttributes()
: 감시할 속성명 배열 반환 (attributeChangedCallback과 함께 사용)this.getAttribute('속성명')
으로 속성값을 읽거나, 클래스 내부에 메서드를 정의해 외부 JS에서 호출할 수 있습니다.this.innerHTML
또는 this.appendChild()
등으로 내부 구조를 동적으로 생성할 수 있습니다.customElements.define('태그명', 클래스명)
으로 HTML에서 사용할 수 있도록 등록합니다. 태그명에는 반드시 하이픈(-)이 포함되어야 합니다.이러한 문법 구조 덕분에 커스텀 엘리먼트는 재사용성, 유지보수성, 확장성이 뛰어난 UI 컴포넌트 개발이 가능합니다.
jQuery 방식 | 커스텀 엘리먼트 방식 |
---|---|
DOM 조작/이벤트 바인딩을 직접 구현 | 태그만 선언하면 동작(내부에 코드 내장) |
코드가 여러 곳에 분산 | 기능별로 한 곳에 집중 |
스타일/동작 충돌 위험 | 캡슐화로 충돌 최소화 |
재사용/유지보수 어려움 | 재사용/유지보수 용이 |
site-ui.js는 다양한 브랜드에서 활용할 수 있도록 간단하면서도 강력한 커스터마이징 시스템을 제공합니다.
HTML 속성을 통해 컴포넌트 설정을 동적으로 변경할 수 있습니다.
// HTML에서 직접 설정
<site-swiper data-config='{
"slidesPerView": 3,
"autoplay": {
"delay": 3000
}
}'>
<div class="swiper-slide">슬라이드 1</div>
<div class="swiper-slide">슬라이드 2</div>
</site-swiper>
// JavaScript로 동적 설정
const swiper = document.querySelector('site-swiper');
// 브랜드별 설정 적용
const brandConfig = {
speed: 1200,
effect: 'cube',
cubeEffect: {
shadow: true,
slideShadows: true
}
};
swiper.setAttribute('data-config', JSON.stringify(brandConfig));
// Swiper 인스턴스에 직접 접근
swiper.addEventListener('swiperReady', (e) => {
const swiperInstance = e.detail.swiper;
// 원하는 슬라이드로 이동
swiperInstance.slideTo(2);
});
CSS를 통해 브랜드별 디자인을 적용할 수 있습니다.
/* 브랜드 A 스타일 */
.brand-a-swiper {
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}
.brand-a-swiper .swiper-button-next,
.brand-a-swiper .swiper-button-prev {
color: #d4af37;
background: rgba(255, 255, 255, 0.9);
border-radius: 50%;
width: 50px;
height: 50px;
}
.brand-a-swiper .swiper-pagination-bullet-active {
background: #d4af37;
}
/* 브랜드별 모달 스타일 */
.brand-premium site-modal .modal {
background: linear-gradient(
135deg,
#667eea 0%,
#764ba2 100%
);
color: white;
border-radius: 15px;
border: 2px solid #d4af37;
}
/* 브랜드별 탭 스타일 */
.brand-minimal site-tabs .tab {
border: none;
border-bottom: 3px solid transparent;
transition: all 0.3s ease;
}