무한 루프의 함정이 발견된 후, 리액트 팀은 eslint-plugin-react-hooks 플러그인을 강화하는 작업에 즉시 착수했다. 훅의 규칙 위반을 잡는 것만으로는 부족했다. 이제 플러그인은 의존성 배열의 문제를 감지하고 해결하는, 더 지능적인 파수꾼이 되어야 했다.
팀이 플러그인에 추가한 새로운 규칙의 이름은 exhaustive-deps였다.
‘철저한 의존성’이라는 이름 그대로, 이 규칙은 useEffect의 의존성 배열이 ‘완전한지’를 검사했다.
플러그인은 이제 다음과 같이 작동했다.
useEffect함수 본문 안에서 사용된 모든 외부 변수(props, state, 다른 함수 등)의 목록을 만든다.- 이 목록을 의존성 배열에 선언된 변수 목록과 비교한다.
- 만약 목록이 일치하지 않으면, 즉시 경고를 띄운다.
댄은 이 새로운 플러그인의 힘을 보여주기 위해, 의도적으로 실수가 있는 코드를 작성했다.
function Counter({ step }) {
const [count, setCount] = useState(0);
useEffect(() => {
// 이 효과는 'step' prop에 의존한다.
const timerId = setInterval(() => {
setCount((c) => c + step);
}, 1000);
return () => clearInterval(timerId);
}, []); // ERROR: 하지만 의존성 배열에 'step'이 빠져있다!
}
이 코드는 미묘한 버그를 품고 있었다. step prop이 부모로부터 바뀌어 전달되어도, useEffect는 처음 마운트될 때의 step 값(예: 1)을 영원히 기억하고 있을 것이다. 카운터는 항상 1씩만 증가하게 된다.
개발자가 이 코드를 작성하는 순간, 그의 코드 에디터는 즉시 경고를 표시했다.
ESLint 경고:
React Hook useEffect has a missing dependency: 'step'. Either include it or remove the dependency array.
플러그인은 마치 숙련된 코드 리뷰어처럼, 개발자가 놓친 부분을 정확하게 짚어주었다. 더 나아가, 많은 코드 에디터에서는 이 경고 메시지에 ‘자동 수정(autofix)’ 기능까지 제공했다. 개발자가 버튼 클릭 한 번으로, 플러그인이 제안하는 대로 의존성 배열에 step을 추가할 수 있었다.
}, [step]); // 자동으로 수정된 코드!
이것은 엄청난 발전이었다.
개발자는 더 이상 어떤 의존성을 넣어야 할지 머리 싸매고 고민할 필요가 없었다. 일단 빈 배열 []로 시작하고, 플러그인이 알려주는 대로 정직하게 의존성을 채워 넣기만 하면 되었다.
하지만 플러그인은 여기서 멈추지 않았다.
이전 화에서 마주했던 ‘객체나 함수 참조’ 문제에 대한 경고 기능도 포함되었다. 만약 부모 컴포넌트의 렌더링 과정에서 매번 새로 생성되는 객체나 함수를 의존성 배열에 넣으려고 하면, 플러그인은 또 다른 경고를 띄웠다.
ESLint 경고:
The 'user' object makes the dependencies of useEffect change on every render. To fix this, wrap the 'user' object creation in a useMemo Hook.
플러그인은 문제의 원인을 알려주는 것을 넘어, useMemo라는 해결책까지 제시해주었다. (물론 useMemo는 아직 이 이야기에서 등장하지 않은, 다음에 다룰 주제였다.)
이 똑똑한 파수꾼, eslint-plugin-react-hooks의 존재는 훅 생태계의 안정성을 극적으로 높여주었다.
팀은 이제 개발자들에게 이렇게 말할 수 있었다.
“규칙은 도구가 강제해야 합니다. 여러분은 비즈니스 로직에만 집중하세요. 의존성 관리의 복잡함은 저희가 만든 플러그인에게 맡기시면 됩니다.”
이 철학은 훅이 커뮤니티에 성공적으로 안착하는 데 결정적인 역할을 했다.
이제 훅의 기본 API와 안전장치는 거의 완성되었다.
리액트 팀은 잠시 숨을 고르며, 훅이 등장하기 이전 시대의 또 다른 난제들을 돌아보기 시작했다. 그들의 시선은 이제, 컴포넌트 트리 깊숙이 데이터를 전달해야 했던 고통, ‘Prop Drilling’을 향하고 있었다. useContext가 이 문제의 해답이었지만, 그들은 이 훅이 실제로 어떻게 그 고통을 해결하는지 구체적인 시나리오를 통해 보여줄 필요가 있었다.


