blog
React forwardRef 정리와 React 19의 ref prop
Front-end / React#React#ref#forwardRef#TypeScript
ref가 필요한 경우
ref는 DOM 요소에 직접 접근하는 탈출구다. 선언적으로 표현하기 어려운 작업에 쓴다.
- 입력 필드에 포커스 주기, 텍스트 선택
<video>재생·정지- 요소의 크기·위치 측정, 스크롤 이동
- DOM을 직접 다루는 서드파티 라이브러리 연동
forwardRef가 필요했던 이유 (React 18 이하)
ref는 key처럼 React가 특별 취급하는 속성이라 일반 props로 전달되지 않는다. 함수 컴포넌트에 ref를 넘기면 null이 되고 경고가 출력된다.
부모가 자식 컴포넌트 내부의 DOM 요소에 접근해야 할 때, 자식이 forwardRef로 감싸서 받은 ref를 원하는 요소에 연결한다. 자식은 내부 구조를 숨긴 채 필요한 노드만 노출할 수 있다.
import { forwardRef, useRef, type ComponentPropsWithoutRef } from 'react';
interface MyInputProps extends ComponentPropsWithoutRef<'input'> {
label: string;
}
const MyInput = forwardRef<HTMLInputElement, MyInputProps>(
({ label, ...inputProps }, ref) => (
<label>
{label}
<input ref={ref} {...inputProps} />
</label>
),
);
MyInput.displayName = 'MyInput';
function Parent() {
const inputRef = useRef<HTMLInputElement>(null);
return (
<>
<MyInput ref={inputRef} label="이름" placeholder="이름을 입력하세요" />
<button onClick={() => inputRef.current?.focus()}>입력창에 포커스</button>
</>
);
}
forwardRef<RefType, PropsType>: 제네릭 순서가 ref 타입, props 타입 순이다(렌더 함수 인자 순서와 반대).label은 구조 분해로 빼고 나머지만<input>에 펼친다.{...props}를 통째로 넘기면label같은 커스텀 prop이 DOM 속성으로 들어간다.forwardRef로 감싼 화살표 함수는 DevTools에서 이름이ForwardRef로만 보이므로displayName을 지정해 두면 디버깅이 편하다.
React 19: ref는 그냥 prop
React 19부터 함수 컴포넌트는 ref를 일반 prop으로 받을 수 있다. forwardRef 없이 같은 컴포넌트를 쓸 수 있고, forwardRef는 앞으로 deprecated될 예정이다.
import type { ComponentProps } from 'react';
interface MyInputProps extends ComponentProps<'input'> {
label: string;
}
function MyInput({ label, ref, ...inputProps }: MyInputProps) {
return (
<label>
{label}
<input ref={ref} {...inputProps} />
</label>
);
}
ComponentProps<'input'>에는 ref 타입이 포함되어 있다. 새로 작성하는 코드가 React 19 이상이라면 이 방식을 사용한다.
함께 알아두면 좋은 것
- useImperativeHandle: DOM 노드 전체 대신
focus(),scrollIntoView()처럼 일부 메서드만 노출하고 싶을 때 사용한다. - ref는 탈출구다. 열기/닫기처럼 props와 상태로 표현할 수 있는 동작은 ref로 명령하지 말고 props로 제어한다.