Guide / 16 min

소프트웨어 엔지니어를 위한 디자인 원칙

소프트웨어 엔지니어를 위한 디자인 원칙: 완벽 가이드

2026년 2월 9일 업데이트

2026년 2월 업데이트: 두 개의 새로운 섹션을 추가했습니다—인터랙션 패턴(Framer, Flighty, Halide, Warp, Bear, Craft, Superhuman을 분석하여 정리한 8가지 패러다임)과 AI 인터페이스 패턴(Perplexity에서 도출한 인용 중심 디자인, 스트리밍 단계, 오류 투명성). 웹 패턴을 2026년 기준으로 업데이트하여 앵커 포지셔닝, 스크롤 기반 애니메이션, @starting-style을 반영했습니다. 접근성 항목은 WCAG 2.2의 ISO 표준화를 반영하여 갱신했습니다. 16개 우수 제품에 대한 심층 분석은 디자인 스터디를 참고하세요.

저는 소프트웨어를 만들면서 수년간 디자인을 연구해 왔습니다. Dieter Rams 같은 거장들의 원칙을 흡수하고, Linear, Stripe, Raycast 같은 제품의 인터페이스를 분석했습니다. 이 가이드는 제가 소프트웨어의 외관과 사용감에 관심을 갖기 시작했을 때 있었으면 했던 종합 레퍼런스로, 그간의 이해를 응축한 결과물입니다.

디자인은 장식이 아닙니다. 커뮤니케이션입니다. 모든 픽셀은 기능, 계층 구조, 의미를 전달합니다. 아마추어처럼 느껴지는 소프트웨어와 프로페셔널하게 느껴지는 소프트웨어의 차이는 이러한 원칙을 이해하고 일관되게 적용하는 데 있습니다.

이 가이드는 이미 코드를 작성할 수 있는 분을 대상으로 합니다. 여기서 배우는 것은 ‘보는 눈’입니다—어떤 인터페이스는 왜 자연스럽게 느껴지고 다른 인터페이스는 왜 혼란스럽게 느껴지는지 이해하는 것, 그리고 더 중요하게는 전자를 직접 만드는 방법을 익히는 것입니다.


목차

Part 1: 기초

  1. Gestalt 심리학
  2. 타이포그래피
  3. 색채 이론
  4. 시각적 위계
  5. 여백과 리듬
  6. 애니메이션 원칙

Part 2: 인터랙션 & AI

  1. 인터랙션 패턴
  2. AI 인터페이스 패턴

Part 3: 디자인 철학

  1. Dieter Rams: 10가지 원칙

Part 4: 구현

  1. 2026 웹 패턴
  2. 디자인 토큰 시스템
  3. 다크 모드 제대로 구현하기
  4. Figma 추출 워크플로

Part 5: 참고자료

  1. 빠른 참조 테이블
  2. 디자인 체크리스트
  3. 디자인 스터디

Gestalt 심리학

“전체는 부분의 합과는 다른 무언가이다.” — Kurt Koffka

Gestalt 심리학은 1920년대 독일에서 발전한 이론으로, 인간이 시각 정보를 어떻게 인식하는지 설명해요. 뇌는 개별 픽셀을 보지 않고, 요소들을 의미 있는 패턴으로 조직해요. 이 원칙들을 익히면 사용자가 인터페이스를 어떻게 인식할지 제어할 수 있어요.

근접성

가까이 있는 요소들은 하나의 그룹으로 인식돼요.

UI 디자인에서 가장 강력한 Gestalt 원칙이에요. 여백은 다른 어떤 시각적 속성보다 관계를 더 효과적으로 전달해요.

잘못된 예 (동일한 간격 = 그룹핑 없음):
┌─────────────────┐
│ Label           │
│                 │
│ Input Field     │
│                 │
│ Label           │
│                 │
│ Input Field     │
└─────────────────┘

올바른 예 (불균등한 간격 = 명확한 그룹):
┌─────────────────┐
│ Label           │
│ Input Field     │ ← 좁은 간격(4px) - 관련 요소
│                 │
│                 │ ← 넓은 간격(24px) - 그룹 분리
│ Label           │
│ Input Field     │ ← 좁은 간격(4px) - 관련 요소
└─────────────────┘

CSS 구현:

.form-group {
  margin-bottom: 24px;  /* Between groups: wide */
}

.form-group label {
  margin-bottom: 4px;   /* Label to input: tight */
  display: block;
}

SwiftUI 구현:

VStack(alignment: .leading, spacing: 4) {  // Tight within group
    Text("Email")
        .font(.caption)
        .foregroundStyle(.secondary)
    TextField("[email protected]", text: $email)
        .textFieldStyle(.roundedBorder)
}
.padding(.bottom, 24)  // Wide between groups

유사성

시각적 특성을 공유하는 요소들은 관련된 것으로 보여요.

요소가 같은 모습이면 사용자는 같은 기능을 한다고 추정해요. 디자인 시스템에서 일관된 버튼 스타일, 카드 처리, 타이포그래피를 사용하는 이유가 바로 이것이에요.

네비게이션 예시:
┌───────────────────────────────────┐
 [Dashboard] [Projects] [Settings]    같은 스타일 = 같은 기능
                                   
 ┌─────┐  ┌─────┐  ┌─────┐        
 Card   Card   Card             같은 스타일 = 같은 콘텐츠 유형
 └─────┘  └─────┘  └─────┘        
                                   
 [+ New Project]                      다른 스타일 = 다른 기능
└───────────────────────────────────┘

전경-배경 관계

콘텐츠는 배경과 명확히 구분되어야 해요.

뇌는 “전경”(집중할 대상)과 “배경”(뒷면)을 구별해야 해요. 전경-배경 관계가 불분명하면 시각적 혼란이 생겨요.

기법: - 대비 (밝은 전경 위에 어두운 배경, 또는 그 반대) - 그림자 (전경을 배경 위로 들어올림) - 테두리 (전경의 경계를 구분) - 블러 (배경을 흐리게, 전경을 선명하게)

/* Strong figure-ground relationship */
.card {
  background: var(--color-surface);     /* Figure */
  border-radius: 12px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);  /* Elevation */
}

.modal-overlay {
  background: rgba(0, 0, 0, 0.5);  /* Dim ground */
  backdrop-filter: blur(4px);      /* Blur ground */
}

공통 영역

경계 안에 있는 요소들은 그룹으로 인식돼요.

시각적 컨테이너(카드, 박스, 테두리가 있는 영역) 안에 요소를 넣으면 서로 관련이 있다는 신호를 줘요.

연속성

눈은 경로, 선, 곡선을 따라가요.

정렬과 시각적 흐름을 활용하면 인터페이스 전체에 걸쳐 시선을 유도할 수 있어요.

