프리온보딩_리액트해부학_React18 업데이트된 기능

Eugenius1st·2023년 3월 30일
0
post-thumbnail

리액트 해부학 사전 과제

과제리스트

  1. React Lifecycle에 대해 간단히 설명해주세요
  2. React18에서 업데이트 된 기능에 대해 설명해주세요
  3. React18에서 추가된 hook들에 대해 설명해주세요
  4. 요즘 관심있는 주제가 있다면 알려주세요

2. React18 에서 업데이트 된 기능

🌿자동 배치(Automatic Batching)

배치란, 리액트가 더 나은 성능을 위해 여러 개의 상태 업데이트를 한 번의 리렌더링(re-render)으로 묶는 작업이다.

function App() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);

  function handleClick() {
    setCount(c => c + 1); // 리렌더링 전
    setFlag(f => !f); // 리렌더링 전
    // 리액트는 오직 마지막에만 리렌더링을 한 번 수행한다. (배치 적용)
  }

  return (
    <div>
      <button onClick={handleClick}>Next</button>
      <h1 style={{ color: flag ? "blue" : "black" }}>{count}</h1>
    </div>
  );
}

위의 handleClick 이벤트 핸들러 함수에서는 상태 업데이트를 두 번(setCount, setFlag) 수행했지만, 리액트는 배치를 통해 두 번의 업데이트를 한 번의 리렌더링으로 처리한다. 이를 통해 불필요한 렌더링을 방지하고 의도치 않은 버그를 예방할 수 있다.

React 17 과 비교

function App() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);

  function handleClick() {
    fetchSomething().then(() => {
      // 리액트 17 및 그 이전 버전에서는 배치가 수행되지 않는다. 왜냐하면
      // 이 코드들은 이벤트 이후의 콜백에서 실행되기 때문이다.
      setCount(c => c + 1); // 리렌더링 
      setFlag(f => !f); // 리렌더링
    });
  }

  return (
    <div>
      <button onClick={handleClick}>Next</button>
      <h1 style={{ color: flag ? "blue" : "black" }}>{count}</h1>
    </div>
  );
}

배치 작업은 React 이벤트 핸들러 내에서만 수행되므로, Promise 내부의 업데이트, setTimeout, 기본 이벤트 핸들러 또는 기타 이벤트에서는 처리되지 않았다.

setTimeout(() => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // 리액트는 오직 마지막에만 리렌더링을 한 번 수행한다. (배치 적용)
}, 1000);

fetch(/*...*/).then(() => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // 리액트는 오직 마지막에만 리렌더링을 한 번 수행한다. (배치 적용)
})

elm.addEventListener('click', () => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // 리액트는 오직 마지막에만 리렌더링을 한 번 수행한다. (배치 적용)
});

하지만, React 18부터 자동 배치(Automatic Batching)라는 것이 추가되었다. 자동 배치는 위와 같이 일반적인 React 이벤트 핸들러 함수 스코프에서 상태 업데이트가 일어나지 않더라도 자동으로 배치를 적용해준다.

자동 배치를 사용하기 위해서는 컴포넌트 트리를 기존의 ReactDOM.render 함수 대신 새로운 ReactDOM.createRoot 함수를 사용해야 한다.

import { flushSync } from 'react-dom';

function handleClick() {
  flushSync(() => {
    setCounter(c => c + 1);
  });  // 리액트는 즉시 DOM을 업데이트 한다.

  flushSync(() => {
    setFlag(f => !f);
  });  // 리액트는 즉시 DOM을 업데이트 한다.
}

그리고 상태 업데이트에 자동 배치가 적용되지 않았으면 하는 경우에는 새롭게 추가된 ReactDOM.flushSync 함수를 사용할 수 있다.

🌿2. Transitions(전환)

전환은 긴급 업데이트와 긴급하지 않은 업데이트를 구분하기 위한 React의 새로운 개념이다.

  • 긴급 업데이트(Urgent updates) : 직접적인 상호 작용 반영(타이핑, 오버, 스크롤링 등)
  • 전환 업데이트(Transition updates) : 하나의 뷰에서 다른 뷰로의 UI 전환

긴급 업데이트는 사용자의 입력에 따라 즉각적으로 업데이트되지 않으면 문제(화면 멈춤, 렉 등)가 있다고 느끼는 영역이다. 반면 전환 업데이트는 화면에 즉시 나타나는 걸 기대하지 않는 영역이다.

React 18 이전까지는 상태 업데이트를 긴급과 전환 업데이트로 명시하는 방법이 없었다. 모든 상태는 긴급 업데이트로 적용하기 때문에, setTimeout이나 throttle, debounce 등의 테크닉을 사용해 긴급 업데이트 방해를 우회하는 것이 최선이었다.

하지만, React 18부터는 startTransitionAPI를 제공함으로써 전환 업데이트를 명시적으로 구분하여 상태 업데이트를 진행할 수 있게 되었다.

import { useTransition } from 'react';

function SearchBar() {
	const [isPending, startTransition] = useTransition();

  // ...

	function handleChange(e) {
		const input = e.target.value;

		// 긴급 업데이트: 타이핑 결과를 보여준다.
		setInputValue(input);

		// 이 안의 모든 상태 업데이트는 전환 업데이트가 된다.
		startTransition(() => {
		  // 전환 업데이트: 결과를 보여준다.
		  setSearchQuery(input);
		});
	}

  // ...
}

startTransition의 경우 크게 두 가지 Use Cases가 있다.

  • 느린 렌더링 : 작업량이 많아 결과를 보여주기 위한 UI 전환까지 시간이 걸린다.
  • 느린 네트워크 : 네크워크로부터 데이터를 기다리기 위한 시간이 걸린다.(Suspense와 연계)

🌿3. 서스펜스(Suspense)를 지원하는 새로운 서버 사이드 렌더링 아키텍처

React18에서는 새로운 서버 사이드 렌더링(이하 SSR) 아키텍처가 적용되었다. 새롭게 pipeToNodeWritable API가 추가 되었고, 이 API를 사용하면 SSR을 통해 <Suspense>를 사용할 수 있게 되었다.

즉, React.lazy를 서버 사이드 렌더링에서 사용할 수 있게 되었다.

// lazy 컴포넌트
const OtherComponent = React.lazy(() => import('./OtherComponent'));

React.lazy는 동적 import를 사용하여 컴포넌트를 렌더링할 수 있게 해주는 함수이다.

이러한 컴포넌트를 lazy 컴포넌트라고 하는데, 이 컴포넌트는 반드시 <Suspense>컴포넌트 하위에서 렌더링되어야 한다. 지금까지는 이 lazy 컴포넌트와 <Suspense>를 서버 사이드 렌더링에서 사용할 수 없었다는 것이 문제였다.

하지만 React 18부터는 새로운 렌더링 API인 pipeToNodeWritable 덕분에, <Suspense>와 함께 lazy 컴포넌트를 사욯할 수 있게 되어 앱을 더 작은 독립적인 유닛으로 만들 수 있다.

현재 리액트 생태계의 주류 환경인 웹팩 기반의 애플리케이션에서, lazy 컴포넌트를 사용하면 코드 스플리팅(Code Splitting)이 적용되어 별도의 자바스크립트 Chunk 파일로 분리된다. 그리고 이 <Suspense> 컴포넌트 하위 트리의 렌더링 외부 트리의 렌더링 과정을 막지 않고 별도의 과정이 진행된다.


출처: https://doqtqu.tistory.com/348#2.5.%20useInsertionEffect

profile
최강 프론트엔드 개발자가 되고싶은 안유진 입니다

0개의 댓글