๐Ÿ˜ฌ React-Query ๊ผญ๊ผญ ์”น์–ด๋จน๊ธฐ

HoJinยท2023๋…„ 12์›” 11์ผ

โš›๏ธ React

๋ชฉ๋ก ๋ณด๊ธฐ
2/2
post-thumbnail
์„œ๋ฒ„์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋“ฑ์žฅ

๐Ÿ“Œ ๋“ฑ์žฅ๋ฐฐ๊ฒฝ

UI/UX์˜ ์ค‘์š”์„ฑ๊ณผ ํ•จ๊ป˜ ํ”„๋กœ๋•ํŠธ ๊ทœ๋ชจ๊ฐ€ ์ปค์ง€๋ฉด์„œ ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ๊ด€๋ฆฌํ•ด์•ผํ•˜๋Š” ์ƒํƒœ๊ฐ€ ๋งŽ์•„์กŒ๋‹ค.

๊ธฐ์กด์—๋Š” ์„œ๋ฒ„์ƒํƒœ์™€ ํด๋ผ์ด์–ธํŠธ์ƒํƒœ๋ฅผ ๊ตฌ๋ถ„ํ•˜์ง€์•Š๊ณ  ์ „์—ญ์ƒํƒœ๊ด€๋ฆฌ ๋„๊ตฌ๋ฅผ ํ†ตํ•ด ๊ด€๋ฆฌํ–ˆ๋‹ค.
์ด๋Ÿฌ๋‹ค๋ณด๋‹ˆ ์ „์—ญ์ƒํƒœ๊ฐ€ ์ €์žฅ๋˜์–ด์•ผํ•  store์— ์ƒํƒœ๊ด€๋ฆฌ๋ณด๋‹จ APIํ†ต์‹ ์„ ์œ„ํ•œ ์ฝ”๋“œ๋กœ ๋„๋ฐฐ๋˜์—ˆ๋‹ค.
์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ์œ„ํ•ด ์„œ๋ฒ„์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋“ฑ์žฅํ–ˆ๋‹ค.

๋Œ€๋ถ€๋ถ„์˜ ๊ธฐ์กด ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ ์ž‘์—…์—๋Š” ์ ํ•ฉํ•˜์ง€๋งŒ ๋น„๋™๊ธฐ ๋˜๋Š” ์„œ๋ฒ„ ์ƒํƒœ ์ž‘์—…์—๋Š” ์ ํ•ฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

TanStack-Query Docs Overview
  • ์ „์—ญ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(Redux, MobX...) + ๋ฏธ๋“ค์›จ์–ด(React-Saga, React-Thunk...)๋ฅผ ์ด์šฉํ•ด ์„œ๋ฒ„์ƒํƒœ์™€ ํด๋ผ์ด์–ธํŠธ์ƒํƒœ๋ฅผ ํ•œ๋ฒˆ์— ํ•ธ๋“ค๋ง โžก๏ธ ๋ณต์žก๋„ ์ƒ์Šน(๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ๋„ ์ƒ๋‹นํ•จ), ์ฝ”๋“œ๋Ÿ‰ ์ฆ๊ฐ€

  • ์„œ๋ฒ„์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(React-query, SWR...) ๋“ฑ์žฅ์œผ๋กœ ์„œ๋ฒ„์ƒํƒœ์™€ ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ด€๋ฆฌ โžก๏ธ ๋ณต์žก๋„ ๊ฐ์†Œ, ์ฝ”๋“œ๋Ÿ‰ ๊ฐ์†Œ

๋ฆฌ๋•์Šค๋Š” ์ˆœ์ˆ˜ํ•จ์ˆ˜๋ฅผ ์ง€ํ–ฅํ•ด์„œ ๋น„๋™๊ธฐ์ฒ˜๋ฆฌ์—๋Š” ๋ถ€์ ํ•ฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฏธ๋“ค์›จ์–ด๋กœ ์ด๋ฅผ ํ•ด๊ฒฐํ•œ๋‹ค.
์ด๋ฅผ ํ†ตํ•ด ๋น„๋™๊ธฐ์ ์ธ ์„œ๋ฒ„์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์„œ๋ฒ„์ƒํƒœ๋งŒ์„ ์œ„ํ•œ ๋กœ์ง์ด ๋น„๋Œ€ํ•ด์ง„๋‹ค.
๋ฆฌ๋•์Šค ์ž์ฒด๊ฐ€ ์ƒ๋‹นํ•œ ์–‘์˜ ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ๋ฅผ ์š”๊ตฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ๋Ÿ‰์ด ์ฆ๊ฐ€ํ•œ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ์ด๋ฅผ ๊ฐ๊ฐ์˜ API ๋งˆ๋‹ค ๋ฐ˜๋ณตํ•ด์•ผํ•œ๋‹ค.
๐Ÿ“š ๊ทธ๋ฆผ์ถœ์ฒ˜

