Skip to content

Conversation

@ongsim0629
Copy link

@ongsim0629 ongsim0629 commented Nov 23, 2025

Chapter3-1. UI 컴포넌트 모듈화와 디자인 시스템

과제 목표

레거시 코드베이스를 현대적인 디자인 시스템으로 개편하는 실무 경험

  1. 정리되지 않은 레거시 코드의 문제점 식별 및 분석
  2. TailwindCSS, shadcn/ui, CVA 등의 현대 도구 활용
  3. 일관된 디자인 토큰과 컴포넌트 API 구축
  4. UI와 비즈니스 로직이 적절한 분리된 리팩토링

Before 패키지 분석 후 After 패키지 개편

개편 목표

디자인 시스템

  • TailwindCSS 기반 일관된 디자인 토큰 정의
  • 하드코딩 제거, 재사용 가능한 스타일 시스템 구축
  • dark mode, 반응형 등 확장 가능한 구조

컴포넌트 아키텍처

  • UI 컴포넌트는 순수하게 UI만 담당
  • 도메인 로직은 적절히 분리
  • 일관된 컴포넌트 API 설계

사용할 도구

TailwindCSS 4.x

  • 디자인 토큰 기반 스타일링
  • 유틸리티 클래스 활용
  • dark mode, 반응형 내장 지원

shadcn/ui

  • Radix UI 기반, 접근성 내장
  • 복사 가능한 컴포넌트 (라이브러리가 아닌 소스코드)
  • 자유로운 커스터마이징

CVA (Class Variance Authority)

  • 선언적 variants 패턴
  • 타입 안전한 스타일 조합
  • 조건부 스타일링 처리

React Hook Form + Zod

  • 선언적 폼 검증
  • 타입 안전한 스키마
  • 최소 리렌더링 최적화

필수 과제

1. 디자인 시스템 구축

  • TailwindCSS 설정 및 디자인 토큰 정의
  • shadcn/ui 컴포넌트 설치 (Button, Input, Select, Card, Table 등)
  • [ x] CVA를 활용한 variants 패턴 적용
  • 일관된 스타일 시스템 구축

2. Before 패키지 분석

  • Before 패키지 실행 및 전체 코드 탐색
  • 스타일링, 컴포넌트 설계, 폼 관리 측면에서 문제점 파악
  • 개선이 필요한 부분과 그 이유 정리

3. 컴포넌트 개편

  • UI와 비즈니스 로직 분리
  • 순수한 UI 컴포넌트로 재구성
  • 일관된 컴포넌트 API 설계
  • 적절한 컴포넌트 구조 설계

심화 과제

  • Dark Mode 완전 지원 (CSS Variables + Tailwind)
  • Design Token 시스템 고도화 (색상 팔레트, 타이포그래피 스케일)
  • 뷰와 비즈니스로직이 분리되도록

과제 회고

과제를 진행하면서 느낀 점, 배운 점을 자유롭게 작성해주세요.

Before 패키지에서 발견한 문제점

1. 스타일링 측면

  • 하드코딩된 색상 값: #1976d2, #e3f2fd, #d32f2f 등 색상이 코드 전체에 흩어져 있어 일관성 유지가 어려움
  • 인라인 스타일 남발: 628줄의 ManagementPage에 수십 개의 style={{}} 객체가 중복되어 유지보수성이 극도로 낮음
  • 디자인 토큰 부재: 동일한 "파란색"이 #1976d2, #2196f3 등 여러 값으로 표현되어 일관성 없음
  • 반응형/Dark Mode 불가능: 인라인 스타일과 하드코딩으로 인해 확장이 불가능한 구조

2. 컴포넌트 설계 측면

  • Props API 불일치: FormInput은 helpText, FormSelect는 help, FormTextarea는 description 사용
  • Atomic Design 오용: atoms/molecules/organisms 폴더 구조가 오히려 개발 속도를 저하시킴
    • "이 컴포넌트가 atom인가 molecule인가?" 고민하는 시간이 생김
    • import 경로가 복잡해짐: ../../../components/atoms/Button
  • UI와 비즈니스 로직 혼재: ManagementPage에 데이터 fetching, 상태 관리, UI 렌더링이 모두 섞여있음

3. 접근성 및 코드 품질

  • 접근성 부족: ARIA 라벨 불완전, 키보드 네비게이션 아쉽
  • 타입 안전성 부족: any 가 이곳저곳 있음

개편 과정에서 집중한 부분

1. 디자인 토큰 시스템 구축

/* Before: 하드코딩 */
background: '#1976d2'
color: '#e3f2fd'

