데이터 통신

통신

대표적인 api 통신 tanstack-query, axios, fetch 정리하겠습니다.


1. React Query의 기본 개념

  • React Query란?

React Query는 React 애플리케이션에서 비동기 데이터를 효율적으로 관리할 수 있도록 도와주는 라이브러리입니다. API 데이터를 캐싱하고, 자동으로 리패칭하며, 네트워크 상태에 따라 데이터를 동기화하는 기능을 제공합니다.

  • 주요 기능

  • 자동 캐싱: API 데이터를 자동으로 저장하여 불필요한 요청을 방지

  • 백그라운드 리패칭: 오래된 데이터를 자동으로 새로고침

  • 데이터 동기화: API 데이터와 UI 상태를 자동으로 동기화

  • 쿼리 키 기반 데이터 관리: 동일한 데이터를 필요로 하는 여러 컴포넌트에서 상태를 공유 가능

  • 에러 핸들링 내장: 네트워크 에러 발생 시 자동으로 재시도


2. useQuery() useMutation() 사용법

2.1 useQuery(): 데이터 가져오기 (Fetching)

useQuery 는 서버에서 데이터를 가져오는 데 사용됩니다.

예제: 사용자 목록 불러오기

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

const fetchUsers = async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/users')
  if (!response.ok) throw new Error('Network response was not ok')
  return response.json()
}

const Users = () => {
  const { data, isLoading, error } = useQuery(['users'], fetchUsers)

  if (isLoading) return <p>Loading...</p>
  if (error) return <p>Error: {error.message}</p>

  return (
    <ul>
      {data.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  )
}

2.2 useMutation(): 데이터 변경 (POST, PUT, DELETE)

useMutation()은 서버 데이터를 변경할 때 사용됩니다.

예제: 사용자 추가

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

const addUser = async (newUser) => {
  const response = await fetch('https://jsonplaceholder.typicode.com/users', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(newUser),
  })
  return response.json()
}

const AddUser = () => {
  const queryClient = useQueryClient()
  const mutation = useMutation(addUser, {
    onSuccess: () => {
      queryClient.invalidateQueries(['users'])
    },
  })

  return (
    <button onClick={() => mutation.mutate({ name: 'New User' })}>
      Add User
    </button>
  )
}

3. 쿼리 키 & 캐싱 전략

3.1 쿼리 키 (queryKey)

  • useQuery() 에서 쿼리 키를 사용하여 캐싱을 제어할 수 있습니다.
  • 동일한 쿼리 키를 가진 요청은 캐싱된 데이터를 반환합니다.
const { data } = useQuery(['users'], fetchUsers)

3.2 캐싱 전략 설정

  • staleTime: 데이터가 만료되기 전까지 재요청하지 않음
  • cacheTime: 캐싱된 데이터가 메모리에 유지되는 시간
const { data } = useQuery(['users'], fetchUsers, {
  staleTime: 1000 * 60 * 5, // 5분 동안 데이터가 신선하다고 간주
  cacheTime: 1000 * 60 * 10, // 10분 후 캐시 삭제
})

4. useMutation() 활용한 데이터 변경

4.1 데이터 추가, 수정, 삭제 처리

  • onMutate: 뮤테이션이 실행되기 전에 캐시 업데이트
  • onSuccess: 성공 시 queryClient.invalidateQueries()를 사용하여 데이터 갱신
const mutation = useMutation(addUser, {
  onMutate: async (newUser) => {
    await queryClient.cancelQueries(['users'])
    const previousUsers = queryClient.getQueryData(['users'])
    queryClient.setQueryData(['users'], (old) => [...old, newUser])
    return { previousUsers }
  },
  onError: (err, newUser, context) => {
    queryClient.setQueryData(['users'], context.previousUsers)
  },
  onSettled: () => {
    queryClient.invalidateQueries(['users'])
  },
})

5. 낙관적 업데이트 & 배치 처리

5.1 낙관적 업데이트 (Optimistic Updates)

const mutation = useMutation(updateUser, {
  onMutate: (newUser) => {
    queryClient.setQueryData(['users', newUser.id], newUser)
  },
  onSuccess: () => {
    queryClient.invalidateQueries(['users'])
  },
})

6. useInfiniteQuery 를 활용한 무한 스크롤

6.1 무한 스크롤 구현

const { data, fetchNextPage } = useInfiniteQuery(['users'], fetchUsers, {
  getNextPageParam: (lastPage, allPages) => lastPage.nextCursor,
})

7. Axios 개요

7.1 Axios란?

Axios는 브라우저와 Node.js 환경에서 사용할 수 있는 HTTP 클라이언트 라이브러리로, Promise 기반으로 동작하며, RESTful API와 쉽게 상호작용할 수 있도록 도와줍니다.

7.2 Fetch API와의 차이

