React 앱에서 Apollo-Client로 클라이언트 측 GraphQl 이해하기

게시 됨: 2022-03-10
간략한 요약 ↬ 클라이언트 측 애플리케이션에서 GraphQL 서버와 상호 작용을 시도하고 도달하기도 전에 포기하고 싶었던 적이 있습니까? GraphQL API로 작업해야 하는 코드 베이스에 참여하라는 초대를 전혀 모르고 거절한 적이 있습니까? GraphQL API를 사용하는 방법을 배우지 않은 유일한 프론트 엔드 엔지니어처럼 느껴본 적이 있습니까? 이러한 질문 중 하나라도 예라고 답했다면 이 튜토리얼이 적합합니다. GraphQL 및 Apollo Client의 몇 가지 기본 사항과 이 두 가지를 모두 사용하는 방법에 대해 자세히 살펴보겠습니다. 결국 우리는 Apollo Client를 사용하는 애완 동물 가게 앱을 만들 것입니다. 그런 다음 다음 프로젝트를 빌드할 수 있습니다.

State of JavaScript 2019에 따르면 개발자의 38.7%가 GraphQL을 사용하고 싶어하는 반면 개발자의 50.8%는 GraphQL을 배우고 싶어합니다.

쿼리 언어인 GraphQL은 클라이언트 애플리케이션 구축 워크플로를 단순화합니다. 단일 HTTP 엔드포인트를 노출하여 필요한 데이터를 가져오기 때문에 클라이언트 측 앱에서 API 엔드포인트를 관리하는 복잡성을 제거합니다. 따라서 REST의 경우와 같이 데이터의 오버페칭 및 언더페칭을 제거합니다.

그러나 GraphQL은 쿼리 언어일 뿐입니다. 그것을 쉽게 사용하기 위해서는 우리를 위해 무거운 것을 들어주는 플랫폼이 필요합니다. 그러한 플랫폼 중 하나가 Apollo입니다.

Apollo 플랫폼은 클라우드(서버) 간에 데이터를 앱의 UI로 전송하는 GraphQL의 구현입니다. Apollo Client를 사용하면 데이터 검색, 추적, 로드 및 UI 업데이트를 위한 모든 로직이 (React의 경우와 같이) useQuery 후크에 의해 캡슐화됩니다. 따라서 데이터 가져오기는 선언적입니다. 또한 구성이 필요 없는 캐싱이 있습니다. 앱에서 Apollo Client를 설정하기만 하면 추가 구성 없이 즉시 사용할 수 있는 지능형 캐시를 얻을 수 있습니다.

Apollo Client는 Angular, Vue.js 및 React와 같은 다른 프레임워크와도 상호 운용 가능합니다.

참고 : 이 튜토리얼은 과거에 클라이언트 측에서 RESTful 또는 기타 API 형식으로 작업한 경험이 있고 GraphQL이 시도해 볼 가치가 있는지 확인하려는 사람들에게 도움이 될 것입니다. 이것은 이전에 API로 작업했어야 함을 의미합니다. 그래야만 GraphQL이 귀하에게 얼마나 유익한지 이해할 수 있을 것입니다. GraphQL 및 Apollo Client의 몇 가지 기본 사항을 다루지만 JavaScript 및 React Hooks에 대한 지식이 있으면 도움이 됩니다.

GraphQL 기초

이 기사는 GraphQL에 대한 완전한 소개 는 아니지만 계속하기 전에 몇 가지 규칙을 정의할 것입니다.

GraphQL이란 무엇입니까?

GraphQL은 클라이언트가 API에 원하는 정확한 데이터를 요청하는 데 사용할 수 있는 선언적 쿼리 언어를 설명하는 사양입니다. 이것은 최고의 유연성으로 API에 대한 강력한 유형 스키마를 생성함으로써 달성됩니다. 또한 API가 데이터를 확인하고 클라이언트 쿼리가 스키마에 대해 검증되도록 합니다. 이 정의는 GraphQL을 선언적 쿼리 언어로 만드는 몇 가지 사양이 포함되어 있다는 것을 의미합니다. API는 정적으로 유형이 지정되고(Typescript를 기반으로 구축됨) 클라이언트가 이러한 유형 시스템을 활용하여 원하는 정확한 데이터를 API에 요청할 수 있습니다. .

