누구나 로고
オンライン講座企業・団体研修ライブラリトレンドインサイト無料コースお知らせお問い合わせ採用サイト
모든 강의를 무료로 볼 수 있어요. 회원가입 없이도 학습 가능합니다.
로그인무료 회원가입
강좌리액트 입문 — 컴포넌트로 만드는 웹21강

21강 / 전체 28강

라우팅 — React Router

6분 읽기 조회 1

TanStack Query(React Query)로 서버 상태를 관리하는 방법, useQuery와 useMutation, 캐싱과 자동 재패칭의 원리를 직접 만든 useFetch와 비교합니다.

🤔 서버 상태 vs 클라이언트 상태

지금까지 배운 useState와 Context는 클라이언트 상태를 관리합니다 — 폼 값, UI 토글, 사용자 선택 등 브라우저에서 만들어지고 브라우저에 사는 데이터입니다. 그런데 실제 앱 데이터 대부분은 서버에서 옵니다. 서버 상태는 성격이 다릅니다:

  • 비동기로 가져와야 합니다
  • 서버의 데이터가 언제든지 바뀔 수 있습니다 (다른 사용자가 수정)
  • 캐싱해서 불필요한 중복 요청을 줄여야 합니다
  • 오래된 데이터를 자동으로 갱신해야 합니다

이 문제를 해결하는 것이 TanStack Query(옛 React Query)입니다.

📦 설치 및 설정

npm install @tanstack/react-query @tanstack/react-query-devtools
// main.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5,  // 5분간 신선한 데이터로 취급
      retry: 2,                    // 실패 시 2번 재시도
    },
  },
})

ReactDOM.createRoot(document.getElementById('root')!).render(
  <QueryClientProvider client={queryClient}>
    <App />
    <ReactQueryDevtools initialIsOpen={false} />  {/* 개발 도구 */}
  </QueryClientProvider>
)

🔍 useQuery — 데이터 패칭

import { useQuery } from '@tanstack/react-query'

interface Post { id: number; title: string; body: string }

async function fetchPosts(): Promise<Post[]> {
  const res = await fetch('https://jsonplaceholder.typicode.com/posts')
  if (!res.ok) throw new Error('API 요청 실패')
  return res.json()
}

function PostList() {
  const { data: posts, isLoading, error, isRefetching } = useQuery({
    queryKey: ['posts'],       // 캐시 키 — 동일 키면 캐시된 데이터 사용
    queryFn: fetchPosts,       // 데이터를 가져오는 함수
  })

  if (isLoading) return <div>불러오는 중...</div>
  if (error) return <div>오류: {error.message}</div>

  return (
    <>
      {isRefetching && <span className="text-xs text-gray-400">업데이트 중...</span>}
      <ul>
        {posts?.map((post) => (
          <li key={post.id}>
            <h3>{post.title}</h3>
          </li>
        ))}
      </ul>
    </>
  )
}

같은 queryKey를 여러 컴포넌트에서 사용하면 하나의 요청만 발생하고, 캐시된 데이터를 공유합니다. 이것이 직접 만든 useFetch와의 결정적 차이입니다.

✏️ useMutation — 데이터 변경

import { useMutation, useQueryClient } from '@tanstack/react-query'

async function createPost(newPost: Omit<Post, 'id'>): Promise<Post> {
  const res = await fetch('https://jsonplaceholder.typicode.com/posts', {
    method: 'POST',
    body: JSON.stringify(newPost),
    headers: { 'Content-Type': 'application/json' },
  })
  if (!res.ok) throw new Error('생성 실패')
  return res.json()
}

