Fetch & Tanstack Query v5 ๐Ÿ”ฅ

์›๋ฏผ๊ด€ยท2025๋…„ 5์›” 21์ผ

[TIL]

๋ชฉ๋ก ๋ณด๊ธฐ
182/201
post-thumbnail

Fetch ๋น„๋™๊ธฐ ํ†ต์‹ ๊ณผ Tanstack Query v5 ์™„์ „ ์ •๋ณต โœ๏ธ

0. Overview ๐ŸŒ€

ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์„ ํ•˜๋‹ค ๋ณด๋ฉด, ์„œ๋ฒ„์™€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š” ๋น„๋™๊ธฐ ํ†ต์‹ ์€ ํ”ผํ•  ์ˆ˜ ์—†๋Š” ๊ณผ์ œ์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๊ณ , ํผ์„ ์ œ์ถœํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„์— ์ €์žฅํ•˜๋ฉฐ, ํŽ˜์ด์ง€๋ฅผ ์ด๋™ํ•˜๋ฉด ๋‹ค์‹œ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•˜๋Š” ์ผ์ด ๋ฐ˜๋ณต๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด๋•Œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” fetch() API๋ฅผ ํ†ตํ•ด ๋น„๋™๊ธฐ HTTP ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ , ์ด๋ฅผ ํ†ตํ•ด ํ”„๋ก ํŠธ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ๊ฐ€ ์†Œํ†ตํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.


ํ•˜์ง€๋งŒ ์‹ค์ œ ์„œ๋น„์Šค์—์„œ๋Š” ๋‹จ์ˆœํžˆ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ ์ด์ƒ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ด๋Ÿฐ ์ƒํ™ฉ๋“ค์„ ์ƒ๊ฐํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

์œ„ ๋ฌธ์ œ๋“ค์„ Fetch๋งŒ์œผ๋กœ๋Š” ํ•ด๊ฒฐํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— TanStack Query(๊ตฌ React Query)๊ฐ€ ๋“ฑ์žฅํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

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

1. Fetch ๋น„๋™๊ธฐ ํ†ต์‹  ๐ŸŒ€

1-1. Promise์™€ async / await ๐ŸŽฏ

1-1-1. Promise ๐ŸŸฃ

Promise๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ์— ์‚ฌ์šฉ๋˜๋Š” ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค. ๊ฐ์ฒด๋Š” ๋ฐ์ดํ„ฐ ๋ฌถ์Œ์ด์ฃ . ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ์— ์‚ฌ์šฉ๋˜๋Š” ๋ฐ์ดํ„ฐ ๋ฌถ์Œ์ด Promise์ž…๋‹ˆ๋‹ค.

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


Promise๋Š” ์ฃผ๋กœ ์„œ๋ฒ„์—์„œ ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ํ™”๋ฉด์— ํ‘œ์‹œํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

$.get('url ์ฃผ์†Œ/products/1', function(response) {
  // ...
});

์œ„ API๊ฐ€ ์‹คํ–‰๋˜๋ฉด, ์„œ๋ฒ„๋กœ "๋ฐ์ดํ„ฐ๋ฅผ ํ•˜๋‚˜ ๋ณด๋‚ด์ฃผ์„ธ์š”."๋ผ๋Š” ์š”์ฒญ์„ ๋ณด๋‚ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๊ธฐ๋„ ์ „์—, ๋งˆ์น˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค ๋ฐ›์•„์˜จ ๊ฒƒ์ฒ˜๋Ÿผ ํ™”๋ฉด์— ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์‹œํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ฑฐ๋‚˜ ๋นˆ ํ™”๋ฉด์ด ๋‚˜์˜ค๊ฒ ์ฃ . ์ด์™€ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜๋กœ Promise๋ฅผ ํ™œ์šฉํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.


Promise๋Š” ์„ธ ๊ฐ€์ง€ ์ƒํƒœ(states)๋ฅผ ๊ฐ–์Šต๋‹ˆ๋‹ค.

  1. Pending(๋Œ€๊ธฐ): ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ๋กœ์ง์ด ์•„์ง ์™„๋ฃŒ๋˜์ง€ ์•Š์€ ์ƒํƒœ
  2. Fulfilled(์ดํ–‰): ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋˜์–ด Promise๊ฐ€ ๊ฒฐ๊ณผ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ ์ƒํƒœ
  3. Rejected(์‹คํŒจ): ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๊ฐ€ ์‹คํŒจํ•˜๊ฑฐ๋‚˜ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ์ƒํƒœ

Pending(๋Œ€๊ธฐ) ๐Ÿท๏ธ

new Promise() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ˆœ๊ฐ„ Pending ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

new Promise();

new Promise() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์„ ์–ธํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ์š”, ์ฝœ๋ฐฑ ํ•จ์ˆ˜์˜ ์ธ์ž๋Š” resolve์™€ reject์ž…๋‹ˆ๋‹ค.

new Promise(function(resolve, reject) {
  // ...
});

Fulfilled(์ดํ–‰) ๐Ÿท๏ธ

์ฝœ๋ฐฑ ํ•จ์ˆ˜์˜ ์ธ์ž resolve๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ์‹คํ–‰ํ•˜๋ฉด Fulfilled ์ƒํƒœ๊ฐ€ ๋˜์ฃ .

new Promise(function(resolve, reject) {
  resolve();
});

Fulfilled ์ƒํƒœ๊ฐ€ ๋˜๋ฉด Promise๋Š” ๊ฒฐ๊ณผ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ๋ฐ˜ํ™˜๊ฐ’์— ๋Œ€ํ•ด์„œ then() ๋ฉ”์„œ๋“œ๋ฅผ ์ ์šฉํ•˜์—ฌ ๋ฐ˜ํ™˜๊ฐ’์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

function getData() {
  return new Promise(function(resolve, reject) {
    var data = 100;
    resolve(data);
  });
}

// resolve()์˜ ๊ฒฐ๊ณผ ๊ฐ’ data๋ฅผ resolvedData๋กœ ๋ฐ›์Œ
getData().then(function(resolvedData) {
  console.log(resolvedData); // 100
});

Rejected(์‹คํŒจ) ๐Ÿท๏ธ

new Promise()๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์„ ์–ธํ•œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜์˜ ์ธ์ž ์ค‘ ํ•˜๋‚˜์ธ reject๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด Rejected ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

new Promise(function(resolve, reject) {
  reject();
});

Rejected ์ƒํƒœ๊ฐ€ ๋˜๋ฉด, ์‹คํŒจ์˜ ์ด์œ ๋ฅผ catch() ๋ฉ”์„œ๋“œ๋กœ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

function getData() {
  return new Promise(function(resolve, reject) {
    reject(new Error("Request is failed"));
  });
}

// reject()์˜ ๊ฒฐ๊ณผ ๊ฐ’ Error๋ฅผ err์— ๋ฐ›์Œ
getData().then().catch(function(err) {
  console.log(err); // Error: Request is failed
});

1-1-2. async / await ๐ŸŸฃ

async / await๋Š” ์œ„์—์„œ ์„ค๋ช…ํ•œ Promise๋ฅผ ๋” ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ฌธ๋ฒ•์ž…๋‹ˆ๋‹ค.

async ๐Ÿท๏ธ

async ํ‚ค์›Œ๋“œ๋Š” function ์•ž์— ์œ„์น˜ํ•ฉ๋‹ˆ๋‹ค.

async function f() {
  return 1;
}

function ์•ž์— async๋ฅผ ๋ถ™์ด๋ฉด, ํ•ด๋‹น ํ•จ์ˆ˜๋Š” ํ•ญ์ƒ Promise๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. Promise๊ฐ€ ์•„๋‹Œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋”๋ผ๋„, Fulfilled ์ƒํƒœ์˜ Promise๋กœ ๊ฐ์‹ธ '์ดํ–‰๋œ Promise'๊ฐ€ ๋ฐ˜ํ™˜๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

async function f() {
  return 1;
}

f().then(alert); // 1

๋ช…์‹œ์ ์œผ๋กœ Promise๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•œ๋ฐ, ๊ฒฐ๊ณผ๋Š” ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

async function f() {
  return Promise.resolve(1);
}

f().then(alert); // 1

await ๐Ÿท๏ธ

