웹컴포넌트 기술에 대해서

웹컴포넌트란?

웹컴포넌트(Web Components)는 재사용 가능한 커스텀 HTML 요소를 만들 수 있게 해주는 웹 표준 기술입니다. 이는 캡슐화된 기능을 가진 독립적인 컴포넌트를 생성할 수 있도록 하며, 어떤 웹 페이지나 애플리케이션에서도 재사용할 수 있습니다.

웹컴포넌트는 세 가지 핵심 기술로 구성됩니다:

  • Custom Elements: 사용자 정의 HTML 태그를 만들 수 있게 해줍니다
  • Shadow DOM: 컴포넌트의 스타일과 마크업을 캡슐화하여 외부 영향으로부터 격리시킵니다
  • HTML Templates: 재사용 가능한 마크업 템플릿을 정의할 수 있게 해줍니다

실제 사용 사례

간단한 사용자 프로필 카드를 웹컴포넌트로 만들어보겠습니다. 한 번 정의하면 어디서든 재사용할 수 있고, 각 인스턴스는 독립적으로 동작합니다.

웹컴포넌트 정의 코드

// user-card.js
class UserCard extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: 'open' });

    // 스타일 정의
    const style = document.createElement('style');
    style.textContent = `
      .card {
        border: 1px solid #ddd;
        border-radius: 8px;
        padding: 15px;
        margin: 10px;
        background: white;
      }
      .name { font-weight: bold; }
      .role { color: #666; font-size: 14px; }
    `;

    // HTML 구조
    const name = this.getAttribute('name') || 'Unknown';
    const role = this.getAttribute('role') || 'User';

    const card = document.createElement('div');
    card.className = 'card';
    card.innerHTML = `
      <div class="name">${name}</div>
      <div class="role">${role}</div>
    `;

    shadow.appendChild(style);
    shadow.appendChild(card);
  }
}

// 커스텀 엘리먼트 등록
customElements.define('user-card', UserCard);

HTML에서 사용하기

<!DOCTYPE html>
<html>
<head>
  <script src="user-card.js"></script>
</head>
<body>
  <h1>우리 팀 소개</h1>

  <user-card name="김철수" role="프론트엔드 개발자"></user-card>
  <user-card name="이영희" role="백엔드 개발자"></user-card>
  <user-card name="박민수" role="디자이너"></user-card>
</body>
</html>

실행 결과

위 코드를 실행하면 다음과 같이 세 개의 간단한 사용자 카드가 표시됩니다. 각 카드는 동일한 스타일을 가지며, name과 role 속성에 따라 다른 내용을 보여줍니다.

💡 실행 화면 예시:

김철수
프론트엔드 개발자
이영희
백엔드 개발자
박민수
디자이너

간단한 <user-card> 태그로 일관된 스타일의 카드를 반복 생성할 수 있습니다.

핵심 포인트

  • 재사용성: <user-card> 태그를 원하는 곳에 몇 번이든 사용 가능
  • 캡슐화: 컴포넌트 내부의 스타일이 외부 CSS와 충돌하지 않음
  • 간결성: 복잡한 HTML/CSS를 간단한 태그로 추상화
  • 속성 기반: HTML 속성으로 데이터를 전달하여 각 인스턴스를 커스터마이징

웹컴포넌트가 만들어진 이유

웹컴포넌트 기술은 현대 웹 개발의 여러 문제점을 해결하기 위해 탄생했습니다:

1. 코드 재사용성

전통적인 웹 개발에서는 동일한 UI 요소를 여러 곳에서 사용하려면 코드를 복사하거나 특정 프레임워크에 종속된 방식으로 컴포넌트를 만들어야 했습니다. 웹컴포넌트는 프레임워크에 독립적인 표준 방식으로 재사용 가능한 컴포넌트를 만들 수 있게 해줍니다.

2. 캡슐화와 격리

대규모 웹 애플리케이션에서는 CSS와 JavaScript의 전역 스코프 문제로 인해 의도하지 않은 스타일 충돌이나 네이밍 충돌이 발생하곤 했습니다. 웹컴포넌트의 Shadow DOM은 이러한 문제를 해결하여 각 컴포넌트를 독립적으로 유지할 수 있게 합니다.

3. 프레임워크 독립성

React, Vue, Angular 등 다양한 프레임워크가 존재하지만, 각 프레임워크는 자체적인 컴포넌트 시스템을 가지고 있어 프레임워크 간 컴포넌트 공유가 어려웠습니다. 웹컴포넌트는 브라우저 네이티브 기술이므로 어떤 프레임워크나 라이브러리와도 함께 사용할 수 있습니다.

4. 표준화

