Skip to content

Latest commit

ย 

History

History
1365 lines (1054 loc) ยท 83.2 KB

LEARN_CLIENT.md

File metadata and controls

1365 lines (1054 loc) ยท 83.2 KB

๐Ÿ’๐Ÿปโ€โ™€๏ธ ์ด ํŽ˜์ด์ง€๋Š”?

๊ฐ•์˜๋ฅผ ๋“ค์œผ๋ฉด์„œ ์ฒ˜์Œ ์ ‘ํ•˜๊ฑฐ๋‚˜ ์ดํ•ด๊ฐ€ ๋˜์ง€ ์•Š๋Š” ๋ถ€๋ถ„๋“ค์ด ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ถ”๊ฐ€ ๊ณต๋ถ€๊ฐ€ ํ•„์š”ํ–ˆ๋‹ค. ์•„๋ž˜์— ๋”ฐ๋กœ ๊ณต๋ถ€ํ•œ ๊ฒƒ๋“ค์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ •๋ฆฌํ•ด๋ณด์•˜๋‹ค.

๐Ÿด JavaScript + React + browser API

URLSearchParams ๋ž€?

URLSearchParams ์ธ์Šคํ„ด์Šค๋Š” new URLSearchParams()์˜ ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ URLSearchParams ํ˜ธ์ถœ์‹œ ์ธ์ˆ˜๋กœ ์ „๋‹ฌํ•œ key/value ์Œ์„ ๋ฐ˜๋ณตํ•˜์—ฌ ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด์„ ์‰ฝ๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋„๋ก ๋•๋Š”๋‹ค.

interface URLSearchParams {
  /** Appends a specified key/value pair as a new search parameter. */
  append(name: string, value: string): void;
  /** Deletes the given search parameter, and its associated value, from the list of all search parameters. */
  delete(name: string): void;
  /** Returns the first value associated to the given search parameter. */
  get(name: string): string | null;
  /** Returns all the values association with a given search parameter. */
  getAll(name: string): string[];
  /** Returns a Boolean indicating if such a search parameter exists. */
  has(name: string): boolean;
  /** Sets the value associated to a given search parameter to the given value. If there were several values, delete the others. */
  set(name: string, value: string): void;
  sort(): void;
  /** Returns a string containing a query string suitable for use in a URL. Does not include the question mark. */
  toString(): string;
  forEach(
    callbackfn: (value: string, key: string, parent: URLSearchParams) => void,
    thisArg?: any
  ): void;
}

Intersection Observer API

์ฐธ๊ณ  ๋ฌธ์„œ

Intersection Observer API ๋ž€?

new IntersectionObserver((entries: IntersectionObserverEntry) => { ... }): IntersectionObserver;
  • ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ œ๊ณตํ•˜๋Š” Intersection Observer API๋Š” ์ƒ์œ„ ์š”์†Œ ๋˜๋Š” ์ตœ์ƒ์œ„ ๋ฌธ์„œ์˜ viewport์™€ target ์š”์†Œ์˜ ๊ต์ฐจ์—์„œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋น„๋™๊ธฐ์‹์œผ๋กœ ๊ด€์ฐฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•œ๋‹ค.
  • ๋น„๋™๊ธฐ ํŠน์„ฑ ๋•Œ๋ฌธ์— ์—ฌ๋Ÿฌ ๊ด€์ฐฐ ํ•ญ๋ชฉ์ด ๋™์‹œ์— ์ฝœ๋ฐฑ ํ•จ์ˆ˜์˜ ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋•Œ๋ฌธ์— ์ฝœ๋ฐฑ ํ•จ์ˆ˜์˜ ์ธ์ž๋Š” ๋‹จ์ผ ํ•ญ๋ชฉ์ด ์•„๋‹Œ Array์ด๋‹ค.
  • IntersectionObserverEntry์˜ ์ธ์Šคํ„ด์Šค๋Š” IntersectionObserver์˜ ์ธ์ˆ˜์ธ entries๋กœ ์ „๋‹ฌ๋˜๋ฉฐ, target ์š”์†Œ์˜ ์ขŒํ‘œ์™€ ๊ต์ฐจํ•˜๋Š” ๊ฒฝ๊ณ„ ๋“ฑ ์—ฌ๋Ÿฌ ์†์„ฑ์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค.
  • ์ฃผ์˜ํ•  ์ 
    • Intersection Observer API ์ž์ฒด๋Š” ๋น„๋™๊ธฐ๋กœ ๋™์ž‘ํ•˜๋‚˜, InterSectionObserver์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ๋‚ด์—์„œ ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ๋Š” ๋น„๋™๊ธฐ๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‚ด๋ถ€ ์ฝ”๋“œ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ์Šค๋ ˆ๋“œ๋ฅผ blocking ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.
    • InterSectionObserver๋ฅผ ์ด๋ฏธ์ง€ ๋“ฑ ์ปจํ…์ธ ๋ฅผ ์ง€์—ฐ ๋กœ๋“œํ•  ๋•Œ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ์ž์‚ฐ์ด ๋กœ๋“œ๋œ ํ›„์— unobserve() ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•ด์•ผ ํ•œ๋‹ค.
    • target ์š”์†Œ๊ฐ€ DOM tree์— ์กด์žฌํ•ด์•ผ๋งŒ ๊ต์ฐจ๋ฅผ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰, target ์š”์†Œ๊ฐ€ ๋ ˆ์ด์•„์›ƒ์— ์˜ํ–ฅ์„ ๋ฏธ์ณ์•ผ ํ•œ๋‹ค.
      • display: none์ธ target ์š”์†Œ๋Š” ๊ต์ฐจ๋ฅผ ๊ฐ์ง€ํ•  ์ˆ˜ ์—†๋‹ค.
      • opacity: 0 ๋˜๋Š” visibility: hidden๋กœ ์ธํ•ด target ์š”์†Œ๊ธฐ ๋ณด์ด์ง€ ์•Š์•„๋„ ๋ ˆ์ด์•„์›ƒ์„ ๊ฐ€์ง€๋ฉด(width์™€ height๊ฐ€ ์กด์žฌํ•˜๋Š” ๋“ฑ) target ์š”์†Œ๋ฅผ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
      • position: absolute; width: 0; height: 0์ธ target ์š”์†Œ๋Š” ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ์œผ๋‚˜, containing block์˜ ์˜์—ญ ๋ฐ–์— ์œ„์น˜ํ•˜๊ณ  overflow: hidden์— ์˜ํ•ด ๊ฐ€๋ ค์ง„ target ์š”์†Œ๋Š” ๊ฐ์ง€ํ•  ์ˆ˜ ์—†๋‹ค.

์ „ํ†ต์ ์ธ ๋ฐฉ๋ฒ• vs Intersection Observer API

๊ณผ๊ฑฐ์— target ์š”์†Œ์™€์˜ ๊ต์ฐจ๋ฅผ ๊ฐ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ(scroll)์™€ Element.getBoundingClientRect()๋กœ ๊ตฌํ˜„ํ–ˆ๋‹ค. ํ˜„์žฌ๋Š” ์ด๋Ÿฌํ•œ ์ „ํ†ต์ ์ธ ๋ฐฉ๋ฒ• ๋Œ€์‹  Intersection Observer API๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๋ฐ, ๊ทธ ์ด์œ ์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž.

  • getBoundingClientRect() ๋ฉ”์„œ๋“œ๋Š” ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ „์ฒด ํŽ˜์ด์ง€๋ฅผ re-layout ํ•˜๋„๋ก ๊ฐ•์ œํ•˜์—ฌ ์†๋„๋ฅผ ๋”๋””๊ฒŒ ํ•˜๋Š” ๋ฐ˜๋ฉด, IntersectionObserverEntry๋Š” ๋ฏธ๋ฆฌ ์ •์˜๋˜๊ณ  ๋ฏธ๋ฆฌ ๊ณ„์‚ฐ๋œ ์†์„ฑ ์ง‘ํ•ฉ์„ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํšจ์œจ์ ์ด๋‹ค.
  • ์ „ํ†ต์ ์ธ ๋ฐฉ๋ฒ•์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ์Šค๋ ˆ๋“œ์—์„œ ๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ์•ผ๊ธฐํ•˜๋Š” ๋ฐ˜๋ฉด, Intersection Observer API๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์Šค๋ ˆ๋“œ์™€๋Š” ๋ณ„๊ฐœ์˜ ์Šค๋ ˆ๋“œ(browser์˜ ์Šค๋ ˆ๋“œ ์ค‘ ํ•˜๋‚˜)์—์„œ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋˜์–ด ์ตœ์ ํ™”๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.
    • ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” ๋น„๋™๊ธฐ๋กœ ํ˜ธ์ถœ๋˜์ง€๋งŒ, ๊ฒฐ๋ก ์ ์œผ๋กœ๋Š” ํ…Œ์Šคํฌ ํ์— ์Œ“์ธ ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋“ค์ด ์ˆœ์„œ๋Œ€๋กœ ํ˜ธ์ถœ๋˜๋ฏ€๋กœ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ(์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์Šค๋ ˆ๋“œ)์˜ ์‘๋‹ต์„ฑ์— ์˜ํ–ฅ์„ ๋ฏธ์นœ๋‹ค.

๊ต์ฐจ ๊ฐ์ง€๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ํ•ญ๋ชฉ ์˜ˆ์‹œ

  • ์Šคํฌ๋กค์‹œ ์ด๋ฏธ์ง€ ๋˜๋Š” ๊ธฐํƒ€ ์ปจํ…์ธ  ์ง€์—ฐ ๋กœ๋“œ
  • ๋ฌดํ•œ ์Šคํฌ๋กค
  • ๊ด‘๊ณ  ์ˆ˜์ต์„ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•œ ๊ด‘๊ณ ์˜ ๊ฐ€์‹œ์„ฑ ๋ณด๊ณ 
  • ํ˜„์žฌ ์Šคํฌ๋กค์ด ์œ„์น˜ํ•œ ์ฝ˜ํ…์ธ ์— ๋”ฐ๋ผ ๋„ค๋น„๊ฒŒ์ด์…˜ ํ•˜์ด๋ผ์ดํŒ…

react๋กœ IntersectionObserver ๊ตฌํ˜„ํ•˜๊ธฐ

๊ฐ•์˜์—์„œ ๊ตฌํ˜„ํ•œ IntsersectionObserver์˜ ๋กœ์ง ์ดํ•ด๋ฅผ ์œ„ํ•ด ์ฐพ์•„๋ณด๋‹ค๊ฐ€ ์•„๋ž˜ ๋ฌธ์„œ๋“ค์„ ์ฝ๊ฒŒ ๋˜์—ˆ๋‹ค. ๋ฌธ์„œ์—์„œ ๊ตฌํ˜„ํ•œ ๋กœ์ง๊ณผ ๊ฐ•์˜์—์„œ ๊ตฌํ˜„ํ•œ ๋กœ์ง์€ ๋‹ค๋ฅธ ํ˜•ํƒœ๋ฅผ ๋„๊ณ  ์žˆ๋‹ค. ์ถ”ํ›„ ๋ฆฌํŒฉํ† ๋งํ•˜๋Š” ์‹œ๊ฐ„์„ ๊ฐ€์ง€๊ธฐ ์œ„ํ•ด ๋ฌธ์„œ์˜ ๋‚ด์šฉ์„ ์ •๋ฆฌํ–ˆ๋‹ค.

์ฐธ๊ณ  ๋ฌธ์„œ

๊ธฐ๋Šฅ ๊ตฌํ˜„

const App = () => {
  const [target, setTarget] = useState(null);

  useEffect(() => {
    let observer;

    if (target) {
      observer = new IntersectionObserver(onIntersect, { threshold: 0.5 });
      observer.observe(target);
    }

    return () => observer && observer.disconnect();
  }, [target]);

  // ...

  return (
    ...
    <div ref={setTarget} />
  );
}
  1. useState๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ IntersectionObserver์˜ callback์—์„œ ์‹คํ–‰๋˜๋Š” ํ•ญ๋ชฉ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด, entry.isIntersecting์˜ ๊ฐ’์— ๋”ฐ๋ผ ์„œ๋ฒ„์— ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜๊ฑฐ๋‚˜ view๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค๋ฉด entry.isIntersecting์˜ ๊ฐ’์„ state(์ƒํƒœ)๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
  2. useEffect๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง์˜ ๊ฒฐ๊ณผ์™€ ๋™๊ธฐํ™”๋œ Intersection Observer API๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • useEffect๋Š” React์—๊ฒŒ ์™ธ๋ถ€ API(Intersection Observer API)๊ฐ€ ํ•˜๋Š” ์ผ์„ ์•Œ๋ฆฌ์ง€ ์•Š์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง ๋˜๊ณ  ๋‚œ ์ดํ›„ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ณ€๊ฒฝ๋œ ์ƒํƒœ๋‚˜ DOM ๋“ฑ React๋ฅผ ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ๋™๊ธฐํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.
  3. useState๋ฅผ ์ด์šฉํ•˜์—ฌ target node๋ฅผ ์ €์žฅํ•˜๊ณ  IntersectionObserver๊ฐ€ target node๋ฅผ ๊ด€์ฐฐํ•  ์ˆ˜ ์žˆ๋„๋ก observe() ๋ฉ”์„œ๋“œ์— ์ „๋‹ฌํ•œ๋‹ค.
    • target node์˜ ref ์†์„ฑ์— callback์œผ๋กœ useState์˜ setState๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ref callback์ด ํ˜ธ์ถœ๋˜๋ฉด์„œ state์— target node๊ฐ€ ๋“ฑ๋ก๋œ๋‹ค.
    • observe() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ์žˆ๋Š” useEffect ๋ฉ”์„œ๋“œ์˜ ์˜์กด์„ฑ ๋ฐฐ์—ด์— useState์˜ ๊ฐ’์„ ์ถ”๊ฐ€ ํ•˜์—ฌ target node๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ useEffect๊ฐ€ ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์•ผ ํ•œ๋‹ค.
    • ๋‹จ, target node๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ IntersectionObserver๊ฐ€ ๊ด€์ฐฐํ•˜๊ณ  ์žˆ๋Š” ๋Œ€์ƒ์„ ๊ธฐ์กด์˜ ๊ด€์ฐฐ ๋Œ€์ƒ์— ์ถ”๊ฐ€ํ•˜๋ ค๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ๊ด€์ฐฐ ๋Œ€์ƒ์„ ๊ต์ฒดํ•˜๋Š” ๊ฒƒ์ด๋ผ๋ฉด useEffect์— disconnect() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” cleanup function์„ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.
    • useRef๋ฅผ ์ด์šฉํ•˜์—ฌ target node๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์ง€๋งŒ, ref ๊ฐ์ฒด๋Š” ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์–ด๋„ ๋ Œ๋”๋ง์„ ์ผ์œผํ‚ค์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— useEffect์˜ ํ˜ธ์ถœ์„ ์ œ์–ดํ•  ์ˆ˜ ์—†๋‹ค. (useEffect๋Š” ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง ์ดํ›„์— ํ˜ธ์ถœ๋œ๋‹ค.)

Custom Hook์œผ๋กœ ๋งŒ๋“ค๊ธฐ

  • ์™„์„ฑ๋œ Custom Hook ์ฝ”๋“œ

    /**
     * @param onIntersect IntersectionObserver callback: (entries, observer) => { ... }
     * @param options IntersectionObserver options: { root = null, threshode = 0, rootMargin = 0 })
     * @return [target, setTarget]: target node์™€ target node๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜
     */
    const useIntersect = (onIntersect, options) => {
      const [target, setTarget] = useState(null);
    
      /**
       * - ์™ธ๋ถ€์—์„œ ์ธ์ž๋กœ ์ „๋‹ฌ๋ฐ›์€ onIntersect ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ useIntersect ํ˜ธ์ถœ์‹œ IntersectionObserver์˜ callback์œผ๋กœ ์ „๋‹ฌ๋  ํ•จ์ˆ˜ ์ƒ์„ฑ.
       * - useCallback ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด์„œ useIntersect ํ•จ์ˆ˜๊ฐ€ ์ฒ˜์Œ ํ˜ธ์ถœ๋˜์—ˆ์„ ๋•Œ๋งŒ checkIntersect ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ตœ์ ํ™”
       */
      const checkIntersect = useCallback(([entry], observer) => {
        // entries ๋ฐฐ์—ด์˜ ์ฒซ๋ฒˆ์งธ entry์˜ isIntersecting ๊ฐ’์„ ํŒ๋‹จํ•˜์—ฌ ์™ธ๋ถ€์—์„œ ์ „๋‹ฌ๋ฐ›์€ onIntersect ํ˜ธ์ถœ
        if (entry.isIntersecting) {
          onIntersect(entry, observer);
        }
      }, []);
    
      /**
       * - target, options๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด useEffect ์ฝœ๋ฐฑ ์‹คํ–‰
       * - target๊ฐ€ ์žˆ์œผ๋ฉด: observer๋ฅผ ์ƒ์„ฑํ•˜๊ณ  observer๊ฐ€ target๋ฅผ ๊ด€์ฐฐํ•˜๋„๋ก ์—ฐ๊ฒฐ
       * - useEffect๊ฐ€ ์–ธ๋งˆ์šดํŠธ ๋  ๋•Œ observer๊ฐ€ ์žˆ์œผ๋ฉด: observer๊ฐ€ ๊ด€์ฐฐํ•˜๊ณ  ์žˆ๋Š” ๋Œ€์ƒ๊ณผ์˜ ์—ฐ๊ฒฐ์„ ํ•ด์ง€
       */
      useEffect(() => {
        let observer;
    
        if (target) {
          observer = new IntersectionObserver(checkIntersect, {
            ...option,
          });
          observer.observe(target);
        }
    
        return () => observer && observer.disconnect();
      }, [
        target,
        options.root,
        options.threshold,
        options.rootMargin,
        checkIntersect,
      ]);
    
      // [target, setTarget]๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ useIntersect ์ปค์Šคํ…€ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ณณ์—์„œ target๋ฅผ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ
      return [target, setTarget];
    };
  • ์‚ฌ์šฉ ์˜ˆ์ œ

    const App = () => {
      const [_, setTarget] = useIntersect(async (entry, observer) => {
        observer.unobserve(entry.target);
        await fetchItems();
        observer.observe(entry.target);
      }, {});
    
      return (
        ...
        <div ref={setTarget}>more</div>
      )
    }

ref callback ์ด๋ž€?

<div ref={(node) => console.log(node)} />
  • ref callback๋Š” ์ด์ „ ๋ Œ๋”๋ง์—์„œ์˜ node๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์•„
  • ref attribute์˜ ๊ฐ’์œผ๋กœ ์ž‘์„ฑ๋œ ํ•จ์ˆ˜๋ฅผ ref callback(ref์˜ ์ฝœ๋ฐฑํ•จ์ˆ˜)์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.
  • React๋Š” ๋ณ€๊ฒฝ๋œ ref callback์„ ์ „๋‹ฌ ๋ฐ›์„ ๋•Œ๋งˆ๋‹ค ref callback์„ ํ˜ธ์ถœํ•œ๋‹ค.
    • React๋Š” ์ด์ „์— ์‚ฌ์šฉ๋œ ref๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ์ƒˆ ref๋ฅผ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋  ๋•Œ ๋งˆ์šดํŠธ๊ฐ€ ํ•ด์ œ๋œ ํ•จ์ˆ˜๋Š” null์„ ์ธ์ž๋กœ ํ•˜์—ฌ ํ˜ธ์ถœํ•˜๊ณ  ๋งˆ์šดํŠธ๋˜๋Š” ํ•จ์ˆ˜๋Š” DOM node๋ฅผ ์ธ์ž๋กœ ํ•˜์—ฌ ํ˜ธ์ถœํ•œ๋‹ค. ์ฆ‰, ์ด 2๋ฒˆ ํ˜ธ์ถœ๋œ๋‹ค.
  • null์„ ์ธ์ž๋กœ ๋ฐ›์•„ ํ˜ธ์ถœ๋œ ref๋Š” ์ฐธ์กฐ๊ฐ’์„ ์‚ญ์ œํ•œ๋‹ค.
  • ๋งค๊ฐœ๋ณ€์ˆ˜: node
    • DOM node ๋˜๋Š” null๋ฅผ ๊ฐ’์œผ๋กœ ๊ฐ€์ง„๋‹ค.
    • ๋ชจ๋“  ๋ Œ๋”๋ง์—์„œ ref ์ฝœ๋ฐฑ์— ๋Œ€ํ•ด ๋™์ผํ•œ ํ•จ์ˆ˜ ์ฐธ์กฐ๋ฅผ ์ „๋‹ฌํ•˜์ง€ ์•Š๋Š” ํ•œ ์ฝœ๋ฐฑ์€ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋‹ค์‹œ ๋ Œ๋”๋งํ•˜๋Š” ๋™์•ˆ ์ผ์‹œ์ ์œผ๋กœ ๋ถ„๋ฆฌ๋˜๊ณ (์–ธ๋งˆ์šดํŠธ) ๋‹ค์‹œ ์—ฐ๊ฒฐ๋œ๋‹ค.

