Reusing Cached Data for Rendering (part. 2)

๋ถ€๋ถ„์ ์œผ๋กœ ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ ๋ Œ๋”๋งํ•˜๊ธฐ

์•ž์„œ ์„ค๋ช…ํ–ˆ๋“ฏ, ๋ฆด๋ ˆ์ด์—์„œ๋Š” ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ด์šฉํ•ด ๋ถ€๋ถ„์ ์œผ๋กœ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ๋‹ค. ์šฐ๋ฆฌ๋Š” **โ€œ๋ถ€๋ถ„์  ๋ Œ๋”๋งโ€**์„ ๋ถ€๋ถ„์ ์œผ๋กœ ์บ์‹œ๋œ ์ฟผ๋ฆฌ๋ฅผ ์ฆ‰์‹œ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ๋Š” ability ๋ผ๊ณ  ์ •์˜ํ•œ๋‹ค. ์ฆ‰, ์ฟผ๋ฆฌ์˜ ์ผ๋ถ€๊ฐ€ ๋น ์งˆ ์ˆ˜ ์žˆ์ง€๋งŒ, ์ฟผ๋ฆฌ์˜ ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์€ ์ด๋ฏธ ์บ์‹œ๋˜์—ˆ์„ ๊ฒƒ์ด๋ผ๋Š” ์˜๋ฏธ์ด๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ, ์ฟผ๋ฆฌ์˜ ์ „์ฒด๊ฐ€ ํŒจ์น˜๋˜๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š์•„๋„ ์ด๋ฏธ ์บ์‹œ๋œ ์ฟผ๋ฆฌ๋ฅผ ์ฆ‰์‹œ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ๋‹ค.

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

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

๋ถ€๋ถ„์ ์ธ ๋ Œ๋”๋ง์„ ์œ„ํ•ด Fragments๋ฅผ ๋ฐ”์šด๋”๋ฆฌ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ

๋ถ€๋ถ„์ ์œผ๋กœ ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ Œ๋”๋งํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š”, fragment ์ปดํฌ๋„ŒํŠธ๋ฅผ suspend ํ•˜๋ฉด ๋œ๋‹ค. Fragment Component ๋Š” ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์— ์„ ์–ธ๋œ ์–ด๋–ค ๋ฐ์ดํ„ฐ๊ฐ€ ๋ Œ๋”๋ง ํ•  ๋•Œ ์—†๋Š” ๊ฒฝ์šฐ suspend ํ•  ๊ฒƒ์ด๋‹ค. ๊ตฌ์ฒด์ ์œผ๋กœ, ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ•„์š”๋กœ ํ•˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ํŒจ์น˜๋  ๋•Œ๊นŒ์ง€ ์ปดํฌ๋„ŒํŠธ๋Š” suspend ๋  ๊ฒƒ์ด๊ณ , ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋ฅผ ํฌํ•จํ•˜๋Š” ์ฟผ๋ฆฌ๊ฐ€ ํŒจ์น˜ ์™„๋ฃŒํ•  ๊นŒ์ง€ ๊ณ„์† ๊ทธ ์ƒํƒœ๋ฅผ ์ง€์†ํ•  ๊ฒƒ์ด๋‹ค.

์•„๋ž˜์™€ ๊ฐ™์€ *UsernameComponent fragment component* ๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž:

/**
 * UsernameComponent.react.js
 *
 * Fragment Component
 */

import type {UsernameComponent_user$key} from 'UsernameComponent_user.graphql';

const React = require('React');
const {graphql, useFragment} = require('react-relay');

type Props = {
  user: UsernameComponent_user$key,
};

function UsernameComponent(props: Props) {
  const user = useFragment(
    graphql`
      fragment UsernameComponent_user on User {
        username
      }
    `,
    props.user,
  );
  return (...);
}

module.exports = UsernameComponent;

๊ทธ๋ฆฌ๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์€ *AppTabs ์ฟผ๋ฆฌ ๋กœ๋” ์ปดํฌ๋„ŒํŠธ์™€,*

/**
 * AppTabs.react.js
 *
 * Query Loader Component
 */

 // ....

  const onSelectHomeTab = () => {
    loadHomeTabQuery({id: '4'}, {fetchPolicy: 'store-or-network'});
  }

*HomeTab ์ฟผ๋ฆฌ ์ปดํฌ๋„ŒํŠธ* ๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•ด๋ณด์ž. ์ด ์ฟผ๋ฆฌ๋Š” ์œ„์˜ ํ”„๋ž˜๊ทธ๋จผํŠธ์— ํฌํ•จ๋œ ๋ฐ์ดํ„ฐ(...UsernameComponent_user)๋ฅผ ํฌํ•จํ•˜์—ฌ ๊ฐ€์ ธ์˜จ๋‹ค.

