Refreshing and Refetching (part 1)

Refreshing Queries

Refreshing Queries (์ฟผ๋ฆฌ ์ƒˆ๋กœ๊ณ ์นจ) ์€ ์„œ๋ฒ„์—์„œ ํ•ด๋‹น ๋ฐ์ดํ„ฐ์˜ ์ตœ์‹  ๋ฒ„์ „์„ ์–ป๊ธฐ ์œ„ํ•ด ์›๋ž˜ ์ฟผ๋ฆฌ์— ์˜ํ•ด ๋ Œ๋”๋ง๋œ ๊ฒƒ๊ณผ ๋˜‘๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

real-time features(์‹ค์‹œ๊ฐ„ ๊ธฐ๋Šฅ๋“ค) ์ด์šฉํ•˜๊ธฐ

์„œ๋ฒ„์˜ ์ตœ์‹ ๋ฒ„์ „์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ตœ์‹  ์ƒํƒœ๋กœ ์œ ์ง€ ํ•˜๊ธฐ์œ„ํ•ด์„œ ๊ฐ€์žฅ ๋จผ์ € ๊ณ ๋ คํ•  ์‚ฌํ•ญ์€ ์ž๋™์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธ์‹œ์ผœ์ฃผ๊ณ  ์ฃผ๊ธฐ์ ์œผ๋กœ ์ƒˆ๋กœ๊ณ ์นจํ•ด์ฃผ๋Š” ์‹ค์‹œ๊ฐ„ ๊ธฐ๋Šฅ๋“ค์„ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด ์ ์ ˆํ•œ์ง€ ์—ฌ๋ถ€์ž…๋‹ˆ๋‹ค.

real-time features(์‹ค์‹œ๊ฐ„ ๊ธฐ๋Šฅ๋“ค) ์— ํ•œ ๊ฐ€์ง€ ์˜ˆ๋กœ๋Š” ์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ํ•ด๋‹น ์ฟผ๋ฆฌ์˜ ์ •๋ณด๊ฐ€ ๋ฐ”๋€”๋•Œ ๋งˆ๋‹ค ์•Œ๋ ค์ฃผ๋Š” GQLS(GraphQL Subscriptions) ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. - ์„œ๋ฒ„๋‚˜ ๋„คํŠธ์›Œํฌ ๋ ˆ์ด์–ด์— ์ถ”๊ฐ€์ ์ธ ์„ค์ •์ด ํ•„์š”ํ•จ

useQueryLoader / loadQuery ๋ฅผ ์‚ฌ์šฉ

useQueryLoader ํ›…์„ ์‚ฌ์šฉํ•ด์„œ ์ฟผ๋ฆฌ๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” loadQuery ํ•จ์ˆ˜๋งŒ ๋‹ค์‹œ ํ˜ธ์ถœํ•˜๋ฉด ๋œ๋‹ค.

/**
 * App.react.js
 */
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';

const AppQuery = require('__generated__/AppQuery.graphql');

function App(props: Props) {
  const [queryRef, loadQuery] = useQueryLoader<AppQueryType>(
    AppQuery,
    props.appQueryRef /* initial query ref */
  );

  const refresh = useCallback(() => {
    // Load the query again using the same original variables.
    // Calling loadQuery will update the value of queryRef.
    // The fetchPolicy ensures we always fetch from the server and skip
    // the local data cache.
    const {variables} = props.appQueryRef;
    loadQuery(variables, {fetchPolicy: 'network-only'});
  }, [/* ... */]);

  return (
    <React.Suspense fallback="Loading query...">
      <MainContent
        refresh={refresh}
        queryRef={queryRef}
      />
    </React.Suspense>
  );
}
/**
 * MainContent.react.js
 */
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';

// Renders the preloaded query, given the query reference
function MainContent(props) {
  const {refresh, queryRef} = props;
  const data = usePreloadedQuery<AppQueryType>(
    graphql`
      query AppQuery($id: ID!) {
        user(id: $id) {
          name
          friends {
            count
          }
        }
      }
    `,
    queryRef,
  );

  return (
    <>
      <h1>{data.user?.name}</h1>
      <div>Friends count: {data.user.friends?.count}</div>
      <Button
        onClick={() => refresh()}>
        Fetch latest count
      </Button>
    </>
  );
}