W3C와 WHATWG의 공식 웹 표준으로 제정되어, 브라우저 제조사들이 직접 구현하고 지원합니다. 이는 장기적으로 안정적이고 일관된 개발 경험을 제공합니다.

웹컴포넌트 라이브러리 예제

웹컴포넌트 표준을 기반으로 한 다양한 라이브러리들이 개발되어 실제 프로젝트에서 활용되고 있습니다:

1. Material Web Components

Google의 Material Design을 웹컴포넌트로 구현한 공식 라이브러리입니다. 버튼, 입력 필드, 카드, 다이얼로그 등 Material Design의 모든 UI 요소를 웹컴포넌트로 제공합니다.
공식 홈페이지 방문하기

<script type="module">
  import '@material/web/button/filled-button.js';
</script>

<md-filled-button>클릭하세요</md-filled-button>

2. Shoelace

프레임워크 독립적인 UI 컴포넌트 라이브러리로, 접근성과 사용자 경험에 중점을 둔 다양한 웹컴포넌트를 제공합니다. CDN을 통해 쉽게 사용할 수 있습니다.
공식 홈페이지 방문하기

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace/dist/themes/light.css" />
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace/dist/shoelace.js"></script>

<sl-button variant="primary">Primary Button</sl-button>

3. Microsoft FAST

Microsoft에서 개발한 웹컴포넌트 프레임워크로, 빠른 성능과 디자인 시스템 통합에 초점을 맞추고 있습니다.
공식 홈페이지 방문하기

이 블로그에서 사용 중인 웹컴포넌트

이 블로그는 순수 웹컴포넌트 기술로 구현되어 있으며, 프레임워크 없이도 재사용 가능하고 유지보수가 쉬운 UI 컴포넌트를 만들 수 있음을 보여줍니다. 실제로 사용 중인 6개의 웹컴포넌트를 소개합니다.

1. blog-header (헤더 컴포넌트)

블로그의 최상단에 고정된 네비게이션 헤더를 제공합니다. 반응형 디자인을 지원하며, 모바일에서는 햄버거 메뉴로 전환됩니다.

<blog-header></blog-header>

주요 기능:

  • 로고와 사이트 이름 표시
  • 홈, 글목록, 회사 소개, 문의하기 메뉴
  • 모바일 환경에서 토글 가능한 메뉴
  • 고정 포지션으로 스크롤 시에도 항상 표시
  • 다크 모드 대응 스타일

2. blog-footer (푸터 컴포넌트)

페이지 하단에 저작권 정보를 표시하는 간단한 푸터 컴포넌트입니다. 현재 연도를 자동으로 업데이트합니다.

<blog-footer></blog-footer>

주요 기능:

  • 현재 연도를 JavaScript로 자동 계산하여 표시
  • 저작권 정보 표시
  • 반응형 레이아웃

3. company-info (회사 정보 컴포넌트)

사이드바에 회사 소개를 표시하는 컴포넌트로, 속성을 통해 내용을 커스터마이징할 수 있습니다.

<company-info
    company-name="KenesLab"
    description="KenesLab은 혁신적인 기술 솔루션을 제공하는 회사입니다."
    link-url="/about.html"
    link-text="자세히 보기 →"
></company-info>

주요 기능:

  • 4개의 속성으로 내용 커스터마이징 가능
  • observedAttributes를 통한 동적 업데이트 지원
  • hover 효과가 있는 링크

4. contact-section (문의 섹션 컴포넌트)

사이드바에 문의 섹션을 표시하는 컴포넌트입니다. 제목, 설명, 링크, 버튼 텍스트를 속성으로 설정할 수 있습니다.

<contact-section
    title="문의하기"
    description="프로젝트 문의나 협업 제안을 환영합니다."
    link-url="/contact.html"
    button-text="문의하기"
></contact-section>

주요 기능:

  • 4개의 속성으로 내용 커스터마이징
  • 동적 속성 변경 감지 및 업데이트
  • 호버 시 애니메이션 효과가 있는 버튼
  • 액센트 컬러를 활용한 CTA 버튼

5. recent-posts (최근 게시물 컴포넌트)

사이드바에 최근 5개의 게시물을 표시하는 컴포넌트입니다. posts-metadata.json 파일에서 데이터를 비동기로 가져와 표시합니다.

<recent-posts></recent-posts>

주요 기능:

  • JSON 파일에서 게시물 메타데이터를 비동기 로딩
  • 날짜 기준 내림차순 정렬 (최신순)
  • 최대 5개 게시물만 표시
  • 날짜 포맷팅 (YYYY.MM.DD)
  • 상대 경로 자동 감지 (현재 위치에 따라 JSON 경로 조정)
  • 전체 목록 보기 링크 제공
  • hover 시 이동 애니메이션

