@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)
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개 가구 전체의 색이 자동으로 다시 칠해진다. 가구 하나하나를 손으로 다시 칠할 필요가 없다.
<style> 태그로 주입하는 방식. antd는 이 방식을 이용해 "지금 계산된 토큰 값"을 그대로 CSS로 바꿔 넣는다.React UI 라이브러리는 매년 새로 뜨고 진다. 그런데 antd는 2015년부터 지금까지 계속 실서비스에 쓰이며 살아남았고, 2026년 현재도 활발히 메이저 버전을 올리는(v6) 몇 안 되는 "레퍼런스급" 라이브러리다. 지금 다시 주목할 이유를 셋으로 정리하면:
2026년 기준 최신 정식 버전인 v6는 기술적으로 큰 전환점이다. CSS 변수(Custom Properties) 사용이 기본값이 되었고, 그 대신 더 이상 IE(인터넷 익스플로러)를 지원하지 않는다. React 17 이하 지원도 끊겼다(React 18+ 필수). "레거시를 다 버리고 현대 브라우저 표준에 올라탄" 결단이 이 시점에 나왔다는 것 자체가 주목할 이유다.
antd의 ConfigProvider 테마 시스템은 "토큰 몇 개만 바꾸면 전체가 다시 칠해진다"는 것을 실제로 대규모 프로덕션 스케일에서 증명한 사례다. 최근 디자인 시스템 업계 전반이 토큰 기반 테마화(Design Tokens, W3C 표준화 논의 포함)로 수렴하는 흐름에서, antd의 seed→map→alias 파이프라인은 실전 구현 참고서 역할을 한다.
저장소에는 AGENTS.md·CLAUDE.md는 물론 .claude/skills, .cursor/skills, public/.well-known/agent-skills 같은 AI 코딩 에이전트용 규칙·스킬 디렉터리가 이미 마련돼 있다. 여기에 대화형 AI UI 구성요소를 제공하는 자매 프로젝트 @ant-design/x도 의존성에 포함돼 있어, "AI 에이전트가 직접 코드를 짜고 리뷰하는 시대"에 맞춰 문서·규범을 정비하는 실험을 관찰할 수 있다.
소스 트리에는 컴포넌트 소스·테스트·데모 파일을 합쳐 수천 개의 TypeScript 파일이 있고, 컴포넌트마다 단위 테스트·데모·영어/중국어 문서가 세트로 딸려 있다. 심지어 테스트 러너를 Jest에서 Vitest로 옮기는 vitest.config.ts POC(개념 증명)까지 저장소에 함께 커밋돼 있어, "오래된 프로젝트가 어떻게 점진적으로 현대화되는지" 실물로 볼 수 있다.
기업용(엔터프라이즈) 웹 서비스 — 관리자 페이지, 백오피스, 대시보드, 내부 도구 — 를 만들 때 개발팀은 매번 같은 문제에 부딪힌다. "버튼 색은? 폼 검증 메시지는 어디에? 테이블 정렬 아이콘은?" 이런 자잘한 결정을 프로젝트마다 새로 내리면 시간이 새고, 제품마다 UI가 제각각이 된다.
antd는 이 문제를 "이미 검증된 완성형 컴포넌트 + 일관된 디자인 토큰"으로 해결한다. 개발자는 <Table columns={...} dataSource={...} /> 한 줄로 정렬·필터·페이지네이션이 되는 테이블을 얻고, 디자이너는 토큰 몇 개만 조정해 회사 브랜드에 맞는 색·둥근 정도를 전체 서비스에 일괄 적용한다. "매번 새로 그리기"에서 "토큰만 바꾸기"로 작업 단위를 바꿔준 것이 핵심 가치다.
여기에 국제화(74개 언어 로케일 파일), 접근성(RTL 레이아웃 지원), 서버사이드 렌더링(SSR) 스타일 추출까지 기본 제공되므로, 글로벌 서비스나 사내 다국어 백오피스를 만들 때 "이 부분은 antd가 이미 풀어놨다"고 믿고 넘어갈 수 있는 영역이 넓다.
사무실 인테리어를 할 때 가구를 하나하나 목수에게 주문 제작하는 대신 이케아 카탈로그에서 골라 조립하는 것과 비슷하다. 다만 antd 카탈로그는 "회사 로고 색"만 입력하면 카탈로그 안의 모든 가구가 그 색에 맞춰 다시 인쇄되어 나온다는 점이 다르다.
antd 저장소의 뼈대는 "각 컴포넌트가 자신의 스타일을 테마 엔진에게서 받아온다"는 구조다. 컴포넌트 자체(로직·마크업)와 스타일(토큰 기반 CSS-in-JS)이 물리적으로 분리돼 있고, 그 사이를 theme/ 디렉터리의 토큰 파이프라인이 연결한다.
이 구조가 주는 이득은 명확하다. 토큰 계층(1)과 스타일 생성 계층(2)이 나뉘어 있어서, 새 테마를 추가할 때 컴포넌트 코드를 단 한 줄도 건드리지 않고 algorithm 함수만 바꿔 끼우면 된다. 실제로 antd는 defaultAlgorithm·darkAlgorithm·compactAlgorithm 세 가지를 기본 제공하며, 이들을 배열로 조합해 "다크 + 컴팩트"처럼 겹쳐 쓸 수도 있다.
이 섹션이 이 문서에서 가장 두꺼운 이유는, 이 3단 변환이 antd 전체를 관통하는 단 하나의 설계 원리이기 때문이다. 컴포넌트가 80개든 800개든, 이 파이프라인만 이해하면 "어디를 고치면 무엇이 바뀌는지" 예측할 수 있다. 실제 소스(components/theme/)를 따라가며 단계별로 뜯어본다.
가장 위층은 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,
// ...
};
두 번째 층은 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]처럼 배열로 넘기면 두 변환이 순서대로 겹쳐 적용된다.
마지막 층은 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,
};
컴포넌트 각각의 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 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부터 antd는 기본적으로 CSS 변수 모드(cssVar)를 켠다. 스타일 규칙 자체는 미리 계산해 캐싱하되, 실제 색상 값은 var(--ant-color-primary) 같은 CSS 변수로 참조한다. 그래서 테마를 바꿀 때 전체 스타일시트를 다시 만드는 게 아니라 CSS 변수 값만 갱신하면 되어 훨씬 가볍다.
여기서 한 걸음 더 나아간 것이 zeroRuntime: true 옵션이다(6.0.0 신설). 이걸 켜면 런타임에는 아예 스타일을 생성하지 않고, 미리 빌드된 antd/dist/antd.css를 수동으로 불러와 쓰는 "완전 정적" 모드가 된다. 컴포넌트가 많은 대형 페이지에서 CSS-in-JS의 초기 계산 비용 자체를 없애고 싶을 때 쓰는 탈출구다.
antd의 CSS-in-JS는 해시 기반 캐싱이 핵심이라, 같은 토큰 조합에서는 스타일 계산이 반복되지 않는다. 진짜 성능 이슈는 "테마(토큰)를 렌더링 중에 자주 바꾸는 코드"에서 생긴다 — 예를 들어 리스트 아이템마다 다른 ConfigProvider theme을 씌우면 캐시가 매번 새로 만들어진다. "정적인 테마 + 캐싱"이 기본 전제라는 점을 기억해야 한다.
서버사이드 렌더링에서는 @ant-design/cssinjs의 createCache() + <StyleProvider cache={cache}>로 렌더링 도중 생성된 스타일을 모아, extractStyle(cache)로 문자열을 뽑아 HTML <head>에 직접 삽입한다. 완전 정적 배포용으로는 @ant-design/static-style-extract의 extractStyle()로 런타임 없이 미리 CSS 파일을 뽑아내는 것도 가능하다 — 이게 위 ⑥의 zeroRuntime 모드와 짝을 이루는 도구다.
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-table·rc-select처럼 접두어만 붙였는데, v6에서 @rc-component/table처럼 스코프 패키지로 정리됐다. antd는 이 위에 스타일·테마·한국어 등 로케일을 입힌 "완성본"이다.package.json뿐이다
antd는 자기 버전을 코드에서 components/version/version.ts로 export하는데, 이 파일은 저장소에 커밋돼 있지 않다(.gitignore에 등록). scripts/generate-version.ts가 빌드 시점에 package.json의 version 필드를 읽어 이 파일을 그때그때 생성한다. 즉 "버전의 진짜 출처"는 항상 package.json 하나뿐이다.
# 설치 (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에 미리 심을 수 있음 |
v6은 대부분의 컴포넌트 API 자체는 호환되지만, bordered→variant, dropdownClassName→classNames.popup.root 처럼 수십 개의 prop이 "지원되지만 콘솔에 경고를 내는" 상태로 폐지 예고돼 있다(v7에서 완전 제거 예정). 마이그레이션 전에 공식 Ant Design CLI로 코드베이스를 스캔해 사용 중인 폐지 예정 API를 먼저 확인하는 것이 권장된다.
가장 먼저 하는 일은 브랜드 색과 기본 값을 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>
);
}
버튼·폼·테이블은 이미 정렬·검증·반응형이 내장돼 있다. 아래는 흔한 검색-테이블 화면 골격이다.
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" />
</>
);
}
다크 모드 토글 버튼을 누르면 상태값 하나로 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장에서 본 파이프라인이 바로 여기서 매번 작동하는 것이다.
antd는 ES 모듈(es/) 빌드를 제공해 모던 번들러 + 트리쉐이킹 조합에서는 실제 쓰는 컴포넌트만 번들에 들어간다. 문제는 아이콘이다 — @ant-design/icons에서 아이콘을 통째로 import하면 수백 개 아이콘이 딸려 온다. 개별 아이콘만 골라 import하는 습관이 필요하다.
내부 AGENTS.md 문서는 "국제화(150+ 언어)"라고 적혀 있지만, 실제로 components/locale/ 디렉터리를 세어 보면 실제 언어 파일은 약 74개다(테스트·인덱스 파일 제외). 이런 문서와 실제 코드 수치의 괴리는 큰 오픈소스 프로젝트에서 흔하니, 숫자를 인용해야 할 때는 항상 소스를 직접 세어보는 습관이 안전하다.
v6이 "다음 메이저 버전 예고편" 수준이라고 생각하기 쉽지만, 이 문서 작성 시점 기준 package.json의 버전은 이미 6.5.2이고 master 브랜치 자체가 v6이다. v5 기반 예제·블로그 글을 그대로 따라 하면 bordered, dropdownClassName 같은 폐지 예고 API 경고를 곧바로 만나게 된다.
공식 문서와 API는 영/중 병기지만, 실제 이슈·PR 논의·내부 커밋 메시지에는 중국어가 섞여 있는 경우가 많다(팀 소속이 Ant Group이기 때문). 깊은 논쟁적 이슈나 설계 배경을 온전히 이해하려면 번역기를 함께 쓸 각오가 필요하다.
v4~v5 초기까지는 Less 변수 오버라이드 방식이라 커스터마이징이 번거로웠지만, CSS-in-JS 기반 토큰 시스템으로 넘어온 뒤로는 ConfigProvider의 token/components 옵션만으로 컴포넌트별·트리별 세밀한 오버라이드가 표준 방법이 됐다. "antd는 다 파랗고 각져서 못 쓴다"는 인상은 대부분 v4 이전 경험에서 온 오해다.
| 라이브러리 | 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는 "설계도만 주고 내 집 목수가 직접 짜게 하는 것"에 가깝다. 어느 쪽이 낫냐가 아니라, 팀이 "빠르게 완성"과 "끝까지 커스터마이징" 중 무엇을 더 중요하게 여기는지에 달려 있다.
Ant Design은 "빠르게, 일관되게, 많은 기능을 갖춘 화면을 만들어야 하는" 팀에게 가장 어울린다. 특히 관리자 페이지·내부 도구·데이터 중심 대시보드처럼 디자인에 쓸 시간보다 기능 구현에 쓸 시간이 훨씬 중요한 프로젝트에서 강점이 극대화된다. 반대로 브랜드 아이덴티티가 강한 소비자용 서비스, 완전히 독자적인 시각 언어가 필요한 프로덕트에는 커스터마이징 부담이 상대적으로 크게 느껴질 수 있다.
그리고 antd를 쓰지 않더라도, 5장에서 다룬 seed→map→alias 토큰 파이프라인과 genStyleHooks 캐싱 전략은 어떤 규모의 디자인 시스템을 만들든 참고할 가치가 있는 설계다. "토큰 몇 개로 전체 테마를 바꾸는 시스템을 어떻게 짜는가"라는 질문에 실제 프로덕션 코드로 답을 볼 수 있는 몇 안 되는 저장소다.
테이블·폼·차트성 UI가 반복적으로 필요하고, 브랜드 색만 바꿔 여러 제품 라인에 재사용해야 하는 팀. 74개 로케일과 RTL 지원 덕분에 다국어·다지역 서비스에서 특히 시간을 아낄 수 있다.