커스텀 엘리먼트란?

커스텀 엘리먼트(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);

커스텀 엘리먼트 문법 설명

이러한 문법 구조 덕분에 커스텀 엘리먼트는 재사용성, 유지보수성, 확장성이 뛰어난 UI 컴포넌트 개발이 가능합니다.

jQuery 방식과의 차이

jQuery 방식 커스텀 엘리먼트 방식
DOM 조작/이벤트 바인딩을 직접 구현 태그만 선언하면 동작(내부에 코드 내장)
코드가 여러 곳에 분산 기능별로 한 곳에 집중
스타일/동작 충돌 위험 캡슐화로 충돌 최소화
재사용/유지보수 어려움 재사용/유지보수 용이

브랜드별 커스터마이징

site-ui.js는 다양한 브랜드에서 활용할 수 있도록 간단하면서도 강력한 커스터마이징 시스템을 제공합니다.

1. data-config를 통한 동적 설정

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);
});

2. CSS 커스터마이징

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;
}

💡 커스터마이징 팁

  • 이벤트 우선: 복잡한 상속보다는 이벤트 리스너를 활용하세요
  • CSS 분리: 브랜드별 CSS 파일을 별도로 관리하세요
  • 설정 집중화: 브랜드 설정을 한 곳에 모아 관리하세요
  • 점진적 적용: 기본 기능부터 시작해서 점진적으로 커스터마이징하세요
  • 성능 고려: 불필요한 이벤트 리스너는 제거하세요