[React 공식문서 정독] useImperativeHandle?

김진서·2025년 5월 1일

우아한테크코스 7기

목록 보기
36/56
post-thumbnail

useImperativeHandle를 사용하면서 가장 헷갈릴 수 있는 부분들은 주로 이 훅이 어떤 목적으로 사용되는지, 그리고 어떤 상황에서 사용해야 하는지에 대한 이해 부족에서 발생합니다. useImperativeHandle는 특정 컴포넌트가 부모 컴포넌트에 노출하고 싶은 특정 메소드나 속성만을 ref를 통해 사용할 수 있도록 커스터마이징할 때 사용되는 React Hook이다. 이 훅을 사용하면서 헷갈릴 수 있는 핵심적인 부분들을 알아보자.

1. useImperativeHandle의 목적과 사용 시점?

useImperativeHandle은 ref를 통해 노출되는 핸들을 커스터마이징하기 위한 도구이다. React는 기본적으로 선언형 패러다임을 지향하므로, 대부분의 상호작용은 props와 state를 통해 이루어져야 합니다. useImperativeHandle는 스크롤, 포커스, 애니메이션 트리거 등과 같이 props로 표현하기 어려운 명령형(imperative) 동작을 노출해야 할 때 사용하는 일종의 "이스케이프 해치(escape hatch)"로 간주된다.

따라서 과도하게 사용해서는 안 되며, 모달 열기/닫기와 같은 기능은 isOpen prop과 같이 선언형 방식으로 처리하는 것이 더 좋다. 언제 useImperativeHandle가 필요한 명령형 상황인지, 언제 props/state로 충분한 선언형 상황인지 구분하는 것이 헷갈릴 수 있다.

2. 기본 ref 동작의 오버라이딩

부모 컴포넌트가 자식 컴포넌트에게 ref prop을 전달하고, 자식 컴포넌트 내부에서 이 ref를 내장 DOM 요소(예: <input ref={ref}>)에 연결하면, 기본적으로 부모는 해당 DOM 노드 자체에 접근할 수 있게 된다.

하지만 자식 컴포넌트에서 useImperativeHandle를 사용하면, 이 기본 동작이 오버라이딩된다. 부모 컴포넌트의 ref.current는 더 이상 내부 DOM 노드를 직접 가리키지 않고, useImperativeHandle의 두 번째 인자인 createHandle 함수가 반환하는 객체를 가리키게 된다. 이 객체에는 개발자가 명시적으로 노출하기로 결정한 메소드나 속성만 포함됩니다. 어떤 메소드를 노출할지는 createHandle 함수 내에서 정의한다. 이처럼 useImperativeHandle 사용 여부에 따라 ref.current가 가리키는 대상이 달라진다는 점이 혼동을 줄 수 있다.

3. 내부 useRef와의 연계

useImperativeHandle을 사용하여 특정 DOM 메소드(예: focus())만 노출하려는 경우, 자식 컴포넌트 내부에서는 여전히 해당 DOM 노드 자체에 접근할 필요가 있다. 이때는 별도의 내부 useRef를 사용하여 실제 DOM 노드를 참조하고, useImperativeHandle의 createHandle 함수 내에서 이 내부 ref를 사용하여 원하는 메소드를 호출하는 로직을 구현한다.

즉, ref.current.focus()와 같은 코드를 부모에서 호출하면, 자식 컴포넌트의 useImperativeHandle에 의해 정의된 focus 함수가 실행되고, 그 함수 내부에서 내부Ref.current.focus()를 호출하는 형태가 된다. useImperativeHandle가 부모에게 노출하는 외부 인터페이스 역할만 하고, 실제 작업은 내부 useRef가 참조하는 요소를 통해 이루어진다는 구조가 복잡하게 느껴질 수 있다.

4. React 버전별 ref prop 전달 방식

React 18 이하 버전에서는 사용자 정의 컴포넌트에서 ref를 받기 위해 forwardRef HOC(Higher-Order Component)를 반드시 사용해야 했다. 그러나 React 19부터는 ref가 일반 prop처럼 직접 전달된다.

useImperativeHandle 자체의 사용법은 비슷하지만, 자식 컴포넌트가 ref prop을 받는 방식의 변화(forwardRef 필수 -> 일반 prop)가 코드를 이해하거나 작성할 때 혼동을 줄 수 있다.

5. 의존성 배열 (dependencies)

useImperativeHandle의 세 번째 인자는 의존성 배열이다. 이 배열에 포함된 값이 변경될 때마다 createHandle 함수가 다시 실행되어 새로운 핸들 객체가 생성되고 ref.current에 할당된다. 다른 훅들(예: useEffect, useMemo)과 마찬가지로, 의존성 배열을 올바르게 관리하지 않으면 원치 않는 시점에 핸들이 재생성되거나, 반대로 필요한 시점에 핸들이 업데이트되지 않는 문제가 발생할 수 있다.

요약하자면, useImperativeHandle의 가장 큰 혼동 지점은 이것이 명령형 동작을 위한 예외적인 도구라는 점, 기본적인 ref 동작을 오버라이드하여 ref.current가 가리키는 대상을 변경시킨다는 점, 그리고 이를 위해 종종 내부 useRef와 함께 사용된다는 복잡한 구조 때문이다. 이러한 점들을 명확히 이해해야 올바르게 사용할 수 있다.

profile
PAy IT forwaRD를 실천하는 프론트엔드 개발자.

0개의 댓글