React 18이 도입한 동시 렌더링이 웹 성능 지표(Total Blocking Time, INP)를 개선하는 원리를 다룬다. 기존 동기식 렌더링은 모두-아니면-무(all-or-nothing) 방식으로 메인 스레드를 차단했는데, React 18은 렌더링을 5ms 단위로 쪼개 더 긴급한 작업을 먼저 처리할 수 있게 했다.
핵심 포인트- Long Task(50ms 초과)는 메인 스레드를 차단해 INP를 높이고 사용자 반응성을 떨어뜨린다.
- useTransition으로 상태 업데이트를 낮은 우선순위로 표시하면 React가 5ms마다 스케줄러를 확인하며 긴급한 작업을 먼저 처리한다.
- React Server Components는 클라이언트 번들에서 서버 전용 코드를 제거하고 DB에 직접 접근해 별도 API 레이어를 없앤다.
- Suspense로 로딩 중인 컴포넌트 대신 다른 트리를 렌더하다가 완료 후 교체해 체감 성능을 높인다.
- cache() 함수는 같은 렌더 패스 내 중복 호출을 메모이제이션하고, fetch()도 자동 캐싱을 지원한다.
상세 정리- 성능 지표 맥락: FCP와 TTI 사이의 Total Blocking Time(TBT)과 인터랙션부터 화면 업데이트까지의 INP(Interaction to Next Paint)가 핵심 지표다.
- 기존 문제: 동기식 렌더링은 시작 후 완료까지 메인 스레드를 점유한다. 50ms가 넘으면 Long Task로 분류되어 그 동안 사용자 입력이 차단된다.
- useTransition 동작 원리: startTransition으로 래핑된 업데이트는 중단 가능으로 표시되고, React 스케줄러가 5ms마다 더 높은 우선순위 작업(사용자 입력 등)을 확인해 먼저 처리한다.
- useTransition 실증: 도시 필터링 데모에서 총 차단 시간이 4425.40ms에서 대폭 감소했다. 사용자 입력이 즉시 반응하고 필터링은 백그라운드에서 진행된다.
- RSC 원리: "use client" 지시어로 클라이언트에서만 실행할 컴포넌트를 명시한다. 나머지는 서버에서 실행되어 클라이언트 번들에 포함되지 않는다.
- RSC 데이터 접근: 서버 컴포넌트는 DB에 직접 쿼리 가능하다. 직렬화된 컴포넌트 트리를 클라이언트로 전송하기 때문에 별도 API 엔드포인트가 불필요하다.
- Suspense 확장: 특정 컴포넌트가 데이터 로딩 중일 때 React가 해당 컴포넌트 대신 다른 트리를 렌더하다가, 로딩이 완료되면 교체한다. 사용자에게 점진적으로 콘텐츠를 보여줄 수 있다.
- cache() 함수: 동일 렌더 패스 내에서 같은 함수 인수로 호출된 결과를 메모이제이션한다. 여러 컴포넌트가 같은 데이터를 요청해도 실제 실행은 1회다.
- fetch() 자동 캐싱: React 통합 환경에서 fetch() 호출은 자동으로 캐시되어 같은 URL 중복 요청을 제거한다.
- 전체 그림: 동시 렌더링(Transitions) + 서버 렌더링 최적화(RSC) + 선언적 로딩 상태(Suspense) + 데이터 캐싱이 합쳐져 성능을 높인다.
왜 읽나React 18 concurrent 기능을 실제 성능 지표와 연결해 이해하려는 프론트엔드 개발자에게 동작 원리부터 최적화 패턴까지 체계적인 레퍼런스.