/**
 * HomeTab.react.js
 *
 * Query Component
 */

const React = require('React');
const {graphql, usePreloadedQuery} = require('react-relay');

const UsernameComponent = require('./UsernameComponent.react');

function HomeTab(props: Props) {
	// UsernameComponent.react.js์˜ ํ”„๋ž˜๊ทธ๋จผํŠธ ์ปดํฌ๋„ŒํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•˜๋Š” ์ฟผ๋ฆฌ
  const data = usePreloadedQuery<AppQuery>(
    graphql`
      query HomeTabQuery($id: ID!) {
        user(id: $id) {
          name
          ...UsernameComponent_user
        }
      }
    `,
    props.queryRef,
  );

  return (
    <>
      <h1>{data.user?.name}</h1>
      <UsernameComponent user={data.user} />
    </>
  );
}

์œ„์˜ HomeTab ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง ๋˜์—ˆ์„ ๋•Œ๋ฉด, ์ด๋ฏธ {id: 4} ๋ฅผ ๊ฐ€์ง„ User ์˜ name ๊ฐ€ ํŒจ์น˜๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— Relay ์Šคํ† ์–ด์— locally ์บ์‹œ๋˜์–ด ์žˆ๊ฒŒ ๋œ๋‹ค.

๋งŒ์•ฝ ๋กœ์ปฌ์— ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š”('**store**-or-network' / '**store**-and-network') fetchPolicy ๋ฅผ ์ด์šฉํ•ด ์ฟผ๋ฆฌ๋ฅผ ๋ Œ๋”๋งํ•˜๋ ค๊ณ  ํ•˜๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ผ์ด ๋ฐœ์ƒํ•œ๋‹ค:

  • ์ฟผ๋ฆฌ๋Š” ๋กœ์ปฌ์—์„œ (ํ”„๋ž˜๊ทธ๋จผํŠธ์—์„œ) ํ•„์š”๋กœ ํ•˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋Š”์ง€ ํ™•์ธํ•˜๋Š”๋ฐ, ์œ„์˜ ๊ฒฝ์šฐ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•œ๋‹ค.

    • ์œ„์˜ ๊ฒฝ์šฐ, ์ฟผ๋ฆฌ๋Š” name ๋งŒ์„ ์ง์ ‘์ ์œผ๋กœ ์ฟผ๋ฆฌํ•˜๊ณ  ์žˆ๊ณ , name์€ ์ด์šฉ ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ํ•ด๋‹น ์ฟผ๋ฆฌ์™€ ์—ฐ๊ด€์ด ์žˆ๋Š” ํ•œ ๋น ์ง„ ๋ฐ์ดํ„ฐ๋Š” ์—†๋‹ค๊ณ  ํŒ๋‹จ๋œ๋‹ค. Relay๋Š” ์ฟผ๋ฆฌ๋ฅผ ๋ Œ๋”๋ง ํ•  ๋•Œ, ํ•ด๋‹น ์ฟผ๋ฆฌ์˜ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ ํŒจ์น˜๋  ๋•Œ๊นŒ์ง€ (์ค‘์ฒฉ๋œ ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ํฌํ•จํ•˜์—ฌ) ํŠธ๋ฆฌ ์ „์ฒด๋ฅผ blocking ํ•˜๋Š” ๋Œ€์‹ , ๋ฐ์ดํ„ฐ์— ๋”ฐ๋ผ ํŠธ๋ฆฌ๋ฅผ ๋ Œ๋”๋งํ•œ๋‹ค. Relay๊ฐ€ ์ปดํฌ๋„ŒํŠธ์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋น ์กŒ๋‹ค๊ณ  ํŒ๋‹จํ•  ๋•Œ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ํ”„๋ž˜๊ทธ๋จผํŠธ(๋กœ์ปฌ)์— ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์ƒํƒœ์ผ ๋•Œ๋‹ค. ์ฆ‰, ๋งŒ์•ฝ ํ˜„์žฌ์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋น ์ง„ ๊ฒฝ์šฐ๋Š” ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ํŒ๋‹จํ•˜์ง€๋งŒ, ์ž์‹ ์ปดํฌ๋„ŒํŠธ์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋Š” ๋ชจ๋‘ ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋น ์กŒ๋‹ค๊ณ  ํŒ๋‹จํ•˜์ง€ ์•Š๋Š”๋‹ค.

    • ์ฟผ๋ฆฌ๊ฐ€ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด, ์ฟผ๋ฆฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋จผ์ € ๋ Œ๋”๋งํ•˜๊ณ  ์ž์‹ ์ปดํฌ๋„ŒํŠธ์ธ UsernameComponent ๋ฅผ ๋ Œ๋”๋ง ํ•  ๊ฒƒ์ด๋‹ค.

    • UsernameComponent ๊ฐ€ UsernameComponent_user ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ๋ Œ๋”๋งํ•˜๋ ค๊ณ  ํ•  ๋•Œ, UsernameComponent ๋Š” ๋ณธ์ธ์ด ํ•„์š”๋กœ ํ•˜๋Š” ๋ฐ์ดํ„ฐ์˜ ๋ถ€๋ถ„์ด ๋น ์กŒ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ๊ฒƒ์ด๋‹ค. ๊ตฌ์ฒด์ ์œผ๋กœ, ์œ„์˜ ๊ฒฝ์šฐ์—๋Š” username ์ด ๋น ์กŒ๋‹ค. ์—ฌ๊ธฐ์„œ, UsernameComponent ๋Š” ์ผ๋ถ€๊ฐ€ ๋น ์ง„ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ๋ Œ๋”๋ง์„ suspend ํ•  ๊ฒƒ์ด๋‹ค. ์–ด๋–ค fetchPolicy ๋ฅผ ๊ณจ๋ž๋Š”์ง€์— ๊ด€๊ณ„์—†์ด, ๋„คํŠธ์›Œํฌ ์š”์ฒญ์€ ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ํฌํ•จํ•ด ์ „์ฒด ์ฟผ๋ฆฌ๋ฅผ ์œ„ํ•œ ์ผ๋ถ€ ๋ฐ์ดํ„ฐ๊ฐ€ ๋น ์ง„ ๊ฒฝ์šฐ๋ผ๋ฉด ์–ธ์ œ๋“  ์‹œ์ž‘๋  ๊ฒƒ์ด๋‹ค.

      ์ฐธ๊ณ . [Fetch Policies]

      • store-or-network (๊ธฐ๋ณธ) : ๋กœ์ปฌ์— ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๊ณ , ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ์˜ ์ผ๋ถ€๊ฐ€ ๋น„๊ฑฐ๋‚˜ ์‚ฌ์šฉ ๋ถˆ๊ฐ€๋Šฅ ํ•œ ๊ฒฝ์šฐ์—๋งŒ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค. ๋งŒ์•ฝ ์ฟผ๋ฆฌ๊ฐ€ ๋ชจ๋‘ ์บ์‹œ๋œ ์ƒํƒœ๋ผ๋ฉด, ๋„คํŠธ์›Œํฌ ์š”์ฒญ์€ ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค.

      • store-and-network : ๋กœ์ปฌ์— ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๊ณ  ๋ฐ์ดํ„ฐ์˜ ์ผ๋ถ€๊ฐ€ ๋น„์—ˆ๋“ ์ง€ ์Šคํ† ์–ด์— ์žˆ๋“ ์ง€์— ์ƒ๊ด€ ์—†์ด ํ•ญ์ƒ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค.

      • network-only : ๋กœ์ปฌ์— ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ , ์ฟผ๋ฆฌ๋ฅผ ํŒจ์น˜ํ•˜๊ธฐ ์œ„ํ•ด ํ•ญ์ƒ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค. ์ด ๋•Œ ๋ฐ์ดํ„ฐ์˜ ์ผ๋ถ€๊ฐ€ ๋กœ์ปฌ์— ์บ์‹œ๋˜์—ˆ๋Š”์ง€, ๋น„์—ˆ๊ฑฐ๋‚˜ ์‚ฌ์šฉ ๋ถˆ๊ฐ€๋Šฅํ•œ์ง€ ํ™•์ธํ•˜์ง€ ์•Š๊ณ  ํ•ญ์ƒ ๋ณด๋‚ธ๋‹ค.

      • store-only : ๋กœ์ปฌ์— ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋งŒ์„ ์‚ฌ์šฉํ•˜๊ณ , ์ ˆ๋Œ€ ์ฟผ๋ฆฌ๋ฅผ ํŒจ์น˜ํ•˜๊ธฐ ์œ„ํ•ด ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ์ „์†กํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ด ๊ฒฝ์šฐ, ์ฟผ๋ฆฌ๋ฅผ ํŒจ์น˜ํ•˜๊ธฐ ์œ„ํ•œ ์ฑ…์ž„์€ caller์—๊ฒŒ ์žˆ์œผ๋‚˜, ์—ฌ๊ธฐ์„œ ๊ทธ์น˜์ง€ ์•Š๊ณ  ์ฟผ๋ฆฌ ๋ฐ์ดํ„ฐ๊ฐ€ fully locally ์ด์šฉ ๊ฐ€๋Šฅํ•  ๋•Œ๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์—ฌ๊ธฐ์„œ UsernameComponent ๊ฐ€ ๋น ์ง„ username ๋กœ ์ธํ•ด suspend ๋œ ๊ฒฝ์šฐ, ์ด์ƒ์ ์œผ๋กœ๋Š” User ์˜ name ์€ ์ด๋ฏธ ๋กœ์ปฌ์— ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ์ด๋ฏ€๋กœ ์ฆ‰์‹œ ๋ Œ๋”๋ง ๋˜์–ด์•ผ ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, Suspense ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋ž˜๊ทธ๋จผํŠธ์˜ suspension์„ ์žก๊ณ  ์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, suspension์€ ๋ฒ„๋ธ”๋ง ๋˜์–ด App ์ปดํฌ๋„ŒํŠธ๊นŒ์ง€ suspend ์‹œํ‚ค๊ฒŒ ๋œ๋‹ค.

