5편 - React + GraphQL + Flowtype으로 트렐로 클론 웹앱 만들기 - 리스트(List), 카드(Card)

이전 편에서 우리는 graphql mutation 후에 스토어를 업데이트 할 수 있는 방법에 대해 다루고, 이 중 refetchQueries 옵션을 사용하여 스토어를 업데이트 하는 방법을 적용 했다. 또한, Flowtype의 기본적인 이론을 커버하고, 실제 소스에 적용 해봤다.
이번 편에서 우리는 List, Card graphql type을 만들고, graphql의 가장 큰 장점 중 하나인 relational data를 graphcool에서 다루는 방법에 대해서 다루도록 하겠다.
참고: 이번 튜토리얼을 진행 하기 위해 진행 되는 코드를 깃헙에 업로드 해놨으니, 이 튜토리얼을 진행하고 싶다면 아래에서 소스를 클론 받은 후 아래의 내용을 따라가기를 추천 드립니다.
튜토리얼 소스:
https://github.com/simsim0709/react-apollo-flow-trello-clone/tree/ch-5
이전 편:
0편: React + GraphQL + Flowtype으로 트렐로 클론 웹앱 만들기 - 소개
1편: React + GraphQL + Flowtype으로 트렐로 클론 웹앱 만들기 - 프로토타입
2편: React + GraphQL + Flowtype으로 트렐로 클론 웹앱 만들기 - graphcool (GraphQL 서버 만들기)
3편: React + GraphQL + Flowtype으로 트렐로 클론 웹앱 만들기 - Trello Board 생성
4편: React + GraphQL + Flowtype으로 트렐로 클론 웹앱 만들기 - Apollo 스토어 업데이트, Flow

먼저 List와 Card graphql 타입을 추가 한다.
// 소스에 graphcool-server/types.graphql을 열어보자. type Board @model { id: ID! @isUnique createdAt: DateTime! updatedAt: DateTime! name: String! description: String lists: [List!]! @relation(name: "ListOnBoard") // (1) # owner: User! @relation(name: "UserBoards") } type List @model { id: ID! @isUnique createdAt: DateTime! updatedAt: DateTime! name: String board: Board! @relation(name: "ListOnBoard") // (2) cards: [Card!]! @relation(name: "CardOnList") // (3) } type Card @model { id: ID! @isUnique createdAt: DateTime! updatedAt: DateTime! name: String list: List! @relation(name: "CardOnList") // (4) }
한개의 Board에는 여러개의 List를 가질 수 있다. 또한, 한개의 List는 여러개의 Card를 가질 수 있다. 그러므로, Board와 List는 one to many 관계이고, List와 Card역시 one to many의 관계가 된다. 이러한 데이터 간의 관계를 graphql에서는 type을 선언해주는 것으로 간단히 해결 할 수 있다.
위의 소스 주석 (1), (2), (3), (4) 을 보면, field의 타입을 scalar 타입을 선언하는 대신, model 타입을 선언 한 걸 알 수있다. 이렇게 scalar 타입 대신 model 타입을 선언 해주면 선언한 model 타입의 데이터가 리턴 된다. 하지만, 여기서 오해하지 않아야 할 것이 이렇게 선언만 한다고, graphql이 알아서 데이터를 찾아 와주는 것은 아니다. 우리가 만약 graphql 백엔드를 직접 만들었다면 이 부분을 resolver를 통해 로직을 구현 해야 하지만, 우리는 graphcool 서비스를 사용하고 있기 때문에 graphcool에서 제공하는 커스텀 디렉티브인 @relation 을 사용해서 이를 처리 해줘야 한다.
위의 소스에서 relation 디렉티브 부분을 보면 알 수 있듯이, name 값을 사용 하여 두 타입간의 관계를 선언 했다.
더 자세한 내용은 공식 문서를 확인하자.
자, 이제 수정된 types.graphql을 graphcool 서비스에 deploy 한다. (터미널에서 프로젝트 폴더로 이동 후에 아래 명령어를 실행 한다.)
cd graphcool-server && graphcool deploy
디플로이가 완료 됐다면 플레이그라운드를 실행해서, 데이터가 정상적으로 query, muation이 되는지 확인 해보자.
graphcool palyground
플레이그라운드에서 임의로 보드를 만들어 보자:
mutation { createBoard(name: "sample", description:"sample") { id } }
presentation
여기에서 생성된 id 값을 복사 해놓고, 왼쪽 하단에 QUERY VARIABLES 부분에 방금 생성된 id를 값을 넣어 준다:
{ "boardId": "cjb5xk0t7np320199avw13vi7" }
그리고 아래처럼 List 데이터를 쿼리 해본다 (소스: pages/BoardPage.js):
query AllListsByBoardIdQuery($boardId: ID) { allLists(filter: { board: { id: $boardId } }) { id name cards { id createdAt name } } }
그럼 당연히 빈 데이터가 나올 것이다.
presentation
List를 mutation을 사용하여 생성 해보자(components/ListCreateButton.js).
mutation CreateListMutation($boardId: ID!, $name: String) { createList(boardId: $boardId, name: $name) { id } }
여기에 boardId는 위에서 사용한 id 값을, name은 임의의 string 값을 넣고 생성 해본후 위에 List query를 다시 해보면 방금 생성한 데이터가 배열로 나올 것이다.
자, 이 과정이 에러없이 진행 됐다면 성공 한 것이다. 그럼 각각의 리스트에 들어가는 Card도 playground에서 테스트 해보자. 과정은 List를 만들었던 것과 동일 하다.
먼저, List mutation을 하고 나온 id를 복사 한 후, listId를 복사한 id로 갖는 Card mutation을 진행한다(components/List.js):
mutation CreateCardMutation($listId: ID!, $name: String!) { createCard(listId: $listId, name: $name) { id } }
이렇게 샘플로 몇개의 카드를 만들어 보자.
이게 완료 됐다면, 다시 한번 위의 List query를 다시 해보자. 그러면 cards 필드에 배열로 데이터가 들어온 것을 알 수 있다. relational database에서 join으로 해결해야 하는 것을 type을 선언 해주는 것으로 간단하게 해결 했다.
이 과정이 에러 없이 진행 됐다면, 나머지 소스 부분은 앞에서 한 과정과 같으니 직접 소스를 확인 해보며 진행 하면 무리가 없을 것으로 보인다.
이번 편은 여기서 마무리 짖고, 다음편에서는 카드 상세 뷰를 만들어 보자.