React.lazy()๋ž€?

routes.tsx์—์„œ route ๊ฒฝ๋กœ์— ํ•ด๋‹นํ•˜๋Š” ํŽ˜์ด์ง€๋“ค์„ import ๋ฐ›์•„์˜ฌ ๋•Œ React.lazy()๋ฅผ ์ด์šฉํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ถˆ๋Ÿฌ์˜ค๋Š”๋ฐ, ๊ทธ๋ ‡๋‹ค๋ฉด React.lazy()๋Š” ๋ญ˜๊นŒ?

  • lazy()๋Š” React์—์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋™์ ์œผ๋กœ ๋กœ๋“œํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ํ•จ์ˆ˜๋กœ, ์ฝ”๋“œ ๋ถ„ํ• ์— ์ฃผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.
    • ์ฝ”๋“œ ๋ถ„ํ• (Code Splitting): ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ดˆ๊ธฐ ๋กœ๋”ฉ ์†๋„๋ฅผ ํ–ฅ์ƒ์‹œํ‚ค๊ธฐ ์œ„ํ•ด ๋ฒˆ๋“ค ํŒŒ์ผ์„ ์ž‘์€ ์กฐ๊ฐ์œผ๋กœ ๋ถ„ํ• ํ•˜๊ณ  ํ•„์š”ํ•œ ์ฝ”๋“œ ์กฐ๊ฐ๋งŒ ํ•„์š”ํ•œ ์‹œ์ ์— ๋™์ ์œผ๋กœ ๋กœ๋”ฉํ•˜๋Š” ๊ธฐ์ˆ .
    • โš ๏ธ ESModule์—์„œ๋Š” ์ฝ”๋“œ ๋ถ„ํ• ์„ ์ง€์›ํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ ๋ถ„ํ• ์„ ์œ„ํ•ด์„œ๋ผ๋ฉด lazy ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. (๋•Œ๋ฌธ์— ์ถ”ํ›„ ๋ณธ ๊ฐ•์˜์—์„œ vite-plugin-next-react-router๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  route๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•  ๋•Œ๋Š” lazy ํ•จ์ˆ˜๋ฅผ ์ง€์› ๋‹ค.)
  • ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ lazy ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์•ˆ๋œ๋‹ค. ๋ฆฌ์•กํŠธ๋Š” lazy ํ•จ์ˆ˜๋ฅผ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ•„์š”ํ•œ ์ˆœ๊ฐ„ ์ดˆ๊ธฐ์— ํ•œ๋ฒˆ๋งŒ ๋ Œ๋”๋ง์„ ํ•˜๋„๋ก ๋˜์–ด ์žˆ๋Š”๋ฐ, ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ lazy ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด state๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋˜๋ฏ€๋กœ ์ข‹์ง€ ์•Š์€ ์„ฑ๋Šฅ์„ ๊ฐ€์ ธ์˜จ๋‹ค.
  • lazy ํ•จ์ˆ˜๋ฅผ <Suspense>์™€ ํ•จ๊ป˜ ์ž‘์„ฑํ•˜๋ฉด lazy ํ•จ์ˆ˜์— ์˜ํ•ด ์ง€์—ฐ ๋กœ๋“œ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋กœ๋“œ๋˜๋Š” ๋™์•ˆ Suspense ํ•จ์ˆ˜์˜ fallback ํ”„๋กœํผํ‹ฐ์— ์ž‘์„ฑ๋œ ๊ฐ’์„ ์ถœ๋ ฅํ•ด์ค€๋‹ค.
  • ๋„คํŠธ์›Œํฌ ์ƒํƒœ์— ๋”ฐ๋ผ ์ง€์—ฐ ๋กœ๋“œ๋Š” ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜๋„ ์žˆ๋‹ค. ์ด๋Ÿด ๊ฒฝ์šฐ Error Boundaries(์—๋Ÿฌ ๊ฒฝ๊ณ„) ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ์—๋Ÿฌ๊ฐ€ ์˜ˆ์ธก๋˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ wrappingํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋™์ž‘์„ ๋ฉˆ์ถ”๊ฒŒ ํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ž‘์„ฑ ๋ฐฉ๋ฒ•

lazy(loadFunction);
  • loadFunction: Promise ๋˜๋Š” thenable(Promise์™€ ๋น„์Šทํ•œ ์—ญํ• ์„ ํ•˜๋Š” ๊ฐ์ฒด)๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ์ž‘์„ฑํ•  ์ˆ˜ ์—†๋‹ค.
  • ์˜ˆ์‹œ
    const ProductPage = lazy(() => import("./src/product/index"));

<Suspense> ์ปดํฌ๋„ŒํŠธ๋ž€?

  • <Suspense>๋Š” children ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋กœ๋“œ๋ฅผ ์™„๋ฃŒํ•  ๋•Œ๊นŒ์ง€ fallback ํ”„๋กœํผํ‹ฐ์— ์ž‘์„ฑ๋œ ๋Œ€์ฒด ui๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค.
    <Suspense fallback={<Loading />}>children */</Suspense>
    • children: ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ ๋“ฑ ์ง€์—ฐ ๋กœ๋“œ๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š” ์‹ค์ œ ui.
    • fallback: ๋กœ๋“œ๋˜๋Š” ๋™์•ˆ ๋Œ€์ฒด ์ถœ๋ ฅ๋  ui.
  • children์—์„œ ๋ณด๋‹ค ๋น ๋ฅด๊ฒŒ ๋กœ๋“œ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ๋”๋ผ๋„ chilren์ด ๋ชจ๋‘ ๋กœ๋“œ๋  ๋•Œ๊นŒ์ง€ ui๋Š” ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š”๋‹ค.
    • ๋ณด๋‹ค ๋นจ๋ฆฌ ๋กœ๋“œ๋˜๋Š” ์ฝ˜ํ…์ธ ๋ฅผ ๋ฏธ๋ฆฌ ๊ณต๊ฐœํ•˜๊ณ , ๋ชจ๋‘ ๋กœ๋“œ๋˜์–ด๋„ ๋ฏธ๋ฆฌ ๊ณต๊ฐœ๋˜์—ˆ๋˜ ์ฝ˜ํ…์ธ ๋ฅผ ์ˆจ๊ธฐ๊ณ  ์‹ถ์ง€ ์•Š๋‹ค๋ฉด startTransition ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • <Suspense>๊ฐ€ ์ค‘์ฒฉ๋œ ๊ตฌ์กฐ๋กœ ์ž‘์„ฑ๋˜์—ˆ๋‹ค๋ฉด ์ง€์—ฐ ๋กœ๋“œ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด <Suspense>๋งŒ ๋™์ž‘ํ•œ๋‹ค.
  • fallback์œผ๋กœ ์ „ํ˜€ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๊ฒƒ์€ ํŽ˜์ด์ง€๊ฐ€ ๋กœ๋”ฉ๋˜๋Š” ์ค‘์— UI์˜ ๋ณ€๊ฒฝ์ด ์ผ์–ด๋‚˜๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๋–จ์–ด๋œจ๋ฆฌ๋ฏ€๋กœ ์‚ฌ์šฉ์„ ์ง€์–‘ํ•ด์•ผ ํ•œ๋‹ค.

๊ธฐ๋Šฅ

  • ์ฝ˜ํ…์ธ ๊ฐ€ ๋กœ๋“œ๋˜๋Š” ๋™์•ˆ ๋Œ€์ฒด ํ‘œ์‹œ (์Šคํ”ผ๋„ˆ, ์Šค์ผˆ๋ ˆํ†ค ๋“ฑ ์†์‰ฌ์šด ๊ตฌํ˜„)
  • ์ฝ˜ํ…์ธ ๋ฅผ ํ•œ ๋ฒˆ์— ๊ณต๊ฐœ
  • ๋กœ๋“œ๋  ๋•Œ ์ค‘์ฒฉ๋œ ์ฝ˜ํ…์ธ  ํ‘œ์‹œ
  • ์ƒˆ๋กœ์šด ์ฝ˜ํ…์ธ ๊ฐ€ ๋กœ๋“œ๋˜๋Š” ๋™์•ˆ ์˜ค๋ž˜๋œ ์ฝ˜ํ…์ธ  ํ‘œ์‹œ
  • ์ด๋ฏธ ๊ณต๊ฐœ๋œ ์ฝ˜ํ…์ธ ๊ฐ€ ์ˆจ๊ฒจ์ง€๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€
  • ์ „ํ™˜์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ์Œ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
  • ๋‚ด๋น„๊ฒŒ์ด์…˜์—์„œ ์„œ์ŠคํŽœ์Šค ๊ฒฝ๊ณ„ ์žฌ์„ค์ •
  • ์„œ๋ฒ„ ์˜ค๋ฅ˜ ๋ฐ ์„œ๋ฒ„ ์ „์šฉ ์ฝ˜ํ…์ธ ์— ๋Œ€ํ•œ ๋Œ€์ฒด ์ œ๊ณต

createPotal

export function createPortal(
  children: ReactNode,
  container: Element | DocumentFragment,
  key?: null | string
): ReactPortal;

createPortal(children, domNode);
  • createPotal๋Š” ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์˜ DOM ๊ณ„์ธต ๊ตฌ์กฐ ๋ฐ–์— ์žˆ๋Š” DOM ๋…ธ๋“œ(domNode)๋กœ children์„ ๋ Œ๋”๋งํ•˜๋Š” Potal์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋Š” ๋ฉ”์„œ๋“œ์ด๋‹ค. ์ฆ‰, ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์•„๋‹Œ ๋‹ค๋ฅธ ๊ณณ์— children์„ ์ถœ๋ ฅํ•  ํ•„์š”๊ฐ€ ์žˆ์„ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ์ด๋‹ค. (์˜ˆ: modal)
  • children์—๋Š” element, string, fragment ๋“ฑ react๊ฐ€ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ์š”์†Œ๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • domNode๋Š” children์ด ๋ Œ๋”๋ง๋˜๋Š” DOM Element๋ฅผ ๋งํ•˜๋ฉฐ, potal์ด ํ˜ธ์ถœ๋˜๊ธฐ ์ „ domNode๊ฐ€ ๋ฐ˜๋“œ์‹œ ์กด์žฌํ•ด์•ผ ํ•œ๋‹ค.
  • potal์€ ์ถœ๋ ฅ๋  DOM(children)์˜ ๋ฌผ๋ฆฌ์  ์œ„์น˜๋งŒ ๋ณ€๊ฒฝํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฒคํŠธ ์ „ํŒŒ ๋“ฑ์€ React Tree ์ƒ์˜ ์œ„์น˜๋ฅผ ๋”ฐ๋ฅธ๋‹ค.
    • ์ฆ‰, children์€ ์ถœ๋ ฅ๋˜๋Š” ์œ„์น˜๊ฐ€ ์•„๋‹Œ ์„ ์–ธ๋œ ์œ„์น˜(๋ถ€๋ชจ ํŠธ๋ฆฌ)์—์„œ ์ œ๊ณตํ•˜๋Š” ์ปจํ…์ŠคํŠธ๋ฅผ ๊ณต์œ ํ•œ๋‹ค.
    • ์ด๋Š” ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ children์˜ ์ด๋ฒคํŠธ ์ „ํŒŒ๋ฅผ ๊ฐ์ง€ํ•˜๋Š” ๋“ฑ์˜ ๊ด€์—ฌ๊ฐ€ ๊ฐ€๋Šฅํ•จ์„ ๋งํ•˜๋ฉฐ, portal์— ์˜์กดํ•˜์ง€ ์•Š๋Š” ์œ ์—ฐํ•œ ๊ฐœ๋ฐœ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•œ๋‹ค.

useCallback ์ด๋ž€?

useCallback(fn, dependencies): fn;
  • useCallback์ด๋ž€ ๋ฆฌ๋ Œ๋”๋ง ๊ฐ„์— ํ•จ์ˆ˜ ์ •์˜๋ฅผ ์บ์‹œํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์„œ๋“œ์ด๋‹ค.
  • ์ฆ‰, ์ผ๋ฐ˜ ํ•จ์ˆ˜์ฒ˜๋Ÿผ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค ๋งค๋ฒˆ ํŠน์ • ํ•จ์ˆ˜๋ฅผ ์ƒˆ๋กญ๊ฒŒ ์ •์˜ํ•˜๋Š” ํ•˜์ง€ ์•Š๊ณ , ํŠน์ • ํ•จ์ˆ˜๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“ค์ง€ ์•Š๊ณ  ์žฌ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ด๋Š” ๋งค๋ฒˆ ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ตœ์ ํ™”์— ์œ ๋ฆฌํ•˜๋‹ค.
  • React์—์„œ ์ œ๊ณตํ•˜๋Š” useMemo์™€ ๋น„์Šทํ•˜๋‚˜, ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ  ํ•จ์ˆ˜ ๊ฐ์ฒด๋ฅผ ์บ์‹œํ•œ ๋’ค ํ•จ์ˆ˜ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋•Œ๋ฌธ์— ๊ฐœ๋ฐœ์ž๊ฐ€ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์‹œ์ ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • useMemo: ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋œ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋œ ๊ฒฐ๊ณผ๊ฐ’์„ ์บ์‹œํ•˜๋Š” ๋ฉ”์„œ๋“œ์ด๋‹ค.
  • ์ธ์ˆ˜
    • fn
      • ์บ์‹œํ•˜๋ ค๋Š” ํ•จ์ˆ˜ ๊ฐ์ฒด์ด๋‹ค.
      • React๋Š” ์ดˆ๊ธฐ ๋ Œ๋”๋ง ์ค‘์— ํ•จ์ˆ˜ ๊ฐ์ฒด๋ฅผ ์บ์‹œํ•œ ํ›„, ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•  ๋•Œ dependencies(์˜์กด์„ฑ ๋ฐฐ์—ด)์ด ์ด์ „ ๋ Œ๋”๋ง๊ณผ ๋น„๊ตํ•˜์—ฌ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ ๋™์ผํ•œ ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ๋ณ€๊ฒฝ๋˜์—ˆ์œผ๋ฉด ํ˜„์žฌ ๋ Œ๋”๋ง ์ค‘์— ํ•ด์„๋˜๋Š” fn ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•œ ๋’ค ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์บ์‹œํ•œ๋‹ค.
      • useCallback์€ fn์„ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ ๋ฐ˜ํ™˜๋œ fn์œผ๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ํ˜ธ์ถœ ์‹œ์ ์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • dependencies: ์˜์กด์„ฑ ๋ฐฐ์—ด๋กœ, ๋ฐฐ์—ด์˜ ๊ฐ’์„ ์ด์ „ ๋ Œ๋”๋ง๊ณผ ๋น„๊ตํ•˜์—ฌ ์บ์‹œํ•  fn์„ ๊ฒฐ์ •ํ•œ๋‹ค.
  • ๋ฐ˜ํ™˜๊ฐ’: fn ํ•จ์ˆ˜ ๊ฐ์ฒด

๐Ÿš€ React Router

ํ˜„ ํ”„๋กœ์ ํŠธ์˜ router

  • ํ•˜์œ„ ๊ฒฝ๋กœ์— ์žˆ๋Š” ํŽ˜์ด์ง€๋“ค์„ router๋กœ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” react-router์—์„œ ์ œ๊ณตํ•˜๋Š” <Outlet> ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ๋•Œ๋ฌธ์— _layout.tsx์—์„œ <Outlet /> ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” Layout ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ ๋‹ค.
  • routes.tsx์—์„œ GlobalLayout(=Layout)์„ ๋ถˆ๋Ÿฌ์™€ routes์˜ element๋กœ ์ „๋‹ฌํ•˜์—ฌ routes ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ ๋‹ค.
  • ์ด๋ฅผ ํ”„๋กœ์ ํŠธ์˜ ์ง„์ž…์  ํŒŒ์ผ์ธ app.tsx์˜ App ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ์—์„œ useRoutes(routes)๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ route ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•œ๋‹ค.

routes.tsx๋Š” ์–ด๋””์„œ ๋‚˜ํƒ€๋‚œ๊ฑธ๊นŒ?

  • vite-plugin-next-react-router๋Š” ๋ผ์šฐํŠธ ํด๋” ๊ตฌ์กฐ๋ฅผ next์™€ ๋™์ผํ•˜๊ฒŒ ๊ฐ€์ ธ๊ฐˆ ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” third-party library์ด๋‹ค.
  • vite.config.ts ํŒŒ์ผ์—์„œ defineConfig ์ธ์ˆ˜๋กœ ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•  ๋•Œ plugins ํ”„๋กœํผํ‹ฐ์˜ ๋ฐฐ์—ด ๋‚ด๋ถ€์— reactRouterPlugin()์„ ์ „๋‹ฌํ•˜๋ฉด ํ”„๋กœ์ ํŠธ root ํด๋”์— routes.tsx ํŒŒ์ผ์„ ์ž๋™ ์ƒ์„ฑํ•ด์ฃผ๋ฉฐ, route ๊ฒฝ๋กœ์— ํ•ด๋‹นํ•˜๋Š” ํŽ˜์ด์ง€๋“ค ๋˜ํ•œ ์ž๋™์œผ๋กœ routes.tsx ํŒŒ์ผ์— ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

<Routes>

  • ์œ„์น˜๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๋ชจ๋“  <Route>๋ฅผ ์‚ดํŽด๋ณด๊ณ  ์ผ์น˜ํ•˜๋Š” ํ•ญ๋ชฉ์„ ์ฐพ์•„ ํ•ด๋‹น๋˜๋Š” UI๋ฅผ ๋ Œ๋”๋งํ•œ๋‹ค.
  • <Route>์˜ props๋กœ๋Š” path, element๊ฐ€ ์žˆ๋‹ค.
  • ์ค‘์ฒฉ ๋ผ์šฐํŒ… ๊ตฌํ˜„์„ ์œ„ํ•ด <Route> ๋‚ด๋ถ€์— <Route>๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋‚ด๋ถ€์— ์ž‘์„ฑ๋œ <Route>์˜ UI๋Š” <Outlet />์— ์˜ํ•ด ํ‘œ์ถœ๋  ์ˆ˜ ์žˆ๋‹ค.

<Outlet />

  • ํ•˜์œ„ route element๊ฐ€ ๋ Œ๋”๋ง๋˜๋Š” ๋ถ€๋ถ„์œผ๋กœ, ํ•˜์œ„ route element๋ฅผ ๋ Œ๋”๋งํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ƒ์œ„ route element์— <Outlet />์„ ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค.
  • ์ค‘์ฒฉ ๋ผ์šฐํŒ…์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜์—ฌ, ํŽ˜์ด์ง€ ๊ฐ„์— ๊ณตํ†ต์ ์œผ๋กœ ๋ Œ๋”๋ง๋˜์–ด์•ผ ํ•˜๋Š” ๊ฒƒ์ด ์žˆ์„ ๋•Œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋œ๋‹ค.

useRoutes()

