본문으로 건너뛰기
5분 AI.
REPO DEEP DIVE · 2026-07-29 · ANT-DESIGN/ANT-DESIGN · 기업용 React UI의 표준 교과서

ant-design/ant-design 딥다이브
— "디자인 토큰 3단 변환"으로 배우는 대규모 CSS-in-JS 아키텍처

UI 라이브러리

빠른 판단

무엇
테이블·폼 등 React 컴포넌트와 디자인 토큰·테마 엔진을 제공하는 UI 라이브러리
이럴 때
관리자·업무용 화면을 조립하며 브랜드 테마와 폼 검증·데이터 표시 패턴을 공통으로 적용할 때
Ant Design(줄여서 antd)은 알리바바 계열 Ant Group이 만든 엔터프라이즈급 React UI 컴포넌트 라이브러리다. 버튼 하나부터 대형 데이터 테이블·폼·날짜 선택기까지 약 80개의 완성형 컴포넌트를 제공하며, 전 세계에서 가장 널리 쓰이는 React 디자인 시스템 중 하나다.

이 문서가 파고드는 핵심은 "디자인 토큰이 seed → map → alias 3단계를 거쳐 실제 CSS로 바뀌는 과정"이다. antd는 색상 하나, 여백 하나를 @ant-design/cssinjs 기반 런타임 CSS-in-JS 엔진으로 계산해 뿌리는데, 이 파이프라인을 이해하면 "테마 하나로 라이트/다크/컴팩트 모드를 동시에 지원"하는 원리가 보인다.

(저장소 ant-design/ant-design · 제작 Ant Design 팀(Ant Group) · 라이선스 MIT · 버전 6.5.2(2026년 v6 정식 릴리스 이후) · React 19 지원(peer는 React ≥18) · CSS-in-JS 엔진 @ant-design/cssinjs · 빌드 father 4 · 컴포넌트 약 80개 · 로케일(언어) 파일 약 74개 · 최신 커밋 4924d63, 2026-07-29)
목차
  1. 프로젝트 한 줄 정체
  2. 왜 지금 앤트디자인인가
  3. 무엇을 해결하는 라이브러리인가
  4. 전체 구조 한눈에 보기
  5. 핵심 설계·동작 원리 — 디자인 토큰 3단 변환
  6. 기술 스택과 의존성 지도
  7. 설치와 요구사항
  8. 실제 사용 흐름 — 코드로 보는 하루
  9. 함정과 오해 바로잡기
  10. 대안 비교 — MUI·Chakra·Mantine·shadcn/ui
  11. 정리 — 누가 쓰면 좋은가
  12. 직접 해볼 수 있는 실습 과제
  13. 관련 기술 심화 학습 로드맵
  14. 핵심 키워드 사전

1프로젝트 한 줄 정체

"레고 블록처럼 조립하는 완성형 React UI 컴포넌트 세트" + "그 블록을 색칠하는 디자인 토큰 엔진"

Ant Design은 두 겹으로 이루어져 있다. 겉으로 보이는 것은 <Button>, <Table>, <DatePicker> 같은 즉시 쓸 수 있는 완성형 컴포넌트 약 80개다. 그 밑을 받치는 것은 디자인 토큰(Design Token) 시스템으로, 색상·간격·둥근 정도 같은 값 하나하나를 "토큰"으로 관리하고 @ant-design/cssinjs라는 런타임 CSS-in-JS 엔진으로 실제 스타일을 생성한다.

단순한 컴포넌트 모음이 아니라 "디자인 언어(Design Language) + 구현체"라는 게 antd의 원래 정체성이다. 실제로 리포지토리 이름의 기원도 "Ant Design"이라는 디자인 스펙 문서였고, 이 저장소는 그 스펙을 React로 구현한 reference implementation이다. 그래서 컴포넌트 API뿐 아니라 테마·다크모드·RTL(오른쪽에서 왼쪽 언어)·SSR·국제화까지 한 몸에 갖췄다.

한 줄 비유

"완성된 가구 세트"이자 "그 가구를 원하는 색으로 다시 칠하는 도료 공장"

antd를 쓴다는 것은 이미 조립된 가구(컴포넌트)를 받는 것과 같다. 버튼도, 테이블도, 폼도 이미 다 만들어져 있어 그대로 갖다 쓰면 된다.

그런데 이 가구들은 전부 "토큰 공장(theme 시스템)"에서 나오는 도료로 마감된다. 공장 설정(seed 토큰)을 하나 바꾸면 — 예를 들어 "브랜드 색을 파랑에서 초록으로" — 80개 가구 전체의 색이 자동으로 다시 칠해진다. 가구 하나하나를 손으로 다시 칠할 필요가 없다.

용어
디자인 토큰 (Design Token)
"주 색상은 #1677ff", "기본 여백은 8px" 처럼 디자인 결정을 이름 붙인 변수로 저장한 것. 색을 코드 곳곳에 직접 적는 대신 토큰 하나만 바꾸면 전체 디자인이 일괄로 바뀐다.
용어
CSS-in-JS
CSS 파일을 따로 두지 않고 자바스크립트 코드 안에서 스타일을 계산해 브라우저에 <style> 태그로 주입하는 방식. antd는 이 방식을 이용해 "지금 계산된 토큰 값"을 그대로 CSS로 바꿔 넣는다.

2왜 지금 앤트디자인인가

10년 넘게 살아남은 "레퍼런스급" 디자인 시스템 · v6 CSS 변수 시대 · AI 코딩 에이전트 친화 실험장

React UI 라이브러리는 매년 새로 뜨고 진다. 그런데 antd는 2015년부터 지금까지 계속 실서비스에 쓰이며 살아남았고, 2026년 현재도 활발히 메이저 버전을 올리는(v6) 몇 안 되는 "레퍼런스급" 라이브러리다. 지금 다시 주목할 이유를 셋으로 정리하면:

① v6 — "CSS 변수 시대"로 정식 전환

2026년 기준 최신 정식 버전인 v6는 기술적으로 큰 전환점이다. CSS 변수(Custom Properties) 사용이 기본값이 되었고, 그 대신 더 이상 IE(인터넷 익스플로러)를 지원하지 않는다. React 17 이하 지원도 끊겼다(React 18+ 필수). "레거시를 다 버리고 현대 브라우저 표준에 올라탄" 결단이 이 시점에 나왔다는 것 자체가 주목할 이유다.

