성능 병목은 API 호출에서 시작된다
대부분의 웹 애플리케이션은 API 기반으로 움직인다.
컴포넌트가 많아지고 상태가 복잡해질수록, 같은 데이터를 여러 컴포넌트가
동시에 요청하는 일이 발생한다.
예를 들어,
A 컴포넌트가 유저 프로필 요청,
B 컴포넌트가 유저 알림 요청,
C 컴포넌트가 프로필 하위 정보 요청
이때 프로필 정보가 서로 다른 위치에서 중복 호출되면
다음과 같은 문제가 발생한다.
- 서버 트래픽 낭비
- 응답 지연
- 사용자 체감 성능 저하
그래서 API 요청 최적화는 성능 튜닝의 시작점이 된다.
중복 요청 방지(Deduplication)
React의 라이프사이클 특성상, 여러 컴포넌트가 같은 시점에 같은 API를 호출할 수 있다.
이런 상황을 해결하려면 다음과 같은 방법이 있다.
SWR / React Query의 요청 Deduplication
동일한 키로 요청하면 내부적으로 캐시를 공유, 중복 네트워크 호출을
차단한다.
→ 두 컴포넌트가 동시에 호출해도 네트워크는 1회만 발생한다const { data: user } = useSWR('/api/user', fetcher);
const { data: profile } = useSWR('/api/user', fetcher);Axios 요청 취소(Cancellation Token)
요청이 중복되었거나, 컴포넌트가 언마운트된 경우 기존 요청을
취소한다. 중복 요청 방지는 불필요한 네트워크 소모를 막고 UI적으로 속도 저하를 해소.const controller = new AbortController();
axios.get('/api/data', { signal: controller.signal });
controller.abort();
캐싱(Caching)
캐싱?
캐시는 한 번 불러온 데이터를 일정 시간 재사용하는 전략,
API 캐싱에는 서버 캐시, CDN 캐시, 클라이언트 캐시가 있다.
프론트엔드에서는 보통 클라이언트 캐시를 다룬다.
캐싱을 통한 전략
SWR / React Query의 캐싱
key 기반으로 데이터 저장 → 같은 요청 시 즉시 반환.TTL 설정이 가능하다(staleTime, cacheTitme)
*SWR(Stale-While-Revalidate)
캐시된 데이터를 즉시 보여주고, 백그라운드에서 최신화.useSWR('/api/posts', fetcher, { staleTime: 10000 });브라우저 캐시 활용
Cache-Control, Etag 헤더를 사용하면 서버 응답을 브라우저가
저장할 수 있음. React-Query는 브라우저 탭 간 캐시 공유도 지원한다.IndexedDB / localStorage 캐시
오프라인 지원이 필요한 경우, 클라이언트 영구 저장소를 활용.
SWR + IndexedDB 커스텀 스토리지 패턴으로 자주 사용된다.
배칭(Batching)
배칭 요청
여러 개의 작은 요청을 합쳐 한 번에 전송하면 네트워크 효율이 크게 향상됨.
예를 들어,
- GraphQL Batching
GraphQL에서는 여러 쿼리를 한 번의 POST로 묶을 수 있다.
[
{ "query": "{ user { name } }" },
{ "query": "{ posts { title } }" }
]
→ 서버는 여러쿼리를 한 번의 POST로 묶을 수 있음.
- Custom Batch API
REST API라도, 백엔드가 /batch 엔드포인트를 제공하면 다음처럼
묶을 수 있다.{
"requests": [
{ "url": "/api/user", "method": "GET" },
{ "url": "/api/notifications", "method": "GET" }
]
} - 브라우저 요청 지연 처리
일정 시간동안 요청을 모아서 보낸다.
e.g. 입력 자동완성, 검색 제안 등 → debounce or throttle로 네트워크 호출 감소.
병렬 vs 직렬 요청 최적화
병렬 요청
서로 의존하지 않는 요청은 Promise.all로 병렬 처리, 렌더링 블로킹 시간을 단축할 수 있다.
const [user, posts] = await Promise.all([
fetch('/api/user'),
fetch('/api/posts')
]);
직렬 요청
앞 요청 결과가 다음 요청의 파라미터로 필요한 경우 순차 진행
단, React Query에서는 enabled 옵션을 통해 의존적 요청을 깔끔하게 처리 가능.
const { data: user } = useQuery('user', fetchUser);
const { data: posts } = useQuery(['posts', user?.id], fetchPosts, {
enabled: !!user
});
정리하자면
- 중복 요청 방지: SWR / React Query deduping
- 최신성 유지: Stale-While-Revalidate, revalidateOnFocus
- 응답 속도 향상:캐싱, prefetching
- 서버 부하 완화: 배치 요청, debounce
- UX 개선: optimistic update, background sync