await๋Š” async ํ•จ์ˆ˜ ์•ˆ์—์„œ๋งŒ ๋™์ž‘ํ•˜๋Š” ํ‚ค์›Œ๋“œ์ž…๋‹ˆ๋‹ค.

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” await ํ‚ค์›Œ๋“œ๋ฅผ ๋งŒ๋‚˜๋ฉด, Promise๊ฐ€ ์ฒ˜๋ฆฌ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ๋Š” ๊ทธ ์ดํ›„์— ๋ฐ˜ํ™˜๋˜๋Š”๋ฐ์š”. 1์ดˆ ํ›„ ์ดํ–‰๋˜๋Š” Promise ์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด awiat์˜ ๋™์ž‘์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

async function f() {

  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("์™„๋ฃŒ!"), 1000)
  });

  let result = await promise; // ํ”„๋ผ๋ฏธ์Šค๊ฐ€ ์ดํ–‰๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆผ (*)

  alert(result); // "์™„๋ฃŒ!"
}

f();

ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ํ•จ์ˆ˜ ๋ณธ๋ฌธ์ด ์‹คํ–‰๋˜๋Š” ๋„์ค‘์— (*) ๋ถ€๋ถ„์—์„œ ์‹คํ–‰์ด ์ž ์‹œ '์ค‘๋‹จ'๋˜์—ˆ๋‹ค๊ฐ€ Promise๊ฐ€ ์ฒ˜๋ฆฌ๋˜๋ฉด ์‹คํ–‰์ด ์žฌ๊ฐœ๋ฉ๋‹ˆ๋‹ค. await๋Š” ๋ง ๊ทธ๋Œ€๋กœ Promise๊ฐ€ ์ฒ˜๋ฆฌ๋  ๋•Œ๊นŒ์ง€ ํ•จ์ˆ˜ ์‹คํ–‰์„ ๊ธฐ๋‹ค๋ฆฌ๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. Promise๊ฐ€ ์ฒ˜๋ฆฌ๋˜๊ธธ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ์—, ์—”์ง„์ด ๋‹ค๋ฅธ ์ผ์„ ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— CPU ๋ฆฌ์†Œ์Šค๊ฐ€ ๋‚ญ๋น„๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Promise.then()๋ณด๋‹ค ๊ฐ€๋…์„ฑ์ด ์ข‹๊ณ  ์“ฐ๊ธฐ๋„ ์‰ฝ๋‹ค๋Š” ์žฅ์  ๋˜ํ•œ ์žˆ์Šต๋‹ˆ๋‹ค.

โš ๏ธ Promise์™€ async / await ์š”์•ฝ
Promise๋Š” ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ์— ์‚ฌ์šฉ๋˜๋Š” ๊ฐ์ฒด๋กœ, ์ž‘์—…์˜ ์ƒํƒœ(๋Œ€๊ธฐ, ์ดํ–‰, ์‹คํŒจ)๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก async/await ๋ฌธ๋ฒ•์ด ๋„์ž…๋˜์—ˆ์œผ๋ฉฐ, ์ด๋Š” ๋น„๋™๊ธฐ ์ฝ”๋“œ๋ฅผ ๋งˆ์น˜ ๋™๊ธฐ ์ฝ”๋“œ์ฒ˜๋Ÿผ ์ฝ๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

1-2. Fetch API ์‚ฌ์šฉํ•˜๊ธฐ ๐ŸŽฏ

1-2-1. Fetch์˜ ์ •์˜์™€ ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ• ๐ŸŸฃ

Fetch API๋Š” HTTP Request๋ฅผ ๋ณด๋‚ด๊ณ  ๊ทธ์— ๋Œ€ํ•œ Response๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ JavaScript ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

fetch() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ Request๋ฅผ ๋ณด๋ƒ…๋‹ˆ๋‹ค. ์š”์ฒญํ•  URL์„ ๋‹ด์€ ๋ฌธ์ž์—ด์ด๋‚˜ Request ๊ฐ์ฒด๋ฅผ ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋กœ ๋„˜๊ธฐ๊ณ , ์„ ํƒ์ ์œผ๋กœ Request๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ์˜ต์…˜ ๊ฐ์ฒด๋ฅผ ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ ๋„˜๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

fetch() ํ•จ์ˆ˜๋Š” Promise๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„ Response ๊ฐ์ฒด์— ๋Œ€ํ•ด ์ ์ ˆํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ Request์˜ ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๊ณ , Response ๋ณธ๋ฌธ์„ ํ…์ŠคํŠธ ๋˜๋Š” JSON๊ณผ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ํ˜•์‹์œผ๋กœ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


async function getData() {
  const url = "https://example.org/products.json";
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`Response status: ${response.status}`);
    }

    const json = await response.json();
    console.log(json);
  } catch (error) {
    console.error(error.message);
  }
}

1-2-2. ์‘๋‹ต ์ฒ˜๋ฆฌ: json() ๋ฉ”์„œ๋“œ ๐ŸŸฃ

json() ๋ฉ”์„œ๋“œ๋Š” ์‘๋‹ต(Response) ๋ฐ์ดํ„ฐ๋ฅผ JSON ๋ฌธ์ž์—ด์—์„œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋กœ ํŒŒ์‹ฑ(parsing) ํ•ด์ฃผ๋Š” ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค. ์ฆ‰, ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ฐ›์€ ๋ฌธ์ž์—ด ํ˜•ํƒœ์˜ JSON ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•ด ์ฃผ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

Fetch API๋กœ ์„œ๋ฒ„์— ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด, ์„œ๋ฒ„๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฌธ์ž์—ด ํ˜•ํƒœ์˜ JSON์„ ์‘๋‹ต ๋ณธ๋ฌธ์— ๋‹ด์•„ ๋ณด๋ƒ…๋‹ˆ๋‹ค. ์ด๋Š” HTTP ํ†ต์‹ ์ด ๋ณธ์งˆ์ ์œผ๋กœ ํ…์ŠคํŠธ(ํ˜น์€ ๋ฐ”์ด๋„ˆ๋ฆฌ) ๊ธฐ๋ฐ˜์˜ ๋ฐ”์ดํŠธ ์ŠคํŠธ๋ฆผ๋งŒ ์ „๋‹ฌํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ด ๋ฌธ์ž์—ด์„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ์—์„œ ์ง์ ‘ ๋‹ค๋ฃจ๊ธฐ๋Š” ๋ถˆํŽธํ•˜๋ฏ€๋กœ, ์ด๋ฅผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•ด์•ผ ํ•˜๋ฉฐ, ์ด๋•Œ Response ๊ฐ์ฒด์˜ json() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

fetch('https://jsonplaceholder.typicode.com/users/1')
  .then(response => response.json()) // ์‘๋‹ต์„ JSON ๋ฌธ์ž์—ด โ†’ JS ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜
  .then(data => {
    console.log(data); // ๋ณ€ํ™˜๋œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด ์ถœ๋ ฅ
    console.log(data.name); // ๊ฐ์ฒด์˜ ํŠน์ • ์†์„ฑ ์ ‘๊ทผ
  });

์œ„ ์ฝ”๋“œ์— ๋Œ€ํ•œ ์‹คํ–‰ ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

{
  id: 1,
  name: "Leanne Graham",
  username: "Bret",
  email: "Sincere@april.biz",
  ...
}
Leanne Graham

1-2-3. ์š”์ฒญ ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜: JSON.stringify(data) ๐ŸŸฃ

JSON.stringify()๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋ฅผ JSON ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ์ฆ‰, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋ฅผ ์„œ๋ฒ„์— ์ „์†กํ•˜๊ธฐ ์ ํ•ฉํ•œ ๋ฌธ์ž์—ด ํ˜•ํƒœ๋กœ ๋ฐ”๊ฟ”์ฃผ๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

fetch API๋ฅผ ์‚ฌ์šฉํ•ด ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ผ ๋•Œ, HTTP ํ†ต์‹ ์€ ํ…์ŠคํŠธ(๋˜๋Š” ๋ฐ”์ด๋„ˆ๋ฆฌ) ๊ธฐ๋ฐ˜์˜ ๋ฐ”์ดํŠธ ์ŠคํŠธ๋ฆผ๋งŒ ์ „์†กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณด๋‚ผ ์ˆ˜๋Š” ์—†๊ณ , ์ด๋ฅผ JSON.stringify()๋ฅผ ์‚ฌ์šฉํ•ด JSON ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•œ ํ›„ ์ „์†กํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