username ์ด ์—†๋”๋ผ๋„ App ์˜ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋Š” name ์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•  ๋•Œ ๋ Œ๋”๋ง ๋  ์ˆ˜ ์žˆ๋„๋ก, UsernameComponent ๋ฅผ Suspense ๋กœ ๊ฐ์‹ธ๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.

/**
 * HomeTab.react.js
 *
 * Query Component
 */

const React = require('React');
const {Suspense} = require('React');
const {graphql, usePreloadedQuery} = require('react-relay');

const UsernameComponent = require('./UsernameComponent.react');

function HomeTab() {
  const data = usePreloadedQuery<AppQuery>(
    graphql`
      query AppQuery($id: ID!) {
        user(id: $id) {
          name
          ...UsernameComponent_user
        }
      }
    `,
    props.queryRef,
  );

  return (
    <>
      <h1>{data.user?.name}</h1>
      <**Suspense** fallback={<LoadingSpinner label="Fetching username" />}>
        <UsernameComponent user={data.user} />
      <**/Suspense**>
    </>
  );
}

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

์œ„์˜ ๋ชจ๋“  ๊ณผ์ •์€ ์•ฑ ์ „์ฒด๊ฐ€ ๋กœ๋”ฉ ์ƒํƒœ์— ๋น ์ง€๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ณ , ๋ถ€๋ถ„์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ค‘๊ฐ„ ๋‹จ๊ณ„์˜ UI ์ƒํƒœ๋ฅผ ๋ Œ๋”๋ง ํ•˜์—ฌ ์ตœ์ข…์ ์œผ๋กœ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ ๋ Œ๋”๋ง ๋œ ๊ฒฐ๊ณผ์™€ ๋น„์Šทํ•œ ํ™”๋ฉด์„ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

