๐Ÿ”„ 16. API ํ˜ธ์ถœ ์ตœ์ ํ™” โ€” SWR vs React Query ์™„์ „ ๋น„๊ต ์ •๋ฆฌ

JM_Devยท2025๋…„ 4์›” 29์ผ
1
post-thumbnail

ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์„ ํ•˜๋‹ค ๋ณด๋ฉด API ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ๊ฐ€ ๋„ˆ๋ฌด๋‚˜ ๊ธฐ๋ณธ์ ์ธ ์ž‘์—…์ธ๋ฐ,
์ง์ ‘ fetch๋งŒ ์จ์„œ ๊ด€๋ฆฌํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์ƒ๊ฐ๋ณด๋‹ค ๊ด€๋ฆฌํ•  ๊ฒŒ ๋งŽ๋‹ค. (๋กœ๋”ฉ ์ƒํƒœ, ์—๋Ÿฌ ์ฒ˜๋ฆฌ, ์บ์‹ฑ ๋“ฑ)
๊ทธ๋ž˜์„œ ๋“ฑ์žฅํ•œ ๊ฒŒ ๋ฐ”๋กœ SWR๊ณผ React Query ๊ฐ™์€ ๋ฐ์ดํ„ฐ ํŒจ์นญ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ!
์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ๋‘ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‹ค์ œ ์จ๋ณด๊ณ  ๋А๋‚€ ์ฐจ์ด์™€ ์„ ํƒ ๊ธฐ์ค€์„ ์ •๋ฆฌํ•ด๋ดค๋‹ค.


โœ… SWR์ด๋ž€?

SWR = Stale-While-Revalidate
(์กฐ๊ธˆ ์˜ค๋ž˜๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋จผ์ € ๋ณด์—ฌ์ฃผ๊ณ , ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ „๋žต)

  • Vercel์—์„œ ๋งŒ๋“  ์˜คํ”ˆ์†Œ์Šค
  • "ํ•ญ์ƒ ์‹ ์„ ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์—ฌ์ฃผ์ž"๋Š” ์ฒ ํ•™
  • ์‚ฌ์šฉ๋ฒ•์ด ๋งค์šฐ ์‹ฌํ”Œํ•˜๊ณ  ์ง๊ด€์ ์ž„

SWR ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

import useSWR from 'swr'

const fetcher = (url: string) => fetch(url).then(res => res.json())

function Profile() {
  const { data, error, isLoading } = useSWR('/api/user', fetcher)

  if (error) return <div>์—๋Ÿฌ ๋ฐœ์ƒ</div>
  if (isLoading) return <div>๋กœ๋”ฉ ์ค‘...</div>

  return <div>์•ˆ๋…•ํ•˜์„ธ์š”, {data.name}๋‹˜</div>
}

SWR ์ฃผ์š” ํŠน์ง•

  • ๊ธฐ๋ณธ์ ์œผ๋กœ ์ž๋™ ์บ์‹ฑ + ์žฌ๊ฒ€์ฆ์„ ์ง€์›
  • mutate๋กœ ์บ์‹œ ์ง์ ‘ ์ˆ˜์ • ๊ฐ€๋Šฅ
  • ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ(์˜ˆ: ํ”„๋กœํ•„, ๋Œ€์‹œ๋ณด๋“œ)์— ์ ํ•ฉ
  • API๋ฅผ ์ง์ ‘ fetcher๋กœ ๋งŒ๋“ค์–ด์„œ ์ž์œ ๋กญ๊ฒŒ ์กฐ์ • ๊ฐ€๋Šฅ

โœ… React Query๋ž€?

React Query = "์„œ๋ฒ„ ์ƒํƒœ ๊ด€๋ฆฌ"๋ฅผ ์œ„ํ•œ ๋„๊ตฌ
ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ(useState)์ฒ˜๋Ÿผ ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•ด์ค€๋‹ค.

  • ๋ฐ์ดํ„ฐ ํŒจ์นญ + ์บ์‹ฑ + ์—๋Ÿฌ ์ฒ˜๋ฆฌ + ๋ฆฌํŠธ๋ผ์ด + ์ธํ”ผ๋‹ˆํ‹ฐ ์Šคํฌ๋กค๊นŒ์ง€ ์ง€์›
  • ๋” ๋ณต์žกํ•˜๊ณ  ๋‹ค์–‘ํ•œ ์ƒํ™ฉ์— ๋Œ€์‘ ๊ฐ€๋Šฅ
  • ํ˜„์žฌ๋Š” TanStack Query๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ๋ฐœ์ „

React Query ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

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

const fetchUser = async () => {
  const res = await fetch('/api/user')
  return res.json()
}

function Profile() {
  const { data, error, isLoading } = useQuery({
    queryKey: ['user'],
    queryFn: fetchUser
  })

  if (isLoading) return <div>๋กœ๋”ฉ ์ค‘...</div>
  if (error) return <div>์—๋Ÿฌ ๋ฐœ์ƒ</div>

  return <div>์•ˆ๋…•ํ•˜์„ธ์š”, {data.name}๋‹˜</div>
}

React Query ์ฃผ์š” ํŠน์ง•

  • API ํ˜ธ์ถœ ์ƒํƒœ๋ฅผ useQuery, useMutation์œผ๋กœ ๊ตฌ๋ถ„ ๊ด€๋ฆฌ
  • ์ž๋™ ์žฌ์‹œ๋„(retry), ์—๋Ÿฌ ํ•ธ๋“ค๋ง, ๋ฐฑ๊ทธ๋ผ์šด๋“œ refetch๊นŒ์ง€ ๋‚ด์žฅ
  • ์ฟผ๋ฆฌ key๋ฅผ ํ†ตํ•œ ๊ฐ•๋ ฅํ•œ ์บ์‹ฑ ์ „๋žต ์ ์šฉ
  • ํŽ˜์ด์ง€๋„ค์ด์…˜์ด๋‚˜ ๋ฌดํ•œ ์Šคํฌ๋กค ๊ธฐ๋Šฅ๋„ ๊ธฐ๋ณธ ์ œ๊ณต