declare function useRoutes(
  routes: RouteObject[],
  location?: Partial<Location> | string;
): React.ReactElement | null;
  • <Routes>์™€ ๋™์ผํ•œ ๊ธฐ๋Šฅ์œผ๋กœ, JSX๊ฐ€ ์•„๋‹Œ javascript๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์–ด์กŒ๋‹ค.
  • ๊ฐ์ฒด์˜ ํ”„๋กœํผํ‹ฐ๋กœ๋Š” <Route>์—์„œ ์‚ฌ์šฉํ•˜๋Š” props์™€ ๋™์ผํ•˜๊ฒŒ path, element, children๊ฐ€ ์žˆ๋‹ค.
    • chldren์„ ํ†ตํ•ด ํ•˜์œ„ ๊ฒฝ๋กœ, ์ฆ‰ ์ค‘์ฒฉ๋ผ์šฐํŒ…์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • v6๋ถ€ํ„ฐ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ hook์œผ๋กœ, ์ด์ „์—๋Š” react-router-config ํŒจํ‚ค์ง€์˜ renderRoutes๋กœ ๊ตฌํ˜„ํ–ˆ๋‹ค.
  • ๋ฐ˜ํ™˜๊ฐ’
    • ReactElement: ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋œ routes ๊ฐ์ฒด์˜ ๋ฐฐ์—ด์„ ํ•ด์„ํ•˜์—ฌ ํ˜„์žฌ ์œ„์น˜์™€ path๊ฐ€ ์ผ์น˜ํ•˜๋Š” ReactElement๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    • null: ์ผ์น˜ํ•˜๋Š” path๊ฐ€ ์—†์„ ๊ฒฝ์šฐ.

<Link> vs. useNavigate vs. redirect

<Link>

  • <Link>๋Š” react-router์—์„œ ์ œ๊ณตํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋กœ, ์‚ฌ์šฉ์ž์˜ ์•ก์…˜์œผ๋กœ ์ธํ•œ ํŽ˜์ด์ง€ ์ด๋™์„ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋ฉฐ ๋ธŒ๋ผ์šฐ์ €์— ์ถœ๋ ฅ์‹œ <a> ํƒœ๊ทธ๋กœ ๋ณ€ํ™˜๋˜์–ด ์ถœ๋ ฅ๋œ๋‹ค.
  • ํด๋ฆญ์‹œ ๋ฐ”๋กœ ํŽ˜์ด์ง€ ์ด๋™์ด ์ผ์–ด๋‚˜๋Š”, ์ฆ‰ <a> ํƒœ๊ทธ์™€ ๋™์ผํ•œ ์—ญํ• ์„ ํ•˜๋Š” ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•œ๋‹ค.
  • <a>ํƒœ๊ทธ์™€ ๋‹ฌ๋ฆฌ ์ƒํƒœ๊ฐ’์„ ์ €์žฅํ•˜๊ณ  ์ด๋™ํ•  ํŽ˜์ด์ง€๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

useNavigate

declare function useNavigate(): NavigateFunction;

interface NavigateFunction {
  (
    to: To,
    options?: {
      replace?: boolean;
      state?: any;
      relative?: RelativeRoutingType;
    }
  ): void;
  (delta: number): void;
}
  • useNavigate๋Š” ์ผ๋ฐ˜์ ์ธ ํŽ˜์ด์ง€ ์ด๋™์ด ์•„๋‹Œ, ํŠน์ • ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ํŽ˜์ด์ง€ ์ด๋™์ด ํ•„์š”ํ•˜๊ฑฐ๋‚˜ ์ถ”๊ฐ€ ๋กœ์ง์ด ํ•„์š”ํ•  ๊ฒฝ์šฐ ์‚ฌ์šฉํ•˜๋Š” hook์ด๋‹ค.

  • ๋ฐ˜ํ™˜๊ฐ’์€ ํ•จ์ˆ˜๋กœ, ์ „๋‹ฌํ•˜๋Š” ์ธ์ˆ˜์— ๋”ฐ๋ผ ๋™์ž‘ํ•˜๋Š” ํ˜•ํƒœ๊ฐ€ ๋‹ค๋ฅด๋‹ค.

    • ์ฒซ๋ฒˆ์งธ ์ธ์ˆ˜์— ํŽ˜์ด์ง€ ์ด๋™ ๊ฒฝ๋กœ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด, ์ž‘์„ฑํ•œ ๊ฒฝ๋กœ๋กœ ํŽ˜์ด์ง€ ์ด๋™์„ ํ•ด์ฃผ๋Š” <Link to={path}>์™€ ๋™์ผํ•œ ๋™์ž‘์„ ํ•˜๋ฉฐ, ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜์—๋Š” replace, state ๋“ฑ ๋ถ€๊ฐ€ ์˜ต์…˜์„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

      • replace์˜ ๊ธฐ๋ณธ๊ฐ’์€ false๋กœ, true๋ฅผ ์ „๋‹ฌํ•˜๋ฉด react-router๋Š” ์ด๋™ํ•  url์„ history์— ์ƒˆ๋กญ๊ฒŒ push ํ•˜๋Š” ๋Œ€์‹  ํ˜„์žฌ history๋ฅผ ์ด๋™ํ•  url๋กœ replaceํ•œ๋‹ค. ๋•Œ๋ฌธ์— NavigateFunction์„ ์ด์šฉํ•˜์—ฌ ํŽ˜์ด์ง€๊ฐ€ ์ด๋™ํ•  ๋•Œ replace: true์ผ ๊ฒฝ์šฐ ๋’ค๋กœ ๊ฐ€๊ธฐ ๋ฒ„ํŠผ์„ ์ด์šฉํ•˜์—ฌ ๊ธฐ์กด์˜ ํŽ˜์ด์ง€๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋‹ค.
    • ์ฒซ๋ฒˆ์งธ ์ธ์ˆ˜์— ์ˆซ์ž๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ € history ์ด๋™์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์•„๋ž˜์™€ ๊ฐ™์ด useNavigate๊ฐ€ ๋ฐ˜ํ™˜ํ•œ ํ•จ์ˆ˜์— ์ˆซ์ž -1์„ ์ „๋‹ฌํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €์˜ ๋’ค๋กœ ๊ฐ€๊ธฐ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•œ ๊ฒƒ๊ณผ ๋™์ผํ•œ ๋™์ž‘์„ ํ•œ๋‹ค.

      const navigate = useNavigate();
      
      navigate(-1);

redirect

type RedirectFunction = (url: string, init?: number | ResponseInit) => Response;
  • redirect์€ loaders๋‚˜ actions์—์„œ ์„œ๋ฒ„์˜ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ ๋ฐ›์•„์„œ ํŽ˜์ด์ง€ ์ด๋™์„ ์ˆ˜ํ–‰ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.
  • ๋”ฐ๋ผ์„œ ๋ฆฌ๋””๋ ‰์…˜์ด ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ์‘๋‹ต์ธ ๊ฒฝ์šฐ useNavigate ๋Œ€์‹  loader ๋ฐ action ํ•จ์ˆ˜์—์„œ redirect์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

๐Ÿ“ฅ React Query

์ฐธ๊ณ  ๋ฌธ์„œ: [๋ฒˆ์—ญ] #10: ๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ๋Š” ์ƒํƒœ ๊ด€๋ฆฌ์ž๋‹ค

  • ์บ์‹œ๋ฅผ ์ด์šฉํ•˜์—ฌ ์„œ๋ฒ„ ์ƒํƒœ๋ฅผ ์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” React ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
  • ๋น„๋™๊ธฐ ์ƒํƒœ ๊ด€๋ฆฌ์ž๋กœ, ์–ด๋–ค ํ˜•ํƒœ์˜ ๋น„๋™๊ธฐ ์ƒํƒœ๋„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ๋Œ€๋ถ€๋ถ„ data fetching์„ ํ†ตํ•ด Promise๋ฅผ ๋ฐ˜ํ™˜ ๋ฐ›๊ณ  ์žˆ๋‹ค.
  • React Query๋Š” ๋ฐ์ดํ„ฐ๋ฅผ refetch ํ•ด์˜ค๋Š” ์ „๋žต ์ง€ํ‘œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. (refetchOnMount, refetchOnWindowFocus, refetchOnReconnect ๋“ฑ)
    • ์ž๋™์œผ๋กœ refetch ์‹œ์ ์„ ์ง€์ •ํ•˜๋Š” ๊ฒƒ ์™ธ์— queryClient.invalidateQueries๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ˆ˜๋™์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฌดํšจํ™”ํ•˜๋Š” ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • fetching ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ

React Query๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์ „ ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๊ด€๋ฆฌ๋˜์—ˆ๋‹ค.

  1. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋งˆ์šดํŠธ ์‹œ์ ์— data๋ฅผ fetchํ•˜๊ณ  ์ „์—ญ ์ƒํƒœ๋กœ ์ €์žฅํ•˜์—ฌ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๊ฐ€ data๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
    • ์ด ๋ฐฉ๋ฒ•์€ fetch๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ž์ฃผ ์—…๋ฐ์ดํŠธ ํ•˜๊ธฐ ์–ด๋ ต๋‹ค.
    • ์ „์—ญ ์ƒํƒœ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์€ ๊ฒƒ์„ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.
  2. data๊ฐ€ ํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ์˜ ๋งˆ์šดํŠธ ์‹œ์ ์— data๋ฅผ fetchํ•˜๊ณ  ์ง€์—ญ ์ƒํƒœ๋กœ ๊ด€๋ฆฌํ•œ๋‹ค.
    • ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ž์ฃผ ๋งˆ์šดํŠธ๋  ๊ฒฝ์šฐ ์žฆ์€ ํ†ต์‹ ์ด ๋ฐœ์ƒ๋œ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.

React Query์˜ ๋‚ด๋ถ€ ๋™์ž‘

์ฐธ๊ณ  ๋ฌธ์„œ: Inside React Query ํ•œ๊ธ€ ๋ฒˆ์—ญ

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘์‹œ QueryClient์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜์—ฌ QueryClientProvider๋กœ ์ปดํฌ๋„ŒํŠธ์— ๋ฐฐ๋ถ€ํ•˜๋ฉด React Query ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • QueryClient๋กœ ์ƒ์„ฑ๋œ ์ธ์Šคํ„ด์Šค๋Š” QueryCache, MutationCache์˜ ์ปจํ…Œ์ด๋„ˆ๋กœ, query์™€ mutation์„ ์กฐ์ž‘ํ•˜๋Š” ๋ฉ”์„œ๋“œ์™€ ์บ์‹œ ์ž‘์—…์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
    • ์บ์‹œ(cache): ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ๋ณต์‚ฌํ•ด๋†“๋Š” ์ž„์‹œ ์žฅ์†Œ๋กœ, ์บ์‹œ์— ์ ‘๊ทผํ•˜๋Š” ์‹œ๊ฐ„์— ๋น„ํ•ด ์›๋ณธ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋Š” ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆด ๊ฒฝ์šฐ ์‹œ๊ฐ„์„ ์ ˆ์•ฝํ•˜๊ธฐ ์œ„ํ•ด ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.
  • QueryCache๋Š” ์•ˆ์ •์ ์ด๊ณ  ์ง๋ ฌํ™”๋œ queryKeys(queryKeyHash) ๋ฒ„์ „์˜ key์™€ Query ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค์ด์ž ๋ฉ”๋ชจ๋ฆฌ ๋‚ด ๊ฐ์ฒด(in-memory object)์ธ value๋กœ ์ด๋ค„์ ธ์žˆ๋‹ค.
    • React Query๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ์—๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋‹ค๋ฅธ ๊ณณ์—๋Š” ์ €์žฅํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ์บ์‹œ๊ฐ€ ์‚ฌ๋ผ์ง„๋‹ค. ์ด๋ฅผ ์›์น˜ ์•Š์œผ๋ฉด ๋‹ค๋ฅธ ์™ธ๋ถ€ ์ €์žฅ์†Œ์— ์ €์žฅํ•˜๋Š” persistQueryClient๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์บ์‹œ์—๋Š” ์ฟผ๋ฆฌ๋“ค์ด ์žˆ๋Š”๋ฐ ์ด ์ฟผ๋ฆฌ์—์„œ ๋Œ€๋ถ€๋ถ„์˜ ๋กœ์ง๋“ค์ด ์‹คํ–‰๋œ๋‹ค. ์—ฌ๊ธฐ์—๋Š” ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ๋ชจ๋“  ์ •๋ณด(๋ฐ์ดํ„ฐ, ์ƒํƒœ ํ•„๋“œ, ๋งˆ์ง€๋ง‰ fetching์ด ๋ฐœ์ƒ๋˜์—ˆ์„ ๋•Œ ๋“ฑ์˜ ๋ฉ”ํƒ€ ์ •๋ณด)์™€ ์ฟผ๋ฆฌ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์žฌ์‹œ๋„, ์ทจ์†Œ, ์ค‘๋ณต ์ œ๊ฑฐ๋ฅผ ํ•˜๋Š” ๋กœ์ง๋„ ํฌํ•จ๋œ๋‹ค.
  • Query๋Š” Observer๋ฅผ ํ†ตํ•ด ๋ˆ„๊ฐ€ ์ฟผ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ตฌ๋…ํ•˜๊ณ  ์žˆ๋Š”์ง€ ์•Œ๊ณ , ํ•ด๋‹น ๊ด€์ฐฐ์ž์—๊ฒŒ ๋ชจ๋“  ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์•Œ๋ฆด ์ˆ˜ ์žˆ๋‹ค.
    • Observer๋Š” useQuery๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์ƒ์„ฑ๋˜๋ฉฐ useQuery์— queryKey๋กœ ์ „๋‹ฌ๋œ ๋‹จ ํ•˜๋‚˜์˜ ์ฟผ๋ฆฌ๋งŒ์„ ๊ตฌ๋…ํ•œ๋‹ค. ์ฆ‰, Observer๋Š” QueryCache์— ์žˆ๋Š” query๋ฅผ ๊ตฌ๋…ํ•œ๋‹ค.
      • Observer๋Š” Query ์—…๋ฐ์ดํŠธ๋ฅผ ์ปดํฌ๋„ŒํŠธ์—๊ฒŒ ์•Œ๋ ค์•ผ ํ•˜๋Š”์ง€ ๊ฒฐ์ •ํ•œ๋‹ค.
      • Observer๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์‚ฌ์šฉ ์ค‘์ธ query์˜ ์†์„ฑ๋“ค์„ ์•Œ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ด€๋ จ ์—†๋Š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์•Œ๋ฆด ํ•„์š”๊ฐ€ ์—†๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฐ์ดํ„ฐ ํ•„๋“œ๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ๋ฐฑ๊ทธ๋ผ์šด๋“œ refetch์—์„œ isFetching์ด ๋ณ€๊ฒฝ๋  ๋•Œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค์‹œ ๋ Œ๋”๋งํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ์ด๋ ‡๋“ฏ Observer๋Š” ๋งŽ์€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฉฐ, ๋Œ€๋ถ€๋ถ„์˜ ์ตœ์ ํ™”๊ฐ€ ์ด๋ค„์ง€๋Š” ๊ณณ์ด๊ธฐ๋„ ํ•˜๋‹ค.
    • Observer๊ฐ€ ์—†๋Š” ์ฟผ๋ฆฌ๋ฅผ ๋น„ํ™œ์„ฑ ์ฟผ๋ฆฌ๋ผ๊ณ  ํ•œ๋‹ค. ์ด ์ฟผ๋ฆฌ๋Š” ์—ฌ์ „ํžˆ ์บ์‹œ์— ์žˆ์ง€๋งŒ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์ง€ ์•Š๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค. React Query ๊ฐœ๋ฐœ ๋„๊ตฌ์—์„œ๋Š” ์ฟผ๋ฆฌ๋ฅผ ๊ตฌ๋…ํ•˜๊ณ  ์žˆ๋Š” Observer์˜ ๊ฐœ์ˆ˜๋ฅผ ํ‘œ๊ธฐํ•˜๊ณ , ๋น„ํ™œ์„ฑ ์ฟผ๋ฆฌ์˜ ๊ฒฝ์šฐ ํšŒ์ƒ‰์œผ๋กœ ํ‘œ์‹œํ•ด๋‘์—ˆ๋‹ค.

image

ํ™˜๊ฒฝ ์„ค์ • (v4)

์ฐธ๊ณ  ๋ฌธ์„œ: Breaking change - React Query v4

  • react-query package ์ด๋ฆ„์ด ๋ณ€๊ฒฝ๋˜์–ด ์„ค์น˜์‹œ ๋ช…๋ น์–ด์™€ import์‹œ ๊ฒฝ๋กœ๊ฐ€ ๋ณ€๊ฒฝ๋จ

    #๊ธฐ์กด
    npm i react-query
    
    #ํ˜„์žฌ
    npm i @tanstack/react-query
    // ๊ธฐ์กด
    import { useQuery } from "react-query";
    
    // ํ˜„์žฌ
    import { useQuery } from "@tanstack/react-query";
  • devtools ์‚ฌ์šฉ์„ ์›ํ•˜๋ฉด package ์„ค์น˜๋ฅผ ํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ณ€๊ฒฝ๋จ

    npm i @tanstack/react-query-devtools
    // ๊ธฐ์กด
    import { ReactQueryDevtools } from "react-query/devtools";
    
    // ํ˜„์žฌ
    import { ReactQueryDevtools } from "@tanstack/react-query-devtools";

QueryClient๋ž€?

  • react-query์—์„œ ์ œ๊ณตํ•˜๋Š” api๋กœ ์บ์‹œ์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, react query ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ง„์ž…์ ์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
  • QueryCache์™€ MutationCache์˜ ์ปจํ…Œ์ด๋„ˆ๋กœ, ๋ชจ๋“  query ๋ฐ mutation์— ๋Œ€ํ•ด ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ๊ธฐ๋ณธ๊ฐ’์„ ์†Œ์œ ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ์บ์‹œ ์ž‘์—…์„ ์œ„ํ•œ ํŽธ๋ฆฌํ•œ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•œ๋‹ค. (๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ cache์— ์ง์ ‘ ์ ‘๊ทผํ•˜์ง€ ์•Š๊ณ  QueryClient๋ฅผ ํ†ตํ•ด ์ ‘๊ทผํ•œ๋‹ค.)
  • query๋ฅผ fetch ๋ฐ›๊ฑฐ๋‚˜ ์บ์‹œํ•˜๊ณ  ์—…๋ฐ์ดํŠธ ํ•˜๋Š” ๋“ฑ ๋‹ค์–‘ํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง„ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํด๋ž˜์Šค๋กœ ๋งŒ๋“ค์–ด์ ธ ์žˆ์–ด new ์—ฐ์‚ฐ์ž์™€ ํ•จ๊ป˜ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.
  • QueryClientProvider๋ฅผ ์ด์šฉํ•˜๋ฉด React Context๋ฅผ ์ด์šฉํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— QueryClient๋ฅผ ๋ฐฐ๋ถ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

options

