00 · 시작하기 TanStack Query 불러오는 중…
00 · 시작하기

따라 하며 이해하는 불안정한 네트워크와 프론트엔드

BE 개발자가 API를 설계할 때 FE가 네트워크 문제를 어떻게 다루는지 이해하기 위한 실습 문서입니다. 각 장의 실습에서는 실제 TanStack Query v5 코어(@tanstack/query-core)가 브라우저에서 동작하고, 네트워크(지연·유실·끊김·오프라인·망 전환)와 서버(Spring 쪽 동작)는 시뮬레이션합니다. Chrome DevTools로는 재현하기 어려운 "서버는 처리했는데 응답만 사라진" 상황도 버튼 하나로 만들 수 있습니다.

실습 화면 읽는 법

FE 서버 요청 실선 = 요청. 기울기 = 네트워크 지연 막대 = 처리·대기 구간 201 Created 점선 = 응답. 색 = 상태 코드 ✕ = 가는 길에 유실·끊김 회색 점선 = 이미 포기한 요청의 응답(버려짐) ⏱ 3.0s 타임아웃 점 = 이벤트 (타임아웃, abort, 캐시 적중)
다이어그램은 위에서 아래로 시간이 흐릅니다. 아무 일도 없는 대기 시간이 길면 "⋯ 대기 (생략) ⋯"로 접혀 표시됩니다.
따라하기

번호 단계를 순서대로 진행합니다. 할 일에 나오는 버튼 이름 칩에 마우스를 올리면 실습 안의 그 버튼이 강조되고, 누르면 그 위치로 이동합니다. 이 단계 설정으로 맞추기를 누르면 필요한 선택·토글이 자동으로 맞춰지고, 실행 버튼은 직접 누릅니다. "예상해 보기"로 결과를 먼저 짐작해 보세요.

개선 전 / 개선 후

흔한 구현과 권장 구현(FE 설정 + BE 계약)을 한 번에 전환합니다. 개별 토글로 한쪽만 바꿔 볼 수도 있습니다.

장애 주입 버튼

점선 테두리 버튼(예: 다음 응답 유실)은 다음 요청 한 번에만 장애를 겁니다. 켜 두면 "대기 중"으로 표시됩니다.

코드 탭

FE(React + TanStack Query), BE(Kotlin + Spring Boot 4), HTTP 메시지를 보여 줍니다. 방식·토글을 바꾸면 코드도 지금 설정 그대로 바뀌고, 달라진 줄은 잠깐 파랗게 강조됩니다(다른 탭이 바뀌면 탭 이름 옆에 점). + 줄은 개선으로 추가된 부분, ! 줄은 문제 지점입니다.

추천 순서

1장부터 차례로 읽어도 되지만, 실습만 따라가려면 아래 순서가 자연스럽습니다. 한 번이라도 단계를 진행한 실습에는 ✓가 붙습니다(이 브라우저에만 저장).

이 문서의 전제

예제 도메인은 가상의 쇼핑몰(products · orders · payments · cart · uploads)입니다. 시뮬레이터의 시간·지연 값은 관찰하기 쉽게 고른 것이고 실측값이 아닙니다. 라이브러리 동작(재시도·중복 제거·취소·일시정지·scope 직렬화)은 실제 @tanstack/query-core가 수행하므로 문서와 같게 움직입니다.

01 · 문제 정의

FE 관점에서 본 네트워크 문제 분류

"네트워크가 불안정하다"는 한 문장은 FE 코드의 서로 다른 계층에서 해결됩니다. 먼저 일상적인 상황이 어느 계층의 문제인지 골라 보고, 요청 하나가 실패하는 방식을 직접 만들어 봅니다.

상황을 고르면 관련 계층이 보입니다

정리: 계층별 문제와 해결 위치

계층문제주로 해결하는 곳
Network지연, 대역폭, 패킷 손실, 타임아웃, 연결 리셋, 오프라인, 재연결타임아웃(AbortSignal.timeout), HTTP/2·3, 재연결 감지, BFF로 RTT 줄이기
Server State캐시, stale 데이터, 백그라운드 재검증, 중복 요청, 요청 취소, 동기화, 페이지네이션TanStack Query 같은 서버 상태 라이브러리 + BE의 ETag·cursor
Mutation재시도, 중복 실행, 낙관적 반영, 롤백, 순서, 오프라인 쓰기mutation 옵션(retry·scope·onMutate) + BE 멱등성(Idempotency-Key, PUT/DELETE, If-Match)
UX로딩, 스켈레톤, 재시도 버튼, 오프라인 배너, 부분 실패, "확인 중" 상태화면 설계. 실패를 단정하지 않는 문구가 중요(응답 유실은 실패가 아닐 수 있음)
File업로드 진행률, 청크, 이어 올리기, 일시정지·재개, 다운로드 이어받기tus / S3 multipart(서버가 진행 상태 보관), Range(다운로드)
이 장의 핵심

