본문 바로가기

React.js

[React]React useImperativeHandle Hook 깊게 이해하기

useImperativeHandle이란?

useImperativeHandle은 React에서 제공하는 Hook으로, ref를 통해 부모 컴포넌트에 노출할 인스턴스 값을 사용자가 직접 정의할 수 있게 해주는 기능입니다. 기본적으로 부모 컴포넌트에서 자식 컴포넌트의 DOM 노드에 직접 접근할 때 ref를 사용하지만, useImperativeHandle을 사용하면 노출하고자 하는 메서드나 값들을 직접 선택하여 제공할 수 있습니다.

사용 시기

useImperativeHandle은 다음과 같은 상황에서 주로 사용됩니다:

  1. 부모 컴포넌트에서 자식 컴포넌트의 특정 메서드를 호출해야 할 때
  2. 컴포넌트의 내부 구현을 숨기고 특정 기능만 외부에 노출하고 싶을 때
  3. 기존 DOM 요소의 메서드나 속성을 커스터마이즈하고 싶을 때

실제 사용 예시 분석

제가 구현한 필터링 기능에서의 사용 예시를 살펴보겠습니다!

// Filter 컴포넌트
const Filter = forwardRef((props, ref) => {
  // ... 상태 및 다른 로직들

  useImperativeHandle(ref, () => ({
    handleFilterClick: (type, value) => {
      handleFilter(type, value);
    }
  }));

  // ... 나머지 구현
});
// 부모 컴포넌트
const ParentComponent = () => {
  const filterRef = useRef();

  const handleCategoryFilter = () => {
    if (filterRef.current) {
      filterRef.current.handleFilterClick('reset');
      filterRef.current.setCategory(product.category_code);
    }
  };

  return (
    <Filter
      ref={filterRef}
      // ... other props
    />
  );
};

이 구현에서는 다음과 같은 목적으로 useImperativeHandle을 사용했습니다:

  1. 필터 초기화 기능을 부모 컴포넌트에서 제어
  2. 카테고리 필터 설정을 외부에서 트리거
  3. 컴포넌트의 복잡한 내부 상태 관리를 숨기면서 필요한 기능만 노출

useImperativeHandle 사용이 권장되지 않는 이유

React 공식 문서에서도 useImperativeHandle의 사용을 신중하게 고려하라고 권고하는데, 그 이유는 다음과 같습니다:

  1. 단방향 데이터 흐름 위배
    • React의 기본 철학은 props를 통한 단방향 데이터 흐름입니다.
    • useImperativeHandle은 이러한 패턴을 깨고 양방향 바인딩과 비슷한 패턴을 만들 수 있습니다.
  2. 컴포넌트 결합도 증가
    • 부모 컴포넌트가 자식 컴포넌트의 내부 메서드에 직접 의존하게 되면서 결합도가 높아집니다.
    • 이는 컴포넌트의 재사용성과 테스트 용이성을 저하시킬 수 있습니다.
  3. 디버깅 어려움
    • ref를 통한 명령형 코드는 선언적 방식에 비해 디버깅이 어렵습니다.
    • 상태 변화의 추적이 복잡해질 수 있습니다.
  4. 코드 예측성 저하
    • 컴포넌트의 동작이 props만으로는 예측하기 어려워집니다.
    • 숨겨진 의존성이 생길 수 있습니다.

대안적 접근 방법

useImperativeHandle 대신 다음과 같은 방법을 고려할 수 있습니다.

  1. 상태 끌어올리기
    • 필터 상태를 부모 컴포넌트로 끌어올려서 관리
    • props를 통한 명시적인 데이터 흐름 유지
  2. 콜백 props 사용
    • 메서드 호출 대신 이벤트 핸들러를 props로 전달
    • 자식 컴포넌트의 변화를 부모에게 알리는 방식 사용
  3. Context API 활용
    • 전역 상태 관리가 필요한 경우 Context 사용
    • 컴포넌트 계층 구조 유지하면서 상태 공유

결론

useImperativeHandle은 강력한 기능이지만, React의 기본 철학과 충돌할 수 있는 위험이 있습니다. 특별한 경우(예: 서드파티 라이브러리 통합, 레거시 코드 마이그레이션 등)를 제외하고는 가급적 props와 상태 끌어올리기 같은 React의 기본적인 패턴을 사용하는 것이 권장됩니다. 필요한 경우에만 신중하게 사용하고, 가능한 한 선언적 방식의 상태 관리를 유지하는 것이 좋습니다.