따라서 일부 필드가 포함된 일부 유형을 만든 경우 클라이언트 측에서 "이 정확한 필드와 함께 이 데이터를 제공하세요"라고 말할 수 있습니다. 그러면 API는 강력한 형식의 언어에서 형식 시스템을 사용하는 것처럼 정확한 모양으로 응답합니다. 내 Typescript 기사에서 자세히 알아볼 수 있습니다.

계속 진행하면서 도움이 될 GraphQl의 몇 가지 규칙을 살펴보겠습니다.

기초

  • 운영
    GraphQL에서 수행되는 모든 작업을 작업이라고 합니다. 다음과 같은 몇 가지 작업이 있습니다.
    • 질문
      이 작업은 서버에서 데이터를 가져오는 것과 관련이 있습니다. 읽기 전용 가져오기라고 부를 수도 있습니다.
    • 돌연변이
      이 작업에는 서버에서 데이터 생성, 업데이트 및 삭제가 포함됩니다. 일반적으로 CUD(생성, 업데이트 및 삭제) 작업이라고 합니다.
    • 구독
      GraphQL의 이 작업에는 특정 이벤트가 발생할 때 서버에서 클라이언트로 데이터를 보내는 작업이 포함됩니다. 일반적으로 WebSocket으로 구현됩니다.

이 기사에서는 쿼리 및 돌연변이 연산만 다룰 것입니다.

  • 작업 이름
    클라이언트 측 쿼리 및 변형 작업에 대한 고유한 이름 이 있습니다.
  • 변수 및 인수
    연산은 대부분의 프로그래밍 언어의 함수와 매우 유사하게 인수를 정의할 수 있습니다. 그런 다음 이러한 변수를 작업 내부의 쿼리 또는 변형 호출에 인수로 전달할 수 있습니다. 변수는 클라이언트에서 작업을 실행하는 동안 런타임에 제공되어야 합니다.
  • 앨리어싱
    이것은 UI에 대해 간단하고 읽기 쉬운 필드 이름으로 장황하거나 모호한 필드 이름을 바꾸는 것과 관련된 클라이언트 측 GraphQL의 규칙입니다. 충돌하는 필드 이름을 원하지 않는 사용 사례에서는 별칭이 필요합니다.
GraphQL 기본 규칙
GraphQL 기본 규칙. (큰 미리보기)
점프 후 더! 아래에서 계속 읽기 ↓

클라이언트 측 GraphQL이란 무엇입니까?

프론트엔드 엔지니어가 Vue.js 또는 (우리의 경우) React와 같은 프레임워크를 사용하여 UI 구성 요소를 빌드할 때 해당 구성 요소는 서버에서 가져올 데이터에 맞게 클라이언트의 특정 패턴에서 모델링 및 설계됩니다.

RESTful API의 가장 일반적인 문제 중 하나는 오버페칭과 언더페칭입니다. 클라이언트가 데이터를 다운로드하는 유일한 방법은 고정 데이터 구조를 반환하는 끝점을 누르는 것이기 때문에 발생합니다. 이 컨텍스트에서 오버페칭 은 클라이언트가 앱에서 요구하는 것보다 더 많은 정보를 다운로드함을 의미합니다.

반면에 GraphQL에서는 필요한 데이터가 포함된 단일 쿼리를 GraphQL 서버에 보내기만 하면 됩니다. 그러면 서버는 요청한 정확한 데이터의 JSON 개체로 응답하므로 오버페칭이 발생하지 않습니다. Sebastian Eschweiler가 RESTful API와 GraphQL의 차이점을 설명합니다.

클라이언트 측 GraphQL은 GraphQL 서버의 데이터와 인터페이스하여 다음 기능을 수행하는 클라이언트 측 인프라입니다.

  • HTTP 요청을 직접 구성할 필요 없이 쿼리를 보내고 데이터를 변경하여 데이터를 관리합니다. 데이터 연결에 소요되는 시간을 줄이고 실제 애플리케이션을 구축하는 데 더 많은 시간을 할애할 수 있습니다.
  • 그것은 당신을 위해 캐시의 복잡성을 관리합니다. 따라서 제3자의 간섭 없이 서버에서 가져온 데이터를 저장 및 검색할 수 있으며 중복 리소스를 다시 가져오는 것을 쉽게 방지할 수 있습니다. 따라서 두 리소스가 동일한 경우를 식별하므로 복잡한 앱에 적합합니다.
  • 이는 사용자의 UI를 Optimistic UI와 일관성을 유지하도록 하며, 이는 돌연변이(즉, 생성된 데이터)의 결과를 시뮬레이션하고 서버로부터 응답을 받기 전에도 UI를 업데이트하는 규칙입니다. 서버에서 응답을 받으면 낙관적인 결과는 버리고 실제 결과로 대체됩니다.

