로직을 테스트하는 법의 변화 (@testing-library/react-hooks)

842025년 11월 07일3

커스텀 훅은 상태 로직을 UI로부터 분리하는 데 성공했다. 이 분리는 예상치 못한, 아주 중요한 이점을 가져왔다. 바로 ‘테스트 용이성(Testability)’의 향상이었다.

과거, 클래스 컴포넌트의 로직을 테스트하는 것은 까다로운 일이었다. 로직이 componentDidMountsetState 같은 UI 렌더링 생명주기에 깊이 결합되어 있었기 때문에, 로직만을 따로 떼어내 테스트하기가 거의 불가능했다. 개발자들은 어쩔 수 없이 실제 컴포넌트를 브라우저 환경(혹은 jsdom 같은 가상 환경)에 렌더링하고, 버튼을 클릭하는 등의 사용자 행동을 시뮬레이션하여 로직이 올바르게 동작하는지 간접적으로 확인해야 했다. 이 과정은 느리고, 불안정했으며, 설정이 복잡했다.

하지만 이제, 상태 로직은 순수한 자바스크립트 함수인 ‘커스텀 훅’ 안에 캡슐화되었다.
그렇다면, 이 훅의 로직 자체를, UI 렌더링 없이 독립적으로 테스트할 수는 없을까?

이 질문에 대한 해답을, ‘리액트 테스팅 라이브러리(React Testing Library)’를 만든 켄트 C. 도즈(Kent C. Dodds)와 그의 커뮤니티가 내놓았다.
그들은 @testing-library/react-hooks라는 이름의 새로운 라이브러리를 발표했다.

이 라이브러리의 핵심은 renderHook이라는 단 하나의 함수였다.
renderHook은 테스트 환경에서 가상의 컴포넌트를 만들어, 우리가 테스트하고 싶은 훅을 대신 실행해주고, 그 결과를 관찰할 수 있게 해주는 마법 같은 도구였다.

리암이 만들었던 useToggle 훅을 테스트하는 시나리오를 생각해보자.

import { renderHook, act } from '@testing-library/react-hooks';
import { useToggle } from './useToggle';

test('should toggle state from false to true', () => {
  // 1. renderHook으로 useToggle 훅을 실행한다.
  const { result } = renderHook(() => useToggle(false));

  // 2. 초기 상태를 확인한다.
  // result.current는 훅의 반환 값 [isOn, toggle]을 가리킨다.
  expect(result.current[0]).toBe(false);

  // 3. 상태를 변경하는 함수를 실행한다.
  //    act()는 상태 업데이트가 모두 처리될 때까지 기다려주는 헬퍼 함수다.
  act(() => {
    result.current[1](); // toggle 함수 호출
  });

  // 4. 변경된 상태를 확인한다.
  expect(result.current[0]).toBe(true);
});

테스트 코드는 놀랍도록 간결하고 명확했다.
어떤 UI도 렌더링되지 않았다. 버튼을 클릭하는 시뮬레이션도 없었다.
오직 useToggle이라는 순수한 로직의 입력(초기값)과 출력(현재 상태)만을 직접 테스트하고 있었다.

이것은 테스트의 단위를 ‘UI 컴포넌트’에서 ‘재사용 가능한 로직’으로 바꾸는, 패러다임의 전환이었다.
개발자들은 이제 복잡한 렌더링 환경 설정 없이도, 커스텀 훅의 모든 엣지 케이스를 빠르고 안정적으로 테스트할 수 있게 되었다.

@testing-library/react-hooks는 훅 생태계의 발전에 기폭제 역할을 했다.
개발자들은 이제 자신들이 만든 커스텀 훅이 견고하고 버그가 없다는 것을 자신감 있게 증명할 수 있게 되었다. 이는 더 많은 개발자들이 안심하고 다른 사람의 커스텀 훅을 가져다 쓰고, 자신의 훅을 공유하는 선순환 구조를 만들었다.

테스트의 패러다임마저 바꾸어 놓은 훅은, 이제 명실상부 리액트 세계의 새로운 표준으로 자리 잡고 있었다.

댄과 리액트 팀은 이 모든 생태계의 자발적인 진화를 자랑스럽게 지켜보았다. 그들이 뿌린 작은 씨앗이, 이제는 그들의 도움 없이도 스스로 가지를 뻗고 풍성한 열매를 맺는 거대한 나무가 되어가고 있었다. 그들은 자신들이 더 이상 이 나무의 주인이 아니라, 그저 수많은 정원사 중 한 명일 뿐임을 깨닫고 있었다.