정렬의 연속성:
┌────────────────────────────────┐
 Logo    [Nav]  [Nav]  [Nav]      수평축으로 정렬
├────────────────────────────────┤
                                
 Headline                       
 ─────────────────────────────     시선이 왼쪽 끝을 따라감
 Paragraph text continues       
 along the same left edge       
                                
 [Primary Action]                  여전히 왼쪽 끝에 위치
└────────────────────────────────┘

폐합

뇌는 불완전한 형태를 완성해요.

사용자는 모든 픽셀이 그려져 있을 필요가 없어요—익숙한 형태는 머릿속에서 자동으로 완성해요. 이를 활용하면 더 미니멀하고 우아한 디자인이 가능해요.

/* Horizontal scroll with partial card (closure) */
.card-carousel {
  display: flex;
  gap: 16px;
  overflow-x: auto;
  padding-right: 48px;  /* Show partial card = scroll hint */
}

.card-carousel .card {
  flex: 0 0 280px;  /* Fixed width, partial visible */
}

Gestalt 빠른 참조

원칙 규칙 주요 활용
근접성 관련 = 가깝게, 비관련 = 멀게 폼 필드, 콘텐츠 섹션
유사성 같은 외관 = 같은 기능 버튼, 카드, 네비게이션
전경-배경 레이어의 명확한 분리 카드, 모달, 오버레이
공통 영역 경계가 콘텐츠를 묶음 설정 섹션, 사용자 카드
연속성 선과 정렬을 따라감 타임라인, 읽기 흐름
폐합 뇌가 형태를 완성함 아이콘, 스크롤 힌트, 스켈레톤

타이포그래피

“타이포그래피는 인간의 언어에 영속적인 시각적 형태를 부여하는 기술이다.” — Robert Bringhurst

타이포그래피는 인터페이스 디자인의 기반이에요. 텍스트는 기능, 위계, 브랜드를 전달해요. 부실한 타이포그래피는 인터페이스를 사용하기 어렵게 만들고, 훌륭한 타이포그래피는 눈에 띄지 않아요—그저 자연스럽게 작동할 뿐이에요.

타입 스케일

일관된 스케일은 시각적 조화를 만들어요. 수학적 비율을 사용하세요.

1.25 스케일 (UI에 권장):

:root {
  /* Base: 16px (1rem) */
  --text-xs: 0.64rem;    /* 10.24px - use sparingly */
  --text-sm: 0.8rem;     /* 12.8px - captions, labels */
  --text-base: 1rem;     /* 16px - body text */
  --text-lg: 1.25rem;    /* 20px - lead text */
  --text-xl: 1.563rem;   /* 25px - h4 */
  --text-2xl: 1.953rem;  /* 31.25px - h3 */
  --text-3xl: 2.441rem;  /* 39px - h2 */
  --text-4xl: 3.052rem;  /* 48.8px - h1 */
}

행간 (Leading)

행간은 가독성에 큰 영향을 미쳐요. 콘텐츠 유형에 따라 다른 행간이 필요해요.

콘텐츠 유형 행간 이유
헤드라인 1.1 - 1.2 짧고 굵은 텍스트
UI 텍스트 1.3 - 1.4 라벨, 버튼
본문 1.5 - 1.7 읽기 쉬운 단락
장문 콘텐츠 1.7 - 2.0 아티클, 문서

줄 길이 (Measure)

적절한 줄 길이는 눈의 피로를 줄이고 읽기 이해도를 높여요.

  • 최적: 한 줄당 45-75자
  • 목표: 50-65자
  • 절대 최대: 85자
p {
  max-width: 65ch;  /* ch unit = width of '0' character */
}

.article-body {
  max-width: 70ch;
  margin: 0 auto;
}

글꼴 선택

시스템 글꼴 우선. 즉시 로드되고, 플랫폼과 어울리며, 화면에 최적화되어 있어요.

:root {
  --font-sans: system-ui, -apple-system, BlinkMacSystemFont,
               'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;

  --font-mono: ui-monospace, 'SF Mono', 'Cascadia Code',
               'JetBrains Mono', Consolas, monospace;
}

커스텀 글꼴을 사용할 때: - 브랜드 차별화 (마케팅 사이트) - 에디토리얼/출판 느낌 - 시스템 글꼴로 달성할 수 없는 특정 디자인 의도

글꼴 굵기로 위계 표현

크기뿐 아니라 굵기를 활용해 위계를 잡으세요.

h1 { font-weight: 700; }  /* Bold */
h2 { font-weight: 600; }  /* Semibold */
h3 { font-weight: 600; }  /* Semibold */
.lead { font-weight: 500; }  /* Medium */
p { font-weight: 400; }   /* Regular */
.meta { font-weight: 400; color: var(--text-muted); }

타이포그래피 빠른 참조

속성 본문 헤드라인 UI 라벨
크기 16-18px 24-48px 12-14px
굵기 400 600-700 500
행간 1.5-1.7 1.1-1.2 1.3-1.4
줄 길이 45-75ch N/A N/A
정렬 왼쪽 가운데 가능 왼쪽

색상 이론

“색은 영혼에 직접적으로 영향을 미치는 힘이다.” — Wassily Kandinsky

색상은 텍스트보다 빠르게 소통합니다. 분위기를 조성하고, 시선을 유도하며, 의미를 전달하고, 브랜드 인지도를 구축합니다.

60-30-10 규칙

균형 잡힌 인터페이스를 위한 가장 신뢰할 수 있는 색상 배분 방식입니다.

┌──────────────────────────────────────────┐
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ 60% - 주조색 (배경)
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░│ 30% - 보조색 (카드, 섹션)
│░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░│
│░░░░░▓▓▓▓▓▓▓▓▓▓▓▓██████▓▓▓▓▓▓▓▓▓▓░░░░░░░░│ 10% - 강조색 (버튼, 링크)
└──────────────────────────────────────────┘

색상 팔레트 구성하기

모든 인터페이스에는 다음과 같은 시맨틱 색상이 필요합니다:

:root {
  /* Brand */
  --color-primary: hsl(220, 80%, 50%);
  --color-primary-hover: hsl(220, 80%, 45%);

  /* Semantic */
  --color-success: hsl(142, 76%, 36%);  /* Green - positive */
  --color-warning: hsl(38, 92%, 50%);   /* Amber - caution */
  --color-error: hsl(0, 84%, 60%);      /* Red - danger */

  /* Neutrals */
  --color-background: hsl(0, 0%, 100%);
  --color-surface: hsl(220, 14%, 96%);
  --color-border: hsl(220, 13%, 91%);

  /* Text */
  --color-text: hsl(220, 13%, 13%);
  --color-text-secondary: hsl(220, 9%, 46%);
  --color-text-muted: hsl(220, 9%, 64%);
}

색상 심리학