const user = {
  name: 'Alice',
  age: 25
};

const jsonString = JSON.stringify(user); // ๊ฐ์ฒด โ†’ JSON ๋ฌธ์ž์—ด ๋ณ€ํ™˜
console.log(jsonString); 

// ์„œ๋ฒ„๋กœ ์ „์†ก (์˜ˆ์‹œ์šฉ์œผ๋กœ ์‹ค์ œ ์ „์†ก๋˜์ง„ ์•Š์Œ)
fetch('https://example.com/api/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json' // JSON ํ˜•์‹์ž„์„ ์„œ๋ฒ„์— ์•Œ๋ฆผ
  },
  body: jsonString // ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†ก
});

jsonString์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

{"name":"Alice","age":25}

โš ๏ธ Fetch API ์‚ฌ์šฉํ•˜๊ธฐ ์š”์•ฝ
Fetch API๋Š” ์„œ๋ฒ„์™€์˜ ํ†ต์‹ ์„ ์œ„ํ•œ ๋‚ด์žฅ ํ•จ์ˆ˜๋กœ, ์‘๋‹ต์„ ์ฒ˜๋ฆฌํ•  ๋•Œ๋Š” json() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด JSON ๋ฌธ์ž์—ด์„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋Œ€๋กœ ์„œ๋ฒ„๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ผ ๋•Œ๋Š” JSON.stringify()๋ฅผ ์‚ฌ์šฉํ•ด ๊ฐ์ฒด๋ฅผ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

1-3. ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ: tryโ€ฆcatch ๐ŸŽฏ

try...catch๋Š” ์ฝ”๋“œ ์‹คํ–‰ ์ค‘ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ํ”„๋กœ๊ทธ๋žจ์ด ๋ฉˆ์ถ”์ง€ ์•Š๋„๋ก ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฌธ๋ฒ•์ž…๋‹ˆ๋‹ค. ์ฆ‰, ์—๋Ÿฌ๋ฅผ ์žก์•„์„œ ์›ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

try {
  // ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ
} catch (error) {
  // ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ
} finally {
  // ์˜ˆ์™ธ ๋ฐœ์ƒ ์—ฌ๋ถ€์™€ ๊ด€๊ณ„์—†์ด ํ•ญ์ƒ ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ
}

์ „์— ์ž‘์„ฑํ–ˆ๋˜ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

const user = {
  name: 'Alice',
  age: 25
};

const jsonString = JSON.stringify(user); // ๊ฐ์ฒด โ†’ JSON ๋ฌธ์ž์—ด ๋ณ€ํ™˜
console.log(jsonString); 

// ์„œ๋ฒ„๋กœ ์ „์†ก (์˜ˆ์‹œ์šฉ์œผ๋กœ ์‹ค์ œ ์ „์†ก๋˜์ง„ ์•Š์Œ)
fetch('https://example.com/api/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json' // JSON ํ˜•์‹์ž„์„ ์„œ๋ฒ„์— ์•Œ๋ฆผ
  },
  body: jsonString // ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†ก
});

์ด์ œ ์œ„ ์ฝ”๋“œ์— try..catch๋ฅผ ์ ์šฉํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

const user = {
  name: 'Alice',
  age: 25
};

async function sendUserData() {
  try {
    const jsonString = JSON.stringify(user); // ๊ฐ์ฒด โ†’ JSON ๋ฌธ์ž์—ด ๋ณ€ํ™˜
    console.log(jsonString);

    const response = await fetch('https://example.com/api/users', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json' // JSON ํ˜•์‹์ž„์„ ์„œ๋ฒ„์— ์•Œ๋ฆผ
      },
      body: jsonString // ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†ก
    });

    if (!response.ok) {
      throw new Error(`์„œ๋ฒ„ ์‘๋‹ต ์˜ค๋ฅ˜: ${response.status} ${response.statusText}`);
    }

    const data = await response.json();
    console.log('์„œ๋ฒ„ ์‘๋‹ต ๋ฐ์ดํ„ฐ:', data);

  } catch (error) {
    console.error('๋ฐ์ดํ„ฐ ์ „์†ก ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:', error);
  }
}

sendUserData();

๋ฐ์ดํ„ฐ ์ „์†ก ์ค‘ ๋ฐœ์ƒ ๊ฐ€๋Šฅํ•œ ์˜ค๋ฅ˜๋ฅผ catch๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ชจ์Šต์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โš ๏ธ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ: tryโ€ฆcatch ์š”์•ฝ
๋น„๋™๊ธฐ ํ†ต์‹  ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ค๋ฅ˜๋Š” try...catch ๊ตฌ๋ฌธ์„ ํ†ตํ•ด ํšจ๊ณผ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์˜ˆ์™ธ ์ƒํ™ฉ์—์„œ๋„ ์ค‘๋‹จ๋˜์ง€ ์•Š๊ณ  ์ ์ ˆํžˆ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2. TanStack Query v5 ๐ŸŒ€

Promise์™€ async/await, Fetch API, ๊ทธ๋ฆฌ๊ณ  ์—๋Ÿฌ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•ด ์‚ดํŽด๋ดค์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ fetch๋Š” ๋‹จ์ˆœํžˆ ์„œ๋ฒ„์— ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ , ์‘๋‹ต์„ ๋ฐ›๋Š” ์—ญํ• ๋งŒ ํ•˜๋Š” API์ž…๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด์— TanStack Query๋Š” ํ”ํžˆ ์„œ๋ฒ„ ์ƒํƒœ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•ด ์ฃผ๋Š” ๋„๊ตฌ๋ผ๊ณ  ๋ถˆ๋ฆฌ์ฃ . ๊ทธ๋Ÿฐ๋ฐ ์—ฌ๊ธฐ์„œ ๋งํ•˜๋Š” โ€˜์„œ๋ฒ„ ์ƒํƒœ ๊ด€๋ฆฌ(Server State Management)โ€™๊ฐ€ ์ •ํ™•ํžˆ ๋ฌด์—‡์„ ์˜๋ฏธํ•˜๋Š”์ง€๋ถ€ํ„ฐ ์งš๊ณ  ๋„˜์–ด๊ฐˆ ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ €๋Š” ์ •์˜๊ฐ€ ๋ช…ํ™•ํ•˜์ง€ ์•Š์œผ๋ฉด ํ›„์† ์ž‘์—…์„ ์ž˜ ๋ชปํ•˜๋Š” ํŽธ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.


์„œ๋ฒ„ ์ƒํƒœ ๊ด€๋ฆฌ๋ž€, ์„œ๋ฒ„์—์„œ ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด์—์„œ ํšจ์œจ์ ์œผ๋กœ ์ €์žฅํ•˜๊ณ , ์—…๋ฐ์ดํŠธํ•˜๋ฉฐ, ์—ฌ๋Ÿฌ ํ™”๋ฉด์ด๋‚˜ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ผ๊ด€๋œ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ด€๋ฆฌํ•˜๋Š” ์ž‘์—…์„ ๋งํ•ฉ๋‹ˆ๋‹ค. fetch๋‚˜ axios๋Š” ๋‹จ์ง€ ์š”์ฒญ๊ณผ ์‘๋‹ต์˜ ๊ธฐ๋Šฅ๋งŒ ์ œ๊ณตํ•  ๋ฟ์ด์ง€๋งŒ, ์„œ๋ฒ„ ์ƒํƒœ ๊ด€๋ฆฌ๋Š” ์ด ๋ฐ์ดํ„ฐ๋ฅผ ์–ธ์ œ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์™€์•ผ ํ• ์ง€, ๋กœ๋”ฉ์ด๋‚˜ ์—๋Ÿฌ ์ƒํƒœ๋Š” ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€, ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ ์–ด๋–ป๊ฒŒ ์ผ๊ด€๋˜๊ฒŒ ๊ณต์œ ํ• ์ง€๊นŒ์ง€ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

์ฆ‰, ์„œ๋ฒ„ ์ƒํƒœ ๊ด€๋ฆฌ๋ž€ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ฐ›์€ ๋ฐ์ดํ„ฐ์— ๊ด€ํ•œ ์‘์šฉ์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. TanStack Query๋Š” ์ด ๋ณต์žกํ•œ ๊ณผ์ •์„ ๋Œ€์‹  ์ฒ˜๋ฆฌํ•ด ์ฃผ๊ธฐ ์œ„ํ•ด ์ด๋ฏธ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ๊ฐ–์ถ˜ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

