#1 [2022 UPDATE] INTRODUCTION
#0.0 Welcome
안녕 모두들, 노마드의 graphql react 시작하기 수업에 온걸 환영해!
우리는 reactjs, graphql에서 어떻게 apollo client를 사용하는지 배울거야.
apollo client는, 상태관리 라이브러리야.
graphql api에서 데이터를 fetch해오는 것을 가능하게 해주고,
어플리케이션에서 로컬로 상태를 관리할 수 있게도 해주지.
같은 라이브러리로 우리는 두 가지 일을 할 수 있어.
서버에 있는 데이터를 가지고 오는 것과, 로컬에 있는 데이터를 관리하는 것.
apollo client로 할 수 있는 가장 좋은 점은, 이 두 종류의 데이터를 동기화할 수 있어.
너가 로컬에 있는 상태를 관리하는데에 라이브러리를 썼다면, 또는 data를 fetch하는데에 썼다면,
Apollo client는 너를 놀라게 할 거야. 왜냐면 apollo client는 그 기능들을 다 사용할 수 있고,
graphql을 더 강력하게 사용할 수 있도록 해주기도 하지.
#0.1 Requirements
이 수업을 잘 들을 수 있으려면,
React.js를 알고 있으면 돼
이 수업에서 REST api를 사용해서 data를 가져오는 것을 배울꺼야.
apollo client는 GraphQL client이지.
또한 알아야 할 것은 GraphQL API가 뭔지, 왜 다른지 알아야 해.
그리고 어떤 사람들에게는 REST API가 더 나을 수도 있어.
그래서 React.js가 뭔지 알게된 후에는, GraphQL 시작하기 수업을 수강해야해.
해당 수업에서 빌드한 서버를 돌려봐야해.
해당 수업에서 구축한 서버를, 현 수업에서 사용될 것이기 때문이야.
왜 그렇게 하냐면, 우리는 request를 보낼 GraphQL API가 필요해.
그래서 GraphQL 시작하기 수업에서 구축한 서버의 코드를 실행해야해.
그 수업에서는, GraphQL을 소개할꺼야.
그러니까 만약 GraphQL을 모른다면, 먼저 그 수업부터 시작하도록해.
GraphQL 시작하기 수업에서는, GraphQL 서버로 GraphQL API를 만들고,
그리고 GraphQL로 movie api를 만들어.
그래서, 반드시 해당 수업을 다 들어야 서버를 돌릴 수 있지.
그 서버가 이번 수업에서 데이터를 요청하는데에 다시 쓰이기 때문이야.
#0.2 Installation
좋아. 우리 프로젝트를 만들어보자.
npx create-react-app movieql-client
documents로 가서, create-react-app을 해주면 돼.
그리고 나는 movieql-client로 이름지을거야 왜냐면 내가 구축한 백엔드 서버가 movieql-server로 지었기 때문에,
이건 movieql-client로 할거야. 좋아. 그러면 새 react 어플리케이션이 설치될거고,
그리고 우리는 graphql하고, apollo-client를 설치해줄거야. 그리고 우린 react-router-dom v6도 깔아줄거야.
우리는 두 화면을 만들거야. 첫번째는 사람들이 모든 영화들을 볼 수 있는 화면을 만들거야.
그리고 그들이 클릭하면, 한 개의 영화를 볼 수 있도록 할거야.
알겠지? 우리는 두 가지 모두를 하는 방법을 배울 거야.
그래서 우리는 Router라는게 필요해
npm install @apollo/client graphql react-router-dom@6
이제 우리는 두 개의 폴더를 만들거야. routes로 하나를 만들고, routes라고 하고, 좋아.
그 안에 Home.js를 만들고 Home 함수를 만들어줘
그리고 다른거 하나를 또 만들자. 우리는 한 개의 영화를 볼 거지?
Movie.js라고 만드자. Movie.js로 만들고, Home 안에서는,
우리는 많은 영화들을 보게 되고, Movie.js안에서는 한 개의 영화만 보게 되겠지.
만약 너가 좀 더 정확하게 하고 싶다면, Movies.js로 이름을 바꾸자.
그러면 우리는 Movie, Movies.js를 가지고 있지.
다음 영상에서는 우리는 react-router-dom을 세팅할거고, 그럼 끝이야.
그러니까 Apollo client를 설치하고, graphql을 설치하고, react-router-dom@6을 설치하고
깃 레포지토리를 만들고 싶다면. routes 폴더를 만들어서 Movie, Movies.js를 만들자.
둘다 React 컴포넌트야. 비어있는 div로 리턴했지. 파일들도 정리했고,
index.js를 가지고 있고, 삭제한 것중에 import 되어있는거 다 지워주고,
왜냐면 너가 여기 파일을 지웠으면, 여기에 import 되어있는것도 지워줘야하지
에러를 보고 싶지 않다면 말야. App.js도 똑같이 해주고. css를 다 지워주고, svg도 지워주고,
#0.3 Router
이제 우리는 App.js로 가서 Router를 만들어볼거야.
좋아. 그러면 import를 해주고, react-router-dom에 있는 BrowserRouter를 import 해줄거야.
Routes, Route도 같이 import 해주고,
여기 App안에서는 BrowserRouter을 써주고, 그 안에 Routes를 써줄거야.
그리고 그 안에 하나의 Route를 써주고, 그 path는 Home 또는 Index 페이지가 될거야.
그리고 element는 렌더링될 곳인데, Movies를 넣을거야.
이제 누군가가 /movies/:id 경로로 가면, Movie 컴포넌트로 이동할거야.
브라우저로 가서, Home 페이지로 가보면,
this is a list of movies를 볼 수 있지.
그리고 movie로 가보면, 그리고 id는 12345를 주고,
좋아. 여긴 movies로 해야하고..this is movie details 보이지?
Home 페이지로 가보면, 영화 리스트를 볼 수 있을거야. 우리 DB에서 가져온 리스트야.
graphql 서버에 있는 DB 이지.
그리고 그 영화들 중에서 하나를 클릭하면, 영화 상세 페이지로 이동할거야.
알겠지? 여기까지가 셋업 이었어.
다음 영상에서 보자.
우린 Apollo client를 써볼거야. bye-bye
#2 [2022 UPDATE] APOLLO CLIENT
#1.0 Setup
좋아 그럼 우린 이제 Apollo client를 생성할거야
여기로 와서
client라는 새 파일을 만들거야. apollo.js라고 해도 돼
아무거나 해. 나는 항상 client로 해서 client로 할게
이 client는 너가 딱 한 번 새로 생성하는거야. 알겠지? 딱 한번이야
GraphQL의 API의 url을 써서 이 client를 구성할 수 있어
따라서 이 단계에서는 실행중인 GraphQL 서버를 가지고 있는게 매우 중요해
알겠지?
난 여기 이미 있어
localhost:4000에서 실행 중이지
여기 있는 이게
내 GraphQL 서버인거지
좋아. 하지만 너는 니 서버를 실행하야 해
왜냐하면 ReactJS의 Apollo client가 GraphQL서버와 통신하도록 구성 할 거거든
그래서 너는 이전 코스에서 만든 서버가 실행 중인지 확인 해 봐야 해
알겠지? 'GraphQL 초보자 코스'에서 말야. 좋아
그럼 이제 여기서 새로운 client를 만들어보자
client = new ApolloClient를 적고
저기에서 내가 import한 거지. 좋아
이렇게 적고
여기로 와서
이제 이렇게 import 안 해도 돼
이렇게 import하고 이렇게 from을 적으면 돼
왜 이렇게 자동완성 됐는지 모르겠네. 다 됐어
여기 이 new ApolloClient에 uri를 적을거야
니 GraphQL 서버가 돌아가고 있는 곳이지
우리 경우에는
내 GraphQL 서버는 localhost:4000에서 돌아가고 있어
여기에 적을 거야
또 다른 할 건
어느 정도 필수라고 할 수 있는데, cache strategy 라고 해
데이터를 caching하는 방법들은 다양해
cache에 관한 부분은 다음에 할거야
우리가 만들고 싶은 쿼리를 전부 만든 다음에
지금도 cache에 대해 아주 조금만 더 얘기 할 거긴 한데
일단 InMemoryCache를 import할거야 이렇게
그 다음 new InMemoryCache를 적어
좋아. 그럼 끝이야
이제 default client를 export 하고
그리고 client를 ReactJS에 연결하기 전에
client가 제대로 작동하는지 확인하려고 해
좋아
보통은 Apollo client가 가지고 있는 hook을 사용하거든
기억해. hook은 ReactJS에서의 예를 들면 useState 같은 거야. 알지?
Apollo client는 아주 멋진 hook을 가지고 있어
예를 들어, useQuery나 useMutation 같은 게 있어. 아주 아주 멋져
알겠지? 너는 hook를 쓸 거야. 99.999%라고 라고 싶은데
하지만 hook이 client의 동작을 확인하는 유일한 방법은 아니라는 걸 알려는 줄게
여기 와서 client를 쓰고
쿼리를 보내고, 이 쿼리에서 GraphQL 쿼리를 보낼거야
GraphQL을 쓰기 위해 gql을 import하고
이 부분은 나중에 해보고
data를 받으면 console.log(data)를 할 거야
알겠지? 어떻게 되는지 보자구
여기에는 뭘 적어야 할까?
우리가 적을 건 바로 이거야
GraphQL 쿼리!
이 화면은 무료 코스인 '초보자용 GraphQL'에서 봤던 화면이니까, 익숙할거야
그러니까 여기서 무슨 일이 일어나는 건지 스스로 잘 알고 있는지 확인해보고
이게 바로 우리 client에 넣을 쿼리야
index로 가서 client를 import하자 우리는 client에서 client를 import할거야.
이게 import될 때 이 코드가 실행돼야 해 ReactJS로 가보자
allMovies를 받았지 보다시피 client는 완~벽하게 작동 해
보다시피 우리는 client를 생성했지.
Apollo client를 그리고 직접 이 쿼리를 작성했어
그 후 그 쿼리를 지금 실행 중인 localhost:4000으로 보냈지.
그리고 데이터가 도착해서 우리는 그 데이터를 console.log 하고있어
보다시피, 여기 있는 이 쿼리가 우리한테 이 결과를 줬어
sandbox의 playground에서 봤던 거랑 같아
그럼 니 client는 아주 잘 작동한다는 거고
너의 백엔드가 작동하고 연결되었다는 거야
console.log로 allMovies가 잘 보이는지 꼭 확인해
#1.1 ApolloProvider
이제 이 client에 우리의 모든 React 컴포넌트를 연결해 볼거야
index.js로 가서 그리고 우리는 우리 앱을 ApolloProvider로 감쌀거야.
여기로 와서 그리고 ApolloProvider를 이렇게 적고
ApolloProvider는 우리가 방금 만든 client가 필요해
그러니까 client를 import하자 그럼 우린 client를 적고 이렇게 적으면
그럼 이제 우리의 홈 페이지, 즉 Movies 페이지로 가보자
만약 client에 접속하고 싶으면 뭘 할 수 있을까?
Movies 화면에서 client가 필요하다면 말이야 한 가지 할 수 있는 건, useClient hook을 쓰는거야
client, useClient를 적고 좋아 useApolloClient 이건 여기서 만든 것과 같은 client될 거야
이게 우리가 Provider를 쓰는 이유야 많은 사람들이 Provider가 뭔지 모르는데
Provider는 기본적으로 애플리케이션 안의 모두가 이 client에 접근할 수 있게 해줘
당연히 애플리케이션 안에는 BrowserRouter랑 Routes가 있지
따라서 애플리케이션 내부의 모두가 client에 접근 가능해
어떤 라우터, 어떤 화면, 어떤 컴포넌트 에서든 client에 접근 할 수 있어!
우리가 여기서 만든 이 client를 Provider로 사용하고 있기 때문에 이제 Movies 화면에서 해당 client에 접근할 수 있지
이건, 내가 원하면, 이런 걸 할 수 있다는 거야
이런 걸 할 수 있어 useEffect를 쓰고 여기 query를 다시 쓰고, 여기는 gql
여전히 잘 작동할거야 그리고 너가 useApolloClient hook에서 받은 client가
니가 여기서 만든 client랑 같다는걸 보여줄게 Provider 덕분이지
알겠지? 보통은 튜토리얼을 따르는데 우린 왜 여기에 Provider를 넣어야 하는지는 몰라
Provider의 포인트는 니가 Provider안에 뭘 넣든 이전에 만든 client에 접근 할 수 있다는 거야
좋아 그럼 내 말이 맞는지 한 번 보자
보다시피 data도 있고, allMovies도 있지? 잘됐어
이제 이건 우리가 그냥 데이터를 가져오는 걸 의미해 그치?
그러니까 여기로 와서 movie를 state안에 넣어볼게
useState를 쓰고, default로 배열이 되도록 하자
그리고 나서 우리가 client로부터 받은 데이터에서 data.allMovies를 쓸거야 setMovies를 쓰면 되겠지
이제 여기서 Movies를 렌더링할 수 있어
movies.map를 적고 각 movie의 movie.id를 줘 여기서 id를 요청하고
그리고 movie.title을 줘 좋아 title
보다시피 잘 작동해 Movies를 완벽하게 받았어
이렇게만 해도 괜찮다고 생각할 수 있는데 사실 이건 진짜 진짜 구려
완전 완전 구려. 왜냐하면 너는 이걸 항상, 매번 해야할테니까
너는 Apollo client를 이용해서 client를 계속 요청해야할거고, 이 hook도
그리고 client query도 써야하지. GraphQL 쿼리도 써야하고
결과를 받아와서 또 state 안에 그것들을 넣어야 하지
즉, state를 만들고 그 state 이름을 니가 다 기억해야된다는 뜻이야
혼자서 이걸 다 하는 건 진짜 귀찮고 성가신 일이야
fetch 작업하는 거랑 비슷하지 만약 그게 REST API라면 url을 fetch안에 적어주잖아
다음 영상에서 니가 상상할 수도 없는 완전 최고의 hook을 보여줄게 그건 useQuery hook이야
useQuery hook은 기본적으로 우릴 위해 이 작업을 해줘!
useQuery hook은 쿼리의 내부적으로 client.query를 실행해
그 내부를 보지는 않을거야 우리가 더 하진 않을거라구
useQuery hook이 우릴 위해서 해줄거야
그리고 useQuery hook은 데이터를 state 안에 넣어 줄 거야 우리가 하는게 아니지
useQuery hook이 우리 대신 해줄거야.
이번 영상에서는 보여주고 싶었어 Provider가 뭘 하는지 왜 우리가 Provider를 생성해야 하는지
그리고 어떻게, 모든 곳에서 client에 접근할 수 있는지
그저 uesApolloClient hook만 쓰면 여기서 구성한 client에 접근할 수 있어
좋아. 보다시피 이 쿼리 안에서 서버의 url을 다시 쓰지 않았잖아
이거 봐. 안했잖아? 여기 한 번만 쓰면, client는 각 쿼리가 어디로 가야하는지 알아
이 지저분한 코드를 훨씬 더 멋지게 바꿀 다음 영상에서 보자!
#1.2 useQuery
이전 영상에서 봤듯이, client를 사용해서 그 어떤 GraphQL 쿼리도 실행할 수 있어
하지만 여기 있는 이 방식은 그렇게 좋지 않아 왜냐하면 state를 우리가 직접 만들어야 하고
useEffect도 써야하고, setMovies도 기억해야 하고 엄청 골치 아파
그러니까 이렇게 하는 대신 GraphQL 쿼리를 쓸거야 일단 이 코드 바깥 쪽인
여기에 적을 거야 const를 적고 그리고 여기 GET_MOVIES를 만들거야. ALL_MOVIES도 되고. 아무거나 해
좋아하는거 아무거나 하고, gql을 적어. 기억해. grl은 우리가 썼던 함수야
여기에 GraphQL 코드를 작성할거야 그래서 여기서는 이 쿼리를 쓸거야
보다시피, 이 쿼리는 우리가 항상 playground에서 썼던 거랑 같아
ALL_MOIVES를 다 작성했다면, 원한다면 이렇게 이름을 붙일 수도 있어
이 이름은 아무렇게나 써도 돼
안 써도 되고, 그냥 다른 이름을 붙여도 돼
allMovies와 같을 필요 없지
getMovies 같은 걸로 다르게 써도 돼
이제 GraphQL 언어로 GraphQL 쿼리를 쓸 거야
컴포넌트의 외부에서 gql tag function을 이용해서 작성할거야
useQuery hook을 쓸 거야 이거. useQuery. 그리고 useQuery는 @apollo/client에서 올거야
보다시피 @apollo/client에서 import 해왔어 알겠지? 그럼 useQuery를 쓰고
그리고 저기 있는 ALL_MOVIES를 써
useQuery는 엄청 멋진 hook이야
우리한테 결과로 많은 것들을 주는데
우리가 어떤 걸 받게될 지 보여줄게
data를 받고 우리가 만든 client도 있고, loading, error가 있어.
이 정보들을 다 받게 될 거야
useQuery hook을 호출하면 말이지
우리는 GraphQL 요청에서 data를 얻어. 아주 좋아 그리고 loading을 하든 안 하든 알림을 받을 거고
네트워크 상태도 받을 수 있네 또 우리가 전에 만든 client에 액세스 할 수 있어
그래서 더 이상 client를 쓰지 않아도 돼
여기에서 결과를 받자 이걸 console.log 해볼게 좋아 새로고침 하고, inspect(검사)로 들어가고
console을 보면 여기 있어 보다시피 객체를 받았어 많은 객체들을 받았네
첫 번째로 called true를 얻었어. 우리가 이 쿼리를 불렀다는 거지
loading도 true를 받았네
좋아. 여기로 가 보자 보다시피 loading이 false야얼마나 멋져?
이제 loading은 false야
이제 우리는 allMovies가 있는 data를 가지고 있어
형태는 전과 같지만,
지금은 hook만으로 데이터에 접근하고 있는 거야
그러니까 이 hook만 사용하면 다 되는 거지!
나는 data를 얻고 싶어. 그러니까 이렇게 data를 적고
data랑 loading 이 hook이 멋진 이유는, 우리가 declarative code(선언형 코드)를 쓰게 해 주기 때문이야.
선언형 코드는 원하는 걸 설명하기 위한 코드만 적는 것을 말해 반면에 imperative(명령형)은 모든 단계의 코드를 적어.
이전 영상에서 작성했던 게 바로 명령형 코드야 첫번째로 쿼리 요청을 보내고, 두번째로 state에 저장하고
단계 별로 진행하는거지 명령형 코드에서는 단계 별로 진행하지 않아
대신에 그저 우리가 무엇을 원하는지를 작성하지
그럼 useQuery hook은 우리가 원하는 것을 줘 매우 선언적이지
이렇게하면 컴포넌트를 파악하는게 정말 쉬워져 무엇을 하고 있는지 이해하기가 매우 쉽지
보다시피 useState를 생성하지 않아도 되고 쿼리를 따로 보내고, 그런 것들을 아무것도 안 해도 되는거야
여기서 error에도 접근할 수 있어. 이걸 return 해보자
fetch에 실패했습니다 :( 좋아. 그리고 data가 들어왔을 때도 또 써주면 돼
여기로 와서 data.allMovies 기억해. data는 우리 GraphQL API를 받을 데이터야
우리가 받을 건, 우리가 요청했던 정보를 가진 객체야 우린 allMovies를 요청했고
그럼 여기로 와서 allMovies를 적고 그리고 movie.title 적고 여기에 key로 movie.id를 적어
우린 movie가 id를 갖고 있을거라는 걸 알지.
내가 GraphQL 쿼리에서 요청했으니까 말이야
이게 내가 말했던 hook의 힘이야 보다시피 개발자 경험(Developer Experience)이 엄청나게 좋지
복습좀 해볼게
알다시피, '초보자용 무료 GraphQL' 과정에서 우리 스키마는 allMovies보다 규모가 컸어
예를 들어, 우리는 allTweets도 있었고 그렇지?
시험 삼아 여기서 allTweets를 해보자 그럼 Tweet의 text, id 그리고 author와 author의 fullName을 요청할거야
그럼 우리는 allMovies를 받고 allTweets도 받을거야
그리고 난 이걸 ReactJS에 넣으면 어떻게 나올지가 궁금해
그럼 이제 너는 data.allTweets를 갖게 될 거야
이 과정에서는 실제로 Tweets로 뭔가를 하진 않을거야. 왜냐하면 그냥 영화 앱을 만들거거든
그냥 이게 단순 GraphQL 쿼리라는 걸 보여준거야
그리고 이 하나의 큰 쿼리에 원하는 만큼의 쿼리들을 추가할 수 있어
#1.3 useQuery Variables
Movies의 screen과 Movie의 screen을 연결할거야
여기에 Movie id로 가기 위한 링크를 만들어줄게
Link를 적고 react-router-dom 에서 Link를 import할거야
movie.title은 여기 넣고 여기 href를 적어 movie.id를 적으면 돼. 끝이야
그럼 이제 여기를 클릭하면 보다시피 movies/{movie_id} 로 갈 거야
이제 이건 닫고 Movie.js로 가보자 여기 있는 이 movie id 를 받아오기 위해
useParams hook을 사용할거야params를 console.log 해볼게
검사에서 console을 클릭하면
이게 파라미터야 보다시피 아주 아주 잘 돼
이제 쿼리를 쓸거야 하지만 이 쿼리가 하나의 movie를 얻기 위한 쿼리라면
일단 우리의 explorer에서 먼저 해보자 movie를 쿼리할게
그리고 기억하듯이 movie는 여기 변수를 가지고 와 우리는 movieId를 변수로 보내야 해
쿼리로 변수를 보내는 방법을 배워볼게 왜냐하면 전에 movies 화면에서는
쿼리만 썼어 맞아 그걸로 괜찮았어 하지만 대부분의 경우에 넌 쿼리와 함께 변수를 보내야 할 거야
예를 들어, 지금처럼 movie 하나를 찾으려면 우리는 movie의 id를 보내야 해. 여기있는 이 숫자를 말이야
컴포넌트의 외부에 쿼리를 작성해볼게
const GET_MOVIE 그리고 gql을 다시 쓰고, 여기서 봤던 코드랑 같은 걸 넣어주면 되는데 이것도 꽤 좋지만 우리 스스로 써보자구
query getMovie 그리고 movie id라는 변수를 받아야 한다고 할게 $movieId
이름은 바꿔도 상관없어. 타입은 String이고, 필수야 변수는 꼭 보내야 하지
그리고 실제로 요청을 우리 백엔드로 보내야 돼
우리 백엔드, 우리 GraphQL 서버에 movie라고 불리는 resolver가 있다는 걸 알아
그리고 movie resolver엔 id를 넘겨야 하고, 우리는 이걸 여기 넣을거야. 이렇게 id랑 title을 가져올게
여기서 우리가 하고 있는 건,이 컴포넌트가 GET_MOVIE 쿼리를 위해 movie id라고 하는 변수를 제공하도록 하는 거야
리액트 컴포넌트가 GET_MOVIE 쿼리를 얻기 위해 변수를 제공하면, GraphQL 즉 Apollo가 String인 movie id를 넣을거야
useQuery를 또 써볼게
useQuery를 쓰고 아까 작성한 GET_MOVIE를 넣어줘 기
Movies 화면에서는 useQuery(ALL_MOVIES) 썼지만
이번엔 변수를 보내야 한다는 게 차이점이야
콤마를 넣고 여기서 우리가 이름을 movieId 라고 했었지?
variables 안에 movieId라고 쓰고 이건 param.id가 되겠지
아니면 니가 원하면 id를 빼내서 써도 돼 이제 파라미터는 지워도 되겠지.
이게 변수를 필요로 하는 쿼리로 변수를 보내는 방법이야 . 이 movieId는 여기 넣은 이름이랑 같아야 해. 똑같아야 한다구
여기도 movieId로 같아야 하고 potato가 돼도 돼. 아무거나 상관 없어. 대신 그러면 전부 potato가 되어야 해
좋아. 이제 내가 보여주고 싶은 건 Apollo cache의 힘이야
여기서 확인할 그 힘이란 바로 loading을 더이상 보지 않게되는거야
보다시피 loading이 false야 보이지. loading false, loading false... loading 된 적이 없어. 다시 해보자
loading된 적이 없어 보다시피 이제 더 이상 load하지 않아
그리고 movies 화면으로 돌아가도 보다시피 loading이 전혀 없어
이건 Apollo cache 때문인데 우리가 처음에 설정했던 in memory cache 설정이 바로 지금 발생한거야
Apollo가 니 쿼리들을 저장할거야 쿼리 결과가 브라우저의 메모리에 있는 cache에 저장될거야
즉, 일단 화면을 한 번 가져오면 다른 화면으로 이동했다가 다시 돌아왔을 때 데이터를 다시 가져오지 않아도 된다는거야
예를 들어, 여기 Room 203을 클릭해볼게 보다시피 나는 title을 즉시 볼 수 있어
Chariot으로 가보자 fetch가 나타났다가 movie가 나오고 뒤로 왔다가 다시 클릭하면 더이상 fetch가 나오지 않아
붐! fetch하고 있어 이제 movie를 받았지. 좋아! 하지만 그 후엔 더 이상 fetch가 없어
fetch를 하면 할 수록 더 많은 게 cache에 저장되는 거야
이제 클릭했던 movie를 또 누르게 되면 더이상 fetch가 없어. 보다시피 fetching은 false야
이게 아주 아주 멋진 Apollo cache야 Apollo cache의 더 많은 것들을 해볼거야
API와는 별개인 데이터를 서로 합쳐볼거야데이터를 합쳐서, 사용자가 영화를 Like나 Dislike할 수 있도록 해볼거야
쿼리에 변수를 어떻게 보내는지 보여주고 싶었어. 보다시피 엄청 간단했어
쿼리를 이런식으로 작성하고 API에 필요한 변수를 올바른 type으로 여기에 추가한 다음 해당 변수를 여기서 보내주면 돼
그리고 우리가 딱히 아무것도 하지 않았는데도 Apollo cache는 완전 멋진 기능이라, fetch는 딱 한 번만 발생해
더 이상 fetch하지 않아도 돼 물론 새로운 페이지에선 fetch를 하고나서 정보를 받겠지
#1.4 Apollo Dev Tools
구글로 가서 Apollo devtools를 검색 해
그러면 Apollo client devtools를 찾을 수 있는 구글 크롬 확장 스토어의 링크가 표시될거야
좋은 확장 프로그램이야. 정말 멋진 방식으로 cache를 확인할 수 있게 해 줘.
이게 추가되면, 추가되고 나면 React.js 어플리케이션으로 돌아가
그리고 페이지를 새로고침 해 그 다음엔 inspect 창을 열어
그리고 elements가 보이고 블라블라블라 마지막에 Apollo가 보일거야
보다시피 니가 이전에 실행했었던 쿼리들을 보여 볼 수 있어. 아주 멋지지
그리고 여기 보이는 것 처럼 너의 API를 탐색할 수 있게 해줘
보면 알겠지만 우리가 좀 전에 썼던 같은 익스플로러야
하지만 니 developer tools에 있는거지.
그러니까 니가 GraphQL 어플리케이션으로 작업을 한다면 sandbox를 열지 않아도 돼
그냥 Apollo developer tool로 가서 Explorer로 가면 쿼리 정보를 찾을 수 있어
root 타입들도 볼 수 있고 모든 쿼리를 볼 수 있어 mutation들도 볼 수 있고
그리고 그것들이 뭘 필요로하는지도 볼 수 있지 여기서 니가 실행하는 쿼리들도 볼 수 있어
그러니까 만약 내가 여기를 클릭하면
보다시피 지금 getMovie가 생겼지 여기 얘네는 내가 이 쿼리를 실행했다는 걸 아는거지
이 variable과 함께 말이야 근데 또 여기, cache 완전 멋져. cache. 여기
붐! cache를 보면Apollo가 너의 Movies에 대한 정보들을 가지고 있어
보다시피, 내 Tweet에 관한 정보도 가지고 있어
별로 중요하진 않지만. 그리고 Tweet 2도 있어 여기서 너의 cache 안에 있는 모든 요소들을 보여줘
모든 요소들이 니 cache안에 있는거지. 진짜 멋져
이번엔 내가 Apollo cache에서 더 멋진 부분을 보여줄게
바로 Apollo에서 너를 위해 데이터 정규화를 해준다는 거야
Room 203 영화로 가면 이 영화는 41507이라는 ID를 가지고 있지
알겠지? 근데 지금 다른것보다 먼저 하고 싶은 건 내 영화의 small_cover_image를 얻고 싶어
바로 여기 Movie.js 화면에서 말이지 그래서 여기서 small_cover_image를 요청할거야
Movies 화면에서 내가 allMovies에 요청할 건 title이랑 id뿐이야 그리고 Movie에서는 title이랑 id는 물론
다른 하나도 있어 이제 Apollo가 어떻게 동작하는 지 보여줄게
Apollo는 Movie가 뭔지 알아. 왜냐하면 type Movie를 아니까
우리 type으로 Apollo가 무엇을 하는지 확인해보자구
다시, 여기 보이듯이 cache로 가면 Apollo는 우리가 어떤 Movie를 가지고 있는지 알고 있고
여기 field id가 여기 있다는 것도 알 정도로 엄청 똑똑해Apollo는 이것들이 서로 다른 Movie라는 것도 알아
우리 Movie로 다시 돌아가보자
여기로 다시 와서 이제 Movies로 가고 완벽해. cache로 가
그럼 이제 id가 41507인 Movie를 찾아봐
Apollo cache가 우리 Movie에 대해 아는거야 이게 id인 걸 알고
이건 Movie고 이건 title인 걸 아는거지 이제 Room 203 영화를 클릭할거야
이제 하나 더 요청해야하잖아 그래서 우린 여기로 와서 클릭하고
이 Movie를 fetch 하고나서 Apollo는 Movie의 데이터를 어디에 둬야할 지 알아
예를들어, 보이는 것 처럼, 이 영화는 small_cover_image가 없어
근데 여기엔 small_cover_image가 있지?
왜냐하면 이건 Apollo가 충분히 똑똑하다는 거거든
만약 너희가 같은 id인 Movie 객체로 더 많은 데이터를 요청하면 Apollo가 새 데이터를 기존 객체에 추가 할 거야
같은 type, 같은 id 객체의 추가적인 데이터를 요청한다면
Apollo는 그 새로운 데이터를 같은 cache안에 넣을거야. 완전 완전 멋있지!
다른 것들은 보다시피 small_cover_image가 없어 이것들은 개체(entity)야.
Apollo client는 그 id로 Movie라는 개체를 생성해
우리도 이 id를 나중에 사용할건데 왜냐하면 나는 사용자가 Movie를 Like랑 Dislike할 수 있게 하고싶거든
이번 영상에선 Apollo cache의 힘이랑, 이게 얼마나 대단한지 보여주고 싶었어
왜냐하면 만약 니가 cache를 조작하고 싶으면 너는 말 그대로 여기 와서 Movie를 조작하면 된다는 걸 의미하거든
그럼 그 영화를 보여주고 있는 모든 화면에서 업데이트 될 거야
왜냐하면 Apollo는 모든 데이터를 조직화해서 가지고 있거든 보다시피 type:id로 구분했지
예를 들어, 사용자가 있다고 해보자. 사용자가 프로필로 이동하면 해당 프로필이 다운로드 되고
Apollo는 그 프로필이 이 사용자의 프로필임을 알아
사용자가 설정으로 이동하면 그 설정도 다운로드 되겠지
Apollo는 이게 같은 사용자의 설정이라는 것을 알고있어 그래서 나중에 사용자가 데이터를 수정하길 원하는 경우에는
같은 entity 안에서 전부 수정할 수 있는거야 이렇게 얘기하는게 더 이해하기 쉽겠다
Apollo cache에서의 편집과 수정에 대해 알아보기에 말이야!
#3 [2022 UPDATE] LOCAL STATE
#2.0 Styles
이 섹션에서는 Apollo client로 local state를 어떻게 다룰 수 있는지를 배울거야
또 local state를 API에서 제공되는 remote data와 병합하기 위해 어떻게 쓰는지도 볼거야
근데 그 전에, 여기서 내가 뭘 했는지 보여줄게 보이겠지만 내가 예쁘게 좀 꾸며봤어
내가 파일 3개를 수정해놨거든 routes폴더의 Movie.js를 수정했고 같은 routes폴더의 Movies.js도 수정했어
그리고 index.html에도 몇 가지 추가했어 reset-css도 여기 추가했고
그러니까 UI를 지금 내 것처럼 하고 싶으면 이 reset-css를 복사하고 이 font-family를 추가해
그리고 movie.js파일로 가서 전부 복사, 붙여넣기 하면 돼
일부 스타일 컴포넌트 몇 개 추가 한 것 외에는 아무것도 안바뀌었어
보다시피 GraphQL, Params.. 우리가 전에 했던 거랑 다 똑같아 그러니까 index.html로 가서 link랑 이 style을 복사하고
그리고 Movie.js로 가서 이 부분을 복사하면 돼
styled component들 말이야 movie의 medium_cover_image랑 rating도 추가로 요청하는 거 잊지말고
그리고 Movies.js에서도 styled component 들을 복사하고 medium_cover_image도 요청하는 것도 잊지마
return이랑 컴포넌트들을 수정하면 다 된거야
우리는 MoviesGrid안에서 allMovies를 렌더링하고 있어 각각엔 /movies/movie.id로 가는 링크가 있지
이미지도 보여주고 있구 이게 전부야 이건 이렇게 보이고 새로고침 하면 Loading되고
The Batman을 클릭하면 Loading 된 후에 The Batman이 떠
#2.1 Local Only Fields
이번 섹션의 목표는 진짜 진짜 간단해
내가 하고 싶은 건, 사용자가 영화에 좋아요 표시를 할 수 있는거야
여기 버튼이 있었으면 좋겠어
그리고 유저가 그 버튼을 누르면 이 영화가 좋아하는 영화로 보이는거지
유저가 영화를 좋아요 할 수 있으면 좋겠어
인스타그램 포스트를 좋아요 하듯이 유저가 영화를 좋아요 할 수 있었으면 좋겠어
문제는 우리 백엔드에는 그런 resolver가 없어 mutation도 없고 또 유저 계정도 없어
그래서 likeMovieMutation 같은건 못 만들어
우리 백엔드는 매우 매우 간단해서 그건 안 될거야
나는 브라우저 안에서 모든 걸 처리하고 싶어
나는 Apollo cache를 사용 할 거야 사용자가 어떤 영화들을 좋아요 표시 했는지 기억하기 위해서 말이지
우리는 local only field를 쓸거야 local only field가 뭐냐
local only field는 remote field의 반대야 이게 remote field야
여기 있는 이 field Apollo는 페이지가 로딩되면 이것들을 cache에서 먼저 찾을거야
그곳에서 찾지 못하면 Apollo는 API에 이 field 들을 요청 할 거야
그럼 GraphQL API는 응답하겠지 그럼 모든게 잘 되겠지
local only field는 절대 API로 가지 않는 field야
Apollo는 절대 local only field를 API에서 찾지 않아. 절대로!
local only field는 apollo의 cache에서만 활동하는 field야
local only field를 선언하거나 요청하는 방법은 매우 간단해
내가 지금 하고 싶은 건 내 영화들에 대해 더 많은 정보를 받고 싶어
이건 isLiked라고 하는 field가 될 거야
만약 지금 여기서 이렇게 저장하면 에러가 날거야 왜냐하면 Apollo는 이 모든 field의 정보들을 내 API에 요청하거든
하지만 내 API movies는 isLiked라는 field를 갖고 있지 않아 Apollo한테 이 field는 cache에만 있다고 알려주기 위해
isLiked 옆에 이렇게 @client를 쓸 거야
우리가 이 isLiked 데이터를 요청하는 방식은 movie의 다른 field를 요청하는 방식과 동일해
이게 Apollo의 멋진 점이야 말했듯이 local 데이터랑 remote 데이터를 합치는 게 엄청 간단하지
여기 버튼을 만들게
만약 사용자가 이 영화를 Like했다면 우리는 Unlike movie를 보여줄거야
만약 사용자가 Like를 안했으면 Like movie를 보여줄거고 그럼 어떻게 isLiked에 접근할까? 엄청 쉬워
data를 쓰고, movie를 쓰고, isLiked를 쓰면 끝이야 엄청 간단해
만약 사용자가 Like했으면 Unlike movie를 적으면 돼
그냥 Unlike만 적어도 되고 만약 사용자가 Like를 안 했으면 Like를 적으면 되지 끝
보다시피, isLiked data에 접근한 방법이 다른 remote data에 접근하는 방법이랑 같아
내가 전에 병합의 힘에 대해 말한게 이거야
예를들어 GraphQL API에서 가져온 사용자 객체를 cache에 저장할 수 있겠지
그럼 너는 네 cache에 사용자 프로필을 가지게 될 거야 그리고 너는 local only field도 가질 수 있을텐데
예를 들어 다크 모드라던지 사용자의 타임존같은 것도 있고 예시로 YouTube 같은 걸 만든다면
사용자가 원하는 볼륨 크기를 사용자 프로필의 local only field에 저장할 수 있어
데이터가 필요할 때가 되면 똑같은 방법으로 요청하면 되고
보다시피, 다른 곳을 볼 필요 없이 모든 데이터는 같은 형태로 와
client only field에 대해서는 이게 다야 보이듯이 매우 매우 유용하지
그리고 이제 여기로 오면 The Batman이랑 Like가 있어
다음 영상에선 이걸 클릭했을 때 Like과 Unlike가 토글 되도록 해보자
여기 이 field를 어떻게 수정하는 지를 배울거야
#2.2 writeFragment
이제 새로운 클릭 핸들러를 만들거야
onClick을 쓰고 그리고 여기 button 안에 onClick을 또 쓰고
좋아
여기서 우리는 cache에 접근해야 해 왜냐하면 cache를 수정해야하거든
그러니까 cache에 접근해야하지 cache는 client의 안에 있고 우린 아직 client에 접근하지 않아도 돼
두 가지 옵션이 있어
하나는 여기로 와서 client를 쓰고 useApolloClient를 써. 이렇게
알겠지? 근데 이건 다른 무언가를 import해야 한다는 뜻이지. 저렇게
또는 원한다면 useQuery에서 client를 가져올 수도 있어
useQuery는 네가 client에 접근할 수 있도록 해줘. 엄청 간단하지
우리는 client 내부의 cache를 가져올거야
지금 우리가 할 일은 여기 와서, cache.writeFragment를쓸거야
Fragment가 뭐냐 Fragment는 타입의 일부야 그게 다야
타입 movie는 타입이고 타입 movie의 Fragment를 만들 수 있어
우선 우리는 우리의 cache에서 수정하고 싶은 객체가 뭔지 알아내야 해
니가 이걸 기억한다면 우리가 cache를 확인 할 때 Apollo developer Tools로 가서
여기 cache 안에, 니가 기억하듯이 Movie랑 Movie의 id가 있어
이게 바로 Movie 객체의 id야. 바로 여기 보이는 이거!
이 Movie라는 단어는 API에서 나온 type Movie 이고 그리고 이 뒤에 건 id야
Apollo client는 이게 id라고 불리기 때문에 유니크하다는 걸 알만큼 똑똑해
바로 이걸로 우리가 수정하고자 하는 영화를 알아낼 수 있어
우리는 cache안에 우리가 수정하고 싶은게 어떤 객체인지를 알아내야 하잖아
그럼 뭘 할거냐면, 여기 Movie 라고 하고 그리고 id를 적을건데
이 경우에 id는 파라미터로 부터 받아오고있어
기억해? 여기서. 여기. 파라미터에서 이게 우리가 받을 id지
알겠지? 그럼 여기로 와서 id를 적을거야 이렇게
그럼 이제 우리는 cache안에서 우리가 수정하고 싶은 객체가 어떤건지 알아낼거야
여기서 fragment를 써야 해 여기 fragment를 쓰고 gql을 쓸 거야
여기는 fragment를 단어 그대로 써야 해 그 뒤엔 니가 원하는 거 아무거나 써도 되고
아무 이름이나 원하는 거 난 MovieFragment로 쓸게
그리고 on Movie를 써 이 fragment는 필수야 이건 못 바꿔
이 이름은 아무거나 니가 원하는 걸로 써 on은 필수
Movie도 필수 여기 있는 이 Movie는 니 GraphQL API에서 온 타입이야
그리고 여기 있는 Movie랑, 여기 있는 Movie는 이름이 같아야 해. Movie. Movie
여기있는 이 fragment, 이 fragment의 이름은 니가 원하는 이름으로 하면 돼
이 fragment 안에서 우리가 수정하고싶은 한 부분을 말해주면 돼
내가 말했듯이 fragment는 하나의 큰 타입의 일부야 그러니까 이 Movie fragment에서
나는 Movie의 한 부분만 수정하고 싶어 바로 isLiked야
여기서 하고있는 건, writeFragement 인데 어떤 type의 일부를 cache에 작성할거야
즉, 우리는 이 Movie를 찾아야지 이 id를 가진 Movie 말이야
그리고 이것들을 이 객체 안에 쓸거야
이 경우 우린 isLiked를 쓰는거고 그리고 여기서 data를 쓰고, isLiked:true를 쓰고..
그러면 끝이야 잘 되는지 보자 여기로 와서 새로고침하고 여기로 와서 클릭하면
보이듯이 Unlike로 바꼈어 Apollo cache를 성공적으로 수정했어!
data가 잘 바뀌었지 cache의 data가 바뀐거야 그래서 이것도 새로고침된거고
왜냐하면 우리가 cache의 안으로 가서 cache를 수정했잖아
Apollo client는 isLiked의 변경사항을 듣는 모든 것들을 새로 고침할거야
니가 원하는 대로 바꿀 수 있어 예를들어 영화 제목을 바꾸고 싶으면
똑같이 하면 돼 title을 여기 fragment 안에 쓰고 여기에 data를 넣어
Hello라고 해볼까 좋아. 다른 것들 더 수정해볼게
여기로 와서 rating을 써줘 rating.. 10점 중 10점을 주자
좋아. 이렇게 data에만 써놓으면 rating은 바뀌지 않을거야 왜냐하면 일단 정의를 먼저 해야해
우리가 뭘 바꿀 건 지를 말이야 이건 매우 매우 중요해
너는 타입에서 어떤 field를 수정할 지 Apollo한테 말해줘야 해
여기서 나는 title field랑 isLiked field를 Movie에서 바꿀거라고 할게
그리고 여기 data를 넣어 이 경우 title, isLiked는 변경되어야 하지만, rating엔 동작하지 않을거야
rating도 추가해주면 모두 잘 동작하겠지
그리고 이제 여기를 클릭하면 보이듯이 cache가 완벽하게 수정됐어
얼마나 멋져 여기 Apollo로 와서, Movie를 보면 우리 Movie를 봐
우리 Movie는 isLiked가 true이고 rating은 10이고
그리고 title은 Hello야
이제 여기서, 현재 들어있는 값의 반대값으로 내 fragment를 수정할거야
그러니까 내 data를 가져와서 반대 값이 되도록 !를 추가해
여기로 와서 Like하고 Unlike하고 Like, Unlike..완벽해
하지만 가장 좋은 점은, 다른 화면으로 이동해도 유지 된다는 거야
다시 전 화면으로 돌아가서 Movies로 돌아가서, 다른 영화를 눌렀다가
Movies로 돌아가서 다시 the Batman으로 들어가면 보이듯이 얘가 기억하고 있어
이 하나의 화면에 한해서 보여지는게 아니야
보이듯이, 얘는 기억하고 있어. 왜냐하면 우리가 Apollo cache를 수정했기 때문이지
Apollo cache는 브라우저의 메모리에 있거든
#2.3 Conclusions
네가 이 코스와 Apollo client를 즐겼으면 좋겠어
여기 보이듯이 이건 진짜 진짜 멋진 기술이야
네가 이 걸 더 더 많이 쓰기 시작했으면 좋겠어
데이터를 얻기 위해 우리가 해야 할 건 이것 뿐이야
코드 딱 한 줄 그럼 데이터를 얻을 수 있어
코드 딱 한 줄만 있으면 컴포넌트에 데이터를 넣을 수 있어
useState를 쓸 필요도 없고 useEffect를 쓸 필요도 없지
우리는 loading state, data state, error state 가 있어
그리고 네가 Apollo client 랑 GraphQL을 써서 더 생산적인 개발자가 됐으면 좋겠어
'모카 스터디 > React' 카테고리의 다른 글
한입 크기로 잘라 먹는 리액트 -Node.js 및 React.js 기초 - [인프런] (0) | 2023.07.16 |
---|---|
리액트 [생활코딩] (0) | 2023.07.14 |
Redux [생활코딩] (0) | 2023.07.13 |
GraphQL로 영화 API 만들기 (1) | 2023.05.01 |
ReactJS로 영화 웹 서비스 만들기 (0) | 2023.04.30 |