GraphQL 튜토리얼 사이트 : https://www.apollographql.com/tutorials/
기본 프로세스
아래 나와 있는 소스는 튜토리얼 4번째 프로젝트
- Client
- index.js
import React from 'react';
import ReactDOM from 'react-dom';
import GlobalStyles from './styles';
import Pages from './pages';
import { ApolloProvider, ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: 'http://localhost:4000',
cache: new InMemoryCache(),
});
ReactDOM.render(
<ApolloProvider client={client}>
<GlobalStyles />
<Pages />
</ApolloProvider>,
document.getElementById('root')
);
ApolloClient 인자로 server의 uri를 넣어주고, cache instance를 생성하여 ApolloClient instance를 생성
Component를 ApolloProvider로 감싸줌
- tracks.js
import React from 'react';
import { useQuery, gql } from '@apollo/client';
import TrackCard from '../containers/track-card';
import { Layout, QueryResult } from '../components';
/** TRACKS gql query to retreive all tracks */
export const TRACKS = gql`
query getTracks {
tracksForHome {
id
title
thumbnail
length
modulesCount
author {
name
photo
}
}
}
`;
/**
* Tracks Page is the Catstronauts home page.
* We display a grid of tracks fetched with useQuery with the TRACKS query
*/
const Tracks = () => {
const { loading, error, data } = useQuery(TRACKS);
return (
<Layout grid>
<QueryResult error={error} loading={loading} data={data}>
{data?.tracksForHome?.map((track, index) => (
<TrackCard key={track.id} track={track} />
))}
</QueryResult>
</Layout>
);
};
export default Tracks;
gql을 이용하여 query 문을 작성하며, 각각의 인자는 server 쪽 schema에 명시되어 있어야 함
useQuery를 이용하여 해당 query 문을 uri로 명시한 server로 보냄
loading, error, data를 통해 각각 loading 중인지 error가 발생했는지 data를 통해 응답한 data를 확인할 수 있음
data는 server 쪽의 resolver에 선언한 로직을 거쳐서 할당됨
- track.js
import React from 'react';
import { useQuery, gql } from '@apollo/client';
import { Layout, QueryResult } from '../components';
import TrackDetail from '../components/track-detail';
/** GET_TRACK gql query to retrieve a specific track by its ID */
export const GET_TRACK = gql`
query getTrack($trackId: ID!) {
track(id: $trackId) {
id
title
author {
id
name
photo
}
thumbnail
length
modulesCount
numberOfViews
modules {
id
title
length
}
description
}
}
`;
/**
* Track Page fetches a track's data from the gql query GET_TRACK
* and provides it to the TrackDetail component to display
*/
const Track = ({ trackId }) => {
const { loading, error, data } = useQuery(GET_TRACK, {
variables: { trackId },
});
return (
<Layout>
<QueryResult error={error} loading={loading} data={data}>
<TrackDetail track={data?.track} />
</QueryResult>
</Layout>
);
};
export default Track;
매개 변수가 필요하다면 위의 query 문처럼 인자를 넣어주면 됨
query 선언할 때의 괄호에는 인자 이름 앞에 $붙이고 : 옆에는 type을 넣어 줌 ($trackId: ID!)
그다음 인자를 넣어줄 때는 넣을 인자 : 옆에 $변수를 넣어줌 (id: $trackId) 위 예시로는 id에 $trackId의 값이 할당
그리고 useQuery 선언 시에 variables를 통해 인자를 전달해줌
매개 변수가 여러 개가 필요하다면 varialbes 객체에 추가로 넣어주고, 똑같이 query 선언에도 추가로 넣어줌
-Server
- schema.js
const { gql } = require('apollo-server');
const typeDefs = gql`
type Query {
"Query to get tracks array for the homepage grid"
tracksForHome: [Track!]!
"Fetch a specific track, provided a track's ID"
track(id: ID!): Track!
"Fetch a specific module, provided a module's ID"
module(id: ID!): Module!
}
type Mutation {
"Increment the number of views of a given track, when the track card is clicked"
incrementTrackViews(id: ID!): IncrementTrackViewsResponse!
}
type IncrementTrackViewsResponse {
"Similar to HTTP status code, represents the status of the mutation"
code: Int!
"Indicates whether the mutation was successful"
success: Boolean!
"Human-readable message for the UI"
message: String!
"Newly updated track after a successful mutation"
track: Track
}
"A track is a group of Modules that teaches about a specific topic"
type Track {
id: ID!
"The track's title"
title: String!
"The track's main Author"
author: Author!
"The track's illustration to display in track card or track page detail"
thumbnail: String
"The track's approximate length to complete, in minutes"
length: Int
"The number of modules this track contains"
modulesCount: Int
"The track's complete description, can be in markdown format"
description: String
"The number of times a track has been viewed"
numberOfViews: Int
"The track's complete array of Modules"
modules: [Module!]!
}
"Author of a complete Track or a Module"
type Author {
id: ID!
"Author's first and last name"
name: String!
"Author's profile picture"
photo: String
}
"A Module is a single unit of teaching. Multiple Modules compose a Track"
type Module {
id: ID!
"The module's title"
title: String!
"The module's length in minutes"
length: Int
"The module's text-based description, can be in markdown format."
content: String
"The module's video url, for video-based modules"
videoUrl: String
}
`;
module.exports = typeDefs;
type 뒤에 Query나 Mutation(추후 포스팅 예정)을 선언 후 그 안에 query를 보낼 query문의 type을 지정할 수 있음
그 외에는 type을 통해 구조체 형식으로 인자들의 type을 지정해 줄 수 있음
위의 소스처럼 ""를 통해 comment를 남길 수 있음
- resolvers.js
const resolvers = {
Query: {
/* returns an array of Tracks that will be used to populate the homepage
grid of our web client*/
tracksForHome: (_, __, { dataSources }) => {
return dataSources.trackAPI.getTracksForHome();
},
// get a single track by ID, for the track page
track: (_, { id }, { dataSources }) => {
return dataSources.trackAPI.getTrack(id);
},
// get a single module by ID, for the module detail page
module: (_, { id }, { dataSources }) => {
return dataSources.trackAPI.getModule(id);
},
},
Mutation: {
// increments a track's numberOfViews property
incrementTrackViews: async (_, { id }, { dataSources }) => {
try {
const track = await dataSources.trackAPI.incrementTrackViews(id);
return {
code: 200,
success: true,
message: `Successfully incremented number of views for track ${id}`,
track,
};
} catch (err) {
return {
code: err.extensions.response.status,
success: false,
message: err.extensions.response.body,
track: null,
};
}
},
},
Track: {
author: ({ authorId }, _, { dataSources }) => {
return dataSources.trackAPI.getAuthor(authorId);
},
modules: ({ id }, _, { dataSources }) => {
return dataSources.trackAPI.getTrackModules(id);
},
},
};
module.exports = resolvers;
client로부터 전달받은 query문을 어떻게 처리할지를 나타내는 과정
redux의 reducer랑 비슷
- index.js
const { ApolloServer } = require('apollo-server');
const typeDefs = require('./schema');
const resolvers = require('./resolvers');
const TrackAPI = require('./datasources/track-api');
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources: () => {
return {
trackAPI: new TrackAPI(),
};
},
});
server.listen().then(() => {
console.log(`
🚀 Server is running!
🔉 Listening on port 4000
📭 Query at https://studio.apollographql.com/dev
`);
});
위에서 작성한 schema와 resolver, dataSources를 인자로 넣어 instance를 생성
server 실행 후 https://studio.apollographql.com/dev를 통해 테스트 query를 보내볼 수 있다.
요약
server에서 schema와 resolver를 생성해놓고, client에서는 해당 type에 맞게 query를 생성
그 후에 useQuery를 통해 query문을 server로 보내고 server는 resolver를 통해 가공한 data를 client에게 전달
'개발에 도움이 되는 > GraphQL' 카테고리의 다른 글
GraphQL + Apollo (0) | 2022.05.04 |
---|