Step-by-step Guide

Step 1. Create React App

# NPM
npx create-react-app your-app-name

# Yarn
yarn create react-app your-app-name
# NPM
cd your-app-name
npm start

# Yarn
cd your-app-name
yarn start

Step 2. Fetch GraphQL (Relay ์—†์ด fetch() ์‚ฌ์šฉ)

2.1. GitHub GraphQL Authentication

# your-app-name/.env.local
REACT_APP_GITHUB_AUTH_TOKEN=<TOKEN>

2.2. A fetchGraphQL Helper

// your-app-name/src/fetchGraphQL.js
async function fetchGraphQL(text, variables) {
	...

  // Fetch data from GitHub's GraphQL API:
  const response = await fetch('https://api.github.com/graphql', {
    method: 'POST',
    ...,
    body: JSON.stringify({
      query: text,
      variables,
    }),
  });

  // Get the response as JSON
  return await response.json();
}

export default fetchGraphQL;

2.3. Fetching GraphQL From React

// your-app-name/src/App.js
...

function App() {
  // We'll load the name of a repository, initially setting it to null
  const [name, setName] = useState(null);

  // When the component mounts we'll fetch a repository name
  useEffect(() => {
    let isMounted = true;
    fetchGraphQL(`
      query RepositoryNameQuery {
        # feel free to change owner/name here
        repository(owner: "facebook" name: "relay") {
          name
        }
      }
    `).then(response => {
      // Avoid updating state if the component unmounted before the fetch completes
			// ๋ฐ์ดํ„ฐ ํŒจ์นญ์ด ์™„๋ฃŒ๋˜๊ธฐ ์ „ unMount ๋˜๋Š” ๊ฒฝ์šฐ ์ฒ˜๋ฆฌ
      if (!isMounted) {
        return;
      }
      const data = response.data;
      setName(data.repository.name);
    }).catch(error => {
      console.error(error);
    });

    return () => {
      isMounted = false;
    };
  }, [fetchGraphQL]);

  // Render "Loading" until the query completes
  return (
    <div className="App">
      <header className="App-header">
        <p>
          {name != null ? `Repository: ${name}` : "Loading"}
        </p>
      </header>
    </div>
  );
}

export default App;

Step 3. When To Use Relay (Relay ์‚ฌ์šฉํ•ด ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ)

step2๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ทœ๋ชจ๊ฐ€ ์ปค์ง€๋Š” ์†๋„์™€ ํฌ๊ธฐ์— ๋Œ€์‘ํ•˜๊ธฐ ์–ด๋ ค์›Œ์ง„๋‹ค.

์ด ๋•Œ Relay๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋น ๋ฅด๊ณ  ๋ฏฟ์„ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ์‹์œผ๋กœ ์ด์— ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด์œ ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. GraphQL ํ”„๋ž˜๊ทธ๋จผํŠธ, ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ, mutations์„ ์ปดํฌ๋„ŒํŠธ์— ์œ„์น˜์‹œ์ผœ ๋ฐ์ดํ„ฐ ์˜์กด์„ฑ์„ ๋ชจ์•„๋†“๊ธฐ (colocating)

Step 4. Adding Relay To Our Project

Relay๋Š” 3๊ฐ€์ง€ ํ•ต์‹ฌ์œผ๋กœ ์ด๋ฃจ์–ด์ ธ์žˆ๋‹ค.

  1. relay-compiler : ์ปดํŒŒ์ผ๋Ÿฌ (๋นŒ๋“œ ํƒ€์ž„์— ์‚ฌ์šฉ๋จ)

  2. relay-runtime : ์ฝ”์–ด ๋Ÿฐํƒ€์ž„ (React ์นœํ™”์ )

  3. react-relay : React integration layer

# NPM Users
npm install --save relay-runtime react-relay
npm install --save-dev relay-compiler babel-plugin-relay

# Yarn Users
yarn add relay-runtime react-relay
yarn add --dev relay-compiler babel-plugin-relay

4.1. Configure Relay Compiler

Relay example app์˜ .graphql ์Šคํ‚ค๋งˆ ์นดํ”ผ๋ฅผ ๋‹ค์šด๋กœ๋“œ ๋ฐ›๊ธฐ ์œ„ํ•ด

cd your-app-name
curl https://raw.githubusercontent.com/relayjs/relay-examples/main/issue-tracker/schema/schema.graphql > schema.graphql

package.json ์„ค์ •์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

// your-app-name/package.json
{
  ...
  "scripts": {
    ...
    "start": "yarn run relay && react-scripts start",
    "build": "yarn run relay && react-scripts build",
    "relay": "yarn run relay-compiler $@"
    ...
  },
  "relay": {
    "src": "./src/",
    "schema": "./schema.graphql"
  }
  ...
}
cd your-app-name
yarn start

์—ฌ๊ธฐ์„œ, GraphQL์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด Relay๋Š” ์ด๋ฅผ ๊ฐ์ง€ํ•˜์—ฌ ํ•ด๋‹น ํ”„๋กœ์ ํŠธ์—์„œ ์ž‘์„ฑํ•œ ์ฟผ๋ฆฌ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์ฝ”๋“œ๋ฅผ your-app-name/src/__generated__/ ์— ์ƒ์„ฑํ•œ๋‹ค.

4.2. Configure Relay Runtime

์ปดํŒŒ์ผ๋Ÿฌ ์„ค์ •์ด ์™„๋ฃŒ๋˜์—ˆ์œผ๋‹ˆ ๋Ÿฐํƒ€์ž„์„ ์„ธํŒ…ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๋Š” Relay์—๊ฒŒ ์šฐ๋ฆฌ์˜ GraphQL ์„œ๋ฒ„์™€ ์–ด๋–ป๊ฒŒ ์—ฐ๊ฒฐํ•  ๊ฒƒ์ธ์ง€์— ๋Œ€ํ•ด ์•Œ๋ ค์ฃผ๋Š” ๊ฒƒ๊ณผ ๊ฐ™๋‹ค.