/* After: 토큰화 */
--primary: 25 118 210;  /* #1976d2 */
--info-light: 227 242 253;  /* #e3f2fd */
bg-[rgb(var(--primary))]
  • RGB 형식으로 투명도 조절 가능 (rgb(var(--primary) / 0.5))
  • 확장 가능한 구조 (새로운 색상 추가 시 한 곳만 수정)

2. CVA 기반 컴포넌트 Variants 패턴

// Before: 조건문 지옥
const getButtonStyle = (variant: string) => {
  if (variant === 'primary') return { backgroundColor: '#007bff', ... };
  if (variant === 'secondary') return { backgroundColor: '#6c757d', ... };
}

// After: 선언적 패턴
const buttonVariants = cva(
  "base-classes",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground",
        secondary: "bg-secondary text-secondary-foreground"
      }
    }
  }
)

3. Before UI를 시각적으로 100% 유지하면서 내부만 현대화

4. 폴더 구조 단순화

Before: components/atoms/molecules/organisms (복잡)
After: components/ui (단순, shadcn/ui 철학)

사용한 기술 스택 경험

TailwindCSS 4.x

  • 장점 체감:
    • @import "tailwindcss" 한 줄로 설정 완료 (PostCSS 설정 불필요)
    • className="grid grid-cols-2 gap-4" 같은 직관적인 레이아웃
    • 인라인 스타일 대비 40% 정도 코드량 감소
  • 어려웠던 점:
    • 처음에 [&:has([role=checkbox])] 같은 복잡한 selector 이해가 어려웠음
    • RGB 변수 형식 rgb(var(--primary)) 문법에 적응 필요

shadcn/ui의 Copy-Paste 철학

  • npm install 없이 코드 복사 → 완전한 커스터마이징 가능
  • 버전 업데이트 걱정 없음
  • 프로젝트에 맞게 자유롭게 수정

CVA (Class Variance Authority)

  • 학습 곡선:
    • 처음엔 "왜 이렇게 복잡하게?" 생각했으나
    • 조건부 스타일링 3개만 넘어가면 가독성이 압도적으로 좋아짐
  • 타입 안전성:
    • variant="primaryy" 오타 시 컴파일 에러 발생
    • IntelliSense로 사용 가능한 variant 자동완성

Storybook 8.4.7

  • 개발 경험 향상:
    • 컴포넌트를 독립적으로 개발/테스트 가능
    • args로 모든 props 조합을 쉽게 확인
  • 협업 도구로서의 가치:

어려웠던 점과 해결 방법

문제: inline style이 너무 많아서 리팩토링 중 UI가 깨짐

  • 시행착오:
    • 처음에 한꺼번에 모든 inline style 제거 → 레이아웃 완전 붕괴
    • Git으로 되돌리고 처음부터 다시 시작
  • 해결:
    • 단계별 접근: 컨테이너 → 탭 → 카드 → Alert → 모달 순서로 점진적 변경
    • 각 단계마다 브라우저에서 확인하며 진행
    • PowerShell 정규식으로 반복되는 패턴 일괄 변환:
      (Get-Content file.tsx) -replace 
        "style=\{\{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px' \}\}", 
        'className="grid grid-cols-2 gap-4"'

리뷰받고 싶거나 질문하고 싶은 내용

디자인 토큰 네이밍 컨벤션

  • 현재: --primary, --success-light, --info-border 사용
  • 질문: Semantic 토큰 vs Component 토큰 네이밍 기준이 궁금합니다
    • --button-primary-bg vs --primary 중 어떤 방식이 확장성이 좋을까요?
    • Figma에서 토큰 추출 시 어떤 구조로 가져오는 것이 더 나을까요?

** Before UI 유지 vs 디자인 개선**

  • 이번 과제에서는 Before UI를 100% 유지했는데
  • 실무에서 "점진적 도입" 시 디자인 개선은 어느 단계에서 하는 게 좋을까요?
    • 1단계: 토큰화만 (이번 과제)
    • 2단계: 컴포넌트 구조 개선
    • 3단계: 디자인 개선?

** 폴더 구조 관련**

  • components/ui로 단순화했는데, 프로젝트가 커지면 어떻게 확장하나요?
    • components/ui/form/ 같은 sub-category?
    • features/ 폴더로 도메인별 분리?
    • Monorepo로 @company/design-system 패키지 분리?

📚 디자인 시스템 정리

디자인 시스템이란?

디자인 시스템의 3가지 핵심 요소

