useImperativeHandle — 컴포넌트의 내부 기능(API)을 외부에 노출하는 방법

장효정·2025년 12월 4일

Udemy React 강의 140번 정리

<ResultModal ref={dialog} />

// 그리고 외부에서
dialog.current.showModal()

이전 방식은 이러하다. TimerChallenge 컴포넌트는 ResultModal 내부가 dialog라는 사실을 알고 있어야 하고, 만약 내부 DOM이 <dialog>에서 <div>로 바뀌면? showModal() 메서드는 존재하지 않고, 호출에 실패하여 결국 에러를 발생시킨다.

즉, 내부 구현을 외부가 알고 있어야 하는 구조, 즉, 매우 취약한 API가 된다.

큰 규모의 팀 프로젝트에서는 컴포넌트가 내부 DOM 구조를 바꿔도 외부 기능은 유지되는 게 중요하다.

해결 방법 — useImperativeHandle

ref.current에 내가 정의한 메서드를 저장해서 외부에서 호출할 수 있게 해준다.

예시 :

useImperativeHandle(ref, () => ({
  open() {
    dialog.current.showModal();
  }
}));

이제 외부에서는 다음만 호출하면 됨 :

dialog.current.open();
  • 이제 외부는 dialog가 뭔지 몰라도 됨.
  • 내부에서 DOM 구조가 바뀌어도 open()만 유지하면 되는 API 구조가 탄생!

코드 구조

ResultModal.jsx (구버전 방식 : forwardRef 사용)

import { forwardRef, useRef, useImperativeHandle } from "react";

const ResultModal = forwardRef(function ResultModal(props, ref) {
  const dialog = useRef();

  useImperativeHandle(ref, () => ({
    open() {
      dialog.current.showModal();
    }
  }));

  return <dialog ref={dialog}>...</dialog>;
});

export default ResultModal;

TimerChallenge.jsx

const dialog = useRef();

<ResultModal ref={dialog} />

dialog.current.open();
  • 외부는 실제 HTML 요소의 메서드를 몰라도 되고, open()이라는 API만 알면 됨. 그리고 이 open이라는 이름은 내 마음대로 지을 수 있음. openSesame()여도 됨. 다만 팀 협업 시 이름은 의미가 명확해야 한다는 것.

useImperativeHandle 정리

개념 설명
사용 목적 컴포넌트 내부 기능을 외부에서 제어할 수 있도록 API 형태로 노출
왜 필요한가? 외부가 내부 DOM 구조(showModal 등)에 의존하지 않도록 하기 위함
전달되는 내용 ref.current에 커스텀 메서드(open, close 등)를 가진 객체가 저장됨
내부 DOM 변경 영향 DOM이 바뀌어도 API(open 등)만 유지하면 외부는 영향 없음
forwardRef 필요? React 최신 버전에서는 선택사항, 구버전에서는 필수
사용하면 안 되는 경우 props로 UI 제어가 가능한 경우 (예: isOpen, toggle 상태 전달)
적합한 사용 예 Modal, toast, scroll, focus 등 '명령 기반(imperative)' UI 제어

0개의 댓글