QueryClient๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์ธ์ˆ˜์— ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•˜์—ฌ ์˜ต์…˜๊ฐ’์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. (queryCache, mutationCache, defaultOptions)

  • queryCache
    • queries
      • staleTime (๊ธฐ๋ณธ๊ฐ’: 0)
        • ๋ฐ์ดํ„ฐ๊ฐ€ ์บ์‹œ์— ์ €์žฅ๋œ ์ดํ›„ ๋‹ค์Œ ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์‹œ๊ฐ„(๋ฐ€๋ฆฌ์ดˆ).
        • ์–ผ๋งˆ๋‚˜ ์ง€๋‚˜์•ผ ๋ฐ์ดํ„ฐ๋ฅผ staleํ•˜๋‹ค๊ณ  ํŒ๋‹จํ•  ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ์˜ต์…˜์œผ๋กœ, ์ง€์ •ํ•œ ์‹œ๊ฐ„ ์ „๊นŒ์ง€๋Š” staleํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์•„๋‹ˆ๋‹ค.
        • stale data: ์‹ ์„ ํ•˜์ง€ ์•Š์€, ์ฆ‰ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋งํ•œ๋‹ค. === ์บ์‹œ๊ฐ€ ๋งŒ๋ฃŒ๋œ ๋ฐ์ดํ„ฐ
        • query๊ฐ€ ์ตœ์‹  ์ƒํƒœ์ธ ๋ฐ์ดํ„ฐ๋Š” ํ•ญ์ƒ ์บ์‹œ์—์„œ๋งŒ ์ฝํžˆ๋ฉฐ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.
        • stale ๋ฐ์ดํ„ฐ ๋˜ํ•œ ์บ์‹œ์—์„œ ๊ฐ€์ง€์˜ค์ง€๋งŒ, ํŠน์ •์กฐ๊ฑด์—์„œ refetch๊ฐ€ ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๋‹ค.
      • cacheTime (๊ธฐ๋ณธ๊ฐ’: Infinity)
        • ์บ์‹œ์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ์˜ ์œ ํšจ ์‹œ๊ฐ„(๋ฐ€๋ฆฌ์ดˆ). ์ฆ‰, ๋น„ํ™œ์„ฑ ์ฟผ๋ฆฌ๊ฐ€ ์บ์‹œ์—์„œ ์ œ๊ฑฐ๋  ๋•Œ๊นŒ์ง€์˜ ์‹œ๊ฐ„์ด๋‹ค.
        • query๋Š” ๋“ฑ๋ก๋œ ๊ด€์ฐฐ์ž(Observer)๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ๋Š” ์ฆ‰์‹œ ๋น„ํ™œ์„ฑ ์ƒํƒœ๋กœ ์ „ํ™˜๋œ๋‹ค. ์ด๋•Œ ํ•ด๋‹น ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ๋Š” ์–ธ๋งˆ์šดํŠธ(unmounted)๋œ๋‹ค.
        • cacheTime์ด ์ง€๋‚˜๋ฉด query๋Š” ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ์— ์˜ํ•ด ์‚ญ์ œ๋œ๋‹ค.
      • refetchInterval (๊ธฐ๋ณธ๊ฐ’: false): ์ฃผ๊ธฐ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ค๋Š” ์‹œ๊ฐ„(๋ฐ€๋ฆฌ์ดˆ).
      • refetchIntervalInBackground (๊ธฐ๋ณธ๊ฐ’: false): ์ฐฝ์ด ๋น„ํ™œ์„ฑํ™”๋˜์—ˆ์„ ๋•Œ์—๋„ refetchInterval์„ ๊ณ„์† ์‹คํ–‰ํ• ์ง€ ์—ฌ๋ถ€.
      • refetchOnWindowFocus (๊ธฐ๋ณธ๊ฐ’: true): ์ฐฝ์ด ํ™œ์„ฑํ™”๋˜์—ˆ์„ ๋•Œ์—๋„ refetchInterval์„ ๊ณ„์† ์‹คํ–‰ํ• ์ง€ ์—ฌ๋ถ€.
      • refetchOnMount (๊ธฐ๋ณธ๊ฐ’: true): ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฒ˜์Œ ๋งˆ์šดํŠธ๋  ๋•Œ๋งˆ๋‹ค ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ฌ์ง€ ์—ฌ๋ถ€.
      • refetchOnReconnect (๊ธฐ๋ณธ๊ฐ’: true): ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ์ด ๋‹ค์‹œ ํ™œ์„ฑํ™”๋  ๋•Œ๋งˆ๋‹ค ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ฌ์ง€ ์—ฌ๋ถ€.
      • retry (๊ธฐ๋ณธ๊ฐ’: 3): ์š”์ฒญ์ด ์‹คํŒจํ•  ๊ฒฝ์šฐ ์ตœ๋Œ€ ์žฌ์‹œ๋„ ํšŸ์ˆ˜.
      • retryDelay (๊ธฐ๋ณธ๊ฐ’: (attempt) => Math.min(1000 * 2 ** attempt, 30000)): ์žฌ์‹œ๋„ ๊ฐ„๊ฒฉ์„ ๊ณ„์‚ฐํ•˜๋Š” ํ•จ์ˆ˜.
      • retryOnMount (๊ธฐ๋ณธ๊ฐ’: true): ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฒ˜์Œ ๋งˆ์šดํŠธ๋  ๋•Œ๋งˆ๋‹ค ์š”์ฒญ์„ ๋‹ค์‹œ ์‹œ๋„ํ• ์ง€ ์—ฌ๋ถ€.
      • retryOnWindowFocus (๊ธฐ๋ณธ๊ฐ’: true): ์ฐฝ์ด ํ™œ์„ฑํ™”๋˜์—ˆ์„ ๋•Œ์—๋„ ์š”์ฒญ์„ ๋‹ค์‹œ ์‹œ๋„ํ• ์ง€ ์—ฌ๋ถ€.
      • suspense (๊ธฐ๋ณธ๊ฐ’: false): ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐํ•˜๋Š” ๋Œ€์‹ , Suspense๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ ์ฒ˜๋ฆฌํ• ์ง€ ์—ฌ๋ถ€.
      • useErrorBoundary (๊ธฐ๋ณธ๊ฐ’: false): ErrorBoundary๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์š”์ฒญ์ด ์‹คํŒจํ–ˆ์„ ๋•Œ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ• ์ง€ ์—ฌ๋ถ€.
      • queryFnParamsFilter (๊ธฐ๋ณธ๊ฐ’: undefined): ์ฟผ๋ฆฌ ํ•จ์ˆ˜์— ์ „๋‹ฌ๋˜๋Š” ์ธ์ˆ˜๋ฅผ ํ•„ํ„ฐ๋งํ•˜๋Š” ํ•จ์ˆ˜.
  • mutationCache
    • mutations
      • mutateOptions (๊ธฐ๋ณธ๊ฐ’: {}): mutate() ํ•จ์ˆ˜์— ์ „๋‹ฌ๋˜๋Š” ์˜ต์…˜.
      • throwOnError (๊ธฐ๋ณธ๊ฐ’: false): ์„œ๋ฒ„ ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ์˜ˆ์™ธ๋ฅผ ๋˜์งˆ์ง€ ์—ฌ๋ถ€.
      • useErrorBoundary (๊ธฐ๋ณธ๊ฐ’: false): ErrorBoundary๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์š”์ฒญ์ด ์‹คํŒจํ–ˆ์„ ๋•Œ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ• ์ง€ ์—ฌ๋ถ€.
  • defaultOptions
    • queries: queryKey์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ์˜ต์…˜์ด๋‹ค.
      • staleTime (๊ธฐ๋ณธ๊ฐ’: 0): ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐฑ์‹ ํ•˜๊ธฐ ์ „์— ๋งŒ๋ฃŒ๋˜์–ด์•ผ ํ•˜๋Š” ์‹œ๊ฐ„ (๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„)์„ ์ง€์ •ํ•œ๋‹ค.
      • cacheTime (๊ธฐ๋ณธ๊ฐ’: 0): ๋ฐ์ดํ„ฐ๋ฅผ ์บ์‹œ์— ์ €์žฅํ•  ์‹œ๊ฐ„ (๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„)์„ ์ง€์ •ํ•œ๋‹ค.
      • retry (๊ธฐ๋ณธ๊ฐ’: true): ์„œ๋ฒ„์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ž๋™์œผ๋กœ ์žฌ์‹œ๋„ ์—ฌ๋ถ€๋ฅผ ์ง€์ •ํ•œ๋‹ค.
      • retryDelay (๊ธฐ๋ณธ๊ฐ’: attempt => Math.min(attempt _ 1000, 30 _ 1000)): ์„œ๋ฒ„์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ํ›„ ์žฌ์‹œ๋„ ๊ฐ„๊ฒฉ์„ ์ง€์ •ํ•œ๋‹ค.
      • refetchOnWindowFocus (๊ธฐ๋ณธ๊ฐ’: true): ์œˆ๋„์šฐ ํฌ์ปค์Šค๊ฐ€ ๋˜๋ฉด ์ƒˆ๋กœ๊ณ ์นจ ์—ฌ๋ถ€๋ฅผ ์ง€์ •ํ•œ๋‹ค.
      • refetchInterval (๊ธฐ๋ณธ๊ฐ’: false): ์ฃผ๊ธฐ์ ์œผ๋กœ ์ƒˆ๋กœ๊ณ ์นจ ํ• ์ง€ ์—ฌ๋ถ€๋ฅผ ์ง€์ •ํ•œ๋‹ค. ๊ฐ’์ด false์ด๋ฉด ์ฃผ๊ธฐ์ ์œผ๋กœ ์ƒˆ๋กœ๊ณ ์นจํ•˜์ง€ ์•Š๋Š”๋‹ค. number ํƒ€์ž…์˜ ๊ฐ’์ด๋ฉด ํ•ด๋‹น ๊ฐ’(๋ฐ€๋ฆฌ์ดˆ)๋งˆ๋‹ค ์ฃผ๊ธฐ์ ์œผ๋กœ ์ƒˆ๋กœ๊ณ ์นจ ํ•œ๋‹ค.
      • queryFnParamsFilter (๊ธฐ๋ณธ๊ฐ’: params => params): queryFn์— ์ „๋‹ฌ๋  ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ํ•„ํ„ฐ๋งํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ง€์ •ํ•œ๋‹ค. ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ฟผ๋ฆฌ ํ•จ์ˆ˜์— ํ•„์š”ํ•œ ๋งค๊ฐœ ๋ณ€์ˆ˜๋งŒ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.
    • mutations: mutation์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ์˜ต์…˜์ด๋‹ค.
      • retry (๊ธฐ๋ณธ๊ฐ’: true): ์„œ๋ฒ„์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ž๋™์œผ๋กœ ์žฌ์‹œ๋„ ์—ฌ๋ถ€๋ฅผ ์ง€์ •ํ•œ๋‹ค.
      • retryDelay (๊ธฐ๋ณธ๊ฐ’: attempt => Math.min(attempt _ 1000, 30 _ 1000)): ์„œ๋ฒ„์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ํ›„ ์žฌ์‹œ๋„ ๊ฐ„๊ฒฉ์„ ์ง€์ •ํ•œ๋‹ค.
      • onError (๊ธฐ๋ณธ๊ฐ’: error => console.error(error)): ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ํ˜ธ์ถœํ•  ํ•จ์ˆ˜๋ฅผ ์ง€์ •ํ•œ๋‹ค.

method

getQueryData()

const data = queryClient.getQueryData(queryKey);
  • ๊ธฐ์กด ์ฟผ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋™๊ธฐ ํ•จ์ˆ˜๋กœ, ์ฟผ๋ฆฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด undefined๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • ํ•œ๋ฒˆ์— ์—ฌ๋Ÿฌ ์ฟผ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋ ค๋ฉด getQueriesData()๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
  • ์˜ต์…˜
    • filters?: QueryFilters: Query filter๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ํ”„๋กœํผํ‹ฐ
  • ๋ฐ˜ํ™˜๊ฐ’: data: TQueryFnData | undefined

setQueryData()

queryClient.setQueryData(queryKey, updater);
  • setQueryData()๋Š” ๋น„๋™๊ธฐ๋กœ ๋™์ž‘ํ•˜๋Š” fetchQuery()์™€ ๋‹ฌ๋ฆฌ ์ฟผ๋ฆฌ์˜ ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋™๊ธฐ ํ•จ์ˆ˜๋กœ, ์ฟผ๋ฆฌ๊ฐ€ ์—†์œผ๋ฉด ์ƒ์„ฑ๋œ๋‹ค.
  • ๊ธฐ๋ณธ cacheTime 5๋ถ„ ๋™์•ˆ Query Hook์—์„œ ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ์ฟผ๋ฆฌ๊ฐ€ ๊ฐ€๋น„์ง€ ์ˆ˜์ง‘๋œ๋‹ค.
  • ํ•œ๋ฒˆ์— ์—ฌ๋Ÿฌ ์ฟผ๋ฆฌ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ  Query key๋ฅผ ๋ถ€๋ถ„์ ์œผ๋กœ ์ผ์น˜์‹œํ‚ค๋ ค๋ฉด setQueriesData()๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
  • updater๊ฐ€ undefined์ผ ๊ฒฝ์šฐ ์ฟผ๋ฆฌ ๋ฐ์ดํ„ฐ๋Š” ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š๋Š”๋‹ค.
  • setQueryData() ๋‚ด์—์„œ onSuccess๋Š” ๋ฌดํ•œ๋ฃจํ”„๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ˜ธ์ถœํ•  ์ˆ˜ ์—†๋‹ค. (v4 ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜)
  • setQueryData()๋Š” ์ˆœ์ˆ˜ํ•ด์•ผ ํ•œ๋‹ค. ์ฆ‰, ๋‚ด๋ถ€์—์„œ getQueryData()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ฆ‰๊ฐ์ ์œผ๋กœ ๊ฐ’์„ ๋ณ€๊ฒฝ์‹œํ‚ค๋ฉด ์•ˆ๋œ๋‹ค.
  • ์˜ต์…˜
    • queryKey: QueryKey: Query key
    • updater: TQueryFnData | undefined | ((oldData: TQueryFnData | undefined) => TQueryFnData | undefined): updater๋กœ ํ•จ์ˆ˜๊ฐ€ ์•„๋‹Œ ๊ฐ’์ด ์ „๋‹ฌ๋˜๋ฉด ํ•ด๋‹น ๊ฐ’์œผ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ์—…๋ฐ์ดํŠธ๋˜๊ณ , ํ•จ์ˆ˜๊ฐ€ ์ „๋‹ฌ๋˜๋ฉด ์ด์ „ ๋ฐ์ดํ„ฐ ๊ฐ’์„ ์ˆ˜์‹ ํ•˜๊ณ  ์ƒˆ๋กœ์šด ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

cancelQueries()

  • ํŠน์ • querykey์— ํ•ด๋‹นํ•˜๋Š” ๋‹ค๋ฅธ ์š”์ฒญ์„ ๋ฌด์‹œํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” api
  • Optimistic Update์—์„œ ๋ฐ์ดํ„ฐ refetch๋ฅผ ์ทจ์†Œํ•˜๋Š”๋ฐ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋œ๋‹ค.

QueryClientProvider๋ž€?

  • <QueryClientProvider>๋Š” QueryClient ์ปดํฌ๋„ŒํŠธ provider๋กœ, ํ•˜์œ„ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ QueryClient ์ปดํฌ๋„ŒํŠธ๋ฅผ JSX๋กœ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค.
    • client (ํ•„์ˆ˜): ์ œ๊ณตํ•  QueryClient์˜ ์ธ์Šคํ„ด์Šค
    • contextSharing (๊ธฐ๋ณธ๊ฐ’: false): context๋ฅผ ๊ณต์œ ํ•  ๊ฒƒ์ธ์ง€๋ฅผ ์„ ํƒํ•˜๋Š” ์˜ต์…˜

useQuery๋ž€?

  • useQuery๋Š” react-query์—์„œ ์ œ๊ณตํ•˜๋Š” api๋กœ, query๋ฅผ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ GET ๋ฐ›์„ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
  • useQuery๋Š” ๋น„๋™๊ธฐ๋กœ ๋™์ž‘ํ•œ๋‹ค.
    • useQuery๋Š” ๋ Œ๋”๋ง ์‹œ์ ์— ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋น„๋™๊ธฐ ํ•จ์ˆ˜์ด๋‹ค. ๋”ฐ๋ผ์„œ ์ฒซ ๋ Œ๋”๋ง์—๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค์ง€ ๋ชปํ•˜๊ณ , undefined๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • useQuery๋Š” ์ฒซ ๋ Œ๋”๋ง ๋•Œ ๋ฐ์ดํ„ฐ๋ฅผ ์บ์‹œํ•˜๊ณ , ์ดํ›„์—๋Š” ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์ฒซ ๋ Œ๋”๋ง ์ดํ›„์— useQuery์˜ ๋น„๋™๊ธฐ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ์ƒˆ๋กญ๊ฒŒ ์บ์‹œํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒ๋˜๋ฉฐ, data์—๋Š” ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ์—์„œ ๊ฐ’์„ ์ฝ์–ด์˜จ๋‹ค.
  • ์ฟผ๋ฆฌ๋Š” ์ž๋™์œผ๋กœ ์ˆ˜ํ–‰๋œ๋‹ค.
    • ์ข…์†์„ฑ์„ ์ •์˜ํ•˜์ง€๋งŒ, React Query๋Š” ์ฆ‰๊ฐ์ ์ธ ์ฟผ๋ฆฌ ์‹คํ–‰์ด๋‚˜ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๊ณ  ํŒ๋‹จ๋˜๋Š” ๊ฒฝ์šฐ ์ž๋™์œผ๋กœ background update๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.
    • ๋ฐฑ์—”๋“œ์˜ ์‹ค์ œ ๋ฐ์ดํ„ฐ์™€ ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๋Š” ๋‚ด์šฉ์„ ๋™๊ธฐํ™” ํ•˜๋Š” ๊ฒƒ์— ์ ํ•ฉํ•˜๋‹ค.
  • React Query๋Š” ๋™์‹œ์— ๋ฐœ์ƒํ•˜๋Š” useQuery ์š”์ฒญ์˜ ์ค‘๋ณต์„ ์ œ๊ฑฐํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ๋™์ผํ•œ QueryClientProvider ๋‚ด๋ถ€์—์„œ ๋™์ผํ•œ ๋ Œ๋” ์ฃผ๊ธฐ์— ํ˜ธ์ถœ๋˜๋Š” ๋™์ผํ•œ data fetching์€ ๋ฌด์‹œ๋˜์–ด ํ•œ๋ฒˆ์˜ ๋„คํŠธ์›Œํฌ ์š”์ฒญ๋งŒ ์ผ์–ด๋‚œ๋‹ค.
  • queryKey๋ฅผ ๊ณ ์œ ํ•˜๊ฒŒ ์‹๋ณ„ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋™์ผํ•œ QueryClientProvider ๋‚ด๋ถ€์—์„œ useQuery()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๋ผ๋„ ๋™์ผํ•œ data๋ฅผ fetch ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
    • ์ฆ‰, ์ปดํฌ๋„ŒํŠธ๊ฐ„์˜ ์ƒํƒœ๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๋งค๋ฒˆ useQuery() ํ•˜๋Š” data fetch ํ•จ์ˆ˜์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค custom hooks๋ฅผ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ํšจ์œจ์ ์ด๋‹ค.

์ž‘์„ฑ ๋ฐฉ๋ฒ•

useQuery(queryKey, queryFunction): UseQueryResult;
  • queryKey: (string, number, object)[]: query๋ฅผ ๊ด€๋ฆฌํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” unique key
  • queryFunction: api ํ˜ธ์ถœ์„ ํ•˜๋Š” promise ํ•จ์ˆ˜

useMutation์ด๋ž€?

  • useMutation์€ mutation ์ƒํƒœ๋ฅผ ์ถ”์ ํ•˜๋Š” API๋กœ, ๋น„๋™๊ธฐ๋กœ ๋™์ž‘ํ•œ๋‹ค.
  • loading, error, status field๋ฅผ ์ œ๊ณตํ•˜์—ฌ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๋Š”์ง€ ์ œ๊ณตํ•œ๋‹ค.
  • useQuery์˜ ์ฝœ๋ฐฑ๊ณผ ๋™์ผํ•˜๊ฒŒ onSuccess, onError, onSettled๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • useQuery์™€ ๋‹ฌ๋ฆฌ ์ปดํฌ๋„ŒํŠธ๊ฐ„์˜ ์ƒํƒœ๋ฅผ ๊ณต์œ ํ•˜์ง€ ์•Š์œผ๋ฉฐ, mutation(๋ณ€ํ˜•)์ด ์ž๋™์œผ๋กœ ์ˆ˜ํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค.
    • refetch๋˜๋Š” ์‹œ์ ๋งˆ๋‹ค mutation ๋ฐ์ดํ„ฐ์˜ ๋ณ€๊ฒฝ์ด ์ผ์–ด๋‚˜๋ฉด ์•ˆ๋˜๊ธฐ ๋•Œ๋ฌธ (ex, ๋ธŒ๋ผ์šฐ์ € ์ฐฝ์— ์ดˆ์ ์„ ๋งž์ถœ ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด todo๊ฐ€ ์ƒ์„ฑ๋จ)
    • mutation์„ ์ž๋™์œผ๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” ๋Œ€์‹ , ๋ณ€ํ˜•์„ ํ•˜๊ณ  ์‹ถ์„ ๋•Œ๋งˆ๋‹ค ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜(mutate)๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  • useMutation๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” mutate์™€ mutateAsync๋Š” ๋น„์Šทํ•˜์ง€๋งŒ mutate๋Š” ๋ฐ˜ํ™˜๊ฐ’์ด ์—†๊ณ , mutateAsync๋Š” Promise๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค๋Š” ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค.
    • ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ mutate๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  mutation์˜ ์‘๋‹ต์— ์ ‘๊ทผํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ mutateAsync๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
    • mutateAsync๋Š” promise ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— try/catch๋‚˜ then/catch ์ฒด์ด๋‹์„ ์ด์šฉํ•œ ์—๋Ÿฌ ํ•ธ๋“ค๋ง์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
  • useMutation์˜ ์ฝœ๋ฐฑ์€ mutate์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ณด๋‹ค ๋จผ์ € ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— useMutation์˜ ์ฝœ๋ฐฑ์— UI์™€ ๊ด€๋ จ๋œ ๋กœ์ง์„ ์‹คํ–‰ํ•œ๋‹ค๋ฉด mutate์˜ ์ฝœ๋ฐฑ์ด ์‹คํ–‰๋˜๊ธฐ ์ „์— ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์–ธ๋งˆ์šดํŠธ ๋  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ useMutation์˜ ์ฝœ๋ฐฑ์—๋Š” ์ฟผ๋ฆฌ ๋ฌดํšจํ™” ๋“ฑ ํ•„์ˆ˜์ ์ด๊ณ  ๋…ผ๋ฆฌ์ ์ธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ , mutate์˜ ์ฝœ๋ฐฑ์—๋Š” ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋‚˜ ํ† ์ŠคํŠธ ์•Œ๋ฆผ ๊ฐ™์€ UI ๊ด€๋ จ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
    • custom hook์„ ์‚ฌ์šฉํ•  ๋•Œ์—๋„ ์ฝœ๋ฐฑ์˜ ์—ญํ•  ๋ถ„๋ฆฌ๊ฐ€ ์ž˜ ๋˜์–ด์žˆ์œผ๋ฉด custom hook์˜ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ๋‹ค.

