안녕하세요. 라포랩스 Frontend Engineer 심은서입니다.
이 아티클에서는 Figma Variables를 활용한 디자인 토큰 자동화로, 단 한 줄의 코드만으로 퀸잇 서비스에 다크 테마를 적용한 경험을 공유합니다.
1. 기존 자동화 시스템의 문제점
라포랩스는 Figma에서 색상과 애셋을 자동 추출해 개발과 디자인 간 커뮤니케이션 비용을 줄이는 시스템을 운영해왔습니다.
이 queenit-ui
라이브러리는 Figma에서 관리되고 있던 색상 팔레트 페이지에 접근해, 특정 노드의 색상 값을 가져오는 방식으로 색상을 추출하고 있었습니다.
그러나 이 시스템으로는 다음과 같은 문제들을 해결하기 어려웠습니다.
다크 테마 지원
퀸잇 앱에서는 특정 영역만 다크 테마로 강조하고 싶은 니즈가 있었습니다. 하지만 일부 컴포넌트에만 다크 테마를 적용하려면, 개발자와 디자이너가 각각 별도로 색상을 하드코딩해 따로 관리해야 했습니다.
예를들어, 기획전 페이지처럼 동적으로 구성되는 콘텐츠의 경우 다음과 같은 분기처리가 필요했습니다.
|
분기 로직이 복잡해지면 다크 테마 전용 컴포넌트를 따로 만들어야 했고, 코드와 디자인을 두 벌씩 관리해야 하는 비효율적인 구조였습니다.
개발자와 디자이너 모두 괴로운 상황이었습니다. 라이트테마에선 WhiteAlpha100
인 색상이 다크 테마에서는 Gray900
이 되어야 한다는 걸 별도로 관리해야하기 때문입니다. 이처럼 반복적인 색상 매핑은 당연히 필요 이상의 리소스를 쓰게 했고, 디자인 시스템의 일관성을 해치는 원인이 되었습니다.
퀸잇과 팔도감 디자인 시스템 통합
두 서비스(퀸잇과 팔도감)의 코드베이스를 통합하면서, 디자인 토큰명이 일치하지 않아 발생하는 충돌도 문제였습니다.
예를 들어, 공통 컴포넌트인 Button에서 퀸잇은 Lotus
색상을, 팔도감은 Green
색상을 사용하고 있어 시맨틱 토큰을 활용한 통합 과정이 필요했습니다.
피그마 구조 의존성으로 인한 취약점
기존의 색상 관리 방식은 특정 피그마 노드 ID, 도형 구조, 인스턴스 이름 등에 의존해 색상을 추출했기에, 피그마 파일 구조가 조금만 바뀌어도 색상 추출에 문제가 생겼고 관리가 번거롭다는 단점이 있었습니다.
|
이 문제를 해결하기 위해서는, 1. 컬러 토큰에 대한 시맨틱 구조를 구축하고, 2. 이를 코드와 디자인에서 동시에 관리할 수 있는 single source 체계가 필요했습니다.
이러한 갈증을 느끼던 중 Figma의 새로운 기능, ‘Variables’가 도입되었습니다. 이 기능은 다양한 테마나 여러 서비스 간의 컬러 시스템을 통합해 관리할 수 있게 해줍니다.
시맨틱 네이밍의 Variable에 색상값을 테마 별로 할당하고, 이를 전환할 수 있는 구조만 갖춘다면, 위의 문제를 해결할 수 있습니다. 나아가 글로벌한 다크 테마 전환까지 쉽게 지원하는 것도 기대할 수 있습니다.
2. Figma Variables와 CSS Variables
Variables는 상황(Mode)에 따라 동적으로 값을 바꿀 수 있는 변수이며, 기존의 정적인 Style
보다 훨씬 유연한 방식입니다.
하나의 변수가 모드에 따라 다른 값을 가질 수 있어서, ‘Primary’ 색상이 라이트테마에서는 밝은 보라색, 다크 테마에서는 어두운 보라색으로 전환됩니다. 또한 숫자, 문자열, 불리언 등 다양한 타입도 지원해 색상 외의 토큰도 시맨틱하게 관리할 수 있습니다.
이러한 Figma의 Variables는 웹 개발에서 사용하는 CSS Variables와 개념적으로 상당히 유사한 지점들이 있습니다. 여기서 저희는 Variables를 single source로 하여, 고스란히 프론트엔드 코드로 옮겨오기 위한 두가지 포인트를 찾을 수 있었습니다.
시맨틱 토큰 구조와 변수 참조
Figma Variables는 한 변수가 다른 변수 값을 참조하는 Alias 기능을 사용할 수 있습니다.
예를 들어 베이스 토큰 Lotus700
을 시맨틱 토큰인 Primary700
이 참조합니다. 그리고 이를 다시 TextAccentPrimary
라는 컴포넌트 토큰이 참조하여 최종적인 사용 맥락을 드러냅니다.
Lotus700
, WhiteAlpha100
과 같은 베이스 토큰만 존재하던 기존 시스템의 한계를 보완하기 위해, 디자인 플랫폼 팀에서는 Figma Variables를 기반으로 위와 같은 계층 구조의 시맨틱 토큰을 구축하는 과정을 거쳤습니다.
이를 통해 각 색상의 역할을 명확히 정의하고, 테마나 브랜드 확장 시 alias만 수정해 유연하게 대응할 수 있는 구조를 마련했습니다. 궁극적으로는 디자이너와 개발자 모두에게 명확한 언어와 기준을 제공하는 single source를 구축하고자 했던 것입니다.
이러한 참조 관계를 CSS에서는 어떻게 표현할 수 있을까요? 모두가 알다시피 CSS에는 사용자 지정 속성, 즉 CSS 변수가 존재합니다. --
로 시작하는 이름으로 정의하고, var()
함수로 값이나 다른 변수를 참조할 수 있습니다.
--primary500: var(--lotus500);
이러한 참조 메커니즘의 유사성 덕분에 디자인 시스템의 계층 구조와 관계를 코드에 그대로 옮길 수 있었습니다. 모든 토큰을 CSS 변수로 정의한 뒤, Variables내의 참조 관계를 그대로 할당하면 됩니다.
테마 전환
Variables에서는 하나의 변수에 모드 별 값을 할당할 수 있기에, 퀸잇과 팔도감 각각의 라이트/다크 테마를 위한 4개의 Mode를 정의했습니다.
CSS에서는 data-theme
속성으로 이 모드를 대응할 수 있습니다.
|
|
|
|
모드에 따라 달라지는 변수 값을 data-theme
속성에 맞춰 정의해두면, Variables를 활용할 준비는 끝입니다!
이제 다크 테마를 적용하고 싶은 영역을 감싸고 data-theme=’queenit-dark’
를 추가하면, 해당 영역 내의 변수 참조값이 자동으로 다크 테마에 맞게 전환됩니다. 이 로직을 앱의 루트 컴포넌트에 적용하면, 앱 전체에 다크 테마를 일괄 적용하는 것도 순식간에 가능해 집니다.
3. 자동화 파이프라인
다음으로는 디자이너의 작업을 코드에 그대로 반영할 수 있는 자동화 시스템이 필요했습니다.
여기서 필요한 처리를 정리해보면 다음과 같습니다.
Figma Variables 값을 가져온다.
React 컴포넌트에서 사용할 수 있는 객체 형태로 변환한다.
export하여 라이브러리로 배포한다.
먼저 Figma Variables를 가져오는 작업은 단순히 Figma REST API에서 제공하는 api를 사용하면 됩니다. 하지만 이 API 기능은 사내에서 사용 중인 Figma 플랜에서는 지원되지 않았습니다. 따라서 플랜을 업그레이드하지 않고도 Variables를 활용할 수 있는 대안을 찾아야 했습니다.
Tokens Studio for Figma 플러그인 활용
이때 활용한 것이 바로 오픈소스 플러그인인 Tokens Studio for Figma입니다.
이 플러그인은 Figma에 저장된 Variables를 JSON 형식으로 변환한 뒤, 지정된 Github 저장소에 직접 Push할 수 있는 기능을 제공합니다.
JSON으로 변환된 Variables는 다음과 같은 형태입니다:
{
"Damoa Colors/queenit-light": {
"Base": {
"Gray": {
"Gray100": {
"value": "#f0f2f6",
"type": "color"
},
"Gray200": {
"value": "#e7eaf0",
"type": "color"
},
// ...
"Damoa Colors/queenit-dark": {
"Base": {
"Gray": {
"Gray100": {
"value": "#222429",
"type": "color"
},
"Gray200": {
"value": "#26282e",
"type": "color"
},
// ...
디자이너가 Variables를 수정한 후 플러그인에서 Push 버튼을 누르면, queenit-ui
레포지토리에 자동으로 Pull Request가 생성되도록 설정했습니다. JSON 파일은 특정 경로에 저장되며, 이 파일이 변경되면 자동화 프로세스가 시작됩니다.
Style Dictionary를 활용한 데이터 처리
저장된 JSON 데이터를 원하는 형식으로 변환하기 위해 Style Dictionary
라는 라이브러리를 활용했습니다. Style Dictionary는 디자인 토큰을 다양한 플랫폼에 맞는 코드로 자동 변환해줍니다.
이를 활용해서 JSON 형태의 Variables를 두 가지 형식으로 변환했습니다.
1. CSS Variables
각 테마(queenit-light
, queenit-dark
, 8dogam-light
, 8dogam-dark
)에 대응하는 CSS Variables를 생성합니다.
const cssDataAttributesFormat: Format = {
name: "css/dataAttributes",
formatter: ({ dictionary, options }) => {
const themeOptions = options as ThemeOptions;
const selector = `[data-theme="${themeOptions.theme}"]`;
return `${selector} {\\n${dictionary.allProperties
.map((prop) => {
const key = prop.path[prop.path.length - 1];
const kebabKey = toKebabCase(key);
return ` --${kebabKey}: ${prop.value};`;
})
.join("\\n")}\\n}\\n`;
},
};
이 포맷터는 모든 Variable을 순회하며, 속성 이름을 kebab-case로 변환해 CSS 변수로 출력합니다. 예를 들어 ‘Base/Gray/Gray100’이라는 Variable은 마지막 depth의 네이밍을 따라 —-gray100
이라는 CSS Variable로 변환됩니다.
최종적으로 다음과 같은 파일이 완성되고, 프론트엔드 프로젝트의 global.css 와 같은 스타일 파일에 import하여 전역 변수로 지정할 수 있습니다.
[data-theme="queenit-light"] {
--gray100: #f0f2f6;
--gray200: #e7eaf0;
--gray300: #d6dae2;
--gray400: #b7bdc9;
--gray500: #a9b0bd;
/* ... */
2. TypeScript 객체
프론트엔드 코드에서 바로 import하여 사용할 수 있도록 CSS Variables를 참조하는 객체를 생성합니다.
const jsObjectFormat: Format = {
name: "javascript/object",
formatter: ({ dictionary }) => {
const obj: Record<string, string> = {};
dictionary.allProperties.forEach((prop) => {
const cssVarName = toKebabCase(prop.path[prop.path.length - 1]);
const safeKey = prop.name.replace(/[^\\w]/g, "");
obj[safeKey] = `var(--${cssVarName})`;
});
const objectEntries = Object.entries(obj)
.map(([key, value]) => ` '${key}': '${value}'`)
.join(",\\n");
return `export default {\\n${objectEntries}\\n};\\n\\n`;
},
};
위 포맷터를 통해, Variable을 key로 하고 참조하는 CSS 변수를 value로 하는 객체를 생성합니다. 결과적으로 프론트엔드에서 import하여 사용하는 값은 다음과 같은 모습입니다.
// DamoaColors.ts
export default {
'TextAccentPrimary': 'var(--text-accent-primary)',
// ...
};
<DamoaText color={DamoaColors.TextAccentPrimary}>{text}</DamoaText>
과정을 도식화해보면 다음과 같습니다.
이제 이 CSS 변수 파일과 객체를 프론트엔드 코드에 적용하기만 하면, 별도의 로직 없이도 테마 전환이 자유롭게 이뤄지는 시스템이 완성됩니다.
4. 결과 및 효과
Figma 플러그인과 Style Dictionary를 활용하여 간단히 마친 작업이었지만 컬러 시스템은 크게 개선되었습니다.
다크모드/라이트모드 전환의 간소화
이제 다크모드용 색상과 컴포넌트를 따로 만들고 관리할 필요 없이, 단순히 wrapper 컴포넌트에 data-theme
속성만 변경하면 됩니다.
return (
<ThemeWrapper themeType={element.theme !== 'WHITE' ? 'queenit-dark' : 'queenit-light'}>
<DamoaText color={DamoaColors.TextAccentPrimary}>{text}</DamoaText>
</ThemeWrapper>
)
기존에 삼항 연산자로 isDarkMode
에 따라 분기하던 것을 생각하면, 말그대로 “딸깍”입니다. data-theme
을 포함한 단 한줄의 코드로 테마 대응이 완료되었습니다.
퀸잇, 팔도감 디자인 시스템의 통합 관리
이제 퀸잇과 팔도감처럼 서로 다른 브랜드의 디자인 시스템을, 하나의 통합된 시스템에서 관리할 수 있게 되었습니다. 이전에는 각 브랜드 별 별도의 컬러 시스템을 만들어 따로 운영해야 했지만, 이제는 하나의 Variables 시스템 안에서 모든 브랜드와 테마를 관리할 수 있게 되었습니다.
앞으로 새로운 브랜드나 서비스가 추가되더라도, 동일한 코드베이스에서 공통 컴포넌트를 그대로 사용할 수 있기 때문에 더 빠르고 일관된 개발이 가능해졌습니다.
single source 기반의 토큰 관리로 디자인-개발 워크플로우 개선
기존에는 Figma Style로 지정되는 색상 값을 코드로 활용하려면, 특정 페이지에 팔레트 형태로 반영해 놓고 거기서 값을 추출해야 했습니다.
하지만 이제는 그럴 필요 없이, Variables라는 단일 출처에서 모든 값을 관리할 수 있게 되면서, 작업 절차도 훨씬 단순해지고 운영의 신뢰성도 크게 향상되었습니다.
Figma와 개발환경에서 컬러 시스템의 간극은 이제 0에 가깝습니다.
5. 마치며
이번 작업은 queenit-ui
의 자동화 목표를 한층 더 강화하는 중요한 전환점이었습니다.
이 라이브러리는 애셋과 디자인 시스템 변경을 자동화해, 개발자와 디자이너가 별도의 커뮤니케이션 없이도 변경 사항을 실시간으로 반영하고 사용할 수 있도록 돕는 것을 목표로 합니다. 즉, 사람이 직접 확인하거나 소통해야 하는 과정을 자동화된 시스템으로 최대한 줄이는 것이 핵심입니다.
특히, variable 자동화를 통해 디자이너와 개발자의 색상 관리 방식을 일치시킴으로써, 테마 또는 브랜드 간 분기를 디자인 시스템과 프론트엔드 코드 수준에서 자연스럽게 해결할 수 있게 된 점이 가장 큰 성과였습니다. 이를 통해 다크테마 대응, 브랜드 통합, 전역 테마 전환 등의 작업이 훨씬 단순해졌고, 디자인과 코드 간의 싱크를 맞추기 위한 커뮤니케이션 비용도 크게 줄어들었습니다.
컬러 시스템 개선 이후에도 queenit-ui
는 계속해서 발전하고 있습니다.
예를 들어, 자동화 과정에서 오류가 발생하면 원인을 슬랙 메시지에 포함해 전송하도록 개선하여, 디자이너가 직접 문제를 파악하고 최소한의 커뮤니케이션으로 해결할 수 있도록 보완했습니다. 이외에도 배포 속도 개선, 사용되지 않는 애셋 탐지 등 지속적인 개선 작업을 이어가고 있습니다.
이 프로젝트는 인턴으로 근무하던 시기에 주도적으로 진행했던 작업입니다. 오너십을 갖고 조직의 생산성에 실질적인 변화를 만들어낼 수 있었던 경험은 의미 있는 기억으로 남았습니다. 라포랩스의 정식 팀원으로 합류한 지금도, 더 나은 코드와 더 나은 시스템을 위해 여전히 치열하게 고민하고 있습니다.
개발자의 중요한 역할 중 하나는 팀원들이 더 본질적인 문제에 집중하고, 더 가치 있는 일에 몰입할 수 있는 환경을 만들어주는 일입니다. 단순히 기능을 구현하는 데에 그치지 않고, 조직 전체의 흐름을 개선하는 방향으로 사고하고 행동하는 것. 이번 경험을 통해 그 점을 다시금 깊이 실감할 수 있었습니다.
라포랩스의 Frontend Chapter는 앞으로도 복잡한 문제를 단순하고 명확하게 풀어내는 접근을 끊임없이 접근하며, 제품과 조직 모두에 지속 가능한 임팩트를 만들어갈 예정입니다.