왜 Headless UI 라이브러리인가
프론트엔드 개발의 흐름이 커스텀 디자인 시스템 쪽으로 이동하면서, 팀들은 스타일링에 대한 완전한 제어권을 원하면서도 다음을 유지하고 싶어합니다.
- 올바른 접근성
- 예측 가능한 동작
- 크로스 브라우저 일관성
- 조합 가능한 API
Headless UI 라이브러리는 로직, 상태 관리, 접근성, 인터랙션에 집중하고 스타일링은 개발자에게 맡깁니다.
Tailwind CSS, 커스텀 디자인 시스템, shadcn 스타일의 컴포넌트 아키텍처와 특히 잘 어울리는 접근법입니다.
Radix UI와 Base UI의 등장
Radix UI는 다음을 제공하며 인기를 얻었습니다.
- 프로덕션 수준의 컴포넌트
- 강력한 접근성 기본 지원
- 안정적이고 잘 문서화된 API
- 명확한 구조의 최소한 스타일링
Base UI는 프리미티브 우선 접근법을 취합니다.
- 극도로 로우레벨의 빌딩 블록
- DOM 구조에 대한 최대한의 제어
- 레이아웃과 스타일링에 대한 가정 최소화
- 커스텀 컴포넌트 시스템 구축에 최적화
두 라이브러리 모두 비슷한 문제를 풀지만, 추상화 수준, 제어권, 개발자 자유도 측면에서 서로 다른 트레이드오프를 선택합니다.
이 글에서 다루는 것
핵심 질문은 “어느 쪽이 더 좋은가?”가 아니라, “내 프로젝트, 팀, 장기적 목표에 어느 쪽이 맞는가?” 입니다.
이 글에서는 다음을 다룹니다.
- Radix UI와 Base UI의 개념적 차이
- API, 접근성 모델, 개발 경험 비교
- 실제 프로젝트에서의 의사결정 포인트
- 각 라이브러리가 적합한 시나리오
Radix UI란
Radix UI는 React용 오픈소스 Headless UI 컴포넌트 라이브러리입니다.
복잡한 접근성과 인터랙션 세부사항을 직접 다루지 않고도 고품질 UI 컴포넌트를 만들 수 있도록 도와줍니다.
핵심 특징
스타일 미포함
- CSS가 포함되지 않음
- 모든 스타일링을 직접 제어
- Tailwind CSS와 잘 동작
열린 구조와 커스터마이징
- 컴포넌트가 부분(Trigger, Content 등)으로 분리됨
- 컴포넌트를 감싸거나 확장 가능
- 자체 props, 이벤트, ref 추가 가능
기본적으로 비제어 방식
- 상태를 직접 관리할 필요 없음
- 필요하면 제어 방식으로 전환 가능
개발 경험
- TypeScript 타입 완벽 지원
- 컴포넌트 간 일관된 패턴
asChildprop으로 렌더링할 HTML 요소 제어
접근성 접근법
접근성은 Radix UI의 핵심 강점입니다.
- WAI-ARIA 디자인 패턴 준수
- 포커스 트랩 처리 (다이얼로그 내부 등)
- Esc, Tab, 방향키 등 키보드 인터랙션 지원
- 스크린 리더와 잘 동작
트레이드오프는 접근성 로직이 Radix의 컴포넌트 구조에 밀접하게 결합되어 있어, 커스텀 레이아웃을 구현할 때 제약이 될 수 있다는 점입니다.
적합한 사용 사례
복잡한 인터랙션 로직을 직접 구축하지 않고 빠르게 접근성을 갖춘 컴포넌트를 원할 때 적합합니다.
포커스 관리, 키보드 내비게이션, ARIA 패턴을 기본으로 제공합니다.
다이얼로그, 드롭다운, 툴팁 같은 일반적인 UI 패턴에 적합하며, 안정성이 중요한 대시보드나 SaaS 애플리케이션에서 특히 강점을 보입니다.
shadcn/ui + Tailwind CSS 조합으로 프로덕션 수준의 프리미티브를 구축할 때도 좋은 선택입니다.

