React 프로젝트에서 Hook을 어떻게 설계할까?

Yujin Jung·2025년 11월 11일

— 커스텀 훅을 기능이 아닌 ‘책임 단위’로 나누는 기준
React Hook은 단순히 코드를 줄이기 위한 문법이 아니다.
프로젝트가 커질수록 상태 관리, 로직 재사용, 데이터 흐름 제어의 중심이 된다.
이번 글에서는 “Hook을 언제, 어떤 기준으로 나누면 좋은가?”에 대해 공부한 내용을 정리해보았다.


왜 Hook을 쓰는가 — 로직 재사용보다 “관심사 분리”

처음엔 useState, useEffect로 충분하다.
하지만 컴포넌트가 커지면 이런 현상이 생긴다 👇

  • UI 로직과 비즈니스 로직이 섞여 가독성이 떨어짐
  • 상태 변화가 많아 디버깅이 어려워짐
  • 비슷한 로직을 여러 컴포넌트에서 반복

이때 커스텀 훅(Custom Hook) 은 “로직을 옮겨놓는 함수”가 아니라,
컴포넌트가 가져야 할 책임을 분리하고 독립시키는 도구로 보는 게 중요하다.

“Hook은 코드를 분리하는 수단이 아니라, 책임을 분리하는 수단이다.”


useState / useEffect로는 충분하지 않을 때

React의 초반 단계에서는 대부분 이렇게 작성한다.

function Example() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log("Count changed:", count);
  }, [count]);
}

문제는, 기능이 늘어날수록 useEffect가 점점 복잡해지고 “의존성 지옥” 이 시작된다는 것이다.

useEffect(() => {
  fetchData();
  handleResize();
  handleAuthCheck();
}, [token, width, isLoggedIn]);

각 로직이 서로 영향을 주고받으며 렌더링 타이밍, 비동기 처리, 상태 동기화 문제가 꼬이기 시작한다.
이 시점이 바로 커스텀 훅을 도입해야 할 타이밍이다.


커스텀 훅으로 분리하는 기준

많은 개발자들이 처음엔 useLogin, useFetch, useForm 처럼“기능 단위”로 훅을 만든다.
하지만 진짜 좋은 분리는 ‘하나의 책임(Responsibility)’에 집중된 훅이다.

잘못된 기준올바른 기준
비슷한 코드가 반복되니까 묶는다하나의 논리적 책임을 가진다
여러 사이드이펙트를 한 곳에 몰아넣는다각 훅은 오직 하나의 역할만 수행한다
훅 내부에서 또 다른 훅을 호출상위 훅이 하위 훅을 조립해 데이터 흐름을 제어

예를 들어 useFetch라는 훅은 “데이터 요청 + 에러 핸들링 + 로딩 상태”라는 하나의 책임에 집중되어 있다면 괜찮다.
하지만 여기에 “토큰 갱신”이나 “자동 재시도”까지 들어간다면 이미 역할이 넘친 상태다.


훅 간 의존성 관리 — Controller 패턴

커스텀 훅을 많이 만들다 보면 이런 문제가 생긴다.

“A 훅 안에서 B 훅을 직접 호출하면 안 되나?”

가능은 하지만, 훅 간 결합도가 올라가고 재사용성이 떨어진다.

이럴 때 효과적인 방법은 Controller 훅 패턴이다.
즉, 여러 하위 훅을 조립하고 상위 레벨에서 데이터 흐름을 제어하는 방식이다.

function useController() {
  const { data, error, refetch } = useFetch("/api/data");
  const { isOpen, toggle } = useModal();
  const { value, onChange } = useFormField("");

  // 데이터 흐름 조립
  useEffect(() => {
    if (error) toggle(true); // 에러 시 모달 오픈
  }, [error]);

  return { data, error, isOpen, toggle, value, onChange, refetch };
}

이렇게 하면 각각의 훅은 하나의 책임에 집중하고,
Controller 훅은 전체 로직을 조율하는 역할만 맡게 된다.


Hook 구조 설계 패턴 3가지

프로젝트 단위에서 커스텀 훅을 설계할 때는 3단계 구조로 나누면 깔끔하다.

① Controller Hook

여러 훅을 조립해 데이터 흐름을 제어

예: useDashboard, useAuthFlow

  • 책임: 전체 로직 통합, 의존성 조율
  • 장점: 상위 로직이 한눈에 들어옴
  • 단점: 커지면 또 비대해질 수 있음

② State Hook

특정 도메인의 상태를 캡슐화

예: useUserState, useThemeState

  • 책임: 상태와 업데이트 로직 관리
  • 장점: 독립적인 상태 관리 가능
  • 단점: 전역 남용 시 데이터 추적 어려움

③ Utility Hook

순수 기능만 수행, 사이드이펙트 없음

예: useInterval, usePrevious, useMediaQuery

  • 책임: 보조 로직 제공
  • 장점: 재사용성 높음, 테스트 쉬움
  • 단점: 너무 세분화하면 관리 복잡

    💡 패턴 정리:
    “Controller → State / Utility” 구조로 나누면 데이터 흐름이 명확하고, 유지보수가 쉬워진다.


훅의 이름은 역할을 드러내야 한다

좋은 훅은 이름만 봐도 무슨 일을 하는지 알 수 있다.
나쁜 훅은 “이 훅이 뭘 하는지 직접 열어봐야” 알 수 있다.

// ❌ 나쁜 예시
useCommonLogic();
useSomething();

// ✅ 좋은 예시
useModalVisibility();
useAuthVerification();
useKeyboardShortcut();

“무엇을 하는가?”가 아니라 “왜 존재하는가?”를 드러내는 네이밍이 좋다.


Hook을 설계할 때 자주 하는 실수

실수문제점
훅 안에서 상태를 너무 많이 관리책임 불분명, 테스트 어려움
훅 안에서 훅을 직접 호출결합도 증가, 순환참조 위험
의존성 배열을 무시하거나 남발렌더링 타이밍 예측 불가
커스텀 훅 내부에서 DOM 조작React의 선언형 철학과 충돌

결론: 훅은 로직 재사용보다 데이터 흐름을 명시화하는 도구

결국 커스텀 훅의 목적은 “로직을 옮기는 것”이 아니라,
상태 → 로직 → UI의 흐름을 명확히 보여주는 것이다.

Hook은 기술이 아니라 사고방식이다.
컴포넌트를 “UI 중심”이 아니라 “데이터 흐름 중심”으로 바라보게 만들어준다.

profile
매일매일 조금씩 성장하려 노력하는 프론트엔드 개발자입니다!

0개의 댓글