본문 바로가기
📄Today I Learned

React Hook은 왜 콜백 안에서 못 쓰는 걸까?

by 영진학생 2025. 4. 18.
  ▲ Next.js 14.2.26
  - Environments: .env.local

   Creating an optimized production build ...
 ✓ Compiled successfully

Failed to compile.

./src/components/calendar/MainCalendar.tsx
88:6  Warning: React Hook useEffect has a missing dependency: 'setSelectPlan'. Either include it or remove the dependency array. If 'setSelectPlan' changes too often, find the parent component that defines it and wrap that definition in useCallback.  react-hooks/exhaustive-deps

./src/components/contacts/addContactForm/ContactTextField.tsx
54:7  Error: React Hook "useEffect" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.  react-hooks/rules-of-hooks

info  - Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/basic-features/eslint#disabling-rules
   Linting and checking validity of types  . ELIFECYCLE  Command failed with exit code 1.

에러 상황

useEffect를 react-hook-form의 <FormField /> 컴포넌트 안에서 사용했더니 위와 같은 에러가 발생했다.
처음엔 컴포넌트 내부에 있으니까 괜찮을 줄 알았는데, 알고 보니 Hook은 반드시 컴포넌트의 최상단에서만 호출되어야 한다는 규칙이 있었다!

원인

React는 Hook의 실행 순서를 추적해서 상태 관리를 한다고한다.
그래서 useEffect, useState 같은 Hook은 조건문, 반복문, 콜백 함수 안에서 사용하면 안 된다.

내 경우에는 FormField의 render prop으로 넘긴 콜백 안에 useEffect가 들어가 있어서 생긴 문제였다.

해결 방법

useEffect를 render 함수 바깥으로, 즉 ContactTextField 컴포넌트의 최상단에 위치시키니까 정상적으로 작동했다.
Hook은 항상 컴포넌트가 시작되자마자 호출되어야 하며, 조건 없이 한 번만 실행되는 구조여야 한다는 걸 명확히 알게 됐다.

느낀 점

다행스럽게도 ESLint가 Hook 규칙을 강하게 잡아준 덕분에 잘못된 구조를 빠르게 발견할 수 있었다.

다른 작업을 하면서 뒤늦게 PR을 올리기전에 발견한 build 에러라 당황했고 작업하면서 자주 빌드를 해봐야겠다는 경험을 하게 되었다. 앞으로는 Hook은 최상단에서 호출한다는 원칙을 명심하자!