options

  • onMutate?: (variables: unknown, mutation: Mutation) => Promise<unknown> | unknown
    • mutation์ด ์‹œ์ž‘๋˜๊ธฐ ์ „์— ๋™๊ธฐ์ ์œผ๋กœ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜๋กœ, ์„œ๋ฒ„์™€์˜ ํ†ต์‹  ์—†์ด ์ด๋ค„์ง„๋‹ค.
    • onMutate์—์„œ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ’์€ onSuccess, onError, onSettled์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ context๋กœ ์ „๋‹ฌ๋œ๋‹ค.
  • onSuccess?: (data: unknown, variables: unknown, context: unknown, mutation: Mutation) => Promise<unknown> | unknown: ์ผ๋ถ€ mutate๊ฐ€ ์„ฑ๊ณตํ•˜๋ฉด ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜๋กœ, mutate ์ดํ›„ ์„œ๋ฒ„์—์„œ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ’์ด ์ฒซ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜์ธ data๋กœ ๋“ค์–ด์˜จ๋‹ค.
  • onError?: (error: unknown, variables: unknown, context: unknown, mutation: Mutation) => Promise<unknown> | unknown
    • ์ผ๋ถ€ mutate๊ฐ€ ์‹คํŒจํ•˜๋ฉด ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜
    • Optimistic Update ๊ตฌํ˜„์‹œ onMutate์—์„œ ๋ฐ˜ํ™˜ํ•œ context๋ฅผ ์ด์šฉํ•˜์—ฌ ์ด์ „๊ฐ’์œผ๋กœ ๋Œ๋ฆฌ๋Š” ๋กค๋ฐฑ ๋กœ์ง์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • onSettled?: (data: unknown | undefined, error: unknown | null, variables: unknown, context: unknown, mutation: Mutation) => Promise<unknown> | unknown: ์ผ๋ถ€ mutate๊ฐ€ settled, ์ฆ‰ ์„ฑ๊ณต์ด๋“  ์‹คํŒจ๋“  ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜

useMutation์ด query์— ๋Œ€ํ•ด ๋ณ€๊ฒฝํ•œ ๋‚ด์šฉ์„ ๋ฐ˜์˜ํ•˜๋Š” ๋ฐฉ๋ฒ•

mutation์€ query์™€ ์ง์ ‘์ ์œผ๋กœ ์—ฐ๊ฒฐ๋˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ mutation์ด query์— ๋Œ€ํ•ด ๋ณ€๊ฒฝํ•œ ๋‚ด์šฉ์„ ๋ฐ˜์˜ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” 2๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ•˜๋‹ค.

  • invalidateQueries()์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ธฐ์กด์˜ ๋ฐ์ดํ„ฐ๋ฅผ stale data๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  refetch ๋ฐ›๋Š”๋‹ค.

  • invalidateQueries ํ˜ธ์ถœ ๊ฒฐ๊ณผ

    1. ๊ธฐ์กด์˜ ์ฟผ๋ฆฌ๋ฅผ stale data๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.
    2. ํ•ด๋‹น ์ฟผ๋ฆฌ๊ฐ€ useQuery๋ฅผ ํ†ตํ•ด ๋ Œ๋”๋ง๋˜๊ฑฐ๋‚˜ ๋น„์Šทํ•œ Hooks๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ๋ฐ์ดํ„ฐ๋ฅผ refetchingํ•œ๋‹ค.
  • ํด๋ผ์ด์–ธํŠธ์—์„œ ์‚ฌ์šฉ์ž์˜ ์•ก์…˜์— ์˜ํ•ด ์–ด๋–ค ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋™๊ธฐํ™”ํ•  ํ•„์š”๊ฐ€ ์žˆ๋Š”๋ฐ, ์ด๋Ÿฐ ๊ฒฝ์šฐ์— ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค.

  • ์ธ์ˆ˜๋กœ QueryFilters๋ฅผ ์ „๋‹ฌํ•˜์—ฌ query ์š”์ฒญ์˜ ์กฐ๊ฑด์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

    queryClient.invalidateQueries({
      queryKey: [QueryKeys.PRODUCTS],
      exact: false,
      refetchType: "all",
    });
    • exact?: boolean: ์ฟผ๋ฆฌ ํ‚ค๋กœ ์ฟผ๋ฆฌ๋ฅผ ๊ฒ€์ƒ‰ํ•  ๋•Œ, ํฌ๊ด„์ ์ธ ๊ฒ€์ƒ‰์„ ์›ํ•˜๋ฉด false๋ฅผ ์ „๋‹ฌํ•˜๊ณ  ์ •ํ™•ํ•œ ์ฟผ๋ฆฌ ํ‚ค๋ฅผ ๊ฒ€์ƒ‰ํ•˜๋ ค๋ฉด true๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.
    • refetchType?: 'active' | 'inactive' | 'all' | 'none': ๊ธฐ๋ณธ๊ฐ’์€ active๋กœ, ์–ด๋–ค ํƒ€์ž…์˜ ์ฟผ๋ฆฌ๋ฅผ ๋‹ค๋ฃฐ ๊ฒƒ์ธ์ง€ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ง์ ‘ ์—…๋ฐ์ดํŠธ

  • setQueryData()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ฟผ๋ฆฌ ์บ์‹œ๋ฅผ ์ง์ ‘ ์—…๋ฐ์ดํŠธ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • setQueryData()๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ์บ์‹œ์— ๋„ฃ์œผ๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ์„œ๋ฒ„์—์„œ ๋ฐ˜ํ™˜๋œ ๊ฒƒ์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜๋ฏ€๋กœ ํ•ด๋‹น ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋œ๋‹ค.
  • ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ fetch ๋ฐ›์•„์˜ค๊ณ  ์‹ถ์ง€ ์•Š์„ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ, mutation์ด ์„œ๋ฒ„๊ฐ€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋‘ ๋ฐ˜ํ™˜ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ง์ ‘ ์—…๋ฐ์ดํŠธ๋Š” ์„œ๋ฒ„์˜ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์„ ์œ„ํ—˜์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋น„๊ต์  ์•ˆ์ „ํ•˜์ง€ ์•Š์€ ์ ‘๊ทผ ๋ฐฉ์‹์ผ ์ˆ˜ ์žˆ๋‹ค.

๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ(Optimistic Update)

  • Optimistic Update๋Š” ์‚ฌ์šฉ์ž์˜ ์•ก์…˜์— ์˜ํ•ด ํŠน์ • ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ์˜ ๋ณ€๊ฒฝ์ด ๋ฐœ์ƒ๋˜์—ˆ์„ ๋•Œ, ์‹ค์ œ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์„ฑ๊ณต ์‘๋‹ต์„ ๋ฐ›๊ธฐ ์ „์— ์„ฑ๊ณตํ•  ๊ฒƒ์ด๋ผ๋Š” ๋‚™๊ด€์ ์ธ ๋งˆ์ธ๋“œ๋กœ ์„ฑ๊ณตํ–ˆ์„ ๋•Œ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ํ™”๋ฉด์— ๊ตฌํ˜„ํ•œ๋‹ค.
    • ๋น„๊ด€์  ์—…๋ฐ์ดํŠธ(Pessimistic Update): ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ์„œ๋ฒ„ ํ†ต์‹ ์œผ๋กœ, ์‚ฌ์šฉ์ž์˜ ์•ก์…˜์œผ๋กœ ์„œ๋ฒ„ ์š”์ฒญ์ด ๋ฐœ์ƒ๋˜๊ณ  ์‘๋‹ต์„ ๋ฐ›์œผ๋ฉด UI๊ฐ€ ์—…๋ฐ์ดํŠธ ๋˜๋Š” ๋กœ์ง์ด๋‹ค.
  • Optimistic Update๋Š” ๋ณด๋‹ค ๋น ๋ฅธ UI ์—…๋ฐ์ดํŠธ๋กœ ์‚ฌ์šฉ์ž์˜ ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•˜๊ณ , ๋น ๋ฅธ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.
  • ์„œ๋ฒ„์˜ ์‘๋‹ต์ด ์„ฑ๊ณต์ ์ด์ง€ ์•Š์„ ๊ฒฝ์šฐ ์ด์ „ ์ƒํƒœ๋กœ ๋Œ์•„๊ฐ€๊ธฐ ๋•Œ๋ฌธ์— ์—…๋ฐ์ดํŠธ ๋กค๋ฐฑ ๋กœ์ง์„ ํ•จ๊ป˜ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์ด ์žˆ๋‹ค.
  • ์„œ๋ฒ„์— ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ˜์˜๋˜๊ธฐ ์ „์— ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ข…๋ฃŒ๋˜๊ฑฐ๋‚˜ ๋„คํŠธ์›Œํฌ์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธด๋‹ค๋ฉด ๋ฐ์ดํ„ฐ ์†Œ์‹ค์˜ ์œ„ํ—˜์ด ์žˆ์œผ๋ฏ€๋กœ, ๊ณผ๋„ํ•œ ์‚ฌ์šฉ์€ ์ง€์–‘ํ•ด์•ผ ํ•œ๋‹ค.
  • ์ฃผ๋กœ, ์ข‹์•„์š” ๋ฒ„ํŠผ ๊ฐ™์€ ๊ฐ€๋ฒผ์šด ์„œ๋ฒ„ ํ†ต์‹ ์— ์‚ฌ์šฉ๋œ๋‹ค.
์ž‘์„ฑ ๋ฐฉ๋ฒ•
  • useMutation์˜ onMutate ์ฝœ๋ฐฑ์„ ์ด์šฉํ•˜์—ฌ Optimistic Update๋ฅผ ์ œ๊ณตํ•˜๊ณ , onError, onSettled ์ฝœ๋ฐฑ์„ ์ด์šฉํ•˜์—ฌ ๋กค๋ฐฑ ๋กœ์ง์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Optimistic Update ๊ตฌํ˜„ ์‹œ ๋ฐ์ดํ„ฐ ๊ผฌ์ž„ ๋ฐฉ์ง€: cancelQueries() ์‚ฌ์šฉ
    • React Query๊ฐ€ ์ œ๊ณตํ•˜๋Š” refetchOnMount ์˜ต์…˜์˜ ๊ธฐ๋ณธ๊ฐ’์€ true์ด๊ธฐ ๋•Œ๋ฌธ์—, React Query๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ๋  ๋•Œ ๋ฐ์ดํ„ฐ๋ฅผ ์ตœ์‹ ์œผ๋กœ ์—…๋ฐ์ดํŠธ ํ•ด์ฃผ๊ธฐ ์œ„ํ•ด refetch๋ฅผ ํ•œ๋‹ค. ์ด๋•Œ refetch ์‹œ์ ์€ ์ •ํ™•ํ•˜๊ฒŒ ์•Œ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ํƒ€์ด๋ฐ์ด ๊ผฌ์ด๋ฉด optimistic update ๋ฐ์ดํ„ฐ๊ฐ€ ๋จผ์ € ๋ณด์ด๊ณ , ๋‚˜์ค‘์— ์‘๋‹ต๋œ refetch ๋ฐ์ดํ„ฐ(์˜ˆ์ „ ๋ฐ์ดํ„ฐ)๊ฐ€ ์ด๋ฅผ override ๋˜์–ด ํ™”๋ฉด์—๋Š” ์˜ˆ์ „ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋ฟŒ๋ ค์ง€๋Š” ํ˜„์ƒ์ด ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด์„œ๋Š” cancelQueries()๋ฅผ ์ด์šฉํ•˜์—ฌ refetch๋ฅผ ์ทจ์†Œํ•ด์•ผ ํ•œ๋‹ค.
  • Optimistic Update ๊ตฌํ˜„ ํ›„ ์•ˆ์ •ํ™” ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋Š” ๋ฐฉ๋ฒ•
    • onMutate ๋‚ด๋ถ€์— ๊ตฌํ˜„ํ•œ ๋กœ์ง์€ ๋ณ€๊ฒฝ ์ „ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์—…๋ฐ์ดํŠธ๋ฅผ ํ•˜๊ณ  UI์— ์ถœ๋ ฅํ•œ๋‹ค. ์ด ์ž‘์—…์€ ์„œ๋ฒ„์™€์˜ ํ†ต์‹  ์—†์ด ์ด๋ค„์ง€๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋ฒ„์˜ ์ตœ์ข… ์‘๋‹ต๊ณผ ๋‹ค๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ์–ด ์•ˆ์ •ํ™” ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
    • ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ์™€์˜ ๋™๊ธฐํ™”์— ์•ˆ์ •์„ฑ์„ ์ฃผ๊ธฐ ์œ„ํ•ด์„œ๋Š” onMutate ์ดํ›„ onSuccess ์ฝœ๋ฐฑ์— ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋กœ ์บ์‹œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ์ž‘์—…์ด ํ•„์š”ํ•˜๋‹ค.
    • onSuccess ์ฝœ๋ฐฑ์€ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์‹ค์ œ ์‘๋‹ต์„ ๋ฐ›์•˜์„ ๋•Œ, ์ฆ‰ Optimistic Update๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ  ๋‚œ ๋’ค ์„œ๋ฒ„์™€ ๋™๊ธฐํ™”๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ UI๋ฅผ ์—…๋ฐ์ดํŠธํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
    • onSuccess ์ฝœ๋ฐฑ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  onMutate๋งŒ ์ž‘์„ฑํ–ˆ์„ ๊ฒฝ์šฐ, ์„œ๋ฒ„ ์‘๋‹ต์ด ์‹คํŒจํ•œ๋‹ค๋ฉด UI์™€ ์‹ค์ œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋™๊ธฐํ™”๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ onSuccess ๋‚ด๋ถ€์— setQueryData()๋ฅผ ์ด์šฉํ•˜์—ฌ ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋กœ ์บ์‹œ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•ด์ฃผ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

useInfiniteQuery๋ž€?

function useInfiniteQuery<
  TQueryFnData = unknown,
  TError = unknown,
  TData = TQueryFnData,
  TQueryKey extends QueryKey = QueryKey
>({
  queryKey,
  queryFn: ({ pageParam = 1 }) => fetchPage(pageParam),
  ...options,
  getNextPageParam: (lastPage, allPages) => lastPage.nextCursor,
  getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor,
}): UseInfiniteQueryResult<TData, TError>;
  • useInfiniteQuery๋Š” ํ•ด๋‹น ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ ์ˆ˜์‹ (useQuery)์„ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’๋งŒ ๋ณ€๊ฒฝํ•˜์—ฌ ๋ฌดํ•œ์ • ํ˜ธ์ถœํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ์ด๋‹ค.
  • ์ˆ˜์‹ ๋œ ๋ฐ์ดํ„ฐ๋Š” ํŽ˜์ด์ง€ ๋‹จ์œ„๋กœ ์ˆ˜์‹ /๊ด€๋ฆฌํ•˜๋ฉฐ, getNextPageParam, getPreviousPageParam์œผ๋กœ ๋‹ค์Œ ์ˆ˜์‹ ์— ํ•„์š”ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’์„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿงถ Recoil

  • Recoil์€ Facebook์—์„œ ๋งŒ๋“  React๋ฅผ ์œ„ํ•œ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, npm์„ ์ด์šฉํ•˜์—ฌ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

API

<RecoilRoot>

  • <RecoilRoot>๋Š” ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์—๊ฒŒ recoil ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ œ๊ณตํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์ด๋‹ค.
  • <RecoilRoot>๋Š” recoil ์ƒํƒœ๋ฅผ ๊ณต์œ ํ•˜๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋“ค์˜ ์กฐ์ƒ ์ปดํฌ๋„ŒํŠธ์—ฌ์•ผ ํ•œ๋‹ค. -> ํ”„๋กœ์ ํŠธ์˜ root ์ปดํฌ๋„ŒํŠธ์— ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด ํšจ์œจ์ ์ผ ์ˆ˜ ์žˆ๋‹ค.
  • ์—ฌ๋Ÿฌ ๊ฐœ์˜ root๊ฐ€ ๊ฐ™์ด ์กด์žฌํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๊ฐ๊ฐ์˜ root๋Š” ๋ณ„๊ฐœ์˜ ์ƒํƒœ๋ฅผ ๊ฐ€์ง€์ง€๋งŒ, <RecoilRoot>๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ ์กด์žฌํ•  ๊ฒฝ์šฐ ์ƒํƒœ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋‹ค๋ฅธ root์™€ ๊ณต์œ ๋˜๋Š” ๋“ฑ ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋™์ž‘์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•˜๋‚˜์˜ root๋งŒ ์‚ฌ์šฉํ•  ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค.
    • selector ์บ์‹œ๊ฐ™์€ ์บ์‹œ๋“ค์€ root ์‚ฌ์ด์—์„œ ๊ณต์œ ๋  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Š” selector ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ด๋ฏธ ์ƒ์„ฑ๋œ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์„ฑ๋Šฅ ํ–ฅ์ƒ์˜ ์žฅ์ ์ด ์žˆ์ง€๋งŒ, root๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค.
    • root๋“ค์ด ์ค‘์ฒฉ๋œ ๊ตฌ์กฐ๋กœ ์ž‘์„ฑ๋˜์–ด ์žˆ๋‹ค๋ฉด ์ตœํ•˜์œ„์˜ root๊ฐ€ ์ตœ์ƒ์œ„์˜ root๋ฅผ override ํ•  ์ˆ˜ ์žˆ๋‹ค.

