카테고리 없음

Custom Hook 그거 굳이 써야 해?? ㅡ Custom Hook 제대로 설계하기

taek2-0310 2026. 4. 29. 11:09

들어가며 🚪

React를 어느 정도 써본 개발자라면 Custom Hook이 낯설지 않을 겁니다. 그런데 "잘 쓰고 있어?"라고 누군가 묻는다면, 선뜻 대답하기가 어렵습니다.

 

Hook을 만들어본 적 있다면 이런 고민 한 번쯤 해봤을 겁니다. "이거 굳이 Hook으로 빼야 하나?" "너무 많은 걸 한 Hook에 넣은 건 아닌가?" 그리고 더 근본적으로는 — 어떻게 해야 잘 쓰는 건지조차 잘 모르겠다는 느낌.

 

오늘은 그 고민을 조금 덜어보고자 합니다. Custom Hook을 언제 만들어야 하는지, 어떤 실수를 피해야 하는지, 그리고 좋은 Hook이란 어떤 모습인지를 컴포넌트의 목적과 책임이라는 관점에서 풀어보겠습니다.

Custom Hook? 🪝

커스텀 훅이란 개발자가 직접 만든 hook으로 몇가지 규칙이 존재한다. React 공식 홈페이지 내용에 따르면 hook은

1. 최상위(at the Top Level)에서만 Hook을 호출해야 한다.

반복문, 조건문 혹은 중첩된 함수 내에서 Hook을 호출하지 않아야 한다.
이 규칙을 따르면 컴포넌트가 렌더링 될 때마다 항상 동일한 순서로 Hook이 호출되는 것이 보장된다. 이러한 점은 React가 useState와 useEffect 가 여러 번 호출되는 중에도 Hook의 상태를 올바르게 유지할 수 있도록 해준다.

2. 오직 React 함수 내에서 Hook을 호출해야 한다.

이 규칙을 지키면 컴포넌트의 모든 상태 관련 로직을 소스코드에서 명확하게 보이도록 할 수 있다.

 

뿐만 아니라 custom hook을 사용할 때는 use로 시작해야한다는 네이밍 규칙도 존재합니다.

Custom Hook, 언제 만들어야 할까?🍎

Custom Hook을 만드는 기준은 크게 두 가지 관점으로 나눌 수 있습니다.

 

1: 로직이 반복될 때

가장 직관적인 기준입니다. 같은 useState + useEffect 패턴이 두 곳 이상에서 반복된다면 Hook으로 분리할 타이밍입니다. (React 공식홈페이지 예시)

//FriendStatus : 친구가 online인지 offline인지 return하는 컴포넌트
function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