1. Design Tokens (디자인 토큰)
   └─ 색상, 간격, 타이포그래피 등의 원자적 값
   └─ 예: --primary-500, --spacing-4, --font-size-sm

2. Components (컴포넌트)
   └─ 토큰을 조합한 재사용 가능한 UI 요소
   └─ 예: Button, Input, Card

3. Patterns (패턴 & 가이드라인)
   └─ 컴포넌트를 조합하는 방법과 사용 규칙
   └─ 예: Form validation pattern, Modal 사용 시점

디자인 토큰 (Design Tokens)

개념

  • 디자인의 "변수"라고 생각하면 됨
  • 하드코딩(#1976d2) → 변수(--primary)로 추상화

토큰의 계층 구조

/* 1. Primitive Tokens (원시 토큰) - 가장 기본 */
--blue-500: 25 118 210;     /* #1976d2 */
--blue-100: 227 242 253;    /* #e3f2fd */

/* 2. Semantic Tokens (의미론적 토큰) - 용도 부여 */
--color-primary: var(--blue-500);
--color-info-bg: var(--blue-100);

/* 3. Component Tokens (컴포넌트 토큰) - 특정 컴포넌트용 */
--button-primary-bg: var(--color-primary);
--alert-info-bg: var(--color-info-bg);

이번 과제에서 배운 점

  • Light/Dark 테마를 같은 변수명으로 관리:
    :root { --primary: 25 118 210; }           /* 밝은 파란색 */
    .dark { --primary: 59 130 246; }           /* 조금 더 밝은 파란색 */

CVA (Class Variance Authority)

왜 필요한가?

// Before: 극한의 조건문
function Button({ variant, size }) {
  let className = "base-button";
  if (variant === "primary") className += " bg-blue-500 text-white";
  if (variant === "secondary") className += " bg-gray-200 text-gray-900";
  if (size === "sm") className += " px-3 py-1 text-sm";
  if (size === "lg") className += " px-6 py-3 text-lg";
  return <button className={className} />;
}

// After: 선언적 패턴
const buttonVariants = cva("base-button", {
  variants: {
    variant: {
      primary: "bg-blue-500 text-white",
      secondary: "bg-gray-200 text-gray-900"
    },
    size: {
      sm: "px-3 py-1 text-sm",
      lg: "px-6 py-3 text-lg"
    }
  }
});

핵심 장점

  1. 타입 안전성: variant="primaryy" 오타 시 TypeScript 에러
  2. 가독성: variant 조합을 한눈에 파악 가능
  3. 복합 variants: compoundVariants로 조합 스타일 정의
    compoundVariants: [
      {
        variant: "primary",
        size: "lg",
        className: "font-bold"  // primary + lg 조합일 때만
      }
    ]

shadcn/ui의 철학: "Not a Component Library"

기존 UI 라이브러리의 문제

npm install @mui/material  # 15MB
npm install antd           # 12MB
  • 번들 크기 증가
  • 커스터마이징 제한
  • 버전 업데이트 시 Breaking Change 위험
  • "라이브러리 문서대로 해야 함" → 자유도 낮음

shadcn/ui의 접근

npx shadcn@latest add button
# → components/ui/button.tsx 파일이 복사됨
# → 이제 이 파일은 "내 코드"

언제 사용하면 좋은가?

  • 디자인 시스템을 커스터마이징하고 싶을 때
  • 번들 크기를 최소화하고 싶을 때
  • 컴포넌트 동작 원리를 학습하고 싶을 때
  • "빠르게 프로토타입만 만들면 됨" → MUI, Ant Design이 더 빠름

Atomic Design Pattern

이론 vs 현실

이론적 계층 구조

Atoms (원자)
└─ 더 이상 쪼갤 수 없는 기본 요소
└─ 예: Button, Input, Label

Molecules (분자)
└─ Atoms의 조합
└─ 예: FormInput (Label + Input + Error Message)

Organisms (유기체)
└─ Molecules/Atoms의 복잡한 조합
└─ 예: Header, Card, Table

Templates (템플릿)
└─ Organisms의 레이아웃
└─ 예: DashboardLayout

Pages (페이지)
└─ 실제 데이터가 들어간 Template
└─ 예: UserDashboardPage

실무에서의 문제점

📁 components/
  📁 atoms/
    📄 Button.tsx        // ← 이게 atom 맞나?
    📄 Icon.tsx          // ← 이건?
  📁 molecules/
    📄 SearchBar.tsx     // ← atom인지 molecule인지 애매
    📄 FormInput.tsx     // ← Input만 있으면 atom 아닌가?
  📁 organisms/
    📄 Card.tsx          // ← 내용에 따라 molecule일 수도

시간 낭비 발생

  • 이 컴포넌트가 atom인가 molecule인가?
  • 폴더를 옮길 때마다 import 경로 수정이 빡세다ㅏ
  • 팀원마다 분류 기준이 달라서 혼란이 발생할 수도 있다

shadcn/ui의 대안: 단순 구조

📁 components/
  📁 ui/              // ← 그냥 다 여기
    📄 button.tsx
    📄 input.tsx
    📄 card.tsx
    📄 form-input.tsx

배운 교훈

  • Atomic Design은 개념으로는 유용 (컴포넌트 조합 사고방식)
  • 하지만 폴더 구조로 강제하면 오히려 비효율
  • 중요한 건 이름이 아니라 재사용성과 책임 분리

디자인 시스템 구축 단계

1단계: 토큰화 (이번 과제)

/* 하드코딩 제거 */
background: #1976d2;  →  background: rgb(var(--primary));

2단계: 컴포넌트 재사용성

// 중복 코드 제거
<button style={{...}}>제출</button>
<button style={{...}}>취소</button>
        
<Button variant="primary">제출</Button>
<Button variant="secondary">취소</Button>

3단계: 문서화 (Storybook)

// 개발자/디자이너가 컴포넌트 사용법을 한눈에
export const Primary: Story = {
  args: { variant: 'primary', children: 'Button' }
};

4단계: 자동화

  • Figma Tokens Plugin → CSS Variables 자동 생성
  • Chromatic으로 비주얼 리그레션 테스트
  • Design to Code (Figma → React 컴포넌트)

실무 적용 순서

  1. 기존 프로젝트에 토큰만 적용 (가장 안전)
  2. 새로운 기능부터 디자인 시스템 컴포넌트 사용
  3. 점진적으로 레거시 코드 마이그레이션
  4. Storybook 문서화 (협업 효율 향상)

생각해본 디자인 시스템의 장점

Q1: "디자이너가 Figma에서 새 색상 추가했어요"

/* globals.css에 한 줄만 추가 */
--color-brand-purple: 147 51 234;  /* #9333ea */

→ 모든 컴포넌트에서 즉시 사용 가능

Q2: "이 버튼만 특별하게 만들어야 해요"

// 나쁜 방법: 인라인 스타일로 덮어쓰기
<Button style={{ background: 'red' }}>

// 좋은 방법 1: variant 추가
<Button variant="danger">

// 좋은 방법 2: 정말 특수한 경우라면 className
<Button className="bg-gradient-to-r from-purple-500 to-pink-500">

Q3: "다크모드 지원해야 하는데요?"

/* 이미 토큰화 되어있다면 */
:root { --background: 255 255 255; }
.dark { --background: 15 23 42; }

→ 추가 코드 수정 최소화

@ongsim0629 ongsim0629 changed the title [7팀 신수빈] [7팀 신수빈] Chapter3-1. UI 컴포넌트 모듈화와 디자인 시스템 Nov 23, 2025
@ongsim0629
Copy link
Author

Before 패키지에서 발견한 문제점

  1. 스타일링 측면

하드코딩된 색상 값: #1976d2, #e3f2fd, #d32f2f 등 색상이 코드 전체에 흩어져 있어 일관성 유지가 어려움
인라인 스타일 남발: 628줄의 ManagementPage에 수십 개의 style={{}} 객체가 중복되어 유지보수성이 극도로 낮음
디자인 토큰 부재: 동일한 "파란색"이 #1976d2, #2196f3 등 여러 값으로 표현되어 일관성 없음
반응형/Dark Mode 불가능: 인라인 스타일과 하드코딩으로 인해 확장이 불가능한 구조
2. 컴포넌트 설계 측면

Props API 불일치: FormInput은 helpText, FormSelect는 help, FormTextarea는 description 사용
Atomic Design 오용: atoms/molecules/organisms 폴더 구조가 오히려 개발 속도를 저하시킴
"이 컴포넌트가 atom인가 molecule인가?" 고민하는 시간이 생김
import 경로가 복잡해짐: ../../../components/atoms/Button
UI와 비즈니스 로직 혼재: ManagementPage에 데이터 fetching, 상태 관리, UI 렌더링이 모두 섞여있음
3. 접근성 및 코드 품질

접근성 부족: ARIA 라벨 불완전, 키보드 네비게이션 아쉽
타입 안전성 부족: any 가 이곳저곳 있음

이렇게 생각했는데 또 다른 문제점이 있을까여????

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant