Vercel: 개발자 경험으로서의 디자인

Vercel이 개발자 경험을 제품으로 만든 방법: 다크 모드 우선 디자인, 탭 상태 표시기, 낙관적 UI, 기능적 빈 상태. CSS 및 JavaScript 구현 패턴 포함.

5 분 소요 845 단어
Vercel: 개발자 경험으로서의 디자인 screenshot

Vercel: 디자인으로서의 개발자 경험

"개발자들은 나쁜 UX에 알레르기 반응을 보인다—속도를 늦추는 '즐거운 온보딩'은 원하지 않는다."

Vercel의 디자인 철학은 철저히 개발자 중심이다. Geist 디자인 시스템은 장식보다 명확성, 속도, 정보 밀도를 우선시한다. 모든 픽셀은 개발자의 워크플로우를 위해 존재한다.


Vercel이 중요한 이유

Vercel은 개발자 도구가 "디자이너스럽지" 않으면서도 뛰어난 디자인을 가질 수 있음을 증명한다. 대시보드는 빠르고, 정보 밀도가 높으며, 방해가 되지 않는다.

주요 성과: - 개발자를 위해 특별히 설계된 서체 Geist 개발 - 대시보드 리디자인으로 First Meaningful Paint 1.2초 단축 - 개발자 도구 분야에서 다크 모드 우선 디자인 선도 - 배포 UX의 표준 정립 - 배포 상태를 반영하는 탭 아이콘 (빌드 중, 오류, 준비 완료)


핵심 요점

  1. 다크 모드는 기능이 아닌 존중이다 - 개발자들은 어두운 배경의 터미널과 IDE에서 작업한다. 흰색 대시보드는 눈의 피로와 불편한 컨텍스트 전환을 유발한다
  2. 상태는 볼 수 있는 모든 곳에 있어야 한다 - 탭 파비콘, 페이지 제목, 타임라인 점: 배포 상태는 포커스를 전환하거나 탭을 열지 않고도 볼 수 있어야 한다
  3. 낙관적 UI는 체감 지연 시간을 제거한다 - 예상되는 상태를 즉시 보여주고, 백그라운드에서 실제 상태와 동기화한다. 개발자들은 300ms의 지연도 알아챈다
  4. 빈 상태는 일러스트가 아닌 안내다 - "시작하기" 버튼이 달린 장식적 그래픽이 아닌, 실행할 정확한 명령어(git push origin main)를 보여줘라
  5. 성능이 곧 디자인이다 - Vercel의 대시보드 리디자인은 First Meaningful Paint를 1.2초 단축했다. 아무리 아름다운 애니메이션도 느린 로딩 시간을 보상하지 못한다

핵심 디자인 철학

개발자 중심 원칙

개발자들은 제품이 자신을 얼마나 방해하지 않는지로 평가한다. Vercel의 디자인은 이를 반영한다:

안티패턴 (개발자가 싫어하는 것)              VERCEL의 접근 방식
───────────────────────────────────────────────────────────────────
지연을 유발하는 "즐거운" 애니메이션          즉각적인, 트랜지션 없는 상태
작업을 막는 온보딩 위자드                    CLI 우선, 대시보드는 선택
탭에 숨겨진 빽빽한 문서                      한눈에 보이는 정보
모든 액션마다 로딩 스피너                    낙관적 업데이트 + SWR
대시보드 내 마케팅 문구                      순수한 기능적 UI

핵심 통찰: 개발자들은 "즐거움"을 원하지 않는다. 그들은 배포하고 싶을 뿐이다.


패턴 라이브러리

1. 다크 모드 완성도

Vercel의 다크 모드는 토글이 아니다. 기본값이다. 디자인은 외과적이다: 선명한 검정과 흰색이 최대 대비를 만든다.

색상 철학:

:root {
  /* Vercel 팔레트는 놀라울 정도로 단순하다 */

  /* 배경 - 순수한 검정, 회색 없음 */
  --bg-000: #000000;
  --bg-100: #0A0A0A;
  --bg-200: #111111;

  /* 전경 - 고대비 흰색 */
  --fg-100: #FFFFFF;
  --fg-200: #EDEDED;
  --fg-300: #A1A1A1;
  --fg-400: #888888;

  /* 테두리 - 미묘하지만 보이는 */
  --border-100: #333333;
  --border-200: #444444;

  /* 시맨틱 - 배포 상태 */
  --color-success: #00DC82;  /* 녹색 - 배포됨 */
  --color-error: #FF0000;    /* 빨강 - 실패 */
  --color-warning: #FFAA00;  /* 황색 - 빌드 중 */
  --color-info: #0070F3;     /* 파랑 - 대기 중 */

  /* 강조색 - Vercel의 시그니처 */
  --accent: #FFFFFF;         /* 검정 위의 강조색으로서 흰색 */
}

순수한 검정이 효과적인 이유: - 텍스트 가독성을 위한 최대 대비 - 개발자가 신뢰하는 터미널 영감의 미학 - 어두운 환경에서 눈의 피로 감소 - 색상 상태 표시기가 돋보이게 함


2. 탭 상태 표시기

Vercel은 브라우저 탭 아이콘에 배포 상태를 반영하여, 탭이 포커스되지 않은 상태에서도 정보를 볼 수 있게 한다.

┌─ 브라우저 탭 바 ───────────────────────────────────────────────────┐
│                                                                    │
│  [▶] acme-web - Building    [✓] blog - Ready    [✕] api - Error   │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

탭 아이콘 상태:
  ⏳ 대기 중 (회색 원)
  ▶  빌드 중 (애니메이션 스피너)
  ✓  준비 완료 (녹색 체크마크)
  ✕  오류 (빨간 X)

구현 패턴:

// 배포 상태에 따른 동적 파비콘
function updateFavicon(status) {
  const link = document.querySelector("link[rel~='icon']");

  const icons = {
    queued: '/favicon-queued.svg',
    building: '/favicon-building.svg',  // 애니메이션
    ready: '/favicon-ready.svg',
    error: '/favicon-error.svg',
  };

  link.href = icons[status];
}

// 제목도 상태를 반영
function updateTitle(projectName, status) {
  const prefixes = {
    queued: '⏳',
    building: '▶',
    ready: '✓',
    error: '✕',
  };

  document.title = `${prefixes[status]} ${projectName} - Vercel`;
}

핵심 통찰: 개발자들은 많은 탭을 열어둔다. 탭 바에 상태가 표시되면 빌드 상태를 확인하기 위해 탭을 전환할 필요가 없다.


3. 배포 타임라인

배포 인스펙터는 배포 프로세스의 명확한 타임라인을 보여준다.

┌─ 배포 타임라인 ────────────────────────────────────────────────────┐
│                                                                    │
│  [o] 대기 중                                 12:34:56 PM           │
│  │                                                                 │
│  [o] 빌드 중                                 12:34:58 PM           │
│  │ └─ 의존성 설치 중... 3.2s                                       │
│  │ └─ 빌드 중... 12.4s                                             │
│  │ └─ 정적 페이지 생성 중... 2.1s                                  │
│  │                                                                 │
│  [o] 배포 중                                 12:35:14 PM           │
│  │ └─ 빌드 출력 업로드 중...                                       │
│  │                                                                 │
│  [*] 준비 완료                               12:35:18 PM           │
│    └─ https://acme-abc123.vercel.app                               │
│                                                                    │
│  총 소요 시간: 22s                                                 │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

시각적 인코딩:

.timeline-step {
  position: relative;
  padding-left: 24px;
}

.timeline-step::before {
  content: '';
  position: absolute;
  left: 0;
  top: 6px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--step-color);
}

/* 연결선 */
.timeline-step:not(:last-child)::after {
  content: '';
  position: absolute;
  left: 4px;
  top: 16px;
  width: 2px;
  height: calc(100% - 6px);
  background: var(--border-100);
}

/* 단계 상태 */
.timeline-step[data-status="complete"]::before {
  background: var(--color-success);
}

.timeline-step[data-status="active"]::before {
  background: var(--color-warning);
  animation: pulse 1.5s infinite;
}

.timeline-step[data-status="error"]::before {
  background: var(--color-error);
}

.timeline-step[data-status="pending"]::before {
  background: var(--fg-400);
}

4. 로그 뷰어 디자인

Vercel의 로그 뷰어는 별도의 페이지가 아닌 배포 개요에 통합되어 있습니다.

┌─ Build Logs ───────────────────────────────────────────────────────┐
│                                                                    │
│  Filter: [All ▼]  [Function: api/hello ▼]           [Copy] [↓]    │
│                                                                    │
├────────────────────────────────────────────────────────────────────┤
│                                                                    │
│  12:34:58.123  info   Installing dependencies...                   │
│  12:35:01.456  info   added 1234 packages in 3.2s                  │
│  12:35:01.789  info   Running build...                             │
│  12:35:14.012  info   ✓ Compiled successfully                      │
│  12:35:14.234  warn   Large bundle size: pages/index.js (245kb)    │
│  12:35:14.567  info   Generating static pages...                   │
│  12:35:16.890  info   ✓ Generated 42 pages                         │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

주요 기능: - 원클릭 클립보드 복사 - 함수 또는 빌드 출력별 필터링 - 로그 레벨별 색상 구분 (info, warn, error) - 밀리초 단위의 타임스탬프 - 특정 로그 라인에 대한 공유 가능한 URL

구현:

.log-line {
  display: flex;
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.6;
  padding: 2px 12px;
}

.log-line:hover {
  background: var(--bg-200);
}

.log-timestamp {
  color: var(--fg-400);
  min-width: 100px;
  margin-right: 12px;
}

.log-level {
  min-width: 48px;
  margin-right: 12px;
}

.log-level[data-level="info"] { color: var(--fg-300); }
.log-level[data-level="warn"] { color: var(--color-warning); }
.log-level[data-level="error"] { color: var(--color-error); }

.log-message {
  color: var(--fg-100);
  white-space: pre-wrap;
  word-break: break-word;
}

5. 빈 상태

Vercel의 빈 상태는 장식적이지 않고 기능적입니다. 다음에 무엇을 해야 하는지 알려줍니다.

┌─ Empty State: No Deployments ──────────────────────────────────────┐
│                                                                    │
│                                                                    │
│                         No deployments yet                         │
│                                                                    │
│               Push to your repository to create                    │
│                    your first deployment                           │
│                                                                    │
│                                                                    │
│           git push origin main                                     │
│                                                                    │
│                                                                    │
│                         [View Documentation]                       │
│                                                                    │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

디자인 원칙: - 장식적인 일러스트레이션 없음 - 명확한 액션 (git 명령어) - 문서로 연결되는 유용한 링크 - 명령어에 고정폭 폰트 사용 (복사하기 용이)


비주얼 디자인 시스템

타이포그래피 (Geist)

Vercel은 개발자 경험을 위해 Geist를 특별히 제작했습니다:

:root {
  /* Geist Sans - UI 및 본문 텍스트 */
  --font-sans: 'Geist', -apple-system, BlinkMacSystemFont, sans-serif;

  /* Geist Mono - 코드 및 기술 콘텐츠 */
  --font-mono: 'Geist Mono', 'SF Mono', monospace;

  /* 크기 스케일 */
  --text-xs: 12px;
  --text-sm: 13px;
  --text-base: 14px;
  --text-lg: 16px;
  --text-xl: 18px;
  --text-2xl: 24px;

  /* 행간 */
  --leading-tight: 1.25;
  --leading-normal: 1.5;
  --leading-relaxed: 1.75;

  /* 자간 */
  --tracking-tight: -0.02em;
  --tracking-normal: 0;
  --tracking-wide: 0.02em;
}

/* 데이터용 고정폭 숫자 */
.tabular-nums {
  font-variant-numeric: tabular-nums;
}

/* 또는 비교 데이터에 Geist Mono 사용 */
.data-value {
  font-family: var(--font-mono);
}

간격 시스템

:root {
  /* 4px 기본 단위 */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;
}

Border Radius

:root {
  /* 절제되고 일관된 반경 */
  --radius-sm: 4px;
  --radius-md: 6px;
  --radius-lg: 8px;
  --radius-xl: 12px;
  --radius-full: 9999px;
}

애니메이션 패턴

낙관적 업데이트

Vercel은 낙관적 UI 업데이트를 사용합니다. 액션이 즉각적으로 느껴집니다.

// 실시간 업데이트를 위한 SWR 패턴
const { data, mutate } = useSWR('/api/deployments');

async function triggerDeploy() {
  // 즉시 "배포 중" 상태 표시
  mutate(
    { ...data, status: 'building' },
    false  // 아직 재검증하지 않음
  );

  // 그 다음 실제로 트리거
  await fetch('/api/deploy', { method: 'POST' });

  // 실제 상태를 얻기 위해 재검증
  mutate();
}

절제된 로딩 상태

/* 스켈레톤 로딩 - 스피너 없음 */
.skeleton {
  background: linear-gradient(
    90deg,
    var(--bg-200) 0%,
    var(--bg-100) 50%,
    var(--bg-200) 100%
  );
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
  border-radius: var(--radius-md);
}

@keyframes shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

버튼 상태

.button {
  transition: background 100ms ease, transform 100ms ease;
}

.button:hover {
  background: var(--fg-100);
}

.button:active {
  transform: scale(0.98);
}

/* 긴 트랜지션 없음 - 즉각적인 피드백 */

성능 최적화 (디자인 관점)

Vercel의 대시보드 리디자인에는 성능을 개선하는 디자인 결정이 포함되었습니다:

사용된 기법: - API, Assets, Avatar 출처에 대한 preconnect - 중요한 API 호출에 더 높은 브라우저 우선순위 부여 - React 컴포넌트 메모이제이션 (useMemo, useCallback) - ReactDOM.unstable_batchedUpdates로 리렌더링 20% 감소 - 효율적인 실시간 데이터 업데이트를 위한 SWR

핵심 인사이트: 성능이 곧 디자인입니다. 아름다운 애니메이션이 있지만 느린 대시보드보다 애니메이션이 없어도 빠른 대시보드가 낫습니다.


우리 작업을 위한 교훈

1. 다크 모드를 기본으로

사용자가 어두운 환경(터미널, IDE)에서 작업할 때, 다크 모드는 기능이 아니라 배려입니다.

2. 모든 곳에 상태 표시

탭 아이콘, 페이지 제목, 타임라인 인디케이터: 상태는 포커스 없이도 보여야 합니다.

3. 기본적으로 낙관적으로

예상되는 상태를 즉시 보여주세요. 실제 결과는 백그라운드에서 업데이트합니다.

4. 개발자는 기다리는 것을 싫어한다

피할 수 있다면 로딩 스피너를 쓰지 마세요. 스켈레톤 상태, 낙관적 업데이트, 프리페칭을 활용하세요.

5. 빈 상태는 안내다

예쁜 일러스트레이션을 보여주지 마세요. 실행해야 할 명령어를 보여주세요.


자주 묻는 질문

Vercel은 왜 배경에 다크 그레이 대신 순수 블랙(#000000)을 사용하나요?

순수 블랙은 흰색 텍스트와 최대 대비를 제공하여 최적의 가독성을 만듭니다. 또한 개발자들이 이미 사용하는 터미널과 코드 에디터의 미학과 일치하여 대시보드가 워크플로우의 자연스러운 일부처럼 느껴지게 합니다. 다크 그레이 배경은 종종 "부드러워" 보이지만 대비를 줄이고 고해상도 디스플레이에서 탁해 보일 수 있습니다.

Vercel의 탭 상태 인디케이터는 어떻게 작동하나요?

Vercel은 배포 상태에 따라 브라우저 파비콘을 동적으로 업데이트합니다: 빌드 중일 때는 스피너, 준비 완료 시 녹색 체크마크, 에러 시 빨간 X. 페이지 제목도 이모지 접두사(▶, ✓, ✕)로 업데이트됩니다. 이로써 개발자들은 포커스를 전환하지 않고도 여러 탭에서 다수의 배포를 모니터링할 수 있습니다—브라우저 탭 바에서 상태를 한눈에 확인할 수 있습니다.

Vercel의 로딩 상태 접근 방식은 무엇인가요?

Vercel은 전통적인 로딩 스피너 대신 낙관적 UI와 스켈레톤 스크린을 선호합니다. 배포를 트리거하면 서버 확인 전에 UI가 즉시 "빌드 중" 상태를 보여줍니다. SWR 라이브러리가 백그라운드 재검증을 처리합니다. 이로써 네트워크 요청이 200-500ms 걸리더라도 액션이 즉각적으로 느껴집니다.

Geist는 무엇이고 Vercel은 왜 커스텀 서체를 만들었나요?

Geist는 Vercel이 개발자 인터페이스를 위해 특별히 디자인한 서체 패밀리입니다. UI 텍스트용 Geist Sans와 코드용 Geist Mono가 포함됩니다. 대시보드에서 흔한 작은 크기(12-14px)에 최적화되어 있고, 정렬된 데이터 컬럼을 위한 고정폭 숫자가 포함되어 있으며, 유사한 글리프(l, 1, I) 간의 혼동을 방지하는 뚜렷한 문자 형태를 가지고 있습니다.

Vercel은 빈 상태를 다른 제품과 어떻게 다르게 처리하나요?

Vercel의 빈 상태는 장식적인 일러스트레이션이 아닌 실행 가능한 명령어를 보여줍니다. 빈 배포 페이지는 일반적인 "시작하기" 버튼이 달린 만화 대신 고정폭 폰트로 git push origin main을 표시합니다(복사하기 쉽게). 개발자들은 시각적 위안이 아닌 정확히 무엇을 해야 하는지 알고 싶어한다는 철학입니다.