์œ„ ์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด,

  • ์šฐ๋ฆฌ๋Š” loadQuery ํ•จ์ˆ˜๋ฅผ ์ƒˆ๋กœ๊ณ ์นจ์„ ์œ„ํ•œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์•ˆ์—์„œ ํ˜ธ์ถœํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋„คํŠธ์›Œํฌ request ๋Š” ์ฆ‰์‹œ ์‹œ์ž‘๋˜๊ณ  ์—…๋ฐ์ดํŠธ๋œ queryRef๋ฅผ usePreloaded๋ฅผ ์‚ฌ์šฉํ•˜๋Š” MainContent ์ปดํฌ๋„ŒํŠธ์— ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.- ์—…๋ฐ์ดํŠธ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.

  • ํ•ญ์ƒ ๋„คํŠธ์›Œํฌ์—์„œ ๊ฐ€์ ธ์˜ค๊ณ  ๋กœ์ปฌ ๋ฐ์ดํ„ฐ ์บ์‹œ๋ฅผ ๊ฑด๋„ˆ๋›ฐ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด 'network-only' ๋ผ๋Š” fetchPolicy๋ฅผ ์ „๋‹ฌ ํ•ฉ๋‹ˆ๋‹ค.

  • loadQuery ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์€ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋˜๋ฉด์„œ ๋ Œ๋”๋ง์„ ๋ง‰๋Š” usePreloadedQuery ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋กœ๋”ฉ์ƒํƒœ๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ Suspense boundary๋กœ MainContent ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ์‹ธ์ ธ์žˆ๋Š”์ง€ ์‚ดํŽด๋ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๋งŒ์•ฝ Suspense ๋ฅผ ํ”ผํ•ด์•ผํ•œ๋‹ค๋ฉด

๊ฒฝ์šฐ์— ๋”ฐ๋ผ Suspense ์˜ fallback ์„ ๋ณด์—ฌ์ฃผ์ง€ ์•Š๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ์—๋Š” ๋กœ๋”ฉ์ƒํƒœ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ถ”์ ํ• ์ˆ˜์žˆ๋Š” fetchQuery ๋ฅผ ๋Œ€์‹  ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ 

๋™์‹œ ๋ Œ๋”๋ง์ด ์ง€์›๋˜๋Š” ์ดํ›„์˜ React ๋ฒ„์ „์—์„œ๋Š” ์ด ๊ฒฝ์šฐ์— ๋Œ€ํ•ด์„œ ์ง€์›ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค Suspending ์‹œ์— ์ด๋ฏธ ๋ฆฌ๋ Œ๋”๋ง๋œ ๋‚ด์šฉ๋“ค์ด Suspense fallback ์œผ๋กœ ์ˆจ๊ฒจ์ง€๋Š” ๊ฒƒ์„ ํ”ผํ•˜๋„๋ก ํ•ด์ค„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

/**
 * App.react.js
 */
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';

const AppQuery = require('__generated__/AppQuery.graphql');

function App(props: Props) {
  const environment = useRelayEnvironment();
  const [queryRef, loadQuery] = useQueryLoader<AppQueryType>(
    AppQuery,
    props.appQueryRef /* initial query ref */
  );
  const [isRefreshing, setIsRefreshing] = useState(false)

  const refresh = useCallback(() => {
    if (isRefreshing) { return; }
    const {variables} = props.appQueryRef;
    setIsRefreshing(true);

    // fetchQuery will fetch the query and write
    // the data to the Relay store. This will ensure
    // that when we re-render, the data is already
    // cached and we don't suspend
    fetchQuery(environment, AppQuery, variables)
      .subscribe({
        complete: () => {
          setIsRefreshing(false);

          // *After* the query has been fetched, we call
          // loadQuery again to re-render with a new
          // queryRef.
          // At this point the data for the query should
          // be cached, so we use the 'store-only'
          // fetchPolicy to avoid suspending.
          loadQuery(variables, {fetchPolicy: 'store-only'});
        }
        error: () => {
          setIsRefreshing(false);
        }
      });
  }, [/* ... */]);

  return (
    <React.Suspense fallback="Loading query...">
      <MainContent
        isRefreshing={isRefreshing}
        refresh={refresh}
        queryRef={queryRef}
      />
    </React.Suspense>
  );
}