โœ… Server state ๐Ÿ†š Client state

๊ทธ๋ ‡๋‹ค๋ฉด ์„œ๋ฒ„ ์ƒํƒœ์™€ ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ๊ฐ€ ๋ญ˜๊นŒ? ๋ญ ๋•Œ๋ฌธ์— ์ด ๋‘˜์„ ์ด์›ํ™”ํ•ด์„œ ๊ด€๋ฆฌํ•ด์•ผ๋ ๊นŒ?

  • ์„œ๋ฒ„์ƒํƒœ โžก๏ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋˜์–ด ์—ฌ๋Ÿฌ ์‚ฌ์šฉ์ž ๊ฐ„์— ๊ณต์œ ๋˜๋Š” ๋™์  ๋ฐ์ดํ„ฐ
  • ํด๋ผ์ด์–ธํŠธ์ƒํƒœ โžก๏ธ ๊ฐœ๋ณ„ ์‚ฌ์šฉ์ž์˜ ์ธํ„ฐํŽ˜์ด์Šค์™€ ์ƒํ˜ธ์ž‘์šฉ์— ๊ด€๋ จ๋œ ๋ฐ์ดํ„ฐ

์„œ๋ฒ„์ƒํƒœ๋Š” ํด๋ผ์ด์–ธํŠธ์ƒํƒœ์™€ ๋‹ค๋ฅด๊ฒŒ ๋™์ ์ด๋ฏ€๋กœ ์‹œ๊ฐ„์— ๋”ฐ๋ฅธ ๋ฐ์ดํ„ฐ์˜ ๋ณ€ํ™”๋ฅผ ํŠธ๋ž˜ํ‚น ํ•ด์•ผํ•˜๊ณ  ์„ฑ๋Šฅ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ด ์š”์ฒญ์„ ์ตœ๋Œ€ํ•œ ์ค„์—ฌ์•ผํ•œ๋‹ค.
์ฆ‰, ์„ฑ๊ฒฉ์ด ๋‹ค๋ฅธ ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ ๊ด€๋ฆฌ์™€ ์„œ๋ฒ„ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ๋ถ„๋ฆฌํ•จ์œผ๋กœ์จ ์บ์‹ฑ๋ฅผ ํ†ตํ•œ ์„ฑ๋Šฅํ–ฅ์ƒ, ๊ด€๋ฆฌ ๋ฐ ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์šฉ์ดํ•˜๊ฒŒํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ตฌ๋ถ„์„œ๋ฒ„ ์ƒํƒœํด๋ผ์ด์–ธํŠธ ์ƒํƒœ
์ €์žฅ ์œ„์น˜์›น์„œ๋ฒ„๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์‚ฌ์šฉ์ž์˜ ๋ธŒ๋ผ์šฐ์ €๋‚˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜
ํŠน์ง•์ค‘์•™์ง‘์ค‘์‹, ๋™์ , ์—ฌ๋Ÿฌ ์‚ฌ์šฉ์ž ๊ฐ„ ๊ณต์œ ๋กœ์ปฌ, ์ž„์‹œ, ์‚ฌ์šฉ์ž๋ณ„ ๊ฐœ๋ณ„์ 
์กฐ์ž‘ ์ œ์•ฝAPI ํ˜ธ์ถœ์„ ํ†ตํ•œ ์กฐํšŒ ๋ฐ ์ˆ˜์ •(์กฐ์ž‘์— ์ œ์•ฝ ์žˆ์Œ)์ œ์•ฝ ์—†์ด ์ž์œ ๋กญ๊ฒŒ ์กฐ์ž‘ ๊ฐ€๋Šฅ
๋ฐ์ดํ„ฐ ํŠน์„ฑํด๋ผ์ด์–ธํŠธ ์š”์ฒญ์— ๋”ฐ๋ผ ๋ณ€๊ฒฝ, ๊ณต์œ  ๋ฐ์ดํ„ฐUI ์ƒํƒœ ๊ด€๋ฆฌ์— ์‚ฌ์šฉ, ์‚ฌ์šฉ์ž ๊ฒฝํ—˜๊ณผ ์ง๊ฒฐ
๊ด€๋ฆฌ ๋ฐฉ๋ฒ•๋ฐ์ดํ„ฐ ์บ์‹ฑ์œผ๋กœ ๋„คํŠธ์›Œํฌ ์š”์ฒญ ์ตœ์†Œํ™”, ์„œ๋ฒ„ ๋ณ€๊ฒฝ ์‹œ ์—…๋ฐ์ดํŠธuseState, Redux, MobX, Context API ๋“ฑ์œผ๋กœ ๊ด€๋ฆฌ
์ƒํƒœ ์œ ์ง€์—…๋ฐ์ดํŠธ๊ฐ€ ์—†์œผ๋ฉด out-of-date ๊ฐ€๋Šฅ์„ฑ ์žˆ์Œํ•ญ์ƒ ์ตœ์‹  ์ƒํƒœ ์œ ์ง€