2-1. staleTime / cacheTime ๐ŸŽฏ

๋ฆฌ๋ˆ…์Šค๋ฅผ ๊ฐœ๋ฐœํ•œ ๋ฆฌ๋ˆ„์Šค ํ† ๋ฐœ์ฆˆ ํ˜•๋‹˜์€ "๋‚˜์œ ํ”„๋กœ๊ทธ๋ž˜๋จธ๋Š” ์ฝ”๋“œ์— ์ง‘์ฐฉํ•˜๊ณ , ํ›Œ๋ฅญํ•œ ํ”„๋กœ๊ทธ๋ž˜๋จธ๋Š” ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์™€ ๊ทธ ๊ด€๊ณ„์— ์ง‘์ค‘ํ•œ๋‹ค."๋ผ๊ณ  ๋ง์”€ํ•˜์…จ์ฃ . ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌํ•  ๊ฒƒ์ธ๋ฐ, ์šฐ์„  ๋ฐ์ดํ„ฐ ์ž์ฒด์˜ ์ƒํƒœ์— ๋Œ€ํ•œ ๊ณต๋ถ€๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋”๋žฌ์ฃ . ๊ทธ ์‹œ์ž‘์ด staleTime๊ณผ cacheTime์ž…๋‹ˆ๋‹ค.

2-1-1. staleTime ๐ŸŸฃ

staleTime์€ useQuery ํ›…์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์†์„ฑ์ž…๋‹ˆ๋‹ค. useQuery ํ›…์€ ๋’ค์—์„œ ์ž์„ธํžˆ ๋‹ค๋ฃฐ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

staleTime์€ ๋ฐ์ดํ„ฐ๊ฐ€ fresh ํ•˜๋‹ค๊ณ  ๊ฐ„์ฃผ๋˜๋Š” ์‹œ๊ฐ„์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์•Œ๊ณ  ์žˆ๋Š” fresh๊ฐ€ ๋งž์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๊ฐ€ fresh ํ•œ ์ƒํƒœ์ผ ๋•Œ๋Š”, ์ฟผ๋ฆฌ๊ฐ€ ๋‹ค์‹œ ๋งˆ์šดํŠธ ๋˜๋”๋ผ๋„ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ํŠธ๋ฆฌ๊ฑฐ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์•„์ง ์ž˜ ๋ชจ๋ฅด๊ฒ ๋Š”๋ฐ์š”. GPTํ•œํ…Œ ๋ฌผ์–ด๋ณด๋‹ˆ, ์ฟผ๋ฆฌ๋Š” ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์š”์ฒญ(fetch)์„ ์ถ”์ƒํ™”ํ•œ ๋‹จ์œ„๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•ด๋‹ฌ๋ผ๊ณ  ํ–ˆ๋Š”๋ฐ ๋‹ต๋ณ€์„ ๋ณด๋‹ˆ ์‹ธ์ด์ฝ”ํŒจ์Šค์ž„์ด ํ‹€๋ฆผ์—†๋‹ค๋Š” ์‚ฌ์‹ค์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


ํŽธ์˜์  ์ง์›(aka. ํŽธ๋Œ์ด)๊ฐ€ ๋˜์—ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ณด์ฃ . ํŽธ๋Œ์ด ์›๋ฏผ๊ด€์”จ๋Š” ์„œ์šธ์šฐ์œ ์˜ ์œ ํ†ต๊ธฐํ•œ์„ ํ™•์ธํ•˜๋ผ๋Š” ์ ์žฅ๋‹˜์˜ ์—…๋ฌด ์ง€์‹œ๋ฅผ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์œ ํ†ต๊ธฐํ•œ์ด 8์ผ์ด๋ผ๋ฉด, 5์ผ์ฐจ ๊นŒ์ง€๋Š” ์‹ ์„ ํ•œ ์ƒํƒœ๋ผ๊ณ  ์ƒ๊ฐํ•ด ๋ด…์‹œ๋‹ค. ์—ฌ๊ธฐ์„œ์˜ 5์ผ์ด staleTime์ž…๋‹ˆ๋‹ค.

const { data } = useQuery({
  queryKey: ['products'],
  queryFn: fetchMilk,
  staleTime: 1000 * 60 * 60 * 24 * 5, // 5์ผ (ms ๊ธฐ์ค€)
});

2-1-2. cacheTime(gcTime) ๐ŸŸฃ

cacheTime์€ v5์—์„œ gcTime์œผ๋กœ ๋ช…์นญ์ด ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์„ค๋ช…์„ ํ•  ๋•Œ์—๋Š” cacheTime์ด๋ผ๋Š” ๋ช…์นญ์„ ์‚ฌ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

cacheTime์€ ์œ ํ†ต๊ธฐํ•œ 8์ผ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, 8์ผ์ด ์ง€๋‚˜๋ฉด ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ์— ์˜ํ•ด ์บ์‹œ์—์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค.

const { data } = useQuery({
  queryKey: ['products'],
  queryFn: fetchMilk,
  staleTime: 1000 * 60 * 60 * 24 * 5, // 5์ผ (ms ๊ธฐ์ค€)
  gcTime: 1000 * 60 * 60 * 24 * 8, // 8์ผ (ms ๊ธฐ์ค€)
});

8์ผ์ด ์ง€๋‚˜๋ฉด ํŽธ๋Œ์ด ์›๋ฏผ๊ด€์”จ๋Š” ์„œ์šธ์šฐ์œ ๋ฅผ ํ๊ธฐ ์ƒํ’ˆ์œผ๋กœ ๋“ฑ๋กํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ 3์ผ์ด ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค.

์‹ ์„ ํ•˜์ง€ ์•Š์€ ์šฐ์œ ์ธ๋ฐ(=5์ผ์ด ๊ฒฝ๊ณผ๋œ ์šฐ์œ ) ํ๊ธฐ ์ƒํ’ˆ์ด ๋˜์ง€๋Š” ์•Š์€(=8์ผ์ด ๊ฒฝ๊ณผ๋˜์ง€ ์•Š์€) ์šฐ์œ ๋Š” ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌ๋ ๊นŒ์š”?

์šฐ์„  stale ํ•œ, ์ฆ‰ ์‹ ์„ ํ•˜์ง€ ์•Š์€ ์šฐ์œ ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ฟผ๋ฆฌ๊ฐ€ ๋งˆ์šดํŠธ ๋˜๋ฉด ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ํŠธ๋ฆฌ๊ฑฐ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋‹จ, 3์ผ ๋™์•ˆ ๋ณ„๋„์˜ ์š”์ฒญ์ด ์—†๋‹ค๋ฉด 8์ผ ๊ฒฝ๊ณผ ํ›„์— ์บ์‹œ๊ฐ€ ์™„์ „ํžˆ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค.

โš ๏ธ staleTime / cacheTime ์š”์•ฝ
staleTime์€ ๋ฐ์ดํ„ฐ๊ฐ€ '์‹ ์„ 'ํ•˜๋‹ค๊ณ  ๊ฐ„์ฃผ๋˜๋Š” ์‹œ๊ฐ„์œผ๋กœ, ์ด ์‹œ๊ฐ„ ๋™์•ˆ์€ ์žฌ์š”์ฒญ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. cacheTime(v5์—์„œ๋Š” gcTime)์€ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์บ์‹œ์— ๋‚จ์•„์žˆ๋Š” ์‹œ๊ฐ„์œผ๋กœ, ์ด ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค. ๋‘ ์„ค์ •์„ ์ ์ ˆํžˆ ์กฐํ•ฉํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ๊ณผ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2-2. useQuery ๐ŸŽฏ

useQuery()๋Š”, ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ํ›„์ˆ ํ•˜๊ฒ ์ง€๋งŒ, ์กฐํšŒ๊ฐ€ ์•„๋‹Œ ๋ณ€๊ฒฝ ์ž‘์—…์„ ํ•  ๋•Œ์—๋Š” useMutation()์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

2-2-1. queryKey ๐ŸŸฃ

