🟨 2-35. React 기반 UX 성능 튜닝 실전 — Suspense, Streaming, Transition 완전 이해
좋아, 이번 편은 React 18 이후의 UX 성능 최적화 핵심 기능을 완전히 정리하는 시간이다.
이전 글에서 “사용자가 느끼는 빠름”을 다뤘다면, 이번엔 그걸 실제로 구현하는 기술적 방법을 배워보자.
1. React 18의 변화 — “빠른 반응”을 위한 진화
React 18은 단순히 동시성(concurrency) 개념을 도입한 게 아니라,
사용자 체감 속도를 높이기 위한 렌더링 제어 기능이 대거 추가되었다.
그중에서도 핵심은 이 세 가지다.
기능 목적
| Suspense | 데이터 로딩 시 자연스러운 화면 제어 |
| Transition | 사용자 인터랙션 중 UI 끊김 방지 |
| Streaming SSR | 서버 렌더링된 콘텐츠를 점진적으로 스트리밍 전송 |
이 세 가지를 제대로 활용하면,
“렌더링은 느려도 사용자에게는 빠르게 느껴지는” 구조를 만들 수 있다.
2. Suspense — “로딩 중에도 멈추지 않는 UI”
2-1. 문제 인식
기존 React는 비동기 데이터가 없으면 그 자리가 비어 있거나 깜빡였다.
즉, API 요청이 끝나야 컴포넌트가 렌더링되었지.
React 18부터는 Suspense를 이용해
로딩 중에도 UI가 자연스럽게 유지되도록 할 수 있다.
2-2. 기본 문법
import { Suspense } from 'react';
import UserProfile from './UserProfile';
function App() {
return (
<Suspense fallback={<div>로딩 중...</div>}>
<UserProfile />
</Suspense>
);
}
✅ fallback
데이터가 준비되지 않았을 때 보여줄 UI (Skeleton UI 등)
✅ 내부 컴포넌트가 Promise를 던지면 React가 알아서 Suspense로 제어
2-3. React Query와의 실전 예시
import { Suspense } from "react";
import { useQuery } from "@tanstack/react-query";
function UserInfo() {
const { data } = useQuery({
queryKey: ["user"],
queryFn: () => fetch("/api/user").then(res => res.json()),
suspense: true,
});
return <div>안녕하세요, {data.name}님!</div>;
}
export default function Page() {
return (
<Suspense fallback={<div className="skeleton">유저 정보 불러오는 중...</div>}>
<UserInfo />
</Suspense>
);
}
- 데이터가 로딩 중일 땐 fallback UI 표시
- 데이터가 준비되면 자동으로 교체
➡ 사용자 입장에서는 “멈추지 않고 자연스럽게 전환되는” 느낌
3. Transition — 느린 연산도 부드럽게
3-1. 문제 인식
대량의 상태 업데이트(검색, 필터, 정렬 등)가 한꺼번에 일어나면
UI가 잠시 멈추는 느낌을 받는다.
useTransition은 이런 업데이트를 “긴급하지 않은 작업” 으로 분리해,
UI가 끊기지 않게 렌더링 우선순위를 조절한다.
3-2. 사용법
import { useState, useTransition } from "react";
export default function SearchList({ items }) {
const [query, setQuery] = useState("");
const [filtered, setFiltered] = useState(items);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
const value = e.target.value;
setQuery(value);
startTransition(() => {
setFiltered(items.filter(item => item.includes(value)));
});
};
return (
<div>
<input type="text" value={query} onChange={handleChange} placeholder="검색..." />
{isPending && <span>검색 중...</span>}
<ul>
{filtered.map((v, i) => <li key={i}>{v}</li>)}
</ul>
</div>
);
}
✅ startTransition() 내부의 상태 업데이트는 “긴급하지 않은 작업”으로 처리됨
✅ 사용자는 입력 중에도 인터랙션이 끊기지 않는다
3-3. 체감 UX
상황 Transition 미적용 Transition 적용
| 1만 개 필터링 | 입력 중 UI 멈춤 | 입력 즉시 반응 |
| 빠른 타이핑 | 지연됨 | 자연스럽게 입력 유지 |
| 연속 클릭 | 이벤트 겹침 | 순서대로 부드럽게 처리 |
→ 체감 속도는 두 배 이상 개선된다.
4. Streaming SSR — 서버 렌더링을 “점진적으로”
4-1. 기존 SSR의 한계
기존 SSR은 HTML 전체가 완성될 때까지 기다린 뒤 한 번에 전송했다.
즉, 서버에서 데이터가 느리면 사용자도 계속 빈 화면을 본다.
4-2. React 18의 Streaming SSR
React 18부터는 서버가 렌더링된 부분부터 스트리밍으로 전송할 수 있다.
- 먼저 중요한 영역(헤더, Hero, 기본 프레임)을 렌더링
- 이후 비동기 데이터가 준비되면 나머지 콘텐츠가 합쳐진다
4-3. Next.js 13 이상에서 자동 적용
Next.js App Router는 React 18 기반의 Streaming SSR을 기본적으로 사용한다.
즉, Suspense가 자동으로 서버 렌더링 단계에서도 작동한다.
// app/page.tsx
import { Suspense } from "react";
import ProductList from "./ProductList";
export default function Page() {
return (
<>
<Hero />
<Suspense fallback={<div>상품 목록 로딩 중...</div>}>
<ProductList />
</Suspense>
</>
);
}
✅ Hero 영역은 즉시 렌더링
✅ ProductList는 서버 데이터 준비 후 점진적 로딩
사용자는 “화면이 바로 뜨는 느낌”을 받는다.
5. 세 가지 기능의 조합
기능 목적 결과
| Suspense | 비동기 로딩 제어 | 깜빡임 없는 데이터 로딩 |
| Transition | 렌더링 우선순위 조절 | 사용자 입력 시 끊김 제거 |
| Streaming SSR | 점진적 렌더링 | 첫 화면 빠르게 노출 |
이 세 가지를 조합하면,
네트워크가 느려도 “끊기지 않는 사용자 경험” 을 만들 수 있다.
6. 실제 서비스 구조 예시
app/
├─ layout.tsx
├─ page.tsx (Streaming SSR + Suspense)
├─ components/
│ ├─ SearchBar.tsx (useTransition)
│ ├─ ProductList.tsx (Suspense)
│ └─ SkeletonCard.tsx
└─ api/
└─ getProducts.ts
✅ ProductList는 서버에서 Suspense + Streaming SSR 적용
✅ SearchBar는 Transition으로 입력 부드럽게 처리
✅ SkeletonCard는 로딩 중 시각적 안정성 제공
→ 이렇게 하면 “느리지 않은 UI”가 아니라
“사용자에게 빠르게 느껴지는 UI”가 된다.
7. 실무 팁
- Suspense는 React Query, Relay, SWR과 함께 써야 진가 발휘
- Transition은 “대량 렌더링 or 복잡한 연산”에서만 써야 효과적
- Streaming SSR은 Next.js 13 이상에서 기본적으로 켜져 있음
- Skeleton UI와 함께 써야 체감 UX 상승 폭이 커짐
8. 마무리
React 18 이후의 UX 성능 최적화는
더 이상 “API 응답 빠르게”가 아니라
“사용자가 느끼기에 빠르게” 로 바뀌었다.
Suspense로 로딩을 매끄럽게,
Transition으로 인터랙션을 부드럽게,
Streaming SSR로 첫 화면을 빠르게.
이 세 가지를 익히면,
React 기반 서비스의 성능은 숫자보다 “경험”으로 측정될 것이다.