FE는 "응답을 못 받았다"는 사실만으로 요청 유실과 응답 유실을 구분할 수 없습니다. 둘 다 같은 TimeoutError입니다. 그래서 쓰기 요청을 안전하게 재시도하게 만드는 일은 서버(멱등성)의 몫이고, 3장에서 직접 해결합니다.

출처: MDN AbortSignal.timeout() · RFC 9110 §9.2.2 멱등 메서드 · RFC 9110 503 · Retry-After

02 · 라이브러리

서버 상태 라이브러리 비교

TanStack Query, SWR, Apollo Client, RTK Query를 "어떤 네트워크 문제를 푸는가" 기준으로 비교합니다. 문제를 고르면 관련 행이 강조됩니다. 이 문서의 실습은 TanStack Query를 사용합니다.

TanStack Query v5SWRApollo ClientRTK Query
캐시 구조queryKey별 문서 캐시키별 문서 캐시정규화 캐시 (__typename:id)엔드포인트+인자별 캐시(비정규화)
신선도staleTime(기본 0) · gcTime(기본 5분)항상 캐시 즉시 반환 + 재검증fetchPolicy(cache-first 등)keepUnusedDataFor(60초) · refetchOnMountOrArgChange
중복 제거같은 키 in-flight 공유dedupingInterval(2초)in-flight 쿼리 공유같은 캐시 키 공유
재시도query 3회·지수 백오프, mutation 0회onErrorRetry로 제어RetryLink 추가 구성retry 유틸
취소queryFn에 signal 자동 전달·자동 취소fetcher에서 직접링크에 signal 연결내장 AbortController, abort()
폴링·재검증refetchInterval, 포커스·재연결 시 재검증refreshInterval, focus·reconnectpollIntervalpollingInterval, refetchOnFocus/Reconnect
낙관적 업데이트onMutate → onError 롤백 → onSettled 무효화optimisticData + rollbackOnErroroptimisticResponse(정규화 캐시에 자동 전파)updateQueryData + undo(), 태그 무효화
오프라인networkMode로 일시정지·재개, mutation 큐, scope 직렬화공식 기능 없음공식 기능 없음공식 기능 없음
영속화persist-client(localStorage·IndexedDB)커스텀 providerapollo3-cache-persist(커뮤니티)redux-persist에 얹음
무한 스크롤useInfiniteQuery(양방향, maxPages)useSWRInfinitefetchMore + merge 정책infiniteQuery 엔드포인트
SSR·RSCdehydrate / HydrationBoundaryNext.js 통합RSC 통합 패키지Redux 특성상 제약
프레임워크React · Vue · Solid · Svelte · AngularReactReact 중심, Vue·AngularRedux를 쓰는 React

정규화 캐시 vs 문서 캐시

Apollo는 객체를 __typename:id로 평탄화해 저장하므로, 같은 상품을 다섯 개 화면이 참조해도 실체는 하나이고 하나를 고치면 모두 갱신됩니다. TanStack Query·SWR·RTK Query는 키 단위 문서 캐시라서 "이 수정이 끝나면 이 키들을 무효화"를 명시합니다(RTK Query는 태그로 선언). 정규화는 편하지만 필드 정책이 복잡해질 수 있고, 문서 캐시는 단순하지만 무효화 대상을 개발자가 알아야 합니다. BE 관점에서는 두 경우 모두 안정적인 id와 수정 후 무엇이 바뀌는지 예측 가능한 응답이 도움이 됩니다.

출처: TanStack Query Important Defaults · SWR Revalidation · Apollo Queries · RTK Query Cache Behavior

03 · Mutation · 재시도

재시도와 멱등성 — 무엇을, 언제, 몇 번

세 개의 실습으로 진행합니다. ① 조회(GET)의 재시도와 백오프, ② 많은 클라이언트가 동시에 재시도할 때 서버에 생기는 일, ③ 결제(POST) 재시도가 이중 결제가 되는 과정과 Idempotency-Key로 막는 방법.