โœ… RFC 5861

  • RFC 5861 ๋ฌธ์„œ๋ฅผ ํ†ตํ•ด stale-while-revalidate๋ผ๋Š” ์ƒˆ๋กœ์šด HTTP ์บ์‹ฑ์ „๋žต์ด ๋Œ€๋‘๋˜์—ˆ๊ณ  ์ด ์ปจ์…‰์„ ๋ฐ”ํƒ•์œผ๋กœ TanStack-Query(๊ตฌ React-Query), SWR ๋“ฑ์ด ํƒ„์ƒ ํ–ˆ๋‹ค.
  • ์ด ์ „๋žต์€ ์šฐ์„ ์ ์œผ๋กœ ์บ์‹œ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ๊ฒ€์ฆ์„ ์œ„ํ•œ ์š”์ฒญ์„ ๋ณด๋‚ธ ํ›„ ์ตœ์‹ ๋ฐ์ดํ„ฐ๋กœ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ณผ์ •์„ ๊ฑฐ์นœ๋‹ค.
  • ์ด๋ฅผ ํ†ตํ•ด ์ง€์†์ ์ด๊ณ  ์ž๋™์ ์œผ๋กœ ์„œ๋ฒ„์ƒํƒœ๊ฐ€ ์—…๋ฐ์ดํŠธ ๋จ์œผ๋กœ์จ ์ตœ์‹ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.

    ์ตœ์ดˆ ์š”์ฒญ ์‹œ ์บ์‹œ๊ฐ€ ์—†์„๋•Œ : ์„œ๋ฒ„์—์„œ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ด
    ์บ์‹œ๊ฐ€ ์ตœ์‹  ์ƒํƒœ : ์ตœ์‹  ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฆ‰์‹œ ๋ฐ›์Œ.
    ์บ์‹œ๊ฐ€ ๋งŒ๋ฃŒ๋œ ์ƒํƒœ : ์ฒ˜์Œ์—๋Š” ๋งŒ๋ฃŒ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๊ณ , ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธ

๐Ÿšจ RFC(Request for Comments) ๋ฌธ์„œ๋Š” "์˜๊ฒฌ์„ ์š”์ฒญํ•˜๋Š” ๋ฌธ์„œ"๋ผ๋Š” ์˜๋ฏธ๋กœ, ๊ตญ์ œ ์ธํ„ฐ๋„ท ํ‘œ์ค€ํ™” ๊ธฐ๊ตฌ(IETF)์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ธฐ์ˆ  ํ‘œ์ค€ โžก๏ธ ๋ชจ๋“  ์ธํ„ฐ๋„ท ํ‘œ์ค€์€ RFC๋กœ ๋ฌธ์„œํ™”๋จ

๐Ÿ“Œ ์ž‘๋™์›๋ฆฌ

