본문 바로가기

React

React 18 알아보기

목차

  • 사전 준비
  • 주요 기능
    • 자동 배치
    • 동시성
      • useTransition
      • Suspense
    • 새로운 훅
      • useId
      • useDeferredValue

 

사전 준비

 

React 18의 새로운 기능을 사용하기 위해서는 createRoot를 통해 앱을 렌더해야 한다.

기존의 ReactDOM.render는 deprecated 된다.

※ ReactDOM의 import 경로도 달라졌으니 주의하자.

// AS-IS
import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
  <React.StrictMode>
      <App />
  </React.StrictMode>,
  document.getElementById('root')
);

 

// TO-BE
import React from 'react';
import ReactDOM from 'react-dom/client';

const rootNode = document.getElementById('root');

ReactDOM.createRoot(rootNode).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
);

 

준비가 되었다면 주요 기능을 알아보자.

주요 기능1. 자동 배치

배치(Batch)란 리액트가 여러 개의 상태 없데이트를 하나의 리렌더링으로 처리하여 성능을 향상시키는 방법이다.

기존에도 배치가 있었지만 '이벤트 핸들러 내부에서 발생하는 상태 업데이트만' 배치처리를 지원했다.

fetch 후 콜백이나, setTimeout 콜백 등에서는 배치처리가 지원되지 않았다.

예를 들어 아래 코드의 setCount와 setFlag가 각각 리렌더링을 발생시킨다.

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

  function handleClick() {
    fetchSomething().then(() => {
      setCount((c) => c + 1)	// 리렌더링
      setFlag((f) => !f)	// 리렌더링
    })
  }

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

 

React 18부터는 자동배치를 통해 모든 업데이트가 배치처리 된다.

위 코드에서 마지막에 1번만 리렌더링이 실행된다.

주요 기능 2. 동시성

리액트가 추구하는 동시성: 여러 작업을 작은 단위로 나눠 우선순위를 정해, 그에 따라 작업을 번갈아 수행한다.

작업 간 전환이 빠르기 때문에 동시에 수행되는 것처럼 보여 사용자 경험을 향상시킨다.

동시성 기능 1. useTransition

useTransition은 상태 업데이트에 대한 우선순위를 설정할 수 있는 훅이다.

상태 업데이트를 두 가지로 분류한다면 다음과 같이 할 수 있습니다.

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

전환 업데이트 때문에 긴급 업데이트가 방해되면 안되는데, React 18 이전에는 상태 업데이트를 구분하는 방법이 없었다.

그래서 setTimeout이나 throttle, debounce 등의 테크닉을 써서 긴급 업데이트 방해를 우회하는 것이 최선이었다.

 

React 18부터는 아래와 같이 하면 된다.

  • startTransition으로 어떤 상태변화를 지연하고 싶은지 지정할 수 있다.
  • isPending을 통해 transition이 지연된 상태인지 여부를 알 수 있다.
const [isPending, startTransition] = useTransition();
const [inputValue, setInputValue] = useState();
const [searchQuery, setSearchQuery] = useState();

const handleChange = (e) => {
    const input = e.target.value;
    setInputValue(input);
    
    startTransition(() => {
    	setSearchQuery(input);
    });
};

return (
    <>
    	<input onChange={handleChange} />
        <button disabled={isPending}>버튼</button>
    </>
);

 

동시성 기능 2. Suspense

SSR에서 사용되는 기능이다.

CSR과 SSR을 비교해보자.

 

SSR은 HTML을 먼저 보내면서 사용자에게 의미있는 데이터를 더 신속하게 출력할 수 있다는 장점이 있다..만

여전히 waterfall 방식이다.

특정 컴포넌트를 렌더링 하는데 필요한 Datafetching이 오래 걸린다면? 혹은 코드량이 많거나, 로직이 매우 복잡하다면?

HTML을 보내는데 지연이 발생해서 결국 사용자는 빈 화면을 오래 보게 된다.

 

Suspense를 사용하면 앱을 더 작은 독립적인 유닛으로 만들 수 있다.

Suspense 내부에 느린 컴포넌트를 감싸, 해당 컴포넌트의 로딩을 지연시킬 수 있다.

해당 컴포넌트가 아직 렌더링되지 않았어도, 다른 컴포넌트들은 hydration을 시작한다.

 

추가적으로 hydration간의 우선순위도 리액트에서 조정해준다.

예를 들어 2개 이상의 Suspense가 있다고 하자.

이때 사용자가 한 영역을 클릭한다면, 리액트는 해당 영역의 우선순위를 높여 먼저 hydration을 진행한다.

결론적으로 '사용자의 관심에 따라 인터랙션이 가능한 컴포넌트 부터 기능을 제공'하여 사용자 경험이 향상된다.

주요 기능 3. 새로운 훅

useId

서버와 클라이언트에서 사용될 고유 ID를 생성한다.

렌더링마다 난수 값이 달라지는 위험을 피할 수 있고, 클라이언트와 서버 간의 hydration 불일치를 피할 수 있다.

function CreateID() {
  const id = useId();

  return <input id={id} type="text" />;
};

useDeferredValue

트리에서 긴급하지 않은 부분의 리렌더링을 연기할 수 있다.

useTransition은 함수를 래핑하여 지연한다면, useDeferredValue는 값을 래핑하여 해당 값의 변화를 지연한다.

(마치 useCallback은 함수를 memoize하고 useMemo는 값을 memoize 하는 것과 비슷)

공식문서의 예시를 보는 것이 가장 좋다.

이 예시도 좋다.

'React' 카테고리의 다른 글

Create-React-App에 대해 알아보자  (0) 2022.11.16
간단한 커스텀 훅 예제 (useInput)  (0) 2021.05.01