React 18의 동시성 렌더링(Concurrent Rendering)은 렌더링 과정을 멈추거나 우선순위를 조절하여 사용자 경험(UX)을 극대화하는 새로운 렌더링 매커니즘이다.
동시성 렌더링은 이런 문제를 우선순위와 가로채기(interrupt)로 풀어줍니다.
참고: 동시성 ≠ 멀티스레드. 여전히 단일 스레드에서 “스케줄링이 똑똑해진 것”입니다.
<script>
import { createRoot } from 'react-dom/client'
createRoot(document.getElementById('root')!).render(<App />);
</script>
1) useTransition / startTransition
<script>
import { useState, useTransition, useMemo } from 'react'
function Search() {
const [text, setText] = useState('');
const [isPending, startTransition] = useTransition();
const [results, setResults] = useState<string[]>([]);
function onChange(e: React.ChangeEvent<HTMLInputElement>) {
const value = e.target.value;
// 1) 즉기 반응 - 검색창 텍스트 입력값
setText(value);
// 2) 무거운 작업은 전환으로 밀어둠
startTransition(() => {
setResults(expensiveFilter(value));
});
}
return (
<>
<input value={text} onChange={onChange} placeholder="검색어 입력" />
{isPending && <span>검색 중...</span>}
<ResultList items={results} />
</>
)
}
</script>
2) useDefferredValue
<script>
import { useDeferredValue, useMemo } from 'react';
function Results({ query }: { query: string }) {
const deferredQuery = useDeferredValue(query);
const filtered = useMemo(() => expensiveFilter(deferredQuery), [deferredQuery]);
return <ResultList items={filtered} />;
}
</script>
3) Suspense (데이터/코드 분할 경계)
<script>
import { Suspense, lazy } from 'react';
const Chart = lazy(() => import('./Chart'));
export default function Dashboard() {
return (
<Suspense fallback={<div>차트 불러오는 중…</div>}>
<Chart />
</Suspense>
);
}
</script>
4) Automatic Batching & flushSync
<script>
// 이 두 setState는 한 번에 배치되어 리렌더 1회로 처리
setTimeout(() => {
setOpen(true);
setCount((c) => c + 1);
}, 0);
</script>
<script>
import { flushSync } from 'react-dom';
flushSync(() => setOpen(false)); // 드물게 사용
</script>
<script>
// SearchPage.tsx
import { Suspense, useState, useTransition } from 'react';
import { SearchResult } from './SearchResult';
export default function SearchPage() {
const [q, setQ] = useState('');
const [isPending, startTransition] = useTransition();
return (
<div>
<input
value={q}
onChange={(e) => {
const v = e.target.value;
startTransition(() => setQ(v)); // 전환으로 관리
}}
placeholder="React 18"
/>
{isPending && <small>업데이트 중…</small>}
<Suspense fallback={<div>결과 불러오는 중…</div>}>
<SearchResult query={q} />
</Suspense>
</div>
);
}
// SearchResult.tsx (예: Next.js 서버 컴포넌트 or 클라에서 SWR/React Query로)
export async function SearchResult({ query }: { query: string }) {
const data = await fetchResults(query); // 서버라면 스트리밍/Suspense 가능
return <ul>{data.map((x) => <li key={x.id}>{x.title}</li>)}</ul>;
}
</script>