기능AxiosFetch API
기본 제공 기능자동 JSON 변환, 요청 인터셉터 등수동 JSON 변환 필요
에러 핸들링상태 코드 기반 자동 오류 처리catch로만 처리 가능
요청 취소CancelToken 제공AbortController 필요

예제:

// Axios 사용 예제
axios
  .get('https://jsonplaceholder.typicode.com/posts')
  .then((response) => console.log(response.data))
  .catch((error) => console.error(error))
// Fetch 사용 예제
fetch('https://jsonplaceholder.typicode.com/posts')
  .then((response) => response.json())
  .then((data) => console.log(data))
  .catch((error) => console.error(error))

8. Axios 설치 및 기본 사용법

8.1 Axios 설치

npm install axios

8.2 기본적인 GET 요청

axios
  .get('https://jsonplaceholder.typicode.com/users')
  .then((response) => console.log(response.data))
  .catch((error) => console.error(error))

8.3 기본적인 POST 요청

axios
  .post('https://jsonplaceholder.typicode.com/posts', {
    title: 'Axios Study',
    body: 'Learning Axios',
    userId: 1,
  })
  .then((response) => console.log(response.data))
  .catch((error) => console.error(error))

9. Axios 요청 옵션

9.1 Headers 설정

axios
  .get('https://api.example.com/data', {
    headers: {
      Authorization: 'Bearer token123',
      'Content-Type': 'application/json',
    },
  })
  .then((response) => console.log(response.data))

9.2 Query Parameters (쿼리 스트링) 사용

axios
  .get('https://api.example.com/users', {
    params: {
      id: 123,
      role: 'admin',
    },
  })
  .then((response) => console.log(response.data))

9.3 Timeout 설정

axios
  .get('https://api.example.com/slow-response', { timeout: 5000 })
  .then((response) => console.log(response.data))
  .catch((error) => console.error('Request timeout!', error))

10. 응답 처리 및 에러 핸들링

10.1 응답 데이터 처리

axios
  .get('https://jsonplaceholder.typicode.com/users')
  .then((response) => console.log(response.data)) // response.data에 실제 데이터가 포함됨
  .catch((error) => console.error(error))

10.2 에러 핸들링

axios
  .get('https://api.example.com/404')
  .then((response) => console.log(response.data))
  .catch((error) => {
    if (error.response) {
      console.log('Error:', error.response.status, error.response.data)
    } else if (error.request) {
      console.log('No response received:', error.request)
    } else {
      console.log('Error setting up request:', error.message)
    }
  })

11. Axios Interceptors (인터셉터)

11.1 요청 인터셉터

axios.interceptors.request.use(
  (config) => {
    config.headers.Authorization = `Bearer token123`
    return config
  },
  (error) => Promise.reject(error),
)

11.2 응답 인터셉터

axios.interceptors.response.use(
  (response) => {
    console.log('Response received:', response)
    return response
  },
  (error) => {
    console.log('Error occurred:', error)
    return Promise.reject(error)
  },
)

12. Axios 인스턴스 활용

12.1 Axios 인스턴스 생성

const apiClient = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 10000,
  headers: { Authorization: 'Bearer token123' },
})

12.2 인스턴스를 이용한 요청

apiClient
  .get('/users')
  .then((response) => console.log(response.data))
  .catch((error) => console.error(error))

13. 동시 요청 처리

13.1 axios.all()axios.spread() 사용

axios
  .all([
    axios.get('https://api.example.com/users'),
    axios.get('https://api.example.com/posts'),
  ])
  .then(
    axios.spread((users, posts) => {
      console.log('Users:', users.data)
      console.log('Posts:', posts.data)
    }),
  )

14. 파일 업로드 및 다운로드

14.1 파일 업로드

const formData = new FormData()
formData.append('file', fileInput.files[0])

axios
  .post('https://api.example.com/upload', formData, {
    headers: { 'Content-Type': 'multipart/form-data' },
  })
  .then((response) => console.log(response.data))
  .catch((error) => console.error(error))

14.2 파일 다운로드

axios({
  url: 'https://api.example.com/file.pdf',
  method: 'GET',
  responseType: 'blob',
}).then((response) => {
  const url = window.URL.createObjectURL(new Blob([response.data]))
  const link = document.createElement('a')
  link.href = url
  link.setAttribute('download', 'file.pdf')
  document.body.appendChild(link)
  link.click()
})

15. CORS 이슈 해결

15.1 CORS란?

Cross-Origin Resource Sharing (CORS)은 보안상의 이유로 도메인 간 요청을 제한하는 정책입니다.

15.2 CORS 문제 해결 방법

  • 서버에서 Access-Control-Allow-Origin: * 헤더 설정
  • 프록시 서버 사용 (proxy 옵션 설정)

Previous
Zustand(대세)
Next
secure