6. posts-list (게시물 목록 컴포넌트)

글목록 페이지에서 모든 게시물을 페이지네이션과 함께 표시하는 컴포넌트입니다. 가장 복잡한 기능을 가진 컴포넌트로, 페이지 상태 관리와 URL 조작까지 수행합니다.

<posts-list></posts-list>

주요 기능:

  • JSON 파일에서 모든 게시물 로딩
  • 페이지당 10개 게시물 표시
  • 페이지네이션 UI (이전/다음 버튼, 페이지 번호)
  • 생략 부호(...) 표시로 긴 페이지 목록 간략화
  • URL 쿼리 파라미터를 통한 페이지 상태 관리 (?page=2)
  • 브라우저 히스토리 API 활용 (뒤로가기 지원)
  • 페이지 전환 시 부드러운 스크롤
  • 총 게시물 수 표시
  • 반응형 디자인

7. shake-form (웹폼 컴포넌트)

문의하기 페이지에서 사용하는 동적 폼 생성 웹컴포넌트입니다. KenesLab에서 사내 테스트 용도로 개발한 간단한 웹폼 생성 라이브러리이며, GitHub에 오픈소스로 공개되어 있습니다.

<shake-form title="문의하기" submit-text="제출하기" theme="dark"></shake-form>

GitHub 저장소:

https://github.com/keneslab/shakeForm

주요 기능:

  • 동적 폼 필드 생성 (setFields() 메서드)
  • 다양한 입력 타입 지원 (text, email, tel, textarea 등)
  • 필수 필드 검증
  • 커스텀 이벤트를 통한 폼 제출 처리 (formsubmit 이벤트)
  • 테마 지원 (dark/light)
  • 반응형 레이아웃 (필드 너비 커스터마이징)
  • 폼 리셋 기능
  • 타임스탬프 자동 추가

실제 사용 예제 (문의하기 페이지)

이 블로그의 문의하기 페이지에서 shakeForm을 실제로 사용하고 있습니다. 다음은 구현 코드입니다:

<!-- HTML: CDN을 통해 라이브러리 로드 -->
<script src="https://keneslab.github.io/shakeForm/shakeForm.js"></script>

<!-- 폼 컴포넌트 배치 -->
<shake-form title="문의하기" submit-text="제출하기" theme="dark"></shake-form>

<script>
  // 폼 필드 정의
  const formFields = [
    { name: 'name', label: '이름', type: 'text', required: true, width: '50%' },
    { name: 'email', label: '이메일', type: 'email', required: true, width: '50%' },
    { name: 'phone', label: '전화번호', type: 'tel', required: false, width: '50%' },
    { name: 'message', label: '문의 내용', type: 'textarea', required: true,
      style: 'height:8rem' }
  ];

  // 폼 초기화
  const form = document.querySelector('shake-form');
  form.setFields(formFields);

  // 폼 제출 이벤트 리스너
  form.addEventListener('formsubmit', (event) => {
    const formData = event.detail;
    console.log('제출된 데이터:', formData);

    // Google Apps Script 등으로 데이터 전송
    fetch('YOUR_GOOGLE_SCRIPT_URL', {
      method: 'POST',
      body: new FormData(formData)
    })
    .then(() => {
      alert('문의가 성공적으로 접수되었습니다.');
      form.reset(); // 폼 초기화
    })
    .catch((error) => {
      alert('오류가 발생했습니다: ' + error.message);
    });
  });
</script>

shakeForm의 장점

  • 간단한 사용법: JavaScript 배열로 폼 필드를 정의하면 자동으로 UI 생성
  • 프레임워크 독립적: 순수 웹컴포넌트로 구현되어 어디서든 사용 가능
  • 커스터마이징: 필드 너비, 스타일, 테마 등을 자유롭게 설정
  • 검증 기능 내장: required 속성으로 간단하게 필수 필드 검증
  • 이벤트 기반: 커스텀 이벤트로 폼 제출을 처리하여 백엔드 연동 용이
  • CDN 지원: GitHub Pages를 통해 직접 로드 가능

개발 배경

shakeForm은 사내에서 간단한 문의 폼이나 피드백 폼을 빠르게 구현할 필요가 있을 때 매번 HTML을 작성하는 번거로움을 줄이기 위해 개발되었습니다. 특히 Google Apps Script와 같은 서버리스 백엔드와 연동할 때 유용합니다.

테스트 목적으로 시작했지만, 실제 프로덕션 환경(이 블로그)에서도 안정적으로 동작하고 있으며, 오픈소스로 공개하여 다른 개발자들도 자유롭게 사용하고 개선할 수 있도록 했습니다.

