Udemy React 강의 140번 정리
<ResultModal ref={dialog} />
// 그리고 외부에서
dialog.current.showModal()
이전 방식은 이러하다. TimerChallenge 컴포넌트는 ResultModal 내부가 dialog라는 사실을 알고 있어야 하고, 만약 내부 DOM이 <dialog>에서 <div>로 바뀌면? showModal() 메서드는 존재하지 않고, 호출에 실패하여 결국 에러를 발생시킨다.
즉, 내부 구현을 외부가 알고 있어야 하는 구조, 즉, 매우 취약한 API가 된다.
큰 규모의 팀 프로젝트에서는 컴포넌트가 내부 DOM 구조를 바꿔도 외부 기능은 유지되는 게 중요하다.
useImperativeHandleref.current에 내가 정의한 메서드를 저장해서 외부에서 호출할 수 있게 해준다.
예시 :
useImperativeHandle(ref, () => ({
open() {
dialog.current.showModal();
}
}));
이제 외부에서는 다음만 호출하면 됨 :
dialog.current.open();
dialog가 뭔지 몰라도 됨.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();
open()이라는 API만 알면 됨. 그리고 이 open이라는 이름은 내 마음대로 지을 수 있음. openSesame()여도 됨. 다만 팀 협업 시 이름은 의미가 명확해야 한다는 것.| 개념 | 설명 |
|---|---|
| 사용 목적 | 컴포넌트 내부 기능을 외부에서 제어할 수 있도록 API 형태로 노출 |
| 왜 필요한가? | 외부가 내부 DOM 구조(showModal 등)에 의존하지 않도록 하기 위함 |
| 전달되는 내용 | ref.current에 커스텀 메서드(open, close 등)를 가진 객체가 저장됨 |
| 내부 DOM 변경 영향 | DOM이 바뀌어도 API(open 등)만 유지하면 외부는 영향 없음 |
| forwardRef 필요? | React 최신 버전에서는 선택사항, 구버전에서는 필수 |
| 사용하면 안 되는 경우 | props로 UI 제어가 가능한 경우 (예: isOpen, toggle 상태 전달) |
| 적합한 사용 예 | Modal, toast, scroll, focus 등 '명령 기반(imperative)' UI 제어 |