클라이언트 측 GraphQL에 대한 추가 정보를 보려면 GraphQL의 공동 제작자 및 GraphQL Radio의 다른 멋진 사람들과 한 시간을 보내십시오.

Apollo 클라이언트란 무엇입니까?

Apollo Client는 JavaScript 및 기본 플랫폼을 위한 상호 운용 가능하고 매우 유연하며 커뮤니티 중심의 GraphQL 클라이언트입니다. 인상적인 기능에는 강력한 상태 관리 도구(Apollo Link), 구성이 필요 없는 캐싱 시스템, 데이터 가져오기에 대한 선언적 접근 방식, 구현하기 쉬운 페이지 매김 및 클라이언트 측 애플리케이션을 위한 낙관적 UI가 포함됩니다.

Apollo Client는 서버에서 가져온 데이터의 상태뿐만 아니라 클라이언트에서 로컬로 생성한 상태도 저장합니다. 따라서 API 데이터와 로컬 데이터 모두에 대한 상태를 관리합니다.

또한 충돌 없이 Redux와 같은 다른 상태 관리 도구와 함께 Apollo Client를 사용할 수 있다는 점도 중요합니다. 또한 Redux에서 Apollo Client로 상태 관리를 마이그레이션하는 것이 가능합니다(이 기사의 범위를 벗어남). 궁극적으로 Apollo Client의 주요 목적은 엔지니어가 API의 데이터를 원활하게 쿼리할 수 있도록 하는 것입니다.

Apollo 클라이언트의 기능

Apollo Client는 현대적이고 강력한 애플리케이션을 쉽게 구축할 수 있는 매우 유용한 기능으로 인해 수많은 엔지니어와 회사를 확보했습니다. 다음 기능이 기본 제공됩니다.

  • 캐싱
    Apollo Client는 즉시 캐싱을 지원합니다.
  • 낙관적 UI
    Apollo Client는 Optimistic UI에 대한 멋진 지원을 제공합니다. 작업이 진행되는 동안 작업(돌연변이)의 최종 상태를 일시적으로 표시하는 작업이 포함됩니다. 작업이 완료되면 실제 데이터가 낙관적 데이터를 대체합니다.
  • 쪽수 매기기
    Apollo Client에는 애플리케이션에서 페이지 매김을 매우 쉽게 구현할 수 있는 기능이 내장되어 있습니다. useQuery 후크와 함께 제공되는 fetchMore 함수를 사용하여 패치로 또는 한 번에 데이터 목록을 가져오는 대부분의 기술적 골칫거리를 처리합니다.

이 기사에서는 이러한 기능 중 일부를 살펴보겠습니다.

이론으로 충분합니다. 우리 손이 더러워지면 안전 벨트를 조이고 팬케이크와 함께 갈 커피 한 잔을 가져 오십시오.

웹 앱 구축

이 프로젝트는 Scott Moss에게서 영감을 받았습니다.

다음과 같은 기능을 가진 간단한 애완 동물 가게 웹 앱을 구축할 것입니다.

  • 서버 측에서 애완 동물을 가져옵니다.
  • 애완 동물 만들기(이름, 애완 동물 유형 및 이미지 만들기 포함)
  • 낙관적 UI 사용
  • 페이지 매김을 사용하여 데이터를 분할합니다.

시작하려면 리포지토리를 복제하고 starter 분기가 복제한 것인지 확인합니다.

시작하기

  • Chrome용 Apollo 클라이언트 개발자 도구 확장을 설치합니다.
  • CLI(명령줄 인터페이스)를 사용하여 복제된 리포지토리의 디렉터리로 이동하고 명령을 실행하여 모든 종속성을 가져옵니다. npm install .
  • npm run app 명령을 실행하여 앱을 시작합니다.
  • 루트 폴더에 있는 동안 npm run server 명령을 실행합니다. 이렇게 하면 계속 진행하면서 사용할 백엔드 서버가 시작됩니다.

앱은 구성된 포트에서 열려야 합니다. 내 주소는 https://localhost:1234/ 입니다. 당신은 아마도 다른 것입니다.

