Front-end/React

[React] React Concurrent: 사용자 경험 향상 기술!🏂

xeunnie 2025. 1. 26. 01:00
728x90
반응형

React의 Concurrent를 톺아보자!🏂

React는 항상 사용자 경험(UX)을 최우선으로 고려하여 설계된 강력한 라이브러리라고 합니다. 특히 Concurrent Rendering(동시성 렌더링)은 이를 한층 더 끌어올린 기술인데요. 복잡한 사용자 인터페이스를 매끄럽게 처리할 수 있게 해주며, 사용자와의 상호작용에 즉각적으로 반응하도록 설계되었습니다.

 

 특히 여러 작업이 동시에 이루어져야 할 때 그 진가를 발휘합니다. 예를 들어, 대량의 데이터가 로드되거나 복잡한 애니메이션이 실행될 때도, 사용자가 애플리케이션을 사용하는 동안 불편함이나 지연을 최소화하여 UI의 부드러움과 반응성을 느낄 수 있습니다. 


🌀 Concurrent Rendering이란?

React가 기존에 렌더링을 처리하는 방식

React의 기본 렌더링은 동기적(Synchronous)입니다. 컴포넌트가 렌더링을 시작하면 전체 작업이 완료될 때까지 브라우저가 멈춘 것처럼 느껴질 수 있어요. 복잡한 UI에서는 이로 인해 버벅거림이 발생할 수 있죠.

Concurrent Rendering의 등장

Concurrent Rendering은 이런 문제를 해결하기 위해 등장했습니다. 작업을 작은 조각으로 쪼개서 브라우저가 사용자와의 상호작용(클릭, 스크롤 등)에 더 빠르게 반응할 수 있도록 만듭니다.

  • 동기 렌더링: 한 번에 하나의 작업을 끝낼 때까지 진행
  • Concurrent Rendering: 여러 작업을 중단했다가 다시 시작하며 진행

💡 Concurrent Rendering이 필요한 이유

  1. UI가 복잡해질수록 성능 문제가 생긴다.
    • 데이터가 많거나, 애니메이션, 그래프 같은 무거운 작업이 많으면 렌더링 시간이 길어져 사용자 경험이 나빠집니다.
  2. 사용자 상호작용의 즉시성을 유지
    • 클릭, 스크롤, 입력 등 즉각적인 반응을 요구하는 작업은 우선순위를 높게 두고 처리해야 합니다.
  3. 빠른 반응과 부드러운 UI를 동시에 제공
    • 중요한 작업과 덜 중요한 작업을 나눠서 처리함으로써 더 나은 UX를 제공합니다.

🔍 Concurrent Rendering의 핵심 개념

1️⃣ 작업을 중단하고 재개하기

React는 긴 작업이 진행되는 동안 특정 지점에서 멈추고,

더 중요한 작업을 처리한 후 다시 원래 작업으로 돌아와서 렌더링을 재개할 수 있습니다.

 

작업 순서 예시:

  1. 사용자가 입력하면 입력 작업이 고우선순위로 즉시 처리됩니다.(인터페이스의 반응성을 높일 수 있음)
  2. 이후 덜 중요한 작업(예: 데이터 로드)은 나중에 처리됩니다.(사용자가 입력하는 동안 방해받지 않고 나중에 처리됨)

2️⃣ 우선순위 기반 렌더링

React는 작업의 중요도를 기준으로 긴급한 작업부터 차례대로 처리합니다. (긴급의 기준: 즉각적인 반응이 요구되는지, 사용자와 직접적으로 상호작용이 필요한지,)

  • 높은 우선순위: 사용자 입력, 클릭, 애니메이션
  • 낮은 우선순위: 데이터 로드, 백그라운드 작업

3️⃣ Interruptible Rendering

중단 가능한 렌더링은 UI를 업데이트하면서도 중요한 사용자 이벤트를 놓치지 않게 도와줍니다.

 

