[태그:] 배칭

  • API 요청 최적화

    성능 병목은 API 호출에서 시작된다

    대부분의 웹 애플리케이션은 API 기반으로 움직인다.

    컴포넌트가 많아지고 상태가 복잡해질수록, 같은 데이터를 여러 컴포넌트가

    동시에 요청하는 일이 발생한다.

    예를 들어,

    A 컴포넌트가 유저 프로필 요청,

    B 컴포넌트가 유저 알림 요청,

    C 컴포넌트가 프로필 하위 정보 요청

    이때 프로필 정보가 서로 다른 위치에서 중복 호출되면

    다음과 같은 문제가 발생한다.

    • 서버 트래픽 낭비
    • 응답 지연
    • 사용자 체감 성능 저하

    그래서 API 요청 최적화는 성능 튜닝의 시작점이 된다.

    중복 요청 방지(Deduplication)

    React의 라이프사이클 특성상, 여러 컴포넌트가 같은 시점에 같은 API를 호출할 수 있다.

    이런 상황을 해결하려면 다음과 같은 방법이 있다.

    • SWR / React Query의 요청 Deduplication

      동일한 키로 요청하면 내부적으로 캐시를 공유, 중복 네트워크 호출을
      차단한다.
      const { data: user } = useSWR('/api/user', fetcher);
      const { data: profile } = useSWR('/api/user', fetcher);
      → 두 컴포넌트가 동시에 호출해도 네트워크는 1회만 발생한다
    • 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