정리

재시도해 볼 가치가 있는 오류
  • 네트워크 오류(연결 실패·리셋·오프라인)
  • 타임아웃
  • 429 — Retry-After만큼 기다린 뒤
  • 502 · 503 · 504 (500은 상황에 따라)
재시도해도 결과가 같은 오류
  • 400 · 401 · 403 · 404 · 409 · 422
  • 401은 토큰 갱신 후 1회만 다시 보내는 별도 흐름
  • 409는 멱등 키 "처리 중" 응답일 때만 대기 후 재시도
이중 결제 방지에서할 수 있는 것할 수 없는 것
FE재시도 여부·횟수·간격 결정, 버튼 잠금, "확인 중" 표시, 키 발급과 재사용응답 없이 서버의 처리 여부를 알아내기, 중복 실행 자체를 막기
BE키 선점(PK), 응답 저장·재생, 처리 중 409, 본문 불일치 422, 키 없음 400, 보관 기간FE가 재시도마다 새 키를 만드는 실수 막기(계약 위반)
표준화 현황

Idempotency-Key는 IETF httpapi 워킹그룹의 Internet-Draft(draft-ietf-httpapi-idempotency-key-header-07)로, 2026년 9월 현재 RFC가 아닙니다. 초안은 키로 UUID 같은 무작위 값을 권장하고, 헤더 값을 Structured Field 문자열(따옴표 포함)로 정의합니다. Stripe 등 결제사가 같은 방식을 오래 써 왔습니다(Stripe는 응답에 Idempotent-Replayed 헤더를 붙입니다).

출처: TanStack Query Retries · Mutations (retry 기본 0, scope) · IETF Idempotency-Key draft-07 · Stripe Idempotent requests · Spring Framework 7 Resilience (@Retryable) · RFC 9110 Retry-After

04 · 서버 상태 · 캐시

오프라인 / 온라인

오프라인에서 사용자가 한 행동을 버리지 않고, 앱을 다시 켜도 남겨 두었다가, 온라인이 되면 순서대로 서버에 반영하는 흐름을 실습합니다. 그 과정에서 BE가 지켜야 할 계약(멱등 키, 재검증, 명확한 거절 이유)이 드러납니다.

정리

단순 오프라인 UX

오프라인이면 배너를 띄우고 요청을 멈춥니다(TanStack Query 기본 networkMode: 'online'). 구현 비용이 낮고 대부분의 관리 도구·대시보드에 충분합니다.

오프라인 우선 (offline-first)

로컬 데이터 사용 → 쓰기를 큐에 저장 → 온라인 시 동기화 → 충돌 해결. 노트, 현장 업무 앱, 협업 문서처럼 오프라인에서도 일이 계속돼야 할 때만 정당화되는 복잡도입니다.

  • navigator.onLine과 online/offline 이벤트는 신뢰도가 낮습니다(LAN만 연결돼도 online). 실제 요청 성공·실패로 보완합니다.
  • Background Sync API는 2026년 현재 Chromium 계열만 지원하고 Safari·Firefox는 지원하지 않습니다. 앱이 다시 열리거나 포그라운드로 돌아올 때 동기화하는 방식이 여전히 필요합니다.
  • 영속화 저장소로는 용량 제한이 작고 동기 API인 localStorage보다 IndexedDB가 적합합니다.

출처: TanStack Query Network Mode · Persisting Offline mutations · persistQueryClient · MDN Navigator.onLine · caniuse Background Sync · RFC 9457 Problem Details

05 · 서버 상태 · 캐시

캐시와 stale 데이터

FE에는 캐시가 여러 층 있습니다. 실습에서는 그중 두 층, TanStack Query의 메모리 캐시(staleTime·gcTime)와 브라우저 HTTP 캐시(ETag·Cache-Control)가 요청 하나를 어떻게 나눠 처리하는지 봅니다.

정리: 네 개의 캐시 층

층저장하는 것답하는 질문BE가 주는 것
브라우저 HTTP 캐시HTTP 응답(바이트)네트워크에 갈 필요가 있나? 가면 본문을 다시 받아야 하나?Cache-Control, ETag, Last-Modified
Service Worker Cache요청-응답 쌍(앱이 직접 관리)네트워크가 아예 없을 때 무엇을 줄까?(정적 자원 버전 관리)
TanStack Query 캐시파싱된 객체 + 상태(stale, fetching, error)화면에 무엇을 보여 주고, 언제 다시 물어볼까?안정적인 응답 구조, 변경 후 무효화할 키를 알 수 있는 API
IndexedDB 영속화위 캐시의 스냅샷앱을 다시 켰을 때 무엇을 바로 보여 줄까?—
staleTime vs gcTime

