PoC 개요 및 목표

디자인 변경에 빠르고 일관되게 반응하는 개발 프로세스를 구축하고, 그 효율성을 검증합니다.

핵심 도구

🎨

Figma

UI 디자인과 컴포넌트 '규칙'을 정의하는 디자인 툴

💎

Token Studio

디자인 규칙을 '토큰' 데이터로 추출하는 플러그인

📚

Style Dictionary

토큰을 각 플랫폼별 코드로 자동 변환하는 도구

핵심 개념 이해

디자인 툴 Figma의 개념이 개발 코드와 어떻게 1:1로 연결되는지 알아봅니다.

Figma와 코드의 연결고리

아래 표의 행을 클릭하면 오른쪽 코드 예제에서 해당 개념이 어떻게 구현되었는지 하이라이트됩니다.

Figma 용어 설명
Component 재사용 가능한 UI 단위
Variants 속성들의 모든 조합 (예: 파란색의 큰 버튼)
Property 컴포넌트의 옵션 (예: `size`, `style`)
Value 옵션의 구체적인 값 (예: `large`, `primary`)
Instance 실제 화면에 배치된 컴포넌트
피그마 Variants 설명 이미지
<!-- 파일명: Button.vue -->
<script setup>
import { computed } from 'vue';

const props = defineProps({
  style: { type: String, default: 'primary' },
  size: { type: String, default: 'medium' },
});

const buttonClasses = computed(() => [
  'button',
  `style-${props.style}`,
  `size-${props.size}`,
]);
</script>

<template>
  <!--Instance 예시-->
  <Button size="large">버튼</Button>
</template>

PoC 개발 워크플로우

디자인부터 코드까지, 4단계로 진행되는 자동화된 개발 과정을 알아봅니다.

1단계: 디자인 토큰 추출 (Figma → JSON)

디자인의 색상, 폰트, 간격 등의 규칙을 데이터(JSON)로 추출합니다. 이 파일이 모든 디자인의 기준이 됩니다.

2단계: 토큰 변환 (JSON → 플랫폼별 코드)

1단계에서 추출한 JSON 파일을 Style Dictionary를 통해 각 플랫폼(웹, 안드로이드)이 이해할 수 있는 코드로 자동 변환합니다.

3단계: 컴포넌트 개발 (토큰 적용)

자동 변환된 테마 코드(CSS 변수, Kotlin 객체)를 사용하여 **가장 작은 단위의 UI 컴포넌트(버튼, 아이콘 등)**를 개발합니다.

4단계: 페이지(View) 개발 (컴포넌트 조립)

3단계에서 만든 기본 컴포넌트들을 조립하여 **더 큰 단위의 복합 컴포넌트 또는 페이지**를 구성합니다. 자세히 보기 →

4. 페이지(View) 개발

이전 단계에서 만든 기본 컴포넌트(Button 등)를 조립하여 더 의미 있는 UI 단위를 만드는 과정입니다.

Vue.js: 프로필 카드 페이지

`Button.vue` 컴포넌트를 가져와(import) 프로필 카드라는 더 큰 컴포넌트 안에서 사용합니다. 이것이 바로 '컴포넌트 조립'입니다.

<!-- 파일명: ProfilePage.vue -->
<script setup>
// 3단계에서 만든 Button 컴포넌트를 가져옵니다.
import Button from './Button.vue';
</script>

<template>
  <div class="profile-card">
    <img src="https://placehold.co/100x100" alt="프로필 이미지" />
    <h3>Vue.js 개발자</h3>
    <p>디자인 시스템 기반의 웹 UI를 구축합니다.</p>
    <!-- 조립된 Button 컴포넌트 -->
    <Button style="primary" size="medium">
      연락하기
    </Button>
  </div>
</template>

<style scoped>
.profile-card {
  /* ... 카드 스타일 ... */
}
</style>

Jetpack Compose: 프로필 카드

`MyButton` Composable 함수를 `ProfileCard` 라는 더 큰 Composable 함수 내에서 호출하여 사용합니다.

// 파일명: ProfileScreen.kt
@Composable
fun ProfileCard() {
    Card(modifier = Modifier.padding(16.dp)) {
        Column(
            horizontalAlignment = Alignment.CenterHorizontally,
            modifier = Modifier.padding(16.dp)
        ) {
            Image(
                painter = rememberImagePainter("https://placehold.co/100x100"),
                contentDescription = "프로필 이미지"
            )
            Text("Jetpack Compose 개발자", style = MaterialTheme.typography.h6)
            Text("디자인 시스템 기반의 네이티브 UI를 구축합니다.")
            
            // 조립된 MyButton Composable
            MyButton(
                text = "연락하기",
                onClick = { /* ... */ },
                style = ButtonStyle.PRIMARY,
                size = ButtonSize.MEDIUM
            )
        }
    }
}

5. 심화: 멀티브랜드 테마 적용하기

하나의 컴포넌트 코드로 여러 브랜드의 디자인을 어떻게 지원할 수 있는지 알아봅니다.

핵심 전략: 컴포넌트는 그대로, 테마(토큰)만 교체

컴포넌트 코드를 수정하지 않고, 외부에서 주입되는 테마를 바꾸는 것만으로 완전히 다른 디자인을 적용할 수 있습니다.

라이브 데모: 테마 스위칭

현재 테마: Brand A

적용 로직 살펴보기

Vue.js

최상위 컴포넌트에 동적으로 클래스를 적용합니다.

<!-- App.vue -->
<script setup>
import { ref } from 'vue';
const theme = ref('theme-a');
</script>
<template>
  <div :class="theme">
    <!-- ... 앱 컨텐츠 ... -->
    <Button>브랜드 버튼</Button>
  </div>
</template>
                            
/* themes.css */
.theme-a {
  --color-primary: #0d6efd;
}
.theme-b {
  --color-primary: #d63384;
}

/* Button.vue style */
.button {
  background-color: var(--color-primary);
}
                            
Jetpack Compose

UI를 브랜드별 테마 Composable로 감싸줍니다.

// Theme.kt
private val BrandAColorScheme = lightColorScheme(
    primary = Color(0xFF0D6EFD)
)
private val BrandBColorScheme = lightColorScheme(
    primary = Color(0xFFD63384)
)

@Composable
fun BrandATheme(content: @Composable () -> Unit) {
    MaterialTheme(
        colorScheme = BrandAColorScheme,
        content = content
    )
}
@Composable
fun BrandBTheme(content: @Composable () -> Unit) {
    MaterialTheme(
        colorScheme = BrandBColorScheme,
        content = content
    )
}
                            
// MyScreen.kt
@Composable
fun MyScreen() {
    BrandATheme { // 또는 BrandBTheme
        MyButton(text = "브랜드 버튼")
    }
}