Develop/React & React Native

[더들어야함] React Apollo

안다희 2020. 2. 5. 11:58
728x90

DaheeSpace - movieql 서버 돌려야 해!

 

 

 

 

https://academy.nomadcoders.co/courses/enrolled/364948

불러오는 중입니다...

 

1. 언제나처럼 깃헙 프로젝트 만들기!

https://github.com/daheeahn/apollo-2020

 

daheeahn/apollo-2020

Movie App build with React, Apollo and GraphQL 노마드 코더 아폴로 강의 - daheeahn/apollo-2020

github.com

2. npm install -g create-react-app // React 위해서 필수인가? react-native 처럼?

3. npx create-react-app apollo-2020

4. src 폴더 안에 있는 것들 중, App.js / index.js 제외 하고 다 삭제, 관련 코드도 다 지워

5. git init && remote add && commit & push (Create-react-app 했기 때문)

 

 

 

필요없는 파일 삭제

 

 

GraphQL을 사용하기에 Apollo가 가장 좋은 방법이다.

 

6. yarn add styled-components react-router-dom react-apollo

router-com은 router를 위한 것

 

7. apollo docs

https://www.apollographql.com/docs/react/get-started/

 

Get started

Set up Apollo Client in your React app

www.apollographql.com

yarn add apollo-boost @apollo/react-hooks graphql

이건 hooks , graphql 사용 위함

 

8. setup

App.js

import { Route, HashRouter as Router } from "react-router-dom";

import Detail from "../routes/Detail";
import Home from "../routes/Home";
import React from "react";

function App() {
  return (
    <Router>
      <Route exact path="/" component={Home} />
      <Route exact path="/:id" component={Detail} />
    </Router>
  );
}

export default App;

ㅁㅁㅇㄴㅎ ㅈ

 

ROute는 주소를 보고서 어떤 컴포넌트를 떠야할지 말지를 결정함.

path가 / 를 포함하면

path

 

Detail, Home 각각 path에 맞게 .js 파일 만들어주고

export default () => "Detail";
------------------------------
export default () => "Home";


or

import React from "react";
export default () => <h1>"Home"</h1>;
이런식으로도 가능~

like this.

 

9. yarn start

route: /

detail: /123 

input url like above, 

 

10. reset style

https://cssreset.com/scripts/eric-meyer-reset-css/

코드 복사

touch public/reset.css

여기에 붙여넣기.

더보기
/* http://meyerweb.com/eric/tools/css/reset/ 
   v2.0 | 20110126
   License: none (public domain)
*/

html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
  display: block;
}
body {
  line-height: 1;
}
ol,
ul {
  list-style: none;
}
blockquote,
q {
  quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
  content: "";
  content: none;
}
table {
  border-collapse: collapse;
  border-spacing: 0;
}

 

public/index.html

<head> 안에

    <link rel="stylesheet" href="%PUBLIC_URL%/reset.css" />

추가하면. css가 reset 됨 완전 디폴트 값처럼 됨!

 

 

11. 아폴로!

직접 graphql query를 axios로 fetch해서 POST로 보낼 수도 있음.

그런데 이걸 대신 해주는게 apollo.

 

1) 클라이언트를 만든다. apollo-boost.

graphql yoga처럼 ㅂ몯느걸 미리 설정해둔 패키지임

graphql yoga: 바로 쓸 수 있게 준비를 다 해둔 server

apollo-boost: (동일) client

 

Create a client !

https://www.apollographql.com/docs/react/get-started/

 

딱 한가지만 필요해. uri

 

src/apollo.js 만들고,

import ApolloClient from "apollo-boost";

const client = new ApolloClient({
  uri: "https://movieql.now.sh/"
});

export default clinet;

uri는 내가 이전에 만들어놓은 graphql server를 이용해야 한다.

https://github.com/daheeahn/movieql

yarn start하고 uri를 localhost:4000으로 하면 된다. 서버의 endpoint가 4000이라서

 

 

12. 이제 이 clinet로 React Application 을 감싸야 해.

index.js

import { ApolloProvider } from "@apollo/react-hooks"; // add this code
import App from "./components/App";
import React from "react";
import ReactDOM from "react-dom";
import clinet from "./apollo"; // add this code

