blog-imgDucklog

Headless UI 적용해보기

Headless UI 적용해보기

프론트엔드 개발을 하다보면, UI에 너무 많은 로직이 들어가있으면, 유지보수가 힘들고 재사용 하기 힘들어진다. 그렇기 때문에 로직과 UI를 분리해서 코드를 만들어야한다.

Headless UI의 핵심 아이디어는 UI 컴포넌트의 뷰와 로직을 분리하는 것이다. 이를 통해 사용자는 컴포넌트의 모양과 스타일을 자유롭게 조절할 수 있으며, 컴포넌트의 행동을 정의하고 제어할 수 있다. 이로써 더 유연하고 재사용 가능한 컴포넌트를 만들 수 있게 된다.

이제 어떤 개념인지 알았으니 적용해보자.

예를 들어서 바텀시트를 만들어주려고 한다. 바텀 시트에서 필요한 가장 기본적인 로직은 상태(open), 시트 열기, 시트 닫기이다. 우선 로직을 훅으로 별도로 빼줬다.


import { useState } from 'react';

export const useBottomSheet = () => {
  const [isOpen, setIsOpen] = useState(false);

  const openSheet = () => {
    setIsOpen(true);
  };

  const closeSheet = () => {
    setIsOpen(false);
  };

  return { isOpen, openSheet, closeSheet };
};

이제 바텀 시트의 UI를 만들어줬다.


import Backdrop from '../Backdrop';

interface BottomSheetProps {
  isOpen: boolean;
  closeSheet: () => void;
  children: React.ReactNode;
  className?: string;
}

export default function BottomSheet({ isOpen, closeSheet, children, className }: BottomSheetProps) {
  return isOpen ? (
    <>
      <Backdrop isOpen={isOpen} onClick={closeSheet} />
      <div
        className={`fixed inset-0 h-fit z-50 mx-auto mt-auto w-full overflow-y-auto rounded-t-xl bg-white md:max-w-[375px] ${className}`}
      >
        {children}
      </div>
    </>
  ) : null;
}

className과 chidren을 이용해서 사용할 때마다 커스텀해서 사용할 수 있게 해줬다.

사용 예시

import Header from '@/components/common/Header';
import Navbar from '@/components/common/Navbar';
import BottomSheet from '@/components/common/Sheet';
import { useBottomSheet } from '@hooks/useBottomSheet';

export default function Mypage() {
  const { isOpen, openSheet, closeSheet } = useBottomSheet();

  return (
    <div className="flex min-h-screen flex-col">
      <Header title="마이페이지" />
      <h1>Mypage</h1>
      <button onClick={openSheet}>ddd</button>
      <BottomSheet className="" isOpen={isOpen} closeSheet={closeSheet}>
        <div className="gap-y-3 flex flex-col p-5 bg-purple-50">
          <button className="border-b rounded-md border-primary-100">1번 시트 버튼</button>
          <button className="border-b rounded-md border-primary-100">2번 시트 버튼</button>
          <button onClick={closeSheet} className="border-b rounded-md border-primary-100">
            닫기
          </button>
        </div>
      </BottomSheet>
      <Navbar />
    </div>
  );
}

useBottomSheet훅을 불러와서 로직을 사용해주고 시트를 위에처럼 사용해주면 아래와 같은 화면을 볼 수 있다!