들어가며
소프트웨어는 살아있는 생명체와 같아서, 시간이 흐르면 그 생애를 마감합니다. 우리 개발자의 역할은 제 몫을 다한 소프트웨어를 조심스럽게 분해하여 새로운 소프트웨어에 생명을 불어넣는 것입니다.
EOL(End of Life)을 맞이한 Vue2는 7년이 넘는 시간 동안 에잇퍼센트 프론트엔드를 무사히 잘 이끌어왔습니다. 이제는 Vue2를 쉬게 하고 새로운 기술로 전환할 때입니다.
에잇퍼센트는 웹 컴포넌트를 도입하여 Vue2에서 React로의 점진적인 마이그레이션을 진행하며 생기는 UI 개발비용을 최소화했습니다. 이 글에서는 웹 컴포넌트 도입 배경과 그 과정을 소개합니다.
** 이 글에서는 React, Vue.js등 통상적인 뜻인 “프레임워크”로 통합하여 사용합니다.
디자인 시스템 도입
UI 개발의 문제점은 참여하는 인원이 늘어날수록 디자인 일관성이 깨지고, 개발 비용이 증가한다는 것입니다. 에잇퍼센트도 성장하면서 개발자와 디자이너들의 구성원이 늘어나게 되었고 아래와 같은 문제점이 생겨났습니다.
- 동일한 컴포넌트라도 개발자마다 서로 다른 컴포넌트를 사용하고 있습니다.
- 이미 개발된 컴포넌트의 존재를 모르고 중복으로 개발하는 경우가 발생합니다.
- 구성원이 늘어날 때마다 디자이너와 개발자 간의 커뮤니케이션 비용이 증가합니다.
- 제대로 구현되었는지 확인하는 디자이너의 디자인 점검 비용이 증가합니다.
불편함이 반복되고 쌓여가면서 디자인 시스템에 대한 공감대가 형성되었고 디자인 시스템 도입을 논의하기 시작했습니다.
레거시 기술로 UI 컴포넌트 라이브러리 구현하기?
하지만 웹 프론트엔드 팀에게 고민이 있었습니다.
당시 주력으로 사용하던 Vue2는 이미 지원 중단이 예고된 상황이었기 때문에 프레임워크 마이그레이션을 고려하고 있었습니다. 여러 가지 이유로 React로의 마이그레이션을 고민하고 있었는데 디자인 시스템을 도입하게 된다면 UI 컴포넌트를 Vue2로 구현하는 것이 적절한 선택인지 고민이 되었습니다. 다음에 React로 전환할 때 UI 컴포넌트를 다시 구현해야 하고 그때마다 디자이너와 개발자 간의 커뮤니케이션 비용이 발생할 것이라는 우려가 있었습니다.
당시 웹 프론트엔드 팀이 선택할 수 있었던 옵션은 다음과 같았습니다.
- Vue2로 우선 UI 컴포넌트를 구현하고, 나중에 React로 마이그레이션 할 때 다시 구현한다.
- 먼저 Vue2를 React로 마이그레이션 한 후 React로 UI 컴포넌트를 구현한다.
- Vue2 UI 컴포넌트와 React UI 컴포넌트를 동시에 구현한다.
기술 부채가 눈덩이처럼 불어나, 모든 선택지가 상당한 시간과 비용을 요구하는 상황이었습니다. 선택지 모두 장단점이 존재하며, 어느 것이 최선인지 판단하기 어려웠습니다.
디자인 시스템 도입하면서 프레임워크 마이그레이션 대비하기
점진적인 마이그레이션 방식은 결국 두 개의 프레임워크가 공존하게 됩니다. 만약 UI 컴포넌트를 각각의 프레임워크에서 구현한다면 두 컴포넌트를 모두 유지 보수해야 하는 상황이 발생합니다. 이는 오히려 UI 일관성을 유지하기 어렵게 만들고, 개발자와 디자이너 간의 커뮤니케이션 비용을 증가시킵니다.
프론트엔드 프레임워크를 마이그레이션 하는 데 있어 큰 비용 중 하나는 UI 컴포넌트 개발 비용입니다. 동일한 “버튼” 컴포넌트를 Vue.js와 React에서 따로 구현해야 할까요? 두 컴포넌트의 유지보수는 어떻게 해야 할까요? 두 컴포넌트의 UI 일관성은 보장할 수 있을까요?
근본적인 문제 해결 방법은 두 프레임워크에서 동일하게 동작하는 UI 컴포넌트를 만드는 것입니다. 표준 웹 기술인 웹 컴포넌트를 도입하면 그것이 가능할 것이라는 의견이 나왔습니다.
웹 컴포넌트(Web components)
프론트엔드 개발자라도 “웹 컴포넌트”라는 기술을 처음 들어보는 분들이 많을 것 같습니다. “컴포넌트”라는 개념은 익숙하실 것 같은데요. 웹 컴포넌트는 이러한 컴포넌트 개념을 표준화하여, 프레임워크에 종속되지 않고 재사용할 수 있는 사용자 정의 요소를 만들 수 있게 해줍니다.
웹 컴포넌트는 브라우저가 제공하는 기본 API로 어느 프레임워크에도 종속되지 않는 독립적인 UI 요소로 만들 수 있습니다. 또한 Shadow DOM으로 캡슐화되어 있어, 외부 스타일이나 스크립트에 영향을 받지 않습니다.
프레임워크에 관계없는 UI 재사용성과 스타일 캡슐화라는 무기를 가진 웹 컴포넌트는 디자인 시스템 분야에서 주목받고 있습니다. 이미 Microsoft Fluent UI, Google Material Design, Adobe spectrum등 유명 기업의 디자인 시스템에서 웹 컴포넌트 UI 라이브러리 구현체를 제공하고 있습니다.
Lit
브라우저가 제공하는 기본 API만을 사용하여 웹 컴포넌트를 작성할 순 있지만, 부족한 기능을 직접 개발해야 하는 개발 생산성을 고려하여 웹 컴포넌트를 쉽게 작성할 수 있도록 도와주는 Lit 라이브러리를 도입했습니다.
Lit은 Google에서 개발한 웹 컴포넌트 라이브러리로 반응성(Reactivity), 타입스크립트, 서버 사이드 렌더링, 데코레이터를 활용한 선언적인 구현 등을 지원합니다.
Lit으로 구현한 간단한 예제입니다. 상태(state)를 선언하고 이벤트 핸들러를 부착하는 개발 경험이 Vue.js나 React와 크게 다르지 않습니다.
import { html, LitElement } from "lit";
import { customElement, state } from "lit/decorators.js";
// 컴포넌트를 정의합니다
@customElement("counter-button")
export class CounterButton extends LitElement {
// Shadow DOM으로 캡슐화되는 스타일을 정의합니다
static styles = css`
button {
color: blue;
}
`;
// 상태를 선언합니다
@state()
count = 0;
// 클릭 이벤트 핸들러를 정의합니다
onIncrease() {
this.count += 1;
}
// 렌더링 함수를 정의합니다
render() {
return html`<button @click=${this.onIncrease}>
increase ${this.count}
</button> `;
}
}
eds-wc
에잇퍼센트 디자인 시스템(EDS)을 웹 컴포넌트로 구현한 UI 라이브러리 eds-wc를 소개합니다.
eds-wc는 Modal, Button, Icon, Textfield등 다양한 UI 컴포넌트를 제공하고 있습니다. eds-wc로 구현된 컴포넌트는 브라우저 환경에서 동작하는 프레임워크라면 어디서든 사용할 수 있습니다.
eds-wc 덕분에 점진적인 프레임워크 마이그레이션 과정에서 생기는 UI 파편화를 방지할 수 있었습니다. 기존 Vue2에서 사용하던 UI 컴포넌트를 React로 마이그레이션 할 때도, eds-wc로 구현된 컴포넌트를 그대로 사용할 수 있었습니다.
- React UI 컴포넌트를 다시 구현할 필요가 없습니다.
- eds-wc를 SOT(Single Source of Truth)로 두어 하나의 코드만 유지 보수하면 됩니다.
- 이 모든 과정에서 생기는 디자이너와 개발자 간의 커뮤니케이션 비용을 줄일 수 있습니다.
적용하기
아래는 Next.js로 전환 중인 “대출” 서비스의 한 화면으로 5개의 eds-wc 컴포넌트로 구성됩니다.
이 화면의 UI를 구현하는 코드는 다음과 같은데, Vue2와 React에서 동일한 코드를 그대로 사용할 수 있습니다. 따로 해야할 일은 이벤트 핸들러, 상태관리등 각 프레임워크에 필요한 코드를 추가하는 것 입니다.
import {
EdsAppBar,
EdsText,
EdsTextfieldBigLine,
EdsDropdownOption,
EdsButton,
} from "@8percent/eds-wc";
// Vue2라면 template, React라면 JSX에 그대로 작성
<>
<EdsAppBar headline="금리 한도 조회" leftIcon="left-arrow" />
<EdsText token="body-l-bold">
금리・한도 조회를 위해 본인 인증을 진행할게요
</EdsText>
<EdsTextfieldBigLine label="이름" placeholder="이름" />
<EdsTextfieldBigLine label="주민등록번호" placeholder="생년월일" />
<EdsTextfieldBigLine placeholder="●●●●●●●" />
<EdsDropdownBigline label="통신사" placeholder="통신사를 선택해주세요.">
<EdsDropdownOption>SKT</EdsDropdownOption>
<EdsDropdownOption>KT</EdsDropdownOption>
// 생략
</EdsDropdownBigline>
<EdsTextfieldBigLin label="휴대폰 번호" />
<EdsButton text="인증번호 받기" fullWidth />
</>;
마이그레이션에서 생기는 추가적인 UI 개발 비용은 0에 가깝기 때문에 프론트엔드 개발자는 중요한 비즈니스 로직에 집중할 수 있습니다.
마무리
요약하면 다음과 같습니다.
- 점진적인 프레임워크 마이그레이션 방식에는 두 프레임워크가 공존하게 됩니다.
- 두 프레임워크를 운영하면 UI 컴포넌트의 파편화가 생겨 UI 일관성을 지키기 어렵고 유지보수 비용이 증가합니다.
- 웹 표준 기술인 웹 컴포넌트를 도입하여 점진적인 마이그레이션 과정에서 생기는 UI 개발 비용을 최소화할 수 있습니다.
그 외에 못 다룬 내용은 다음과 같습니다.
- 웹 컴포넌트도 서버 사이드 렌더링을 할 수 있을까요?
- React에서 웹 컴포넌트의 Wrapper 컴포넌트를 사용해야 하는 이유 무엇일까요?
- shadow DOM으로 생기는 다양한 이슈들과 해결 방법
앞으로도 웹 컴포넌트와 디자인 시스템을 도입하면서 겪은 경험에 대해 공유할 예정이니 많은 관심 부탁드립니다.