์œ„ ์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด,

  • ์šฐ๋ฆฌ๊ฐ€ ์ƒˆ๋กœ๊ณ ์นจํ• ๋•Œ, ์ด์ œ๋Š” ์šฐ๋ฆฌ์˜ isRefreshing ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๊ณ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•ด์„œ MainContent ๊ตฌ์„ฑ ์š”์†Œ ๋‚ด์—์„œ MainContent๋ฅผ ์ˆจ๊ธฐ์ง€ ์•Š๊ณ  ์‚ฌ์šฉ ์ค‘์ธ ์Šคํ”ผ๋„ˆ ๋˜๋Š” ์œ ์‚ฌํ•œ ๋กœ๋”ฉ UI๋ฅผ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์—์„œ ๋จผ์ € fetchQuery๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ๋กœ์ปฌ ๋ฆด๋ ˆ์ด ์ €์žฅ์†Œ์— ๋ฐ์ดํ„ฐ๋ฅผ ์”๋‹ˆ๋‹ค. fetchQuery ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ์™„๋ฃŒ๋˜๋ฉด loadQuery๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์—…๋ฐ์ดํŠธ๋œ queryRef๋ฅผ ์–ป์€ ๋‹ค์Œ usePreloaded๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.์ฟผ๋ฆฌ๋Š” ์ด์ „ ์˜ˆ์ œ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์—…๋ฐ์ดํŠธ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.

  • ์ด๋•Œ loadQuery๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋กœ์ปฌ ๋ฆด๋ ˆ์ด ์ €์žฅ์†Œ์— ์ด๋ฏธ ์บ์‹œ๋˜์–ด ์žˆ์–ด์•ผ ํ•˜๋ฏ€๋กœ ์ผ์‹œ ์ค‘๋‹จ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ด๋ฏธ ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋งŒ ์ฝ๊ธฐ ์œ„ํ•ด 'store-only'์˜ fetchPolicy๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

useLazyLoadQuery ๋ฅผ ์‚ฌ์šฉ

useLazyLoadQuery Hook์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ์ƒˆ๋กœ ๊ณ ์น˜๋ ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

/**
 * App.react.js
 */
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';

const AppQuery = require('__generated__/AppQuery.graphql');