② "토큰 하나로 라이트/다크/컴팩트를 동시에" — 테마 엔진의 정석

antd의 ConfigProvider 테마 시스템은 "토큰 몇 개만 바꾸면 전체가 다시 칠해진다"는 것을 실제로 대규모 프로덕션 스케일에서 증명한 사례다. 최근 디자인 시스템 업계 전반이 토큰 기반 테마화(Design Tokens, W3C 표준화 논의 포함)로 수렴하는 흐름에서, antd의 seed→map→alias 파이프라인은 실전 구현 참고서 역할을 한다.

③ AI 코딩 에이전트에 발 빠르게 대응

저장소에는 AGENTS.md·CLAUDE.md는 물론 .claude/skills, .cursor/skills, public/.well-known/agent-skills 같은 AI 코딩 에이전트용 규칙·스킬 디렉터리가 이미 마련돼 있다. 여기에 대화형 AI UI 구성요소를 제공하는 자매 프로젝트 @ant-design/x도 의존성에 포함돼 있어, "AI 에이전트가 직접 코드를 짜고 리뷰하는 시대"에 맞춰 문서·규범을 정비하는 실험을 관찰할 수 있다.

규모가 주는 신뢰
80개 컴포넌트 · 74개 로케일 · Jest→Vitest 전환 진행 중

소스 트리에는 컴포넌트 소스·테스트·데모 파일을 합쳐 수천 개의 TypeScript 파일이 있고, 컴포넌트마다 단위 테스트·데모·영어/중국어 문서가 세트로 딸려 있다. 심지어 테스트 러너를 Jest에서 Vitest로 옮기는 vitest.config.ts POC(개념 증명)까지 저장소에 함께 커밋돼 있어, "오래된 프로젝트가 어떻게 점진적으로 현대화되는지" 실물로 볼 수 있다.

3무엇을 해결하는 라이브러리인가

"매번 버튼·테이블·폼을 새로 디자인·구현하지 말라" + "팀 전체가 하나의 시각 언어를 쓰게 하라"

기업용(엔터프라이즈) 웹 서비스 — 관리자 페이지, 백오피스, 대시보드, 내부 도구 — 를 만들 때 개발팀은 매번 같은 문제에 부딪힌다. "버튼 색은? 폼 검증 메시지는 어디에? 테이블 정렬 아이콘은?" 이런 자잘한 결정을 프로젝트마다 새로 내리면 시간이 새고, 제품마다 UI가 제각각이 된다.

antd는 이 문제를 "이미 검증된 완성형 컴포넌트 + 일관된 디자인 토큰"으로 해결한다. 개발자는 <Table columns={...} dataSource={...} /> 한 줄로 정렬·필터·페이지네이션이 되는 테이블을 얻고, 디자이너는 토큰 몇 개만 조정해 회사 브랜드에 맞는 색·둥근 정도를 전체 서비스에 일괄 적용한다. "매번 새로 그리기"에서 "토큰만 바꾸기"로 작업 단위를 바꿔준 것이 핵심 가치다.

여기에 국제화(74개 언어 로케일 파일), 접근성(RTL 레이아웃 지원), 서버사이드 렌더링(SSR) 스타일 추출까지 기본 제공되므로, 글로벌 서비스나 사내 다국어 백오피스를 만들 때 "이 부분은 antd가 이미 풀어놨다"고 믿고 넘어갈 수 있는 영역이 넓다.

비유

사무실 인테리어를 할 때 가구를 하나하나 목수에게 주문 제작하는 대신 이케아 카탈로그에서 골라 조립하는 것과 비슷하다. 다만 antd 카탈로그는 "회사 로고 색"만 입력하면 카탈로그 안의 모든 가구가 그 색에 맞춰 다시 인쇄되어 나온다는 점이 다르다.

4전체 구조 한눈에 보기

"컴포넌트(components/*) ← 디자인 토큰 엔진(theme/) ← CSS-in-JS 런타임(@ant-design/cssinjs)" 3계층

antd 저장소의 뼈대는 "각 컴포넌트가 자신의 스타일을 테마 엔진에게서 받아온다"는 구조다. 컴포넌트 자체(로직·마크업)와 스타일(토큰 기반 CSS-in-JS)이 물리적으로 분리돼 있고, 그 사이를 theme/ 디렉터리의 토큰 파이프라인이 연결한다.

┌────────────────────────────────────────────────────────────────┐ │ ANT DESIGN 전체 아키텍처 (단순화) │ └────────────────────────────────────────────────────────────────┘ [0] ConfigProvider (앱 최상단) theme={{ token, algorithm, components, cssVar }} · 앱 전체에 "지금 쓸 토큰 설정"을 React Context로 뿌린다 │ ▼ [1] theme/ — 디자인 토큰 파이프라인 (components/theme) seedToken (디자이너가 정하는 몇 개의 "씨앗" 값) │ derivative() — algorithm 함수 적용 ▼ MapToken (씨앗에서 파생된 팔레트 10단계 + 크기 체계) │ formatToken() — 개발자 편의용 별칭 부여 ▼ AliasToken (colorTextDisabled 처럼 "쓰임새 이름"이 붙은 최종 토큰) │ ▼ [2] 각 컴포넌트의 style/index.ts genStyleHooks('Button', token => [ ...CSSObject 스타일 ] ) · AliasToken + 컴포넌트별 ComponentToken 을 받아 스타일 객체 생성 │ ▼ [3] @ant-design/cssinjs (런타임 엔진) useStyleRegister — 스타일 객체를 해시로 캐싱하고 <style> 태그로 <head>에 주입 (한 번 계산되면 재사용) │ ▼ [4] 실제 DOM에 렌더링된 <button class="ant-btn ant-btn-css-var-abc123">

이 구조가 주는 이득은 명확하다. 토큰 계층(1)과 스타일 생성 계층(2)이 나뉘어 있어서, 새 테마를 추가할 때 컴포넌트 코드를 단 한 줄도 건드리지 않고 algorithm 함수만 바꿔 끼우면 된다. 실제로 antd는 defaultAlgorithm·darkAlgorithm·compactAlgorithm 세 가지를 기본 제공하며, 이들을 배열로 조합해 "다크 + 컴팩트"처럼 겹쳐 쓸 수도 있다.