ํ•„๋“œ ํ•ธ๋“ค๋Ÿฌ: ๋น ์ง„ ๋ฐ์ดํ„ฐ ์ฑ„์›Œ๋„ฃ๊ธฐ

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

ํ•˜์ง€๋งŒ, ๋‹ค๋ฅธ ์ฟผ๋ฆฌ์ž„์—๋„ ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ๊ฒฝ์šฐ, ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ด์ƒ์ ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋‹ค์Œ๊ณผ ๊ฐ™์€ 2๊ฐœ์˜ ์ฟผ๋ฆฌ๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž:

query UserQuery {
  user(id: 4) {
    name
  }
}
query NodeQuery {
  node(id: 4) {
    ... on User {
      name
    }
  }
}

2๊ฐœ์˜ ์ฟผ๋ฆฌ๋Š” ๋‹ค๋ฅด์ง€๋งŒ, ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค. ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ๋ฒ•, ์ฆ‰, ์ฟผ๋ฆฌ๋งŒ ๋‹ค๋ฅผ ๋ฟ์ด๋‹ค. ์ด์ƒ์ ์œผ๋กœ, ๋งŒ์•ฝ ํ•˜๋‚˜์˜ ์ฟผ๋ฆฌ๊ฐ€ ์ด๋ฏธ ์Šคํ† ์–ด์— ์บ์‹œ๋œ ๊ฒฝ์šฐ, ๋‹ค๋ฅธ ์ฟผ๋ฆฌ๋ฅผ ๋ Œ๋”๋ง ํ•  ๋•Œ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ์‚ฌ์šฉ ํ•ด์•ผํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, Relay๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ด๋Ÿฌํ•œ ๋กœ์ง์— ๋Œ€ํ•œ ์ง€์‹์ด ์—†๊ธฐ ๋•Œ๋ฌธ์—, ์šฐ๋ฆฌ๊ฐ€ ๋‘˜์€ ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค๋Š” ์ง€์‹์„ ์ธ์ฝ”๋”ฉ ํ•  ์ˆ˜ ์žˆ๋„๋ก node(id: 4) ๋Š” user(id: 4) ๋ผ๋Š” ์„ค์ •์„ ์ œ๊ณตํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