query๋ž€ ๋ฌธ์˜ํ•˜๋‹ค, ์งˆ๋ฌธํ•˜๋‹ค๋ผ๋Š” ๋œป์œผ๋กœ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ ์œผ๋กœ๋Š” DB์— ์ •๋ณด๋ฅผ ์š”์ฒญํ•˜๋‹ค๋ผ๋Š” ๋œป์œผ๋กœ ํ•ด์„ํ•  ์ˆ˜ ์žˆ๋‹ค.
๊ทธ๋Ÿฌ๋ฏ€๋กœ TanStack-Query(๊ตฌ React-Query)๋ผ๋Š” ์ด๋ฆ„์€ DB์— ์ •๋ณด๋ฅผ ์š”์ฒญํ•˜๋Š”๊ธฐ๋Šฅ์„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌํ™” ํ•ด๋‘”๊ฒƒ์ด๋ผ๊ณ  ์•”์‹œ์ ์œผ๋กœ ๋‚˜ํƒ€๋‚ธ๋‹ค.
์ž‘๋™์›๋ฆฌ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด RFC 5861์˜ ์ปจ์…‰์„ ๋”ฐ๋ฅธ๋‹ค.

  • Stale-While-Recalidate:
    ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ stale response๋ฅผ revalidateํ•˜๋Š” ๋™์•ˆ ์บ์‹œ๊ฐ€ ๊ฐ€์ง„ stale response๋ฅผ ๋ฐ˜ํ™˜
    StaleTime์ด ์ง€๋‚˜๊ธฐ์ „๊นŒ์ง„ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š”๋‹ค(freshํ•œ ์ƒํƒœ).
    ์ง€์†์ ์œผ๋กœ UI๋ฅผ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด ์ตœ์‹  sever data๋ฅผ ๋ฐ›์•„์˜ฌ๋•Œ๊นŒ์ง€ Stale data๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

  • StaleTime : ์–ผ๋งˆ์˜ ์‹œ๊ฐ„์ด ํ๋ฅธ ํ›„์— ๋ฐ์ดํ„ฐ๋ฅผ stale(๋‚ก์€) ์ทจ๊ธ‰ํ• ๊ฒƒ์ธ์ง€

  • CacheTime : ๋ฉ”๋ชจ๋ฆฌ์— ์–ผ๋งˆ๋งŒํผ ์žˆ์„๊ฑด์ง€

    Query ์ƒํƒœํ๋ฆ„ (ํ™”๋ฉด์— ์žˆ๋‹ค๊ฐ€ ์‚ฌ๋ผ์ง€๋Š” query)

    Query ์ƒํƒœํ๋ฆ„ (ํ™”๋ฉด์— ์žˆ๋‹ค๊ฐ€ ์—†๋‹ค๊ฐ€ ์ข€ ๋” ๋ณต์žกํ•œ query)

๋‚ด๋ถ€์ ์œผ๋กœ๋Š” Context API๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
์ด๋ฅผ ํ†ตํ•ด ์ปดํฌ๋„ŒํŠธ๊ฐ„์˜ ์ฟผ๋ฆฌ์ƒํƒœ ๋ฐ ์บ์‰ฌ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋‹ค.


๐Ÿ“Œ ์‚ฌ์šฉ๋ฒ•

๐ŸŒŸ ๊ตฌ์กฐ๋ถ„ํ•ดํ• ๋‹น ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด ์„ ์–ธํ•œ ๋ณ€์ˆ˜๋ฅผ ๋ชจ๋‘์‚ฌ์šฉํ•ด์•ผ๋œ๋‹ค.

โœ… useQuery

GET ์š”์ฒญ์— ์‚ฌ์šฉ

  • Query key: ๋ฐฐ์—ด, ์ฟผ๋ฆฌ ํ‚ค์— ๋”ฐ๋ผ ์ฟผ๋ฆฌ ์บ์‹ฑ์„ ๊ด€๋ฆฌ(์ฟผ๋ฆฌํ‚ค๊ฐ€ ๊ฐ™์œผ๋ฉด ๋‹ค๋ฅธํ•จ์ˆ˜๋ผ๋„ ํ˜ธ์ถœ โŒ, ์ฟผ๋ฆฌํ‚ค๊ฐ€ ๋‹ค๋ฅด๋ฉด ๊ฐ™์€ํ•จ์ˆ˜๋ผ๋„ ํ˜ธ์ถœ โญ•๏ธ)
  • Query Fn : Promise๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜(APIํŒจ์นญ ํ•จ์ˆ˜)
  • ์˜ต์…˜ : useQuery ๊ธฐ๋Šฅ์„ ์ œ์–ด