staleTime: 다시 물어볼 필요가 있는가(신선도). gcTime: 화면에서 사라진 캐시를 언제 지울까(보관). stale이어도 캐시 값은 즉시 보여 주고 뒤에서 재검증합니다(stale-while-revalidate, 원래 RFC 5861의 HTTP 개념).

staleTime vs max-age

staleTime은 "JS가 fetch를 다시 부를지", max-age·ETag는 "그 fetch가 서버까지 갈지, 본문을 다시 받을지"를 정합니다. API에는 보통 no-cache + ETag, 신선도 판단은 staleTime에 맡깁니다.

Spring에서

ResponseEntity.ok().eTag(...)로 응답하면 Spring MVC가 GET/HEAD의 If-None-Match를 비교해 자동으로 304를 돌려줍니다. 본문을 만드는 비용 자체를 아끼려면 WebRequest.checkNotModified(etag)로 먼저 확인합니다.

출처: TanStack Query Important Defaults · MDN HTTP Caching · RFC 9111 · RFC 5861 · Spring MVC HTTP Caching

06 · Mutation · 재시도

낙관적 업데이트와 동시 수정

좋아요 버튼으로 낙관적 업데이트가 틀어지는 다섯 가지 상황을 재현하고, 두 관리자가 같은 상품을 고칠 때의 "잃어버린 수정"을 If-Match로 막습니다.

정리: 어디까지가 FE의 몫인가

상황FE(TanStack Query)가 하는 일BE가 보장해야 하는 것
빠른 연타버튼 잠금 또는 scope로 직렬화멱등한 상태 지정 API(PUT/DELETE)
응답 순서 역전중간 응답을 믿지 않고 마지막 mutation 뒤 invalidateGET이 최종 진실을 돌려줌
요청 도착 순서 역전scope 직렬화필요하면 클라이언트 시퀀스·버전으로 늦은 요청 무시
부분 실패스냅샷 롤백(동시 mutation이 있으면 invalidate로 재동기화)실패한 요청이 아무것도 바꾸지 않았음을 보장(트랜잭션)
다른 사용자의 동시 수정412를 받으면 최신 값을 보여 주고 사용자가 결정ETag(버전) + If-Match → 412, JPA @Version
서버 검증 실패롤백 + 이유 표시ProblemDetail로 무엇이 왜 거절됐는지

출처: TanStack Query Optimistic Updates · Mutation Scopes · RFC 9110 If-Match · 412 · RFC 6585 428 Precondition Required

07 · Mutation · 재시도

요청 취소

검색어 자동완성에서 "a → ap → app"이 빠르게 입력되면 응답이 뒤죽박죽 도착합니다. 취소 없이, AbortController로, TanStack Query로 각각 무엇이 보이는지 비교합니다.

정리

  • AbortController + fetch가 표준 취소 방법입니다. Axios의 CancelToken은 deprecated이고 AbortController(signal)를 쓰는 것이 현재 권장입니다.
  • TanStack Query는 queryFn에 signal을 넘기고, 쿼리 키가 바뀌거나 컴포넌트가 언마운트돼 구독자가 사라지면 그 signal을 abort합니다(signal을 fetch에 넘긴 경우).
  • 취소는 브라우저가 응답을 기다리지 않게 할 뿐입니다. 서버 부하를 줄이려면 디바운스(FE)와 최소 검색어 길이·캐시(BE)가 필요합니다.

출처: TanStack Query Query Cancellation · Axios Cancellation · MDN AbortController

08 · 서버 상태 · 캐시

페이지네이션과 무한 스크롤

같은 피드를 offset과 cursor 두 방식으로 불러오면서 글을 추가·삭제해 봅니다. 불안정한 네트워크에서는 페이지 하나가 실패하고 다시 불러오는 일이 잦아서, 페이지 경계가 흔들리지 않는 것이 더 중요해집니다.

정리

