공부/Html & CSS

Headless UI 공부하면서 정리한 것들

dev_jiwonpark 2025. 12. 21. 16:26

디자이너 없이 개발을 하려고할때 주로 UI 라이브러리를 써볼까? 하는 생각이 든다.

그래서 이런 저런 UI 라이브러리를 사용하다보면 항상 부딪히는 문제가 있다.

프로젝트에 맞게 스타일을 바꾸려고 하면 생각보다 손이 많이 간다는 것이다ㅠㅠ

그런데 최근에 Headless UI를 알게 됐는데, 꽤 괜찮아서 공부해본것들을 정리해보려고 한다.


https://headlessui.com/

 

Headless UI

Completely unstyled, fully accessible UI components, designed to integrate beautifully with Tailwind CSS.

headlessui.com

우선 Headless UI 공식 홈페이지를 가면 아래와 같이 설명하고 있다.

"Completely unstyled, fully accessible UI components, designed to integrate beautifully with Tailwind CSS."

 

한줄로 정리하면, 스타일은 아예 없고 접근성은 완벽하게 갖춘 컴포넌트인데 Tailwind CSS랑 잘어울리게 만들었다는 것이다.

여기서 핵심은 "unstyled"이다. 기존 UI 라이브러리들은 기본 스타일이 같이 오기 마련이라 그 점이 참 편하기는 한데

우리 프로젝트에 맞게 바꾸려면 그 스타일을 덮어써야 해서 참 귀찮아진다..

 

Headless UI는 아예 스타일이 없어 그런 문제가 없어진다! 처음부터 원하는대로 만들면 되기 때문이다.

그리고 "fully accessible"도 중요한 포인트이다. 

키보드 네비게이션, 스크린 리더 지원 같은 접근성 기능도 직접 구현하려면 손이 많이 가게되는데 ..

이런 것들도 Headless UI가 알아서 처리해주기 때문에 굉장히 편리하게 UI 개발을 할수 있게 된다.

 

그럼 어떤 컴포넌트들이 있나?

Headless UI는 React, Vue 둘 다 지원한다.

제공하는 컴포넌트가 많지는 않지만 자주 쓰이는 것들 위주로 있다.

크게 일반 UI 랑 Form 관련으로 나뉘는데

일반 UI로는 

 

  • Menu - 드롭다운 메뉴
  • Dialog - 모달창
  • Disclosure - 아코디언, 펼치기/접기
  • Popover - 팝오버
  • Tabs - 탭
  • Transition - 트랜지션/애니메이션

그리고 Form 관련으로는 아래와 같다.

 

  • Button
  • Checkbox
  • Input
  • Textarea
  • Select
  • Listbox - 커스텀 셀렉트
  • Combobox - 검색 가능한 셀렉트
  • Radio Group
  • Switch - 토글
  • Fieldset

Form 관련 컴포넌트가 생각보다 많은데 그냥 <input> 이나 <button> 쓰면 되는거 아닌가? 싶지만 접근성 속성들이

붙기 때문에 신경쓸 게 줄어든다. 

 

실제로 써보면 어떤지 살펴보자!

Dialog(모달) 컴포넌트로 예제를 살펴보면 아래와 같다.

import { useState } from 'react'
import { Dialog, DialogPanel, DialogTitle, Description } from '@headlessui/react'

function Example() {
  let [isOpen, setIsOpen] = useState(false)

  return (
    <>
      <button onClick={() => setIsOpen(true)}>Open dialog</button>
      <Dialog open={isOpen} onClose={() => setIsOpen(false)} className="relative z-50">
        <div className="fixed inset-0 flex w-screen items-center justify-center p-4">
          <DialogPanel className="max-w-lg space-y-4 border bg-white p-12">
            <DialogTitle className="font-bold">Deactivate account</DialogTitle>
            <Description>This will permanently deactivate your account</Description>
            <p>Are you sure you want to deactivate your account? All of your data will be permanently removed.</p>
            <div className="flex gap-4">
              <button onClick={() => setIsOpen(false)}>Cancel</button>
              <button onClick={() => setIsOpen(false)}>Deactivate</button>
            </div>
          </DialogPanel>
        </div>
      </Dialog>
    </>
  )
}

 

useState로 열림/닫힘 상태를 관리하고, open 과 onClose이 props로 Dialog에 전달된다.

