Relay: fragments (KR translation)

Reference: https://relay.dev/docs/tutorial/fragments-1/

Fragment는 Relay에서 주된 피쳐들 중 하나이다.

싱글 쿼리의 효율성을 유지하면서 각각의 컴포넌트들이 그들의 자신만의 데이터 요구사항을 각자 선언할 수 있게 한다.

이 섹션에서, 우리는 하나의 쿼리를 어떻게 여러 fragment들로 분해할 수 있는지 보여줄 것 입니다.


시작하기에 앞서, Story 컴포넌트가 story가 포스팅된 날짜를 보여주고 싶다고 가정해봅시다.

그렇게 하려면, 서버로 부터 더 많은 데이터가 필요합니다. 그래서 우리는 쿼리에 필드를 추가 할 것 입니다.

Newsfeed.tsx 로 가서 NewsfeedQuery 를 찾아, 새로운 필드를 추가해봅시다:

const NewsfeedQuery = graphql`
  query NewsfeedQuery {
    topStory {
      title
      summary
      createdAt // Add this line
      poster {
        name
        profilePicture {
          url
        }
      }
      image {
        url
      }
    }
  }
`;

이제 Story.tsx 로 가서 날짜를 보여주도록 변경해 봅시다.

import Timestamp from './Timestamp';

type Props = {
  story: {
    createdAt: string; // Add this line
    ...
  };
};

export default function Story({story}: Props) {
  return (
    <Card>
      <PosterByline person={story.poster} />
      <Heading>{story.title}</Heading>
      <Timestamp time={story.createdAt} /> // Add this line
      <Image image={story.image} />
      <StorySummary summary={story.summary} />
    </Card>
  );
}

이제 날짜가 보여져야 합니다. GraphQL 덕분에, 우리는 새로운 서버코드를 배포하지 않아도 되었습니다.

하지만 만약 이걸 생각해본다면, Newsfeed.tsx 를 변경해야 한다면? 리액트 컴포넌트 자체가 포함되어 있지 않다면? 왜 Newsfeed가 Story 컴포넌트가 필요로 하는 특정 데이터를 생각해야 할까? 만약 데이터가 Story의 자식 컴포넌트에 의해 요구된다면? 만약 컴포넌트가 다른 여러곳에서 쓰이는 것이라면?

그렇다면 우리는 데이터 요구사항이 변경된다면 많은 여러 컴포넌트를 변경해줘야 할 것입니다.

이걸 피하기 위해서 그리고 다른 많은 문제들은 피하기 위해서, 우리는 Story를 위한 데이터 요구사항들을 Story.tsx 로 옮길 수 있습니다.

Story 의 데이터 요구사항을 Story.tsx 로 fragment로 분리합니다. Fragment는 GraphQL의 각각 나눠진 조각들입니다. Relay 컴파일러가 완성된 쿼리들로 모아 사용합니다. Fragment는 각각 컴포넌트가 그들 자신만의 데이터 요구사항을 정의할 수 있도록 합니다.

Story 의 데이터 요구사항들을 이제 fragment로 분해 해 봅시다.


Step 1 — Define a fragment

Story.tsx 에 StoryFragment를 추가합시다.

import { graphql } from 'relay-runtime';

const StoryFragment = graphql`
  fragment StoryFragment on Story {
    title
    summary
    createdAt
    poster {
      name
      profilePicture {
        url
      }
    }
    thumbnail {
      url
    }
  }
`;

topStory 안에 우리는 모든 셀렉션들을 가져왔습니다. 그리고 그들을 우리는 새로운 Fragment 선언으로 복사했습니다. 쿼리와 비슷하게, fragment들은 이름이 있습니다 (StoryFragment ), 우리는 이걸 잠시동안 사용할 것이고, 하지만 그들은 GraphQL 타입인 (Story)를 가지고 있습니다. “on”. 이 뜻은 이 fragment가 Story 노드를 가지고 있는 곳에서 모두 쓰일 수 있다는 의미입니다.

Step 2 — Spread the fragment

Go to Newsfeed.tsx and modify NewsfeedQuery to look like this:

const NewsfeedQuery = graphql`
  query NewsfeedQuery {
    topStory {
      ...StoryFragment
    }
  }
`;

Step 3 — Call useFragment

import { useFragment } from 'react-relay';

export default function Story({story}: Props) {
  const data = useFragment(
    StoryFragment,
    story,
  );
  return (
    <Card>
      <Heading>{data.title}</Heading>
      <PosterByline person={data.poster} />
      <Timestamp time={data.createdAt} />
      <Image image={data.image} />
      <StorySummary summary={data.summary} />
    </Card>
  );
}

useFragment 는 두가지 인자를 가집니다:

  • Graphql tagged string: 우리가 읽고 싶은 fragment의 literal
  • story object: fragment Key

Step 4 — Typescript types for fragment refs

import type {StoryFragment$key} from './__generated__/StoryFragment.graphql';

type Props = {
  story: StoryFragment$key;
};

Reusing a Fragment in Multiple Places

fragment는 말합니다, 각 노드에서 어떤 데이터를 읽기 원하는지에 대한 특정 타입의 그래프 노드들이 주어집니다. fragment 키는 그래프의 어떤 노드가 데이터가 셀렉트 되었는지에 대해 특정합니다.

Step 1 — Define the fragment

Image.tsx

import { graphql } from 'relay-runtime';

const ImageFragment = graphql`
  fragment ImageFragment on Image {
    url
  }
`;

Step 2 — Spread the fragment

Story.tsx

const StoryFragment = graphql`
  fragment StoryFragment on Story {
    title
    summary
    postedAt
    poster {
      ...PosterBylineFragment
    }
    thumbnail {
      ...ImageFragment
    }
  }
`;

PosterByline.tsx

const PosterBylineFragment = graphql`
  fragment PosterBylineFragment on Actor {
    name
    profilePicture {
      ...ImageFragment
    }
  }
`;

Step 3 — Call useFragment

Image.tsx

import { useFragment } from 'react-relay';
import type { ImageFragment$key } from "./__generated__/ImageFragment.graphql";

type Props = {
  image: ImageFragment$key;
  ...
};

function Image({image}: Props) {
  const data = useFragment(ImageFragment, image);
  return <img key={data.url} src={data.url} ... />
}

Step 4 — Modify once, enjoy everywhere

const ImageFragment = graphql`
  fragment ImageFragment on Image {
    url
    altText
  }
`;

function Image({image}) {
  // ...
  <img
    alt={data.altText}
  //...
}

← Go home