웹컴포넌트란?
웹컴포넌트(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을 사용하여 간단한 폼부터 시작해보세요!