function App(props: Props) {
  const variables = {id: '4'};
  const [refreshedQueryOptions, setRefreshedQueryOptions] = useState(null);

  const refresh = useCallback(() => {
    // Trigger a re-render of useLazyLoadQuery with the same variables,
    // but an updated fetchKey and fetchPolicy.
    // The new fetchKey will ensure that the query is fully
    // re-evaluated and refetched.
    // The fetchPolicy ensures that we always fetch from the network
    // and skip the local data cache.
    setRefreshedQueryOptions(prev => ({
      fetchKey: (prev?.fetchKey ?? 0) + 1,
      fetchPolicy: 'network-only',
    }));
  }, [/* ... */]);

  return (
    <React.Suspense fallback="Loading query...">
      <MainContent
        refresh={refresh}
        queryOptions={refreshedQueryOptions ?? {}}
        variables={variables}
      />
    </React.Suspense>
  );
/**
 * MainContent.react.js
 */
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';

// Fetches and renders the query, given the fetch options
function MainContent(props) {
  const {refresh, queryOptions, variables} = props;
  const data = useLazyLoadQuery<AppQueryType>(
    graphql`
      query AppQuery($id: ID!) {
        user(id: $id) {
          name
          friends {
            count
          }
        }
      }
    `,
    variables,
    queryOptions,
  );

  return (
    <>
      <h1>{data.user?.name}</h1>
      <div>Friends count: {data.user.friends?.count}</div>
      <Button
        onClick={() => refresh()}>
        Fetch latest count
      </Button>
    </>
  );
}

์œ„ ์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด,

  • ์ƒํƒœ์—์„œ์˜ ์ƒˆ ์˜ต์…˜์„ ์„ค์ •ํ•˜์—ฌ ์ƒˆ๋กœ ๊ณ ์น  ์ˆ˜ ์žˆ๋„๋ก ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด useLazyLoadQuery๋ฅผ ์‚ฌ์šฉํ•˜๋Š” MainContent ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ƒˆ fetchKey ๋ฐ fetchPolicy๋กœ ๋‹ค์‹œ ๋ Œ๋”๋งํ•˜๊ณ  ๋ Œ๋”๋ง ์‹œ ์ฟผ๋ฆฌ๋ฅผ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

  • ์—…๋ฐ์ดํŠธ ๋งˆ๋‹ค ์ฆ๊ฐ€ํ•˜๋Š” fetchKey ์˜ ์ƒˆ๋กœ์šด ๊ฐ’์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์—…๋ฐ์ดํŠธ์—์„œ LazyLoadQuery๋ฅผ ์‚ฌ์šฉํ•  ์ƒˆ fetchKey๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ์ฟผ๋ฆฌ๊ฐ€ ์™„์ „ํžˆ ์žฌํ‰๊ฐ€๋˜๊ณ  ๋‹ค์‹œ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

  • ํ•ญ์ƒ ๋„คํŠธ์›Œํฌ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ๋กœ์ปฌ๋ฐ์ดํ„ฐ ์บ์‹œ๋ฅผ ๊ฑด๋„ˆ ๋›ฐ๊ธฐ ์œ„ํ•ด 'network-only' ์˜ fetchPolicy ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ์ƒˆ๋กœ ๊ณ ์นจ ์‹œ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋Š” ์‚ฌ์šฉ ์ค‘์ธ fetchPolicy๋กœ ์ธํ•ด ํ•ญ์ƒ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ์ด๋ฃจ์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์— ์ปดํฌ๋„ŒํŠธ๊ฐ€ suspend ๋ฉ๋‹ˆ๋‹ค. ๋กœ๋”ฉ์ƒํƒœ๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ Suspense boundary๋กœ MainContent ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ์‹ธ์ ธ์žˆ๋Š”์ง€ ์‚ดํŽด๋ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๋งŒ์•ฝ Suspense ๋ฅผ ํ”ผํ•ด์•ผํ•œ๋‹ค๋ฉด

๊ฒฝ์šฐ์— ๋”ฐ๋ผ Suspense ์˜ fallback ์„ ๋ณด์—ฌ์ฃผ์ง€ ์•Š๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ์—๋Š” ๋กœ๋”ฉ์ƒํƒœ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ถ”์ ํ• ์ˆ˜์žˆ๋Š” fetchQuery ๋ฅผ ๋Œ€์‹  ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ 

๋™์‹œ ๋ Œ๋”๋ง์ด ์ง€์›๋˜๋Š” ์ดํ›„์˜ React ๋ฒ„์ „์—์„œ๋Š” ์ด ๊ฒฝ์šฐ์— ๋Œ€ํ•ด์„œ ์ง€์›ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค Suspending ์‹œ์— ์ด๋ฏธ ๋ฆฌ๋ Œ๋”๋ง๋œ ๋‚ด์šฉ๋“ค์ด Suspense fallback ์œผ๋กœ ์ˆจ๊ฒจ์ง€๋Š” ๊ฒƒ์„ ํ”ผํ•˜๋„๋ก ํ•ด์ค„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

/**
 * App.react.js
 */
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';

const AppQuery = require('__generated__/AppQuery.graphql');

function App(props: Props) {
  const variables = {id: '4'}
  const environment = useRelayEnvironment();
  const [refreshedQueryOptions, setRefreshedQueryOptions] = useState(null);
  const [isRefreshing, setIsRefreshing] = useState(false)

  const refresh = useCallback(() => {
    if (isRefreshing) { return; }
    setIsRefreshing(true);

    // fetchQuery will fetch the query and write
    // the data to the Relay store. This will ensure
    // that when we re-render, the data is already
    // cached and we don't suspend
    fetchQuery(environment, AppQuery, variables)
      .subscribe({
        complete: () => {
          setIsRefreshing(false);

          // *After* the query has been fetched, we update
          // our state to re-render with the new fetchKey
          // and fetchPolicy.
          // At this point the data for the query should
          // be cached, so we use the 'store-only'
          // fetchPolicy to avoid suspending.
          setRefreshedQueryOptions(prev => ({
            fetchKey: (prev?.fetchKey ?? 0) + 1,
            fetchPolicy: 'store-only',
          }));
        }
        error: () => {
          setIsRefreshing(false);
        }
      });
  }, [/* ... */]);

  return (
    <React.Suspense fallback="Loading query...">
      <MainContent
        isRefreshing={isRefreshing}
        refresh={refresh}
        queryOptions={refreshedQueryOptions ?? {}}
        variables={variables}
      />
    </React.Suspense>
  );
}
  • ์šฐ๋ฆฌ๊ฐ€ ์ƒˆ๋กœ๊ณ ์นจํ• ๋•Œ, ์ด์ œ๋Š” ์šฐ๋ฆฌ์˜ isRefreshing ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๊ณ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•ด์„œ MainContent ๊ตฌ์„ฑ ์š”์†Œ ๋‚ด์—์„œ MainContent๋ฅผ ์ˆจ๊ธฐ์ง€ ์•Š๊ณ  ์‚ฌ์šฉ ์ค‘์ธ ์Šคํ”ผ๋„ˆ ๋˜๋Š” ์œ ์‚ฌํ•œ ๋กœ๋”ฉ UI๋ฅผ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์—์„œ ๋จผ์ € fetchQuery๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ๋กœ์ปฌ ๋ฆด๋ ˆ์ด ์ €์žฅ์†Œ์— ๋ฐ์ดํ„ฐ๋ฅผ ์”๋‹ˆ๋‹ค. fetchQuery ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ์™„๋ฃŒ๋˜๋ฉด ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ์—…๋ฐ์ดํŠธ๋œ fetchKey์™€ fetchPolicy๋ฅผ ๋‹ค์‹œ ๋ Œ๋”ํ•œ ๋‹ค์Œ useLazyLoadQuery๋กœ ์ „๋‹ฌํ•˜์—ฌ ์ด์ „ ์˜ˆ์ œ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์—…๋ฐ์ดํŠธ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.

  • ์ด ์‹œ์ ์—์„œ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•  ๋•Œ ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋กœ์ปฌ ๋ฆด๋ ˆ์ด ์ €์žฅ์†Œ์— ์ด๋ฏธ ์บ์‹œ๋˜์–ด์•ผ ํ•˜๋ฏ€๋กœ ์ผ์‹œ ์ค‘๋‹จ์„ ํ”ผํ•˜๊ณ  ์ด๋ฏธ ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋งŒ ์ฝ๊ธฐ ์œ„ํ•ด 'store-only'์˜ fetchPolicy๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

Refetching Queries with Different Data

"refetching a query" ๋Š” ๋‹ค์‹œ ๊ทธ ์ฟผ๋ฆฌ๋กœ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญ ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํ˜„์žฌ ์„ ํƒํ•œ ํ•ญ๋ชฉ์„ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜, ํ‘œ์‹œ๋˜๋Š” ํ•ญ๋ชฉ๊ณผ ๋‹ค๋ฅธ ํ•ญ๋ชฉ ๋ชฉ๋ก์„ ๋ Œ๋”๋งํ•˜๊ฑฐ๋‚˜, ์ผ๋ฐ˜์ ์œผ๋กœ ํ˜„์žฌ ๋ Œ๋”๋ง๋œ ๋‚ด์šฉ์„ ์ƒˆ ๋‚ด์šฉ์ด๋‚˜ ๋‹ค๋ฅธ ๋‚ด์šฉ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ ์œ„ํ•œ ๊ฒƒ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

useQueryLoader / loadQuery ์‚ฌ์šฉ

Refreshing Queries with useQueryLoader ์™€ ์œ ์‚ฌํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ refetch ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

/**
 * App.react.js
 */
const AppQuery = require('__generated__/AppQuery.graphql');

function App(props: Props) {
  const variables = {id: '4'};
  const [queryRef, loadQuery] = useQueryLoader<AppQueryType>(
    AppQuery,
    props.appQueryRef /* initial query ref */
  );

  const refetch = useCallback(() => {
    // Load the query again using the same original variables.
    // Calling loadQuery will update the value of queryRef.
    loadQuery({id: 'different-id'});
  }, [/* ... */]);

  return (
    <React.Suspense fallback="Loading query...">
      <MainContent
        refetch={refetch}
        queryRef={queryRef}
      />
    </React.Suspense>
  );
}
/**
 * MainContent.react.js
 */
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';

// Renders the preloaded query, given the query reference
function MainContent(props) {
  const {refetch, queryRef} = props;
  const data = usePreloadedQuery<AppQueryType>(
    graphql`
      query AppQuery($id: ID!) {
        user(id: $id) {
          name
          friends {
            count
          }
        }
      }
    `,
    queryRef,
  );

  return (
    <>
      <h1>{data.user?.name}</h1>
      <div>Friends count: {data.user?.friends?.count}</div>
      <Button
        onClick={() => refetch()}>
        Fetch latest count
      </Button>
    </>
  );
}

์œ„ ์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด,

  • ์šฐ๋ฆฌ๋Š” loadQuery ํ•จ์ˆ˜๋ฅผ ์ƒˆ๋กœ๊ณ ์นจ์„ ์œ„ํ•œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์•ˆ์—์„œ ํ˜ธ์ถœํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋„คํŠธ์›Œํฌ request ๋Š” ์ฆ‰์‹œ ์‹œ์ž‘๋˜๊ณ  ์—…๋ฐ์ดํŠธ๋œ queryRef๋ฅผ usePreloaded๋ฅผ ์‚ฌ์šฉํ•˜๋Š” MainContent ์ปดํฌ๋„ŒํŠธ์— ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.- ์—…๋ฐ์ดํŠธ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.

  • loadQuery์— fetchPolicy๋ฅผ ์ „๋‹ฌํ•˜์ง€ ์•Š๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, 'store-or-network'์˜ ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ ์บ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ• ์ง€ ์—ฌ๋ถ€๋ฅผ ์ง€์ •ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค๋ฅธ ์ •์ฑ…์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    • store-or-network : ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฆด๋ ˆ์ด ์ €์žฅ์†Œ์— ์บ์‹œ๋˜์–ด์žˆ๋Š”์ง€ ์—†๋Š”์ง€ ํ™•์ธ ํ›„ ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” fetch Policy

  • ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๋ณด๋‚ผ ์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— loadQuery ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์€ ๋ Œ๋”๋ง์„ ๋ง‰๋Š” usePreloadedQuery ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋กœ๋”ฉ์ƒํƒœ๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ Suspense boundary๋กœ MainContent ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ์‹ธ์ ธ์žˆ๋Š”์ง€ ์‚ดํŽด๋ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๋งŒ์•ฝ Suspense ๋ฅผ ํ”ผํ•ด์•ผํ•œ๋‹ค๋ฉด

๊ฒฝ์šฐ์— ๋”ฐ๋ผ Suspense ์˜ fallback ์„ ๋ณด์—ฌ์ฃผ์ง€ ์•Š๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ์—๋Š” ๋กœ๋”ฉ์ƒํƒœ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ถ”์ ํ• ์ˆ˜์žˆ๋Š” fetchQuery ๋ฅผ ๋Œ€์‹  ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ 

๋™์‹œ ๋ Œ๋”๋ง์ด ์ง€์›๋˜๋Š” ์ดํ›„์˜ React ๋ฒ„์ „์—์„œ๋Š” ์ด ๊ฒฝ์šฐ์— ๋Œ€ํ•ด์„œ ์ง€์›ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค Suspending ์‹œ์— ์ด๋ฏธ ๋ฆฌ๋ Œ๋”๋ง๋œ ๋‚ด์šฉ๋“ค์ด Suspense fallback ์œผ๋กœ ์ˆจ๊ฒจ์ง€๋Š” ๊ฒƒ์„ ํ”ผํ•˜๋„๋ก ํ•ด์ค„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

/**
 * App.react.js
 */
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';

const AppQuery = require('__generated__/AppQuery.graphql');

function App(props: Props) {
  const environment = useRelayEnvironment();
  const [queryRef, loadQuery] = useQueryLoader<AppQueryType>(
    AppQuery,
    props.appQueryRef /* initial query ref */
  );
  const [isRefetching, setIsRefetching] = useState(false)

  const refetch = useCallback(() => {
    if (isRefetching) { return; }
    setIsRefetching(true);

    // fetchQuery will fetch the query and write
    // the data to the Relay store. This will ensure
    // that when we re-render, the data is already
    // cached and we don't suspend
    fetchQuery(environment, AppQuery, variables)
      .subscribe({
        complete: () => {
          setIsRefetching(false);

          // *After* the query has been fetched, we call
          // loadQuery again to re-render with a new
          // queryRef.
          // At this point the data for the query should
          // be cached, so we use the 'store-only'
          // fetchPolicy to avoid suspending.
          loadQuery({id: 'different-id'}, {fetchPolicy: 'store-only'});
        }
        error: () => {
          setIsRefetching(false);
        }
      });
  }, [/* ... */]);

  return (
    <React.Suspense fallback="Loading query...">
      <MainContent
        isRefetching={isRefetching}
        refetch={refetch}
        queryRef={queryRef}
      />
    </React.Suspense>
  );
}
  • ์šฐ๋ฆฌ๊ฐ€ ์ƒˆ๋กœ๊ณ ์นจํ• ๋•Œ, ์ด์ œ๋Š” ์šฐ๋ฆฌ์˜ isRefreshing ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๊ณ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•ด์„œ MainContent ๊ตฌ์„ฑ ์š”์†Œ ๋‚ด์—์„œ MainContent๋ฅผ ์ˆจ๊ธฐ์ง€ ์•Š๊ณ  ์‚ฌ์šฉ ์ค‘์ธ ์Šคํ”ผ๋„ˆ ๋˜๋Š” ์œ ์‚ฌํ•œ ๋กœ๋”ฉ UI๋ฅผ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์—์„œ ๋จผ์ € fetchQuery๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ๋กœ์ปฌ ๋ฆด๋ ˆ์ด ์ €์žฅ์†Œ์— ๋ฐ์ดํ„ฐ๋ฅผ ์”๋‹ˆ๋‹ค. fetchQuery ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ์™„๋ฃŒ๋˜๋ฉด loadQuery๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์—…๋ฐ์ดํŠธ๋œ queryRef๋ฅผ ์–ป์€ ๋‹ค์Œ usePreloaded๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.์ฟผ๋ฆฌ๋Š” ์ด์ „ ์˜ˆ์ œ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์—…๋ฐ์ดํŠธ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.

  • ์ด๋•Œ loadQuery๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋กœ์ปฌ ๋ฆด๋ ˆ์ด ์ €์žฅ์†Œ์— ์ด๋ฏธ ์บ์‹œ๋˜์–ด ์žˆ์–ด์•ผ ํ•˜๋ฏ€๋กœ ์ผ์‹œ ์ค‘๋‹จ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ด๋ฏธ ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋งŒ ์ฝ๊ธฐ ์œ„ํ•ด 'store-only'์˜ fetchPolicy๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

useLazyLoadQuery ์‚ฌ์šฉ

Refreshing Queries with useLazyLoadQuery ์™€ ์œ ์‚ฌํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ refetch ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

/**
 * App.react.js
 */
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';

const AppQuery = require('__generated__/AppQuery.graphql');

function App(props: Props) {
  const [queryArgs, setQueryArgs] = useState({
    options: {fetchKey: 0},
    variables: {id: '4'},
  });

  const refetch = useCallback(() => {
    // Trigger a re-render of useLazyLoadQuery with new variables,
    // *and* an updated fetchKey.
    // The new fetchKey will ensure that the query is fully
    // re-evaluated and refetched.
    setQueryArgs(prev => ({
      options: {
        fetchKey: (prev?.options.fetchKey ?? 0) + 1,
      },
      variables: {id: 'different-id'}
    }));
  }, [/* ... */]);

  return (
    <React.Suspense fallback="Loading query...">
      <MainContent
        refetch={refetch}
        queryArgs={queryArgs}
      />
    </React.Suspense>
  );
}
/**
 * MainContent.react.js
 */
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';

// Fetches and renders the query, given the fetch options
function MainContent(props) {
  const {refetch, queryArgs} = props;
  const data = useLazyLoadQuery<AppQueryType>(
    graphql`
      query AppQuery($id: ID!) {
        user(id: $id) {
          name
          friends {
            count
          }
        }
      }
    `,
    queryArgs.variables,
    queryArgs.options,
  );

  return (
    <>
      <h1>{data.user?.name}</h1>
      <div>Friends count: {data.user.friends?.count}</div>
      <Button
        onClick={() => refetch()}>
        Fetch latest count
      </Button>
    </>
  );
}
  • ์ƒํƒœ์—์„œ์˜ ์ƒˆ ์˜ต์…˜์„ ์„ค์ •ํ•˜์—ฌ ์ƒˆ๋กœ ๊ณ ์น  ์ˆ˜ ์žˆ๋„๋ก ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด useLazyLoadQuery๋ฅผ ์‚ฌ์šฉํ•˜๋Š” MainContent ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ƒˆ ๋ณ€์ˆ˜์™€ fetchKey๋กœ ๋‹ค์‹œ ๋ Œ๋”๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ  ๋ Œ๋”๋ง ์‹œ ์ฟผ๋ฆฌ๋ฅผ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

  • ์—…๋ฐ์ดํŠธ ๋งˆ๋‹ค ์ฆ๊ฐ€ํ•˜๋Š” fetchKey ์˜ ์ƒˆ๋กœ์šด ๊ฐ’์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์—…๋ฐ์ดํŠธ์—์„œ LazyLoadQuery๋ฅผ ์‚ฌ์šฉํ•  ์ƒˆ fetchKey๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ์ฟผ๋ฆฌ๊ฐ€ ์™„์ „ํžˆ ์žฌํ‰๊ฐ€๋˜๊ณ  ๋‹ค์‹œ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

  • LazyLoadQuery๋ฅผ ์‚ฌ์šฉํ•  fetchPolicy๋ฅผ ์ „๋‹ฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฆ‰, 'store-or-network'์˜ ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ์ƒˆ๋กœ ๊ณ ์นจ ์‹œ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋Š” ์‚ฌ์šฉ ์ค‘์ธ fetchPolicy๋กœ ์ธํ•ด ํ•ญ์ƒ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ์ด๋ฃจ์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์— ์ปดํฌ๋„ŒํŠธ๊ฐ€ suspend ๋ฉ๋‹ˆ๋‹ค. ๋กœ๋”ฉ์ƒํƒœ๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ Suspense boundary๋กœ MainContent ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ์‹ธ์ ธ์žˆ๋Š”์ง€ ์‚ดํŽด๋ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๋งŒ์•ฝ Suspense ๋ฅผ ํ”ผํ•ด์•ผํ•œ๋‹ค๋ฉด

๊ฒฝ์šฐ์— ๋”ฐ๋ผ Suspense ์˜ fallback ์„ ๋ณด์—ฌ์ฃผ์ง€ ์•Š๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ์—๋Š” ๋กœ๋”ฉ์ƒํƒœ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ถ”์ ํ• ์ˆ˜์žˆ๋Š” fetchQuery ๋ฅผ ๋Œ€์‹  ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ 

๋™์‹œ ๋ Œ๋”๋ง์ด ์ง€์›๋˜๋Š” ์ดํ›„์˜ React ๋ฒ„์ „์—์„œ๋Š” ์ด ๊ฒฝ์šฐ์— ๋Œ€ํ•ด์„œ ์ง€์›ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค Suspending ์‹œ์— ์ด๋ฏธ ๋ฆฌ๋ Œ๋”๋ง๋œ ๋‚ด์šฉ๋“ค์ด Suspense fallback ์œผ๋กœ ์ˆจ๊ฒจ์ง€๋Š” ๊ฒƒ์„ ํ”ผํ•˜๋„๋ก ํ•ด์ค„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

/**
 * App.react.js
 */
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';

const AppQuery = require('__generated__/AppQuery.graphql');

function App(props: Props) {
  const environment = useRelayEnvironment();
  const [isRefreshing, setIsRefreshing] = useState(false)
  const [queryArgs, setQueryArgs] = useState({
    options: {fetchKey: 0, fetchPolicy: 'store-or-network'},
    variables: {id: '4'},
  });

  const refetch = useCallback(() => {
    if (isRefreshing) { return; }
    setIsRefreshing(true);

    // fetchQuery will fetch the query and write
    // the data to the Relay store. This will ensure
    // that when we re-render, the data is already
    // cached and we don't suspend
    fetchQuery(environment, AppQuery, variables)
      .subscribe({
        complete: () => {
          setIsRefreshing(false);

          // *After* the query has been fetched, we update
          // our state to re-render with the new fetchKey
          // and fetchPolicy.
          // At this point the data for the query should
          // be cached, so we use the 'store-only'
          // fetchPolicy to avoid suspending.
          setQueryArgs(prev => ({
            options: {
              fetchKey: (prev?.options.fetchKey ?? 0) + 1,
              fetchPolicy: 'store-only',
            },
            variables: {id: 'different-id'}
          }));
        },
        error: () => {
          setIsRefreshing(false);
        }
      });
  }, [/* ... */]);

  return (
    <React.Suspense fallback="Loading query...">
      <MainContent
        isRefetching={isRefetching}
        refetch={refetch}
        queryArgs={queryArgs}
      />
    </React.Suspense>
  );
}
  • ์šฐ๋ฆฌ๊ฐ€ ์ƒˆ๋กœ๊ณ ์นจํ• ๋•Œ, ์ด์ œ๋Š” ์šฐ๋ฆฌ์˜ isRefreshing ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๊ณ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•ด์„œ MainContent ๊ตฌ์„ฑ ์š”์†Œ ๋‚ด์—์„œ MainContent๋ฅผ ์ˆจ๊ธฐ์ง€ ์•Š๊ณ  ์‚ฌ์šฉ ์ค‘์ธ ์Šคํ”ผ๋„ˆ ๋˜๋Š” ์œ ์‚ฌํ•œ ๋กœ๋”ฉ UI๋ฅผ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์—์„œ ๋จผ์ € fetchQuery๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ๋กœ์ปฌ ๋ฆด๋ ˆ์ด ์ €์žฅ์†Œ์— ๋ฐ์ดํ„ฐ๋ฅผ ์”๋‹ˆ๋‹ค. fetchQuery ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ์™„๋ฃŒ๋˜๋ฉด ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ์—…๋ฐ์ดํŠธ๋œ fetchKey์™€ fetchPolicy๋ฅผ ๋‹ค์‹œ ๋ Œ๋”ํ•œ ๋‹ค์Œ useLazyLoadQuery๋กœ ์ „๋‹ฌํ•˜์—ฌ ์ด์ „ ์˜ˆ์ œ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์—…๋ฐ์ดํŠธ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.

  • ์ด ์‹œ์ ์—์„œ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•  ๋•Œ ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋กœ์ปฌ ๋ฆด๋ ˆ์ด ์ €์žฅ์†Œ์— ์ด๋ฏธ ์บ์‹œ๋˜์–ด์•ผ ํ•˜๋ฏ€๋กœ ์ผ์‹œ ์ค‘๋‹จ์„ ํ”ผํ•˜๊ณ  ์ด๋ฏธ ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋งŒ ์ฝ๊ธฐ ์œ„ํ•ด 'store-only'์˜ fetchPolicy๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

Last updated