웹컴포넌트 사용의 장점 (실제 경험)

이 블로그를 웹컴포넌트로 구현하면서 다음과 같은 이점을 경험했습니다:

1. 프레임워크 없이도 충분히 강력함

React, Vue 같은 프레임워크 없이도 재사용 가능한 컴포넌트를 만들 수 있었습니다. 번들러나 빌드 도구 없이 순수 JavaScript만으로 동작하므로 배포가 간단합니다.

2. 완벽한 캡슐화

각 컴포넌트의 스타일이 다른 컴포넌트나 전역 스타일과 충돌하지 않습니다. addStyles() 메서드로 컴포넌트 내부에 스타일을 주입하면, 해당 스타일은 그 컴포넌트에만 적용됩니다.

3. HTML처럼 쉬운 사용

복잡한 설정 없이 HTML 태그처럼 사용할 수 있습니다. 팀원들이나 다른 개발자가 쉽게 이해하고 사용할 수 있습니다.

<!-- 이렇게 간단하게 사용 가능 -->
<blog-header></blog-header>
<recent-posts></recent-posts>
<blog-footer></blog-footer>

4. 유지보수 용이성

헤더를 수정하고 싶다면 header.js 파일만 수정하면 됩니다. 해당 컴포넌트를 사용하는 모든 페이지에 자동으로 반영됩니다. 각 HTML 파일을 일일이 수정할 필요가 없습니다.

5. 점진적 향상 가능

간단한 컴포넌트부터 시작해서 필요에 따라 기능을 추가할 수 있습니다. 예를 들어 posts-list 컴포넌트는 처음에는 단순한 목록이었지만, 점진적으로 페이지네이션, URL 관리, 반응형 디자인 등을 추가했습니다.

프로젝트 구조

블로그의 웹컴포넌트는 다음과 같이 구성되어 있습니다:

blog/
├── index.html           # 메인 페이지
├── posts.html          # 글목록 페이지
├── about.html          # 회사 소개 페이지
├── contact.html        # 문의 페이지 (shakeForm 사용)
├── posts-metadata.json # 게시물 메타데이터
└── js/
    └── components/
        ├── header.js           # <blog-header>
        ├── footer.js           # <blog-footer>
        ├── company-info.js     # <company-info>
        ├── contact-section.js  # <contact-section>
        ├── recent-posts.js     # <recent-posts>
        └── posts-list.js       # <posts-list>

# 외부 웹컴포넌트
- shakeForm (CDN): https://keneslab.github.io/shakeForm/shakeForm.js

HTML 페이지에서 사용하기

각 HTML 페이지에서는 필요한 컴포넌트 스크립트만 불러와 사용합니다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>KenesLab Blog</title>
    <link rel="stylesheet" href="/css/style.css">
</head>
<body>
    <!-- 헤더 컴포넌트 -->
    <blog-header></blog-header>

    <main>
        <!-- 메인 콘텐츠 -->
        <div class="content">
            <h1>환영합니다</h1>
        </div>

        <!-- 사이드바 -->
        <aside>
            <recent-posts></recent-posts>
            <company-info></company-info>
            <contact-section></contact-section>
        </aside>
    </main>

    <!-- 푸터 컴포넌트 -->
    <blog-footer></blog-footer>

    <!-- 컴포넌트 스크립트 로드 -->
    <script src="/js/components/header.js"></script>
    <script src="/js/components/footer.js"></script>
    <script src="/js/components/recent-posts.js"></script>
    <script src="/js/components/company-info.js"></script>
    <script src="/js/components/contact-section.js"></script>
</body>
</html>

결론

웹컴포넌트는 단순한 기술 데모가 아닌, 실제 프로덕션 환경에서 충분히 사용할 수 있는 강력한 도구입니다. 이 블로그가 그 실용성을 증명하며, 6개의 자체 제작 컴포넌트와 1개의 오픈소스 컴포넌트(shakeForm)를 통해 프레임워크 없이도 완전히 기능하는 웹사이트를 구축할 수 있음을 보여줍니다.

특히 작은 규모의 프로젝트나 무거운 프레임워크가 필요 없는 경우, 웹컴포넌트는 간단하면서도 유지보수가 쉬운 훌륭한 선택지가 될 것입니다. 또한 shakeForm처럼 오픈소스로 공유하면 다른 프로젝트에서도 재사용할 수 있어 개발 생산성을 크게 향상시킬 수 있습니다.

여러분도 다음 프로젝트에서 웹컴포넌트를 고려해보시기 바랍니다. 혹은 shakeForm을 사용하여 간단한 폼부터 시작해보세요!