function Example() {
  const { isLoading, error, data } = useQuery({
    queryKey: ['repoData'],
    queryFn: () =>
      fetch('https://api.github.com/repos/TanStack/query').then(
        (res) => res.json(),
      ),
  })

  if (isLoading) return 'Loading...'

  if (error) return 'An error has occurred: ' + error.message

์ž์„ธํ•œ ๋‚ด์šฉ์€ useQuery์„ ์ฐธ๊ณ 

โœ… useQueries

๋ฐ์ดํ„ฐ ํŒจ์นญ์ด ํ•œ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ์—ฌ๋Ÿฌ๊ฐœ ์‹คํ–‰๋˜์–ด์•ผํ•œ๋‹ค๋ฉด useQuery๋ฅผ ๋ณ‘๋ ฌ๋กœ ์„ ์–ธํ•˜๋ฉด ๋œ๋‹ค.
๋ณต์ˆ˜ ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ๋กœ๋”ฉ, ์„ฑ๊ณต, ์‹คํŒจ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋‚˜๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด useQueries๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

const results = useQueries({
  queries: [
    { queryKey: ['post', 1], queryFn: fetchPost, staleTime: Infinity},
    { queryKey: ['post', 2], queryFn: fetchPost, staleTime: Infinity}
  ]
})

โœ… useMutation

POST, PUT, DELETE ์š”์ฒญ์— ์‚ฌ์šฉ

  • useQuery์™€ ๋น„์Šทํ•œ ๋ฌธ๋ฒ•์ด๋ฉฐ ์„ ์–ธ ํ›„ mutation(๋ณ€์ˆ˜๋ช…).mutate()๋กœ ์‚ฌ์šฉํ•œ๋‹ค.
function App() {
  const mutation = useMutation({
    mutationFn: (newTodo) => {
      return axios.post('/todos', newTodo)
    },
  })

  return (
    <div>
      {mutation.isLoading ? (
        'Adding todo...'
      ) : (
        <>
          {mutation.isError ? (
            <div>An error occurred: {mutation.error.message}</div>
          ) : null}

          {mutation.isSuccess ? <div>Todo added!</div> : null}

          <button
            onClick={() => {
              mutation.mutate({ id: new Date(), title: 'Do Laundry' })
            }}
          >
            Create Todo
          </button>
        </>
      )}
    </div>
  )
}
  • onSuccess()์— invalidateQueries() ์ด์šฉํ•˜์—ฌ ์บ์‹œ๋ฅผ ๋ฌดํšจํ™”ํ•˜๊ณ  ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋ฉฐ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์ฆ‰์‹œ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ๋‹ค.
onSuccess: () => {
      queryClient.invalidateQueries({
        queryKey: ['ํƒ€๊ฒŸ ์ฟผ๋ฆฌํ‚ค'],
      });
    },

์ž์„ธํ•œ ์‚ฌํ•ญ์€ useMutation์„ ์ฐธ๊ณ 

์ด์™ธ์—๋„ infinite Queries, suspense, SSR, Optimistic Updates๋ฅผ ์ œ๊ณตํ•œ๋‹ค.


๐Ÿ“š ์ฐธ๊ณ ๋ฌธํ—Œ

https://tanstack.com/query/v4/docs/react/overview
https://velog.io/@jhjung3/1-React-Query-Overview
https://velog.io/@hyunjine/Server-State-Client-State https://velog.io/@jkl1545/React-Query
https://velog.io/@jkl1545/React-Query
https://velog.io/@jay/5-minute-react-query-essential
https://velog.io/@jay/10-minute-react-query-concept
https://techblog.woowahan.com/6339/
https://www.youtube.com/watch?v=MArE6Hy371c
https://medium.com/hcleedev/web-react-query-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-%EB%93%B1%EC%9E%A5-%EB%B0%B0%EA%B2%BD-%EC%9E%A5%EC%A0%90-%EA%B8%B0%EC%B4%88-%EA%B0%9C%EB%85%90-e82a3733036c
https://leego.tistory.com/entry/react-query%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%9E%91%EB%8F%99%ED%95%A0%EA%B9%8C
https://tkdodo.eu/blog/inside-react-query

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