ReactDOM.render(
  <ApolloProvider client={clinet}> // add this code
    <App />
  </ApolloProvider>, // add this code
  document.getElementById("root")
);

yarn start하면 콘솔에서 이렇게 아무런 문제도 없어야 함, Network도 눈에 띄는거 없어야 함

13. 쿼리를 작성하자.

자바스크립트는 graphql을 이해하지 못해. 그래서 gql을 import해

 

Home.js

import React from "react";
import { gql } from "apollo-boost"; // query는 component 밖에 있어.
import { useQuery } from "@apollo/react-hooks";

// ` is backtick.
const GET_MOVIES = gql`
  {
    movies {
      id
      medium_cover_image
    }
  }
`;
export default () => {
  const { loading, error, data } = useQuery(GET_MOVIES);
  console.log("loading");
  console.log(loading, error, data);
  if (loading) {
    return "loading...";
  }
  if (data && data.movies) {
    return data.movies.map(m => <h1>{m.id}</h1>);
  }
  return <h1>"Home"</h1>;
};

useQuery 잘 이용하기. 그래서 React-hooks를 들으라는 것! (알려준적없는데??? 강의가 또있나?)

아무튼 @apollo/react-hooks 가 좋다는 점!!!! gql을 잘 쓸 수 있게 도와줘.

 

 

젤 좋은건 cache를 이용하는거야.

 

의문점.. 그럼 기존 서버가 있는데, fetch 말고 graphql을 가져오고 싶으면 서버도 내가 만들어야겠네?

일이 2배...? 흠 일단 해보자!! redux를 쓰고싶지 않으면.

 

14. 이제 Movie rendering 할거야

 

src/components/Movie.js 생성

import { Link } from "react-router-dom";
import React from "react";
export default ({ id }) => (
  <div>
    <Link to={`/${id}`}>{id}</Link>
  </div>
); // id를 props로 받는 것.

 

a href는 react app을 죽게 만든다. 그래서 Link를 사용!

 

src/routes/Home.js

import Movie from "../components/Movie";
import React from "react";
import { gql } from "apollo-boost"; // query는 component 밖에 있어.
import { useQuery } from "@apollo/react-hooks";

// ` is backtick.
const GET_MOVIES = gql`
  {
    movies {
      id
      medium_cover_image
    }
  }
`;
export default () => {
  const { loading, error, data } = useQuery(GET_MOVIES);
  return (
    <div>
      <h1>Apollo 2020</h1>
      {loading && <h2>Loading...</h2>}
      {!loading &&
        data.movies &&
        data.movies.map(m => <Movie key={m.id} id={m.id} />)} // add this line
    </div>
  );
};

Router에서 /:id는 Detail로 해놔서, 자동으로 Detail.js로 옮겨질거임!

 

15. useParams 

movie(id: 111) 이렇게 요청해야되는게 있어서, graphql 서버도 만짐! 내가 만들었던 movieql.

schema.graphql이랑, resolvers.js, db.js 건들면 됨!!

https://github.com/daheeahn/movieql/commit/020058b976d1795a65b44c38a39a5a085a620c5e\

 

 

 

src/routes/Detail.js

 

 

쿼리에 variable이 없으면 그냥

const GET_MOVIES = gql`
  {
    movies {
      id
      medium_cover_image
    }
  }
`;

이렇게 하면 되는데,

 

넣어야 한다면!?

 

import React from "react";
import { gql } from "apollo-boost";
import { useParams } from "react-router-dom";
import { useQuery } from "react-apollo";

// 그 쿼리의 이름을 적어야 해. 쿼리에 파람이 있으면
// 저 query getMovie line은 이건 우리 서버를 위한게 아니라, ******apllo를******* 위한거야. 타입 써줬으니까 검사해줄거야.
// 그 다음은 *****우리 서버를 위해******* 날리는 쿼리를 그대로 써주면 되는거야.
const GET_MOVIE = gql`
  query getMovie($id: Int!) {
    movie(id: $id) {
      id
      title
      summary
      medium_cover_image
    }
  }
`;
export default () => {
  let { id } = useParams(); // /:id로 보냈는데, 이걸 param으로 사용 가능한가봄! 맞나?
  id = parseInt(id);
  const { loading, data } = useQuery(GET_MOVIE, {
    // open option object
    variables: { id } // === { id:id }
  });
  console.log(loading, data);

  return (
    <div>
      {loading && <h1>Loading...</h1>}
      {!loading && data.movie && <h1>{data.movie.title}</h1>}
    </div>
  ); // 여기서 data.movie는, GET_MOVIE의 movie임!
};

 