//FriendListItem : 친구가 online일 때 초록색으로 표시하는 컴포넌트
function FriendListItem(props) {
  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

이처럼 두 컴포넌트에서 공통으로 사용되는 로직을 분리하여 useFriendStatus 함수로 만들면 코드 재사용성을 높일 수 있습니다.

function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

이렇게 공통 로직을 커스텀 훅으로 분리하면 코드 재사용성을 높일 수 있고 중복 코드를 줄여 전체 코드량을 줄일 수 있습니다.
또한 로직과 UI를 분리할 수 있어 가독성과 유지보수성이 좋아지고 여러 컴포넌트에서 동일한 로직을 일관되게 사용할 수 있다는 장점이 있습니다.

관점 2: 관심사를 분리할 때

반복이 없더라도 한 컴포넌트 안에 서로 다른 관심사의 로직이 섞여 있다면 분리할 수 있습니다. 컴포넌트가 "무엇을 렌더링하는가"에만 집중하도록 만드는 것이 목표입니다. 

 

컴포넌트는 본래 "무엇을 렌더링할 것인가" 라는 하나의 책임을 가집니다. 그런데 데이터를 불러오는 로직, 상태를 관리하는 로직, 이벤트를 처리하는 로직이 한 컴포넌트 안에 뒤섞이다 보면 그 책임의 경계가 무너집니다. 컴포넌트가 렌더링도 하고, 데이터도 가져오고, 상태도 관리하는 모든 것을 다 떠안는 존재가 되어버리는 거죠. 또한 코드를 읽는 사람 입장에서도 "이 컴포넌트가 뭘 하는 컴포넌트지?"라는 질문에 바로 답하기 어려워집니다.

 

이때 Custom Hook으로 로직을 분리하면 각자의 책임이 선명해집니다. 컴포넌트는 화면을 그리는 일만, Hook은 그에 필요한 로직을 처리하는 일만 담당하게 됩니다. 책임이 나뉜 코드는 읽기도 쉽고 고치기도 쉽습니다. "UI를 바꿔야 한다"면 컴포넌트만, "데이터 처리 방식을 바꿔야 한다"면 Hook만 건드리면 되니까요.

function useInputState(
  initialValue: string = '',
  transformValue: (value: string) => string = (v: string) => v
): [value: string, onChange: ChangeEventHandler<HTMLInputElement>];
function Example() {
  const [value, onChange] = useInputState('');
  return <input type="text" value={value} onChange={onChange} />;
}

위 코드는 토스의 오픈소스 라이브러리 react-simplikit의 useInputState 입니다.  이렇게 사용했을 때 이름만 봐도 input의 상태를 관리하는 Hook임을 바로 알 수 있고 input의 상태 관리라는 하나의 책임만 가지고 있기 때문에 이를 사용하는 컴포넌트는 값을 어떻게 관리할지 고민할 필요 없이 렌더링에만 집중할 수 있습니다.

만들지 말아야 할 경우도 있다

Hook이 단순히 useState 하나만 감싸거나, 한 곳에서만 쓰이는 데 복잡하지도 않다면 굳이 Hook으로 분리할 필요가 없습니다. 추상화는 비용입니다.

흔히 하는 실수들💦

실수 1: 너무 많은 걸 한 Hook에 몰아넣기

Hook을 만들다 보면 "이것도 넣고, 저것도 넣고"가 되기 쉽습니다. 그러다 보면 재사용할 수 없는 Hook이 됩니다.

function useUserDashboard() {
  // 유저 정보 fetch
  // 알림 목록 fetch
  // 모달 열림 상태
  // 탭 선택 상태
  // ... 다 여기에
}

 

실수 2: 의존성 배열 관리 실수

Hook 내부에서 useEffect를 쓸 때 의존성 배열을 잘못 관리하면 무한 렌더링이나 오래된 값(stale closure) 문제가 생깁니다.

useEffect(() => {
  fetchData(options); // options가 객체라면?
}, [options]); // 매 렌더마다 새 객체 → 무한 호출

 

실수 3: 반환값 설계 실수

반환값에 이름이 없거나 일관성이 없으면 사용하는 쪽에서 혼란이 생깁니다. 다음 섹션에서 자세히 다룹니다.

 좋은 Hook의 인터페이스 설계🎨

배열 vs 객체, 언제 어떤 걸 쓸까?

useState처럼 배열을 반환하면 사용하는 쪽에서 이름을 자유롭게 지을 수 있어 여러 번 사용하기 좋습니다. 반면 반환값이 3개 이상이 되면 객체가 훨씬 읽기 편합니다.

// 2개 이하 → 배열도 괜찮음 (useState 패턴)
const [isOpen, setIsOpen] = useToggle(false);

// 3개 이상 → 객체로
const { data, loading, error, refetch } = useFetch(url);

 

네이밍 컨벤션

Hook 이름은 use로 시작하는 건 기본이고, 그 다음이 중요합니다. 무엇을 하는지가 이름에 드러나야 합니다.

// 모호한 이름
useData(), useHandler(), useHelper()

// 명확한 이름
useFetch(url)
useLocalStorage(key, defaultValue)
useIntersectionObserver(ref, options)

재사용성과 유연성의 트레이드오프

Hook을 너무 유연하게 만들려다 보면 옵션 파라미터가 폭발적으로 늘어납니다. 반대로 너무 구체적으로 만들면 재사용이 어렵습니다.

좋은 기준은 지금 필요한 것만 파라미터로 나중에 필요해지면 그때 확장하는 것입니다. 미리 유연하게 만들어둔 Hook은 대부분 복잡도만 높아집니다.

Hook의 파라미터가 5개를 넘어간다면, 객체 하나로 받는 것을 고려하세요. 그리고 정말 이게 하나의 Hook이어야 하는지 다시 생각해보는 것도 좋습니다.

마치며 — 체크리스트

Custom Hook을 만들기 전후로 이 체크리스트를 확인해보세요.

  1. 이 Hook이 하나의 관심사만 다루고 있는가?
  2. Hook 이름만 보고 무엇을 하는지 알 수 있는가?
  3. 반환값이 3개 이상이면 객체로 반환하고 있는가?
  4. useEffect 의존성 배열에 객체나 함수를 직접 넣지 않는가?
  5. 이 Hook 없이도 간단하게 해결되는 문제는 아닌가?

참고🍿

https://leego.tistory.com/entry/React-Custom-hook%EC%9D%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%A0%84%EC%97%90-%EA%B3%A0%EB%A0%A4%ED%95%B4%EC%95%BC-%ED%95%A0-%EA%B2%83%EB%93%A4

 

[React] Custom hook을 만들기 전에 고려해야 할 것들

들어가면서🚪 Building your own Hooks lets you extract component logic into reusable functions. 최근 내가 컴포넌트를 설계할 때 가장 중요하게 생각하는 것은 컴포넌트의 역할이 명확하게 나타나는가 이다. 예를

leego.tistory.com

https://ko.legacy.reactjs.org/docs/hooks-rules.html

 

Hook의 규칙 – React

A JavaScript library for building user interfaces

ko.legacy.reactjs.org

https://ko.legacy.reactjs.org/docs/hooks-custom.html

 

자신만의 Hook 만들기 – React

A JavaScript library for building user interfaces

ko.legacy.reactjs.org

https://velog.io/@oka1313/React-Custom-Hooks

 

[React] Custom Hooks

Custom Hooks이란 개발자가 스스로 커스텀한 훅을 의미한다. 이를 이용해 반복되는 로직을 함수로 뽑아내어 재사용할 수 있다.

velog.io