5핵심 설계·동작 원리 — 디자인 토큰 3단 변환

seed → map(algorithm) → alias, 그리고 이를 실제 CSS로 굽는 genStyleHooks + cssVar 모드

이 섹션이 이 문서에서 가장 두꺼운 이유는, 이 3단 변환이 antd 전체를 관통하는 단 하나의 설계 원리이기 때문이다. 컴포넌트가 80개든 800개든, 이 파이프라인만 이해하면 "어디를 고치면 무엇이 바뀌는지" 예측할 수 있다. 실제 소스(components/theme/)를 따라가며 단계별로 뜯어본다.

① Seed Token — "디자이너가 정하는 씨앗 값"

가장 위층은 components/theme/themes/seed.ts에 정의된 SeedToken이다. colorPrimary(주 색상), fontSize(기본 글자 크기), borderRadius(둥근 정도), colorError 같은 30여 개의 "원시 값"만 담고 있다. 이것이 테마 커스터마이징의 유일한 입구다 — 나머지는 전부 이 씨앗에서 계산돼 나온다.

// components/theme/themes/seed.ts (요지)
export const defaultPresetColors: PresetColorType = {
  blue: '#1677ff', purple: '#722ed1', cyan: '#13c2c2',
  green: '#52c41a', magenta: '#eb2f96', red: '#f5222d', /* ... */
};
const seedToken: SeedToken = {
  ...defaultPresetColors,
  colorPrimary: '#1677ff',
  colorError: '#ff4d4f',
  fontSize: 14,
  borderRadius: 6,
  // ...
};

② Map Token — algorithm 함수가 씨앗을 팔레트로 "파생"시킨다

두 번째 층은 derivative(seedToken) 함수, 즉 algorithm이다. components/theme/themes/default/index.ts를 보면 colorPrimary 하나에서 @ant-design/colors의 generate()를 이용해 10단계 색상 팔레트(blue-1 ~ blue-10)를 만들고, 글꼴 크기·여백·컨트롤 높이 체계까지 함께 계산한다. 결과가 MapToken이다.

// components/theme/themes/default/index.ts (요지)
export default function derivative(token: SeedToken): MapToken {
  const colorPalettes = /* colorPrimary → blue-1..blue-10 등 팔레트 생성 */;
  return {
    ...token,
    ...colorPalettes,
    ...genColorMapToken(token, { generateColorPalettes, generateNeutralColorPalettes }),
    ...genFontMapToken(token.fontSize),   // fontSizeSM/LG/XL... 자동 생성
    ...genSizeMapToken(token),             // sizeXXS ~ sizeXXL 자동 생성
    ...genControlHeight(token),
    ...genCommonMapToken(token),
  };
}

antd는 이 algorithm 함수를 3종 기본 제공한다. defaultAlgorithm(밝은 테마), darkAlgorithm(어두운 팔레트로 파생), compactAlgorithm(같은 색은 유지하되 크기·여백만 줄인 파생). ConfigProvider에 algorithm: [theme.darkAlgorithm, theme.compactAlgorithm]처럼 배열로 넘기면 두 변환이 순서대로 겹쳐 적용된다.

③ Alias Token — "개발자가 실제로 쓰는 이름"으로 재포장

마지막 층은 components/theme/util/alias.ts의 formatToken()이다. colorFillContent, colorTextDisabled, controlItemBgHover처럼 "이 값이 어디 쓰이는지" 이름에 드러나는 별칭을 MapToken 값에서 다시 계산해 붙인다. 예를 들어 colorTextDisabled는 colorTextQuaternary를 그대로 가리키는 별칭이다 — 실제 색상 계산은 이미 끝났고, 이 층은 "의미 있는 이름표"만 다는 단계다.

// components/theme/util/alias.ts (요지 발췌)
const aliasToken: AliasToken = {
  ...mergedToken,
  colorTextDisabled: mergedToken.colorTextQuaternary,
  colorTextHeading: mergedToken.colorText,
  controlItemBgHover: mergedToken.colorFillTertiary,
  controlItemBgActive: mergedToken.colorPrimaryBg,
  lineWidthFocus: mergedToken.lineWidth * 3,
  // override(사용자가 ConfigProvider로 넘긴 커스텀 값)가 최종적으로 이 위에 덮어써진다
  ...overrideTokens,
};
용어
seed → map → alias
seed는 사람이 정하는 소수의 원시 값, map은 그 값에서 자동 파생된 팔레트·크기 체계, alias는 "쓰임새 이름"이 붙은 최종 토큰. 셋을 나눠두면 "적게 입력하고 많이 얻는" 테마 시스템이 가능해진다.

④ genStyleHooks — 토큰을 실제 CSS 객체로 굽는 마지막 공정

컴포넌트 각각의 style/index.ts는 위에서 완성된 AliasToken을 받아 genStyleHooks('Button', token => [...]) 형태로 스타일을 등록한다. genStyleHooks·genComponentStyleHook·genSubStyleComponent는 antd가 자체 구현한 것이 아니라 공식 유틸 패키지 @ant-design/cssinjs-utils에서 가져와, 컴포넌트별 프리픽스·CSP nonce·해시 처리를 표준화한다.

// components/button/style/index.ts (요지)
export default genStyleHooks(
  'Button',
  (token) => {
    const buttonToken = prepareToken(token);
    return [
      genSharedButtonStyle(buttonToken),
      genSizeBaseButtonStyle(buttonToken),
      genVariantStyle(buttonToken),
      genGroupStyle(buttonToken),
    ];
  },
);

이 함수가 반환한 CSS 객체는 내부적으로 @ant-design/cssinjs의 useStyleRegister가 해시로 캐싱한다. 즉 같은 토큰 조합으로 같은 컴포넌트를 100번 렌더링해도 스타일 계산과 <style> 태그 삽입은 최초 1회만 일어난다 — "런타임 CSS-in-JS는 무조건 느리다"는 통념을 정면으로 반박하는 캐싱 전략이다.

⑤ ConfigProvider — 토큰 설정을 트리 전체에 주입