offsetcursor (keyset)
앞쪽 추가·삭제경계가 밀려 중복·누락영향 없음
실패한 페이지 재시도그사이 데이터가 바뀌면 다른 결과같은 커서로 같은 위치부터
DB 비용OFFSET만큼 읽고 버림 (뒤로 갈수록 느림)인덱스 seek
임의 페이지 이동가능 (게시판 번호 페이지)어려움 (무한 스크롤에 적합)

BE 설계 포인트: 커서는 정렬 키를 인코딩한 불투명 토큰으로 주고(FE가 내부 구조에 의존하지 않게), 동률을 막기 위해 (created_at, id) 같은 복합 키를 쓰며, 하나 더 읽어 hasNext/nextCursor를 명시합니다.

출처: TanStack Query Infinite Queries · RTK Query Infinite Queries

09 · 전송

대용량 파일 업로드

48MB 영상을 단일 POST, tus, S3 multipart로 올리면서 연결 끊김·브라우저 재시작·서명 만료를 일으킵니다. 핵심 질문은 "끊겼을 때 누가 진행 상태를 기억하는가"입니다.

정리

단일 multipart POSTtus (Uppy + tusd)S3 multipart (Uppy + presigned)
진행 상태를 기억하는 곳없음tus 서버 (업로드 URL + Upload-Offset)S3 (uploadId + 완료된 파트)
끊긴 뒤처음부터HEAD → 받은 지점부터 PATCH전송 중이던 파트만 다시
브라우저 재시작처음부터 (파일 재선택)저장된 URL로 이어서저장된 uploadId + listParts로 이어서
서버 구현가장 단순tusd 운영 + 훅 처리BFF의 생성·서명·완료 API, 버킷 CORS·라이프사이클
본문이 거치는 곳API 서버tusd (스토리지는 S3 등 선택)브라우저 → S3 직접
  • Uppy(현재 v5)는 프로토콜이 아니라 업로드 UI·오케스트레이션 라이브러리입니다. 실제 전송은 @uppy/tus(tus-js-client) 또는 @uppy/aws-s3가 합니다. 둘 중 하나를 고릅니다.
  • @uppy/tus의 기본 chunkSize는 Infinity, retryDelays 기본값은 [0, 1000, 3000, 5000]입니다. @uppy/aws-s3는 기본적으로 100MiB 초과 파일에 multipart를 씁니다.
  • S3 파트는 마지막을 제외하고 최소 5MiB, 최대 10,000개입니다. 버킷 CORS의 ExposeHeaders에 ETag가 없으면 브라우저가 파트 ETag를 읽지 못해 완료할 수 없습니다.
  • tus는 IETF에서 HTTP 재개 업로드 표준으로 논의되고 있습니다(draft-ietf-httpbis-resumable-upload).

출처: tus 1.0 프로토콜 · tusd Hooks · Uppy Tus · Uppy AWS S3 · AWS S3 Multipart Upload · IETF Resumable Uploads draft

10 · 전송

대용량 파일 다운로드

다운로드 이어받기는 브라우저와 HTTP 표준이 대부분 처리합니다. 실습에서는 BE가 지원하지 않거나(Range 미지원), 그사이 파일이 바뀌는(If-Range 필요) 경우를 봅니다.

정리: 업로드와 다운로드가 비대칭인 이유

누가 상태를 아나

다운로드는 받은 쪽(클라이언트)이 받은 만큼만 기억하면 되고 서버는 상태가 없습니다. 업로드는 서버가 받은 만큼을 기억해 줘야 합니다.

표준화

Range·206·If-Range는 HTTP 표준이고 서버·CDN·브라우저가 기본 지원합니다. 업로드 재개는 tus 같은 별도 프로토콜이 필요합니다.

중간 상태

다운로드는 완성된 파일에서 구간을 떼어 주면 됩니다. 업로드는 "절반만 존재하는 파일"을 서버가 보관·병합·검증해야 합니다.

출처: MDN HTTP Range requests · RFC 9110 If-Range · Spring MVC 소스(AbstractMessageConverterMethodProcessor): Resource 반환 시 Range 처리, If-Range 미평가

11 · 전송

오래 걸리는 작업

8초짜리 작업을 긴 요청, 202 + 폴링, SSE로 처리하면서 도중에 Wi-Fi → LTE 전환을 일으킵니다. "긴 연결을 유지하는 것"과 "짧은 요청 + 상태 조회"의 차이가 드러납니다.

정리

