Published on

[번역] Relay - Graphql Arrays and Lists

Authors
  • avatar
    Name
    Kim, Dong-Wook
    Twitter

GraphQL Arrays and Lists

여태까지 우리는 조립된 하나의 컴포넌트 인스턴스를 다루었다. 예를 들면 단 하나의 뉴스피드 스토리에서 또 하나의 저자와 해당 저자의 프로필 사진을 보여주는 것과 같이 말이다. 그러면 이제 하나 이상의 컴포넌트 인스턴스를 다루는 방법을 알아보자.

GraphQL은 lists라고 불리는 array를 지원한다. 즉 field가 단 하나의 edge가 아닌 edge들의 배열이 될 수 있다는 것이다. 스키마는 각각의 field가 list인지 아닌지 지정하고 있다, 그러나 이상하게도 GraphQL 쿼리 문법상 해당 field가 array인지 아닌지 구분하지 않는다. 이는 GraphQL 응답이 query와 같은 모양이어야 한다는 설계 원칙에 대한 기발한? 예외입니다.

즉 아래와 같은 요청을 보내면


query MyQuery {
  viewer {
    contacts { // List of edges
      id // field on a single item
      name
    }
  }
}

아래와 같은 응답이 온다

{
  "viewer": {
    "contacts": [
      // array in response
      {
        "id": "123",
        "name": "Chris"
      },
      {
        "id": "789",
        "name": "Sue"
      }
    ]
  }
}

위의 결과를 가지고 우리는 Story 리스트를 반환하는 앱을 만들 것이다(이전까지는 단 하나의 Story만 반환했다)

우리의 뉴스피드에 여러개의 Story를 보여주기 위해서, Newsfeed.tsx 컴포넌트를 수정할 것이다

Step 1 - Select a list in the fragment

먼저, Newsfeed.tsx 파일에서 NewsfeedQuery를 찾고, topStory쿼리를 topStories로 바꾼다.

const NewsfeedQuery = graphql`
  query NewsfeedQuery {
    topStories {
      ...StoryFragment
    }
  }
`

Step 2 -- Map over the list in the component

Newsfeed 컴포넌트에서, data.topStories는 fragment ref들의 배열이다. 각각의 요소들은 Story 컴포넌트에 넘겨져 story를 렌더링할 것이다.

export default function Newsfeed({}) {
  const data = useLazyLoadQuery<NewsfeedQueryType>(NewsfeedQuery, {})
  // 추후 매우 중요한 요소
  const stories = data.topStories
  return (
    <div className="newsfeed">
      {stories.map((story) => (
        <Story story={story} />
      ))}
    </div>
  )
}

Step 3 -- Add a React key based on the node ID

이 지점에서, 화면에서 Story들의 list가 그려진다. 그렇지만, 당연하게도 list 컴포넌트를 그릴때 각 컴포넌트들에 key 속성을 부여하지 않았음으로 에러가 발생한다. 이 부분은 특히 중요한데, key를 제공함으로써 list 아이템이 재배치 되거나 삭제될때 React가 적절한 아이템을 선택하도록 만들어주기 때문에 꼭 key를 제공해야한다

다행히도 GraphQL 노드는 보통 ID를 가지고 있다. 즉 Graphql 쿼리에서 id를 선택해서, 해당 id를 컴포넌트 렌더링시 key로 넘겨주면 된다.


const NewsfeedQuery = graphql`
  query NewsfeedQuery {
    topStories {
      id
      ...StoryFragment
    }
  }
`;

...

export default function Newsfeed({}) {
  const data = useLazyLoadQuery<NewsfeedQueryType>(NewsfeedQuery, {});
  const stories = data.topStories;
  return (
    <div className="newsfeed">
      {stories.map(story => (
        <Story
          key={story.id}
          story={story}
        />
      )}
    </div>
  );
}

이것으로 우리는 화면에 Story 들을 그릴 수 있게 되었다. 쿼리에서 같은 공간에 fragment들을 펼침으로써 여러개의 field를 혼합하여 사용할 수 있기 때문에 매우 중요한 부분이다. 이것이 의미하는 바는 Newsfeed가 원하는 필드들을 선택(useLazyLoadQuery Hook을 통해서)할 수 있다는 것 뿐만 아니라 Story 컴포넌트 또한 관심있는 필드들을 선택할 수 있다는 것이다.(useFragment Hook을 통해서) 즉, 같은 객체가 Newsfeed가 선택한 id와 StoryFragment를 위한 fragment key 모두 가지고 있다.

Tip

GraphQL List들은 collection들을 다루는데 가장 기초적인 방법이다. 이후 자세히 설명하겠지만, 우리는 Connection이라는 특별한 시스템을 사용해서 Pagination 또는 Infinite scrolling을 실습할 것이다. 해당 내용을 배우게 된다면, 아이템 콜렉션을 가지는 상황에서 Connection을 적극적으로 사용할 것이다.(단, 같은 GraphQL List building 블럭을 사용하면서 말이다)