색상 심리적 효과 UI 활용
파랑 신뢰, 안정감, 차분함 금융, 기술, 기업
초록 성장, 자연, 성공 헬스케어, 친환경, 긍정적 상태
빨강 에너지, 긴급함, 위험 알림, 세일, 오류
주황 따뜻함, 열정 CTA, 활기찬 브랜드
노랑 낙관, 주의 경고, 하이라이트
보라 고급스러움, 창의성 프리미엄 제품

다크 모드 우선 디자인 (Vercel)

Vercel은 다크 모드를 먼저 디자인한 뒤, 라이트 모드를 파생합니다. 이 접근 방식은 다크 모드를 부가적인 요소가 아닌 주요 고려 사항으로 다루기 때문에 더 나은 다크 인터페이스를 만들어냅니다.

/* Design dark first, derive light */
:root {
  /* Dark mode defaults */
  --color-background: hsl(0, 0%, 0%);
  --color-surface: hsl(0, 0%, 7%);
  --color-border: hsl(0, 0%, 15%);
  --color-text: hsl(0, 0%, 93%);
  --color-text-secondary: hsl(0, 0%, 63%);
}

@media (prefers-color-scheme: light) {
  :root {
    --color-background: hsl(0, 0%, 100%);
    --color-surface: hsl(0, 0%, 97%);
    --color-border: hsl(0, 0%, 89%);
    --color-text: hsl(0, 0%, 9%);
    --color-text-secondary: hsl(0, 0%, 40%);
  }
}

적합한 경우: 개발자 도구, 미디어 앱, 대시보드 등 사용자가 장시간 작업하며 다크 모드가 눈의 피로를 줄여주는 환경에 적합합니다.

접근성 명도 대비

수준 일반 텍스트 큰 텍스트 UI 컴포넌트
AA 4.5:1 3:1 3:1
AAA 7:1 4.5:1 N/A

WCAG 2.2는 2025년 10월에 ISO 표준(ISO/IEC 40500:2025)으로 채택되었으며, 포커스 가시성, 중복 입력, 접근 가능한 인증에 대한 기준이 추가되었습니다. 주요 추가 사항: 포커스 표시기가 다른 콘텐츠에 의해 완전히 가려져서는 안 되며(2.4.11), 인증이 인지 기능 테스트에만 의존해서는 안 됩니다(3.3.8).

도구: WebAIM Contrast Checker, Chrome DevTools 색상 선택기


시각적 계층 구조

“디자인은 브랜드의 침묵하는 대사다.” — Paul Rand

시각적 계층 구조는 사용자가 무엇을 첫 번째, 두 번째, 세 번째로 보는지를 제어합니다. 명확한 계층 구조가 없으면 사용자는 정보를 찾기 위해 노력해야 합니다. 계층 구조가 잘 잡혀 있으면 인터페이스가 자연스럽게 느껴집니다.

계층 구조의 6가지 도구

1. 크기 — 큰 요소가 먼저 시선을 끕니다

.hero-title { font-size: 3rem; }      /* Dominant */
.section-title { font-size: 1.5rem; } /* Secondary */
.body-text { font-size: 1rem; }       /* Baseline */

2. 굵기 — 볼드는 앞으로 튀어나오고, 가벼운 서체는 뒤로 물러납니다

h1 { font-weight: 700; }
.lead { font-weight: 500; }
p { font-weight: 400; }

3. 색상과 대비 — 높은 대비 = 주목

.title { color: var(--color-text); }  /* Near black */
.meta { color: var(--color-text-muted); }  /* Gray */

4. 위치 — 핵심 위치가 중요합니다

F-PATTERN (content pages):     Z-PATTERN (landing pages):
████████████████████████      1 ──────────────────► 2
████████                            ↘
████                                     ↘
██                                            ↘
                                   3 ──────────────────► 4

5. 여백 — 분리가 중요성을 만듭니다

.hero { padding: 120px 48px; }  /* Generous space */
.data-table { padding: 12px; }  /* Dense content */

6. 깊이와 고도 — 앞으로 돌출된 요소가 시선을 끕니다

:root {
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
  --shadow-md: 0 4px 6px rgba(0,0,0,0.1);
  --shadow-lg: 0 10px 15px rgba(0,0,0,0.1);
}

.card { box-shadow: var(--shadow-sm); }
.card:hover { box-shadow: var(--shadow-md); }
.modal { box-shadow: var(--shadow-lg); }

적용 패턴

협업 프레즌스 (Figma): 이름 라벨이 달린 여러 사용자 커서, 선택 강조, 컴포넌트 아웃라인이 살아있는 문서를 만들어냅니다. 각 협업자의 색상은 구별되지만 동일한 비중을 가져서 어떤 커서도 다른 것보다 “더 눈에 띄지” 않습니다.

주변 상태 표시기 (Vercel): 배포 상태는 방해가 되는 알림 대신 미묘하고 지속적인 표시기를 사용합니다. 상단의 얇은 색상 바가 워크플로를 방해하지 않으면서 상태(빌드 중, 배포 완료, 실패)를 전달합니다.

현실 세계 디자인 비유 (Flighty): 비행 진행 시각화는 물리적 항공 계기를 반영합니다. 고도 곡선, 속도 표시기, 게이트 맵 등 추상적인 진행률 바 대신 익숙한 시각적 메타포를 사용합니다.

눈 찡그리기 테스트

디자인을 눈을 찡그리고 바라보세요. 그래도 계층 구조가 보이나요? 보인다면 잘 된 것입니다.


간격과 리듬

“여백은 공기와 같다: 디자인이 숨 쉬기 위해 반드시 필요하다.” — Wojciech Zieliński

간격은 디자인의 보이지 않는 구조입니다. 일관된 간격은 시각적 리듬을 만들어냅니다. 요소들이 하나의 일관된 시스템 안에 함께 속해 있다는 느낌을 주는 것입니다.

8px 그리드

8px 그리드가 업계 표준인 이유는 다음과 같습니다: - 균등하게 나눌 수 있습니다 (8, 16, 24, 32, 40, 48…) - 일반적인 화면 밀도와 호환됩니다 (1x, 1.5x, 2x, 3x) - 복잡한 계산 없이 일관된 리듬을 만들어냅니다

:root {
  --space-1: 4px;    /* Tight: icon gaps */
  --space-2: 8px;    /* Compact: inline elements */
  --space-3: 12px;   /* Snug: form fields */
  --space-4: 16px;   /* Default: most gaps */
  --space-6: 24px;   /* Spacious: card padding */
  --space-8: 32px;   /* Section gaps */
  --space-12: 48px;  /* Major sections */
  --space-16: 64px;  /* Page sections */
  --space-20: 80px;  /* Hero spacing */
}

내부 간격 vs 외부 간격

내부 간격 (padding): 요소 안쪽의 공간 외부 간격 (margin): 요소 사이의 공간

원칙: 관련된 그룹 내에서 내부 간격은 보통 외부 간격보다 커야 합니다.

.card {
  padding: 24px;        /* Internal: spacious */
  margin-bottom: 16px;  /* External: less than padding */
}

컴포넌트 간격 패턴

카드:

.card { padding: 24px; border-radius: 12px; }
.card-header { margin-bottom: 16px; }
.card-title { margin-bottom: 4px; }  /* Tight to subtitle */

버튼:

.btn { padding: 12px 24px; border-radius: 8px; }
.btn--sm { padding: 8px 16px; }
.btn--lg { padding: 16px 32px; }
.btn-group { display: flex; gap: 12px; }

폼:

.form-row { margin-bottom: 24px; }
.form-label { margin-bottom: 4px; }
.form-help { margin-top: 4px; }
.form-actions { margin-top: 32px; display: flex; gap: 12px; }

간격 빠른 참조표

맥락 권장 간격
아이콘과 텍스트 사이 4-8px
라벨과 입력 필드 사이 4px
폼 그룹 사이 24px
카드 패딩 20-24px
카드 간격 16-24px
섹션 패딩 (모바일) 48-64px
섹션 패딩 (데스크톱) 80-96px
버튼 패딩 (가로/세로) 24px / 12px

애니메이션 원칙

“애니메이션은 움직이는 그림의 예술이 아니라, 그려진 움직임의 예술이다.” — Norman McLaren

애니메이션은 인터페이스에 생명을 불어넣어요. 잘 활용하면 시선을 유도하고, 상태를 전달하며, 감정적 연결을 만들어 줘요. 잘못 사용하면 사용자를 짜증나게 하고 주의를 분산시켜요.

핵심 원칙

애니메이션은 장식이 아니라 필연적으로 느껴져야 해요.

좋은 애니메이션: 1. 정적인 디자인으로는 전달할 수 없는 정보를 전달해요 2. 관계를 시각적으로 보여줌으로써 인지 부하를 줄여요 3. 자연스럽고 예상 가능하게 느껴져요 4. 의식하지 못할 정도로 자연스럽게 녹아들어요

나쁜 애니메이션: 1. 단지 “멋져 보이기 때문에” 존재해요 2. 사용자를 느리게 만들어요 3. 그 자체로 시선을 끌어요 4. 불안감이나 초조함을 유발해요

UI를 위한 핵심 원칙

1. 예비 동작(Anticipation) — 사용자에게 다음에 일어날 일을 준비시켜요.

.button {
  transition: transform 0.1s ease-out;
}

.button:active {
  transform: scale(0.97);  /* Slight press before action */
}

2. 관성(Follow-Through) — 모션이 스프링처럼 자연스럽게 안착하도록 해요.

.panel {
  transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}
withAnimation(.spring(response: 0.4, dampingFraction: 0.7)) {
    isOpen = true
}

3. Ease-In, Ease-Out — 자연계에서 일정한 속도로 움직이는 것은 없어요.

곡선 사용 시점 특성
ease-out 요소 진입 시 빠르게 시작, 부드럽게 정지
ease-in 요소 퇴장 시 부드럽게 시작, 빠르게 퇴장
ease-in-out 상태 전환 시 전체적으로 부드러움
linear 로딩 인디케이터 연속적이고 기계적

4. 연출(Staging) — 중요한 것에 시선을 유도하세요. 그룹으로 안무된 경우가 아니라면 한 번에 하나만 움직여야 해요.

5. 시차 배치(Staggering) — 요소들은 한꺼번에가 아니라 순차적으로 등장해야 해요.

.list-item {
  animation: fadeSlideIn 0.3s ease-out both;
}

.list-item:nth-child(1) { animation-delay: 0ms; }
.list-item:nth-child(2) { animation-delay: 50ms; }
.list-item:nth-child(3) { animation-delay: 100ms; }
.list-item:nth-child(4) { animation-delay: 150ms; }

@keyframes fadeSlideIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

타이밍 가이드라인

지속 시간 사용 사례 느낌
50-100ms 마이크로 인터랙션 (호버, 누르기) 즉각적인 피드백
150-200ms 간단한 상태 변화 (토글, 선택) 경쾌함
250-350ms 중간 전환 (패널 슬라이드, 카드 플립) 부드러움
400-500ms 큰 움직임 (페이지 전환, 모달) 의도적

성능: 황금 규칙

transformopacity만 애니메이션하세요 — 이 속성들은 GPU 가속을 활용하며 레이아웃을 트리거하지 않아요.

/* BAD: Animating layout */
.panel { transition: left 0.3s, width 0.3s; }

/* GOOD: Using transform */
.panel { transition: transform 0.3s; }

애니메이션을 적용하지 말아야 할 때

  1. 사용자가 prefers-reduced-motion을 활성화한 경우 css @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } }

  2. 애니메이션이 정보를 추가하지 않는 경우 — 불필요한 스피너, 튀는 요소들

  3. 사용자가 급한 경우 — 에러 상태, 폼 유효성 검사, 검색 결과
  4. 반복 작업을 느리게 만드는 경우 — 키보드 단축키는 애니메이션을 건너뛰어야 해요
  5. 데이터가 이미 로드된 경우 — Bear는 콘텐츠를 미리 로드하여 로딩 상태를 전혀 사용하지 않아 앱이 즉각적으로 느껴져요. 미리 로드할 수 있다면 스켈레톤이나 스피너를 아예 건너뛰세요.
// Bear's approach: preload so no loading state is needed
struct NoteListView: View {
    @Query var notes: [Note]  // SwiftData loads from disk instantly
    // No loading state, no skeleton, no spinner — data is always there
    var body: some View {
        List(notes) { note in
            NoteRow(note: note)
        }
    }
}

애니메이션 빠른 참조

:root {
  /* Durations */
  --duration-instant: 0.1s;
  --duration-fast: 0.15s;
  --duration-normal: 0.25s;
  --duration-slow: 0.4s;

  /* Easings */
  --ease-out: cubic-bezier(0.0, 0.0, 0.58, 1.0);
  --ease-in: cubic-bezier(0.42, 0.0, 1.0, 1.0);
  --ease-in-out: cubic-bezier(0.42, 0.0, 0.58, 1.0);
  --ease-out-back: cubic-bezier(0.34, 1.56, 0.64, 1);
}

인터랙션 패턴

“최고의 인터페이스는 인터페이스가 없는 것이다.” — Golden Krishna

인터랙션 패턴은 사용자가 제품을 조작하고, 탐색하고, 이해하는 방식을 정의해요. 이 패턴들은 뛰어난 인터랙션을 제공하는 제품들을 연구하여 추출한 것이에요.

직접 조작 (Framer)