queryKey๋Š” useQuery์—์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ค‘ ํ•˜๋‚˜๋กœ, ์ฟผ๋ฆฌ๋ฅผ ๊ณ ์œ ํ•˜๊ฒŒ ์‹๋ณ„ํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. TanStack Query๋Š” ์ด ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‚ด๋ถ€์ ์œผ๋กœ ์บ์‹ฑ, ์ค‘๋ณต ์ œ๊ฑฐ, ์—…๋ฐ์ดํŠธ ๋“ฑ์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.


queryKey์˜ ๊ธฐ๋ณธ ํ˜•ํƒœ ๐Ÿท๏ธ

queryKey๋Š” ๋ฌธ์ž์—ด, ์ˆซ์ž, ๋˜๋Š” ๊ฐ์ฒด๋ฅผ ํฌํ•จํ•œ '๋ฐฐ์—ด ํ˜•ํƒœ'๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

// ๊ธฐ๋ณธ ๋ฌธ์ž์—ด ํ‚ค
const { data } = useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos
});

ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํฌํ•จํ•œ ํ‚ค ๐Ÿท๏ธ

๋™์  ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํฌํ•จํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ๋Š” ํ•ด๋‹น ๊ฐ’์„ queryKey์— ํฌํ•จ์‹œํ‚ต๋‹ˆ๋‹ค.

// ํŠน์ • ํ•  ์ผ ํ•ญ๋ชฉ ๊ฐ€์ ธ์˜ค๊ธฐ
const { data: todoItem } = useQuery({
  queryKey: ['todo', todoId],
  queryFn: () => fetchTodoById(todoId)
});

์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑํ•˜๋ฉด todoId๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์ฟผ๋ฆฌ๊ฐ€ ์ž๋™์œผ๋กœ ๋‹ค์‹œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.


ํ•„ํ„ฐ๋‚˜ ์ •๋ ฌ ์กฐ๊ฑด์„ ํฌํ•จํ•œ ํ‚ค ๐Ÿท๏ธ

ํ•„ํ„ฐ๋‚˜ ์ •๋ ฌ ์กฐ๊ฑด๊ณผ ๊ฐ™์€ ๋ณต์žกํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ๊ฐ์ฒด ํ˜•ํƒœ๋กœ ํฌํ•จ์‹œํ‚ต๋‹ˆ๋‹ค.

// ์ƒํƒœ์™€ ์šฐ์„ ์ˆœ์œ„๋กœ ํ•„ํ„ฐ๋ง๋œ ํ•  ์ผ ๋ชฉ๋ก
const { data: filteredTodos } = useQuery({
  queryKey: ['todos', { status, priority, page }],
  queryFn: () => fetchTodosByFilter({ status, priority, page })
});

queryKey์˜ ์ค‘์š”์„ฑ ๐Ÿท๏ธ

queryKey๋Š” ๋‹จ์ˆœํ•œ ์‹๋ณ„์ž ์ด์ƒ์˜ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

  1. ์บ์‹œ ๊ด€๋ฆฌ: ๋™์ผํ•œ queryKey๋ฅผ ๊ฐ€์ง„ ์ฟผ๋ฆฌ๋Š” ์บ์‹œ๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค.
  2. ์ž๋™ ๋ฆฌํŽ˜์นญ: queryKey๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์ฟผ๋ฆฌ๊ฐ€ ์ž๋™์œผ๋กœ ๋‹ค์‹œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
  3. ์ฟผ๋ฆฌ ๋ฌดํšจํ™”: invalidateQueries๋ฅผ ์‚ฌ์šฉํ•ด ํŠน์ • ํŒจํ„ด์˜ queryKey๋ฅผ ๊ฐ€์ง„ ๋ชจ๋“  ์ฟผ๋ฆฌ๋ฅผ ๋ฌดํšจํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
// 'todos'๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ์ฟผ๋ฆฌ ๋ฌดํšจํ™”
queryClient.invalidateQueries({ queryKey: ['todos'] });

// ํŠน์ • ํ•  ์ผ ํ•ญ๋ชฉ ๊ด€๋ จ ์ฟผ๋ฆฌ๋งŒ ๋ฌดํšจํ™”
queryClient.invalidateQueries({ queryKey: ['todo', todoId] });

queryKey ๊ตฌ์กฐํ™” ์ „๋žต ๐Ÿท๏ธ

ํšจ์œจ์ ์ธ queryKey ๊ตฌ์กฐํ™”๋ฅผ ์œ„ํ•œ ๋ช‡ ๊ฐ€์ง€ ์ „๋žต์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ๊ณ„์ธต์  ๊ตฌ์กฐ ์‚ฌ์šฉ: ์ฒซ ๋ฒˆ์งธ ์š”์†Œ๋Š” ์—”ํ‹ฐํ‹ฐ ํƒ€์ž…, ์ดํ›„ ์š”์†Œ๋Š” ์‹๋ณ„์ž๋‚˜ ๊ด€๊ณ„๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
['users']                           // ์‚ฌ์šฉ์ž ๋ชฉ๋ก
['users', userId]                   // ํŠน์ • ์‚ฌ์šฉ์ž
['users', userId, 'projects']       // ํŠน์ • ์‚ฌ์šฉ์ž์˜ ํ”„๋กœ์ ํŠธ
['users', userId, 'projects', projectId]  // ํŠน์ • ์‚ฌ์šฉ์ž์˜ ํŠน์ • ํ”„๋กœ์ ํŠธ
  1. ๊ฐ์ฒด ์‚ฌ์šฉ: ํ•„ํ„ฐ, ์ •๋ ฌ, ํŽ˜์ด์ง€๋„ค์ด์…˜ ๋“ฑ์˜ ๋ณ€์ˆ˜๋Š” ๊ฐ์ฒด๋กœ ๊ทธ๋ฃนํ™”ํ•ฉ๋‹ˆ๋‹ค.
['posts', { filters: { status, category }, sort, page }]

์ด๋Ÿฌํ•œ ์ „๋žต์€ ์ฟผ๋ฆฌ ๊ด€๋ฆฌ๋ฅผ ๋” ์‰ฝ๊ฒŒ ๋งŒ๋“ค๊ณ , ๊ด€๋ จ ์ฟผ๋ฆฌ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ๋ฌดํšจํ™”ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

2-2-2. queryFn ๐ŸŸฃ

queryFn์€ useQuery์˜ ๋˜ ๋‹ค๋ฅธ ํ•„์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ, ์‹ค์ œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋น„๋™๊ธฐ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” Promise๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•˜๋ฉฐ, ์„œ๋ฒ„๋‚˜ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ ์†Œ์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.


๊ธฐ๋ณธ ๊ตฌํ˜„ ๐Ÿท๏ธ

queryFn์˜ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ํ˜•ํƒœ๋Š” Promise๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

const { data } = useQuery({
  queryKey: ['todos'],
  queryFn: async () => {
    const response = await fetch('https://api.example.com/todos');
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json();
  }
});

axios, fetch ๋“ฑ ์›ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


queryFn์— ์ „๋‹ฌ๋˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜ ํ™œ์šฉ ๐Ÿท๏ธ

TanStack Query v5์—์„œ queryFn์€ ์œ ์šฉํ•œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•œ ๊ฐ์ฒด๋ฅผ ์ž๋™์œผ๋กœ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค.

const { data } = useQuery({
  queryKey: ['todos', { status, page }],
  queryFn: async ({ queryKey, signal, meta }) => {
    // queryKey์—์„œ ํ•„์š”ํ•œ ์ •๋ณด ์ถ”์ถœ
    const [_key, { status, page }] = queryKey;
    
    // ์š”์ฒญ ์ทจ์†Œ๋ฅผ ์œ„ํ•œ AbortSignal ์‚ฌ์šฉ
    const response = await fetch(
      `https://api.example.com/todos?status=${status}&page=${page}`,
      { signal }
    );
    
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    
    return response.json();
  },
  meta: { authRequired: true }
});

์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๐Ÿท๏ธ

queryFn์—์„œ ๋ฐœ์ƒํ•œ ์˜ค๋ฅ˜๋Š” ์ž๋™์œผ๋กœ ํฌ์ฐฉ๋˜์–ด ์ฟผ๋ฆฌ์˜ error ์ƒํƒœ๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค.

const { data, error, isError } = useQuery({
  queryKey: ['todos'],
  queryFn: async () => {
    const response = await fetch('https://api.example.com/todos');
    if (!response.ok) {
      throw new Error(`Error ${response.status}: ${response.statusText}`);
    }
    return response.json();
  }
});

// ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ์ฒ˜๋ฆฌ
if (isError) {
  return <div>Error: {error.message}</div>;
}

์ฟผ๋ฆฌ ํ•จ์ˆ˜ ์ถ”์ƒํ™” ๐Ÿท๏ธ

๋ณต์žกํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ์ฟผ๋ฆฌ ํ•จ์ˆ˜๋ฅผ ๋ณ„๋„์˜ ๋ชจ๋“ˆ๋กœ ์ถ”์ถœํ•˜์—ฌ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// api.js
export const fetchTodos = async ({ queryKey }) => {
  const [_key, { status, page }] = queryKey;
  const response = await fetch(
    `https://api.example.com/todos?status=${status}&page=${page}`
  );
  if (!response.ok) {
    throw new Error('Network response was not ok');
  }
  return response.json();
};

// TodoList.jsx
const { data } = useQuery({
  queryKey: ['todos', { status, page }],
  queryFn: fetchTodos
});

์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ API ํ˜ธ์ถœ ๋กœ์ง์„ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด, ์ฝ”๋“œ ๊ด€๋ฆฌ์™€ ํ…Œ์ŠคํŠธ๊ฐ€ ์šฉ์ดํ•ด์ง‘๋‹ˆ๋‹ค.


์˜์กด์  ์ฟผ๋ฆฌ ๊ตฌํ˜„ ๐Ÿท๏ธ

queryFn์€ ์ด์ „ ์ฟผ๋ฆฌ์˜ ๊ฒฐ๊ณผ์— ์˜์กดํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

// ๋จผ์ € ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ด
const { data: user } = useQuery({
  queryKey: ['user', userId],
  queryFn: () => fetchUserById(userId)
});

// ์‚ฌ์šฉ์ž ์ •๋ณด๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ ํ”„๋กœ์ ํŠธ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ด
const { data: projects } = useQuery({
  queryKey: ['projects', user?.id],
  queryFn: () => fetchProjectsByUserId(user.id),
  enabled: !!user // user๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ ํ™œ์„ฑํ™”
});

์ด์ฒ˜๋Ÿผ enabled ์˜ต์…˜์„ ํ™œ์šฉํ•˜๋ฉด ์˜์กด์„ฑ ์ฒด์ธ์„ ํšจ๊ณผ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โš ๏ธ useQuery ์š”์•ฝ
useQuery๋Š” TanStack Query์˜ ํ•ต์‹ฌ ํ›…์œผ๋กœ, ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ ์กฐํšŒ์™€ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค. queryKey๋Š” ๋ฐฐ์—ด ํ˜•ํƒœ์˜ ๊ณ ์œ  ์‹๋ณ„์ž๋กœ, ์บ์‹ฑ ๋ฐ ์ž๋™ ๋ฆฌํŽ˜์นญ์˜ ๊ธฐ์ค€์ด ๋˜๋ฉฐ, ๊ณ„์ธต์  ๊ตฌ์กฐ์™€ ๊ฐ์ฒด๋ฅผ ํ™œ์šฉํ•ด ํšจ์œจ์ ์œผ๋กœ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. queryFn์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋กœ, ๋‚ด์žฅ๋œ ํŒŒ๋ผ๋ฏธํ„ฐ(queryKey, signal)๋ฅผ ํ™œ์šฉํ•ด ๋™์  ๋ฐ์ดํ„ฐ ํŽ˜์นญ๊ณผ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋‘ ์š”์†Œ๋ฅผ ์ ์ ˆํžˆ ๊ตฌ์„ฑํ•˜๋ฉด ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ์š”๊ตฌ์‚ฌํ•ญ๋„ ํšจ๊ณผ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2-3. useMutation ๐ŸŽฏ

useMutation์€ TanStack Query์—์„œ ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํ›…์ž…๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑ, ์—…๋ฐ์ดํŠธ, ์‚ญ์ œํ•˜๋Š” ๋“ฑ์˜ ์ž‘์—…์— ์‚ฌ์šฉ๋˜๋ฉฐ, useQuery์™€ ๋‹ฌ๋ฆฌ ์ž๋™์œผ๋กœ ์‹คํ–‰๋˜์ง€ ์•Š๊ณ  ๋ช…์‹œ์ ์ธ ํ˜ธ์ถœ์„ ํ†ตํ•ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

2-3-1. ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ• ๐ŸŸฃ

useMutation์˜ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ํ˜•ํƒœ๋Š” mutationFn์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ์„œ๋ฒ„์— ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋น„๋™๊ธฐ ํ•จ์ˆ˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ๊ตฌํ˜„ ๐Ÿท๏ธ

const { mutate, isPending, isError, isSuccess, error } = useMutation({
  mutationFn: async (newTodo) => {
    const response = await fetch('https://api.example.com/todos', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(newTodo)
    });
    
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    
    return response.json();
  }
});

// ๋ณ€์ด ์‹คํ–‰ํ•˜๊ธฐ
const handleSubmit = () => {
  mutate({ title: 'Buy groceries', completed: false });
};

useMutation์—์„œ ๋ฐ˜ํ™˜๋˜๋Š” ์ฃผ์š” ๊ฐ’:

  • mutate: ๋ณ€์ด๋ฅผ ์‹คํ–‰ํ•˜๋Š” ํ•จ์ˆ˜
  • isPending: ๋ณ€์ด๊ฐ€ ์ง„ํ–‰ ์ค‘์ธ์ง€ ์—ฌ๋ถ€
  • isError: ๋ณ€์ด๊ฐ€ ์˜ค๋ฅ˜ ์ƒํƒœ์ธ์ง€ ์—ฌ๋ถ€
  • isSuccess: ๋ณ€์ด๊ฐ€ ์„ฑ๊ณต ์ƒํƒœ์ธ์ง€ ์—ฌ๋ถ€
  • error: ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ์—๋Ÿฌ ๊ฐ์ฒด
  • data: ์„ฑ๊ณต ์‹œ ์„œ๋ฒ„ ์‘๋‹ต ๋ฐ์ดํ„ฐ

๋ณ€์ด ํ•จ์ˆ˜ ์ „๋‹ฌ ๋ฐฉ์‹ ๐Ÿท๏ธ

๋ณ€์ด ํ•จ์ˆ˜๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

// ํ•จ์ˆ˜ ์ง์ ‘ ์ •์˜
const mutation = useMutation({
  mutationFn: (newTodo) => addTodo(newTodo)
});

// ๋˜๋Š” ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ
const mutation = useMutation({
  mutationFn: addTodo
});

2-3-2. ๋ณ€์ด ์ƒ๋ช…์ฃผ๊ธฐ์™€ ์ฝœ๋ฐฑ ๐ŸŸฃ

useMutation์€ ๋ณ€์ด์˜ ๋‹ค์–‘ํ•œ ๋‹จ๊ณ„์—์„œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์—ฌ๋Ÿฌ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ฃผ์š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ๐Ÿท๏ธ

const queryClient = useQueryClient();

const { mutate } = useMutation({
  mutationFn: addTodo,
  
  // ๋ณ€์ด ์‹œ์ž‘ ์ „
  onMutate: async (newTodo) => {
    // ๊ด€๋ จ ์ฟผ๋ฆฌ ์ทจ์†Œ
    await queryClient.cancelQueries({ queryKey: ['todos'] });
    
    // ์ด์ „ ์ƒํƒœ ๋ฐฑ์—…
    const previousTodos = queryClient.getQueryData(['todos']);
    
    // ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ
    queryClient.setQueryData(['todos'], old => [...old, newTodo]);
    
    // ๋กค๋ฐฑ์„ ์œ„ํ•œ ์ปจํ…์ŠคํŠธ ๋ฐ˜ํ™˜
    return { previousTodos };
  },
  
  // ๋ณ€์ด ์„ฑ๊ณต ์‹œ
  onSuccess: (data, variables, context) => {
    // data: ์„œ๋ฒ„ ์‘๋‹ต ๋ฐ์ดํ„ฐ
    // variables: mutate์— ์ „๋‹ฌ๋œ ๋ณ€์ˆ˜
    // context: onMutate์—์„œ ๋ฐ˜ํ™˜๋œ ๊ฐ’
    
    // ์„ฑ๊ณต ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ
    toast.success('ํ•  ์ผ์ด ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค!');
    
    // ํ•„์š” ์‹œ ํŠน์ • ์ฟผ๋ฆฌ ๋ฌดํšจํ™”
    queryClient.invalidateQueries({ queryKey: ['todos'] });
  },
  
  // ๋ณ€์ด ์‹คํŒจ ์‹œ
  onError: (error, variables, context) => {
    // ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ ๋กค๋ฐฑ
    if (context?.previousTodos) {
      queryClient.setQueryData(['todos'], context.previousTodos);
    }
    
    // ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ
    toast.error(`์˜ค๋ฅ˜ ๋ฐœ์ƒ: ${error.message}`);
  },
  
  // ์„ฑ๊ณต ๋˜๋Š” ์‹คํŒจ ํ›„ ํ•ญ์ƒ ์‹คํ–‰
  onSettled: (data, error, variables, context) => {
    // ํ•ญ์ƒ ์ฟผ๋ฆฌ ๋ฌดํšจํ™”
    queryClient.invalidateQueries({ queryKey: ['todos'] });
    
    // ์ดํ›„ ์ž‘์—… ์ˆ˜ํ–‰
    console.log('๋ณ€์ด ์™„๋ฃŒ');
  }
});

