ywc.life
Development 3분 읽기

React v19, forwardRef를 대체할 방법에 대해서 알아보자

작성자:
React v19, forwardRef를 대체할 방법에 대해서 알아보자

React v19에서 forwardRef가 더 이상 필요없어졌어요. 향후 버전에서는 forwardRef를 제거한다고도 하는데요. 대신 이제 refprop에서 접근할 수 있도록 바뀌었어요. ref를 일반 prop처럼 전달할 수 있게 되면서 코드가 간결해지고 타입 정의가 단순해졌어요. 이 글에서는 기존 forwardRef의 문제점과 React v19의 새로운 ref 전달 방식을 알아봐요.

forwardRef란?

forwardRef는 부모 컴포넌트가 자식 컴포넌트의 DOM 요소나 인스턴스에 직접 접근할 수 있도록 해주는 React API예요.

원래 React에서는 데이터가 부모에서 자식으로 한 방향으로만 흐르며, 함수형 컴포넌트는 기본적으로 ref를 직접 받을 수 없어요. 이런 한계를 극복하기 위해 제공된 기능이 forwardRef예요.

즉, 부모 컴포넌트에서 생성된 ref를 자식 컴포넌트 내부까지 전달하여, 부모가 자식의 특정 DOM 요소를 직접 조작(예: 포커스 주기, 스크롤 이동 등)할 수 있게 해주는 기능이에요.

리액트는 컴포넌트 간의 참조 전달을 위해 forwardRef를 도입하여 함수형 컴포넌트에서도 ref를 전달할 수 있도록 했어요. 그러나 forwardRef는 다음과 같은 문제점이 있었어요.

  • 가독성 저하: forwardRef를 사용하면 컴포넌트 정의가 복잡해져 코드의 가독성이 떨어져요.

  • 타입스크립트 문제: forwardRef를 사용할 때 타입 정의가 복잡해져요.

이러한 이유때문에 자식 컴포넌트의 DOM 요소를 조작할 때 불편함이 있었는데요.

리액트 설문조사

2024년 한 리액트 설문조사에서는 forwardRef를 사용해본 사용자를 대상으로 부정평가가 23%나 나왔어요. useState의 부정평가가 1%인 것을 대조했을 때 꽤나 큰 부정평가인데요.

forwardRef 사용법

forwardRef를 이용해 버튼 클릭 시 텍스트 영역을 맨 아래로 스크롤하는 예제예요.

import { forwardRef, useRef } from "react";

const ChildComponent = forwardRef<
  HTMLTextAreaElement,
  React.TextareaHTMLAttributes<HTMLTextAreaElement>
>((props, ref) => {
  return <textarea ref={ref} placeholder="여기에 입력하세요." {...props} />;
});

function ParentComponent() {
  const textareaRef = useRef<HTMLTextAreaElement>(null);
  const handleChildComponentScrollToBottom = () => {
    if (textareaRef.current) {
      textareaRef.current.scrollTop = textareaRef.current.scrollHeight;
    }
  };
  return (
    <div>
      <ChildComponent
        ref={textareaRef}
        rows={10}
        value={new Array(100000).fill("a").join("")}
        style={{ height: "100px" }}
      />
      <button onClick={handleChildComponentScrollToBottom}>한번에 아래로 스크롤하기</button>
    </div>
  );
}

자식 컴포넌트(ChildComponent)의 DOM 요소를 조작하기 위해 자식 컴포넌트를 forwardRef로 선언하고 부모 컴포넌트에서 생성한 참조(ref)를 전달하여 자식 컴포넌트 내부의 DOM 요소에 직접 접근할 수 있게 해요.

부모 컴포넌트인 ParentComponent에서는 textareaRef라는 참조를 생성하여 ChildComponent에 전달해요. 이렇게 전달된 참조는 자식 컴포넌트 내부의 <textarea> 요소와 연결돼요.

부모 컴포넌트에 있는 버튼을 클릭하면 handleChildComponentScrollToBottom 함수가 호출되면서, 자식 컴포넌트의 텍스트 영역을 가장 아래로 스크롤하도록 동작해요. 즉, 부모 컴포넌트에서 자식 컴포넌트 내부의 DOM에 직접 접근하여 특정 동작(스크롤 등)을 수행할 수 있게 돼요.

이러한 방식은 코드의 복잡성을 증가시키고 DOM에 직접 접근하기 때문에 useImperativeHandle같은 대체 방법을 사용하는 게 권장돼요.

하지만 리액트 v19 부터 forwardRef 가 사용 중단됨으로써 부모 컴포넌트가 자식 컴포넌트를 참조하는 방식이 바뀌었어요.

React v19에서 ref를 prop으로 직접 전달하기

이제 React v19 부터 자식 컴포넌트의 DOM 요소를 조작하기 위해서 자식 컴포넌트에서 ref를 노출시키면 돼요.

import { useRef } from "react";

function ChildComponent({ ref, ...props }: React.ComponentProps<"textarea">) {
  return <textarea ref={ref} placeholder="여기에 입력하세요." {...props} />;
}

function ParentComponent() {
  const textareaRef = useRef<HTMLTextAreaElement>(null);
  const handleChildComponentScrollToBottom = () => {
    if (textareaRef.current) {
      textareaRef.current.scrollTop = textareaRef.current.scrollHeight;
    }
  };
  return (
    <div>
      <ChildComponent
        ref={textareaRef}
        rows={10}
        value={new Array(100000).fill("a").join("")}
        style={{ height: "100px" }}
      />
      <button onClick={handleChildComponentScrollToBottom}>한번에 아래로 스크롤하기</button>
    </div>
  );
}

이제 자식 컴포넌트가 ref를 일반 prop처럼 받아서 바로 사용할 수 있어, 코드가 훨씬 간결해졌어요.

마무리하며

forwardRef 설문조사

React v19부터 forwardRef 대신 ref를 prop으로 전달하는 방식을 알아봤어요. 이를 통해 컴포넌트가 더 직관적이고 명확해졌으며, 코드의 가독성과 유지보수성도 향상되었어요. 마지막으로 forwardRef에 대한 설문조사 댓글을 보시며 마칠게요.

댓글