카테고리 없음

apollo 캐시 안에 정확히 필드가 있고 눈으로 보이는데, cache.readQuery로 불러오질 못하네요 ...

김영수 2024. 11. 13. 22:41

질문:

graphQL 사용하시는분 계신가요? apollo 캐시 안에 정확히 필드가 있고 눈으로 보이는데, cache.readQuery로 불러오질 못하네요 ㅠㅠ 설정을 따로한건 없고 처음 불러와보는데, 다른걸 설정해줘야할까요?
      update(cache, { data }) {
        console.log('cache', cache);
        //debug
        const eventData = cache.readQuery({ query: EventsDocument });
        console.log('Event data in cache:', eventData);

 

Question about graphQL: I have exactly the fields in the apollo cache and I can see them visually, but I can't get them with cache.readQuery.
 I don't have anything configured and this is my first time loading it, do I need to configure anything else?
      update(cache, { data }) {
        console.log('cache', cache);
        //debug
        const eventData = cache.readQuery({ query: EventsDocument });
        console.log('Event data in cache:', eventData);

 

답:

아폴로 클라이언트에서는 읽으려는 정확한 쿼리 구조에 대한 데이터가 캐시에 있는 경우 cache.readQuery가 작동해야 합니다. 하지만 cache.readQuery를 처음 사용하는 경우 몇 가지 확인해야 할 사항이 있습니다:

쿼리가 일치하는지 확인합니다: 이벤트도큐먼트의 구조(cache.readQuery에 전달하는 쿼리)가 캐시에 있는 데이터 구조와 정확히 일치해야 합니다. 필드나 구조가 일치하지 않으면 readQuery가 null을 반환하거나 오류가 발생할 수 있습니다.

데이터가 존재하는지 확인합니다: 처음 로드하는 경우, 특히 쿼리가 이전에 실행된 적이 없는 경우에는 아직 캐시된 데이터가 없을 수 있습니다. readQuery를 시도하기 전에 cache.writeQuery를 실행하거나 useQuery를 통해 쿼리를 가져와서 데이터가 캐시되어 있는지 확인하세요.

캐시 정책을 확인합니다: 쿼리의 가져오기 정책이 네트워크 전용 또는 캐시 없음으로 설정되어 있으면 캐시를 우회하므로 읽을 데이터가 없을 수 있습니다. 이 경우 캐시 우선 또는 캐시 및 네트워크 정책을 사용해 보세요.

쿼리 실행을 확인합니다: 쿼리가 실제로 실행되어 캐시에 저장되고 있는지 확인합니다. 원본 쿼리에 문제가 있거나 아직 실행되지 않은 경우 readQuery가 데이터를 찾지 못할 수 있습니다.

Apollo 개발자 도구: Apollo DevTools를 사용하여 캐시를 검사하고 캐시된 데이터의 구조를 확인할 수 있습니다. 이를 통해 불일치를 식별하는 데 도움이 될 수 있습니다.

In Apollo Client, cache.readQuery should work if the cache has data for the exact query structure you're trying to read. However, if this is your first time using cache.readQuery, here are a few things to check:

  1. Ensure the Query Matches: The structure of EventsDocument (the query you’re passing to cache.readQuery) must match exactly with the data structure in the cache. If there’s any mismatch in fields or structure, readQuerymight return null or throw an error.
  2. Check if Data Exists: Since it's the first time loading, there might not be cached data yet, especially if the query hasn’t been executed before. Make sure the data is cached by running cache.writeQuery or fetching the query via useQuery before attempting readQuery.
  3. Check Cache Policy: If your query’s fetch policy is set to network-only or no-cache, it bypasses the cache, so there wouldn’t be any data to read. Try using cache-first or cache-and-network policies if that’s the case.
  4. Verify Query Execution: Ensure that the query is actually being executed and stored in the cache. Sometimes readQuery might not find data if there’s an issue with the original query or if it hasn’t been run yet.
  5. Apollo DevTools: Use Apollo DevTools to inspect the cache and verify the structure of cached data. This can help identify any mismatches.

 

추가질문:

What is apollo and
What is apollo cache?

apollo 란 무엇이고
apollo 캐시란 무엇인가요?

 

답:

Apollo is a suite of tools for working with GraphQL, a query language for APIs. Apollo helps developers manage data from a GraphQL backend in a way that integrates seamlessly with frontend frameworks like React, Angular, and Vue. The primary component in the suite is Apollo Client, which allows you to interact with a GraphQL API from the client side, managing data fetching, caching, and updating the UI as data changes.

Apollo Client

Apollo Client is a JavaScript library used for querying a GraphQL API and handling data within the client application. It provides an abstraction layer on top of GraphQL that simplifies data management, making it easier to fetch data, handle state, and synchronize it across the app.

Apollo Cache

The Apollo cache is a data store that stores the results of GraphQL queries on the client side. Once data is fetched from the server via a GraphQL query, Apollo Client caches this data locally, allowing the app to quickly retrieve and reuse it without needing to re-fetch from the server every time. This cache serves two main purposes:

  1. Efficient Data Fetching: Once a query is executed, its result is stored in the cache, so the same query doesn’t have to hit the server repeatedly. Instead, Apollo can directly retrieve the cached result and display it instantly.
  2. State Management: Apollo’s cache also acts as a state manager for the application, syncing server data with the UI. This reduces the need for separate state management libraries for handling server-side data, making it easier to track data changes across the app.

How the Apollo Cache Works

  • Normalizing Data: Apollo normalizes data in the cache. For example, if multiple queries return the same Userobject with the same id, Apollo will store only one instance of this User object in the cache. This avoids redundancy and ensures that changes to the user’s data are reflected everywhere it’s used.
  • Automatic Cache Updates: Apollo automatically updates the cache when you perform certain operations, like mutations. You can also manually update or read from the cache using methods like cache.readQuery and cache.writeQuery.

The cache behavior can be controlled with fetch policies like:

  • cache-first: Checks the cache first; if data is present, it uses it; otherwise, it fetches from the network.
  • network-only: Always fetches from the network and does not cache the result.
  • no-cache: Never caches the result and always fetches a fresh response.

Apollo Client and its cache are powerful tools for efficient, flexible data management in frontend applications that use GraphQL. 

 

Apollo는 API용 쿼리 언어인 GraphQL로 작업하기 위한 도구 모음입니다. Apollo는 개발자가 GraphQL 백엔드의 데이터를 React, Angular, Vue와 같은 프론트엔드 프레임워크와 원활하게 통합하는 방식으로 관리할 수 있도록 도와줍니다. 이 제품군의 기본 구성 요소는 클라이언트 측에서 GraphQL API와 상호 작용하여 데이터 가져오기, 캐싱, 데이터 변경에 따른 UI 업데이트를 관리할 수 있는 Apollo 클라이언트입니다.

아폴로 클라이언트
Apollo 클라이언트는 클라이언트 애플리케이션 내에서 GraphQL API를 쿼리하고 데이터를 처리하는 데 사용되는 JavaScript 라이브러리입니다. 이 라이브러리는 데이터 관리를 간소화하여 데이터를 쉽게 가져오고, 상태를 처리하고, 앱 전체에서 동기화할 수 있도록 GraphQL 위에 추상화 계층을 제공합니다.

아폴로 캐시
Apollo 캐시는 클라이언트 측에 GraphQL 쿼리 결과를 저장하는 데이터 저장소입니다. GraphQL 쿼리를 통해 서버에서 데이터를 가져오면 Apollo 클라이언트는 이 데이터를 로컬에 캐시하여 앱이 매번 서버에서 다시 가져올 필요 없이 데이터를 빠르게 검색하고 재사용할 수 있도록 합니다. 이 캐시는 크게 두 가지 용도로 사용됩니다:

효율적인 데이터 가져오기: 쿼리가 실행되면 그 결과가 캐시에 저장되므로 동일한 쿼리가 서버에 반복적으로 호출될 필요가 없습니다. 대신 Apollo는 캐시된 결과를 직접 검색하여 즉시 표시할 수 있습니다.

상태 관리: Apollo의 캐시는 애플리케이션의 상태 관리자 역할도 수행하여 서버 데이터를 UI와 동기화합니다. 따라서 서버 측 데이터를 처리하기 위한 별도의 상태 관리 라이브러리가 필요하지 않으므로 앱 전반에서 데이터 변경 사항을 더 쉽게 추적할 수 있습니다.

아폴로 캐시 작동 방식
데이터 정규화: Apollo는 캐시에 있는 데이터를 정규화합니다. 예를 들어, 여러 쿼리가 동일한 ID를 가진 동일한 사용자 개체를 반환하는 경우 Apollo는 이 사용자 개체의 인스턴스 하나만 캐시에 저장합니다. 이렇게 하면 중복을 방지하고 사용자 데이터의 변경 사항이 데이터가 사용되는 모든 곳에 반영됩니다.

자동 캐시 업데이트: 아폴로는 변경과 같은 특정 작업을 수행할 때 캐시를 자동으로 업데이트합니다. cache.readQuery 및 cache.writeQuery와 같은 메서드를 사용하여 캐시를 수동으로 업데이트하거나 캐시에서 읽을 수도 있습니다.

캐시 동작은 다음과 같은 가져오기 정책으로 제어할 수 있습니다:

캐시 우선: 캐시를 먼저 확인하고 데이터가 있으면 캐시를 사용하며, 그렇지 않으면 네트워크에서 가져옵니다.
네트워크 전용: 항상 네트워크에서 가져오며 결과를 캐시하지 않습니다.
캐시 없음: 결과를 캐시하지 않고 항상 새 응답을 가져옵니다.
Apollo 클라이언트와 캐시는 GraphQL을 사용하는 프론트엔드 애플리케이션에서 효율적이고 유연한 데이터 관리를 위한 강력한 도구입니다. 

 

추가질문:

apollo and apollo cache to write production-ready code.

 

답:

GraphQL 기반 애플리케이션에서 프로덕션에 바로 사용할 수 있는 코드를 작성하려면 Apollo 클라이언트와 Apollo 캐시를 효과적으로 사용하는 것이 핵심입니다. 다음은 프로덕션 환경에서 이러한 도구를 활용하는 모범 사례에 대한 가이드입니다:

1. 강력한 스키마 설계
프론트엔드 앱의 데이터 요구사항과 일치하는 잘 구조화된 GraphQL 스키마로 시작하세요. 여기에는 명확한 유형, 쿼리 및 변형을 정의하는 것이 포함됩니다.
스키마 지시어(예: @deprecated)를 사용하여 시간이 지남에 따라 단계적으로 폐지될 수 있는 필드나 작업을 표시하여 이전 버전과의 호환성을 유지하세요.
2. 효율성을 위한 Apollo 캐시 최적화
정규화된 캐싱 사용: Apollo의 정규화된 캐싱을 사용하면 각 개체를 ID별로 고유하게 저장하여 복잡하고 상호 연결된 데이터를 처리할 수 있습니다. 모든 주요 개체에 대해 ID 필드(또는 이와 유사한 고유 식별자)를 구성해야 Apollo가 캐시에서 이를 정규화할 수 있습니다.
캐시 읽기/쓰기: 최적화 UI 업데이트 처리 또는 UI와 변경 결과 동기화 등 특정 사용 사례에 대해 캐시된 데이터를 수동으로 관리하려면 cache.readQuery 및 cache.writeQuery를 사용하세요.
캐시 정책 제어: 데이터 새로 고침 요구 사항에 따라 캐시 정책(캐시 우선, 네트워크 전용 등)을 조정할 수 있습니다:
정적 데이터(예: 앱 설정): 캐시 우선 사용.
자주 업데이트되는 데이터(예: 실시간 피드): 네트워크 전용 또는 캐시 및 네트워크를 사용합니다.
3. 글로벌 오류 처리 구현
오류 정책을 사용하세요: 오류 처리 방법(예: 무시, 모두)을 결정하기 위해 Apollo 클라이언트에 대한 오류 정책을 설정합니다. 예를 들어 오류 정책: '모두'를 설정하면 쿼리의 일부만 실패하는 경우 부분적인 결과에 액세스할 수 있습니다.
전역 오류 처리: Apollo의 onError 링크를 사용하여 전역적으로 네트워크 및 GraphQL 오류를 기록하거나, 사용자에게 알리거나, 폴백을 트리거할 수 있습니다. 이렇게 하면 오류가 눈에 띄지 않게 되는 것을 방지하고 일관된 오류 처리 전략을 제공합니다.
4. 로컬 상태 관리를 위한 Apollo 클라이언트 상태 활용
Redux와 같은 다른 상태 관리 라이브러리를 추가하는 대신 로컬 클라이언트 전용 데이터(예: UI 상태)를 Apollo의 캐시에 직접 저장할 수 있습니다.
특히 서버와 클라이언트 전용 필드가 혼합된 타입의 경우, 타입폴리시를 사용하여 캐시에서 다양한 타입을 처리하는 방법을 지정할 수 있습니다.
5. 페이지 매김 및 지연 쿼리로 데이터 가져오기 최적화하기
페이지 매김: 앱에서 큰 목록(예: 글 또는 사용자)을 가져오는 경우 커서 또는 오프셋 기반 페이지 매김을 구현하여 큰 데이터 집합을 가져오는 것을 방지하세요.
지연 쿼리: 필요한 경우(예: 버튼 클릭 후)에만 데이터를 로드하려면 useLazyQuery를 사용합니다. 이렇게 하면 앱의 초기 로드 시간이 단축됩니다.
6. 더 나은 사용자 경험을 위한 낙관적 UI
최적화된 업데이트를 사용하여 사용자에게 양식 제출이나 항목 추가/제거와 같은 작업에 대한 즉각적인 피드백을 제공하세요. 낙관적 응답을 사용하면 서버가 변경 사항을 확인할 때까지 기다리기 전에 아폴로가 UI를 일시적으로 업데이트합니다.
이를 구현하려면 서버의 예상 응답 형태를 반영하는 optimisticResponse 객체를 변이에 전달하세요.
7. 효율성을 위해 조각화된 쿼리 관리하기
GraphQL 조각을 사용하여 쿼리의 여러 부분을 재사용 가능한 단위로 분리하세요. 조각은 중첩된 데이터 구조에서 특히 중복을 줄이고, 쿼리 전체에 일관된 필드를 적용하며, 캐싱을 더 쉽게 만드는 데 도움이 됩니다.
8. 일괄 요청 및 중복 제거 고려하기
일괄 처리: 여러 GraphQL 요청을 단일 네트워크 요청으로 일괄 처리하려면 @apollo/client/link/batch-http를 활성화하면 여러 요청이 있는 구성 요소에 특히 유용합니다.
중복 제거: 중복 제거: 중복 제거를 true로 설정하면 동일한 쿼리가 동시에 전송되지 않도록 방지할 수 있으며, 여러 컴포넌트에 동일한 데이터가 필요한 경우에 유용합니다.
9. 개발 시 Apollo 개발자 도구 사용
Apollo DevTools는 캐시, 활성 쿼리, 변이 및 성능에 대한 인사이트를 제공합니다. 개발 중에 정기적으로 캐시와 쿼리를 검사하여 캐싱 및 데이터 가져오기 전략이 의도한 대로 작동하는지 확인하세요.
10. 서버 측 렌더링(SSR) 최적화
앱이 SSR 기반인 경우(예: Next.js 사용) Apollo는 서버 측에서 데이터를 미리 가져와 초기 캐시를 채우고 클라이언트의 로드 시간을 단축할 수 있습니다.
초기 페이지 로드를 위한 쿼리를 프리페치하려면 getStaticProps 또는 getServerSideProps를 사용하여 사용자의 페이지 로드 속도를 높이고 SEO를 개선하세요.
11. 프로덕션 지원 보안 관행 염두에 두기
사용자 입력 검증: GraphQL을 사용하더라도 항상 입력값을 검증하고 위생 처리하여 인젝션 공격을 방지하세요.
민감한 데이터 노출 방지: 클라이언트에서 민감한 데이터를 캐싱할 때는 주의하세요. 캐시 정책을 설정하여 임시로 유지되어야 하는 데이터를 저장하지 않도록 하세요.
인증 사용: 적절한 인증(예: OAuth2, JWT)을 사용하여 GraphQL API를 보호하고 요청에 토큰을 자동으로 추가하기 위해 Apollo 링크를 고려하세요.
프로덕션 준비된 Apollo 클라이언트 설정 샘플

Here’s an example of configuring Apollo Client with some of these best practices:

다음은 이러한 모범 사례 중 일부를 사용하여 Apollo 클라이언트를 구성하는 예제입니다:

import { ApolloClient, InMemoryCache, ApolloLink, HttpLink } from '@apollo/client';
import { onError } from '@apollo/client/link/error';

const httpLink = new HttpLink({ uri: '/graphql' });

const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors) {
    graphQLErrors.forEach(({ message, locations, path }) =>
      console.error(`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`)
    );
  }
  if (networkError) console.error(`[Network error]: ${networkError}`);
});

const cache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        items: {
          merge(existing = [], incoming) {
            return [...existing, ...incoming];
          },
        },
      },
    },
  },
});

const client = new ApolloClient({
  link: ApolloLink.from([errorLink, httpLink]),
  cache,
  connectToDevTools: process.env.NODE_ENV === 'development',
});

export default client;

 

이 설정에서는

오류 처리는 errorLink로 중앙 집중화됩니다.
정규화된 캐싱 및 페이지 매김은 typePolicies로 구성됩니다.
개발 도구는 개발 모드에서만 조건부로 활성화됩니다.
이러한 모범 사례를 통해 Apollo 클라이언트 및 캐시를 강력하고 효율적이며 확장 가능한 프로덕션용으로 설정할 수 있습니다.