패턴적합한 경우불안정한 네트워크에서
긴 요청수 초 안에 끝나고 재시도가 싼 작업NAT·프록시 idle timeout, 망 전환, 백그라운드 전환에 결과 유실. 재시도하면 작업 중복
202 + 폴링수십 초~수 시간, 배치성 작업폴링 한 번 실패해도 다음 폴링이 이어받음. 인프라 호환성 최고
SSE진행률·토큰 스트리밍 같은 서버 → 클라이언트 단방향EventSource 자동 재연결 + Last-Event-ID로 이어서
WebSocket채팅·협업 편집 같은 양방향 실시간재연결 후 상태 동기화(시퀀스·체크포인트)를 앱이 직접 설계
Web Push앱이 닫혀 있어도 알려야 할 때OS 푸시 인프라 경유. 폴링·SSE와 함께 사용

출처: Azure Asynchronous Request-Reply · WHATWG Server-sent events · RFC 9110 202 Accepted

12 · 전송

BFF와 API 집계

홈 화면을 위해 FE가 API 여섯 개를 직접 부를지, BFF가 한 번에 모아 줄지. RTT·프로토콜·의존 관계·장애를 바꿔 가며 두 타임라인을 비교합니다.

정리

관점FE가 여러 번 호출BFF가 한 번에
RTT의존 호출마다 클라이언트 RTT가 쌓임클라이언트 왕복 1번, 의존 호출은 데이터센터 안에서
캐시리소스별로 세밀하게 (ETag·staleTime)화면 단위 응답이라 일부만 바뀌어도 전체 무효화
부분 실패자연스럽게 영역별 처리호출별 타임아웃 + null + errors 설계가 필수
페이로드API마다 공통 필드 중복 가능화면에 필요한 필드만 골라 줄 수 있음(모바일용 BFF)
비용클라이언트가 여러 계약을 앎BFF 계층의 개발·배포·관측 부담

출처: Sam Newman — Backends For Frontends · Spring Kotlin Coroutines

13 · 전송

HTTP/2 · HTTP/3가 바꾸는 것과 바꾸지 않는 것

멀티플렉싱과 QUIC이 해결하는 문제(연결 수 제한, 전송 계층 HOL blocking, 망 전환)를 눈으로 확인하고, "HTTP/2·3면 요청을 합칠 필요가 없다"는 오해를 정리합니다.

정리

HTTP/1.1HTTP/2HTTP/3 (QUIC)
동시 요청출처당 연결 약 6개, 넘으면 대기연결 하나에 멀티플렉싱연결 하나에 독립 스트림
패킷 손실그 연결의 요청이 대기모든 스트림이 대기 (TCP HOL)손실된 스트림만 대기
망 전환새 연결 + TLS새 연결 + TLSconnection ID로 연결 유지 (지원 시)
새 연결 비용TCP+TLS 1.3 = 2 RTT2 RTT1 RTT (재방문 0-RTT)
오해: "HTTP/2·3면 요청을 합칠 필요가 없다"

사라진 것은 연결 수 제한과 연결 수립 오버헤드입니다. 남아 있는 것: 요청 수만큼의 서버 처리(인증·조회·직렬화), 의존 호출의 RTT 누적, 요청마다 붙는 헤더, 화면 단위 캐시·부분 실패 설계. BFF의 이유 중 "연결 제한 회피"는 약해졌지만 나머지는 그대로입니다. 2026년 현재 HTTP/3는 주요 브라우저 대부분이 지원합니다.

출처: RFC 9000 QUIC (Connection Migration, disable_active_migration) · RFC 9114 HTTP/3 · caniuse HTTP/3 · RFC 7838 Alt-Svc

14 · 라이브러리 · 계층

실전 라이브러리 조합 (2026)

프레임워크를 고르면 어떤 라이브러리가 어느 계층을 맡는지와 최소 설정 코드가 바뀝니다. 서버 상태·전송·업로드 계층은 프레임워크가 달라도 거의 같다는 점을 확인하세요.

분리 원칙

Zustand·Redux·Pinia 같은 상태 관리는 클라이언트 상태(모달, 폼 입력, 필터)를, TanStack Query는 서버 상태(서버가 진실인 데이터)를 맡습니다. 서버 응답을 전역 store에 복사해 두면 재검증·무효화·중복 제거를 다시 구현하게 됩니다.

출처: TanStack Query (React·Vue·Solid 어댑터) · Solid useQuery · Uppy

15 · 라이브러리 · 계층

