React 19.2 동시성 패턴
useTransition·Suspense·Server Components가 모바일에서 의미하는 것
핵심 요약
- Expo SDK 56은 React 19.2.3 기준이며, 모바일에서 중요한 건 API 이름보다 입력을 막지 않는 렌더링·느린 데이터 분리·interactive 시점의 명확화입니다.
useTransition은 검색·필터처럼 값은 즉시 반영하되 무거운 결과 렌더링을 뒤로 미룰 때 쓰고 pending state로 UX를 알립니다.- Suspense는 화면 전체 spinner 대신 탭·카드·sheet·버튼 단위 boundary로 나누고 navigation shell은 계속 터치 가능하게 둡니다.
- native 앱에서는 서버 컴포넌트를 그대로 기대하기보다 route loader·React Query·SWR 캐시 계층과 Suspense boundary를 맞춥니다.
- 검증은 느린 3G·저사양 Android의 input latency, 접근성 label·screen reader 순서, EAS Observe TTI를 단순 평균이 아닌 P95 기준으로 봅니다.
모바일에서의 React 19.2.3
Expo SDK 56의 공식 SDK 표는 React 19.2.3을 기준으로 합니다. 모바일 앱에서 중요한 것은 API 이름보다 사용자 입력을 막지 않는 렌더링, 느린 데이터 로딩의 분리, splash 이후 interactive 시점의 명확화입니다.
useTransition
검색, 필터, 정렬처럼 값 변경은 즉시 반영하되 무거운 결과 렌더링은 뒤로 미룰 때 사용합니다.
import { useState, useTransition } from 'react';
export function CustomerSearch({ customers }) {
const [query, setQuery] = useState('');
const [filtered, setFiltered] = useState(customers);
const [pending, startTransition] = useTransition();
function onChangeText(next: string) {
setQuery(next);
startTransition(() => {
setFiltered(searchCustomers(customers, next));
});
}
return <SearchList query={query} data={filtered} pending={pending} onChangeText={onChangeText} />;
}Suspense boundary 설계
모바일에서는 화면 전체 spinner보다 작은 boundary가 낫습니다. 탭, 카드, sheet 단위로 fallback을 나누고, navigation shell은 계속 터치 가능하게 둡니다.
| 위치 | 권장 fallback |
|---|---|
| app root | splash 또는 skeleton shell |
| tab content | list skeleton |
| modal/sheet | compact loading row |
| critical action | button-level pending state |
Expo Router와 data loading
SDK 56 Expo Router는 web의 streaming SSR과 data loader helper를 강화했습니다. native 앱에서는 서버 컴포넌트를 그대로 기대하기보다 route-level loader·React Query·SWR 같은 캐시 계층에 Suspense boundary를 맞추는 편이 현실적입니다.
운영 원칙:
- 인증·feature flag·remote config는 root layout에서 bootstrap하고
markInteractive()기준을 명확히 합니다. - route 전환 중 데이터 로딩은 transition으로 감싸 터치 지연을 줄입니다.
- OTA로 loader contract를 바꿀 때는 backend compatibility window를 둡니다.
- web streaming SSR은 Expo Router web 배포에서만 별도 성능 예산을 둡니다.
Anti-pattern
- 네트워크 요청을 render path에서 직접 시작
- root Suspense 하나로 앱 전체를 막기
- splash 화면을 숨긴 뒤 인증·remote config를 다시 기다리기
- React Navigation import를 SDK 56 Expo Router 앱 코드에 직접 섞기
- optimistic update rollback 없이
startTransition만으로 UX를 해결하려 하기
검증
- 느린 3G와 저사양 Android에서 input latency를 측정합니다.
- Suspense fallback이 접근성 label과 screen reader 순서를 깨지 않는지 확인합니다.
- EAS Observe의 TTI가 개선되는지 확인하고, 단순 평균이 아니라 P95 기준으로 rollout gate를 둡니다.