추상적인 개념을 실체화하세요. Framer는 추상적인 숫자에 불과한 CSS 브레이크포인트를 드래그 가능한 핸들로 변환해요. 사용자는 레이아웃이 실시간으로 적응하는 모습을 직접 확인할 수 있어요.

/* Breakpoint handle styling */
.breakpoint-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 4px;
  background: var(--accent);
  cursor: col-resize;
  opacity: 0.6;
  transition: opacity 0.15s ease;
}

.breakpoint-handle:hover,
.breakpoint-handle:active {
  opacity: 1;
  width: 6px;
}

사용 시점: 결과가 시각적인 모든 설정 — 드래그로 크기 조절, 색상 선택기, 타임라인 스크러빙 등.

맥락 인식 인터페이스 (Flighty, Figma)

현재 순간에 관련된 것만 보여주세요. Flighty는 항공편 추적에 15가지 고유 상태를 사용해요. Figma의 속성 패널은 선택한 대상에 따라 완전히 달라져요.

단계 (Flighty) 사용자에게 표시되는 내용
24시간 전 확인 코드, 터미널 정보
공항 도착 시 게이트 번호, 탑승 시간
비행 중 남은 시간, 진행 상황, 도착 예정 시간
착륙 시 환승 게이트, 이동 경로
enum ContextState: CaseIterable {
    case farOut, dayBefore, headToAirport, atAirport
    case atGate, boarding, inFlight, landed, connection

    static func current(for flight: Flight, context: UserContext) -> ContextState {
        // Factor in: time, location, flight status
        // Return the single most relevant state
    }
}

안티패턴: 모든 컨트롤을 표시하고 관련 없는 것들을 회색 처리하는 것. 이는 시각적 노이즈를 만들어요.

지능형 활성화 (Halide)

도구는 맥락을 감지하고 스스로 활성화되어야 해요. Halide의 초점 루페는 초점 드래그 시 나타나고 손을 떼면 사라져요. 토글 버튼이 필요 없어요.

struct IntelligentlyActivated<Content: View>: ViewModifier {
    let isInteracting: Bool
    @State private var isVisible = false

    func body(content: Content) -> some View {
        content
            .opacity(isVisible ? 1 : 0)
            .scaleEffect(isVisible ? 1 : 0.95)
            .animation(.easeInOut(duration: 0.2), value: isVisible)
            .onChange(of: isInteracting) { _, newValue in
                if newValue {
                    withAnimation { isVisible = true }
                } else {
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                        if !isInteracting { isVisible = false }
                    }
                }
            }
    }
}

듀얼 모드 디자인 (Halide, Warp)

모드 전환은 단순히 요소를 토글하는 게 아니라 UI를 근본적으로 변환해야 해요. Halide의 자동 모드와 수동 모드는 완전히 다른 인터페이스예요. Warp는 네 가지 입력 방식(타이핑, 팔레트, AI, 마우스)을 통해 CLI와 GUI를 연결하면서도 사용자를 하나의 패러다임에 강제하지 않아요.

구조화된 콘텐츠 (Warp, Bear, Craft)

전통적으로 비구조화된 콘텐츠에 구조를 부여하세요. Warp는 터미널 출력을 복사, 공유, 재실행이 가능한 개별 블록으로 만들어요. Bear는 글을 쓰면서 인라인으로 노트를 정리할 수 있어요 (#tag/subtag). Craft는 모든 블록이 페이지가 될 수 있어서, 미리 정의된 계층 구조가 아닌 사용 과정에서 구조가 자연스럽게 형성돼요.

점진적 학습 (Superhuman)

반복적 노출을 통해 사용자에게 빠른 방법을 가르치세요. Superhuman의 Cmd+K 팔레트는 결과 옆에 항상 키보드 단축키를 표시해요. 매번 사용할 때마다 짧은 학습이 이루어져요.

/* Always show shortcut alongside command name */
.command-result {
  display: flex;
  justify-content: space-between;
  padding: 8px 12px;
}

.command-shortcut {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-muted);
  background: var(--bg-subtle);
  padding: 2px 6px;
  border-radius: 4px;
}

안티패턴: 기능을 설명하는 튜토리얼 모달. 설명은 잊히지만, 직접 해본 경험은 기억에 남아요.


AI 인터페이스 패턴

“최고의 AI 인터페이스는 기계의 프로세스를 보여주고, 기계의 출력을 검증 가능하게 만들어요.”

AI 인터페이스는 독특한 과제에 직면해요: 사용자는 출력을 예측할 수 없고, 검사만으로 정확성을 검증할 수 없으며, 시스템이 제대로 작동하는지 고장 났는지 구분하기 어려워요.

핵심 문제

기존 소프트웨어 AI 소프트웨어
출력이 예측 가능해요 출력이 매번 달라요
오류가 명확해요 오류가 그럴듯하게 보여요
사용자가 테스트로 검증해요 사용자가 출처 확인으로 검증해요
로딩 = 대기 로딩 = 작업 중 (보여줘야 해요)
기본적으로 신뢰해요 신뢰는 쌓아가야 해요

출처 우선 디자인 (Perplexity)

모든 사실 주장은 출처에 연결되어야 해요. Perplexity는 모든 주장에 인라인 인용 [1]을 포함하고, 호버 프리뷰와 고정 출처 패널을 제공해요.

.citation-marker {
  position: relative;
  color: var(--accent);
  cursor: pointer;
  font-size: 0.8em;
  vertical-align: super;
}