모든 것이 잘 작동했다면 앱은 다음과 같아야 합니다.

복제된 스타터 브랜치 UI
복제된 스타터 브랜치 UI. (큰 미리보기)

표시할 애완 동물이 없음을 알 수 있습니다. 아직 그런 기능을 만들지 않았기 때문입니다.

Apollo Client 개발자 도구를 올바르게 설치했다면 개발자 도구를 열고 트레이 아이콘을 클릭하십시오. "Apollo"와 다음과 같은 항목이 표시됩니다.

Apollo 클라이언트 개발자 도구
Apollo 클라이언트 개발자 도구. (큰 미리보기)

Redux 및 React 개발자 도구와 마찬가지로 Apollo 클라이언트 개발자 도구를 사용하여 쿼리 및 변형을 작성하고 테스트합니다. 확장 프로그램은 GraphQL Playground와 함께 제공됩니다.

애완 동물 가져오기

반려동물을 불러오는 기능을 추가해 봅시다. client/src/client.js 로 이동합니다. Apollo Client를 작성하여 API에 연결하고 기본 클라이언트로 내보내고 새 쿼리를 작성합니다.

다음 코드를 복사하여 client.js 에 붙여넣습니다.

 import { ApolloClient } from 'apollo-client' import { InMemoryCache } from 'apollo-cache-inmemory' import { HttpLink } from 'apollo-link-http' const link = new HttpLink({ uri: 'https://localhost:4000/' }) const cache = new InMemoryCache() const client = new ApolloClient({ link, cache }) export default client

위에서 일어나는 일에 대한 설명은 다음과 같습니다.

  • ApolloClient
    이것은 앱을 래핑하여 HTTP와 인터페이스하고 데이터를 캐시하고 UI를 업데이트하는 기능이 될 것입니다.
  • InMemoryCache
    이것은 애플리케이션에서 캐시를 조작하는 데 도움이 되는 Apollo Client의 정규화된 데이터 저장소입니다.
  • HttpLink
    이것은 GraphQL 요청의 제어 흐름을 수정하고 GraphQL 결과를 가져오기 위한 표준 네트워크 인터페이스입니다. 링크가 실행될 때마다 GraphQL 서버에서 결과를 가져오는 미들웨어 역할을 합니다. 또한 Axioswindow.fetch 와 같은 다른 옵션을 대신할 수 있습니다.
  • HttpLink 인스턴스에 할당된 링크 변수를 선언합니다. uri 속성과 값을 서버에 가져옵니다 https://localhost:4000/ 입니다.
  • 다음은 InMemoryCache 의 새 인스턴스를 보유하는 캐시 변수입니다.
  • 클라이언트 변수는 또한 ApolloClient 의 인스턴스를 취하고 linkcache 를 래핑합니다.
  • 마지막으로 애플리케이션 전체에서 사용할 수 있도록 client 를 내보냅니다.

이것을 실제로 보기 전에 전체 앱이 Apollo에 노출되고 앱이 서버에서 가져온 데이터를 수신할 수 있고 해당 데이터를 변경할 수 있는지 확인해야 합니다.

이를 달성하기 위해 client/src/index.js 로 이동합니다.

 import React from 'react' import ReactDOM from 'react-dom' import { BrowserRouter } from 'react-router-dom' import { ApolloProvider } from '@apollo/react-hooks' import App from './components/App' import client from './client' import './index.css' const Root = () => ( <BrowserRouter>
 <ApolloProvider client={client}> <App /> </ApolloProvider>
 </BrowserRouter> ); ReactDOM.render(<Root />, document.getElementById('app')) if (module.hot) { module.hot.accept() }

강조 표시된 코드에서 알 수 있듯이 App 구성 요소를 ApolloProvider 에 래핑하고 클라이언트를 소품으로 client 에 전달했습니다. ApolloProvider 는 React의 Context.Provider 와 유사합니다. React 앱을 래핑하고 컨텍스트에 클라이언트를 배치하여 구성 요소 트리의 모든 위치에서 액세스할 수 있습니다.

서버에서 애완 동물을 가져오려면 원하는 정확한 필드 를 요청하는 쿼리를 작성해야 합니다. client/src/pages/Pets.js 로 이동하여 다음 코드를 복사하여 붙여넣습니다.

 import React, {useState} from 'react' import gql from 'graphql-tag' import { useQuery, useMutation } from '@apollo/react-hooks' import PetsList from '../components/PetsList' import NewPetModal from '../components/NewPetModal' import Loader from '../components/Loader'

