거대 커스텀 훅의 그림자

832025년 11월 06일4

리암이 만든 useInfiniteScroll 훅은 팀 내에서 큰 인기를 끌었다. 여러 프로젝트에서 이 훅을 가져다 쓰기 시작했고, 새로운 기능 요구사항들이 쏟아져 들어왔다.

“데이터를 캐싱하는 기능도 추가해주면 좋겠어요.”
“에러 처리 로직도 훅 안에서 처리해주면 안 될까요?”
“스크롤 방향에 따라 위로도 로딩할 수 있는 옵션이 필요해요.”

리암은 자신의 훅이 인정받는다는 사실에 고무되어, 모든 요구사항을 수용하기 시작했다. 그는 useInfiniteScroll에 온갖 종류의 옵션과 새로운 상태, 그리고 복잡한 조건부 로직을 추가했다.

훅의 코드는 점점 길어졌다.
처음에는 50줄에 불과했던 함수가, 100줄, 200줄을 넘어 하나의 거대한 모듈이 되어가고 있었다.
훅이 반환하는 값도 늘어났다. 처음에는 { items, loading, targetRef } 세 개뿐이었지만, 이제는 { items, loading, error, hasMore, fetchNextPage, fetchPreviousPage, reset, ... } 등 열 개가 넘는 값을 반환했다.

새로운 기능이 추가될수록, 훅은 점점 더 강력해졌지만 동시에 이해하기 어려워졌다.
이제 이 훅을 처음 사용하려는 개발자는, 수많은 옵션과 반환 값의 의미를 파악하기 위해 긴 문서를 읽어야만 했다. 훅의 내부 로직은 너무 복잡하게 얽혀있어, 창시자인 리암조차도 작은 부분을 수정하는 데 오랜 시간이 걸렸다.

어느 날, 댄이 코드 리뷰 과정에서 리암의 거대해진 useInfiniteScroll 훅을 보게 되었다.
그는 칭찬 대신, 조심스러운 우려를 표했다.

“리암, 훌륭한 작업을 했어요. 이 훅은 아주 많은 문제를 해결해주고 있죠. 하지만, 이 훅이 지금 너무 많은 일을 하려고 하는 것 같지 않나요?”

댄은 리암을 자신의 자리로 불렀다.
“우리가 클래스 컴포넌트의 문제를 해결하려고 훅을 만들었던 이유를 기억하나요? 클래스의 문제는, 너무 많은 상태와 로직을 하나의 거대한 단위 안에 담고 있어서 복잡하고 유지보수하기 어려웠다는 겁니다.”

그는 리암의 코드를 가리켰다.
“지금 이 useInfiniteScroll 훅은, 마치 또 다른 형태의 클래스처럼 보입니다. 모든 기능이 하나의 거대한 추상화 계층 뒤에 숨겨져 있죠. 이것은 우리가 피하려고 했던 바로 그 문제입니다.”

리암은 순간 말문이 막혔다.
그는 자신의 훅이 팀에 기여하고 있다고만 생각했지, 그것이 과거의 안티패턴을 되풀이하고 있을 수 있다는 생각은 해보지 못했다.

댄은 부드러운 목소리로 조언을 이어갔다.
“좋은 훅은, 좋은 함수와 같습니다. 작고, 한 가지 일만 잘해야 합니다. 이것을 ‘단일 책임 원칙(Single Responsibility Principle)’이라고 하죠.”

그는 useInfiniteScroll을 더 작은 훅들로 분리할 것을 제안했다.

  • useIntersection: 요소의 화면 노출 여부만 감지하는 범용적인 훅.
  • usePagination: 페이징(페이지 번호, 데이터 로딩 상태 등) 로직만 관리하는 훅.
  • useCache: 데이터 캐싱만 담당하는 훅.

그리고 최상위의 useInfiniteScroll은 이 작은 훅들을 ‘조합’하여 사용하는, 훨씬 더 가볍고 읽기 쉬운 형태로 만드는 것이었다. 이렇게 하면, 다른 개발자들은 필요에 따라 useIntersection이나 usePagination 같은 저수준 훅을 직접 가져다 쓸 수도 있었다.

리암은 댄의 조언을 통해 중요한 교훈을 얻었다.
커스텀 훅은 강력한 추상화 도구지만, 모든 강력한 추상화에는 책임이 따른다는 것.
추상화의 목적은 복잡성을 숨기는 것이지만, 과도한 추상화는 오히려 내부를 알 수 없는 ‘블랙박스’를 만들어 또 다른 복잡성을 낳는다는 사실이었다.

그는 자신의 거대한 훅을 리팩토링하는, 고통스럽지만 의미 있는 작업에 착수했다.
이 경험은 그를 한 단계 더 성장시켰다. 그는 이제 단순히 훅을 ‘만들 줄 아는’ 개발자를 넘어, ‘잘 설계할 줄 아는’ 개발자로 나아가고 있었다.
그리고 이 ‘잘 설계된’ 훅의 로직을 어떻게 하면 안정적으로 테스트할 수 있을지에 대한, 새로운 과제가 커뮤니티 앞에 놓여 있었다.