16. cool 캐시

클릭했던걸 클릭하면 미리 저장해둬서, 로딩을 하지 않아!

has cache!!!!!!

react apollo는 cache를 주기 때문에, user는 loading을 덜 준다.

very very cool........ 미쳤당

redux는 이거 알아서 못해주지!!! 내가 알아서 해야했어

 

17. CSS time-lapse

 

이미지 url 

const Poster = styled.div`

    background-image: url(${props => props.bg});

`;

이런식으로 사용함!

props는 컴포넌트 만들고

<Movie bg={bg} />

이렇게 했으면

Movie.js에서는

export default ({ bg }) => (

    // 여기서 bg 사용 가능

)

그래서 props.bg로 쓴거임! 아마~

 

18. 항상 데이터가 있는지 확인해야함

data && 

useQuery로 얻은 loading을 이용해도 되겠지! loading이 false면 data는 항상 있을테니까.

!loading && data.movie && // data는 이미 있을 테니까 data.movie로~

 

아니면 삼항연산자로~ ternary operator

loading ? 'Loading...' : data.movie.title

 

19. apollo cache 더 보고 싶으면,

apollo client developer tools

chrome 익스텐션 설치!

 

gql은 개발자 도구가 좋지않음.

redux는 좋음.

 

서버 재시작 하고, react 웹페이지에서 개발자도구 - Apollo 찾을 수 있다! (Network, Console 있는 그곳)

cache도 볼 수 있다.

이리저리 웹페이지 클릭하면, cache - ROOT_QUERY에 캐시 쌓이는거 볼 수 있음

캐시에서 찾으면, 요청 보내지 않음!

 

가장 좋은 도구는 아님. redux가 젤 좋긴 한데,,,

 

20. one more suggestions

이건 서버에 쿼리 만들고 그냥 추가로 요청하면 됨

const GET_MOVIE = gql`
  query getMovie($id: Int!) {
    movie(id: $id) {
      id
      title
      summary
      medium_cover_image
    }
    suggestions(id: $id) { // 이렇게!!
      title
    }
  }
`;

 

21. optional chaining

?.

 

22. conclusions

fetching data, mutation 도 있어!

instagram clone

apollo uber

들어봐!

 

 

apollo / gql 깃헙

https://github.com/daheeahn/apollo-2020

https://github.com/daheeahn/movieql

 

 

 

23. Local State (2.0 Local State)

api에서 온걸 바꿔보자 local state 이용해서! 정확히는 내가 원하는 데이터와 합쳐보자. 

 

하고싶은건, isLiked랑 원래 데이터랑 합치고 싶은거! 여기서 isLiked만 내가 가공하는거. 내가 만든거.

 

src/apollo.js

const client = new ApolloClient({
  uri: "http://localhost:4000/",
  resolvers: { // add resolvers
    Movie: {
      isLiked: () => false
    }
  }
});

 

src/routes/Home.js

const GET_MOVIES = gql`
  {
    movies {
      id
      medium_cover_image
      isLiked @client // add this code. 서버에서 오는 값이 아니라 client 붙여준다!
    }
  }
`;

 

그러면 캐시에 이렇게 보인다!!!
개발자도구 - Apollo - Cache

Super Easy!

 

 

이제 mutation 작성해보자

 

https://www.apollographql.com/docs/react/data/local-state/

 

Local state management

Learn how to work with your local data in Apollo Client

www.apollographql.com

 

client쪽에 mutation 추가하면서 내가 직접 data 바꿀 수 있겠지. 그냥 state인거야 setState할 때 그 state

 

src/apollo.js

import ApolloClient from "apollo-boost";