const GET_PETS = gql` query getPets { pets { id name type img } } `;

export default function Pets () { const [modal, setModal] = useState(false)
 const { loading, error, data } = useQuery(GET_PETS); if (loading) return <Loader />; if (error) return <p>An error occured!</p>;


 const onSubmit = input => { setModal(false) } if (modal) { return <NewPetModal onSubmit={onSubmit} onCancel={() => setModal(false)} /> } return ( <div className="page pets-page"> <section> <div className="row betwee-xs middle-xs"> <div className="col-xs-10"> <h1>Pets</h1> </div> <div className="col-xs-2"> <button onClick={() => setModal(true)}>new pet</button> </div> </div> </section> <section>
 <PetsList pets={data.pets}/>
 </section> </div> ) }

몇 비트의 코드로 서버에서 애완 동물을 가져올 수 있습니다.

gql이란 무엇입니까?

GraphQL의 작업은 일반적으로 graphql-tag 와 백틱으로 작성된 JSON 객체라는 점에 유의하는 것이 중요합니다.

gql 태그는 GraphQL 쿼리 문자열을 GraphQL AST(추상 구문 트리)로 구문 분석하는 JavaScript 템플릿 리터럴 태그입니다.

  • 쿼리 작업
    서버에서 애완 동물을 가져오려면 쿼리 작업을 수행해야 합니다.
    • query 작업을 하기 때문에 이름을 지정하기 전에 작업 type 을 지정해야 했습니다.
    • 쿼리 이름은 GET_PETS 입니다. 필드 이름에 camelCase를 사용하는 것이 GraphQL의 명명 규칙입니다.
    • 우리 필드의 이름은 pets 입니다. 따라서 서버에서 필요한 정확한 필드 (id, name, type, img) 합니다.
    • useQuery 는 Apollo 애플리케이션에서 쿼리를 실행하기 위한 기초가 되는 React 후크입니다. React 구성 요소에서 쿼리 작업을 수행하기 위해 처음에 @apollo/react-hooks 에서 가져온 useQuery 후크를 호출합니다. 다음으로 GraphQL 쿼리 문자열을 전달합니다. 이 경우 GET_PETS 입니다.
  • 구성 요소가 렌더링될 때 useQuery 는 로드, 오류 및 데이터 속성이 포함된 Apollo Client의 개체 응답을 반환합니다. 따라서 UI를 렌더링하는 데 사용할 수 있도록 구조가 해제됩니다.
  • useQuery 는 굉장합니다. async-await 를 포함할 필요는 없습니다. 이미 백그라운드에서 처리되었습니다. 꽤 멋지죠?
    • loading
      이 속성은 응용 프로그램의 로드 상태를 처리하는 데 도움이 됩니다. 우리의 경우 애플리케이션이 로드되는 동안 Loader 구성 요소를 반환합니다. 기본적으로 로드는 false 입니다.
    • error
      만일을 대비하여 이 속성을 사용하여 발생할 수 있는 오류를 처리합니다.
    • data
      여기에는 서버의 실제 데이터가 포함됩니다.
    • 마지막으로 PetsList 구성 요소에서 data.pets 를 개체 값으로 사용하여 pets 소품을 전달합니다.

이 시점에서 서버에 성공적으로 쿼리했습니다.

애플리케이션을 시작하려면 다음 명령을 실행해 보겠습니다.

  • 클라이언트 앱을 시작합니다. CLI에서 npm run app 명령을 실행합니다.
  • 서버를 시작합니다. 다른 CLI에서 npm run server 명령을 실행합니다.
VScode CLI는 클라이언트와 서버를 모두 시작하도록 분할되었습니다.
VScode CLI는 클라이언트와 서버를 모두 시작하도록 분할되었습니다. (큰 미리보기)

모든 것이 잘 되었다면 다음이 표시되어야 합니다.

서버에서 쿼리한 애완동물입니다.
서버에서 쿼리한 애완동물입니다.

데이터 변형

Apollo Client에서 데이터를 변경하거나 데이터를 생성하는 것은 약간의 변경만 제외하면 데이터 쿼리와 거의 동일합니다.

여전히 client/src/pages/Pets.js 에서 강조 표시된 코드를 복사하여 붙여넣습니다.

 .... const GET_PETS = gql` query getPets { pets { id name type img } } `;