<RecoilRoot>์˜ override ์†์„ฑ

  • ๊ธฐ๋ณธ๊ฐ’์€ true๋กœ, override ์†์„ฑ์ด true์ผ ๊ฒฝ์šฐ ํ•ด๋‹น root๋Š” ์ƒˆ๋กœ์šด recoil scope๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
  • override ์†์„ฑ์ด false์ผ ๊ฒฝ์šฐ
    • ์ผ๋ฐ˜์ ์œผ๋กœ override ์†์„ฑ์„ false๋กœ ํ•˜๋Š” ๊ฒƒ์€ recoil์—์„œ ์ œ๊ณตํ•˜๋Š” ๋””๋ฒ„๊น… ๋ฐ ๊ฐœ๋ฐœ ๋„๊ตฌ๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋˜๋ฏ€๋กœ ๊ถŒ์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค.
    • ์žฅ์ 
      • Recoil ๊ฐœ๋ฐœ ๋„๊ตฌ๋Š” Recoil์˜ ์ƒํƒœ ๋ฐ ์•ก์…˜ ๋กœ๊ทธ๋ฅผ ๋ธŒ๋ผ์šฐ์ € ์ฝ˜์†”์— ํ‘œ์‹œํ•˜๋Š”๋ฐ, ๋ฐฐํฌ ์‹œ ๋ณด์•ˆ ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒ๋  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Ÿด ๊ฒฝ์šฐ override ์†์„ฑ์„ false๋กœ ํ•ด์„œ recoil ๊ฐœ๋ฐœ ๋„๊ตฌ๋ฅผ ๋น„ํ™œ์„ฑํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๋‹จ์ 
      • ์ƒํƒœ ๋ณ€๊ฒฝ ์•Œ๋ฆผ ์—†์Œ: atom ๋˜๋Š” selector์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์—์„œ ์ƒํƒœ ๋ณ€๊ฒฝ ์•Œ๋ฆผ์ด ํ‘œ์‹œ๋˜์ง€ ์•Š๋Š”๋‹ค.
      • ๋””๋ฒ„๊น… ๋ชจ๋“œ ์ œํ•œ: ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ์ƒํƒœ ๋””๋ฒ„๊น…, ์Šค๋ƒ…์ƒท ๋“ฑ์˜ ๊ธฐ๋Šฅ์ด ์ œํ•œ๋œ๋‹ค.
      • ์•ก์…˜ ๋กœ๊น… ์ œํ•œ: ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ์•ก์…˜ ๋กœ๊น… ๊ธฐ๋Šฅ์ด ์ œํ•œ๋œ๋‹ค. (์•ก์…˜ ๋กœ๊น… ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ชจ๋“  ์ƒํƒœ ๋ณ€๊ฒฝ ์ž‘์—…์ด ๋กœ๊น…๋˜๋ฏ€๋กœ ๋ฌธ์ œ ํ•ด๊ฒฐ์— ๋„์›€์ด ๋œ๋‹ค.)
      • ์„ฑ๋Šฅ ์ €ํ•˜: ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์™€ ๊ด€๋ จ๋œ ๋ถ€๊ฐ€์ ์ธ ์ž‘์—…์ด ์ˆ˜ํ–‰๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ผ๋ถ€ ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ๋ฐœ์ƒ๋  ์ˆ˜ ์žˆ๋‹ค.

<RecoilRoot>์˜ ๊ตฌ์กฐ

export type RecoilRootProps =
  | {
      initializeState?: (mutableSnapshot: MutableSnapshot) => void;
      override?: true;
      children: React.ReactNode;
    }
  | {
      override: false;
      children: React.ReactNode;
    };

/**
 * Root component for managing Recoil state.  Most Recoil hooks should be
 * called from a component nested in a <RecoilRoot>
 */
export const RecoilRoot: React.FC<RecoilRootProps>;

atom

  • atom์€ ์ „์—ญ ์ƒํƒœ๋กœ, ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ์—์„œ๋“  ์ฐธ์กฐํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ปดํฌ๋„ŒํŠธ๊ฐ€ atom์„ ์ฐธ์กฐํ•˜๋Š” ์ˆœ๊ฐ„๋ถ€ํ„ฐ ์ปดํฌ๋„ŒํŠธ๋Š” atom์„ ๊ตฌ๋…ํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์œผ๋กœ, atom ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด atom์„ ๊ตฌ๋…ํ•˜๊ณ  ์žˆ๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋Š” ๋ฆฌ๋ Œ๋”๋ง ๋œ๋‹ค.
  • atom์˜ ๋ฐ˜ํ™˜๊ฐ’ RecoilState์ด๊ธฐ ๋•Œ๋ฌธ์— ์ปดํฌ๋„ŒํŠธ๊ฐ€ atom์„ ์ฐธ์กฐํ•  ๋•Œ๋Š” useRecoilState ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ธ์ˆ˜์— atom์œผ๋กœ ์„ ์–ธ๋œ state๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค.

atom์˜ ๊ตฌ์กฐ

interface AtomOptionsWithoutDefault<T> {
  key: NodeKey;
  effects?: ReadonlyArray<AtomEffect<T>>;
  effects_UNSTABLE?: ReadonlyArray<AtomEffect<T>>;
  dangerouslyAllowMutability?: boolean;
}
interface AtomOptionsWithDefault<T> extends AtomOptionsWithoutDefault<T> {
  default: RecoilValue<T> | Promise<T> | Loadable<T> | WrappedValue<T> | T;
}
export type AtomOptions<T> =
  | AtomOptionsWithoutDefault<T>
  | AtomOptionsWithDefault<T>;

/** ๊ธฐ๋ณธ atom: RecoilState */
export function atom<T>(options: AtomOptions<T>): RecoilState<T>;

/** ๋‹จ์ˆœํžˆ ๊ฐ’์„ ๊ฐ์‹ธ๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” atom: WrappedValue */
export namespace atom {
  function value<T>(value: T): WrappedValue<T>;
}

์ž‘์„ฑ ๋ฐฉ๋ฒ•

import { atom, useRecoilState } from "recoil";

const atomState = atom<defaultValueType>({
  key: "uniqueKey",
  default: "initValue",
});

const [state, setState] = useRecoilState(atomState);

Selector

  • selector๋Š” Derived state๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๋ฉ”์„œ๋“œ๋กœ, ๋‹ค๋ฅธ atom์ด๋‚˜ selector๋ฅผ ์ฝ์–ด๋“ค์—ฌ ์ƒˆ๋กœ์šด ๊ฐ’์„ ๊ณ„์‚ฐํ•˜๊ณ  ์ด ๊ฐ’์„ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค.
    • Derived state: ๋‹ค๋ฅธ ์ƒํƒœ(state)๋“ค๋กœ ๊ณ„์‚ฐํ•ด์„œ ์–ป์–ด๋‚ด๋Š” ์ƒˆ๋กœ์šด ์ƒํƒœ๊ฐ’์œผ๋กœ, props์™€ ์˜์กด์„ฑ์ด ์žˆ๋Š” state์ด๋‹ค.
    • ์ฃผ์–ด์ง„ ์ข…์†์„ฑ ๊ฐ’ ์ง‘ํ•ฉ์— ๋Œ€ํ•ด ํ•ญ์ƒ ๋™์ผํ•œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ˆœ์ˆ˜ํ•จ์ˆ˜๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.
  • selector๋Š” ์ผ๋ฐ˜์ ์ธ ์ƒํƒœ๊ฐ’์ด ์•„๋‹Œ, ์ฝ๊ธฐ ์ „์šฉ(read-only)๋กœ ์‚ฌ์šฉ๋œ๋‹ค.
  • selector ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋  ๋•Œ ๋งˆ๋‹ค selector ์บ์‹œ๊ฐ€ ์ƒ์„ฑ๋˜๋ฉฐ ์ด ์บ์‹œ๋Š” ๋ฉ”๋ชจ๋ฆฌ์— ๋ณด๊ด€๋˜์–ด ์žฌ์‚ฌ์šฉ๋œ๋‹ค. ์ด๋Š” selector ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค ํ•„์š”๊ฐ€ ์—†์–ด์ ธ ์„ฑ๋Šฅ ํ–ฅ์ƒ์— ๋„์›€์ด ๋œ๋‹ค.
  • return ๊ฐ’์ด RecoilValueReadOnly์ผ ๋•Œ selector ๊ฐ’์„ ์ฐธ์กฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” useRecoilValue ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

selector์˜ ๊ตฌ์กฐ

export interface ReadOnlySelectorOptions<T> {
  key: string;
  get: (opts: {
    get: GetRecoilValue;
    getCallback: GetCallback;
  }) => Promise<T> | RecoilValue<T> | Loadable<T> | WrappedValue<T> | T;
  dangerouslyAllowMutability?: boolean;
  cachePolicy_UNSTABLE?: CachePolicyWithoutEquality;
}

/** ์ฝ๊ธฐ ์ „์šฉ(get)์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ์„ ๋•Œ์˜ ๋ฐ˜ํ™˜๊ฐ’: RecoilValueReadOnly */
export function selector<T>(
  options: ReadOnlySelectorOptions<T>
): RecoilValueReadOnly<T>;

/** ์ฝ๊ธฐ์™€ ์“ฐ๊ธฐ(get, set)๊ฐ€ ๋ชจ๋‘ ํ—ˆ์šฉ๋˜์—ˆ์„ ๋•Œ์˜ ๋ฐ˜ํ™˜๊ฐ’: RecoilState */
export function selector<T>(
  options: ReadWriteSelectorOptions<T>
): RecoilState<T>;

/** ๋‹จ์ˆœํžˆ ๊ฐ’์„ ๊ฐ์‹ธ๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๋ฐ˜ํ™˜๊ฐ’: WrappedValue */
export namespace selector {
  function value<T>(value: T): WrappedValue<T>;
}

์ž‘์„ฑ ๋ฐฉ๋ฒ•

  • selector์˜ ์ธ์ˆ˜๋กœ key ํ”„๋กœํผํ‹ฐ์™€ get ๋ฉ”์„ธ๋“œ๋ฅผ ๊ฐ€์ง„ ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.
  • recoil์˜ get ๋ฉ”์„œ๋“œ๋Š” ๋ฉ”์„œ๋“œ ์ถ•์•ฝํ‘œํ˜„์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋กœ ์ž‘์„ฑํ•œ๋‹ค.
  • selector ๊ฐ’์„ ์ฐธ์กฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” useRecoilValue ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
import { selctor, useRecoilValue } from "recoil";

const mySelector = selector({
  key: "mySelector",
  get: ({ get }) => {
    const value1 = get(myAtom1);
    const value2 = get(myAtom2);

    return value1 + value2;
  },
});

export function Component() {
  const derivedValue = useRecoilValue(mySelector);
  return <div>derivedValue</div>;
}

selectorFamily ๋ฉ”์„œ๋“œ

  • selectorFamily ๋ฉ”์„œ๋“œ๋Š” key ํ”„๋กœํผํ‹ฐ์™€ get, set ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง„ ๊ฐ์ฒด๋ฅผ ์ธ์ˆ˜๋กœ ์ „๋‹ฌํ•˜๋ฉด selector๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ์ด๋‹ค.
  • selectorFamily๋Š” ๊ฒฐ๊ณผ๊ฐ’์ด RecoilState์ด๊ธฐ ๋•Œ๋ฌธ์— useRecoilState ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ selector์˜ ๊ฐ’์„ ์ฐธ์กฐํ•ด์•ผ ํ•œ๋‹ค.
  • selectorFamily๋Š” ์ธ์ˆ˜ ์ž‘์„ฑ ํƒ€์ž…์ด ReadWriteSelectorFamilyOptions์œผ๋กœ get, set ํ”„๋กœํผํ‹ฐ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ (param) => (options) => returnValue ๋ฐฉ์‹์œผ๋กœ ์ž‘์„ฑํ•œ๋‹ค.

selectorFamily์˜ ๊ตฌ์กฐ

export interface ReadWriteSelectorFamilyOptions<
  T,
  P extends SerializableParam
> {
  key: string;
  get: (
    param: P
  ) => (opts: {
    get: GetRecoilValue;
    getCallback: GetCallback;
  }) => Promise<T> | Loadable<T> | WrappedValue<T> | RecoilValue<T> | T;
  set: (param: P) => (
    opts: {
      set: SetRecoilState;
      get: GetRecoilValue;
      reset: ResetRecoilState;
    },
    newValue: T | DefaultValue
  ) => void;
  cachePolicy_UNSTABLE?: CachePolicyWithoutEquality;
  dangerouslyAllowMutability?: boolean;
}

export function selectorFamily<T, P extends SerializableParam>(
  options: ReadWriteSelectorFamilyOptions<T, P>
): (param: P) => RecoilState<T>;

์ž‘์„ฑ ๋ฐฉ๋ฒ•

import { selectorFamily } from "recoil";

const mySelector = selectorFamily({
  key: "mySelector",
  get: ({ get }) => {
    // ...
  },
  set: ({ get, set }, newValue) => {
    // ...
  },
});

useRecoilState()

function useRecoilState<T>(
  recoilState: RecoilState<T>
): [T, SetterOrUpdater<T>];
  • ์ „์—ญ ์ƒํƒœ์ธ atom์— ์ ‘๊ทผํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋ฉ”์„œ๋“œ (์ฝ๊ณ  ์“ฐ๊ธฐ๊ฐ€ ๊ฐ€๋Šฅ)
  • atom์˜ ๊ฐ’์„ ๊ตฌ๋…ํ•˜์—ฌ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋Š” hook์œผ๋กœ, useState์™€ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • atom์— ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋“ฑ๋กํ•˜๋Š” hook์ด๋‹ค.
  • ๋ฐ˜ํ™˜๊ฐ’์€ useState์™€ ๋น„์Šทํ•˜๊ฒŒ [recoilState, setRecoilState]์ด๋‹ค.

useRecoilValue()

function useRecoilValue<T>(recoilValue: RecoilValue<T>): T;
  • setter ํ•จ์ˆ˜ ์—†์ด atom์˜ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์ฆ‰, atom์„ ์ฝ๊ธฐ๋งŒ ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
  • atom์— ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋“ฑ๋กํ•˜๋Š” hook์ด๋‹ค. (atom์ด ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒƒ์„ ๊ตฌ๋…ํ•œ๋‹ค.)

useSetRecoilState()

function useSetRecoilState<T>(recoilState: RecoilState<T>): SetterOrUpdater<T>;
  • setter ํ•จ์ˆ˜๋งŒ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์ฆ‰, atom ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋Š” ๋“ฑ ์“ฐ๊ธฐ๋งŒ ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

useResetRecoilState()

function useResetRecoilState(recoilState: RecoilState<any>): Resetter;
  • atom์„ ์ดˆ๊นƒ๊ฐ’์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

useRecoilCallback()

function useRecoilCallback<Args extends ReadonlyArray<unknown>, Return>(
  fn: (interface: CallbackInterface) => (...args: Args) => Return,
  deps?: ReadonlyArray<unknown>
): (...args: Args) => Return;
  • ์ปดํฌ๋„ŒํŠธ๋ฅผ atom์— ๋“ฑ๋กํ•˜์ง€ ์•Š๊ณ  ๊ฐ’์„ ์ฝ์–ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ด๋ฒคํŠธ์— ๋Œ€ํ•œ ์‘๋‹ต์œผ๋กœ Recoil state์— ์ ‘๊ทผํ•˜๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

๐ŸŒŒ Mocking

์ฐธ๊ณ  ๋ฌธ์„œ

Mocking ์ด๋ž€?

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

์œ„ํ‚ค ๋ฐฑ๊ณผ์—์„œ๋Š” Mock์˜ ์˜๋ฏธ๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ์ •์˜ํ–ˆ๋‹ค.

"๋ชจ์˜ ๊ฐ์ฒด(Mock Object) ๋ž€ ์ฃผ๋กœ ๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์œผ๋กœ ๊ฐœ๋ฐœํ•œ ํ”„๋กœ๊ทธ๋žจ์„ ํ…Œ์ŠคํŠธ ํ•  ๊ฒฝ์šฐ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•  ๋ชจ๋“ˆ๊ณผ ์—ฐ๊ฒฐ๋˜๋Š” ์™ธ๋ถ€์˜ ๋‹ค๋ฅธ ์„œ๋น„์Šค๋‚˜ ๋ชจ๋“ˆ๋“ค์„ ์‹ค์ œ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์‹ค์ œ์˜ ๋ชจ๋“ˆ์„ "ํ‰๋‚ด"๋‚ด๋Š” "๊ฐ€์งœ" ๋ชจ๋“ˆ์„ ์ž‘์„ฑํ•˜์—ฌ ํ…Œ์ŠคํŠธ์˜ ํšจ์šฉ์„ฑ์„ ๋†’์ด๋Š”๋ฐ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ์ฒด์ด๋‹ค. ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค(UI)๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ŠคํŠธ ๋“ฑ๊ณผ ๊ฐ™์ด ์ž๋™ํ™”๋œ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์–ด๋ ค์šด ๋•Œ ๋„๋ฆฌ ์‚ฌ์šฉ๋œ๋‹ค."

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ŠคํŠธ : ์ž๋ฃŒ์˜ ๋ณ€๊ฒฝ์„ ์ˆ˜๋ฐ˜ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋Œ€ํ•œ ์ž‘์—…์„ ํ…Œ์ŠคํŠธ ํ•˜๋Š” ๊ฒฝ์šฐ ํ…Œ์ŠคํŠธ ์ˆ˜ํ–‰ ํ›„ ๋งค๋ฒˆ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ž๋ฃŒ๋ฅผ ์›๋ž˜๋Œ€๋กœ ๋Œ๋ ค๋†”์•ผ ํ•˜๋Š”๋ฐ ์ด๋Ÿด ๊ฒฝ์šฐ ๋ชจ์˜ ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์‘๋‹ต์„ ํ‰๋‚ด๋‚ด์–ด ๋ฐ์ดํ„ฐ์˜ ๋ณ€๊ฒฝ ์—†์ด ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

์ฆ‰, ํ”„๋ก ํŠธ์—”๋“œ์˜ Mocking์ด๋ž€ ๋ฐฑ์—”๋“œ API์— ๋Œ€ํ•œ ์˜์กด์„ฑ์„ ๋‚ฎ์ถ”๊ณ ์ž API๊ฐ€ ํ•„์š”ํ•œ ์‹œ์ ์— ์‹ค์ œ API๊ฐ€ ์•„๋‹Œ ๋ชจ์˜ API๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

Mockingํ•˜๋Š” ๋ฐฉ๋ฒ•

  1. ํ™”๋ฉด์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ์ƒํƒœ๋ฅผ ๋‚ด๋ถ€ ๋กœ์ง์— ์ง์ ‘ Mocking ์ž‘์„ฑ

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

    • ์žฅ์ : ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„œ๋น„์Šค ๋กœ์ง์„ ์ˆ˜์ •ํ•˜์ง€ ์•Š์•„๋„ ๋˜๊ณ , HTTP ๋ฉ”์†Œ๋“œ์™€ ๋„คํŠธ์›Œํฌ ์‘๋‹ต ์ƒํƒœ์— ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์Œ
    • ๋‹จ์ : ์„œ๋ฒ„๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๊ณ , ๋กœ์ปฌ์ด ์•„๋‹Œ ๋‹ค๋ฅธ ๊ณณ์— ๊ณต์œ ๋ฅผ ํ•ด์•ผ ํ•œ๋‹ค๋ฉด ์ถ”๊ฐ€ ํ™˜๊ฒฝ ๊ตฌ์„ฑ ์ž‘์—… ๋“ฑ ๋น„์šฉ๊ณผ ๊ณต์ˆ˜๊ฐ€ ๋งŽ์ด ๋“ค์–ด๊ฐ.
  3. Mocking ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ

๊ฐ•์˜์—์„œ๋Š” 3๋ฒˆ์„ ๋ฐฉ๋ฒ•์„ ์ฑ„ํƒํ•˜์—ฌ, MSW(Mock Service Worker)์™€ GraphQl์„ ์‚ฌ์šฉํ•ด์„œ Mocking์„ ๊ตฌํ˜„ํ–ˆ๋‹ค.

๐Ÿ’พ MSW(Mock Service Worker)๋ž€?

  • MSW๋Š” Service Worker๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„์˜ ์‹ค์ œ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„์„œ ๋ชจ์˜ ์‘๋‹ต(Mocked response)์„ ๋ณด๋‚ด์ฃผ๋Š” API Mocking library๋กœ, Mock ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•˜์ง€ ์•Š์•„๋„ API๋ฅผ ๋„คํŠธ์›Œํฌ ์ˆ˜์ค€์—์„œ Mocking ํ•  ์ˆ˜ ์žˆ๋‹ค.

