[React]React 버전 18과 19에서의 ref 전달 방식 비교 분석하기(forwardRef)

    ForwardRef란?

    ForwardRef는 부모 컴포넌트가 자식 컴포넌트의 DOM 요소에 직접 접근할 수 있게 해주는 React의 기능이다. React 18 버전까지는 컴포넌트 간에 ref를 전달하기 위해 필수적으로 사용되었다.

     

    부모 컴포넌트에서 자식 컴포넌트의 특정 이벤트를 직접 호출할 때 사용했다. 자식 컴포넌트에 forwardRef 함수로 감싸주면, 부모의 ref를 자식 컴포넌트에 사용할 수 있다. 나의 경우 자식 컴포넌트의 refetch 함수를 부모 컴포넌트에서 특정 이벤트가 발생하면 호출되도록 하기 위해 사용했다. 

     

    React 버전별 ref 전달 방식 비교

    구분 React 18 이전 React 19
    전달 방식 forwardRef 필수 사용 prop으로 직접 전달 가능
    코드 예시 const ChildInput = forwardRef((props, ref) => { return <input ref={ref}/> }); const ChildInput = ({ ref }) => { return input; };
    특징 - ref는 일반 함수형 컴포넌트의 매개변수로 전달 불가
    - forwardRef로 ref 연결 필요
    - ref가 props의 일부로 처리
    - 구조 분해 할당으로 간단히 사용
    장점 - 명시적인 ref 전달 방식 - 더 직관적이고 간결한 코드 작성

     

    React 19

    2024년 12월 기준 React 19 공식 문서의 내용은 다음과 같다.

    • forwardRef가 deprecated될 예정
    • 함수형 컴포넌트에서 ref를 일반 prop으로 직접 전달 가능 (출처)
    //forwardRef 없이도 ref를 컴포넌트에서 사용할 수 있다.
    const ChildInput = ({ref}) => {
        return <input ref={ref}/>
    };

    React 18.3.1 

    forwardRef 사용법 

    forwardRef를 사용하는 방법은 간단하다:

    1. 자식 컴포넌트를 forwardRef로 감싸기
    2. 두 번째 매개변수로 ref 받기
    3. 받은 ref를 원하는 DOM 요소에 연결하거나 useImperativeHandle를 이용해서 자식 컴포넌트의 메서드 호출하면 된다.
    // 자식 컴포넌트
    import {forwardRef, useImperativeHandle, useRef} from "react";
    import {Button} from "@chakra-ui/react";
    
    const ChildComponent = forwardRef((props, ref) => {
        useImperativeHandle(ref, () => ({
            customFocus: () => {
                console.log("자식 컴포넌트의 메서드 호출됨!");
            }
        }));
    
        return <div>자식 컴포넌트</div>;
    });
    
    // 부모 컴포넌트
    const AsIs = () => {
        const childRef = useRef();
    
        const handleClick = () => {
            childRef.current.customFocus();
        };
    
        return (
            <>
                <ChildComponent ref={childRef} />
                <Button onClick={handleClick}>AsIs</Button>
            </>
        );
    };
    
    export default AsIs;

     

    useImperativeHandle를 사용하면 다음과 같은 장점이 있다.

    • 자식 컴포넌트의 내부 메서드를 부모에 노출시킬 수 있다.
    • DOM 요소에 직접 접근하지 않고도 자식 컴포넌트와 상호작용이 가능하다.
    • 커스텀 메서드나 값들을 ref를 통해 부모에게 제공할 수 있다.

    React 19 이전 버전에서 prop으로 직접 ref를 전달하면 어떻게 될까?

    직접 전달을 시도하면 다음과 같은 문제가 발생한다:

    • ref prop으로 전달 시 undefined 반환
    • 함수형 컴포넌트에서 ref 사용 불가 경고 발생
    • TypeError 발생 가능성

    에러로그는 다음과 같다.

     

    1. `ref`는 prop이 아니고 여기에 접근하면 `undefined`가 반환됩니다. 자식 컴포넌트 내에서 동일한 값에 접근해야 하는 경우, 다른 prop으로 전달해야 합니다.

    Before: `ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop.

     

    2. 함수형 컴포넌트에는 ref를 전달할 수 없습니다. ref에 접근하려는 시도는 실패할 것입니다. React.forwardRef()를 사용하려고 하셨나요?

    Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

     

    그래서 forwardRef가 되기 전인 16.3 이전 버전에서는 다음과 같이 우회하는 방식을 사용했다고 한다. (출처)

     <ChildComponent inputRef={childRef} />

     

    forwardRef를 사용하지 않은 상태에서 위와 같은 방법으로 테스트하니 정상적으로 동작되는 걸 확인했다. 

     

     


    결론

    React 19 이전: forwardRef를 사용해야 ref를 전달할 수 있다.

    React 19 이후: ref를 prop으로 직접 받을 수 있으므로, { ref }로 구조 분해 할당하면 간단히 사용할 수 있다.

     

    마치며

    더보기

    업무에서 사용하고는 있었지만 딥하게 공부하지 못했던 개념이었는데, 이번 기회에 정리할 수 있어서 좋았다.

    리액트 강의 들을 때는 최신 공식 문서와 비교해서 공부해야겠다는 생각이 들었다. 

    728x90

    댓글