Ver2.5 디자인 시스템 완전 정복: 웹 컴포넌트 기반으로 레벨 업!

디자인 시스템 완전 정복: 웹 컴포넌트 기반으로 레벨 업!
UI 개발의 혼돈에서 벗어나 일관성과 효율성을 잡는 우리만의 무기 만들기 🚀
들어가며: "이 버튼, 어제 본 그 버튼이랑 왜 다르죠?" 🤔
자, 여기 개발자 A와 디자이너 B가 있습니다.
B는 오늘도 완벽한 디자인 시안을 A에게 전달했습니다. 픽셀 하나까지 완벽하게 계산된 아름다운 버튼이죠. A는 시안을 보고 열심히 코드를 짭니다. `button.css` 파일을 열고, 클래스 이름을 고민하고, 패딩과 마진을 맞추고... 드디어 완성! 🎉
그런데 며칠 뒤, 다른 페이지에서 비슷한 버튼이 필요해졌습니다. A는 생각합니다. '어, 저번에 만들었던 거랑 비슷한데... 그냥 복붙해서 색깔만 좀 바꿀까?' 그렇게 `button-new.css` 파일이 탄생합니다. 또 며칠 뒤, 또 다른 버튼이 필요해지고... `button-final.css`, `button-real-final.css` 같은 파일들이 좀비처럼 늘어납니다.
결국 프로젝트는 걷잡을 수 없이 커지고, 디자이너 B는 외칩니다. "아니, 왜 페이지마다 버튼 모양, 색깔, 심지어 마우스를 올렸을 때 반응까지 다 다른 거죠?!" A는 할 말이 없습니다. 수백 개의 CSS 파일 속에서 어떤 게 진짜 '마스터 버튼'인지 알 길이 없으니까요.
이거 완전 남 얘기 아니죠? 우리 모두 한 번쯤 겪어봤을 법한, 웃프지만 현실적인 이야기입니다. 이런 문제를 '개발자와 디자이너의 소통 부족'이나 '개인의 부주의' 탓으로만 돌리기엔 너무나 구조적인 문제입니다. 우리는 매번 같은 실수를 반복하고, 시간과 에너지를 낭비하고 있죠.
이 지긋지긋한 혼돈의 고리를 끊어줄 구원투수가 바로 '디자인 시스템(Design System)'입니다. 그리고 그 시스템을 더욱 강력하고 유연하게 만들어 줄 최신 기술이 바로 '웹 컴포넌트(Web Components)'고요.
"디자인 시스템? 그거 그냥 예쁜 UI 모아놓은 가이드북 아니야?" 라고 생각하셨다면, 오늘 이 글을 통해 그 생각이 180도 바뀔 거라고 확신합니다. 디자인 시스템은 단순한 결과물이 아니라, 제품을 만드는 *모두*가 같은 언어로 소통하고, 더 빠르고, 더 일관되게, 더 효율적으로 일하게 만드는 '문화'이자 '프로세스'입니다.
이 글에서는 디자인 시스템이 왜 필요한지, 그 본질은 무엇인지부터 시작해서, 왜 하필 '웹 컴포넌트'라는 기술이 이 시대의 디자인 시스템에 가장 적합한 접근법인지 깊이 있게 파고들 겁니다. 그리고 마지막에는 직접 웹 컴포넌트로 우리만의 작은 디자인 시스템을 구축해보는 실전 예제까지 함께할 거예요.
자, 이제 커피 한 잔 들고 편하게 앉으세요. 여러분의 개발 라이프를 한 단계 업그레이드시켜 줄 '디자인 시스템 with 웹 컴포넌트'의 세계로 함께 떠나볼까요? 🚀
1. 디자인 시스템, 너 정체가 뭐니? 🧐
디자인 시스템이라는 말을 들으면 많은 사람들이 'UI 키트'나 '스타일 가이드'를 떠올립니다. 물론 틀린 말은 아니에요. 하지만 그건 디자인 시스템의 아주 작은 일부일 뿐입니다. 코끼리의 다리만 만져보고 "코끼리는 기둥처럼 생겼다!"고 말하는 것과 같죠.
진정한 의미의 디자인 시스템은 제품을 만드는 원칙과 패턴, 그리고 도구들의 집합체입니다. 마치 레고 블록 세트와 같아요. 정해진 모양과 색깔의 블록(컴포넌트)들이 있고, 그 블록들을 어떻게 조립해야 멋진 성을 만들 수 있는지에 대한 설명서(가이드라인)와 철학(원칙)이 함께 제공되는 거죠.
디자인 시스템의 3대 핵심 요소: 철학, 벽돌, 그리고 설명서
제대로 된 디자인 시스템은 보통 다음 세 가지 요소를 유기적으로 포함하고 있습니다.
1. 디자인 원칙 (Design Principles) - 우리의 '북극성' ✨
이건 우리 팀이 디자인과 개발을 할 때 가장 중요하게 생각하는 가치, 즉 '철학'입니다. "단순함이 최고다", "사용자에게 명확한 피드백을 제공한다", "접근성을 최우선으로 고려한다" 와 같은 것들이죠. 이 원칙들은 우리가 길을 잃었을 때 방향을 제시해주는 북극성 역할을 합니다. 새로운 컴포넌트를 만들거나 디자인 결정을 내릴 때, "이 결정이 우리의 원칙에 부합하는가?"를 스스로에게 물어보게 만들죠.
2. 컴포넌트 라이브러리 (Component Library) - 우리의 '레고 블록' 🧱
이게 바로 우리가 흔히 생각하는 디자인 시스템의 실체입니다. 버튼, 인풋, 모달, 드롭다운, 카드 등 재사용 가능한 UI 조각들이죠. 중요한 건, 이 컴포넌트들은 단순히 디자인 시안으로만 존재하는 게 아니라, 실제 코드로 구현되어 언제든 가져다 쓸 수 있는 상태여야 한다는 겁니다. 개발자들은 더 이상 매번 버튼을 새로 만들 필요 없이, 라이브러리에서 `our-button` 같은 태그 하나만 가져와서 사용하면 됩니다. 효율성이 폭발적으로 증가하는 지점이죠!
3. 스타일 가이드 & 문서 (Style Guide & Documentation) - 우리의 '조립 설명서' 📜
최고의 레고 블록이 있어도 설명서가 없으면 제대로 된 성을 만들기 어렵습니다. 스타일 가이드는 바로 그 설명서 역할을 합니다.
여기에는 다음과 같은 내용이 포함됩니다.
- 디자인 토큰 (Design Tokens): 색상 팔레트, 타이포그래피(폰트 크기, 굵기, 행간), 간격(spacing), 그림자 효과 등 디자인의 가장 기본이 되는 값들을 변수화한 것입니다. 예를 들어, 프라이머리 색상을 `#3498db`라고 하드코딩하는 대신 `--color-primary`라는 변수로 관리하는 거죠. 이렇게 하면 나중에 브랜드 색상이 바뀌었을 때, 변수 값 하나만 바꾸면 시스템 전체에 적용됩니다. 🤯
- 사용법 가이드 (Usage Guidelines): 각 컴포넌트를 언제, 어떻게 사용해야 하는지에 대한 규칙입니다. "에러 메시지를 표시할 때는 빨간색 알림(Alert) 컴포넌트를 사용하세요", "버튼은 한 줄에 최대 3개까지만 배치하세요" 와 같은 'Do & Don't' 목록이 포함되죠.
- 보이스 앤 톤 (Voice & Tone): 우리 서비스가 사용자에게 어떤 말투로 말을 거는지에 대한 가이드입니다. "친근하고 유머러스하게" 또는 "전문적이고 신뢰감 있게" 등 서비스의 정체성을 규정합니다.
- 접근성 (Accessibility): 모든 사용자가 우리 서비스를 동등하게 이용할 수 있도록 지켜야 할 지침입니다. 키보드만으로 조작이 가능한지, 스크린 리더가 제대로 내용을 읽어주는지 등에 대한 내용이 담겨있죠.
- Atoms (원자): 더 이상 쪼갤 수 없는 가장 작은 단위. Label, Input, Button 같은 것들이죠.
- Molecules (분자): 원자들이 모여 하나의 기능을 수행하는 단위. 예를 들어, Label, Input, Button이 모여 '검색 폼'이라는 분자가 됩니다.
- Organisms (유기체): 분자들이 모여 만들어진 더 복잡한 UI 영역. 검색 폼, 로고, 내비게이션 메뉴가 모여 '헤더'라는 유기체가 되죠.
- Templates (템플릿): 유기체들이 모여 페이지의 전체적인 레이아웃 구조를 형성합니다. 실제 콘텐츠는 없는 뼈대 상태예요.
- Pages (페이지): 템플릿에 실제 텍스트, 이미지 등의 콘텐츠가 채워진 최종 결과물입니다.
그래서 디자인 시스템을 도입하면 뭐가 좋은데? (a.k.a 경영진 설득 자료)
"와, 좋은 건 알겠는데... 저거 다 만들려면 시간 엄청 걸리겠네요. 그냥 하던 대로 하면 안 되나요?" 라는 생각이 드시나요? 맞습니다. 디자인 시스템 구축은 단기적으로는 시간과 노력이 드는 일입니다. 하지만 장기적으로는 그 이상의 보상을 가져다주는 엄청난 투자죠.
1. 미친 개발 속도와 효율성 ⚡️
더 이상 디자이너는 매번 버튼을 새로 그릴 필요가 없고, 개발자는 매번 CSS를 새로 짤 필요가 없습니다. 이미 만들어진 '레고 블록'을 가져다 조립하기만 하면 되니까요. 개발자는 비즈니스 로직에 더 집중할 수 있고, 디자이너는 더 큰 그림의 사용자 경험(UX)을 고민할 시간을 벌게 됩니다.
2. 흔들리지 않는 디자인 일관성 🎨
어느 페이지를 가든 사용자는 통일된 경험을 하게 됩니다. 버튼의 모양, 아이콘의 스타일, 용어 하나까지 모두 일관성을 유지하죠. 이는 사용자에게 안정감과 신뢰감을 주고, 브랜드의 정체성을 강화하는 데 결정적인 역할을 합니다.
3. 디자이너와 개발자의 완벽한 소통 🤝
디자인 시스템은 디자이너와 개발자 사이의 '공용어'가 됩니다. 더 이상 "이거 패딩 8px만 줄여주세요" 같은 모호한 대화 대신, "여기에는 `spacing-small` 토큰을 적용해주세요" 또는 "이 기능에는 `primary-button` 컴포넌트를 사용합시다" 와 같이 명확하고 구체적인 소통이 가능해집니다. 오해와 재작업이 줄어드는 건 당연한 결과겠죠.
4. 손쉬운 유지보수와 확장성 🛠️
만약 브랜드의 메인 컬러를 바꾸기로 결정했다면? 디자인 시스템이 없다면 수십, 수백 개의 파일을 뒤져서 일일이 색상 코드를 바꿔야 하는 끔찍한 일이 벌어집니다. 하지만 디자인 토큰 기반의 시스템에서는 단 한 줄, `--color-primary` 변수의 값만 바꾸면 모든 것이 마법처럼 변경됩니다. 새로운 기능이나 페이지를 추가할 때도 기존 컴포넌트를 재활용하므로 확장도 매우 용이합니다.
이처럼 디자인 시스템은 단순히 '예쁘게' 만드는 것을 넘어, '제대로, 빠르게, 함께' 만드는 문화를 정착시키는 핵심적인 도구입니다. 이제 이 강력한 시스템을 어떤 기술로 구현해야 가장 효과적일지, 다음 챕터에서 본격적으로 알아보겠습니다.
2. 왜 하필 '웹 컴포넌트'인가? 🤔
자, 디자인 시스템의 필요성에는 모두가 공감했습니다. 이제 남은 질문은 '어떻게(How)?' 입니다. 어떤 기술을 사용해서 우리의 컴포넌트 라이브러리를 만들어야 할까요?
과거에는 jQuery 플러그인 형태로 만들거나, 부트스트랩 같은 CSS 프레임워크를 커스터마이징해서 사용하곤 했습니다. 그리고 최근 몇 년간은 React, Vue, Angular 같은 자바스크립트 프레임워크가 대세가 되면서, 각 프레임워크에 종속된 컴포넌트 라이브러리를 만드는 것이 일반적이었습니다.
예를 들어, 회사의 메인 기술 스택이 React라면, 모든 컴포넌트를 React로 만듭니다. 그런데 문제가 생깁니다. 마케팅팀에서 운영하는 블로그는 Vue 기반의 정적 사이트 생성기(SSG)를 쓰고 있고, 레거시 시스템 중 일부는 여전히 jQuery와 바닐라 자바스크립트로 돌아가고 있다면? React로 만든 컴포넌트는 이 환경들에서 쓸 수가 없습니다. 결국 각 기술 스택에 맞춰 똑같은 버튼을 또 만들어야 하는 비극이 반복됩니다. 😭
이것이 바로 '프레임워크 파편화' 문제입니다. 기술의 발전 속도가 워낙 빠르다 보니, 오늘은 React가 대세지만 5년 뒤에는 어떤 프레임워크가 시장을 지배할지 아무도 모릅니다. 특정 프레임워크에 종속된 디자인 시스템은 그 프레임워크의 운명과 함께하는 '시한부' 시스템이 될 수밖에 없습니다.
바로 이 지점에서, 구원투수처럼 등장한 기술이 바로 웹 컴포넌트(Web Components)입니다.
웹 컴포넌트: 프레임워크의 지배에서 벗어난 표준 기술
웹 컴포넌트는 특정 라이브러리나 프레임워크가 아닙니다. React나 Vue처럼 누군가 만든 기술이 아니라, 웹 브라우저 자체가 네이티브로 지원하는 표준 기술 모음입니다. W3C라는 웹 표준 기구에서 "앞으로 웹 페이지의 재사용 가능한 부품은 이렇게 만들자!"고 약속한 공식적인 방법론인 셈이죠.
이게 왜 중요하냐고요? 브라우저가 직접 지원한다는 건, 어떤 프레임워크를 쓰든, 혹은 아예 쓰지 않든 상관없이 어디서나 동일하게 동작한다는 의미입니다. 마치 `
`, `` 태그처럼, 우리가 만든 `
웹 컴포넌트는 크게 네 가지 핵심 기술로 이루어져 있습니다. 이 '4대 천왕'만 이해하면 웹 컴포넌트의 절반은 마스터한 셈이니, 조금만 집중해 주세요!
1. 커스텀 엘리먼트 (Custom Elements) 🛠️
말 그대로 '나만의 HTML 태그'를 만들 수 있게 해주는 기술입니다. 자바스크립트 클래스를 이용해서 새로운 HTML 요소의 동작과 모양을 정의하고, `customElements.define('my-button', MyButtonClass)`와 같은 코드로 브라우저에 등록할 수 있습니다. 등록이 끝나면, 우리는 HTML 어디서든 `
2. 섀도우 돔 (Shadow DOM) 🎭
웹 컴포넌트의 '킬러 기능'이라고 할 수 있습니다. 섀도우 돔은 컴포넌트의 내부 구조, 스타일, 동작을 외부로부터 완벽하게 격리시키는 '비밀의 방'을 만들어줍니다.
이게 왜 강력하냐면, CSS 스타일 충돌 문제를 원천적으로 해결해주기 때문입니다. 컴포넌트 안에 정의된 스타일은 절대 밖으로 새어 나가지 않고(No Leak Out), 바깥에 정의된 스타일도 컴포넌트 안으로 함부로 침투하지 못합니다(No Leak In). 우리는 더 이상 BEM 같은 복잡한 CSS 작명 규칙이나 `!important` 남용에 시달리지 않아도 됩니다. 컴포넌트는 스스로의 스타일에만 신경 쓰면 되는 '독립적인 왕국'이 되는 거죠.
3. HTML 템플릿 (`` & `
`` 태그는 화면에 바로 렌더링되지 않는 HTML 조각을 담아두는 '설계도' 역할을 합니다. 이 안에 컴포넌트의 기본 구조를 마크업 해두고, 필요할 때마다 자바스크립트로 복제해서 사용하는 방식이죠.
`
4. ES 모듈 (ES Modules) 📦
이건 사실 웹 컴포넌트만의 기술은 아니지만, 웹 컴포넌트를 실용적으로 만들어주는 중요한 표준입니다. `import`와 `export` 구문을 사용해서 자바스크립트 파일을 모듈화하고, 필요한 컴포넌트 정의를 다른 파일에서 쉽게 가져와 사용할 수 있게 해줍니다. 과거의 복잡한 모듈 시스템(AMD, CommonJS)을 대체하는 표준 방식이죠.
- Lit (구 LitElement): 구글에서 만든 라이브러리로, 가볍고 빠르며 반응형 템플릿 시스템을 제공해 React와 비슷한 개발 경험을 줍니다. 현재 가장 인기 있는 선택지 중 하나입니다.
- Stencil.js: Ionic 프레임워크 팀에서 만든 '웹 컴포넌트 컴파일러'입니다. JSX, 타입스크립트 등 최신 개발 도구를 사용해서 컴포넌트를 만들면, 최종적으로는 최적화된 순수 웹 컴포넌트 코드를 생성해줍니다.
웹 컴포넌트 기반 디자인 시스템의 압도적인 장점
자, 이제 웹 컴포넌트가 무엇인지 알았으니, 이것을 디자인 시스템에 적용했을 때 어떤 엄청난 일들이 벌어지는지 정리해볼까요?
1. 프레임워크 독립성 (Framework Agnostic) 🌐
가장 큰 장점입니다. 한 번 웹 컴포넌트로 만들어두면, 우리 회사가 지금 React를 쓰든, 내년에 Vue로 전환하든, 심지어 10년 뒤에 등장할 새로운 프레임워크를 도입하든 상관없이 디자인 시스템을 그대로 사용할 수 있습니다. 기술 변화에 대한 '보험'을 들어두는 셈이죠.
2. 미래 지향성 및 안정성 (Future-Proof & Stable) 🏛️
웹 컴포넌트는 특정 회사가 주도하는 라이브러리가 아닌, 웹 표준입니다. 이는 곧 브라우저 제조사들(구글, 애플, 모질라, 마이크로소프트)이 함께 유지보수하고 발전시켜나간다는 의미입니다. 갑자기 프로젝트가 중단되거나, 호환성이 깨지는 업데이트가 발생할 위험이 훨씬 적습니다. 장기적으로 운영해야 하는 디자인 시스템에겐 최고의 안정성을 제공하죠.
3. 완벽한 캡슐화 (Perfect Encapsulation) 🛡️
섀도우 돔 덕분에, 디자인 시스템의 컴포넌트는 외부 환경의 영향을 거의 받지 않습니다. 어떤 복잡한 레거시 프로젝트에 우리 컴포넌트를 가져다 놓아도, 기존 CSS와 충돌하여 스타일이 깨질 걱정을 할 필요가 없습니다. 이는 컴포넌트의 예측 가능성과 신뢰도를 극적으로 높여줍니다.
4. 점진적 도입 가능 (Progressive Adoption) 🚶♂️
전체 프로젝트를 한 번에 뒤엎을 필요가 없습니다. 기존 프로젝트의 아주 작은 부분, 예를 들어 버튼 하나부터 웹 컴포넌트로 교체해볼 수 있습니다. 성공적인 경험을 바탕으로 점차 적용 범위를 넓혀나가는 '점진적 도입' 전략을 사용하기에 매우 용이합니다.
물론 웹 컴포넌트가 만능 해결책은 아닙니다. 서버 사이드 렌더링(SSR) 지원이 프레임워크에 비해 다소 까다롭고, 아직은 생태계가 성숙하는 과정에 있다는 점 등 고려해야 할 부분도 있습니다. 하지만 디자인 시스템이라는 '보편적이고 장기적인' 목표를 생각했을 때, 웹 컴포넌트가 제공하는 '상호운용성'과 '안정성'이라는 가치는 다른 어떤 기술도 따라오기 힘든 강력한 매력입니다.
이제 이론은 충분히 배웠으니, 다음 장에서는 직접 코드를 만져보며 우리만의 웹 컴포넌트 기반 디자인 시스템을 만들어보는 실전으로 들어가 보겠습니다!
3. 실전! 웹 컴포넌트 기반 디자인 시스템 구축하기 👩💻👨💻
백문이 불여일견! 이제 직접 키보드를 잡고 우리만의 작은 디자인 시스템을 만들어 볼 시간입니다. 거창한 시스템을 한 번에 다 만들 수는 없으니, 가장 핵심적인 단계들을 차근차근 밟아가며 감을 익혀보겠습니다. 여기서는 별도의 라이브러리 없이 순수한 바닐라 자바스크립트(Vanilla JS)를 사용해서 웹 컴포넌트의 기본 원리를 깊이 이해하는 데 초점을 맞출게요.
Step 0: 시스템의 뼈대, 디자인 토큰 정의하기
컴포넌트를 만들기 전에 가장 먼저 해야 할 일은 디자인의 기본 단위, 즉 '디자인 토큰'을 정의하는 것입니다. 색상, 폰트, 간격 등을 변수로 만들어두면, 나중에 일관성을 유지하고 전체 디자인을 한 번에 변경하기가 매우 쉬워집니다.
가장 좋은 방법은 이 토큰들을 CSS 커스텀 속성(Custom Properties)으로 만드는 것입니다. 보통 `:root` 가상 클래스에 선언하여 전역적으로 사용할 수 있게 합니다.
`design-tokens.css` 파일을 하나 만들어 볼까요?
/* design-tokens.css */
:root {
/* 🎨 Colors */
--color-primary: #667eea;
--color-primary-dark: #5a6fd5;
--color-secondary: #764ba2;
--color-text: #333333;
--color-text-light: #ffffff;
--color-background: #ffffff;
--color-gray-light: #f3f4f6;
--color-border: #e5e7eb;
--color-success: #2ecc71;
--color-error: #e74c3c;
/* ண்ட் Typography */
--font-family-base: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
--font-size-small: 0.875rem; /* 14px */
--font-size-medium: 1rem; /* 16px */
--font-size-large: 1.125rem; /* 18px */
--font-weight-regular: 400;
--font-weight-bold: 700;
/* 📏 Spacing */
--spacing-xs: 0.25rem; /* 4px */
--spacing-sm: 0.5rem; /* 8px */
--spacing-md: 1rem; /* 16px */
--spacing-lg: 1.5rem; /* 24px */
--spacing-xl: 2rem; /* 32px */
/* ✨ Effects */
--border-radius-sm: 4px;
--border-radius-md: 8px;
--box-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
이제 이 CSS 파일을 우리의 프로젝트에 포함시키기만 하면, 어디서든 `var(--color-primary)`와 같은 방식으로 이 변수들을 사용할 수 있습니다. 디자인의 '단일 진실 공급원(Single Source of Truth)'이 마련된 셈이죠!
Step 1: 첫 컴포넌트 만들기 - '힙스터 버튼' (Hipster Button)
가장 기본적이면서도 가장 많이 쓰이는 컴포넌트, 바로 버튼부터 만들어 보겠습니다. 우리가 만들 버튼의 이름은 `
- 기본 스타일을 가진다.
- `variant` 속성으로 'primary', 'secondary' 스타일을 지정할 수 있다.
- `disabled` 속성을 받으면 비활성화된다.
- 버튼 안에 들어가는 내용은 자유롭게 넣을 수 있다. (`
` 활용)
`hipster-button.js` 파일을 생성하고 코드를 작성해 봅시다.
// hipster-button.js
// 1. 템플릿(설계도) 정의
const template = document.createElement('template');
template.innerHTML = `
<style>
/* 섀도우 돔 안의 스타일은 외부에 영향을 주지 않아요! */
:host {
display: inline-block;
/* :host는 커스텀 엘리먼트 자체를 가리킵니다. */
}
.btn {
padding: var(--spacing-sm) var(--spacing-md);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-md);
background-color: var(--color-background);
color: var(--color-text);
font-family: var(--font-family-base);
font-size: var(--font-size-medium);
font-weight: var(--font-weight-bold);
cursor: pointer;
transition: all 0.2s ease-in-out;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: var(--box-shadow-md);
}
/* variant 속성에 따른 스타일 변화 */
:host([variant="primary"]) .btn {
background-color: var(--color-primary);
color: var(--color-text-light);
border-color: var(--color-primary);
}
:host([variant="primary"]) .btn:hover {
background-color: var(--color-primary-dark);
border-color: var(--color-primary-dark);
}
:host([variant="secondary"]) .btn {
background-color: var(--color-secondary);
color: var(--color-text-light);
border-color: var(--color-secondary);
}
/* disabled 속성이 있을 때의 스타일 */
:host([disabled]) .btn {
background-color: var(--color-gray-light);
color: #999;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
</style>
<button class="btn">
<slot>Default Text</slot>
<!-- slot: 이 위치에 외부 콘텐츠가 들어옵니다. -->
</button>
`;
// 2. 커스텀 엘리먼트 클래스 정의
class HipsterButton extends HTMLElement {
constructor() {
super();
// 섀도우 돔을 생성하고, 템플릿을 복제해서 붙여넣습니다.
this.attachShadow({ mode: 'open' }); // 'open' 모드는 외부 JS에서 섀도우 돔에 접근 가능
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
// disabled 속성을 진짜 button 요소와 동기화하는 로직
static get observedAttributes() {
return ['disabled']; // 'disabled' 속성 변경을 감지
}
attributeChangedCallback(name, oldValue, newValue) {
const button = this.shadowRoot.querySelector('button');
if (name === 'disabled') {
if (newValue !== null) {
button.setAttribute('disabled', '');
} else {
button.removeAttribute('disabled');
}
}
}
}
// 3. 커스텀 엘리먼트 등록
window.customElements.define('hipster-button', HipsterButton);
코드가 조금 길어 보이지만, 구조는 간단합니다.
- ``을 이용해 컴포넌트의 내부 HTML 구조와 CSS 스타일을 정의합니다. CSS는 `
- `HTMLElement`를 상속받는 클래스를 만듭니다. `constructor`에서 `attachShadow`를 호출하여 섀도우 돔을 활성화하고, 준비된 템플릿을 그 안에 삽입합니다.
- `customElements.define()`을 사용해 우리가 만든 클래스를 `
`이라는 태그 이름과 연결하여 브라우저에 공식적으로 등록합니다.
이제 이 컴포넌트를 어떻게 사용할까요? 아주 간단합니다. HTML 파일에서 `hipster-button.js` 파일을 로드하고, 그냥 HTML 태그처럼 쓰면 됩니다.
`index.html`
<!DOCTYPE html>
<html lang="ko">
<head>
<title>Web Component Test</title>
<!-- 1. 디자인 토큰 CSS 로드 -->
<link rel="stylesheet" href="design-tokens.css">
</head>
<body>
<h1>Hipster Button 컴포넌트 테스트</h1>
<hipster-button>기본 버튼</hipster-button>
<hipster-button variant="primary">프라이머리 버튼</hipster-button>
<hipster-button variant="secondary">세컨더리 버튼</hipster-button>
<hipster-button disabled>비활성 버튼</hipster-button>
<!-- 2. 웹 컴포넌트 JS 로드 -->
<script type="module" src="hipster-button.js"></script>
</body>
</html>
이 HTML 파일을 브라우저에서 열어보세요. 우리가 정의한 스타일대로 멋진 버튼들이 나타나는 것을 볼 수 있습니다! 🤩 개발자 도구를 열어 `hipster-button` 요소를 살펴보면 `#shadow-root`가 생성된 것도 확인할 수 있을 거예요.
Step 2: 시스템화 & 문서화 - Storybook으로 날개 달기
컴포넌트를 하나둘 만들다 보면, 이걸 어떻게 체계적으로 관리하고 다른 팀원들에게 공유할지 고민이 됩니다. 그냥 폴더에 파일만 쌓아두는 건 좋은 방법이 아니죠. 이럴 때 사용하는 최고의 도구가 바로 Storybook입니다.
Storybook은 UI 컴포넌트를 위한 개발, 테스트, 문서화를 위한 독립적인 환경을 제공하는 오픈소스 도구입니다. 실제 애플리케이션과 분리된 환경에서 컴포넌트를 하나씩 따로따로 만들어보고, 다양한 상태(props)에 따라 어떻게 보이는지 시각적으로 확인할 수 있게 해줍니다.
Storybook은 React, Vue 등 다양한 프레임워크를 지원하며, 당연히 웹 컴포넌트도 완벽하게 지원합니다. Storybook을 도입하면 다음과 같은 장점이 있습니다.
- 살아있는 문서(Living Documentation): 디자이너, 기획자, 개발자 모두가 항상 최신 버전의 컴포넌트를 직접 눈으로 보고 인터랙션 해볼 수 있습니다. 더 이상 오래된 스크린샷 기반의 가이드 문서를 보지 않아도 되죠.
- 독립적인 개발 환경: 복잡한 비즈니스 로직이나 데이터 로딩 없이, 오직 UI 컴포넌트 개발에만 집중할 수 있습니다.
- 다양한 케이스 테스트: 컴포넌트가 가질 수 있는 모든 경우의 수(예: 텍스트가 매우 길어질 때, disabled 상태일 때 등)를 '스토리(Story)'로 만들어 쉽게 테스트하고 시각적으로 회귀 테스트(Visual Regression Testing)를 할 수 있습니다.
Storybook을 웹 컴포넌트 프로젝트에 설치하고 `hipster-button`의 스토리를 작성하는 것은 생각보다 간단합니다. (설치 과정은 공식 문서를 참고해주세요!) 스토리는 대략 이런 모습일 겁니다.
`hipster-button.stories.js`
import './hipster-button.js'; // 컴포넌트 정의를 불러옵니다.
export default {
title: 'Components/HipsterButton', // Storybook 사이드바에 표시될 이름
argTypes: { // 컴포넌트의 속성을 컨트롤할 수 있는 UI를 정의
variant: {
control: { type: 'select' },
options: ['default', 'primary', 'secondary'],
},
disabled: { control: 'boolean' },
slotContent: { control: 'text', name: 'slot' }
},
};
// 템플릿 함수
const Template = ({ variant, disabled, slotContent }) => `
<hipster-button
variant="${variant}"
${disabled ? 'disabled' : ''}
>
${slotContent}
</hipster-button>
`;
// 각 상태에 대한 스토리들
export const Default = Template.bind({});
Default.args = {
variant: 'default',
disabled: false,
slotContent: 'Default Button',
};
export const Primary = Template.bind({});
Primary.args = {
variant: 'primary',
disabled: false,
slotContent: 'Primary Button',
};
export const Disabled = Template.bind({});
Disabled.args = {
variant: 'primary',
disabled: true,
slotContent: 'Disabled Button',
};
이렇게 스토리를 작성하고 Storybook을 실행하면, 웹 브라우저에서 `hipster-button`의 다양한 모습을 실시간으로 바꿔보며 테스트할 수 있는 멋진 UI가 나타납니다. 이것이 바로 여러분의 디자인 시스템의 '얼굴'이 되는 셈이죠.
Step 3: 배포 및 사용 - 세상 밖으로!
자, 이제 우리만의 컴포넌트도 만들었고, Storybook으로 멋진 문서도 갖췄습니다. 이제 이걸 다른 프로젝트에서 실제로 사용할 수 있도록 배포해야겠죠?
가장 일반적인 방법은 우리의 디자인 시스템을 하나의 패키지로 만들어 npm(Node Package Manager)에 배포하는 것입니다. 프라이빗 패키지로 만들면 회사 내부에서만 안전하게 사용할 수 있습니다.
배포 과정은 다음과 같이 요약할 수 있습니다.
- `package.json` 설정: 프로젝트의 이름, 버전, 메인 파일 등을 설정합니다.
- 빌드 프로세스 구축: 여러 개의 컴포넌트 파일들을 하나의 파일로 합치거나(Bundling), 최신 자바스크립트 문법을 구형 브라우저에서도 동작하도록 변환하는(Transpiling) 과정을 설정합니다. Rollup이나 Webpack 같은 번들러를 주로 사용합니다.
- npm에 배포: `npm publish` 명령어를 통해 패키지를 npm 레지스트리에 업로드합니다.
배포가 완료되면, 이제 어떤 프로젝트에서든 우리 디자인 시스템을 설치해서 사용할 수 있습니다.
`npm install my-design-system`
그리고 사용하는 프로젝트의 메인 자바스크립트 파일에서 컴포넌트를 불러오기만 하면 됩니다.
`main.js`
// 필요한 모든 컴포넌트를 한 번에 불러옵니다.
import 'my-design-system';
// 또는 필요한 컴포넌트만 개별적으로 불러올 수도 있습니다.
// import 'my-design-system/dist/hipster-button.js';
이제 이 `main.js`를 포함하는 모든 HTML 파일에서 `
4. 현실적인 조언과 꿀팁 🍯
지금까지 웹 컴포넌트 기반 디자인 시스템의 이론과 실전을 모두 맛보았습니다. 하지만 실제 현업에 도입하는 과정은 또 다른 이야기입니다. 성공적인 안착을 위해 몇 가지 현실적인 조언을 덧붙이고자 합니다.
1. 처음부터 완벽을 추구하지 마세요 (Start Small)
"우리도 구글의 머티리얼 디자인처럼 만들자!"는 의욕은 좋지만, 처음부터 너무 거대한 목표를 세우면 지치기 쉽습니다. 가장 많이 사용되고, 가장 문제가 많이 발생하는 핵심 컴포넌트 몇 개(예: Button, Input, Color Palette)부터 시작하세요. 작은 성공을 만들어 팀원들에게 디자인 시스템의 가치를 증명하고, 점진적으로 시스템을 확장해나가는 것이 현명한 전략입니다.
2. 디자인 시스템은 '제품'입니다 (It's a Product, not a Project)
디자인 시스템은 한 번 만들고 끝나는 프로젝트가 아닙니다. 비즈니스가 성장하고 새로운 요구사항이 생기면서 끊임없이 진화하고 유지보수되어야 하는 '제품'입니다. 전담 팀이나 담당자를 두는 것이 이상적이며, 버그 수정, 기능 추가, 사용성 개선을 위한 지속적인 노력이 필요합니다. 버전 관리(Semantic Versioning)를 철저히 해서, 시스템의 변경 사항이 다른 팀에 미치는 영향을 명확히 알려주는 것도 매우 중요합니다.
3. 협업, 또 협업! (Collaboration is Key)
디자인 시스템은 개발자 혼자, 혹은 디자이너 혼자 만들 수 없습니다. 기획자, 디자이너, 개발자, QA 등 제품을 만드는 모든 구성원이 함께 참여하고 의견을 내야 합니다. 시스템 구축 초기부터 모두를 참여시켜 공감대를 형성하고, '우리 모두의 시스템'이라는 인식을 심어주는 것이 성공의 가장 큰 열쇠입니다. 정기적인 회의나 워크숍을 통해 피드백을 수렴하고 개선 방향을 함께 논의하세요.
4. 문서는 생명입니다 (Documentation is Oxygen)
아무리 좋은 컴포넌트를 만들어도 사용법을 모르면 무용지물입니다. 위에서 소개한 Storybook 같은 도구를 활용하여 항상 최신의 '살아있는 문서'를 제공해야 합니다. 각 컴포넌트의 속성(Props)은 무엇인지, 어떤 상황에서 사용해야 하는지(Do & Don't), 접근성은 어떻게 구현되었는지 등을 상세하게 기록하세요. 잘 만들어진 문서는 시스템의 사용률을 높이고, 팀원들의 문의에 답변하는 시간을 줄여줍니다.
5. 전문가의 도움을 받는 것을 두려워하지 마세요
만약 팀 내에 디자인 시스템 구축 경험이 부족하다면, 외부 전문가의 도움을 받는 것도 좋은 방법입니다. 초기 방향 설정이나 기술 스택 선정, 구축 프로세스에 대한 자문을 구하는 것만으로도 수많은 시행착오를 줄일 수 있습니다. 재능넷과 같은 플랫폼을 통해 디자인 시스템 구축 경험이 풍부한 프리랜서 개발자나 컨설턴트의 도움을 받아 프로젝트의 성공 확률을 높일 수 있습니다.
마치며: 코드로 세상을 디자인하다
우리는 오늘 '버튼 하나가 왜 페이지마다 다른가'라는 작은 질문에서 시작하여, 디자인 시스템의 거대한 세계를 탐험하고, 웹 컴포넌트라는 강력한 무기를 손에 넣었습니다.
디자인 시스템 구축은 단순히 중복 코드를 줄이고 개발 속도를 높이는 기술적인 문제를 넘어섭니다. 그것은 파편화된 작업을 하나로 모으고, 디자이너와 개발자가 같은 언어로 소통하게 하며, 우리 팀이 더 창의적인 문제에 집중할 수 있도록 만드는 '문화 혁신'에 가깝습니다.
웹 컴포넌트를 기반으로 만든 디자인 시스템은 특정 기술의 유행에 휩쓸리지 않는 견고한 반석이 되어, 앞으로 수년간 여러분의 제품과 함께 성장해나갈 것입니다.
물론, 그 과정이 항상 쉽지만은 않을 겁니다. 수많은 기술적, 조직적 장벽에 부딪힐 수도 있습니다. 하지만 오늘 우리가 함께 나눈 지식과 경험이 그 여정의 든든한 나침반이 되어줄 것이라 믿습니다.
이제 여러분도 자신만의 디자인 시스템을 구축할 준비가 되었습니다. 더 이상 혼돈 속에서 헤매지 말고, 질서와 효율, 그리고 협업의 즐거움을 되찾으세요.
코드를 통해 세상을 디자인하는 멋진 여정에 여러분을 초대합니다! 🚀
댓글 작성
이 글에 대한 여러분의 생각을 들려주세요
로그인이 필요합니다
댓글을 작성하려면 먼저 로그인해주세요.
댓글 0
지식인의 숲 - 지적 재산권 보호 고지
지적 재산권 보호 고지
- 저작권 및 소유권: 본 컨텐츠는 재능넷의 독점 AI 기술로 생성되었으며, 대한민국 저작권법 및 국제 저작권 협약에 의해 보호됩니다.
- AI 생성 컨텐츠의 법적 지위: 본 AI 생성 컨텐츠는 재능넷의 지적 창작물로 인정되며, 관련 법규에 따라 저작권 보호를 받습니다.
- 사용 제한: 재능넷의 명시적 서면 동의 없이 본 컨텐츠를 복제, 수정, 배포, 또는 상업적으로 활용하는 행위는 엄격히 금지됩니다.
- 데이터 수집 금지: 본 컨텐츠에 대한 무단 스크래핑, 크롤링, 및 자동화된 데이터 수집은 법적 제재의 대상이 됩니다.
- AI 학습 제한: 재능넷의 AI 생성 컨텐츠를 타 AI 모델 학습에 무단 사용하는 행위는 금지되며, 이는 지적 재산권 침해로 간주됩니다.
아직 댓글이 없습니다.