.citation-preview {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  width: 280px;
  padding: 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

안티패턴: 추적 가능한 출처 없이 주장을 생성하는 AI 인터페이스. 모델이 인용할 수 없다면, 인터페이스가 이를 표시해야 해요.

스트리밍 단계 표시기 (Perplexity)

AI가 단순히 작동 중이라는 것만이 아니라, 무엇을 하고 있는지 보여주세요. 일반적인 스피너 대신 단계 표시기를 사용하세요: “검색 중…” → “4개 출처 읽는 중…” → “답변 작성 중…”

.phase-indicator {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  background: color-mix(in srgb, var(--phase-color) 10%, transparent);
  border-radius: 16px;
  font-size: 13px;
  color: var(--phase-color);
  transition: all 0.3s ease;
}

.loading-dots span {
  width: 4px;
  height: 4px;
  background: currentColor;
  border-radius: 50%;
  animation: pulse 1.4s ease-in-out infinite;
}

오류 투명성

AI가 실패하거나 불확실할 때, 자신감 있게 들리는 텍스트 뒤에 숨기지 말고 명확하게 보여주세요.

상황 나쁜 패턴 좋은 패턴
낮은 신뢰도 자신 있게 말하기 “확실하지 않지만…” + 낮은 스타일링
출처 없음 텍스트 조작 “이 주장에 대한 출처를 찾지 못했어요”
상충되는 출처 조용히 하나 선택 충돌을 강조하며 둘 다 보여주기
오래된 정보 최신인 것처럼 제시 “[날짜] 기준…” + 최신성 표시기

핵심 인사이트: 사용자는 불확실성에 솔직한 AI를 용서해요. 하지만 자신 있게 틀리는 AI는 용서하지 않아요.


Dieter Rams: 10가지 원칙

“더 적게, 하지만 더 좋게.” — Dieter Rams

Dieter Rams는 20세기 가장 영향력 있는 산업 디자이너예요. 1961년부터 1995년까지 Braun의 디자인 총괄로서, 수십 년이 지나도 변하지 않는 제품을 만들었어요. 그의 작업은 Apple의 디자인 언어에 직접적인 영감을 주었어요.

좋은 디자인의 10가지 원칙

1. 좋은 디자인은 혁신적이에요 복제하지 마세요. 발전하는 기술과 혁신적인 디자인을 결합하세요.

2. 좋은 디자인은 제품을 유용하게 만들어요 모든 요소는 목적이 있어야 해요. 형태는 기능을 따라요.

3. 좋은 디자인은 아름다워요 아름다움은 피상적인 것이 아니라 본질적이에요. 매일 사용하는 제품은 우리의 삶의 질에 영향을 미쳐요.

4. 좋은 디자인은 제품을 이해하기 쉽게 만들어요 사용자가 설명서를 필요로 해서는 안 돼요. 인터페이스 자체가 가르쳐야 해요.

5. 좋은 디자인은 눈에 띄지 않아요 디자인은 지원해야지, 압도해서는 안 돼요. 주인공은 UI가 아니라 사용자의 콘텐츠예요.

/* Obtrusive: UI competes with content */
.editor {
  background: linear-gradient(135deg, purple, blue);
  border: 3px dashed gold;
}

/* Unobtrusive: UI recedes, content shines */
.editor {
  background: var(--color-background);
  border: 1px solid var(--color-border);
}

6. 좋은 디자인은 정직해요 다크 패턴을 사용하지 마세요. 과대 약속하지 마세요. 한계에 대해 투명하게 밝히세요.

7. 좋은 디자인은 오래가요 빨리 낡아 보일 트렌드를 피하세요. 유행보다 클래식을 선택하세요.

TRENDY (will date):           TIMELESS:
- Extreme glassmorphism       - Clean typography
- Neon colors, glitch effects - Subtle elevation
- Aggressive gradients        - Neutral palette with considered accent

8. 좋은 디자인은 마지막 디테일까지 철저해요 임의적인 것이 있어서는 안 돼요. 로딩 상태, 빈 상태, 오류 상태—모두 디자인되어야 해요.

9. 좋은 디자인은 환경 친화적이에요 성능은 곧 환경이에요. 사용자의 주의력을 존중하세요. 효율적인 코드를 작성하세요.

10. 좋은 디자인은 가능한 한 적은 디자인이에요 필요하지 않은 모든 것을 제거하세요. 최고의 디자인은 눈에 보이지 않아요.


2026년 웹 패턴

현대 웹 디자인은 많은 경우 JavaScript가 필요 없는 네이티브 CSS 기능을 활용해요. 2025-2026년에는 앵커 포지셔닝, 스크롤 기반 애니메이션, @starting-style이 프로덕션 브라우저에 도입되었어요.

Container Queries

뷰포트가 아닌 컨테이너 크기를 기준으로 컴포넌트 크기를 조정하세요.

.card-grid {
  container-type: inline-size;
  container-name: card-grid;
}

.card {
  display: grid;
  gap: 16px;
  padding: 20px;
}

@container card-grid (min-width: 400px) {
  .card {
    grid-template-columns: auto 1fr;
  }
}

@container card-grid (min-width: 600px) {
  .card {
    padding: 32px;
    gap: 24px;
  }
}

:has() 선택자

자식 요소를 기반으로 부모를 선택하는 기능—이전에는 JavaScript 없이 불가능했어요.

/* Card with image gets different padding */
.card:has(img) {
  padding: 0;
}

.card:has(img) .card-content {
  padding: 20px;
}

/* Form group with error */
.form-group:has(.input:invalid) .form-label {
  color: var(--color-error);
}

/* Highlight navigation when on that page */
.nav-item:has(a[aria-current="page"]) {
  background: var(--color-surface);
}

CSS 네이티브 중첩

전처리기 없이 네이티브 중첩을 사용할 수 있어요.

.card {
  background: var(--color-surface);
  border-radius: 12px;
  padding: 24px;

  & .card-title {
    font-size: 1.25rem;
    font-weight: 600;
    margin-bottom: 8px;
  }

  & .card-body {
    color: var(--color-text-secondary);
    line-height: 1.6;
  }

  &:hover {
    box-shadow: var(--shadow-md);
  }

  @media (min-width: 768px) {
    padding: 32px;
  }
}

HTMX 통합

무거운 JavaScript 프레임워크 없이 서버 주도 인터랙티비티를 구현할 수 있어요.

<!-- Load content on click -->
<button hx-get="/api/more-items"
        hx-target="#item-list"
        hx-swap="beforeend"
        hx-indicator="#loading">
  Load More
</button>

<!-- Form with inline validation -->
<form hx-post="/api/contact"
      hx-target="#form-response"
      hx-swap="outerHTML">
  <input type="email" name="email"
         hx-post="/api/validate-email"
         hx-trigger="blur"
         hx-target="next .error" />
  <span class="error"></span>
</form>

앵커 포지셔닝

한 요소를 다른 요소에 상대적으로 배치하는 네이티브 CSS 기능—JavaScript가 필요 없어요. 트리거를 따라가는 툴팁, 팝오버, 드롭다운 메뉴를 만들 수 있어요.

/* Anchor an element to another */
.trigger {
  anchor-name: --my-trigger;
}

.tooltip {
  position: fixed;
  position-anchor: --my-trigger;
  top: anchor(bottom);
  left: anchor(center);
  translate: -50% 8px;
}

스크롤 기반 애니메이션

애니메이션 진행도를 스크롤 위치에 연결하세요. 읽기 진행 표시기, 패럴렉스 효과, 순차적 등장 시퀀스를 JavaScript 없이 구현할 수 있어요.

/* Reading progress bar */
.progress-bar {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 3px;
  background: var(--color-primary);
  transform-origin: left;
  animation: progress linear;
  animation-timeline: scroll();
}

@keyframes progress {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

@starting-style

DOM에 추가되는 요소의 초기 스타일을 정의하세요—JavaScript 없이 CSS만으로 진입 애니메이션을 구현할 수 있어요.

.card {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 0.3s ease, transform 0.3s ease;

  @starting-style {
    opacity: 0;
    transform: translateY(10px);
  }
}

디자인 토큰 시스템

애플리케이션 전반의 일관성을 위한 완전한 토큰 시스템이에요.

:root {
  /* Colors */
  --color-text: #1a1a1a;
  --color-text-secondary: #666666;
  --color-text-muted: #999999;

  --color-background: #ffffff;
  --color-surface: #f8f9fa;
  --color-surface-elevated: #ffffff;

  --color-border: #e5e7eb;
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;

  --color-success: #10b981;
  --color-warning: #f59e0b;
  --color-error: #ef4444;

  /* Typography */
  --font-sans: system-ui, -apple-system, sans-serif;
  --font-mono: "SF Mono", Consolas, monospace;

  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.25rem;
  --text-2xl: 1.5rem;
  --text-3xl: 2rem;

  --leading-tight: 1.25;
  --leading-normal: 1.5;
  --leading-relaxed: 1.75;

  /* Spacing (8px base) */
  --space-1: 0.25rem;   /* 4px */
  --space-2: 0.5rem;    /* 8px */
  --space-3: 0.75rem;   /* 12px */
  --space-4: 1rem;      /* 16px */
  --space-6: 1.5rem;    /* 24px */
  --space-8: 2rem;      /* 32px */
  --space-12: 3rem;     /* 48px */
  --space-16: 4rem;     /* 64px */

  /* Borders */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-full: 9999px;

  /* Shadows */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
  --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);

  /* Transitions */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --duration-fast: 100ms;
  --duration-normal: 200ms;
}

다크 모드 제대로 구현하기

단순히 반전하지 마세요—어두운 환경에 맞게 재설계해야 합니다.

@media (prefers-color-scheme: dark) {
  :root {
    /* Neutrals */
    --color-background: hsl(220, 13%, 10%);
    --color-surface: hsl(220, 13%, 15%);
    --color-surface-elevated: hsl(220, 13%, 18%);
    --color-border: hsl(220, 13%, 23%);

    /* Text (inverted) */
    --color-text: hsl(220, 9%, 93%);
    --color-text-secondary: hsl(220, 9%, 70%);
    --color-text-muted: hsl(220, 9%, 55%);

    /* Adjust saturation for dark mode */
    --color-primary: hsl(220, 80%, 60%);
    --color-success: hsl(142, 70%, 45%);
    --color-error: hsl(0, 80%, 65%);

    /* Shadows in dark mode need adjustment */
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
    --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4);
  }
}

