Apollo Movie API

Coaspe·2021년 2월 22일
0

Setting

npm add styled-components react-router-dom
npm install @apollo/client graphql

@apollo/client:

This single package contains virtually everything you need to set up Apollo Client. It includes the in-memory cache, local state management, error handling, and a React-based view layer.

graphql:

This package provides logic for parsing GraphQL queries.

index.js (렌더링하는 js파일)

import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
import client from "./apollo";
import { ApolloProvider } from '@apollo/client';

ReactDOM.render(
    <ApolloProvider client={client}>
        {/* client is defined in apollo.js */}
        <App />
    </ApolloProvider>, 
    document.getElementById("root")
    );

App.js (렌더링되는 js파일)

import React from "react";
import {HashRouter as Router, Route} from "react-router-dom";
// Router for routing
import Home from '../routes/Home';
import Detail from '../routes/Detail';

const App = () => {
    return (
    <Router>
        <Route exact path="/" component={Home} />
        <Route exact path="/:id" component={Detail} />
        {/* If it has id behind '/', browser routes you to Detail.js */}
    </Router>
    );
}

export default App;

Detail.js (Movie를 하나 눌렀을 때 나오는 세부사항)

import React from "react";
import { useParams } from "react-router-dom";
import { useQuery } from "@apollo/client";
import { gql } from "@apollo/client";
import styled from "styled-components";

const GET_MOVIES = gql`
  query getMovie($id: Int!) {
    # query getMovie($id: Int)는 apollo를 위한 것이다.
    movie(id: $id) {
      id
      title
      language
      rating
      medium_cover_image
      description_intro
      isLiked @client
      # isLiked field는 client에 있다는 뜻이다.
    }
    suggestions(id: $id) {
      id
      medium_cover_image
    }
  }
`;
// 서버에서 받아올 query이다.
const Detail = () => {
  const { id } = useParams();
  // App.js에서 /:id의 그 아이디를 받아오는 useParams()
  // url parameter를 받아온다.
  const { loading, data } = useQuery(GET_MOVIES, {
    variables: { id: parseInt(id) },
    // GET_MOVIES에게 parameter로 부터 받아온 id를 넘겨준다.
  });

  return (
    <Container>
      <Column>
        <Title>
          {loading
            ? "Loading..."
            : `${data?.movie?.title} ${data.movie.isLiked ? "❤" : "😢"}`}
          {/* if data exists? then movie -> if movie exists? then title */}
        </Title>
        <Subtitle>
          {data?.movie?.language} * {data?.movie?.rating}
        </Subtitle>
        <Description>{data?.movie?.description_intro}</Description>
      </Column>
      {/* <Poster bg={data && data.movie ? data.movie.medium_cover_image : "" }></Poster> */}
      <Poster bg={data?.movie?.medium_cover_image}></Poster>
      {/* {data && data.suggestions && data.suggestions.map()} */}
    </Container>
  );
};

export default Detail;

@client에 대한 부가 설명

const GET_LAUNCH_DETAILS = gql`
  query LaunchDetails($launchId: ID!) {
    launch(id: $launchId) {
      isInCart @client
      site
      rocket {
        type
      }
    }
  }
`;

위의 query는 remote, local fields를 모두 갖고 있다.
Apollo Client가 이 query를 실행하고 isInCart field에 대한 결과를 찾으려고 할 때 다음과 같은 단계를 거친다.

1. isInCart field와 관련된 resolver가 정의되어 있는가?
(ApolloClient constructor resolvers parameter이나 Apollo Client's setResolvers/addResolvers methods도 포함)
2. 만약에 resolver가 없다면 isInCart value를 찾기 위해 Apollo Client cache를 탐색하고 값을 반환한다.

Home.js (처음에 Movie목록이 나오는 화면)

import React from "react";
import { gql, useQuery } from "@apollo/client";
import styled from "styled-components";
import Movie from "../components/Movie";

const GET_MOVIES = gql`
  {
    movies {
      id
      medium_cover_image
      isLiked @client
    }
  }
`;
// 서버로 부터 요청하는 query이다.

const Home = () => {
  const { loading, error, data } = useQuery(GET_MOVIES);
  return (
    <Container>
      <Header>
        <Title>Apollo 2021</Title>
        <Subtitle>I love GraphQL</Subtitle>
      </Header>
      {loading && <Loading>Loading...</Loading>}
      {/* && 연산자 설명 => 마지막 &&을 기준으로 앞에 있는 조건들이 만족 할 때 맨 마지막 && 뒤에 있는 것을 실행하라 */}
      <Movies>
        {data?.movies?.map((m) => (
          <Movie
            key={m.id}
            id={m.id}
            isLiked={m.isLiked}
            bg={m.medium_cover_image}
          />
        ))}
      </Movies>
    </Container>
  );
};

export default Home;

apollo.js (Apollo Client를 생성)

import { ApolloClient, InMemoryCache } from "@apollo/client";

const client = new ApolloClient({
  // ApolloClient를 생성한다.
  uri: "http://localhost:4000/",
  // GraphQL server의 주소이다.
  cache: new InMemoryCache(),
  // Data를 fetching할 준비가 되었다.
  resolvers: {
    Movie: {
      isLiked: () => false,
    },
    Mutation: {
      likeMovie: (_, { id }, { cache }) => {
        cache.modify({
          id: `Movie:${id}`,
          fields: {
            isLiked: (isLiked) => !isLiked, // 현재 isLiked 값을 받아서 true-false 토글
          },
        });
      },
    },
  },
});

export default client;

Movie.js(Home.js에 나오는 Movie)

import { gql, useMutation } from "@apollo/client";
import React from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";

const LIKE_MOVIE = gql`
  mutation likeMovie($id: Int!) {
    likeMovie(id: $id) @client
  }
`;
const Movie = ({ id, bg, isLiked }) => {
  const [likeMovie] = useMutation(LIKE_MOVIE, {
// Mutation을 하기위해 client 쪽에서 likeMovie를 받아온다.
    variables: { id: parseInt(id) },
  });
  return (
    <Container>
      <Link to={`/${id}`}>
        <Poster bg={bg} />
      </Link>
      <button onClick={likeMovie}>{isLiked ? "Unlike" : "Like"}</button>
    </Container>
  );
}; //id = props.id

export default Movie;
profile
https://github.com/Coaspe

0개의 댓글