본문 바로가기

개발에 도움이 되는/GraphQL

useQuery 기초

GraphQL 튜토리얼 사이트 : https://www.apollographql.com/tutorials/

 

Learn with our GraphQL Tutorials, Examples, and Training

Level up with Apollo's official GraphQL tutorials. Get practical, hands-on trainings and become an Apollo GraphQL certified developer.

www.apollographql.com

 

기본 프로세스

아래 나와 있는 소스는 튜토리얼 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