const client = new ApolloClient({
  uri: "http://localhost:4000/",
  resolvers: {
    // 서버에서 작성하는 것과 아주 유사하지!
    Movie: {
      isLiked: () => true // 왜 func일까?
    },
    Mutation: {
      toggleLikeMovie: (_, { id, isLiked }, { cache }) => {
        // console.log("id", id);
        // console.log(cache.writeData);
        // writeData! 뭐뭐가 있는지 궁금하면 cache 콘솔 찍어보기
        // 근데 cache에 writeData 안보여서 cache.writeData했는데 함수가 찍히긴 찍힘. 꼭 보여야지 사용할 수 있는건 아닌가보다

        // 오프라인 노트앱에서 했던 것과 같은 맥락!  nomad-notes -> dataIdFromObject 참고
        // medium_image_cover도 바꿔도 돼~
        // 따로 ...는 안해줘도 되네??? 알아서 해주나봐
        cache.writeData({ id: `Movie:${id}`, data: { isLiked: !isLiked } });
      }
    }
  }
});

export default client;

Mutation 보세요~

 

src/components/Movie.js

import { Link } from "react-router-dom";
import React from "react";
import { gql } from "apollo-boost";
import { useMutation } from "react-apollo";

// likeMovie 리턴값 없어도 되나? TODO:
// 서버로 보내지 않을거고 클라이언트에서만 사용하니까 @client 써준다.
const TOGGLE_LIKE_MOVIE = gql`
  mutation toggleLikeMovie($id: Int!, $isLiked: Boolean!) {
    toggleLikeMovie(id: $id, isLiked: $isLiked) @client
  }
`;

export default ({ id, medium_cover_image, isLiked }) => {
  const [toggleLikeMovie] = useMutation(TOGGLE_LIKE_MOVIE, {
    variables: { id: parseInt(id), isLiked }
  }); // [potato]도 가능, useMutation이 arr의 첫번째를 리턴하기 때문에 likeMovie는 그 arr의 첫 번째 원소가 된다

  return (
    <div>
      <Link to={`/${id}`}>
        <img src={medium_cover_image} width={100} />
      </Link>
      <button onClick={toggleLikeMovie}>{isLiked ? "UnLike" : "Like"}</button>
    </div>
  );
}; // id를 props로 받는 것.

 

근데 toggleLikeMovie가 toggleLikeMovie()라서 가능한건가? 이미 useMutation에서 다 작업이 된건가 id isLiked에 대해? 그래서 그냥 button onClick에 바로 toggleLikeMovie 넣을 수 있는건가?

 

쿼리 작성하고 버튼하나 만들어주고~

- useMutation (love react apollo !!!!)

- Boolean

 

여기 있는 Movie:15431

이거를 보고 

 

`Movie:${id}` 이렇게 적어준거야! apollo.js에서.

 

src/routes/Detail.js 에서 isLiked 부분을 추가해주면 detail 페이지에서도 볼 수 있겠지~ isLiked를~

const GET_MOVIE = gql`
  query getMovie($id: Int!) {
    movie(id: $id) {
      title
      language
      rating
      summary
      medium_cover_image
      isLiked @client // add this code
    }
    suggestions(id: $id) {
      title
    }
  }
`;

 

Detail에서 라이크 이모티콘 싱크가 Home이랑 안맞을 때가 있다

src/routes/Detail.js 에서 id를 요청하지 않아서이다...

흠..... 캐시도 보면은 movie ({"id":~}): Movie랑 movies: [Movie] 랑 같은 아이디에 대해서 isLiked가 다른걸 볼 수 있다.

그래서 id도 요청해야해 그래야 아폴로가 안다.

id가 없으니 isLiked가 서로 다르지!

 

src/routes/Detail.js

const GET_MOVIE = gql`
  query getMovie($id: Int!) {
    movie(id: $id) {
      id // add this code!!!!
      title
      language
      rating
      summary
      medium_cover_image
      isLiked @client
    }
    suggestions(id: $id) {
      title
    }
  }
`;

id 추가해주기~

 

 

 

 

Apollo 오래걸리면 reload frame 한 번 해주고!

 

 

[챌린지]

api에 오는거 말고 그냥 아예 new state를 만들어서 걔만 그냥 독립적으로 움직일 수 있는 방법...!!

 

'Develop > React & React Native' 카테고리의 다른 글

[React / RN] Context API (Anti Redux!) with Hooks  (0) 2020.02.06
[React] 실전형 React Hooks 10개  (0) 2020.02.05
Facebook도 쓰는 GraphQL 정복하기  (0) 2019.12.04
TDD 개발 방법론  (0) 2019.09.14
react-navigation  (0) 2019.07.07