개요
최근에 저와 팀은 차세대 프로젝트를 준비 중이에요. 팀에서 개발하고 있는 웹 에디터는 레거시한 아키텍처와 ES5로 이루어져 있어 유지보수와 새로운 기능을 넣기에는 한계적인 순간이 많았어요. 현재는 IE11를 완전히 지원하지 않아 ES6를 사용할 수 있지만 구조적인 부분에서 개선이 되지 않았기 때문에 아쉬울 수밖에 없어요. 그래서 저희는 차세대 웹 에디터를 개발하고자 팀 내 목표를 수립했습니다. 저는 개발 단계 수립 중에서 UI 라이브러리 및 프레임워크에 대해서 조사를 맡았는데요. 조사를 하던 가운데 자세히 알지 못했던 개념인 Headless UI의 정의와 사용하는 이유에 대해서 공유를 해보고자 합니다.
정의
Headless UI란 비즈니스 로직과 접근성(a11y) 기능은 제공하지만, 시각적인 스타일은 전혀 포함하지 않는 컴포넌트 설계 방식을 말한다. 즉, Head(=시각적인 부분)이 제거되어 있고 개발자는 UI의 모양을 직접 입히는 역할만 담당한다.
왜 Headless 구조를 사용할까?
- 디자인 완전 제어
: 프레임워크나 CSS 시스템에 구애받지 않고 자유롭게 스타일링 가능 - 접근선 보장
: 포커스, 키보드 네비게이션, Aria 속성 등을 자동으로 처리 - 재사용성과 유지보수성 향상
: 시각적 UI를 바꿔도 로직은 그대로 유지 - 디자인 시스템 확장에 유리
: 조직의 테마, 다크 모드, 브랜딩 변화에 유연하고 강함
예시 비교
일반 UI 라이브러리 ( ex, MUI)
import { Dialog, DialogTitle, DialogContent, Button } from '@mui/material';
function Example() {
return (
<Dialog open>
<DialogTitle>Settings</DialogTitle>
<DialogContent>
<Button>Save</Button>
</DialogContent>
</Dialog>
);
}
- 이미 디자인, 위치, 애니메이션, 배경 색상이 정해져 있음
- 커스터마이징을 하기 위해서는 sx나 style로 오버라이드를 해야 함
- 로직이 숨겨져 있음
Headless 방식 ( ex, Radix UI)
import * as Dialog from '@radix-ui/react-dialog';
function Example() {
return (
<Dialog.Root>
<Dialog.Trigger asChild>
<button className="btn-primary">Open Dialog</button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-black/50" />
<Dialog.Content className="bg-white rounded-lg p-6 w-[400px]">
<Dialog.Title className="text-xl font-semibold mb-4">Settings</Dialog.Title>
<Dialog.Description className="text-gray-500 mb-4">
Manage your preferences here.
</Dialog.Description>
<button className="btn">Save</button>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}
- 키보드 접근성, focus 관리, portial 및 aria-label 자동 처리
- 색상, 여백, 디자인은 전혀 포함되지 않았기 때문에 직접 스타일링
시각적 구조 요약
[MUI, Chakra, Mantine 등 일반 UI]
├── 로직 (내부에 있음)
└── 스타일 (내장됨)
[Headless UI (Radix, HeadlessUI, Ark)]
├── 로직 (제공)
└── 스타일 (직접 구현)
실제 구조 예시
[Radix UI] ← Headless 로직 제공
↓
[shadcn/ui] ← Tailwind 스타일 적용 + 재구성
↓
[내 프로젝트] ← 커스터마이징 및 디자인 시스템 적용
요약 및 정리
Headless UI는 "UI의 로직과 구조는 제공하지만, 스타일은 전혀 제공하지 않는 UI 엔진"이라고 볼 수 있습니다. 개발자가 디자인의 모든 부분을 제어할 수 있고 접근성과 로직을 신경 쓰지 않아도 되기 때문에 개발의 효율성이 높아진다는 장점이 있습니다. 저희 팀은 빠르게 프로토타입을 개발해야하기 때문에 Headless UI를 적용하기로 결정했습니다. 추가적으로 도움이 될 만한 내용이 있다면 포스팅하도록 하겠습니다.
'개발' 카테고리의 다른 글
| npm에서 pmpm으로 마이그레이션 하기 (0) | 2025.09.19 |
|---|---|
| 왜 npm 대신 pnpm을 사용하게 됐을까? npm <-> pnpm 알아보기 (2) | 2025.09.11 |
| ProseMirror 뜯어보기 - prosemirror-model의 fragment.ts (1) | 2025.08.18 |
| ProseMirror 뜯어보기 - prosemirror-model의 node.ts (3) | 2025.08.12 |
| 자바스크립트 환경에서 모바일 대응하기 (2) | 2025.06.17 |