์ด ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋“ค์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ณ€์ด์˜ ์ „์ฒด ์ƒ๋ช…์ฃผ๊ธฐ์— ๊ฑธ์ณ ์„ธ๋ฐ€ํ•œ ์ œ์–ด๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

2-3-3. ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ ๊ตฌํ˜„ ๐ŸŸฃ

๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋Š” ์„œ๋ฒ„ ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  UI๋ฅผ ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•˜๋Š” ๊ธฐ๋ฒ•์ž…๋‹ˆ๋‹ค.

๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ์˜ ์ฃผ์š” ๋‹จ๊ณ„ ๐Ÿท๏ธ

  1. ํ˜„์žฌ ์ƒํƒœ ๋ฐฑ์—…: ๋กค๋ฐฑ์„ ์œ„ํ•ด ํ˜„์žฌ ์ƒํƒœ๋ฅผ ์ €์žฅ
  2. ์บ์‹œ ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธ: ์˜ˆ์ƒ ๊ฒฐ๊ณผ๋กœ UI ์—…๋ฐ์ดํŠธ
  3. ์˜ค๋ฅ˜ ์‹œ ๋กค๋ฐฑ: ์‹คํŒจํ•  ๊ฒฝ์šฐ ์ด์ „ ์ƒํƒœ๋กœ ๋˜๋Œ๋ฆผ
const queryClient = useQueryClient();

const { mutate } = useMutation({
  mutationFn: async (todo) => {
    const response = await fetch(`https://api.example.com/todos/${todo.id}`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ completed: todo.completed })
    });
    
    if (!response.ok) {
      throw new Error('Failed to update todo');
    }
    
    return response.json();
  },
  
  // ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ ๊ตฌํ˜„
  onMutate: async (updatedTodo) => {
    // ์ง„ํ–‰ ์ค‘์ธ ์ฟผ๋ฆฌ ์ทจ์†Œ
    await queryClient.cancelQueries({ queryKey: ['todos'] });
    
    // ์ด์ „ ์ƒํƒœ ๋ฐฑ์—…
    const previousTodos = queryClient.getQueryData(['todos']);
    
    // ์บ์‹œ ์—…๋ฐ์ดํŠธ
    queryClient.setQueryData(['todos'], old => 
      old.map(todo => 
        todo.id === updatedTodo.id 
          ? { ...todo, completed: updatedTodo.completed } 
          : todo
      )
    );
    
    return { previousTodos };
  },
  
  // ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ๋กค๋ฐฑ
  onError: (err, updatedTodo, context) => {
    // ์ด์ „ ์ƒํƒœ๋กœ ๋ณต์›
    queryClient.setQueryData(['todos'], context.previousTodos);
    toast.error('ํ•  ์ผ ์—…๋ฐ์ดํŠธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.');
  },
  
  // ๋ฌด์กฐ๊ฑด ์ฟผ๋ฆฌ ๋ฌดํšจํ™”
  onSettled: () => {
    queryClient.invalidateQueries({ queryKey: ['todos'] });
  }
});

// ์‚ฌ์šฉ ์˜ˆ์‹œ
const toggleTodoCompletion = (todoId, isCompleted) => {
  mutate({ id: todoId, completed: isCompleted });
};

2-3-4. mutate ํ•จ์ˆ˜์˜ ํ™œ์šฉ ๐ŸŸฃ

mutate ํ•จ์ˆ˜๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์œผ๋กœ ํ™œ์šฉ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํ•„์š”์— ๋”ฐ๋ผ ์ถ”๊ฐ€์ ์ธ ์˜ต์…˜์„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ํ˜ธ์ถœ ๋ฐฉ์‹ ๐Ÿท๏ธ

// ๋‹จ์ˆœ ํ˜ธ์ถœ
mutate(newTodo);

// ์„ฑ๊ณต/์‹คํŒจ ํ•ธ๋“ค๋Ÿฌ ํฌํ•จ ํ˜ธ์ถœ
mutate(newTodo, {
  onSuccess: (data) => {
    // ์ด ํ•ธ๋“ค๋Ÿฌ๋Š” ์ „์—ญ onSuccess ํ›„์— ์‹คํ–‰๋จ
    console.log('Todo ์ถ”๊ฐ€ ์„ฑ๊ณต:', data);
    
    // ์ถ”๊ฐ€ ์ž‘์—… ์ˆ˜ํ–‰ (์˜ˆ: ํผ ์ดˆ๊ธฐํ™”, ํ™”๋ฉด ์ „ํ™˜ ๋“ฑ)
    resetForm();
    navigate('/todos');
  },
  onError: (error) => {
    console.error('Todo ์ถ”๊ฐ€ ์‹คํŒจ:', error);
    // ์‚ฌ์šฉ์ž์—๊ฒŒ ํŠน์ • ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ
  }
});

mutateAsync ํ™œ์šฉ ๐Ÿท๏ธ

๋น„๋™๊ธฐ ํ๋ฆ„ ์ œ์–ด๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ mutateAsync๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

const { mutateAsync } = useMutation({
  mutationFn: addTodo
});

const handleSubmit = async () => {
  try {
    // mutateAsync๋Š” Promise๋ฅผ ๋ฐ˜ํ™˜
    const newTodo = await mutateAsync({ title: 'Buy milk', completed: false });
    console.log('์ƒˆ ํ•  ์ผ์ด ์ถ”๊ฐ€๋จ:', newTodo);
    
    // ์„ฑ๊ณต ํ›„ ์ถ”๊ฐ€ ์ž‘์—…
    await saveToLocalStorage(newTodo);
    
    // ๋‹ค๋ฅธ ๋ณ€์ด ์—ฐ์† ์‹คํ–‰
    await updateUserStats();
    
  } catch (error) {
    console.error('์˜ค๋ฅ˜ ๋ฐœ์ƒ:', error);
  }
};

2-3-5. ์ฟผ๋ฆฌ ๋ฌดํšจํ™”์™€ ์—…๋ฐ์ดํŠธ ์ „๋žต ๐ŸŸฃ

๋ณ€์ด ํ›„ ๊ด€๋ จ ๋ฐ์ดํ„ฐ๋ฅผ ์ตœ์‹  ์ƒํƒœ๋กœ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์—ฌ๋Ÿฌ ์ „๋žต์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ง์ ‘ ์ฟผ๋ฆฌ ๋ฌดํšจํ™” ๐Ÿท๏ธ

๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ๋ฐฉ๋ฒ•์€ ๊ด€๋ จ ์ฟผ๋ฆฌ๋ฅผ ๋ฌดํšจํ™”ํ•˜์—ฌ ๋ฆฌํŽ˜์นญ์„ ํŠธ๋ฆฌ๊ฑฐ ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค:

const { mutate } = useMutation({
  mutationFn: addTodo,
  onSuccess: () => {
    // 'todos'๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ์ฟผ๋ฆฌ ๋ฌดํšจํ™”
    queryClient.invalidateQueries({ queryKey: ['todos'] });
  }
});

์ฟผ๋ฆฌ ์ง์ ‘ ์—…๋ฐ์ดํŠธ ๐Ÿท๏ธ

