React : 헤드리스 컴포넌트 디자인 패턴

정윤호·2024년 6월 12일
1

코드잇잇잇!

목록 보기
24/30

헤드리스 컴포넌트 디자인 패턴은 무엇일까

헤드리스 컴포넌트란 ?

헤드리스 컴포넌트를 한 줄로 요약하면 다음과 같다.

로직과 스타일을 분리하여 높은 유연성재사용성을 제공하는 디자인 패턴

이 디자인 패턴은 UI와 관련된 로직을 독립적으로 관리할 수 있게 하여, 다양한 프로젝트에서 동일한 로직을 사용하면서도 다른 스타일을 적용할 수 있도록 한다. 이를 통해 코드의 재사용성과 유지보수성을 높이며, 개발자는 로직 업데이트 시 스타일을 변경할 필요 없이 작업할 수 있다.

  • 스타일에 해당하는 부분은 컴포넌트에서
  • 로직에 해당하는 부분은 커스텀 훅으로 관리

예제 1: 헤드리스 드롭다운 컴포넌트

// useDropdown.js (헤드리스 컴포넌트)
import { useState } from 'react';

export const useDropdown = (options) => {
  const [isOpen, setIsOpen] = useState(false);
  const [selectedOption, setSelectedOption] = useState(null);

  const toggleDropdown = () => setIsOpen(!isOpen);
  const selectOption = (option) => {
    setSelectedOption(option);
    setIsOpen(false);
  };

  return {
    isOpen,
    selectedOption,
    toggleDropdown,
    selectOption,
    options,
  };
};
// Dropdown.js (프레젠테이션 컴포넌트)
import React from 'react';
import { useDropdown } from './useDropdown';

const Dropdown = ({ options }) => {
  const { isOpen, selectedOption, toggleDropdown, selectOption } = useDropdown(options);

  return (
    <div className="dropdown">
      <button onClick={toggleDropdown}>
        {selectedOption ? selectedOption.label : '옵션을 선택하세요'}
      </button>
      {isOpen && (
        <ul>
          {options.map((option) => (
            <li key={option.value} onClick={() => selectOption(option)}>
              {option.label}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
};

export default Dropdown;

예제 2: 헤드리스 폼 컴포넌트

// useForm.js (헤드리스 컴포넌트)
import { useState } from 'react';

export const useForm = (initialValues) => {
  const [values, setValues] = useState(initialValues);

  const handleChange = (e) => {
    const { name, value } = e.target;
    setValues({
      ...values,
      [name]: value,
    });
  };

  const handleSubmit = (callback) => (e) => {
    e.preventDefault();
    callback(values);
  };

  return {
    values,
    handleChange,
    handleSubmit,
  };
};
// Form.js (프레젠테이션 컴포넌트)
import React from 'react';
import { useForm } from './useForm';

const Form = ({ initialValues, onSubmit }) => {
  const { values, handleChange, handleSubmit } = useForm(initialValues);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        type="text"
        name="username"
        value={values.username}
        onChange={handleChange}
        placeholder="사용자 이름"
      />
      <input
        type="password"
        name="password"
        value={values.password}
        onChange={handleChange}
        placeholder="비밀번호"
      />
      <button type="submit">제출</button>
    </form>
  );
};

export default Form;

헤드리스 컴포넌트 사용의 이점

헤드리스 컴포넌트는 스타일이나 레이아웃에 종속되지 않고 순수하게 기능에 집중하는 컴포넌트다. 다음은 헤드리스 컴포넌트를 고려해야 하는 주요 이유다:

  1. 관심사의 분리: 로직과 UI를 분리함으로써, 개발자는 프레젠테이션 계층과 독립적으로 논리를 관리하고 테스트할 수 있다
  2. 재사용성: 헤드리스 컴포넌트는 특정 스타일이나 레이아웃에 얽매이지 않고 다양한 프로젝트와 맥락에서 재사용할 수 있다
  3. 커스터마이징 용이성: 개발자는 어떤 디자인이나 테마도 헤드리스 컴포넌트에 적용할 수 있어 동일한 논리를 재사용하면서 일관된 UI를 유지하기가 쉽다
  4. 유지보수 용이성: 논리와 스타일을 분리하면 비즈니스 로직을 업데이트할 때 UI 컴포넌트를 변경할 필요가 없으므로 시각적 측면에서 버그가 발생할 위험이 줄어든다.

    |600
    https://headlessui.com/
    재사용성을 극대화했기에, 공용으로 쓰이는 컴포넌트는 위 페이지에서처럼 개발자들간 공유도 자유롭게 한다.

비교할 만한 다른 디자인 패턴

Presentational & Container 컴포넌트

// 프레젠테이셔널 컴포넌트
const Button = ({ onClick, children }) => (
  <button onClick={onClick} className="btn-primary">
    {children}
  </button>
);
// 컨테이너 컴포넌트
class ButtonContainer extends React.Component {
  handleClick = () => {
    console.log("Button clicked");
  };

  render() {
    return <Button onClick={this.handleClick}>Click Me</Button>;
  }
}
  1. 관심사의 분리:
  • 프레젠테이셔널 & 컨테이너: 프레젠테이셔널 컴포넌트는 UI를, 컨테이너 컴포넌트는 로직과 데이터를 담당
  • 헤드리스: UI 렌더링을 컴포넌트에서 제거하고 동작과 상태만 제공하여 관심사를 분리
  1. 재사용성:
  • 프레젠테이셔널 & 컨테이너: UI와 로직을 별도의 컴포넌트로 분리하여 재사용성을 높임
  • 헤드리스: 특정 구조를 강요하지 않고 동일한 로직을 다양한 UI 컴포넌트에서 재사용할 수 있도록 하여 재사용성을 더욱 향상
  1. 유연성:
  • 프레젠테이셔널 & 컨테이너: 로직과 UI가 컨테이너와 프레젠테이셔널 컴포넌트로 나뉘어 있어 유연성이 제한될 수 있음
  • 헤드리스: 동일한 로직을 다양한 UI 컴포넌트에서 사용할 수 있어 더 큰 유연성을 제공함
  1. 구현:
  • 프레젠테이셔널 & 컨테이너: 컴포넌트를 두 부분으로 나누어 구현하는 경우가 많아 구조가 명확하지만 다소 장황할 수 있음
  • 헤드리스: 렌더 프롭이나 훅을 사용하여 구현하며, 특히 훅을 사용하면 코드가 더 간결함
profile
우리 인생 화이팅~

0개의 댓글