작업 순서 예시:

  1. 사용자가 버튼을 클릭하면 그 클릭 이벤트는 시 처리되며, UI는 즉각적으로 반응합니다.
  2. 이 과정에서 긴 작업이 중단될 수 있습니다.

React는 사용자의 상호작용을 최우선으로 고려하여 이를 처리합니다.

따라서 사용자는 지연 없이 필요한 작업을 수행할 수 있어, 보다 원활한 경험을 하게 됩니다. 이러한 기능은 특히 복잡한 애플리케이션에서 사용자와의 인터랙션을 더욱 매끄럽게 만들어 주는 핵심 요소가 됩니다.


⚙️ Concurrent Rendering을 사용하는 방법

React의 Concurrent Rendering은 React 18 이상에서 지원됩니다.

1️⃣ createRoot 사용

Concurrent Rendering은 ReactDOM의 createRoot API를 통해 Concurrent Mode를 활성화할 수 있습니다. 주로 pages/_app.js 파일 또는 특정 페이지 컴포넌트에서 설정할 수 있습니다.

import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('root');
const root = createRoot(container); // Concurrent Rendering이 활성화되는 구간

root.render(<App />);

 

2️⃣ <Suspense>와 useTransition

Suspense

Suspense는 데이터가 준비될 때까지 대기 상태를 처리하는 데 사용됩니다. 예를 들어, 네트워크 요청 중 로딩 상태를 부드럽게 보여줄 수 있습니다.

import React, { Suspense } from 'react';

const Profile = React.lazy(() => import('./Profile'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Profile />
    </Suspense>
  );
}

 

useTransition

useTransition은 우선순위가 낮은 작업을 처리할 때 유용합니다.

import React, { useState, useTransition } from 'react';

function App() {
  const [isPending, startTransition] = useTransition();
  const [filter, setFilter] = useState('');

  const handleChange = (e) => {
    const value = e.target.value;
    startTransition(() => {
      setFilter(value); // 낮은 우선순위로 처리
    });
  };

  return (
    <div>
      <input type="text" onChange={handleChange} />
      {isPending ? <p>Updating...</p> : <List filter={filter} />}
    </div>
  );
}

 

startTransition

startTransition은 상태 업데이트를 비동기적으로 처리하여, 사용자 인터페이스의 반응성을 유지합니다.

3️⃣ useDeferredValue

useDeferredValue 훅은 사용자의 입력 값과 비동기적으로 업데이트할 값을 다르게 처리하여, 입력 반응성을 높이는 데 도움이 됩니다.

import React, { useState, useDeferredValue } from 'react';

const App = () => {
  const [inputValue, setInputValue] = useState('');
  const deferredValue = useDeferredValue(inputValue);

  return (
    <div>
      <input type="text" onChange={(e) => setInputValue(e.target.value)} />
      <p>Immediate value: {inputValue}</p>
      <p>Deferred value: {deferredValue}</p>
    </div>
  );
};

🖼️ Concurrent Rendering으로 UX 개선하기

1️⃣  느린 로딩 문제 해결

  • 사용자가 입력한 검색어에 따라 데이터를 필터링할 때, 이전에는 입력할 때마다 렌더링이 발생해 느려졌다면 Concurrent Rendering으로 부드럽게 처리 가능

2️⃣ 스크롤과 데이터 로드 동시 처리

  • 긴 목록을 스크롤할 때, 데이터 로딩이 진행 중이더라도 UI가 멈추지 않도록 최적화

3️⃣ 애니메이션 중단 없는 경험 제공

  • 애니메이션이 진행되는 도중, 새로운 데이터를 로드하거나 렌더링할 때에도 부드러운 화면 유지

🛠️ 실무에서의 적용 사례

검색 필터