โœ… SWR vs React Query ๋น„๊ต

ํ•ญ๋ชฉSWRReact Query
๋งŒ๋“  ๊ณณVercelTanner Linsley (TanStack)
์ฃผ์š” ๋ชฉ์ ๊ฐ„๋‹จํ•œ ๋ฐ์ดํ„ฐ fetch + ์บ์‹ฑ์„œ๋ฒ„ ์ƒํƒœ ๊ด€๋ฆฌ ์ „์ฒด
์ฝ”๋“œ ๋ณต์žก๋„์‹ฌํ”Œ, ์ตœ์†Œ ์„ค์ •๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์— ๋”ฐ๋ผ ๋ณต์žก๋„ ์ƒ์Šน
๊ธฐ๋Šฅ ํ™•์žฅ์„ฑ๊ธฐ๋ณธ fetch + ์บ์‹œ ๊ด€๋ฆฌMutation, Pagination, etc.
์ž๋™ ๋ฆฌํŒจ์น˜์ง€์›๋” ์„ธ๋ฐ€ํ•œ ์„ค์ • ๊ฐ€๋Šฅ
SSR ์ง€์›์ง์ ‘ fetch ์กฐ์ • ํ•„์š”๊ณต์‹์ ์œผ๋กœ SSR ์ง€์› ๊ฐ•ํ™”
๋ฌดํ•œ์Šคํฌ๋กค ์ง€์›์ง์ ‘ ๊ตฌํ˜„ ํ•„์š”๊ธฐ๋ณธ ์ง€์›

โœ… ๊ฐœ์ธ์ ์ธ ์‚ฌ์šฉ ๊ฒฝํ—˜ ์ •๋ฆฌ

ํ”„๋กœ์ ํŠธ ์„ฑ๊ฒฉ์ถ”์ฒœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
๊ฐ„๋‹จํ•œ ๋ฐ์ดํ„ฐ fetch ์œ„์ฃผSWR
CRUD๊ฐ€ ๋งŽ๊ณ , ์„œ๋ฒ„ ์ƒํƒœ๋ฅผ ํ†ตํ•ฉ ๊ด€๋ฆฌํ•ด์•ผ ํ•จReact Query
SEO, SSR ๊ณ ๋ ค ํ•„์š”React Query

โœ… ์‹ค์ „ ํŒ

  • Next.js์—์„œ๋Š” SWR์ด ๋” ๊ฐ„ํŽธํ•  ๋•Œ๊ฐ€ ๋งŽ๋‹ค. (ํŠนํžˆ Client Components์—์„œ)
  • ๋Œ€ํ˜• ํ”„๋กœ์ ํŠธ์—์„œ๋Š” React Query๋ฅผ ์“ด๋‹ค. (Mutation ๊ด€๋ฆฌ๊ฐ€ ์ค‘์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ)
  • API ์ŠคํŽ™์ด ๋ณต์žกํ•˜๊ณ  ์„œ๋ฒ„ ์ƒํƒœ๊ฐ€ ๋ณต์žกํ•œ ์•ฑ์ด๋ผ๋ฉด ๋ฌด์กฐ๊ฑด React Query๊ฐ€ ์ข‹๋‹ค.

๐Ÿ“ ๋‚ด๊ฐ€ ๋А๋‚€ ์ 

์ฒ˜์Œ์—๋Š” "๋‘˜ ๋‹ค ๋˜‘๊ฐ™์ด fetch๋งŒ ํ•ด์ฃผ๋Š” ๊ฑฐ ์•„๋ƒ?"๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋Š”๋ฐ,
์จ๋ณด๋‹ˆ๊นŒ SWR์€ ๋‹จ์ˆœํ•œ fetch + ์บ์‹ฑ,
React Query๋Š” ์•„์˜ˆ ์„œ๋ฒ„ ์ƒํƒœ(state)๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ˆ˜์ค€์ด์—ˆ๋‹ค.
ํŠนํžˆ ์—๋Ÿฌ ๋ฆฌํŠธ๋ผ์ด, ์˜ตํ‹ฐ๋ฏธ์Šคํ‹ฑ ์—…๋ฐ์ดํŠธ, ์ฟผ๋ฆฌ ๋ฌดํšจํ™”(invalidate) ๊ฐ™์€ ๊ธฐ๋Šฅ์ด
์ง„์งœ ๋ณต์žกํ•œ ์ƒํ™ฉ์—์„œ ์—„์ฒญ ํฐ ๋„์›€์ด ๋œ๋‹ค๋Š” ๊ฑธ ๋ชธ์œผ๋กœ ๋А๊ผˆ๋‹ค.


๐ŸŽฏ "SWR์€ ๊ฐ€๋ฒผ์šด ์˜คํ† ๋ฐ”์ด, React Query๋Š” ํ’€ ์˜ต์…˜ SUV๋‹ค. ํ”„๋กœ์ ํŠธ ํฌ๊ธฐ์— ๋งž์ถฐ ๊ณ ๋ฅด์ž!"

profile
๊ฐœ๋ฐœ์ž๋กœ ์ทจ์—…์„ ์ค€๋น„ ์ค‘ ์ด๋ฉฐ, ์—ด์‹ฌํžˆ ๊ณต๋ถ€ ์ค‘ ์ž…๋‹ˆ๋‹ค!

0๊ฐœ์˜ ๋Œ“๊ธ€