이 모든 것을 코드 한 곳에서 켜는 진입점이 <ConfigProvider theme={...}>다. 내부적으로 algorithm을 createTheme()으로 감싸고, 사용자가 넘긴 token·components(컴포넌트별 오버라이드)를 DesignTokenContext.Provider로 하위 트리 전체에 뿌린다. 중첩된 ConfigProvider는 부모 테마를 이어받아 부분만 덮어쓸 수 있다.

import { ConfigProvider, theme } from 'antd';

function App() {
  return (
    <ConfigProvider
      theme={{
        algorithm: theme.darkAlgorithm,     // 다크 모드
        token: { colorPrimary: '#00b96b' }, // 브랜드 색만 교체
        components: { Button: { borderRadius: 2 } }, // 버튼만 각지게
      }}
    >
      <YourApp />
    </ConfigProvider>
  );
}

⑥ v6의 두 가지 렌더링 모드 — CSS 변수 vs 완전 정적(zero-runtime)

v6부터 antd는 기본적으로 CSS 변수 모드(cssVar)를 켠다. 스타일 규칙 자체는 미리 계산해 캐싱하되, 실제 색상 값은 var(--ant-color-primary) 같은 CSS 변수로 참조한다. 그래서 테마를 바꿀 때 전체 스타일시트를 다시 만드는 게 아니라 CSS 변수 값만 갱신하면 되어 훨씬 가볍다.

여기서 한 걸음 더 나아간 것이 zeroRuntime: true 옵션이다(6.0.0 신설). 이걸 켜면 런타임에는 아예 스타일을 생성하지 않고, 미리 빌드된 antd/dist/antd.css를 수동으로 불러와 쓰는 "완전 정적" 모드가 된다. 컴포넌트가 많은 대형 페이지에서 CSS-in-JS의 초기 계산 비용 자체를 없애고 싶을 때 쓰는 탈출구다.

함정
"CSS-in-JS라서 무조건 느리다"는 절반만 맞는 말

antd의 CSS-in-JS는 해시 기반 캐싱이 핵심이라, 같은 토큰 조합에서는 스타일 계산이 반복되지 않는다. 진짜 성능 이슈는 "테마(토큰)를 렌더링 중에 자주 바꾸는 코드"에서 생긴다 — 예를 들어 리스트 아이템마다 다른 ConfigProvider theme을 씌우면 캐시가 매번 새로 만들어진다. "정적인 테마 + 캐싱"이 기본 전제라는 점을 기억해야 한다.

⑦ SSR과 스타일 추출 — 서버에서 미리 계산해 넘기기

서버사이드 렌더링에서는 @ant-design/cssinjs의 createCache() + <StyleProvider cache={cache}>로 렌더링 도중 생성된 스타일을 모아, extractStyle(cache)로 문자열을 뽑아 HTML <head>에 직접 삽입한다. 완전 정적 배포용으로는 @ant-design/static-style-extract의 extractStyle()로 런타임 없이 미리 CSS 파일을 뽑아내는 것도 가능하다 — 이게 위 ⑥의 zeroRuntime 모드와 짝을 이루는 도구다.

6기술 스택과 의존성 지도