Base UI란
Base UI는 유연성과 제어에 초점을 맞춘 Headless UI 라이브러리입니다.
Radix UI가 완성된 프리미티브를 제공하는 반면, Base UI는 직접 조합하는 동작 빌딩 블록을 제공합니다.
Base UI의 철학은 “접근성 우선, 구조는 나중에” 입니다.
접근 가능한 인터랙션을 위한 로직은 제공하지만, 마크업과 레이아웃, 스타일링은 개발자에게 맡깁니다.
핵심 특징
로우레벨 프리미티브
- 미리 정의된 레이아웃 없음
- 강제되는 컴포넌트 구조 없음
- 컴포넌트 구성 방식을 직접 결정
유연한 API
initialFocus,finalFocus같은 props- 커스텀 시스템에 쉽게 통합
환경 간 테스트 완료
- 브라우저, 디바이스, 스크린 리더, 플랫폼 전반에서 테스트
컴포넌트 아키텍처
Base UI 컴포넌트는 직접 사용하기보다 조합하도록 설계되었습니다.
완성된 컴포넌트를 import하는 대신 다음과 같은 방식으로 작업합니다.
- Base UI의 동작을 자체 컴포넌트에 부착
- 상태가 어디에 위치할지 직접 결정
- DOM 구조를 완전히 제어
개념적으로 비교하면 이렇습니다.
- Radix UI: “여기 Dialog 구조가 있습니다.”
- Base UI: “여기 dialog 동작이 있습니다 — 원하는 방식으로 적용하세요.”
이런 특성 때문에 커스텀 디자인 시스템을 구축할 때 Base UI가 이상적입니다.
접근성 모델
Base UI는 접근성을 핵심 우선순위로 두면서도 개발자에게 더 많은 제어권을 줍니다.
방향키, Enter, Esc를 이용한 키보드 내비게이션 같은 필수 동작을 처리하고, 포커스를 자동으로 관리하며, 접근 가능한 라벨과 폼 요소를 위한 구조를 제공합니다.
개발자는 포커스 상태(:focus, :focus-visible)의 스타일링, 적절한 색상 대비 유지, 접근 가능한 이름 제공을 직접 책임져야 합니다.
접근성에 대한 더 깊은 이해가 필요하지만, 그만큼 유연성도 높아집니다.
적합한 사용 사례
복사-붙여넣기 가능한 코드 블록을 제공하는 컴포넌트 레지스트리 플랫폼에 적합합니다.
사용자가 마크업과 스타일링을 커스터마이징하는 애니메이션이 풍부한 인터랙티브 컴포넌트에 특히 유용합니다.
이런 시나리오에서는 미리 정의된 구조보다 유연성이 더 중요합니다.
Base UI가 동작과 접근성을 처리하고, 구조와 비주얼 디자인은 전적으로 개발자의 몫입니다.