๊ทธ๋Ÿฌ๊ธฐ ์œ„ํ•ด์„œ๋Š”, missingFieldHandlers ๋ฅผ RelayEnvironment ์— ์ œ๊ณตํ•˜์—ฌ ํ•ด๋‹น ์ง€์‹์„ ๊ตฌ์ฒดํ™”ํ•œ๋‹ค.

const {**ROOT_TYPE**, Environment} = require('relay-runtime');

const missingFieldHandlers = [
  {
    handle(field, record, argValues): ?string {
      if (
        record != null &&
        **record.__typename === ROOT_TYPE** &&
        **field.name === 'user'** &&
        **argValues.hasOwnProperty('id')**
      ) {
				// ํ•„๋“œ๊ฐ€ user(id: $id)์ธ ๊ฒฝ์šฐ,
				// $id๋ฅผ ์ด์šฉํ•ด record ๊ฐ€์ ธ์˜ค๊ธฐ
        return argValues.id;
      }
      if (
        record != null &&
        **record.__typename === ROOT_TYPE** &&
        **field.name === 'story'** &&
        **argValues.hasOwnProperty('story_id')**
      ) {
				// ํ•„๋“œ๊ฐ€ story(story_id: $story_id)์ธ ๊ฒฝ์šฐ,
				// $story_id๋ฅผ ์ด์šฉํ•ด record ๊ฐ€์ ธ์˜ค๊ธฐ
        return argValues.story_id;
      }
      return undefined;
    },
    kind: 'linked',
  },
];

const environment = new Environment({/*...*/, missingFieldHandlers});
  • ROOT_TYPE : ์Šคํ† ์–ด์˜ ๋ฃจํŠธ์— ๋‹ด๊ธด type_name

  • missingFieldHandlers ๋Š” handlers๋ฅผ ๋‹ด์€ ๋ฐฐ์—ด์ด๋‹ค. ๊ฐ๊ฐ์˜ ํ•ธ๋“ค๋Ÿฌ๋Š” **handle ๋ฉ”์†Œ๋“œ**๋ฅผ ๊ตฌ์ฒดํ™”ํ•˜๊ณ  ๋น ์ง„ ํ•„๋“œ๋ฅผ ํ•ธ๋“ค๋งํ•˜๋Š” ๋ฐฉ๋ฒ•(kind) ๋ฅผ ์ง€๋…€์•ผ ํ•œ๋‹ค. ํ•ธ๋“ค๋งํ•˜๊ณ ์ž ํ•˜๋Š” 2๊ฐœ์˜ ์ฃผ์š” ํ•„๋“œ ํƒ€์ž…์€:

    • **scalar** : ์Šค์นผ๋ผ ๊ฐ’์„ ๊ฐ–๋Š” ํ•„๋“œ e.g. number, string

    • **linked** : ๋‹ค๋ฅธ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜๋Š” ํ•„๋“œ e.g. scalar๊ฐ€ ์•„๋‹Œ ๊ฐ’

  • handle ๋ฉ”์†Œ๋“œ๋Š” (๋น ์ง„ ํ•„๋“œ, ํ•ด๋‹น ํ•„๋“œ๊ฐ€ ์†ํ•˜๋Š” record, ๊ทธ๋ฆฌ๊ณ  ํ˜„์žฌ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ํ•„๋“œ์— ์ œ๊ณต๋˜๋Š” arguments) ๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๊ฐ–๋Š”๋‹ค.

    • scalar ํ•„๋“œ๋ฅผ ํ•ธ๋“ค๋ง ํ•  ๊ฒฝ์šฐ, ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜๋Š” ๋น ์ง„ ํ•„๋“œ์˜ ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์Šค์นผ๋ผ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค.

    • linked ํ•„๋“œ๋ฅผ ํ•ธ๋“ค๋ง ํ•  ๊ฒฝ์šฐ, ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜๋Š” ๋น ์ง„ ํ•„๋“œ์— ๋„ฃ์„ ์ˆ˜ ์žˆ๋„๋ก ์Šคํ† ์–ด์— ์žˆ๋Š” ๋‹ค๋ฅธ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜๋Š” ID ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค.

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

Last updated