검색어를 입력할 때, 필터링 작업을 낮은 우선순위로 처리하고 입력 반응은 즉각적으로 처리합니다.

  1. 즉각적인 사용자 입력 반응
    • 사용자가 검색어를 입력할 때마다 즉시 필터링된 결과를 보여주는 것이 중요합니다. Concurrent Rendering을 사용하면 사용자의 입력 이벤트를 높은 우선순위로 처리하여, 즉각적으로 UI를 업데이트할 수 있습니다.
    • 예를 들어, 사용자가 검색창에 입력하는 순간, 이전의 검색 결과를 즉시 지우고 새로운 검색 결과를 렌더링합니다.
  2. Debouncing 활용
    • 입력할 때마다 API 호출이 발생하면 성능 저하가 발생할 수 있습니다. 이를 방지하기 위해 Debouncing 기법을 적용할 수 있습니다. 사용자가 입력을 멈춘 후 일정 시간 후에 API 호출을 하여, 불필요한 렌더링을 줄일 수 있습니다.
    • 이 과정에서도 Concurrent Rendering을 활용하여, 사용자가 입력한 내용을 반영한 결과가 빠르게 나타나도록 할 수 있습니다.
  3. Loading 상태 관리
    • 필터링된 결과를 가져오는 동안 로딩 상태를 표시할 수 있습니다. Concurrent Rendering을 통해 로딩 중에도 UI가 멈추지 않고, 사용자가 다른 작업을 수행할 수 있도록 해줍니다.
    • Suspense를 사용하여 데이터 로딩 중에는 로딩 스피너를 보여주고, 데이터가 준비되면 결과를 표시하는 방식으로 사용자 경험을 개선할 수 있습니다.
  4. 결과 캐싱 및 최적화
    • 이미 검색한 결과를 캐싱하여, 같은 검색어에 대한 요청이 들어올 경우 즉시 결과를 보여줄 수 있습니다. 이때도 Concurrent Rendering을 통해 사용자에게 즉각적으로 피드백을 제공할 수 있습니다.
    • 이러한 캐싱 메커니즘은 특히 대량의 데이터를 다룰 때 성능을 크게 향상시킵니다.
import React, { useState, useEffect, Suspense } from 'react';

const fetchFilteredResults = async (query) => {
  // API 호출 로직 아무거나 뿅뿅
};

const SearchResults = ({ query }) => {
  const results = fetchFilteredResults(query);
  return (
    <div>
      {results.map(result => (
        <div key={result.id}>{result.name}</div>
      ))}
    </div>
  );
};

const SearchComponent = () => {
  const [query, setQuery] = useState('');
  const [debouncedQuery, setDebouncedQuery] = useState(query);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedQuery(query);
    }, 300); // 부가적으로 Debounce 설정해봄!

    return () => {
      clearTimeout(handler);
    };
  }, [query]);

  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Search..."
      />
      <Suspense fallback={<div>Loading...</div>}>
        <SearchResults query={debouncedQuery} />
      </Suspense>
    </div>
  );
};

export default SearchComponent;

 

❣️ 참고: Concurrent Rendering과 디바운스는 서로 다른 개념입니다. Concurrent Rendering은 React의 렌더링 방식으로, UI를 더 부드럽고 반응성 있게 만드는 기술입니다. 반면, 디바운스는 사용자 입력을 처리하는 방식으로, 특정 시간 동안 입력이 없을 때만 함수를 실행하도록 하는 기법입니다.


🌟 Concurrent Rendering의 한계

  1. 학습 곡선
    • 새로운 개념과 API를 익히는 데 시간이 필요합니다.
  2. 브라우저와의 협력
    • 브라우저의 이벤트 루프와 긴밀히 작동하기 때문에 디버깅이 어렵거나 예상치 못한 문제가 생길 수 있습니다.
  3. 호환성
    • 일부 오래된 라이브러리나 패턴은 Concurrent Rendering과 완벽히 호환되지 않을 수 있습니다.

Concurrent Rendering은 React 앱의 성능과 사용자 경험(UX)을 한 차원 높이는 기술입니다. 이를 활용하면 더 부드럽고 반응성 높은 웹 애플리케이션을 만들 수 있어요. 사용자의 클릭과 스크롤에 실시간으로 반응하는 앱, 이제 React Concurrent로 만들어 보세요! 🚀

 

🌷 전설의 개발자가 되어봅시당! 🌷

728x90
반응형