핵심 차이점 비교 (코드 포함)
설계 철학
Radix UI는 모범 사례를 따르는 미리 정의된 구조를 제공합니다.
Base UI는 동작만 제공하고, 구조는 개발자가 결정합니다.
컴포넌트 추상화 수준
Radix UI (구조화된 컴포넌트)
import * as Dialog from '@radix-ui/react-dialog';
function RadixDialog() {
return (
<Dialog.Root>
<Dialog.Trigger>열기</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="overlay" />
<Dialog.Content className="content">
<Dialog.Title>다이얼로그 제목</Dialog.Title>
<Dialog.Description>다이얼로그 설명</Dialog.Description>
<Dialog.Close>닫기</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}이 방식에서는 다음과 같이 합니다.
- Radix의 컴포넌트 트리를 따름
- 제공된 파트에 스타일을 입힘
- 올바른 동작이 자동으로 적용됨
Base UI (동작 우선)
import { Dialog } from '@base-ui-components/react/dialog';
function BaseDialog() {
return (
<Dialog.Root>
<Dialog.Trigger>열기</Dialog.Trigger>
<Dialog.Popup className="content">
<h2>다이얼로그 제목</h2>
<p>다이얼로그 설명</p>
<button>닫기</button>
</Dialog.Popup>
</Dialog.Root>
);
}이 방식에서는 다음과 같이 합니다.
- 어떤 요소를 사용할지 직접 결정
- 레이아웃과 구조를 직접 구성
- 다이얼로그 동작을 자체 마크업에 부착
스타일링 유연성
Radix UI에서는 스타일링이 자유롭지만 구조는 고정됩니다.
Overlay, Content 등 제공된 파트를 기준으로 스타일을 입힙니다.
<Dialog.Content className="rounded-lg shadow-lg" />Base UI에서는 필수 레이아웃도, 필수 요소 타입도 없습니다.
커스텀 디자인을 맞추기 더 쉽습니다.
<Dialog.Popup className="grid gap-4 rounded-xl shadow-lg" />Base UI에서는 미리 정의된 조각에 제약받지 않습니다.
커스터마이징 제어
Dialog Close 커스터마이징 비교
두 라이브러리 모두 다이얼로그 내부에 닫기 버튼을 렌더링하는 방법을 제공합니다.
차이는 그 동작에 대해 얼마나 많은 제어권을 갖는가입니다.
Radix UI — Boolean 방식
Radix UI에서 닫기는 보통 전용 Dialog.Close 컴포넌트나 단순한 boolean 기반 prop 패턴으로 처리합니다.
<DialogContent close />close는 boolean- true면 기본 닫기 버튼이 렌더링됨
- 설정 옵션이 제한적
- 스타일링은 보통 외부에서 처리
닫기 기능은 의견이 반영된(opinionated), 미리 구조화된 형태로 API 노출 면적이 최소화되어 있습니다.
커스텀 렌더링 로직, 고급 스타일링, 동적 동작이 필요하면 수동으로 별도 로직을 구현해야 합니다.

Base UI — 객체 기반 제어
Base UI에서는 close prop이 객체를 받습니다.
<DialogContent
close={{
nativeButton: true,
className: 'absolute top-4 right-4',
style: { color: 'red' },
render: props => <MyCustomIcon {...props} />,
}}
/>설정 가능한 옵션:
| 속성 | 타입 | 용도 |
|---|---|---|
| nativeButton | boolean | 네이티브 <button> 요소 사용 |
| className | string | function | 스타일 완전 커스터마이징 |
| style | React.CSSProperties | function | 인라인 스타일 제어 |
| render | ReactElement | function | 기본 렌더링 교체 |
비교 요약
| 기능 | Radix UI | Base UI |
|---|---|---|
| 단순 활성화/비활성화 | Boolean | Object |
| 스타일링 제어 | 제한적 | 완전한 제어 |
| 렌더링 교체 | 수동 우회 필요 | 내장 지원 |
| 동적 스타일 로직 | 외부 처리 | 기본 지원 |
| 네이티브 요소 제어 | 간접적 | 명시적 |

실질적 차이
Radix — 닫기 버튼을 활성화합니다. 커스터마이징하려면 추가 구조나 오버라이드가 필요합니다.
Base UI — 닫기 버튼을 설정합니다. API가 처음부터 깊은 커스터마이징을 위해 설계되었습니다.
왜 중요한가
컴포넌트 라이브러리가 성장할수록 이 차이가 드러납니다.
- Boolean API는 간단하지만 경직됨
- 객체 기반 API는 확장성이 더 좋음
- 고급 디자인 시스템은 설정 기반 제어에서 이점을 얻음
Base UI는 추상화 계층을 깨뜨리지 않으면서 개발자에게 제어권을 줍니다.
애니메이션과 트랜지션
Radix UI는 data-state 속성을 사용합니다.
[data-state='open'] {
animation: fadeIn 200ms ease;
}Base UI는 애니메이션에 대한 가정이 없어 어떤 방식이든 호환됩니다.
<Dialog.Popup className="animate-scale-in" />Base UI가 더 적합한 경우:
- 애니메이션이 복잡할 때
- 모션 라이브러리를 사용할 때
- 레이아웃이 동적으로 변할 때
학습 곡선
Radix UI는 시작이 쉽습니다.
명확한 멘탈 모델과 빠른 초기 셋업을 제공합니다.
Base UI는 배우는 데 시간이 더 걸립니다.
시스템 수준의 사고가 필요하지만, 복잡도가 높아질수록 투자한 시간이 빛을 발합니다.
개발 경험 비교
개발 경험은 단순히 문서화나 TypeScript 타입 수준의 문제가 아닙니다.
UI를 만들고 유지보수하면서 느끼는 마찰의 정도가 핵심입니다.
API 설계
Radix UI의 API는 컴포넌트 간에 일관됩니다.
Root, Trigger, Content 같은 예측 가능한 패턴을 따릅니다.
하나의 컴포넌트를 배우면 나머지도 이미 아는 것이나 마찬가지입니다.
“Dialog가 어떻게 동작하는지 알면, Dropdown이나 Tooltip도 이미 아는 겁니다.”
Base UI의 API는 더 유연하고 덜 의견적(opinionated)입니다.
컴포넌트 구조에 대한 가정이 적어, 훅과 프리미티브로 작업하는 느낌에 가깝습니다.
“도구를 받고, 어떻게 사용할지는 내가 결정합니다.”
조합 패턴
Radix UI는 미리 정의된 슬롯을 통한 조합을 권장합니다.
표준 레이아웃에는 잘 동작하지만, 특이한 구조에서는 유연성이 떨어집니다.
<Dialog.Root>
<Dialog.Trigger /> {/* Radix가 제공 */}
<Dialog.Content /> {/* Radix가 제공 */}
</Dialog.Root>Base UI는 자체 컴포넌트를 통한 조합을 권장합니다.
커스텀 레이아웃과 재사용 가능한 블록에 잘 동작하며, 필수 컴포넌트 트리가 없습니다.
<Dialog.Root>
<CustomHeader /> {/* 직접 설계 */}
<CustomContent /> {/* 직접 설계 */}
</Dialog.Root>Base UI는 컴포넌트를 조합하는 방식에서 더 많은 자유를 줍니다.
TypeScript 지원
두 라이브러리 모두 TypeScript를 잘 지원합니다.
Radix UI는 완전히 타입이 지정된 API와 매우 안정적인 자동완성을 제공합니다.
Base UI도 강력한 TypeScript 지원을 제공하되, 커스텀 조합을 지원하기 위해 타입이 더 유연한 편입니다.
대부분의 팀에게 두 라이브러리 모두 탄탄한 TypeScript 경험을 제공합니다.
Tailwind & shadcn 스타일 시스템과의 통합
Radix UI는 Tailwind CSS와 매우 잘 동작하며, shadcn 스타일 컴포넌트에 적합합니다.
구조가 복사-붙여넣기 컴포넌트 패턴에 잘 맞습니다.
Base UI는 컴포넌트 레지스트리 구축에 이상적입니다.
원시 마크업과 로직을 노출하기 쉽고, 애니메이션이 있는 커스터마이징 가능한 코드 블록에 더 적합합니다.
고도로 커스터마이징 가능한 컴포넌트를 사용자가 자기 프로젝트에 맞게 수정하도록 제공하는 것이 목표라면, Base UI가 더 자연스럽게 맞습니다.
개발 경험 요약
| 영역 | Radix UI | Base UI |
|---|---|---|
| 조합 방식 | 슬롯 기반 | 자유 형식 |
| 상태 제어 | 쉬운 기본값 | 명시적 제어 |
| TypeScript | 우수 | 우수 |
| API 스타일 | 일관적, 구조화 | 유연, 로우레벨 |
| 최적 용도 | 앱 개발 | 디자인 시스템 & 레지스트리 |
Radix UI를 선택해야 할 때
Radix UI는 강력한 기본값, 예측 가능한 동작, 바로 사용할 수 있는 접근성이 필요하면서
컴포넌트 로직을 직접 설계하는 데 시간을 쓰고 싶지 않을 때 가장 잘 맞습니다.
적합한 프로젝트 시나리오
다음과 같은 경우 Radix UI를 강력히 고려하세요.
바로 사용 가능한 접근성이 필요한 경우 — Radix가 키보드 내비게이션, 포커스 관리, ARIA 역할을 처리합니다.
접근성 전문가가 아니어도 올바른 컴포넌트를 만들 수 있습니다.
UI 시스템이 아닌 제품 UI를 만드는 경우 — 대시보드, 관리자 패널, SaaS 앱, 내부 도구에서 빠르게 진행할 수 있습니다.
검증된 프로덕션 수준의 프리미티브가 필요한 경우 — Radix는 많은 실제 프로젝트에서 사용되었고,
엣지 케이스가 이미 처리되어 있습니다.
명확하고 안정적인 API를 선호하는 팀 — 컴포넌트 API가 일관되고 갑자기 변경될 가능성이 낮습니다.
Tailwind나 shadcn 스타일 컴포넌트와 조합하는 경우 — Radix는 스타일이 입혀진 컴포넌트 뒤의 “로직 레이어”로서 매우 잘 동작합니다.
적합한 프로젝트 예시: SaaS 대시보드, 관리자 패널, 내부 도구, MVP와 스타트업 제품, 안정성 우선의 디자인 시스템
장단점
장점:
- 바로 사용 가능한 강력한 접근성
- 프로덕션 수준의 컴포넌트
- 일관되고 예측 가능한 API
- 우수한 TypeScript 지원
- Tailwind CSS와 탁월한 호환
- 큰 커뮤니티와 생태계
단점:
- 다소 의견적인(opinionated) 구조
- DOM 마크업에 대한 제어가 적음
- 커스텀 레이아웃에서 제한적
- 추상화 레이어가 더 많음
한 마디로
“제품을 빠르고 안전하게 만든다”가 목표라면, Radix UI가 보통 더 나은 선택입니다.
좋은 기본값, 적은 의사결정, 적은 버그를 제공합니다.
Base UI를 선택해야 할 때
Base UI는 모든 프로젝트에 최적인 선택이 되려고 하지 않습니다.
유연성, 제어, 커스터마이징이 완성된 동작보다 중요할 때 빛을 발합니다.
적합한 프로젝트 시나리오
다음을 구축한다면 Base UI를 선택하세요.
1. 컴포넌트 레지스트리 플랫폼
복사-붙여넣기 가능한 UI 컴포넌트를 제공하고, API만이 아니라 전체 컴포넌트 코드를 공유하며, 개발자가 구조와 로직과 스타일을 편집하길 기대하는 플랫폼.
Base UI는 무거운 추상화 뒤에 로직을 숨기지 않기 때문에 이런 용도에 잘 맞습니다.
2. 여러 레지스트리의 UI 블록을 사용하는 웹사이트
다양한 레지스트리에서 UI 블록을 가져오고, 오픈소스 라이브러리에서 컴포넌트를 복사하며, 레이아웃과 섹션과 인터랙션을 혼합하고, 서드파티 코드를 빠르게 적용해야 하는 경우.
Base UI는 라이브러리와 싸우지 않고 복사한 코드를 완전히 제어할 수 있게 합니다.
엄격한 컴포넌트 API, 숨겨진 내부 로직, 의견적인 구조에 갇히지 않습니다.
대신 평범하고 이해하기 쉬운 React 코드를 얻습니다.
3. 높은 커스터마이징이 필요한 디자인 시스템
디자인 시스템이 자주 변하고, 프로젝트마다 마크업과 동작을 커스터마이징하며, 경직된 컴포넌트가 아닌 일관된 패턴을 원하는 경우.
Base UI는 시스템이 진화하면서 컴포넌트를 재구성할 자유를 줍니다.
4. 애니메이션 중심 인터페이스
애니메이션이 브랜드 아이덴티티의 일부이고, Framer Motion이나 CSS 애니메이션을 사용하며, 트랜지션과 타이밍에 대한 완전한 제어가 필요한 경우.
Base UI는 애니메이션을 컴포넌트 로직 바깥에 두어, 본래 있어야 할 위치에 놓습니다.
5. 코드에 대한 소유권을 원하는 팀
UI 코드의 모든 줄을 이해하고 싶고, 설정보다 조합을 선호하며, 벤더 종속을 원하지 않고, 장기 유지보수성을 중시하는 팀에 적합합니다.
한 마디로
레지스트리에서 UI 블록을 가져와 사용하면서 완전한 제어가 필요하다면, Base UI가 더 나은 기반입니다.
장단점
장점:
- DOM 구조에 대한 최대한의 제어
- 컴포넌트 레지스트리에 적합
- 유연한 조합 기반 API
- 애니메이션과 잘 동작
- 벤더 종속 없음
- 커스터마이징 가능한 시스템에 이상적
단점:
- 가파른 학습 곡선
- 개발자 책임이 더 큼
- 안내가 적음
- 접근성에 대한 인식 필요
- 초기 셋업이 더 많음
마무리
Radix UI와 Base UI는 모두 모던 React 인터페이스를 구축하기 위한 강력한 도구이지만, 컴포넌트 설계에 대한 접근법이 다릅니다.
Radix UI는 잘 테스트된 프리미티브에 강력한 접근성과 구조화된 API를 제공하여, 안정적인 애플리케이션 인터페이스를 구축하는 데 좋은 선택입니다.
Base UI는 유연성에 초점을 맞춰 컴포넌트 구조, 로직, 동작을 수정할 수 있는 더 큰 자유를 개발자에게 줍니다.
올바른 선택은 결국 UI를 어떻게 만들고 싶은지, 그리고 기반 코드에 대한 제어를 얼마나 원하는지에 달려 있습니다.
결국 가장 좋은 도구는 자신의 워크플로우에 맞고,
더 나은 인터페이스를 만드는 데 도움이 되는 것입니다.