브라우저 API vs HTTP 표준 vs 라이브러리 vs 직접 구현

가장 헷갈리기 쉬운 구분입니다. 카드마다 네 가지 중 하나를 골라 보세요. 누가 보장하는지 알아야 "라이브러리를 바꾸면 사라지는 것"과 "BE가 구현하지 않으면 어떤 라이브러리로도 안 되는 것"이 보입니다.

정리

구분예의미
브라우저 제공fetch, AbortController, AbortSignal.timeout, IndexedDB, Service Worker, EventSource, online 이벤트, Background Sync표준 API. 브라우저별 지원 차이는 확인해야 함
HTTP·프로토콜 표준Cache-Control, ETag/304, If-Match/412, Range/206, Retry-After, Problem Details, QUIC, tus서버와 클라이언트의 계약. 서버가 구현하지 않으면 FE 라이브러리가 아무리 좋아도 소용없음
라이브러리 제공staleTime·gcTime, 재시도·백오프, 중복 제거, networkMode, mutation 큐·scope, 영속화, Uppy 복원위 두 층을 조합해 자동화. 걷어내도 직접 구현은 가능
앱·BE 직접 구현Idempotency-Key 저장·재생, 커서 발급, 충돌 해결 UI, 되돌릴 범위, 재시도할 오류 판단표준도 라이브러리도 대신해 주지 않는 설계
16 · 종합

쇼핑몰 아키텍처 — 10가지 시나리오 재생

지금까지의 전략을 하나의 쇼핑몰로 묶었습니다. 시나리오를 고르고 다음 ▶으로 한 단계씩 넘기며 각 요청의 이유를 읽어 보세요. 관련 실습으로 바로 이동할 수 있습니다.

React/Vue/Solid 화면 TanStack Query 캐시·재시도·취소 낙관적 반영·오프라인 큐 BFF / API (Spring) ETag·If-Match·멱등 키 cursor·202 작업·ProblemDetail DB · Redis PG사 · S3 (multipart) 업로드 본문은 presigned URL로 브라우저 → S3 직접
본문이 큰 업로드만 BFF를 거치지 않고, 나머지 요청은 모두 BFF의 계약(ETag·멱등 키·cursor·ProblemDetail)을 통과합니다.
17 · 종합

BE 개발자가 FE에서 알아야 할 것

FE 프레임워크를 배우려는 게 아니라, API를 설계할 때 FE의 네트워크 요구를 받아 줄 계약을 갖추기 위한 정리입니다. 아래 점검표로 지금 만드는 API를 체크해 보세요.

이해 수준별 정리

수준항목담당BE 관점에서 왜
반드시 이해Query 캐시 · stale/freshFEstaleTime이 곧 API 호출 빈도. 트래픽 예측의 기준
재시도 · 지수 백오프FE장애 때 받게 될 재시도 트래픽 모양. Retry-After로 유도 가능
Idempotency-Key계약BE가 강제하지 않으면 FE의 재시도 설계가 무의미
낙관적 업데이트 · 롤백계약명확한 에러와 멱등 API가 있어야 롤백이 정확
mutation 순서 · scopeFE서버 도착 순서는 보장되지 않는다는 전제로 API 설계
요청 취소FE취소돼도 서버는 보통 끝까지 처리 — 비싼 API는 BE도 방어
오프라인 · 재연결FE재연결 순간 몰리는 요청(큐 재전송·재검증)을 감당해야 함
cursor 페이지네이션계약커서·hasNext를 BE가 줘야 무한 스크롤이 정합성을 지킴
이어 올리기 · 진행률BE업로드 상태를 서버(tus·S3)가 보관하는 구조를 BE가 운영
개념만정규화 캐시 vs 문서 캐시FE안정적인 id가 있으면 어느 쪽이든 편하다
SSE 재연결 세부계약이벤트 id와 재전송 범위만 맞추면 됨
Service Worker 캐시 전략FE정적 자원 버전 관리와 연결됨
HTTP/3 · QUICBE엣지(CDN·LB) 설정 문제에 가깝다
깊이 몰라도 됨React 훅 문법, 리렌더 최적화FEAPI 설계에 영향 없음
Uppy·Golden Retriever 저장 형식FEBE는 업로드 프로토콜 쪽만 맞추면 됨
QUIC 패킷 수준 동작BE인프라가 대신 처리
18 · 종합

최종 표와 우선순위