다크 모드 원칙: - 넓은 면적에는 채도를 낮추세요 - 강조 색상의 밝기를 높이세요 - 그림자를 강화하세요 (더 높은 대비가 필요합니다) - 다크 모드는 부가적인 요소가 아니라 의도적으로 설계하세요


Figma 추출 워크플로우

디자인 파일을 프로덕션 코드로 변환하려면 디자인 언어를 정의하는 디자인 토큰(색상, 타이포그래피, 간격, 효과)을 체계적으로 추출해야 합니다.

Figma Variables 내보내기

Figma의 네이티브 Variables 기능은 가장 깔끔한 추출 경로를 제공합니다.

내보내기 단계: 1. Figma 파일 열기 → Local Variables 패널 2. 컬렉션 메뉴 클릭 → “Export to JSON” 3. figma-variables.json으로 저장

JSON 토큰 구조:

{
  "colors": {
    "primitive": {
      "blue-500": { "value": "#3b82f6", "type": "color" },
      "blue-600": { "value": "#2563eb", "type": "color" }
    },
    "semantic": {
      "primary": { "value": "{colors.primitive.blue-500}", "type": "color" },
      "primary-hover": { "value": "{colors.primitive.blue-600}", "type": "color" }
    }
  },
  "spacing": {
    "1": { "value": "4px", "type": "spacing" },
    "2": { "value": "8px", "type": "spacing" },
    "4": { "value": "16px", "type": "spacing" }
  }
}

토큰에서 CSS로 변환

CSS 커스텀 속성:

:root {
  /* Primitive colors (direct values) */
  --color-blue-50: #eff6ff;
  --color-blue-100: #dbeafe;
  --color-blue-500: #3b82f6;
  --color-blue-600: #2563eb;
  --color-blue-900: #1e3a8a;

  /* Semantic colors (reference primitives) */
  --color-primary: var(--color-blue-500);
  --color-primary-hover: var(--color-blue-600);
  --color-background: var(--color-white);
  --color-surface: var(--color-gray-50);

  /* Spacing (8px grid) */
  --space-1: 0.25rem;  /* 4px */
  --space-2: 0.5rem;   /* 8px */
  --space-4: 1rem;     /* 16px */
  --space-6: 1.5rem;   /* 24px */
  --space-8: 2rem;     /* 32px */

  /* Typography */
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
  --line-height-tight: 1.25;
  --line-height-normal: 1.5;

  /* Effects */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
}

다크 모드 토큰:

@media (prefers-color-scheme: dark) {
  :root {
    --color-background: var(--color-gray-900);
    --color-surface: var(--color-gray-800);
    --color-text: var(--color-gray-100);
    --color-text-secondary: var(--color-gray-400);

    /* Adjusted shadows for dark mode */
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
    --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4);
  }
}

토큰에서 SwiftUI로 변환

Color 확장:

import SwiftUI

extension Color {
    // MARK: - Primitive Colors
    static let blue50 = Color(hex: "eff6ff")
    static let blue500 = Color(hex: "3b82f6")
    static let blue600 = Color(hex: "2563eb")

    // MARK: - Semantic Colors
    static let brandPrimary = Color.blue500
    static let brandPrimaryHover = Color.blue600

    // MARK: - Surface Colors
    static let surfaceBackground = Color(light: .white, dark: Color(hex: "0f172a"))
    static let surfaceElevated = Color(light: Color(hex: "f8fafc"), dark: Color(hex: "1e293b"))
}

extension Color {
    init(hex: String) {
        // Standard hex parsing implementation
    }

    init(light: Color, dark: Color) {
        self.init(UIColor { traits in
            traits.userInterfaceStyle == .dark ? UIColor(dark) : UIColor(light)
        })
    }
}

간격 상수:

enum Spacing {
    static let xs: CGFloat = 4    // --space-1
    static let sm: CGFloat = 8    // --space-2
    static let md: CGFloat = 16   // --space-4
    static let lg: CGFloat = 24   // --space-6
    static let xl: CGFloat = 32   // --space-8
}

// Usage
VStack(spacing: Spacing.md) {
    // ...
}
.padding(Spacing.lg)

디자이너 핸드오프 체크리스트

디자이너가 내보내야 할 항목:

에셋 유형 포맷 참고 사항
색상 Variables JSON 라이트 + 다크 모드 포함
타이포그래피 Styles 내보내기 폰트, 크기, 두께, line-height
간격 Variables JSON 기본 단위 문서화
아이콘 SVG 아웃라인, 단색
이미지 PNG @2x/@3x 또는 WebP 압축 적용
컴포넌트 Figma 링크 구현 시 참조용

