Geuni

리액트 19에서 추가된 기능, ref cleanup function

최근 callbackRef에 관한 글을 썼었다.
과정에서 알게 된 내용이 있는데, React 19에서는 ref에 clean up 함수가 추가되었다는 점이다.

마침 블로그에서 Sandpack을 설정해두었는데, 사용사례가 없어서 고민하던 찰나였다.
따로 보일러플레이트 설정 없이 여기서 해당 부분을 테스트해보자.

TL;DR

React 19에서는 ref에 clean up 함수가 추가되었다.

const ChildComponent = () => {
  const callbackRef = (node: HTMLDivElement) => {
    if (node !== null) {
      console.log("element is mounted");
    } else {
      console.log("element is null");
    }

    return () => {
      console.log("unmounted");
    };
  };

  return <div ref={callbackRef}>Child</div>;
};

두 개의 예제 로그를 확인해보자.

React 18

import { useState } from "react";

function App() {
  const [showChild, setShowChild] = useState(true);

  const toggleChild = () => {
    setShowChild((prev) => !prev);
  };

  return (
    <div>
      <button onClick={toggleChild}>Toggle Child</button>
      {showChild && <ChildComponent />}
    </div>
  );
}

const ChildComponent = () => {
  const callbackRef = (node: HTMLDivElement) => {
    if (node !== null) {
      console.log("element is mounted");
    } else {
      console.log("element is null");
    }

    // 주석을 해제하면 Warning이 발생한다.
    // return () => {
    //   console.log("unmounted");
    // };
  };

  return <div ref={callbackRef}>Child</div>;
};

export default App;

18버전에서는 clean up 함수를 설정하면 에러가 난다.
즉 unmounted 시, null로 설정된다.


React 19

import { useState } from "react";

function App() {
  const [showChild, setShowChild] = useState(true);

  const toggleChild = () => {
    setShowChild((prev) => !prev);
  };

  return (
    <div>
      <button onClick={toggleChild}>Toggle Child</button>
      {showChild && <ChildComponent />}
    </div>
  );
}

const ChildComponent = () => {
  const callbackRef = (node: HTMLDivElement) => {
    if (node !== null) {
      console.log("element is mounted");
    } else {
      console.log("element is null");
    }

    return () => {
      console.log("unmounted");
    };
  };

  return <div ref={callbackRef}>Child</div>;
};

export default App;

19버전에서는 clean up 함수가 정상적으로 동작한다. unmounted 시, clean up 함수가 실행된다.
DEV 기준으로 strict mode가 켜져있어서 mount 될 때 log가 두 번 찍히는 것을 볼 수 있다.

mount → clean up(unmount) → mount

cc. Fixing bugs found by re-running ref callbacks in development


Release Note의 중요성

경험 하나를 공유하고 싶다.
나는 최근까지 회사에서 React 16버전을 사용했었다.
내가 개발을 처음 시작할 때, React 17버전이었고, 부트캠프에서 마지막 프로젝트를 진행하던 중 18버전이 릴리스되었다.

즉 최근에 다녔던 회사에서 처음으로 React 16버전을 사용해본 것이다.

'큰 차이는 없을거야.'

당시 나는 큰 차이 없을 것이라고 생각했는데, 우연히 회사 컨플루언스를 뒤적이다가 발견한 글에서 처음보는 단어를 접했다. 바로 'Automatic Batching'이라는 단어였다.

키워드를 미리 숙지해놓은 덕분에, 16버전에서 setTimeout을 사용할 때 유연하게 대처할 수 있었다.
동료들이 한번씩 "이거 로그가 너무 많이 찍혀요"와 같은 문제를 겪을 때 해당 내용에 대해서도 설명해줄 수 있었다.

때론 메이저버전의 변경으로 라이브러리 기틀이 바뀌는 경우도 빈번히 존재한다.
Tanstack Table만 해도 v7문서를 참고해서 v8을 개발하려고 하면 도통 문제가 잘 해결되지 않는다.

작은 기능의 변화라도 본인이 사용하고 있는 라이브러리의 최신 트렌드를 따라갈 필요성이 있다고 느꼈다. 예상했던 동작이, 예상대로 동작하지 않을 때 머릿속에 혼란이 몰려온다.

'뭐지? 어디서 잘못된거지? 사이드이펙트가 발생했나? 이렇게 동작하는게 아닌가? 내가 잘못 이해하고 있었나? 에???'

사용한 외부 의존성(라이브러리)이 많을수록 더 혼란스러웠던 것 같다. 그래서 나는 되도록 가능하다면 최대한 최신버전을 유지하려고 했다.

무분별하게 버전이 나오자마자 업데이트하는게 아닌, 정말 필요하다 판단되면 회사 외적인 시간에 회사업무를 진행했다.

업무시간 외 일을 하는게 불만일 수도 있지만 전혀 그렇지 않다

이러한 개선은 나에게 온전히 이득으로 돌아온다. 개발할 때 예측가능하게 개발을 진행할 수 있기 때문이다.

적어도 버전이슈는 머릿속에서 배제할 수 있었다.


참고자료

React 19: Cleanup functions for refs
React docs: ref callback function
#15176: React callback ref cleanup function
ref 콜백의 클린업 함수

리액트 19에서 추가된 기능, ref cleanup function | geuni