Service Worker API๋ž€?

  • Service Worker๋Š” ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜, ๋ธŒ๋ผ์šฐ์ € ๋ฐ ๋„คํŠธ์›Œํฌ(์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ) ์‚ฌ์ด์— ์žˆ๋Š” ํ”„๋ก์‹œ ์„œ๋ฒ„ ์—ญํ• ๋กœ, ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„๊ณ , ๋„คํŠธ์›Œํฌ ์‚ฌ์šฉ ๊ฐ€๋Šฅ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ์กฐ์น˜๋ฅผ ์ทจํ•˜๊ณ , ์„œ๋ฒ„์— ์ƒ์ฃผํ•˜๋Š” ์ž์‚ฐ์„ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ์œ„ํ•œ API์ด๋‹ค.
  • ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์™€ ๋ถ„๋ฆฌ๋œ ๋ณ„๋„์˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ(worker context)์—์„œ ์‹คํ–‰์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๊ธฐ์ˆ ๋กœ, DOM์— ์ ‘๊ทผ ๊ถŒํ•œ์ด ์—†๊ณ  JavaScript์™€ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋˜์–ด UI Blocking ์—†์ด ์—ฐ์‚ฐ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. (MSW๊ฐ€ ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, ํ”„๋ ˆ์ž„์›Œํฌ์— ์ข…์†์ ์ด์ง€ ์•Š๊ณ  ํ˜ธํ™˜์„ฑ ๋†’๊ฒŒ ์ž‘๋™ํ•˜๋Š” ์ด์œ )
  • ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์˜ ์ž‘์—…์ž์ด๋ฉฐ, ๋น„๋™๊ธฐ์‹์œผ๋กœ ์„ค๊ณ„๋˜์–ด ์žˆ์Œ
  • ์ฃผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ณณ
    • ๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋™๊ธฐํ™” ๊ธฐ๋Šฅ
    • ํ‘ธ์‹œ ๋ฉ”์„ธ์ง€์— ๋ฐ˜์‘
    • ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„๋Š” ํ–‰์œ„: Service Worker๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ์„œ๋ฒ„ ์‚ฌ์ด์—์„œ Request๋ฅผ ๊ฐ€๋กœ์ฑ„ ์ง์ ‘ Fetch์— ๋Œ€ํ•œ ์ปจํŠธ๋กค์„ ํ•  ์ˆ˜ ์žˆ์Œ (HTTP Request, Response๋ฅผ ๋ณด๊ณ  ์บ์‹ฑ ์ฒ˜๋ฆฌ, ๋กœ๊น… ๋“ฑ)
  • ์‚ฌ์šฉ์ด ์ œํ•œ๋˜๋Š” ๊ฒฝ์šฐ
    • IE์™€ ๊ฐ™์€ ์ผ๋ถ€ ๋ธŒ๋ผ์šฐ์ €
    • ์ค‘๊ฐ„์— ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ์„ ๊ฐ€๋กœ์ฑ„๊ณ  ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ๊ฐ€์กŒ๊ธฐ ๋•Œ๋ฌธ์— ๋ณด์•ˆ์ƒ์˜ ์ด์œ ๋กœ localhost๊ฐ€ ์•„๋‹Œ ํ™˜๊ฒฝ์ด๋ผ๋ฉด HTTPS ๋ณด์•ˆ ํ”„๋กœํ† ์ฝœ ํ™˜๊ฒฝ์—์„œ๋งŒ ๋™์ž‘ํ•จ (FireFox ์ œ์™ธ)

MSW ๋™์ž‘ ๋ฐฉ์‹

  1. App์—์„œ Service Worker API ์„ค์น˜
  2. Service Worker ์„ค์น˜ ์™„๋ฃŒ
  3. App์—์„œ Service Worker์— ์‹ค์ œ ์š”์ฒญ์„ ์ „๋‹ฌ
  4. Service Worker๊ฐ€ ์‹ค์ œ ์š”์ฒญ์„ ๋ณต์‚ฌํ•ด์„œ MSW์— ์ „๋‹ฌ
  5. MSW๋Š” ์š”์ฒญ์— ๋Œ€ํ•œ ๋ชจ์˜ ์‘๋‹ต์„ ์ƒ์„ฑํ•˜์—ฌ Service Worker์— ์‘๋‹ต ์ œ๊ณต
  6. Service Worker๊ฐ€ ์ตœ์ข…์ ์œผ๋กœ App์— ๋ชจ์˜ ์‘๋‹ต ์ „๋‹ฌ

integrate๋ฅผ browser๋กœ ์„ค์ •ํ•  ๋•Œ์™€ node ํ™˜๊ฒฝ์ผ ๋•Œ์˜ ์ฐจ์ด

  • Service Worker๋Š” browser ํ™˜๊ฒฝ์—์„œ๋งŒ ๋™์ž‘ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ browser ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐœ๋ฐœ๊ณผ ๋””๋ฒ„๊น…์„ ํ•˜๊ธฐ์— ํŽธ๋ฆฌํ•˜๋‹ค.
  • node ํ™˜๊ฒฝ์—์„œ์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉํ•  ๋•Œ์ด๋ฉฐ, Jest๋กœ ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

GraphQL์„ ์ด์šฉํ•œ MSW

origin graphq ํŒจํ‚ค์ง€๊ฐ€ ์•„๋‹Œ graphql-tag, graphql-request ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ?

  • graphql-tag: Apollo GraphQL ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ํŒจํ‚ค์ง€๋กœ, GraphQL ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋œ๋‹ค. GraphQL ์ฟผ๋ฆฌ๋ฅผ ๋ฌธ์ž์—ด๋กœ ์ž‘์„ฑํ•˜๋Š” ๋Œ€์‹  ํƒœ๊ทธ๋œ ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด(gql)์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. -> ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์„ ๋†’์ž„
  • graphql-request: ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„๋กœ GraphQL ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ํŒจํ‚ค์ง€๋กœ, fetch API๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑ๋˜์–ด ์žˆ์œผ๋ฉฐ ์ฟผ๋ฆฌ๋ฅผ ๋ณด๋‚ด๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  • ๋‘ ํŒจํ‚ค์ง€์˜ ์œ„์™€ ๊ฐ™์€ ํŠน์ง•์œผ๋กœ graphql-tag์™€ graphql-request๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด GraphQL ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์„œ๋ฒ„์— ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๋ฐ ํ•„์š”ํ•œ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋‹ค๋ฅธ ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ถ”๊ฐ€์ ์ธ ์„ค์ •๊ณผ ๊ตฌ์„ฑ์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ MSW์™€ ํ˜ธํ™˜์„ฑ์ด ๋ณด์žฅ๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค.

GraphQL๋กœ API ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•

MSW๋Š” GraphQL API์— ๋Œ€ํ•œ ์š”์ฒญ์„ ์บก์ณํ•˜๊ธฐ ์œ„ํ•œ Request handlers(query, mutation)์™€ utilities(operation, link)๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ์ด๋ฅผ ์ด์šฉํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„ API๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๊ฐ€์งœ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

Method

  • ์ข…๋ฅ˜
    • query(): GraphQL query์— ๋Œ€ํ•œ ๊ฐ€์งœ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์‘๋‹ต ํ•ธ๋“ค๋Ÿฌ๋กœ, graphql.query(queryName, callbackFunction)์œผ๋กœ ์ž‘์„ฑํ•œ๋‹ค.
    • mutation(): GraphQL mutation์— ๋Œ€ํ•œ ๊ฐ€์งœ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์‘๋‹ต ํ•ธ๋“ค๋Ÿฌ๋กœ, graphql.mutation(queryName, callbackFunction)์œผ๋กœ ์ž‘์„ฑํ•œ๋‹ค.
  • ์ธ์ˆ˜ ์„ค๋ช… (๋‘ ๋ฉ”์„œ๋“œ ๋ชจ๋‘ ๊ณตํ†ต๋จ)
    • queryName: ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ, ์„œ๋ฒ„์—์„œ ์ •์˜ํ•œ GraphQL query name ๋˜๋Š” mutation name๊ณผ ์ผ์น˜ํ•ด์•ผ ํ•œ๋‹ค.
    • callbackFunction: ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋˜๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜์ด๋‹ค. ์ด ํ•จ์ˆ˜๋Š” req, res, ctx ๊ฐ์ฒด๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„ (req, res, ctx) => { return res( /* ๋‚ด์šฉ */ ) }๋กœ ์ž‘์„ฑํ•œ๋‹ค.
      • req: ์š”์ฒญ ๊ฐ์ฒด๋กœ, GraphQL query ๋˜๋Š” mutation, ๋ณ€์ˆ˜ ๋ฐ ๊ธฐํƒ€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•œ๋‹ค.
      • res: ์‘๋‹ต ๊ฐ์ฒด๋กœ, ํด๋ผ์ด์–ธํŠธ์— ๋ฐ˜ํ™˜ํ•  ๋ฐ์ดํ„ฐ๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํฌํ•จํ•œ๋‹ค.
      • ctx: ์ปจํ…์ŠคํŠธ ๊ฐ์ฒด๋กœ, ์‘๋‹ต ๊ฐ์ฒด ๋ฐ ์š”์ฒญ ๊ฐ์ฒด๋ฅผ ์กฐ์ž‘ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•œ๋‹ค.

Utility

  • operation()
    • GraphQL query๋ฅผ ํŒŒ์‹ฑํ•˜๊ณ  ํ•ด๋‹น operation ํƒ€์ž…์„ ์‹๋ณ„ํ•˜์—ฌ MSW ์š”์ฒญ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. ์ฆ‰, query(), mutation() ๋“ฑ ๋ชจ๋“  operation ํƒ€์ž…์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด ์œ ์—ฐํ•˜๋‹ค.
    • graphql.operation(operationType, queryName, callbackFunction): ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ operationType์„ ์ถ”๊ฐ€ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ ์™ธ์—๋Š” ๋‹ค๋ฅธ ์š”์ฒญ ํ•ธ๋“ค๋Ÿฌ์™€ ์ž‘์„ฑ ๋ฐฉ๋ฒ•์ด ๋™์ผํ•˜๋‹ค.
  • link()

๐Ÿ“œ GraphQL ์ด๋ž€?

์ฐธ๊ณ  ๋ฌธ์„œ: GraphQL ๊ฐœ๋…์žก๊ธฐ - ์นด์นด์˜ค Tech

  • GraphQL์€ API๋ฅผ ์‰ฝ๊ฒŒ ์„ค๊ณ„ํ•˜๊ณ  ํ˜ธ์ถœํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ์ฟผ๋ฆฌ ์–ธ์–ด๋กœ, REST API๋ฅผ ๋Œ€์ฒดํ•˜๊ธฐ ์œ„ํ•œ ๋ชฉ์ ์œผ๋กœ ๋งŒ๋“ค์–ด์กŒ๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•œ ์–ธ์–ด์ธ sql๊ณผ ๋‹ฌ๋ฆฌ gql์€ ์›น ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„๋กœ ๋ถ€ํ„ฐ ํšจ์œจ์ ์œผ๋กœ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์ด ๋ชฉ์ ์ด๋‹ค. ๋•Œ๋ฌธ์— sql ๋ฌธ์žฅ์€ ๋ฐฑ์—”๋“œ์—์„œ ์ž‘์„ฑํ•˜๊ณ  ํ˜ธ์ถœํ•˜๋Š” ๋ฐ˜๋ณ€, gql ๋ฌธ์žฅ์€ ์ฃผ๋กœ ํด๋ผ์ด์–ธํŠธ ์‹œ์Šคํ…œ์—์„œ ์ž‘์„ฑํ•˜๊ณ  ํ˜ธ์ถœํ•œ๋‹ค.
  • ์š”์ฒญํ•˜๋Š” ์ฟผ๋ฆฌ๋ฌธ์˜ ๊ตฌ์กฐ์™€ ์‘๋‹ต ๋‚ด์šฉ์˜ ๊ตฌ์กฐ๊ฐ€ ๊ฑฐ์˜ ๋™์ผํ•˜๋‹ค.

GraphQL vs REST API

์ฐธ๊ณ  ๋ฌธ์„œ: GraphQL vs REST API - Apollo Blog

  • REST API๋Š” url, method ๋“ฑ์„ ์กฐํ•ฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์–‘ํ•œ EndPoint๊ฐ€ ์กด์žฌํ•˜์ง€๋งŒ, gql์€ ํ•˜๋‚˜์˜ EndPoint๋งŒ ์กด์žฌํ•œ๋‹ค. ๋˜ํ•œ gql API๋Š” ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋ฐ์ดํ„ฐ์˜ ์ข…๋ฅ˜๋ฅผ ์ฟผ๋ฆฌ ์กฐํ•ฉ์„ ํ†ตํ•ด ๊ฒฐ์ •ํ•œ๋‹ค.
    • EndPoint: ์„œ๋ฒ„ ์š”์ฒญ์„ ๊ตฌ๋ถ„ํ•ด์ฃผ๋Š” url
    • ์˜ˆ์‹œ: REST API์—์„œ๋Š” ๊ฐ Endpoint๋งˆ๋‹ค ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค SQL ์ฟผ๋ฆฌ๊ฐ€ ๋‹ฌ๋ผ์ง€๋Š” ๋ฐ˜๋ฉด, gql API๋Š” gql ์Šคํ‚ค๋งˆ์˜ ํƒ€์ž…๋งˆ๋‹ค ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค SQL ์ฟผ๋ฆฌ๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค.
  • REST API์™€ ๋‹ฌ๋ฆฌ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์ด๋Š” ๋ฐ์ดํ„ฐ ์ „์†ก ์–‘์„ ์ค„์ด๊ณ , ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ ํ•„์š”ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค€๋‹ค.
    • ๋˜ํ•œ, ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์—ฌ๋Ÿฌ ๋ฆฌ์†Œ์Šค๋ฅผ ๋ณ‘๋ ฌ๋กœ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์–ด ์„œ๋ฒ„์—์„œ ๋ณ‘๋ชฉ ํ˜„์ƒ์ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

GraphQL์„ ์‚ฌ์šฉํ•˜๋ฉด ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์—ฌ๋Ÿฌ ๋ฆฌ์†Œ์Šค๋ฅผ ๋ณ‘๋ ฌ๋กœ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋Š” ์ด์œ ?

GraphQL์€ ์—ฌ๋Ÿฌ ํ•„๋“œ๋ฅผ ๊ฐ€์ง€๋Š” ๋‹จ์ผ ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์—ฌ๋Ÿฌ ๋ฆฌ์†Œ์Šค๋ฅผ ํ•œ๋ฒˆ์— ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด ์‚ดํŽด๋ณด์ž.

// GraphQL ์Šคํ‚ค๋งˆ

type User {
  id: ID!
  name: String!
  email: String!
}

type Post {
  id: ID!
  title: String!
  body: String!
  author: User!
}

type Query {
  posts: [Post!]!
  user(id: ID!): User!
}

์œ„ ์˜ˆ์ œ์™€ ๊ฐ™์€ ์Šคํ‚ค๋งˆ์—์„œ ํด๋ผ์ด์–ธํŠธ๋Š” posts ํ•„๋“œ๋ฅผ ์„ ํƒํ•˜์—ฌ ๋ชจ๋“  ๊ฒŒ์‹œ๋ฌผ๊ณผ ํ•ด๋‹น ๊ฒŒ์‹œ๋ฌผ์˜ ์ž‘์„ฑ์ž๋ฅผ ํ•œ ๋ฒˆ์— ์š”์ฒญํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, user ํ•„๋“œ๋ฅผ ์„ ํƒํ•˜์—ฌ ํŠน์ • ์‚ฌ์šฉ์ž์˜ ์ด๋ฆ„, ์ด๋ฉ”์ผ, ์‚ฌ์šฉ์ž๊ฐ€ ์˜ฌ๋ฆฐ ๋ชจ๋“  ๊ฒŒ์‹œ๋ฌผ์„ ์š”์ฒญ ํ•  ์ˆ˜ ์žˆ๋‹ค.

// ์—ฌ๋Ÿฌ ๋ฆฌ์†Œ์Šค๋ฅผ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋Š” ๋‹จ์ผ ์ฟผ๋ฆฌ

query {
  posts {
    id
    title
    author {
      name
    }
  }
  user(id: "123") {
    name
    email
    posts {
      title
    }
  }
}

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

์Šคํ‚ค๋งˆ๋ž€?

  • ์Šคํ‚ค๋งˆ๋Š” GraphQL API๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ์ •์˜ํ•˜๋Š” ๊ตฌ์กฐ์ฒด(structure)๋กœ, gql ์ฟผ๋ฆฌ์˜ ์ง„์ž…์ (EndPoint)์ด๋‹ค.
  • GraphQL์€ ์Šคํ‚ค๋งˆ๋กœ ํƒ€์ž… ์‹œ์Šคํ…œ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ์„ ์ •์˜ํ•œ๋‹ค.
    • GraphQL ์ฟผ๋ฆฌ์—์„œ ํ—ˆ์šฉ๋˜๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ ์œ ํ˜•๊ณผ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์œผ๋ฉฐ, ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ธฐ ์œ„ํ•œ ์ฟผ๋ฆฌ๋‚˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๊ธฐ ์œ„ํ•œ ๋ฎคํ…Œ์ด์…˜ ๋“ฑ์„ ์ •์˜ํ•œ๋‹ค.
    • ์ด๋Š” ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ API์˜ ๊ตฌ์กฐ์™€ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
    • ํด๋ผ์ด์–ธํŠธ๋Š” ์ด ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์š”์ฒญ์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
    • GraphQL์€ ๋Ÿฐํƒ€์ž„์—์„œ ์ฟผ๋ฆฌ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๊ณ , ํ‘œ์ค€ํ™”๋œ JSON ํ˜•์‹์œผ๋กœ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

operation๊ณผ ์ข…๋ฅ˜

  • ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„๋กœ ์ „์†ก๋˜๋Š” GraphQL ์š”์ฒญ์˜ ์œ ํ˜•์„ ๋งํ•˜๋ฉฐ, query, mutation, subscription์ด ์žˆ๋‹ค.
    • query๋ž€ ์„œ๋ฒ„์— ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜๋Š” ์ž‘์—…์œผ๋กœ, REST API์—์„œ์˜ GET ๋ฉ”์„œ๋“œ์— ํ•ด๋‹นํ•œ๋‹ค. (CRUD์˜ R)
    • mutation์ด๋ž€ ์„œ๋ฒ„์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์ƒ์„ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜๋Š” ์ž‘์—…์œผ๋กœ, REST API์—์„œ์˜ POST, PATCH, DELETE ๋ฉ”์„œ๋“œ์— ํ•ด๋‹นํ•œ๋‹ค. (CRUD์˜ CUD)
    • subscription: ๋ฐ์ดํ„ฐ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ตฌ๋…ํ•˜๋Š” ์ž‘์—…์œผ๋กœ, ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
  • operation์€ ์„œ๋ฒ„์—์„œ ์š”์ฒญํ•˜๋Š” ์ž‘์—…์˜ ์œ ํ˜•์„ ๋ช…ํ™•ํ•˜๊ฒŒ ๋‚˜ํƒ€๋‚ด๋ฏ€๋กœ, ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„ ๊ฐ„์˜ ํ†ต์‹ ์„ ํšจ์œจ์ ์œผ๋กœ ๋งŒ๋“ ๋‹ค.

graphql-request ํŒจํ‚ค์ง€์˜ request ๋ฉ”์„œ๋“œ๋ž€?

  • request ๋ฉ”์„œ๋“œ: GraphQL์˜ query๋‚˜ mutation์„ ์ง€์ •ํ•œ HTTP EndPoint(url)๋กœ POST ์š”์ฒญ(๋ณ€๊ฒฝ ๊ฐ€๋Šฅ)์„ ๋ณด๋‚ด๋Š” ๋ฉ”์„œ๋“œ์ด๋‹ค.
  • request ๋ฉ”์„œ๋“œ๋Š” Promise ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— then ์ฒด์ด๋‹์ด๋‚˜ async/await์˜ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
  • request ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ GraphQL์—์„œ๋Š” query์™€ variables๋ฅผ ๋ชจ๋‘ JSON ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ „๋‹ฌํ•œ๋‹ค.

์ž‘์„ฑ ๋ฐฉ๋ฒ•