๋•Œ๋กœ๋Š” ์„œ๋ฒ„ ์‘๋‹ต์„ ์‚ฌ์šฉํ•˜์—ฌ ์บ์‹œ๋ฅผ ์ง์ ‘ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒƒ์ด ํšจ์œจ์ ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

const { mutate } = useMutation({
  mutationFn: addTodo,
  onSuccess: (newTodo) => {
    // ๊ธฐ์กด 'todos' ์ฟผ๋ฆฌ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
    const previousTodos = queryClient.getQueryData(['todos']);
    
    // ์ƒˆ ํ•  ์ผ์„ ํฌํ•จํ•˜์—ฌ ์บ์‹œ ์—…๋ฐ์ดํŠธ
    if (previousTodos) {
      queryClient.setQueryData(['todos'], [...previousTodos, newTodo]);
    }
    
    // ์ƒˆ ํ•  ์ผ์˜ ์ƒ์„ธ ์ฟผ๋ฆฌ ๋ฏธ๋ฆฌ ์ฑ„์šฐ๊ธฐ
    queryClient.setQueryData(['todo', newTodo.id], newTodo);
  }
});

์„œ๋กœ ๋‹ค๋ฅธ ์ „๋žต ๊ฒฐํ•ฉ ๐Ÿท๏ธ

๋ณต์žกํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ์—ฌ๋Ÿฌ ์ „๋žต์„ ๊ฒฐํ•ฉํ•˜๋Š” ๊ฒƒ์ด ์œ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

const { mutate } = useMutation({
  mutationFn: updateTodo,
  onSuccess: (updatedTodo) => {
    // 1. ์—…๋ฐ์ดํŠธ๋œ ํ•  ์ผ์˜ ์ƒ์„ธ ์ฟผ๋ฆฌ ์ง์ ‘ ์—…๋ฐ์ดํŠธ
    queryClient.setQueryData(['todo', updatedTodo.id], updatedTodo);
    
    // 2. ๋ชฉ๋ก ์ฟผ๋ฆฌ๋ฅผ ์ง์ ‘ ์—…๋ฐ์ดํŠธ (ํŠน์ • ์กฐ๊ฑด์— ๋งž๋Š” ๊ฒฝ์šฐ)
    queryClient.setQueriesData({ queryKey: ['todos'] }, (old) => {
      if (!old) return old;
      return old.map(todo => 
        todo.id === updatedTodo.id ? updatedTodo : todo
      );
    });
    
    // 3. ๊ด€๋ จ ์ฟผ๋ฆฌ ๋ฌดํšจํ™” (ํ•„ํ„ฐ๋ง๋œ ๋ชฉ๋ก ๋“ฑ)
    queryClient.invalidateQueries({
      queryKey: ['todos'],
      // ํŠน์ • ์ฟผ๋ฆฌ๋งŒ ๋ฌดํšจํ™”
      predicate: (query) => 
        query.queryKey.length > 1 && 
        typeof query.queryKey[1] === 'object'
    });
  }
});

์ด๋Ÿฌํ•œ ๋‹ค์–‘ํ•œ ์ „๋žต์„ ์ƒํ™ฉ์— ๋งž๊ฒŒ ํ™œ์šฉํ•˜๋ฉด ํšจ์œจ์ ์ด๊ณ  ์ผ๊ด€๋œ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โš ๏ธ useMutation ์š”์•ฝ
useMutation์€ ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์ž‘์—…์„ ์œ„ํ•œ ํ›…์œผ๋กœ, mutationFn์„ ํ†ตํ•ด ์‹ค์ œ API ํ˜ธ์ถœ์„ ์ •์˜ํ•˜๊ณ  mutate ํ•จ์ˆ˜๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ƒ๋ช…์ฃผ๊ธฐ ์ฝœ๋ฐฑ(onMutate, onSuccess, onError, onSettled)์„ ํ™œ์šฉํ•œ ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋ฅผ ํ†ตํ•ด ์ฆ‰๊ฐ์ ์ธ UI ์‘๋‹ต์„ ์ œ๊ณตํ•˜๊ณ , queryClient์˜ ๋‹ค์–‘ํ•œ ๋ฉ”์„œ๋“œ(invalidateQueries, setQueryData)๋ฅผ ์‚ฌ์šฉํ•ด ๊ด€๋ จ ์ฟผ๋ฆฌ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ฐฑ์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ธฐ๋Šฅ๋“ค์„ ํ™œ์šฉํ•˜๋ฉด ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ๋„ ์ผ๊ด€๋œ ์ƒํƒœ ๊ด€๋ฆฌ์™€ ํ–ฅ์ƒ๋œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3. Conclusion ๐ŸŒ€

ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์—์„œ ๋น„๋™๊ธฐ ํ†ต์‹ ์€ ๋‹จ์ˆœํ•œ ๋ฐ์ดํ„ฐ ๊ตํ™˜์„ ๋„˜์–ด ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์กฐ์™€ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ํ•ต์‹ฌ ์š”์†Œ์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ JavaScript์˜ Promise์™€ async/await, ๊ทธ๋ฆฌ๊ณ  Fetch API๋Š” ๋น„๋™๊ธฐ ํ†ต์‹ ์˜ ๊ธฐ์ดˆ๋ฅผ ์ œ๊ณตํ•˜์ง€๋งŒ, ์‹ค์ œ ์„œ๋น„์Šค์—๋Š” ๋” ๋ณต์žกํ•œ ์š”๊ตฌ์‚ฌํ•ญ์ด ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. TanStack Query๋Š” ์ด๋Ÿฌํ•œ ๊ณ ๊ธ‰ ์š”๊ตฌ์‚ฌํ•ญ์„ ์ถฉ์กฑ์‹œํ‚ค๋Š” ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๋กœ, ๋ฐ์ดํ„ฐ ์บ์‹ฑ๊ณผ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ์ž ๊ฒฝํ—˜๊ณผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ฑ๋Šฅ์„ ๋ชจ๋‘ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.


TanStack Query์˜ staleTime/gcTime์œผ๋กœ ๋ฐ์ดํ„ฐ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ , queryKey๋ฅผ ํ†ตํ•ด ํšจ์œจ์ ์ธ ์บ์‹ฑ ์ „๋žต์„ ๊ตฌํ˜„ํ•˜๋ฉฐ, useQuery์™€ useMutation์œผ๋กœ ๋ฐ์ดํ„ฐ ์กฐํšŒ์™€ ๋ณ€๊ฒฝ์„ ์ง๊ด€์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ, ์ž๋™ ๋ฆฌํŽ˜์นญ, ์˜์กด์  ์ฟผ๋ฆฌ์™€ ๊ฐ™์€ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์€ ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ๊ฐ„์†Œํ™”ํ•˜๊ณ  ์‚ฌ์šฉ์ž์—๊ฒŒ ๋” ์œ ๋ คํ•œ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ฒฐ๊ตญ Fetch API์—์„œ TanStack Query๋กœ์˜ ์ „ํ™˜์€ ๋‹จ์ˆœํžˆ ๋„๊ตฌ๋ฅผ ๋ฐ”๊พธ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์„œ๋ฒ„ ์ƒํƒœ๋ฅผ ๋ฐ”๋ผ๋ณด๋Š” ๊ด€์  ์ž์ฒด๋ฅผ ์ „ํ™˜ํ•˜๋Š” ํŒจ๋Ÿฌ๋‹ค์ž„์˜ ๋ณ€ํ™”๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๊ฒ ์Šต๋‹ˆ๋‹ค.


Reference:
1. https://joshua1988.github.io/web-development/javascript/promise-for-beginners/
2. https://ko.javascript.info/async-await
3. https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
4. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
5. https://developer.mozilla.org/en-US/docs/Web/API/Response/json
6. https://tanstack.com/query/latest/docs/framework/react/guides/important-defaults
7. https://tanstack.com/query/latest/docs/framework/react/guides/important-defaults
8. https://tanstack.com/query/latest/docs/framework/react/guides/query-keys
9. https://tanstack.com/query/latest/docs/framework/react/guides/query-functions
10. https://tanstack.com/query/latest/docs/framework/react/reference/useQuery
11. https://tanstack.com/query/latest/docs/framework/react/reference/useMutation

profile
Write a little every day, without hope, without despair โœ๏ธ

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