- 본 시리즈에서는 How to GraphQL의 Tutorial 문서들을 차례대로 번역합니다.
- 이 글은 GraphQL React + Apollo Tutorial - Mutations: Creating Links을 번역한 글입니다.
- 본 시리즈는 GraphQL Basic and Advanced 시리즈와 GraphQL-Node Tutorial 시리즈에서 이어집니다. GraphQL을 처음 접하는 분들은 해당 시리즈를 먼저 읽고 오시는 것을 추천드립니다.
- 오역 또는 의역이 있을 수 있습니다. 양해 부탁드리며, 수정이 필요한 부분은 댓글로 요청해주세요.
이번 장에서는 Apollo를 사용하여 뮤테이션을 전송하는 방법을 배웁니다. 쿼리를 전송하는 것과 크게 다르지 않고 똑같이 세 단계를 거치는데, 약간의 (하지만 논리적인) 차이점이 마지막 두 단계에 존재합니다.
gql
파서 함수를 사용하여 자바스크립트 상수로 뮤테이션을 작성합니다.
<Mutation />
컴포넌트에 GraphQL 뮤테이션과 (필요한 경우) 변수를 props
로 전달하여 사용합니다.
컴포넌트의 Render Props
함수에 주입된 뮤테이션 함수를 사용합니다.
이전과 마찬가지로, 사용자가 새로운 링크를 추가할 수 있는 React 컴포넌트를 우선 만들어봅시다.
src/components
디렉토리에CreateLink.js
라는 새로운 파일을 만들고 아래의 코드를 입력합니다.
($ .../hackernews-react-apollo/src/components/CreateLink.js)import React, { Component } from 'react' class CreateLink extends Component { state = { description: '', url: '', } render() { const { description, url } = this.state return ( <div> <div className="flex flex-column mt3"> <input className="mb2" value={description} onChange={e => this.setState({ description: e.target.value })} type="text" placeholder="A description for the link" /> <input className="mb2" value={url} onChange={e => this.setState({ url: e.target.value })} type="text" placeholder="The URL for the link" /> </div> <button onClick={`...🔜 이 부분을 구현할 겁니다 `}>Submit</button> </div> ) } } export default CreateLink
사용자가 만들고자 하는 Link
의 url
과 description
을 제공할 수 있는 두 개의 input
필드를 가진, 아주 전형적인 React 컴포넌트 구성입니다. 이 필드에 입력되는 데이터는 컴포넌트의 state
에 저장되고, 뮤테이션이 전송될 때에 사용될 겁니다.
하지만 서버에 뮤테이션을 어떻게 전송할까요? 이전 장에서 사용했던 3단계를 그대로 따라가도록 하겠습니다.
우선, 자바스크립트 코드로 뮤테이션을 정의하고, 컴포넌트를 graphql
컨테이너로 감싸야 합니다. 쿼리를 다룰 때에 했던 것과 비슷한 작업입니다.
CreateLink.js
파일에 아래와 같은 코드를 추가합니다.
($ .../hackernews-react-apollo/src/components/CreateLink.js)const POST_MUTATION = gql` mutation PostMutation($description: String!, $url: String!) { post(description: $description, url: $url) { id createdAt url description } } `
또한,
button
요소를 아래의 코드로 대체합니다.
($ .../hackernews-react-apollo/src/components/CreateLink.js)<Mutation mutation={POST_MUTATION} variables={{ description, url }}> {() => ( <button onClick={`...🔜 이 부분을 구현할 겁니다 `}> Submit </button> )} </Mutation>
위 코드를 한번 살펴보겠습니다.
우선, POST_MUTATION
이라는 자바스크립트 상수를 만들어 뮤테이션을 저장합니다.
이제, POST_MUTATION
을 props
로 전달하는 <Mutation />
컴포넌트의 Render Prop
함수로 button
요소를 감쌉니다.
마지막으로 description
과 url
를 variables
이라는 이름의 props
로 전달합니다.
다음으로 진행하기 전에, Apollo 의존성을 불러와야 합니다. 아래 코드를
CreateLink.js
파일의 최상단에 추가합니다.
($ .../hackernews-react-apollo/src/components/CreateLink.js)import { Mutation } from 'react-apollo' import gql from 'graphql-tag'
이제, 뮤테이션이 작동하도록 코드를 만들어봅시다!
CreateLink.js
파일에서<Mutation />
컴포넌트를 아래의 내용으로 대체합니다.
($ .../hackernews-react-apollo/src/components/CreateLink.js)<Mutation mutation={POST_MUTATION} variables={{ description, url }}> {postMutation => <button onClick={postMutation}>Submit</button>} </Mutation>
앞에서 말씀드렸듯이, 단지 <Mutation />
컴포넌트의 Render Props
함수를 통하여 주입된 함수 postMutation
을 버튼의 onClick
내부에서 실행하도록 하기만 하면 됩니다.
뮤테이션이 제대로 작동하는지 확인해봅시다. 코드를 테스트할 수 있도록,
App.js
파일을 열고render
함수를 아래와 같이 수정합니다.
($ .../hackernews-react-apollo/src/components/App.js)render() { return <CreateLink /> }
다음으로,
App.js
파일의 최상단에 아래의 코드를 추가하여CreateLink
컴포넌트를 불러옵니다.
($ .../hackernews-react-apollo/src/components/App.js)import CreateLink from './CreateLink'
이제 yarn start
를 실행하면, 아래와 같은 화면이 보일 겁니다.
두 개의 input
필드와 submit 버튼입니다. 썩 이쁘지는 않지만, 그래도 제 기능은 다 합니다.
필드에 데이터를 적어보겠습니다.
The best learning resource for GraphQL
www.howtographql.com
그리고 subimt 버튼을 눌러보세요. UI가 바로 바뀌지는 않겠지만, 그래도 뮤테이션이 제대로 이루어졌는지 확인해보도록 하죠. Playground에서 현재 링크의 리스트를 확인해보겠습니다.
브라우저에서 http://localhost:4000
에 접속하면 Playground를 열 수 있습니다. 거기서 아래의 쿼리를 전송합니다.
# Try to write your query here
{
feed {
links {
description
url
}
}
}
서버로부터 다음과 같은 응답을 받을 수 있을 겁니다.
{
"data": {
"feed": {
"links": [
// ...
{
"description": "The best learning resource for GraphQL",
"url": "www.howtographql.com"
}
]
}
}
}
아주 좋아요! 뮤테이션이 잘 작동하는군요. 💪
post
뮤테이션 실행시키기지금부터는 본 튜토리얼을 따라가는 과정에서 발생하는 오류를 해결하기 위한 방편을 역자가 임의로 추가한 내용입니다.
이번 프로젝트에서 사용하는 post
뮤테이션을 사용하면, 사용자가 Hackersnews 서비스에 원하는 링크와 설명을 추가할 수 있게 됩니다. 그런데, 해당 뮤테이션을 현재 단계에서 사용하려고 하면 인증 문제가 발생할 겁니다.
// `post` 뮤테이션 실행시 반환되는 JSON 데이터
{
"data": null,
"errors": [
{
"message": "Not authenticated",
"locations": [
{
"line":2,
"column":3
}
],
"path": [
"post"
]
}
]
}
별도의 설정을 따로 하지 않으셨다면, 위와 같은 오류가 발생하는 것이 정상입니다. post
뮤테이션은 Node.js Resolver에서 요청을 보낸 사용자를 식별한 뒤, 해당 사용자 정보를 링크에 저장하도록 구현되어있기 때문입니다.
($ .../hackernews-node/src/Mutation.js)
function post(parent, args, context, info) {
const userId = getUserId(context)
const newLink = context.prisma.link.create({
data: {
url: args.url,
description: args.description,
postedBy: { connect: { id: userId } },
}
})
context.pubsub.publish("NEW_LINK", newLink)
return newLink
}
여기서 사용되는 getUserId()
메서드가 요청 헤더에 포함된 사용자 토큰을 토대로 현재 사용자의 userId
를 추출 및 활용하도록 준비하며, 만약 토큰이 헤더 상에 없다면 오류를 발생시키게 됩니다.
($ .../hackernews-node/src/utils.js)
function getUserId(context) {
const Authorization = context.request.get('Authorization')
if (Authorization) {
const token = Authorization.replace('Bearer ', '')
const { userId } = jwt.verify(token, APP_SECRET)
return userId
}
throw new Error('Not authenticated')
}
따라서 아직 요청 헤더 상에 토큰을 포함시키는 로직이 구현되지 않은 현재 단계에서는, 토큰 검증 로직을 제외시킨 뒤에 GraphQL 서비스를 실행시키면 문제 없이 post
뮤테이션이 실행될 것입니다. 아래와 같이 코드를 수정한 뒤 서버를 실행시키고, 다시 React 프로젝트에서 뮤테이션을 실행해보세요.
function post(parent, args, context, info) {
// const userId = getUserId(context)
const newLink = context.prisma.link.create({
data: {
url: args.url,
description: args.description,
// postedBy: { connect: { id: userId } },
}
})
context.pubsub.publish("NEW_LINK", newLink)
return newLink
}
Quiz
다음 중 올바른 설명은?
- 오직 쿼리만
graphql
고차 컴포넌트로 감쌀 수 있다.<Mutation />
컴포넌트는varibale
,optimisticResponse
,refetchQueries
,update
등을props
로 전달받는다.graphql
을 사용하여 컴포넌트를 뮤테이션을 감쌀 때, Apollo는Render Props
함수 내로 뮤테이션 함수만 주입한다.- GraphQL 뮤테이션은 인자를 일절 받지 않는다.
Mutation을 전송할 때 token 값이 헤더에 포함되지 않아서 Not Authorize 에러가 발생하는데 토큰을 별도로 포함하지 않아도 되야하는 것이 맞나요?? 물론 로그인 구현해서 로컬스토리지에 토큰 저장해놓고 헤더에 포함해주면 해결될 문제인것 같긴한데, 지금 이 단계에서 토큰을 포함하지 않고 정상적으로 뮤테이션이 되어야 하는지 궁금합니다!! 포스트 너무 잘봤습니다 ㅎㅎ :)