문제별로 FE 전략·대표 라이브러리·브라우저 API·HTTP/프로토콜·BE 대응을 한 줄에 모았습니다. Tier와 담당으로 걸러 보세요. 행의 실습 버튼으로 바로 해당 실습에 갈 수 있습니다.

문제FE 전략대표 라이브러리Browser APIHTTP / ProtocolBE 대응실습
느린 응답캐시 즉시 표시 + 재검증, prefetchTanStack QueryfetchCache-Control, ETagETag·304, 응답 시간 관리, 필요 시 BFF
타임아웃모든 요청에 타임아웃, 재시도와 예산 분리TanStack Query + fetch 래퍼AbortSignal.timeout / any—p99 처리 시간 공유, 과부하 시 빠른 503
재시도재시도할 오류만, 지수 백오프 + 지터TanStack QueryfetchRetry-After (429·503)정확한 상태 코드, Retry-After, 레이트 리밋
중복 요청 (결제 등)버튼 잠금 + 의도당 멱등 키 재사용useMutationcrypto.randomUUIDIdempotency-Key (IETF 초안)키 선점·응답 재생·409·422·400
오래된 데이터staleTime·gcTime, 포커스·재연결 재검증, 수정 후 무효화TanStack Query / SWRvisibilitychangeETag / If-None-Match / 304ETag 발급, 의도적인 Cache-Control
늦게 온 응답 (경쟁 상태)키 기반 쿼리 + 자동 취소, 디바운스TanStack QueryAbortController—비싼 검색 방어(최소 길이·캐시)
낙관적 업데이트onMutate 스냅샷 → onError 롤백 → 마지막에 invalidateTanStack Query—PUT / DELETE (멱등)상태 지정 API, 명확한 에러
동시 수정412 시 최신 값 보여 주고 사용자 결정TanStack Query—ETag + If-Match → 412, 428버전 = ETag, JPA @Version
목록 정합성커서를 그대로 전달, id로 dedupeuseInfiniteQuery——불투명 cursor + hasNext, keyset 쿼리
오프라인일시정지 + mutation 큐 + 영속화 + scopeTanStack Query persistIndexedDB, online 이벤트—큐 재전송 대비 멱등 키, 재검증·충돌 409
오래 걸리는 작업짧은 요청 + 폴링 / SSETanStack Query refetchIntervalEventSource202 Accepted, Location, Last-Event-ID작업 큐 + 상태 API + 이벤트 id
모바일 고지연요청 수·의존 호출 줄이기——HTTP/2 · HTTP/3BFF(호출별 타임아웃·부분 응답)
재시도 폭주지터 있는 백오프TanStack Query retryDelay—Retry-After레이트 리밋, Retry-After 분산
대용량 업로드청크·진행률·일시정지UppyFile, Blob.slice, IndexedDBtus / S3 Multiparttusd 또는 서명·완료 API, CORS ETag, 라이프사이클
업로드 재개저장된 URL·uploadId로 이어서Uppy + tus-js-client / aws-s3localStoragetus HEAD/PATCH, S3 ListParts진행 상태 보관, 미완료 정리
다운로드 재개브라우저 기본 다운로드에 맡김—브라우저 다운로드 관리자Range / 206 / If-RangeRange + 강한 ETag (또는 CDN·S3 위임)
망 전환재연결 후 재검증——QUIC connection migrationHTTP/3, LB connection ID 라우팅

우선순위

Tier 1 · 대부분의 서비스

Query 캐시·staleTime, 재시도할 오류 구분, 타임아웃, Idempotency-Key, ETag/304, 상태 지정 API, If-Match, cursor 페이지네이션, 요청 취소, ProblemDetail

Tier 2 · 모바일·불안정 네트워크 비중이 큰 서비스

오프라인 큐와 영속화, 202 + 폴링·SSE, BFF와 부분 응답, 지터·레이트 리밋, 재연결 폭주 대비

Tier 3 · 대용량·특수 요구

tus·S3 multipart 이어 올리기, 다운로드 Range/If-Range, HTTP/3 connection migration, Background Sync, 충돌 해결 UI

이 문서는 2026년 9월 기준 공식 문서(TanStack Query, Spring Framework, Uppy, tus, AWS), IETF RFC·Internet-Draft, WHATWG·MDN을 우선해 조사했습니다. 각 장 하단에 출처가 있습니다. 시뮬레이터 수치는 관찰용 가정값입니다.