function NewPostForm() {
  const queryClient = useQueryClient()
  const [title, setTitle] = useState('')

  const mutation = useMutation({
    mutationFn: createPost,
    onSuccess: () => {
      // 성공 시 게시글 목록 캐시 무효화 → 자동 재패치
      queryClient.invalidateQueries({ queryKey: ['posts'] })
      setTitle('')
    },
    onError: (error) => {
      console.error('생성 실패:', error)
    },
  })

  return (
    <form onSubmit={(e) => { e.preventDefault(); mutation.mutate({ title, body: '...', userId: 1 }) }}>
      <input value={title} onChange={(e) => setTitle(e.target.value)} placeholder="제목" />
      <button type="submit" disabled={mutation.isPending}>
        {mutation.isPending ? '저장 중...' : '게시글 추가'}
      </button>
      {mutation.isError && <p className="text-red-500">{mutation.error.message}</p>}
    </form>
  )
}

🔄 직접 만든 useFetch vs TanStack Query

기능useFetch (직접)TanStack Query
캐싱없음자동
중복 요청 방지없음자동 (같은 queryKey)
자동 재패치없음탭 포커스, 재연결 시 자동
재시도없음기본 3회
로딩/에러 상태수동자동
MutationuseEffect 직접 구현useMutation

✅ 정리

  • TanStack Query는 서버 상태 관리 전문 도구 — 캐싱, 재패치, 재시도를 자동으로 처리합니다.
  • useQuery: queryKey + queryFn으로 데이터를 패칭하고 캐싱합니다.
  • useMutation: 생성/수정/삭제 + invalidateQueries로 캐시 갱신.
  • 실무 프로젝트에서는 직접 useFetch를 만드는 것보다 TanStack Query를 사용하세요.

관련 주제

  • react-router-dom 설치
  • Routes Route Link
  • 페이지 이동
  • 동적 경로
  • 개발·프로그래밍
  • 개발·프로그래밍 강의
  • 리액트 입문 — 컴포넌트로 만드는 웹
  • 무료강의
  • 무료 온라인 강의
  • NUGUNA
  • 누구나
이전 20강리스트 앱 실전 ②다음 22강 스타일링 방법들

댓글

0/1000

불러오는 중...

목차

  1. 1.리액트란 무엇인가
  2. 2.개발 환경 만들기 — Vite
  3. 3.JSX 기초
  4. 4.컴포넌트 만들기
  5. 5.Props로 데이터 전달
  6. 6.State와 useState
  7. 7.이벤트 처리
  8. 8.조건부 렌더링
  9. 9.리스트 렌더링과 key
  10. 10.폼 다루기
  11. 11.useEffect 기초
  12. 12.데이터 가져오기(fetch)
  13. 13.상태 끌어올리기
  14. 14.컴포넌트 합성과 재사용
  15. 15.useRef와 DOM 접근
  16. 16.커스텀 훅 만들기
  17. 17.Context API
  18. 18.useReducer
  19. 19.리스트 앱 실전 ①
  20. 20.리스트 앱 실전 ②
  21. 21.라우팅 — React Router
  22. 22.스타일링 방법들
  23. 23.리스트·상세 페이지 연결
  24. 24.비동기 상태 관리 패턴
  25. 25.성능 기초 — memo
  26. 26.에러 처리와 경계
  27. 27.환경변수와 빌드
  28. 28.배포하기

カスタマーサポート

  • お知らせ
  • よくある質問
  • お問い合わせ
  • コミュニティ

ご利用案内

  • 利用規約
  • プライバシーポリシー
  • 返金ポリシー

サービス

  • 会社紹介
  • 新規登録
  • 新着コース
  • 無料コース
누구나 로고
利用規約プライバシーポリシー返金ポリシー

상호명: NUGUNA  |  대표자: 정우진  |  사업자등록번호: 392-32-01817  |  통신판매업신고: 제 2026-서울양천-0564 호

주소: 서울특별시 양천구 목동서로 100  |  이메일: nugunapass@gmail.com  |  전화: 010-6395-3043

© 2026 NUGUNA. All rights reserved.

KB예금주인증관리자
ホームコースライブラリお問い合わせMY