์œ„์˜ ์ฝ”๋“œ๋ฅผ ๋™์ผํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋˜, ์ถ”๊ฐ€์ ์œผ๋กœ Relay Environment ๋ฅผ ์ •์˜ํ•œ๋‹ค. ์ด๋Š” ์„œ๋ฒ„ (Relay Network) ์— ์ €์žฅ๋œ ์บ์‹œ๋ฅผ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•  ๊ฒƒ์ธ์ง€์— ๋Œ€ํ•ด ์บก์Šํ™”ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

// your-app-name/src/RelayEnvironment.js
import {Environment, Network, RecordSource, Store} from 'relay-runtime';
import fetchGraphQL from './fetchGraphQL';

// Relay passes a "params" object with the query name and text. So we define a helper function
// to call our fetchGraphQL utility with params.text.
async function fetchRelay(params, variables) {
  console.log(`fetching query ${params.name} with ${JSON.stringify(variables)}`);
  return fetchGraphQL(params.text, variables);
}

// Export a singleton instance of Relay Environment configured with our network function:
export default new Environment({
  network: Network.create(fetchRelay),
  store: new Store(new RecordSource()),
});

Step 5. Fetching a Query With Relay

...
import fetchGraphQL from './fetchGraphQL';
import graphql from 'babel-plugin-relay/macro';
import {
  RelayEnvironmentProvider,
  loadQuery,
  usePreloadedQuery,
} from 'react-relay/hooks';
import RelayEnvironment from './RelayEnvironment';

const { Suspense } = React;

// Define a query
const RepositoryNameQuery = graphql`
  query AppRepositoryNameQuery {
    repository(owner: "facebook", name: "relay") {
      name
    }
  }
`;

// ์•ฑ์ด ์‹œ์ž‘ํ•˜์ž๋งˆ์ž ์ฟผ๋ฆฌ๋ฅผ ์ฆ‰์‹œ ๋กœ๋“œํ•œ๋‹ค.
// ์‹ค์ œ ์•ฑ์—์„œ๋Š” ๋ผ์šฐํŒ… configuration์— ์ด๋ฅผ ์„ค์ •ํ•˜๊ณ , ์ƒˆ๋กœ์šด route๋กœ ์ด๋™ ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ํ”„๋ฆฌ-๋กœ๋“œํ•œ๋‹ค.
const preloadedQuery = loadQuery(RelayEnvironment, RepositoryNameQuery, {
  /* query variables */
});

// ํ”„๋ฆฌ๋กœ๋“œ๋œ ์ฟผ๋ฆฌ๋ฅผ ์ฝ๋Š” ์ด๋„ˆ ์ปดํฌ๋„ŒํŠธ๋Š” `usePreloadedQuery`๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
// - ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰ ์™„๋ฃŒ๋˜๋ฉด, ์ฟผ๋ฆฌ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.
// - ์ฟผ๋ฆฌ๊ฐ€ ์•„์ง ํŽœ๋”ฉ ์ค‘์ด๋ฉด, Suspend ํ•œ๋‹ค. ์ด๋Š” ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ ์ค‘ ๊ฐ€์žฅ ๊ฐ€๊นŒ์ด ์œ„์น˜ํ•œ fallback์— ๊ทผ๊ฑฐํ•œ๋‹ค.
// - ์ฟผ๋ฆฌ๊ฐ€ (๋ฐ์ดํ„ฐ ํŒจ์นญ์„) ์‹คํŒจํ•˜๋ฉด, ์‹คํŒจ ์—๋Ÿฌ๋ฅผ ๋„์šด๋‹ค.
function App(props) {
  const data = usePreloadedQuery(RepositoryNameQuery, props.preloadedQuery);

  return (
    <div className="App">
      <header className="App-header">
        <p>{data.repository.name}</p>
      </header>
    </div>
  );
}

// ์œ„ App ์ปดํฌ๋„ŒํŠธ๋Š” ์–ด๋–ป๊ฒŒ Relay Environment์— ์ ‘๊ทผํ• ์ง€์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ์žˆ์–ด์•ผ ํ•˜๊ณ ,
// Suspend ๊ฒฝ์šฐ๋ฅผ ์œ„ํ•œ fallback์„ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค.
function AppRoot(props) {
  return (
    <RelayEnvironmentProvider environment={RelayEnvironment}>
      <Suspense fallback={'Loading...'}>
        <App preloadedQuery={preloadedQuery} />
      </Suspense>
    </RelayEnvironmentProvider>
  );
}

export default AppRoot;
  1. RepositoryNameQuery

    • ์ฟผ๋ฆฌ๋ฅผ ์ •์˜ํ•œ๋‹ค.

  2. preloadQuery

    • ์‚ฌ์ „์— ์ •์˜ํ•œ RelayEnvironment์™€ RepositoryNameQuery, ์ฟผ๋ฆฌ ๋ณ€์ˆ˜(args)๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.

  3. AppRoot

    • <RelayEnvironmentProvider> ์€ ํ˜„์žฌ Relay Environment ์ธ์Šคํ„ด์Šค์™€์˜ ์†Œํ†ต ๋ฐฉ์‹์„ child ์ปดํฌ๋„ŒํŠธ์— ์ „๋‹ฌํ•œ๋‹ค.

    • <Suspense> ๋Š” child๊ฐ€ suspendํ•  ๊ฒฝ์šฐ์˜ fallback์„ ์ง€์ •ํ•œ๋‹ค.

Last updated