스타일은 Tailwind 클래스로 넣으면 된다. 

또는 배경 오버레이가 필요하면 DialogBackdrop 컴포넌트를 추가하면 된다.

import { Dialog, DialogBackdrop, DialogPanel, DialogTitle } from '@headlessui/react'

<Dialog open={isOpen} onClose={() => setIsOpen(false)} className="relative z-50">
  <DialogBackdrop className="fixed inset-0 bg-black/30" />
  <div className="fixed inset-0 flex w-screen items-center justify-center p-4">
    <DialogPanel className="max-w-lg space-y-4 bg-white p-12">
      {/* ... */}
    </DialogPanel>
  </div>
</Dialog>

 

여기서 눈여겨볼 점은 접근성 관련 기능들이 자동으로 처리된다는 것이다.

 

  • ESC 누르면 닫힘
  • DialogPanel 밖 클릭하면 닫힘
  • 모달 열리면 포커스가 안으로 이동하고 Tab으로 순환
  • 모달 외부 요소는 자동으로 비활성화(inert)
  • 스크롤 잠금 처리
  • Portal로 렌더링되어 DOM 최상위에 위치

이런걸 직접 구현하려면 꽤 손이 가게 되는데 Headless UI가 알아서 해준다는 것이다..!

이전에 모달 컴포넌트를 직접 구현해보면서 접근성 관련해 신경써야할것들이 많아서 구현하기 까다로웠었는데 

Headless UI는 이를 해결할수 있다..!

 

Headless UI의 장단점

써보면서 느낀 장단점을 정리해보자면..

장점으론 

- 스타일이 없어서 커스텀이 자유로움! 기존 디자인 시스템에 맞추기 너무 편하다.

- 접근성이 자동으로 처리됨.

- Tailwind CSS와 궁합이 좋음.

- 가벼움, 필요한 컴포넌트만 import 해서 쓰면 됨.

 

하지만 단점으로는..

- 기본 스타일이 없으니 직접 다 스타일을 작성해줘야함.

- 디자인을 알아서 해야함. 예쁜 UI를 원하면 본인이 직접 만들어야함.

- 컴포넌트 종류가 많지 않음. 복잡한 컴포넌트가 필요하다면 다른 라이브러리를 찾아서 개발해야함.

 

스타일이 없다는 것이 큰 장점이지만 반대로 스타일이 없어서 직접 신경써줘야하는 부분들이 많아 불편한 점도 있었다.

때문에 이를 도입하기 전에 각자 팀의 상황을 잘 고려해서 도입해야할것 같다는 생각이 들었다.

예를 들어

현재 디자이너가 없고 기존 디자인 시스템이 있지 않지만 ..!  디자인이 중요한 프로젝트일때는 

Headless UI가 맞지 않을 수 있다.

 

다만 디자인 시스템이 잘 갖춰져 있지만 개발 인력이 부족해 컴포넌트 개발에 많은 시간을 쓰지 못하는 상황이라면 

도입을 긍정적으로 생각해도 좋을것 같다. 

그리고 기존에 Tailwind를 쓰고 있는 프로젝트라면 더더욱 좋을 것 같다.

 

비슷한 컨셉의 라이브러리로는 Radix UI, ReaKit가 있다.

이런 라이브러리들은 Headless UI처럼 접근성과 기능성을 갖춘 컴포넌트를 제공하면서

스타일링은 자유롭게 할 수 있게 해준다.

Tailwind를 안쓰거나 다 많은 컴포넌트가 필요하다면 이쪽도 고려해볼만한 것 같다.

 

 


마무리

정리하자면 Headless UI는 이런 라이브러이다.

- 스타일 없이 기능과 접근성만 제공하는 라이브러리

- Tailwind CSS랑 잘 어울리게 설계됨.

- Dialog, Menu, Listbox 같은 자주 쓰는 컴포넌트 제공

- 키보드 네비게이션, 포커스 관리 같은 접근성은 알아서 처리

 

UI 라이브러리 스타일을 커스텀하다가 지쳤다면 한번 써볼만 한것 같다.

처음엔 코드가 꽤 길어 보여도, 컴포넌트 개발에 시간을 많이 안 써도 되지만 원하는 대로 스타일링 할 수 있다는게 꽤 편리한것 같다.