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;