React의 Concurrent를 톺아보자!🏂
React는 항상 사용자 경험(UX)을 최우선으로 고려하여 설계된 강력한 라이브러리라고 합니다. 특히 Concurrent Rendering(동시성 렌더링)은 이를 한층 더 끌어올린 기술인데요. 복잡한 사용자 인터페이스를 매끄럽게 처리할 수 있게 해주며, 사용자와의 상호작용에 즉각적으로 반응하도록 설계되었습니다.
특히 여러 작업이 동시에 이루어져야 할 때 그 진가를 발휘합니다. 예를 들어, 대량의 데이터가 로드되거나 복잡한 애니메이션이 실행될 때도, 사용자가 애플리케이션을 사용하는 동안 불편함이나 지연을 최소화하여 UI의 부드러움과 반응성을 느낄 수 있습니다.
🌀 Concurrent Rendering이란?
React가 기존에 렌더링을 처리하는 방식
React의 기본 렌더링은 동기적(Synchronous)입니다. 컴포넌트가 렌더링을 시작하면 전체 작업이 완료될 때까지 브라우저가 멈춘 것처럼 느껴질 수 있어요. 복잡한 UI에서는 이로 인해 버벅거림이 발생할 수 있죠.
Concurrent Rendering의 등장
Concurrent Rendering은 이런 문제를 해결하기 위해 등장했습니다. 작업을 작은 조각으로 쪼개서 브라우저가 사용자와의 상호작용(클릭, 스크롤 등)에 더 빠르게 반응할 수 있도록 만듭니다.
- 동기 렌더링: 한 번에 하나의 작업을 끝낼 때까지 진행
- Concurrent Rendering: 여러 작업을 중단했다가 다시 시작하며 진행
💡 Concurrent Rendering이 필요한 이유
- UI가 복잡해질수록 성능 문제가 생긴다.
- 데이터가 많거나, 애니메이션, 그래프 같은 무거운 작업이 많으면 렌더링 시간이 길어져 사용자 경험이 나빠집니다.
- 사용자 상호작용의 즉시성을 유지
- 클릭, 스크롤, 입력 등 즉각적인 반응을 요구하는 작업은 우선순위를 높게 두고 처리해야 합니다.
- 빠른 반응과 부드러운 UI를 동시에 제공
- 중요한 작업과 덜 중요한 작업을 나눠서 처리함으로써 더 나은 UX를 제공합니다.
🔍 Concurrent Rendering의 핵심 개념
1️⃣ 작업을 중단하고 재개하기
React는 긴 작업이 진행되는 동안 특정 지점에서 멈추고,
더 중요한 작업을 처리한 후 다시 원래 작업으로 돌아와서 렌더링을 재개할 수 있습니다.
작업 순서 예시:
- 사용자가 입력하면 입력 작업이 고우선순위로 즉시 처리됩니다.(인터페이스의 반응성을 높일 수 있음)
- 이후 덜 중요한 작업(예: 데이터 로드)은 나중에 처리됩니다.(사용자가 입력하는 동안 방해받지 않고 나중에 처리됨)
2️⃣ 우선순위 기반 렌더링
React는 작업의 중요도를 기준으로 긴급한 작업부터 차례대로 처리합니다. (긴급의 기준: 즉각적인 반응이 요구되는지, 사용자와 직접적으로 상호작용이 필요한지,)
- 높은 우선순위: 사용자 입력, 클릭, 애니메이션
- 낮은 우선순위: 데이터 로드, 백그라운드 작업
3️⃣ Interruptible Rendering
중단 가능한 렌더링은 UI를 업데이트하면서도 중요한 사용자 이벤트를 놓치지 않게 도와줍니다.
작업 순서 예시:
- 사용자가 버튼을 클릭하면 그 클릭 이벤트는 즉시 처리되며, UI는 즉각적으로 반응합니다.
- 이 과정에서 긴 작업이 중단될 수 있습니다.
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️⃣ 애니메이션 중단 없는 경험 제공
- 애니메이션이 진행되는 도중, 새로운 데이터를 로드하거나 렌더링할 때에도 부드러운 화면 유지
🛠️ 실무에서의 적용 사례
검색 필터
검색어를 입력할 때, 필터링 작업을 낮은 우선순위로 처리하고 입력 반응은 즉각적으로 처리합니다.
- 즉각적인 사용자 입력 반응
- 사용자가 검색어를 입력할 때마다 즉시 필터링된 결과를 보여주는 것이 중요합니다. Concurrent Rendering을 사용하면 사용자의 입력 이벤트를 높은 우선순위로 처리하여, 즉각적으로 UI를 업데이트할 수 있습니다.
- 예를 들어, 사용자가 검색창에 입력하는 순간, 이전의 검색 결과를 즉시 지우고 새로운 검색 결과를 렌더링합니다.
- Debouncing 활용
- 입력할 때마다 API 호출이 발생하면 성능 저하가 발생할 수 있습니다. 이를 방지하기 위해 Debouncing 기법을 적용할 수 있습니다. 사용자가 입력을 멈춘 후 일정 시간 후에 API 호출을 하여, 불필요한 렌더링을 줄일 수 있습니다.
- 이 과정에서도 Concurrent Rendering을 활용하여, 사용자가 입력한 내용을 반영한 결과가 빠르게 나타나도록 할 수 있습니다.
- Loading 상태 관리
- 필터링된 결과를 가져오는 동안 로딩 상태를 표시할 수 있습니다. Concurrent Rendering을 통해 로딩 중에도 UI가 멈추지 않고, 사용자가 다른 작업을 수행할 수 있도록 해줍니다.
- Suspense를 사용하여 데이터 로딩 중에는 로딩 스피너를 보여주고, 데이터가 준비되면 결과를 표시하는 방식으로 사용자 경험을 개선할 수 있습니다.
- 결과 캐싱 및 최적화
- 이미 검색한 결과를 캐싱하여, 같은 검색어에 대한 요청이 들어올 경우 즉시 결과를 보여줄 수 있습니다. 이때도 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의 한계
- 학습 곡선
- 새로운 개념과 API를 익히는 데 시간이 필요합니다.
- 브라우저와의 협력
- 브라우저의 이벤트 루프와 긴밀히 작동하기 때문에 디버깅이 어렵거나 예상치 못한 문제가 생길 수 있습니다.
- 호환성
- 일부 오래된 라이브러리나 패턴은 Concurrent Rendering과 완벽히 호환되지 않을 수 있습니다.
Concurrent Rendering은 React 앱의 성능과 사용자 경험(UX)을 한 차원 높이는 기술입니다. 이를 활용하면 더 부드럽고 반응성 높은 웹 애플리케이션을 만들 수 있어요. 사용자의 클릭과 스크롤에 실시간으로 반응하는 앱, 이제 React Concurrent로 만들어 보세요! 🚀
🌷 전설의 개발자가 되어봅시당! 🌷
'Front-end > React' 카테고리의 다른 글
[React] 데이터 로딩 전략의 모든 것 🛠️ (0) | 2025.01.28 |
---|---|
[React] React Fiber: 리액트의 새로운 엔진 심층 탐구! 🚀 (0) | 2025.01.27 |
[React] Redux Middleware: 리덕스의 숨은 영웅들 🦸♂️🦸♀️ (0) | 2025.01.10 |
[React] Mutation: 데이터의 비밀 작업실 🛠️ (0) | 2025.01.09 |
[React] Redux Saga: 🌀 비동기 마법사로 변신하기! 🧙♂️ (0) | 2025.01.08 |