request(url, query, variables);
  • url: GraphQL์˜ EndPoint === ์š”์ฒญ์ด ์ „์†ก๋  ์„œ๋ฒ„

  • query: ์„œ๋ฒ„์—์„œ ์‘๋‹ตํ•  query ํ˜•ํƒœ

  • variables

    • ๊ฐ์ฒด ํ˜•ํƒœ๋กœ, method, headers, body๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋‹ค.
    • method: ์ž‘์„ฑํ•˜์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ ๊ธฐ๋ณธ๊ฐ’์€ POST์ด๋ฉฐ, GET, PUT, PATCH, DELETE์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
    • body: method, headers์™€ ๋‹ฌ๋ฆฌ body๋ผ๋Š” ์ด๋ฆ„์˜ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š๊ณ , body๋กœ ์ „๋‹ฌํ•  ํ”„๋กœํผํ‹ฐ key/value ์Œ์˜ ๊ฐ์ฒด๋กœ ์ž‘์„ฑํ•œ๋‹ค. -> GraphQL์ด ์„œ๋ฒ„์— ์š”์ฒญํ•  ๋•Œ ์•”๋ฌต์ ์œผ๋กœ ๊ฐ์ฒด ๋‚ด์šฉ์„ HTTP ์š”์ฒญ ๋ฐ”๋””์— ๋‹ด๋Š” ๊ณผ์ •์„ ๊ฑฐ์นœ๋‹ค.
    • variables์— ์ž‘์„ฑํ•œ ํ”„๋กœํผํ‹ฐ๋Š” query๋ฌธ์—์„œ $๋ฅผ ๋ถ™์—ฌ ๋ณ€์ˆ˜๋ช…์œผ๋กœ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋‹ค.

์˜ˆ์‹œ

import { gql } from 'graphql-tag';
import { request } from 'graphql-request';

const query = gql`
  query GET_VALUE($id: string) {
    key1: value1,
    key2: value2,
  }
`;

const variables = {
  method: 'POST',
  headers: {
    Authorization: 'Bearer MY_TOKEN',
  },
  { id: 'ID' },
};

request('/graphql', query, variables);

๐Ÿชช UUID

  • UUID(Universally Unique IDentifier, ๋ฒ”์šฉ ๊ณ ์œ  ์‹๋ณ„์ž)๋Š” ๋„คํŠธ์›Œํฌ ์ƒ์—์„œ ๊ณ ์œ ์„ฑ์„ ๋ณด์žฅํ•˜๋Š” ID๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ํ‘œ์ค€ ๊ทœ์•ฝ์œผ๋กœ, GUID(Globally Unique IDentifier)๋ผ๊ณ ๋„ ๋ถˆ๋ฆฐ๋‹ค.
  • UUID๋Š” 32๊ฐœ์˜ 16์ง„์ˆ˜ ์ˆซ์ž๊ฐ€ ํ•˜์ดํ”ˆ(-)์œผ๋กœ ๊ตฌ๋ถ„๋œ 5๊ฐœ์˜ ๊ทธ๋ฃน์œผ๋กœ ํ‘œ์‹œ๋˜๋ฉฐ, ์ด 36์ž์— ๋Œ€ํ•ด 8-4-4-4-12 ํ˜•์‹์œผ๋กœ ํ‘œํ˜„๋œ๋‹ค.
    • ์˜ˆ์‹œ: 123e4567-e89b-12d3-a456-426614174000
  • ๋…ธ๋“œ ํ™˜๊ฒฝ์—์„œ๋Š” UUID๋ฅผ ์‰ฝ๊ฒŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ œ๊ณตํ•˜์—ฌ, ์†์‰ฝ๊ฒŒ ์„ค์น˜ํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

uuid ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ž€?

UUID๋ฅผ ์‰ฝ๊ฒŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

uuid ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜

์ œ๊ณต๋˜๋Š” API

export const NIL: NIL;
export const parse: parse;
export const stringify: stringify;
export const v1: v1;
export const v3: v3;
export const v4: v4;
export const v5: v5;
export const validate: validate;
export const version: version;
  • uuid.v1(): ํ˜„์žฌ ์‹œ๊ฐ์„ ๊ธฐ์ค€์œผ๋กœ UUID๋ฅผ ์ƒ์„ฑํ•˜๋ฉฐ, ์ด๋Š” uuid๊ฐ€ ์ƒ์„ฑ๋œ ์‹œ๊ฐ๊ณผ MAC์ฃผ์†Œ๋กœ uuid๋ฅผ ์œ ์ถ”ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์•ˆ์ „์„ฑ์ด ๋–จ์–ด์ง„๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.
  • uuid.v3(): MD5 ํ•ด์‹œ ๊ธฐ์ค€์œผ๋กœ UUID๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
  • uuid.v4(): ๋žœ๋ค๊ฐ’์„ ๊ธฐ๋ฐ˜์œผ๋กœ UUID๋ฅผ ์ƒ์„ฑํ•˜๋ฉฐ, ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ์ฃผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ฒ„์ „์ด๋‹ค.
  • uuid.v5(): SHA-1 ํ•ด์‹œ ๊ธฐ์ค€์œผ๋กœ UUID๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

์‚ฌ์šฉ ๋ฐฉ๋ฒ• ์˜ˆ์‹œ

import { v4 as uuidv4 } from "uuid"; // as๋Š” import ๋ฐ›์€ ์š”์†Œ์˜ ๋ณ„์นญ์„ ์ •ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋‹ค๋ฅธ ๋„ค์ด๋ฐ์„ ์ž‘์„ฑํ•ด๋„ ๋œ๋‹ค. -> ex: { v4 as uuid }

const productItem = {
  id: uuidv4(), // import ๋ฐ›์€ uuidv4 ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด import ๋ฐ›์€ ๋ฒ„์ „์— ๋งž๋Š” UUID๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค.
};

ํ•œ๊ณ„

  • ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ๋  ๋•Œ๋งˆ๋‹ค uuid๊ฐ€ ํ˜ธ์ถœ๋˜์–ด uuid๋Š” ๋Š˜ ์ƒˆ๋กœ์šด id๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ธฐ์กด์˜ id๊ฐ’์„ ์œ ์ง€ํ•˜๊ธฐ ์–ด๋ ต๋‹ค. (์˜ˆ์‹œ: ์ƒํ’ˆ id๋ฅผ ์ด์šฉํ•˜์—ฌ ์„œ๋ฒ„์— ์ƒํ’ˆ ์ •๋ณด๋ฅผ GET ์š”์ฒญํ•˜๋Š” ๋กœ์ง์ด๋ผ๋ฉด, ํ•ด๋‹น ํŽ˜์ด์ง€์—์„œ ์ƒˆ๋กœ๊ณ ์นจ์„ ํ–ˆ์„ ๊ฒฝ์šฐ uuid๊ฐ€ ์ƒ์„ฑํ•œ ์ƒํ’ˆ id๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋™์ผํ•œ ์ƒํ’ˆ id๋ฅผ ์„œ๋ฒ„์—์„œ ์ฐพ์ง€ ๋ชปํ•œ๋‹ค.)

๐Ÿ“ TypeScript

  • TypeScript ํŒŒํŠธ์—์„œ ๋ฏธ์ž‘์„ฑ๋œ ๋‚ด์šฉ์€ Learning TypeScript ์ฑ…์„ ์™„๋…ํ•œ ๋’ค ์ž‘์„ฑํ•˜๊ฒ ์Œ

๋ฌธ๋ฒ•

(๋ฏธ์ž‘์„ฑ) as๋ž€?

(๋ฏธ์ž‘์„ฑ) interface์™€ type์˜ ์ฐจ์ด

Pick<type, keys>

  • type์—์„œ ํ”„๋กœํผํ‹ฐ๋“ค์„ ๋ฝ‘์€ ์ง‘ํ•ฉ keys๋ฅผ ์„ ํƒํ•˜์—ฌ type ์œ ํ˜•์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
  • keys๋Š” type์— ์ž‘์„ฑ๋œ ํ”„๋กœํผํ‹ฐ ํ‚ค๋ฅผ ๋ฌธ์ž์—ด๋กœ ์ž‘์„ฑํ•˜๊ณ  |๋ฅผ ์ด์šฉํ•˜์—ฌ ๋‚˜์—ดํ•œ๋‹ค.

์˜ˆ์‹œ

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Pick<Todo, "title" | "completed">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
};

(๋ฏธ์ž‘์„ฑ) Omit

(๋ฏธ์ž‘์„ฑ) Partial

ํƒ€์ž… ์œ ํ˜•

RequestInt Type

RequestInit ์ธํ„ฐํŽ˜์ด์Šค๋Š” fetch() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ์ฒด์˜ ํƒ€์ž…์„ ์ •์˜ํ•œ๋‹ค. ์ด ๊ฐ์ฒด๋Š” HTTP ์š”์ฒญ์„ ๊ตฌ์„ฑํ•˜๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์†์„ฑ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

  • body (BodyInit | null): HTTP ์š”์ฒญ ๋ฐ”๋””์˜ ๋‚ด์šฉ์„ ๋‹ด๋Š” BodyInt ํƒ€์ž…์˜ ๊ฐ์ฒด์ด๋‹ค. ์ด ์†์„ฑ์€ ์ƒ๋žต ๊ฐ€๋Šฅํ•˜๋ฉฐ ์ƒ๋žตํ•  ๊ฒฝ์šฐ HTTP ์š”์ฒญ์—๋Š” ๋ฐ”๋””๊ฐ€ ์—†์Œ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
  • cache (RequestCache): HTTP ์š”์ฒญ์— ๋Œ€ํ•œ ์บ์‹œ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•์„ ๋‚˜ํƒ€๋‚ธ๋‹ค. RequestCache ์—ด๊ฑฐํ˜• ๊ฐ’ ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • credentials (RequestCredentials): HTTP ์š”์ฒญ ์‹œ ํ•จ๊ป˜ ๋ณด๋‚ผ ์ธ์ฆ ์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฌธ์ž์—ด์ด๋‹ค. RequestCredentials ์—ด๊ฑฐํ˜• ๊ฐ’ ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • headers (HeadersInit): HTTP ์š”์ฒญ ํ—ค๋”๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” HeadersInit ํƒ€์ž…์˜ ๊ฐ์ฒด์ด๋‹ค. ๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด, Headers ๊ฐ์ฒด, ๋˜๋Š” ๋‘ ํ•ญ๋ชฉ ๋ฐฐ์—ด์˜ ๋ฐฐ์—ด๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • integrity (string): ์š”์ฒญ ๋ฆฌ์†Œ์Šค์˜ ๋ฌด๊ฒฐ์„ฑ์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฌธ์ž์—ด์ด๋‹ค. ๋Œ€๊ฐœ SHA-256 ํ•ด์‹œ ๊ฐ’์œผ๋กœ ํ‘œํ˜„๋œ๋‹ค
  • keepalive (boolean): HTTP keep-alive ๊ธฐ๋Šฅ ์‚ฌ์šฉ ์—ฌ๋ถ€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ถˆ๋ฆฌ์–ธ ๊ฐ’์ด๋‹ค.
  • method (string): HTTP ์š”์ฒญ ๋ฉ”์„œ๋“œ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฌธ์ž์—ด์ด๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ "GET"์ด๋‹ค.
  • mode (RequestMode): HTTP ์š”์ฒญ์˜ CORS ์ฒ˜๋ฆฌ ์—ฌ๋ถ€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฌธ์ž์—ด์ด๋‹ค. RequestMode ์—ด๊ฑฐํ˜• ๊ฐ’ ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • redirect (RequestRedirect): HTTP ์š”์ฒญ ์ค‘ ๋ฆฌ๋””๋ ‰์…˜ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฌธ์ž์—ด์ด๋‹ค. RequestRedirect ์—ด๊ฑฐํ˜• ๊ฐ’ ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • referrer (string): HTTP ์š”์ฒญ์—์„œ Referrer ์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฌธ์ž์—ด์ด๋‹ค. ๋ณดํ†ต ์ด์ „ ํŽ˜์ด์ง€ URL์„ ์ „๋‹ฌํ•œ๋‹ค.
  • referrerPolicy (ReferrerPolicy): HTTP ์š”์ฒญ์—์„œ Referrer ์ •๋ณด์˜ ์ „๋‹ฌ ๋ฐฉ๋ฒ•์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฌธ์ž์—ด์ด๋‹ค. ReferrerPolicy ์—ด๊ฑฐํ˜• ๊ฐ’ ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • signal (AbortSignal | null): ์š”์ฒญ์„ ์ทจ์†Œํ•  ์ˆ˜ ์žˆ๋Š” AbortSignal ๊ฐ์ฒด๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.
  • window (null): HTTP ์š”์ฒญ์„ ์‹คํ–‰ํ•  Window ๊ฐ์ฒด์ด๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ null ๊ฐ’์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.
interface RequestInit {
  /** A BodyInit object or null to set request's body. */
  body?: BodyInit | null;
  /** A string indicating how the request will interact with the browser's cache to set request's cache. */
  cache?: RequestCache;
  /** A string indicating whether credentials will be sent with the request always, never, or only when sent to a same-origin URL. Sets request's credentials. */
  credentials?: RequestCredentials;
  /** A Headers object, an object literal, or an array of two-item arrays to set request's headers. */
  headers?: HeadersInit;
  /** A cryptographic hash of the resource to be fetched by request. Sets request's integrity. */
  integrity?: string;
  /** A boolean to set request's keepalive. */
  keepalive?: boolean;
  /** A string to set request's method. */
  method?: string;
  /** A string to indicate whether the request will use CORS, or will be restricted to same-origin URLs. Sets request's mode. */
  mode?: RequestMode;
  /** A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect. */
  redirect?: RequestRedirect;
  /** A string whose value is a same-origin URL, "about:client", or the empty string, to set request's referrer. */
  referrer?: string;
  /** A referrer policy to set request's referrerPolicy. */
  referrerPolicy?: ReferrerPolicy;
  /** An AbortSignal to set request's signal. */
  signal?: AbortSignal | null;
  /** Can only be null. Used to disassociate request from any Window. */
  window?: null;
}

SyntheticEvent Type

CartItem ์ปดํฌ๋„ŒํŠธ์—์„œ input์˜ onChange ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ, input์— ์ž‘์„ฑ๋œ amount ๊ฐ’์„ ์ฝ๊ธฐ ์œ„ํ•ด ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋‚ด๋ถ€์—์„œ ์ด๋ฒคํŠธ๋ฅผ ์ผ์œผํ‚จ ๋Œ€์ƒ(e.target)์„ ์ฐธ์กฐํ•ด์•ผ ํ–ˆ๋‹ค. ์ด๋•Œ, ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ž‘์„ฑ๋œ e์˜ ํƒ€์ž…์„ React.SyntheticEvent์œผ๋กœ ์ž‘์„ฑํ–ˆ๋Š”๋ฐ SyntheticEvent๊ฐ€ ์–ด๋–ค ํƒ€์ž…์ธ์ง€ ๋‚ด๋ถ€๋ฅผ ์‚ดํŽด๋ณด์ž.

  • SyntheticEvent ํƒ€์ž…์€ BaseSyntheticEvent ํƒ€์ž…์„ ํ™•์žฅํ•œ ๊ฒƒ์œผ๋กœ, BaseSyntheticEvent์—๋Š” currentTarget, target, preventDefault() ๋“ฑ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ event๊ฐ€ ์ œ๊ณตํ•˜๋Š” ํ”„๋กœํผํ‹ฐ์™€ ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•œ ํƒ€์ž…์ด ์ •์˜๋˜์–ด ์žˆ๋‹ค.
interface BaseSyntheticEvent<E = object, C = any, T = any> {
  nativeEvent: E;
  currentTarget: C;
  target: T;
  bubbles: boolean;
  cancelable: boolean;
  defaultPrevented: boolean;
  eventPhase: number;
  isTrusted: boolean;
  preventDefault(): void;
  isDefaultPrevented(): boolean;
  stopPropagation(): void;
  isPropagationStopped(): boolean;
  persist(): void;
  timeStamp: number;
  type: string;
}

/**
 * currentTarget - a reference to the element on which the event listener is registered.
 *
 * target - a reference to the element from which the event was originally dispatched.
 * This might be a child element to the element on which the event listener is registered.
 * If you thought this should be `EventTarget & T`, see https://github.com/DefinitelyTyped/DefinitelyTyped/issues/11508#issuecomment-256045682
 */
interface SyntheticEvent<T = Element, E = Event>
  extends BaseSyntheticEvent<E, EventTarget & T, EventTarget> {}

๐Ÿ“ซ HTTP ํ†ต์‹ 

Access-Control-Allow-Origin๋ž€?

src/queryClient.tsx์—์„œ restfetcher ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค ๋•Œ fetchOptions์˜ body์— Access-Control-Allow-Origin ํ”„๋กœํผํ‹ฐ๋ฅผ ์ž‘์„ฑํ–ˆ๋‹ค. Access-Control-Allow-Origin๊ฐ€ ๋ญ˜๊นŒ?

  • HTTP ํ†ต์‹ ์œผ๋กœ ์›น์‚ฌ์ดํŠธ์˜ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ๋•Œ ๋„๋ฉ”์ธ์ด ๋‹ค๋ฅผ ๊ฒฝ์šฐ ๋ณด์•ˆ์ƒ์˜ ์ด์œ ๋กœ ์„œ๋ฒ„ ์ ‘๊ทผ์„ ์ œํ•œํ•˜๋Š”๋ฐ, Access-Control-Allow-Origin๋Š” ์ด ๊ถŒํ•œ์„ ๋‹ค๋ฃจ๋Š” HTTP ํ—ค๋”์ด๋‹ค.
  • ์ด ํ—ค๋”๋Š” ์„œ๋ฒ„ ์‘๋‹ต์— ํฌํ•จ๋˜์–ด ํ•ด๋‹น ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋„๋ฉ”์ธ์„ ํ‘œ์‹œํ•œ๋‹ค.
  • ์ฆ‰, Access-Control-Allow-Origin ํ—ค๋”์— ๋ช…์‹œ๋œ ๋„๋ฉ”์ธ๊ณผ ์›น ํŽ˜์ด์ง€์˜ ํ˜ธ์ŠคํŠธ๊ฐ€ ์ผ์น˜ํ•ด์•ผ ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

ํ”„๋กœํผํ‹ฐ ๊ฐ’ ํ•ด์„ ๋ฐฉ๋ฒ•

  • *: ๋ชจ๋“  ๋„๋ฉ”์ธ์—์„œ ์ ‘๊ทผ ํ—ˆ์šฉํ•œ๋‹ค๋Š” ๋œป์œผ๋กœ, ์„œ๋ฒ„๋Š” ๋ชจ๋“  ๋„๋ฉ”์ธ์—์„œ์˜ ์š”์ฒญ์— ๋Œ€ํ•ด ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•˜๋‹ค.
  • ํŠน์ • ๋„๋ฉ”์ธ๋ช…: Access-Control-Allow-Origin ํ—ค๋”์— ํ•ด๋‹น ๋„๋ฉ”์ธ๋ช…์ด ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉด, ํ•ด๋‹น ๋„๋ฉ”์ธ์—์„œ๋งŒ ์ ‘๊ทผ์ด ํ—ˆ์šฉ๋œ๋‹ค.
  • null: ๋ธŒ๋ผ์šฐ์ €๊ฐ€ CORS ํ”„๋กœํ† ์ฝœ์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ, ํ•ด๋‹น ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๊ฒฝ์šฐ, ์„œ๋ฒ„๋Š” Access-Control-Allow-Origin ํ—ค๋”๋ฅผ ์‘๋‹ตํ•˜์ง€ ์•Š์œผ๋ฉฐ, ๋ธŒ๋ผ์šฐ์ €๋Š” ์ž๋™์œผ๋กœ null์„ ์ง€์ •ํ•œ๋‹ค.

๐Ÿคฆ๐Ÿปโ€โ™€๏ธ ์žŠ์–ด๋ฒ„๋ฆฐ ๊ฐœ๋… ๋˜์ƒˆ๊ธฐ๊ธฐ!

HTTP ํ†ต์‹  ๋ฉ”์„œ๋“œ์— ๋”ฐ๋ฅธ ์š”์ฒญ ๋ชธ์ฒด(body) ์œ ๋ฌด

  • GET, DELETE: body๋ฅผ ์ž‘์„ฑํ•ด์„œ ์„œ๋ฒ„์— ์š”์ฒญ์„ ๋„ฃ์–ด๋„ body๊ฐ’์€ ๋ฌด์‹œ๋œ๋‹ค.
  • POST, PUT, PATCH: body๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋ฉฐ, ๊ฐ’์€ JSON ํ˜•ํƒœ๋กœ ์ „๋‹ฌํ•œ๋‹ค. (JSON.stringify() ์‚ฌ์šฉ)

๐Ÿ‘€ ํ•จ๊ป˜ ์ฝ์œผ๋ฉด ์ข‹์„ ๋ฌธ์„œ