const NEW_PETS = gql` mutation CreateAPet($newPet: NewPetInput!) { addPet(input: $newPet) { id name type img } } `;

 const Pets = () => { const [modal, setModal] = useState(false) const { loading, error, data } = useQuery(GET_PETS);
 const [createPet, newPet] = useMutation(NEW_PETS);
 const onSubmit = input => { setModal(false)
 createPet({ variables: { newPet: input } }); } if (loading || newPet.loading) return <Loader />; if (error || newPet.error) return <p>An error occured</p>;
  
 if (modal) { return <NewPetModal onSubmit={onSubmit} onCancel={() => setModal(false)} /> } return ( <div className="page pets-page"> <section> <div className="row betwee-xs middle-xs"> <div className="col-xs-10"> <h1>Pets</h1> </div> <div className="col-xs-2"> <button onClick={() => setModal(true)}>new pet</button> </div> </div> </section> <section> <PetsList pets={data.pets}/> </section> </div> ) } export default Pets

돌연변이를 생성하려면 다음 단계를 수행합니다.

1. mutation

생성, 업데이트 또는 삭제하려면 mutation 작업을 수행해야 합니다. mutation 작업에는 하나의 인수와 함께 CreateAPet 이름이 있습니다. 이 인수에는 NewPetInput 유형의 $newPet 변수가 있습니다. ! 작업이 필요함을 의미합니다. 따라서 GraphQL은 유형이 NewPetInputnewPet 변수를 전달하지 않는 한 작업을 실행하지 않습니다.

2. addPet

mutation 연산 내부에 있는 addPet 함수는 input 인수를 취하고 우리의 $newPet 변수로 설정됩니다. addPet 함수에 지정된 필드 세트는 쿼리의 필드 세트와 같아야 합니다. 우리 작업의 필드 세트는 다음과 같습니다.

  • id
  • name
  • type
  • img

3. useMutation

useMutation React 후크는 Apollo 애플리케이션에서 돌연변이를 실행하기 위한 기본 API입니다. 데이터를 변경해야 할 때 React 구성 요소에서 useMutation 을 호출하고 GraphQL 문자열(이 경우 NEW_PETS )을 전달합니다.

구성 요소가 useMutation 을 렌더링할 때 다음을 포함하는 배열에 튜플(즉, 레코드를 구성하는 정렬된 데이터 집합)을 반환합니다.

  • 돌연변이를 실행하기 위해 언제든지 호출할 수 있는 mutate 함수;
  • 돌연변이 실행의 현재 상태를 나타내는 필드가 있는 개체입니다.

useMutation 후크는 GraphQL 돌연변이 문자열(이 경우 NEW_PETS )에 전달됩니다. 데이터와 객체 필드( newPets )를 변경하는 함수( createPet )인 튜플을 구조화했습니다.

4. createPet

onSubmit 함수에서 setModal 상태 직후에 createPet 을 정의했습니다. 이 함수는 { newPet: input } 으로 설정된 값의 객체 속성을 가진 variable 를 취합니다. input 은 양식의 다양한 입력 필드(예: 이름, 유형 등)를 나타냅니다.

완료되면 결과는 다음과 같아야 합니다.

즉각적인 업데이트 없는 돌연변이
즉각적인 업데이트가 없는 돌연변이.

GIF를 자세히 관찰하면 페이지를 새로 고칠 때만 생성된 애완 동물이 즉시 표시되지 않는다는 것을 알 수 있습니다. 그러나 서버에서 업데이트되었습니다.

가장 큰 질문은 애완 동물이 즉시 업데이트되지 않는 이유입니다. 다음 섹션에서 알아보겠습니다.

Apollo 클라이언트에서 캐싱

앱이 자동으로 업데이트되지 않는 이유는 새로 생성된 데이터가 Apollo Client의 캐시 데이터와 일치하지 않기 때문입니다. 따라서 캐시에서 정확히 무엇을 업데이트해야 하는지에 대한 충돌이 있습니다.

간단히 말해서, 여러 항목(노드)을 업데이트하거나 삭제하는 변형을 수행하는 경우 해당 노드를 참조하는 쿼리를 업데이트하여 돌연변이가 우리의 -에 대한 수정 사항과 일치하도록 캐시된 데이터를 수정할 책임이 있습니다. 종료 데이터.

캐시 동기화 유지