품질 게이트 기준:

  • [ ] 모든 색상이 변수로 정의됨 (하드코딩된 hex 값 없음)
  • [ ] 타이포그래피가 정의된 텍스트 스타일을 사용함
  • [ ] 간격이 그리드 시스템을 따름 (8px 기본)
  • [ ] 다크 모드 변형이 제공됨
  • [ ] 인터랙티브 상태가 문서화됨 (hover, active, disabled)
  • [ ] 반응형 브레이크포인트가 표시됨
  • [ ] 접근성 요구사항이 명시됨 (대비율)

개발자가 받는 항목:

  1. 토큰 파일 (플랫폼에 따라 JSON/CSS/Swift)
  2. 측정값이 포함된 컴포넌트 스펙
  3. 필요한 포맷의 에셋 내보내기
  4. 인터랙션 문서 (상태, 애니메이션)
  5. 접근성 주석

빠른 참조 테이블

Gestalt 원칙

원칙 규칙 용도
근접성 관련 요소 = 가까이 폼, 섹션
유사성 같은 모양 = 같은 기능 버튼, 카드
전경-배경 명확한 레이어 구분 모달, 카드
연속성 선을 따라감 타임라인, 정렬
폐합성 뇌가 형태를 완성함 아이콘, 스크롤 힌트

타이포그래피

요소 크기 두께 Line Height
본문 16px 400 1.5-1.7
헤드라인 24-48px 600-700 1.1-1.2
UI 라벨 12-14px 500 1.3-1.4
캡션 12px 400 1.4

색상 역할

역할 라이트 모드 다크 모드
배경 #ffffff #0f172a
표면 #f4f5f7 #1e293b
테두리 #e4e6ea #334155
텍스트 #1a1a2e #f1f5f9
보조 텍스트 #6b7280 #94a3b8
Primary #3b82f6 #60a5fa
Success #22c55e #4ade80
Error #ef4444 #f87171

간격 스케일

토큰 용도
–space-1 4px 아이콘 간격
–space-2 8px 인라인 요소
–space-4 16px 기본 간격
–space-6 24px 카드 패딩
–space-8 32px 섹션 간격
–space-16 64px 페이지 섹션

디자인 체크리스트

인터페이스를 배포하기 전에 다음을 확인하세요:

Gestalt

  • [ ] 관련 요소가 비관련 요소보다 가까이 배치됨 (근접성)
  • [ ] 유사한 기능은 유사한 스타일을 가짐 (유사성)
  • [ ] 전경과 배경이 명확히 구분됨 (전경-배경)
  • [ ] 시선이 레이아웃을 자연스럽게 따라감 (연속성)

타이포그래피

  • [ ] 기본 폰트 크기가 최소 16px
  • [ ] 본문 텍스트의 line height가 1.5 이상
  • [ ] 줄 길이가 75자 미만
  • [ ] 계층 구조가 명확함 (3단계 구분 가능)
  • [ ] 일관된 스케일 사용

색상

  • [ ] 모든 텍스트가 4.5:1 대비를 충족 (WCAG AA)
  • [ ] 색상만으로 정보를 전달하지 않음 (아이콘/라벨 병행)
  • [ ] 다크 모드가 의도적으로 설계됨
  • [ ] 60-30-10 배분 규칙 준수

시각적 계층 구조

  • [ ] 가장 중요한 요소를 즉시 식별 가능
  • [ ] 시선이 의도한 순서로 흐름
  • [ ] 섹션당 하나의 명확한 CTA
  • [ ] 일관된 타입 스케일

간격

  • [ ] 모든 간격이 정의된 스케일 사용 (매직 넘버 없음)
  • [ ] 카드/컴포넌트의 패딩이 일관됨
  • [ ] 모바일 간격이 편안함
  • [ ] 그리드 정렬이 일관됨 (8px 기본)

인터랙션

  • [ ] 사용자가 도구에 대해 생각하지 않고 목표를 달성할 수 있는가?
  • [ ] UI가 현재 맥락에 맞게 적응하는가?
  • [ ] 도구가 관련 있을 때만 표시되는가?
  • [ ] 반복 사용이 더 빠른 방법을 학습시키는가?

AI 인터페이스

  • [ ] 모든 사실적 주장에 추적 가능한 출처가 있는가?
  • [ ] 스트리밍이 단순 스피너가 아닌 처리 단계를 보여주는가?
  • [ ] 오류 상태가 숨겨지지 않고 투명하게 표시되는가?
  • [ ] 낮은 신뢰도의 출력이 시각적으로 구분되는가?

Dieter Rams 체크

  • [ ] 제거할 수 있는 요소가 있는가?
  • [ ] 모든 요소가 기능적 역할을 하는가?
  • [ ] 5년 후에도 구식으로 느껴지지 않을 것인가?
  • [ ] 모든 상태를 디자인했는가?

리소스

도서: - As Little Design as Possible — Sophie Lovell 저 (Dieter Rams) - The Elements of Typographic Style — Robert Bringhurst 저

도구: - WebAIM Contrast Checker - Type Scale Generator - Figma Tokens Studio — 디자인 토큰 관리

디자인 시스템: - Apple HIG - Material Design 3 - Radix UI - shadcn/ui


디자인 스터디

16개의 뛰어난 제품을 심층 분석하여, 참고할 만한 패턴과 원칙을 정리했어요.

개발자 도구

제품 주요 기여
Figma 멀티플레이어 프레즌스, 컨텍스트 인식 패널
Warp 블록 기반 터미널, CLI-GUI 브릿지
Framer 비주얼 반응형 디자인, 속성 컨트롤
Vercel 다크 모드의 정수, 앰비언트 상태 표시
Linear Optimistic UI, 키보드 중심 워크플로
Raycast 확장 시스템, 빠른 액션

iOS 네이티브 (Apple Design Award 수상작)

제품 주요 기여
Flighty 15가지 스마트 상태, Live Activities, 데이터 시각화
Halide 지능형 활성화, 제스처 컨트롤
Bear 타이포그래피 우선, 인라인 태깅
Craft 네이티브 우선 크로스 플랫폼, 중첩 페이지
Things 지연 날짜, 빠른 입력 패턴

생산성 & AI

제품 주요 기여
Superhuman 100ms 규칙, 커맨드 팔레트 트레이닝, 실습형 온보딩
Perplexity 인용 중심 AI, 스트리밍 단계
Notion 블록 시스템, 슬래시 커맨드
Arc 스페이스, 분할 뷰, 커맨드 바
Stripe 문서화의 정수, API 디자인

이 가이드는 실천을 통해 성장해요. 디자인 원칙은 시대를 초월하지만, 그 적용 방식은 기술과 이해의 발전에 따라 진화합니다.