React 19 · @ant-design/cssinjs · @rc-component/* 저수준 패키지군 · father 빌드 · biome+eslint 이중 린트

antd는 "자체 저수준 컴포넌트(headless) 패키지군 위에 완성형 컴포넌트를 얹는" 2층 생태계를 갖고 있다. 실제 package.json의 dependencies 47개 중 절반 가까이가 @rc-component/* 스코프 패키지다.

영역실제 사용 패키지 / 버전
UI 프레임워크React 19.x(내부 개발) · peerDependencies는 react/react-dom >= 18.0.0(하위 호환)
CSS-in-JS 엔진@ant-design/cssinjs ^2.1.2 (런타임 스타일 등록·캐싱·SSR 추출) + @ant-design/cssinjs-utils ^2.1.2 (genStyleHooks 등 헬퍼)
색상 계산@ant-design/colors ^8, @ant-design/fast-color ^3 (팔레트 10단계 생성·색상 연산)
아이콘@ant-design/icons ^6 — v6과 버전이 강하게 결합(icons@6은 antd@5와 호환 안 됨)
저수준 헤드리스 컴포넌트@rc-component/table·select·picker·menu·dialog·trigger·tree 등 30개 이상 — v6에서 옛 rc-* 네이밍을 @rc-component/* 스코프로 통합
날짜/시간dayjs ^1.11 (moment.js 대신 경량 날짜 라이브러리 채택)
빌드 도구father ~4.6 (umi 팀의 라이브러리 번들러) + 내부 antd-tools 스크립트 모음
린트/포맷eslint 10 + @antfu/eslint-config 와 biome 2.x 를 병행 도입 중(lint:script = eslint, lint:biome = biome)
테스트jest(기본, .jest.js) — 동시에 vitest 4 로 옮기는 POC가 vitest.config.ts에 커밋돼 있음(test:vitest 스크립트)
문서 사이트dumi(umi 기반 문서 프레임워크) · 각 컴포넌트는 index.en-US.md/index.zh-CN.md 쌍으로 문서화
용어
rc-* / @rc-component/*
antd의 각 컴포넌트를 실제로 구현하는 "스타일 없는(headless)" 저수준 React 패키지군. 예전에는 rc-table·rc-select처럼 접두어만 붙였는데, v6에서 @rc-component/table처럼 스코프 패키지로 정리됐다. antd는 이 위에 스타일·테마·한국어 등 로케일을 입힌 "완성본"이다.

버전의 진실원(source of truth)은 package.json뿐이다

antd는 자기 버전을 코드에서 components/version/version.ts로 export하는데, 이 파일은 저장소에 커밋돼 있지 않다(.gitignore에 등록). scripts/generate-version.ts가 빌드 시점에 package.json의 version 필드를 읽어 이 파일을 그때그때 생성한다. 즉 "버전의 진짜 출처"는 항상 package.json 하나뿐이다.

7설치와 요구사항

React 18+ 필수 · 아이콘 패키지 버전 동기화 · 모던 브라우저 전제(IE 지원 종료)
# 설치 (npm / yarn / pnpm 모두 지원)
npm install antd
# 아이콘을 쓴다면 반드시 짝이 맞는 메이저 버전으로 함께 설치
npm install @ant-design/icons@6
항목요구사항
React 버전18.0.0 이상 필수(v6부터 17 이하 지원 종료). React 19 사용 시 @ant-design/v5-patch-for-react-19 패치 패키지는 더 이상 필요 없음(제거 권장)
아이콘 패키지@ant-design/icons는 antd와 같은 메이저 버전으로 맞춰야 함 — icons@6은 antd@5와 호환되지 않음
브라우저CSS 변수를 기본 사용하는 모던 브라우저 전제. IE는 더 이상 지원되지 않음. browserslist: ["defaults"]
번들러Vite·webpack·Next.js 등 대부분의 현대 번들러와 호환. CSS-in-JS라 별도 CSS 전처리 설정이 필수는 아님
스타일 계산 없이 쓰기ConfigProvider theme={{ zeroRuntime: true }} + import 'antd/dist/antd.css' 조합으로 런타임 스타일 계산을 완전히 끌 수 있음
서버사이드 렌더링@ant-design/cssinjs의 createCache/StyleProvider/extractStyle로 서버에서 만들어진 스타일을 HTML에 미리 심을 수 있음
주의
v5에서 그대로 올라오면 콘솔 경고가 쏟아진다

v6은 대부분의 컴포넌트 API 자체는 호환되지만, bordered→variant, dropdownClassName→classNames.popup.root 처럼 수십 개의 prop이 "지원되지만 콘솔에 경고를 내는" 상태로 폐지 예고돼 있다(v7에서 완전 제거 예정). 마이그레이션 전에 공식 Ant Design CLI로 코드베이스를 스캔해 사용 중인 폐지 예정 API를 먼저 확인하는 것이 권장된다.

8실제 사용 흐름 — 코드로 보는 하루

설치 → ConfigProvider로 테마 걸기 → 컴포넌트 조립 → 폼 검증 → 다크모드 토글
단계 1

앱 최상단에 테마를 건다

가장 먼저 하는 일은 브랜드 색과 기본 값을 ConfigProvider에 한 번 설정하는 것이다. 이 설정은 하위 트리 전체에 자동으로 상속된다.

import { ConfigProvider } from 'antd';
import koKR from 'antd/locale/ko_KR';

export default function Root() {
  return (
    <ConfigProvider
      locale={koKR}
      theme={{ token: { colorPrimary: '#1677ff', borderRadius: 8 } }}
    >
      <App />
    </ConfigProvider>
  );
}
단계 2

완성형 컴포넌트를 조립한다 — 폼 + 테이블

버튼·폼·테이블은 이미 정렬·검증·반응형이 내장돼 있다. 아래는 흔한 검색-테이블 화면 골격이다.

import { Button, Form, Input, Table } from 'antd';

function UserSearch() {
  const [form] = Form.useForm();
  const columns = [
    { title: '이름', dataIndex: 'name' },
    { title: '이메일', dataIndex: 'email' },
  ];
  return (
    <>
      <Form form={form} layout="inline" onFinish={(v) => search(v)}>
        <Form.Item name="keyword" rules={[{ required: true }]}>
          <Input placeholder="이름으로 검색" />
        </Form.Item>
        <Button type="primary" htmlType="submit">검색</Button>
      </Form>
      <Table columns={columns} dataSource={users} rowKey="id" />
    </>
  );
}
단계 3

다크 모드는 algorithm 하나만 스위치

다크 모드 토글 버튼을 누르면 상태값 하나로 algorithm을 바꿔치기하면 끝이다 — 컴포넌트 코드는 전혀 건드리지 않는다.

import { ConfigProvider, theme } from 'antd';

function ThemedApp() {
  const [isDark, setIsDark] = useState(false);
  return (
    <ConfigProvider
      theme={{ algorithm: isDark ? theme.darkAlgorithm : theme.defaultAlgorithm }}
    >
      <App />
    </ConfigProvider>
  );
}

이 한 줄의 스위치 뒤에서 seed → map → alias 파이프라인 전체가 다시 계산되고, @ant-design/cssinjs가 새 해시로 스타일을 다시 캐싱한다 — 5장에서 본 파이프라인이 바로 여기서 매번 작동하는 것이다.

9함정과 오해 바로잡기

번들 크기 · "무조건 느리다" 통념 · 문서-코드 불일치 · 중국어 우선 개발 문화
함정 ①
"antd 하나 설치하면 번들이 커진다"는 부분적으로만 사실

antd는 ES 모듈(es/) 빌드를 제공해 모던 번들러 + 트리쉐이킹 조합에서는 실제 쓰는 컴포넌트만 번들에 들어간다. 문제는 아이콘이다 — @ant-design/icons에서 아이콘을 통째로 import하면 수백 개 아이콘이 딸려 온다. 개별 아이콘만 골라 import하는 습관이 필요하다.

함정 ②
문서의 "150+ 언어" ≠ 실제 로케일 파일 수

내부 AGENTS.md 문서는 "국제화(150+ 언어)"라고 적혀 있지만, 실제로 components/locale/ 디렉터리를 세어 보면 실제 언어 파일은 약 74개다(테스트·인덱스 파일 제외). 이런 문서와 실제 코드 수치의 괴리는 큰 오픈소스 프로젝트에서 흔하니, 숫자를 인용해야 할 때는 항상 소스를 직접 세어보는 습관이 안전하다.

함정 ③
"v6은 아직 준비 중"이 아니라 이미 정식 출시됐다

v6이 "다음 메이저 버전 예고편" 수준이라고 생각하기 쉽지만, 이 문서 작성 시점 기준 package.json의 버전은 이미 6.5.2이고 master 브랜치 자체가 v6이다. v5 기반 예제·블로그 글을 그대로 따라 하면 bordered, dropdownClassName 같은 폐지 예고 API 경고를 곧바로 만나게 된다.

함정 ④
중국어 우선 개발 문화 — 이슈·커밋의 실제 소통 언어

공식 문서와 API는 영/중 병기지만, 실제 이슈·PR 논의·내부 커밋 메시지에는 중국어가 섞여 있는 경우가 많다(팀 소속이 Ant Group이기 때문). 깊은 논쟁적 이슈나 설계 배경을 온전히 이해하려면 번역기를 함께 쓸 각오가 필요하다.

오해 바로잡기
"antd는 커스터마이징이 어렵다"는 옛말

v4~v5 초기까지는 Less 변수 오버라이드 방식이라 커스터마이징이 번거로웠지만, CSS-in-JS 기반 토큰 시스템으로 넘어온 뒤로는 ConfigProvider의 token/components 옵션만으로 컴포넌트별·트리별 세밀한 오버라이드가 표준 방법이 됐다. "antd는 다 파랗고 각져서 못 쓴다"는 인상은 대부분 v4 이전 경험에서 온 오해다.

10대안 비교 — MUI·Chakra·Mantine·shadcn/ui

"완성형 컴포넌트"라는 antd의 좌표를 다른 라이브러리들과 비교
라이브러리antd와 비교했을 때
MUI(Material UI)구글 Material 디자인을 따르는 가장 큰 경쟁자. 컴포넌트 수·생태계 규모는 비슷하나, MUI는 sx prop과 Emotion 기반, antd는 토큰 3단 변환 + ConfigProvider 기반이라 커스터마이징 진입점이 다르다. antd는 "엔터프라이즈 백오피스", MUI는 "범용 웹앱"에 더 자주 선택된다.
Chakra UI더 작고 유연한 스타일 프롭 중심 라이브러리. 완성도 높은 대형 컴포넌트(복잡한 Table·Form)는 antd가 훨씬 많고 무겁게 갖춰져 있는 반면, Chakra는 가볍게 처음부터 직접 조립하는 데 강하다.
Mantine비교적 최근 부상한 올인원 라이브러리로 훅(hooks) 생태계가 풍부하다. 컴포넌트 개수·완성도는 antd에 근접하지만, 엔터프라이즈 데이터 테이블·폼 검증의 성숙도는 antd가 오랜 실전 경험만큼 앞서는 편이다.
shadcn/ui완전히 다른 철학 — 라이브러리가 아니라 "복사해서 내 코드로 만드는" 컴포넌트 템플릿이다(Radix + Tailwind 기반). antd처럼 "설치 후 즉시 완성형" 접근과 정반대로, "내 코드베이스 안에서 자유롭게 뜯어고치고 싶다"는 팀에 맞는다.
비유

antd·MUI·Mantine은 "완제품 가구 브랜드"이고, Chakra는 "조립 부품이 많은 반제품 브랜드", shadcn/ui는 "설계도만 주고 내 집 목수가 직접 짜게 하는 것"에 가깝다. 어느 쪽이 낫냐가 아니라, 팀이 "빠르게 완성"과 "끝까지 커스터마이징" 중 무엇을 더 중요하게 여기는지에 달려 있다.

11정리 — 누가 쓰면 좋은가

엔터프라이즈 백오피스·대시보드 팀에게 가장 큰 이득 · 토큰 시스템은 모든 프론트엔드 개발자에게 좋은 공부거리

Ant Design은 "빠르게, 일관되게, 많은 기능을 갖춘 화면을 만들어야 하는" 팀에게 가장 어울린다. 특히 관리자 페이지·내부 도구·데이터 중심 대시보드처럼 디자인에 쓸 시간보다 기능 구현에 쓸 시간이 훨씬 중요한 프로젝트에서 강점이 극대화된다. 반대로 브랜드 아이덴티티가 강한 소비자용 서비스, 완전히 독자적인 시각 언어가 필요한 프로덕트에는 커스터마이징 부담이 상대적으로 크게 느껴질 수 있다.

그리고 antd를 쓰지 않더라도, 5장에서 다룬 seed→map→alias 토큰 파이프라인과 genStyleHooks 캐싱 전략은 어떤 규모의 디자인 시스템을 만들든 참고할 가치가 있는 설계다. "토큰 몇 개로 전체 테마를 바꾸는 시스템을 어떻게 짜는가"라는 질문에 실제 프로덕션 코드로 답을 볼 수 있는 몇 안 되는 저장소다.

이런 팀에 특히 잘 맞는다
사내 어드민 · 데이터 대시보드 · 다국어 B2B SaaS

테이블·폼·차트성 UI가 반복적으로 필요하고, 브랜드 색만 바꿔 여러 제품 라인에 재사용해야 하는 팀. 74개 로케일과 RTL 지원 덕분에 다국어·다지역 서비스에서 특히 시간을 아낄 수 있다.

12직접 해볼 수 있는 실습 과제

버튼의 상태 처리부터 폼 검증·토큰 변환·테마 상속까지, 소스와 테스트로 확인하기

경로와 명령은 모두 ant-design 저장소 루트를 기준으로 한다. 첫 과제는 소스만 읽어도 수행할 수 있다. 테스트를 추가하는 나머지 과제는 의존성이 준비된 별도 작업 사본에서 진행한다. npm test는 사전 단계에서 scripts/generate-version.ts를 실행해 버전 파일을 생성하므로, 읽기 전용 원본에서 실행하지 않는다.

과제 1 · 입문

버튼 하나의 사용법·구현·스타일을 연결한다

목표: 화면에서 쓰는 프롭이 어느 파일에서 동작과 모양으로 나뉘어 처리되는지 찾는다.

  1. components/button/demo/basic.tsx·disabled.tsx·loading.tsx를 읽고, 기본 상태·비활성 상태·로딩 상태의 입력값을 적는다. 뒤의 두 파일도 같은 demo/ 폴더에 있다.
  2. 아래 검색으로 내보내기 진입점과 구현을 찾는다. components/button/Button.tsx의 handleClick에서 어떤 조건이 사용자 콜백 호출을 막는지 확인한다.
  3. useStyle이 연결하는 components/button/style/index.ts를 읽고, 이벤트 처리와 스타일 생성이 서로 다른 파일에 놓이는 이유를 설명한다.
rg -n 'Button' components/button/index.tsx
rg -n 'handleClick|innerLoading|mergedDisabled|useStyle' components/button/Button.tsx
rg -n 'genStyleHooks|prepareToken' components/button/style/index.ts

완료 기준: 세 상태별로 “사용한 프롭 → 클릭 처리 조건 → 근거 파일”을 정리한다. 특히 로딩 중 클릭이 막히는 이유를 handleClick의 분기로 설명할 수 있어야 한다.

과제 2 · 중급

필수 입력과 초기화를 폼 테스트로 검증한다

목표: 입력값 저장, 검증, 제출 콜백, 초기화가 이어지는 순서를 이해한다.

  1. components/form/demo/basic.tsx에서 rules·onFinish·onFinishFailed를 읽고, components/form/demo/control-hooks.tsx에서 Form.useForm()·resetFields()의 사용법을 확인한다.
  2. 작업 사본의 components/form/__tests__/index.test.tsx에 “필수 입력과 초기화를 확인한다”라는 테스트를 추가한다. 초기값이 없는 username 필드와 제출 버튼을 만들고, 성공·실패 콜백은 jest.fn()으로 기록한다. 폼 참조는 React.createRef<FormInstance>()로 받는다.
  3. 빈 폼 제출 → 이름 입력 후 제출 → resetFields() 호출 순으로 실행한다. 기존 테스트가 사용하는 fireEvent·act·waitFakeTimer를 따라 비동기 검증과 상태 갱신을 기다린다.

필드의 핵심 설정은 다음처럼 작성한다. name은 저장할 값의 이름이고, rules는 값이 통과해야 할 조건이다.

<Form.Item
  name="username"
  label="이름"
  rules={[{ required: true, message: '이름을 입력하세요' }]}
>
  <Input />
</Form.Item>
npm test -- components/form/__tests__/index.test.tsx --runInBand -t '필수 입력과 초기화'

완료 기준: 빈 값에서는 실패 콜백이 호출되고 성공 콜백은 호출되지 않는다. 이름을 입력하면 성공 콜백이 해당 값을 받으며, 초기화 뒤 getFieldValue('username')은 undefined가 된다.

과제 3 · 중급

같은 씨앗을 서로 다른 테마로 변환한다

목표: 색을 직접 지정하는 일과 알고리즘으로 크기·팔레트를 파생하는 일을 구분한다.

  1. components/theme/getDesignToken.ts에서 씨앗 기본값과 사용자 설정을 합치는 부분을 읽는다. 이어 components/theme/themes/compact/index.ts와 components/theme/util/alias.ts에서 높이 계산과 별칭 연결을 찾는다.
  2. 작업 사본의 components/theme/__tests__/token.test.tsx에 아래 테스트를 추가한다. 이 파일에 이미 있는 theme 가져오기를 그대로 사용한다.
  3. 기본·조밀한 테마·어둡고 조밀한 테마의 colorBgContainer와 controlHeight를 표로 적고, 각 차이를 만든 알고리즘 파일을 표시한다.
it('토큰 변환을 비교한다', () => {
  const config = { token: { colorPrimary: '#00b96b' } };
  const normal = theme.getDesignToken(config);
  const compact = theme.getDesignToken({
    ...config,
    algorithm: theme.compactAlgorithm,
  });
  const darkCompact = theme.getDesignToken({
    ...config,
    algorithm: [theme.darkAlgorithm, theme.compactAlgorithm],
  });

  expect(normal.colorPrimary).toBe('#00b96b');
  expect(compact.controlHeight).toBeLessThan(normal.controlHeight);
  expect(darkCompact.colorBgContainer).not.toBe(compact.colorBgContainer);
  expect(normal.colorTextDisabled).toBe(normal.colorTextQuaternary);
});
npm test -- components/theme/__tests__/token.test.tsx --runInBand -t '토큰 변환을 비교한다'

완료 기준: 테스트가 통과하고, 조밀한 테마의 높이가 줄어드는 이유와 colorTextDisabled가 별칭인 이유를 소스로 설명한다. 이 과제는 토큰 계산을 확인하므로 실제 버튼의 배치나 시각적 품질은 별도 확인 대상이다.

과제 4 · 심화

중첩 테마가 적용되는 범위를 회귀 테스트로 고정한다

목표: 부모 설정의 상속, 자식의 덮어쓰기, 형제 영역의 독립성을 구분한다.

  1. components/config-provider/hooks/useTheme.ts에서 부모·자식의 token을 합치는 순서와 inherit: false 분기를 읽는다.
  2. 작업 사본의 components/config-provider/__tests__/theme.test.tsx에 “중첩 테마의 범위를 확인한다”라는 테스트를 추가한다. theme.useToken()으로 colorPrimary와 borderRadius를 기록하고 null을 반환하는 관찰용 컴포넌트를 만든다.
  3. 바깥 ConfigProvider에는 주 색상 '#00b96b'와 둥근 정도 10을 지정한다. 그 안에 둥근 정도만 2로 바꾼 자식 영역과, 그 자식 영역 바깥의 형제 영역을 각각 두고 값을 기록한다.
  4. 바깥 설정 아래에 inherit: false와 둥근 정도 2를 지정한 영역도 만든다. 이 영역의 주 색상이 theme.getDesignToken().colorPrimary와 같은지 검증한다.
npm test -- components/config-provider/__tests__/theme.test.tsx --runInBand -t '중첩 테마의 범위'

완료 기준: 바깥·자식·형제는 지정한 주 색상을 공유하고, 둥근 정도는 각각 10·2·10이다. 상속을 끈 영역은 기본 주 색상과 지정한 둥근 정도 2를 사용한다. 결과를 검사할 때 자동 생성되는 해시 문자열 대신 관찰한 토큰 값을 비교한다.

13관련 기술 심화 학습 로드맵

6주 동안 사용 예제 → 상태 관리 → 토큰 엔진 → 스타일 생성 → 검증 도구로 범위를 넓히기

처음부터 모든 컴포넌트를 읽기보다 버튼과 폼을 기준점으로 삼는다. 매주 읽은 파일의 역할을 이전 주의 결과물에 연결하면, 파일 목록이 실제 동작 경로로 바뀐다.

주차배울 개념과 읽을 소스결과물
1주차 React 프롭과 TypeScript 타입. components/button/demo/basic.tsx → components/button/index.tsx → components/button/Button.tsx 순으로 읽는다. tsconfig.json의 paths에서 antd가 로컬 소스로 연결되는 방식도 확인한다. 버튼의 사용 예제·공개 진입점·구현을 연결한 경로 지도와 과제 1의 상태 비교표.
2주차 폼 상태와 비동기 검증. components/form/Form.tsx·components/form/FormItem/index.tsx·components/form/hooks/useForm.ts를 읽고, 값과 검증을 맡는 @rc-component/form과 antd가 추가하는 화면·설정 처리를 구분한다. 입력 → 검증 → 성공 또는 실패 콜백의 흐름도와 과제 2의 제출·초기화 테스트.
3주차 씨앗·파생값·별칭의 분리. components/theme/themes/seed.ts·components/theme/themes/default/index.ts·components/theme/util/alias.ts·components/theme/getDesignToken.ts를 읽는다. 입력 토큰과 파생 토큰을 구분한 표. 과제 3에서 얻은 테마별 배경·높이 비교와 근거 함수.
4주차 React 콘텍스트와 테마 상속. components/config-provider/context.ts·components/config-provider/hooks/useTheme.ts·components/theme/useToken.ts를 읽는다. components/config-provider/__tests__/theme.test.tsx의 상속·CSS 변수 사례로 연결한다. 부모·자식·형제별 설정 범위도와 과제 4의 회귀 테스트.
5주차 컴포넌트 토큰과 스타일 생성. components/button/style/token.ts·components/button/style/index.ts·components/theme/util/genStyleUtils.ts를 읽고, 공통 토큰이 버튼 전용 값과 스타일 규칙으로 바뀌는 연결을 찾는다. 버튼의 높이 또는 그림자 하나를 골라 “공통 입력 → 전용 값 → 스타일 사용처”를 표시한 추적 메모.
6주차 라이브러리의 테스트·문서·빌드 환경. package.json·.jest.js·.dumirc.ts·.fatherrc.ts를 읽는다. 테스트 경로 별칭, 문서 예제의 소스 연결, 배포 빌드 설정을 서로 비교한다. 사용한 테스트 명령, 사전 생성 단계, 소스 진입점, 빌드 산출 위치를 담은 실행 안내서.

마지막 주에는 docs/react/customize-theme.en-US.md와 docs/react/contributing.en-US.md를 읽으며 자신의 설명을 대조한다. 토큰 계산 테스트, 컴포넌트 동작 테스트, 실제 화면 확인이 각각 무엇을 검증하는지 나눠 적으면 다음 컴포넌트를 공부할 때도 같은 절차를 재사용할 수 있다.

14핵심 키워드 사전

사용할 때 만나는 이름과 소스에서 만나는 이름을 같은 뜻으로 연결하기
프롭 — props
부모가 컴포넌트에 건네는 입력값으로, 기기의 설정 버튼과 비슷하다. 이 레포에서는 components/button/Button.tsx가 disabled·loading·onClick 같은 값을 받아 버튼의 상태와 동작을 결정한다.
콘텍스트 — React Context
여러 층 아래의 컴포넌트에 공통 설정을 전달하는 통로로, 층마다 전달자를 따로 두지 않는 공용 안내판과 비슷하다. 이 레포의 components/theme/context.ts는 디자인 토큰 설정을 공유할 콘텍스트를 정의한다.
설정 공급자 — ConfigProvider
자신 아래의 컴포넌트에 테마·언어·크기 같은 공통 설정을 공급하는 컴포넌트다. components/config-provider/에서 구현하며, 중첩하면 특정 영역의 설정을 바꿀 수 있다.
디자인 토큰
색상·글자 크기·간격 같은 디자인 결정을 이름으로 저장한 값이다. 이 레포에서는 colorPrimary나 borderRadius를 설정하고, components/theme/의 계산을 거쳐 여러 컴포넌트가 사용할 값을 얻는다.
씨앗 토큰 — SeedToken
여러 파생값을 만들 때 출발점으로 삼는 기본 재료다. components/theme/themes/seed.ts에는 주 색상·기본 글자 크기·기본 높이 등의 기본값이 들어 있다.
파생 토큰 — MapToken
씨앗에 알고리즘을 적용해 얻는 색상·크기 체계로, 기본 재료에서 만든 여러 중간 재료에 해당한다. components/theme/themes/default/index.ts가 색상 팔레트와 글자 크기·높이 계산 결과를 합쳐 만든다.
별칭 토큰 — AliasToken
계산된 값에 용도를 드러내는 이름을 붙이거나 추가로 가공한 토큰이다. components/theme/util/alias.ts에서 colorTextDisabled를 colorTextQuaternary에 연결하는 것이 한 예다.
컴포넌트 토큰
버튼 그림자처럼 특정 컴포넌트에 필요한 전용 설정값이다. 버튼의 primaryShadow는 components/button/style/token.ts에서 확인할 수 있고, ConfigProvider의 theme.components.Button으로 바꿀 수 있다.
테마 알고리즘 — algorithm
같은 씨앗으로 밝은 테마·어두운 테마·조밀한 테마의 파생값을 만드는 계산 규칙이다. components/theme/index.tsx가 defaultAlgorithm·darkAlgorithm·compactAlgorithm을 공개하며 배열로 조합할 수도 있다.
CSS 변수 — cssVar
스타일 규칙에서 실제 값을 이름으로 참조하는 브라우저 기능으로, var(--ant-color-primary)처럼 사용한다. 이 레포의 components/theme/useToken.ts와 components/config-provider/hooks/useTheme.ts에서 토큰의 변수 처리와 설정 병합을 확인할 수 있다.
자바스크립트 기반 스타일 — CSS-in-JS
자바스크립트에서 스타일 객체와 규칙을 만드는 방식이다. 이 레포는 컴포넌트의 style/에서 토큰을 사용해 스타일을 구성하고, @ant-design/cssinjs와 관련 유틸에 스타일 등록 처리를 맡긴다.
스타일 훅 생성 도구 — genStyleHooks
컴포넌트 이름과 토큰 기반 스타일 함수를 연결해 스타일 훅을 만드는 공통 도구다. components/theme/util/genStyleUtils.ts가 관련 유틸을 설정하고, components/button/style/index.ts가 이를 사용해 버튼 스타일을 구성한다.
로케일
언어와 지역에 맞는 기본 문구 등의 설정을 묶은 것으로, 컴포넌트용 번역 꾸러미에 가깝다. components/locale/ko_KR.ts에는 한국어 기본 문구가 있으며, ConfigProvider의 locale로 전달해 사용한다.
회귀 테스트
코드를 바꾼 뒤에도 이전에 보장하던 동작이 유지되는지 확인하는 테스트다. 이 레포의 components/button/__tests__/index.test.tsx는 로딩 중 클릭 처리 같은 동작을 검증하며, 실습에서도 제출과 테마 상속의 규칙을 테스트로 남긴다.