돌연변이 작업을 수행할 때마다 캐시를 ​​동기화 상태로 유지하는 몇 가지 방법이 있습니다.

첫 번째는 refetchQueries 객체 속성을 사용하여 돌연변이 후 일치하는 쿼리를 다시 가져오는 것입니다(가장 간단한 방법).

참고: 이 메서드를 사용하는 경우 refetchQueries 라는 createPet 함수의 개체 속성을 사용하고 쿼리 값이 refetchQueries: [{ query: GET_PETS }] 인 개체 배열을 포함합니다.

이 섹션의 초점은 UI에서 생성된 애완 동물을 업데이트하는 것이 아니라 캐시를 조작하는 것이므로 이 방법을 사용하지 않습니다.

두 번째 방법은 update 기능을 사용하는 것입니다. Apollo Client에는 캐시 데이터를 수정하는 데 도움이 되는 update 도우미 기능이 있어 백엔드 데이터에 대한 변경 사항과 동기화되도록 합니다. 이 함수를 사용하여 캐시를 읽고 쓸 수 있습니다.

캐시 업데이트

다음 강조 표시된 코드를 복사하여 client/src/pages/Pets.js 에 붙여넣습니다.

 ...... const Pets = () => { const [modal, setModal] = useState(false) const { loading, error, data } = useQuery(GET_PETS);
 const [createPet, newPet] = useMutation(NEW_PETS, { update(cache, { data: { addPet } }) { const data = cache.readQuery({ query: GET_PETS }); cache.writeQuery({ query: GET_PETS, data: { pets: [addPet, ...data.pets] }, }); }, } );
 .....

update 함수는 두 개의 인수를 받습니다.

  • 첫 번째 인수는 Apollo Client의 캐시입니다.
  • 두 번째는 서버의 정확한 돌연변이 응답입니다. data 속성을 구조화하고 이를 돌연변이( addPet )로 설정합니다.

다음으로 함수를 업데이트하려면 업데이트해야 하는 쿼리(이 경우 GET_PETS 쿼리)를 확인하고 캐시를 읽어야 합니다.

두 번째로 읽은 query 에 작성하여 업데이트할 것임을 알 수 있도록 해야 합니다. query 작업( GET_PETS )으로 설정된 data 과 함께 query 개체 속성이 포함된 개체를 전달하고 값이 pet 개체이고 addPet 돌연변이의 배열과 애완 동물의 데이터.

이 단계를 주의 깊게 따랐다면 애완 동물을 만들 때 자동으로 업데이트되는 것을 볼 수 있을 것입니다. 변경 사항을 살펴보겠습니다.

애완 동물은 즉시 업데이트됩니다.
애완 동물은 즉시 업데이트됩니다.

낙관적 UI

많은 사람들이 로더와 스피너의 열렬한 팬입니다. 로더를 사용하는 데 아무런 문제가 없습니다. 로더가 최선의 선택인 완벽한 사용 사례가 있습니다. 나는 로더 대 스피너 및 최고의 사용 사례에 대해 썼습니다.

로더와 스피너는 실제로 UI 및 UX 디자인에서 중요한 역할을 하지만 낙관적 UI의 등장으로 주목을 샀습니다.

낙관적 UI 란 무엇입니까?

낙관적 UI는 돌연변이(생성된 데이터)의 결과를 시뮬레이션하고 서버로부터 응답을 받기 전에 UI를 업데이트하는 규칙입니다. 서버에서 응답을 받으면 낙관적인 결과는 버리고 실제 결과로 대체됩니다.

결국 낙관적 UI는 인지된 성능을 관리하고 로드 상태를 방지하는 방법에 불과합니다.

Apollo Client에는 Optimistic UI를 통합하는 매우 흥미로운 방법이 있습니다. 그것은 우리에게 돌연변이 후에 로컬 캐시에 쓸 수 있게 해주는 간단한 후크를 제공합니다. 어떻게 작동하는지 봅시다!

1 단계

client/src/client.js 로 이동하여 강조 표시된 코드만 추가합니다.

 import { ApolloClient } from 'apollo-client' import { InMemoryCache } from 'apollo-cache-inmemory' import { HttpLink } from 'apollo-link-http'
import { setContext } from 'apollo-link-context' import { ApolloLink } from 'apollo-link' const http = new HttpLink({ uri: "https://localhost:4000/" }); const delay = setContext( request => new Promise((success, fail) => { setTimeout(() => { success() }, 800) }) ) const link = ApolloLink.from([ delay, http ])
const cache = new InMemoryCache() const client = new ApolloClient({ link, cache }) export default client

첫 번째 단계에는 다음이 포함됩니다.

  • apollo-link-context 에서 setContext 를 가져옵니다. setContext 함수는 콜백 함수를 사용하고 돌연변이 작업이 수행될 때 지연을 생성하기 위해 setTimeout800ms 로 설정된 프라미스를 반환합니다.
  • ApolloLink.from 메소드는 HTTP 에서 링크(당사 API)를 나타내는 네트워크 활동이 지연되도록 합니다.

2 단계

다음 단계는 낙관적 UI 후크를 사용하는 것입니다. client/src/pages/Pets.js 로 다시 슬라이드하고 아래에 강조 표시된 코드만 추가합니다.

 ..... const Pets = () => { const [modal, setModal] = useState(false) const { loading, error, data } = useQuery(GET_PETS); const [createPet, newPet] = useMutation(NEW_PETS, { update(cache, { data: { addPet } }) { const data = cache.readQuery({ query: GET_PETS }); cache.writeQuery({ query: GET_PETS, data: { pets: [addPet, ...data.pets] }, }); }, } ); const onSubmit = input => { setModal(false) createPet({ variables: { newPet: input },
 optimisticResponse: { __typename: 'Mutation', addPet: { __typename: 'Pet', id: Math.floor(Math.random() * 10000 + ''), name: input.name, type: input.type, img: 'https://via.placeholder.com/200' } }
 }); } .....

optimisticResponse 객체는 서버 응답을 기다리는 대신 애완 동물을 만들 때 UI를 즉시 업데이트하려는 경우에 사용됩니다.

위의 코드 조각에는 다음이 포함됩니다.

  • __typename 은 쿼리된 엔터티의 type 을 가져오기 위해 Apollo에 의해 쿼리에 주입됩니다. 이러한 유형은 Apollo Client에서 apollo-cache 에서 캐싱 목적으로 id 속성(기호)을 빌드하는 데 사용됩니다. 따라서 __typename 은 쿼리 응답의 유효한 속성입니다.
  • 돌연변이는 optimisticResponse__typename 으로 설정됩니다.
  • 앞에서 정의한 것처럼 돌연변이의 이름은 addPet 이고 __typenamePet 입니다.
  • 다음은 낙관적 응답이 업데이트되기를 원하는 돌연변이 필드입니다.
    • id
      서버의 ID가 무엇인지 모르기 때문에 Math.floor 를 사용하여 만들었습니다.
    • name
      이 값은 input.name 으로 설정됩니다.
    • type
      유형의 값은 input.type 입니다.
    • img
      이제 서버에서 이미지를 생성하기 때문에 자리 표시자를 사용하여 서버에서 이미지를 모방했습니다.

이것은 참으로 긴 여정이었습니다. 막바지에 이르면 주저하지 말고 커피 한 잔과 함께 의자에서 휴식을 취하십시오.

결과를 살펴보겠습니다. 이 프로젝트의 지원 리포지토리는 GitHub에 있습니다. 복제하고 실험하십시오.

펫샵 앱 최종 결과물
우리 앱의 최종 결과입니다.

결론

Optimistic UI 및 페이지 매김과 같은 Apollo Client의 놀라운 기능은 클라이언트 측 앱 구축을 현실로 만듭니다.

Apollo Client는 Vue.js 및 Angular와 같은 다른 프레임워크와 매우 잘 작동하지만 React 개발자는 Apollo Client Hooks가 있으므로 훌륭한 앱을 빌드하는 것을 즐깁니다.

이 기사에서는 표면만 긁었습니다. Apollo Client를 마스터하려면 끊임없는 연습이 필요합니다. 따라서 리포지토리를 복제하고 페이지 매김을 추가한 다음 제공하는 다른 기능을 사용해 보세요.

아래 의견 섹션에서 피드백과 경험을 공유하십시오. Twitter에서 진행 상황에 대해 논의할 수도 있습니다. 건배!

참고문헌

  • "클라이언트 측 GraphQL In React", Scott Moss, 프론트엔드 마스터
  • "문서", Apollo 클라이언트
  • "React를 사용한 낙관적 UI", Patryk Andrzejewski
  • "낙관적인 사용자 인터페이스의 진실", Smashing Magazine