useNavigate로 커스텀 훅 만들기

붕붕·2025년 2월 6일
1
post-thumbnail

페이지를 나눠놨더니 useNavigate를 너무 반복적으로 쓰게 돼서 깔끔한 코드를 위해 커스텀 훅을 만들기로 했다.

1. 처음 시도했던 코드


import { useNavigate } from "react-router-dom";

export const useNavigator = (page) => {
  const navigate = useNavigate();
  return navigate(page);
};

<BtnStyle onClick={() => useNavigator("/dex")}>

이런 식으로 이동할 page를 받으면 해당 페이지로 이동해주는 훅을 만들었다.
그런데 실행해보니 다음과 같은 오류가 발생했다.

Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app

즉, 콜백 함수 안에서는 훅을 사용할 수 없다는 것이다. 그래서 훅을 밖으로 빼서 다시 시도해봤다.

<BtnStyle onClick={useNavigator("/dex")}>포켓몬 도감 시작하기</BtnStyle>

이제 에러는 사라졌지만, 실행해보니 페이지 이동이 되지 않았다.
콘솔을 확인해보니 이런 오류가 떠 있었다.

You should call navigate() in a React.useEffect(), not when your component is first rendered.

"굳이 useEffect를 써야 하는 건가?" 라는 생각이 들어 useEffect 없이 해결할 방법을 찾아봤다.

2. 해결 시도

1. 커스텀 훅 내부 코드 수정


export const useNavigator = (page) => {
  const navigate = useNavigate();
  return navigate(page);
};

return을 빼면 해결될까 싶어서 시도해봤지만 실패.

2. useEffect 사용

export const useNavigator = (page) => {
  const navigate = useNavigate();
  useEffect(() => navigate(page));
};

이렇게 하면 home에 들어갈 때마다 자동으로 페이지가 전환됐다.
혹시나 싶어 의존성 배열에 빈 배열 []을 넣어봤지만 마찬가지였다.

빈 배열을 넣거나 아무것도 넣지 않으면 마운트 될 때 무조건 실행되기 때문에 이런 문제가 발생하는 것을 깨달았다.

그래서 아예 함수 자체를 반환하는 방식으로 수정해봤다.

3. 함수 자체를 반환하기

export const useNavigator = () => {
  const navigate = useNavigate();
  return (page) => navigate(page);
};

<BtnStyle onClick={() => useNavigator("/dex")}>뒤로 가기</BtnStyle>

하지만 이것도 실패.
훅은 반드시 React 함수 컴포넌트 내에서 실행되어야 한다는 오류가 떴다.

4. useCallback을 사용하여 해결

export const useNavigator = (page) => {
  const navigate = useNavigate();
  return useCallback(() => navigate(page), [navigate, page]);
};

<BtnStyle onClick={useNavigator("/dex")}>뒤로 가기</BtnStyle>

이렇게 하니까 성공했다!

왜 useCallback이 필요한가?

You should call navigate() in a React.useEffect(), not when your component is first rendered.

이 오류 메세지를 다시 보면 "navigate()를 컴포넌트가 렌더링될 때마다 즉시 실행하고 있다."라는 말이다. (리액트는 렌더링 중에 navigate()를 실행하는 것을 금지한다.)

그래서 이따구로 쓸거면 차라리 useEffect로 감싸서 마운트될 때만 실행하라고 경고하는 거다. (기분 나쁘게 친절한 자식..)

하지만, useEffect를 사용하면 컴포넌트가 마운트될 때 반드시 navigate()가 실행되기 때문에 오히려 문제가 더 커진다.

나는 버튼을 클릭했을 때 navigate가 실행되는 것을 원하기 때문에 useCallback()을 사용해서 메모이제이션하여 리렌더링 되더라도 navigate()가 마음대로 실행되는 것을 막아주는 것이다!

느낀점

사실 커스텀 훅을 사용하지 않는 게 더 간단한 로직이었지만, 그래도 배운 걸 써먹어 보고 싶어서 한번 만들어봤다ㅎㅎ 비록 커스텀 훅이 불필요한 것 같아서 결국 삭제하긴 했지만… 덕분에 useCallback의 쓰임새를 다시 한 번 정리할 수 있었다!

profile
프론트엔드 개발자(가 되고 싶은 대학생)

0개의 댓글

관련 채용 정보