# React Native와 GraphQL 2026: Apollo Client, 쿼리 및 면접 질문
> Apollo Client 4.0을 사용하여 React Native에 GraphQL을 통합하는 방법을 알아봅니다. 쿼리, 뮤테이션, 캐싱 전략, 모바일 GraphQL 개발 면접 질문을 다룹니다.
- Published: 2026-08-20
- Updated: 2026-08-20
- Author: Anthony Fillion-Maillet
- Tags: react-native, graphql, apollo-client, mobile, api, interview
- Reading time: 9 min
---
GraphQL은 유연한 데이터 페칭과 효율적인 네트워크 사용이 필요한 React Native 애플리케이션에서 선호되는 API 레이어로 자리 잡았습니다. 2025년 후반에 출시된 Apollo Client 4.0은 모바일 환경을 위해 특별히 설계된 20-30% 작은 번들과 개선된 TypeScript 지원을 제공하는 경량 아키텍처를 도입했습니다.
> **Apollo Client 4.0 주요 변경 사항**
>
> Apollo Client 4.0은 React 전용 export를 `@apollo/client/react`로 분리했습니다. React 19 Suspense와 React Compiler를 지원하며, 모놀리식 `ApolloError`를 디버깅 개선을 위한 특정 에러 클래스로 대체했습니다.
## React Native에서 Apollo Client 4 설정하기
설치 과정은 간단합니다. Apollo Client 4.0은 Expo와 베어 React Native 프로젝트 모두에서 작동합니다.
```bash
# Installation
npm install @apollo/client graphql
```
클라이언트 초기화에는 네트워크 요청을 위한 `HttpLink`와 로컬 상태 관리를 위한 `InMemoryCache`가 필요합니다.
```typescript
// src/apollo/client.ts
import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';
const httpLink = new HttpLink({
uri: 'https://api.example.com/graphql',
// Enable text streaming for @defer and subscriptions
fetchOptions: {
reactNative: { textStreaming: true },
},
});
export const apolloClient = new ApolloClient({
link: httpLink,
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
// Merge paginated results
products: {
keyArgs: ['category'],
merge(existing = [], incoming) {
return [...existing, ...incoming];
},
},
},
},
},
}),
});
```
`typePolicies` 설정은 무한 스크롤이 있는 모바일 앱에서 일반적인 요구 사항인 페이지네이션 데이터의 캐시 정규화를 처리합니다.
## ApolloProvider로 앱 래핑하기
프로바이더 패턴은 Apollo Client를 React 컴포넌트 트리에 연결합니다. 일반적으로 `App.tsx` 또는 Expo Router 레이아웃의 루트 레벨에 배치합니다.
```tsx
// App.tsx
import { ApolloProvider } from '@apollo/client/react';
import { apolloClient } from './src/apollo/client';
import { RootNavigator } from './src/navigation';
export default function App() {
return (
);
}
```
Apollo Client 4.0은 메인 진입점이 아닌 `@apollo/client/react`에서 React 훅을 import합니다. 이 변경으로 코어 클라이언트가 React가 아닌 컨텍스트에서 실행될 때 번들 크기가 줄어듭니다.
## 타입 안전한 GraphQL 쿼리 작성하기
GraphQL Code Generator는 스키마에서 TypeScript 타입을 생성하여 런타임 타입 불일치를 제거합니다. 클라이언트 플러그인과 함께 설치합니다.
```bash
# Install codegen tools
npm install -D @graphql-codegen/cli @graphql-codegen/client-preset
```
codegen 설정은 스키마를 지정하고 출력 위치를 지정합니다.
```typescript
// codegen.ts
import type { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
schema: 'https://api.example.com/graphql',
documents: ['src/**/*.tsx', 'src/**/*.ts'],
generates: {
'./src/gql/': {
preset: 'client',
config: {
documentMode: 'string',
},
},
},
};
export default config;
```
`npx graphql-codegen`을 실행하면 `useQuery` 훅이 사용하는 타입이 지정된 document 노드가 생성됩니다.
## useQuery로 데이터 페칭하기
`useQuery` 훅은 로딩 상태, 에러, 캐시 업데이트를 관리합니다. Apollo Client 4.0은 더 예측 가능한 상태 전환을 위한 `dataState` API를 도입했습니다.
```tsx
// src/screens/ProductListScreen.tsx
import { useQuery } from '@apollo/client/react';
import { graphql } from '../gql';
import { ActivityIndicator, FlatList, Text, View } from 'react-native';
const GET_PRODUCTS = graphql(`
query GetProducts($category: String!, $limit: Int, $offset: Int) {
products(category: $category, limit: $limit, offset: $offset) {
id
name
price
imageUrl
}
}
`);
export function ProductListScreen({ category }: { category: string }) {
const { data, loading, error, fetchMore } = useQuery(GET_PRODUCTS, {
variables: { category, limit: 20, offset: 0 },
// Fetch from cache first, then network
fetchPolicy: 'cache-and-network',
// Keep previous data while fetching new
notifyOnNetworkStatusChange: true,
});
if (error) {
return Error: {error.message};
}
const loadMore = () => {
fetchMore({
variables: { offset: data?.products.length ?? 0 },
});
};
return (
item.id}
renderItem={({ item }) => }
onEndReached={loadMore}
onEndReachedThreshold={0.5}
ListFooterComponent={loading ? : null}
/>
);
}
```
`cache-and-network` 페치 정책은 서버에서 새로고침하는 동안 캐시된 데이터를 즉시 표시합니다. 이 패턴은 모바일 네트워크에서 체감 지연 시간을 줄입니다.
## 뮤테이션과 낙관적 업데이트
뮤테이션은 서버 데이터를 수정하고 로컬 캐시를 업데이트합니다. 낙관적 응답은 서버가 변경을 확인하기 전에 즉각적인 피드백을 제공합니다.
```tsx
// src/hooks/useAddToCart.ts
import { useMutation } from '@apollo/client/react';
import { graphql } from '../gql';
const ADD_TO_CART = graphql(`
mutation AddToCart($productId: ID!, $quantity: Int!) {
addToCart(productId: $productId, quantity: $quantity) {
id
items {
id
product {
id
name
}
quantity
}
totalPrice
}
}
`);
export function useAddToCart() {
const [addToCart, { loading }] = useMutation(ADD_TO_CART, {
// Optimistic response for instant UI feedback
optimisticResponse: ({ productId, quantity }) => ({
addToCart: {
__typename: 'Cart',
id: 'current-cart',
items: [],
totalPrice: 0,
},
}),
// Update cache after mutation
update(cache, { data }) {
if (!data?.addToCart) return;
cache.modify({
id: cache.identify({ __typename: 'Cart', id: 'current-cart' }),
fields: {
items: () => data.addToCart.items,
totalPrice: () => data.addToCart.totalPrice,
},
});
},
});
return { addToCart, loading };
}
```
`cache.modify` 메서드는 전체 쿼리를 다시 가져오지 않고 캐시된 필드를 직접 업데이트합니다. 이 접근 방식은 셀룰러 연결에서 네트워크 요청을 최소화합니다.
## 모바일 앱에서 네트워크 에러 처리하기
Apollo Client 4.0은 일반적인 `ApolloError`를 특정 에러 클래스로 대체했습니다. 정적 `.is()` 메서드를 통해 타입 안전한 에러 처리가 가능합니다.
```tsx
// src/components/ErrorBoundary.tsx
import { NetworkError, GraphQLErrors } from '@apollo/client/errors';
import { Text, Button, View } from 'react-native';
interface Props {
error: Error;
onRetry: () => void;
}
export function QueryErrorHandler({ error, onRetry }: Props) {
// Network failure (offline, timeout, DNS)
if (NetworkError.is(error)) {
return (
Connection failed. Check your internet.
);
}
// GraphQL validation or resolver errors
if (GraphQLErrors.is(error)) {
const firstError = error.graphQLErrors[0];
if (firstError?.extensions?.code === 'UNAUTHENTICATED') {
return Session expired. Please log in again.;
}
return Request failed: {firstError?.message};
}
return Something went wrong;
}
```
네트워크 에러와 GraphQL 에러를 구분하면 다른 복구 전략을 사용할 수 있습니다. 일시적인 장애에는 재시도, 인증 문제에는 리다이렉트를 수행합니다.
## 캐시 영속화를 통한 오프라인 지원
모바일 앱은 이전에 가져온 데이터에 대한 오프라인 액세스가 필요합니다. `apollo3-cache-persist` 라이브러리는 캐시를 AsyncStorage에 직렬화합니다.
```typescript
// src/apollo/persistence.ts
import AsyncStorage from '@react-native-async-storage/async-storage';
import { persistCache } from 'apollo3-cache-persist';
import { apolloClient } from './client';
export async function initializeCache() {
await persistCache({
cache: apolloClient.cache,
storage: AsyncStorage,
maxSize: 1024 * 1024 * 5, // 5 MB limit
debounce: 1000, // Write at most once per second
});
}
```
앱을 렌더링하기 전에 `initializeCache`를 호출하여 디스크에서 캐시된 데이터를 복원합니다. 사용자는 네트워크 액세스 없이도 마지막으로 가져온 콘텐츠를 즉시 볼 수 있습니다.
## GraphQL 구독을 통한 실시간 업데이트
구독은 WebSocket을 통해 서버 이벤트를 클라이언트에 푸시합니다. Apollo Client 4.0은 최신 [graphql-ws](https://github.com/enisdenjo/graphql-ws) 프로토콜을 지원합니다.
```typescript
// src/apollo/client.ts
import { split, HttpLink, ApolloClient, InMemoryCache } from '@apollo/client';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';
import { getMainDefinition } from '@apollo/client/utilities';
const httpLink = new HttpLink({ uri: 'https://api.example.com/graphql' });
const wsLink = new GraphQLWsLink(
createClient({
url: 'wss://api.example.com/graphql',
connectionParams: async () => ({
authToken: await getStoredToken(),
}),
})
);
// Route subscriptions to WebSocket, others to HTTP
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink
);
export const apolloClient = new ApolloClient({
link: splitLink,
cache: new InMemoryCache(),
});
```
`split` 함수는 오퍼레이션 타입에 따라 오퍼레이션을 적절한 트랜스포트로 라우팅합니다.
## 자주 묻는 면접 질문: React Native GraphQL
기술 면접에서는 GraphQL의 모바일 특화 트레이드오프에 대한 이해도를 자주 확인합니다. 다음 질문들은 React Native 포지션에서 자주 출제됩니다.
### 모바일 앱에서 REST 대신 GraphQL을 선택하는 이유는?
GraphQL은 요청된 필드만 반환하여 오버페칭을 줄입니다. 단일 쿼리가 여러 REST 엔드포인트를 대체하여 고지연 셀룰러 연결에서 라운드 트립을 줄입니다. 타입이 지정된 스키마로 컴파일 타임 검증이 가능해져 런타임 전에 API 불일치를 감지할 수 있습니다.
### Apollo Client는 데이터를 어떻게 캐시합니까?
Apollo는 `__typename`과 `id`로 응답을 정규화하여 각 엔티티를 한 번만 저장합니다. 쿼리는 캐시 키로 정규화된 객체를 참조합니다. 이 중복 제거로 메모리를 절약하고 일관성을 보장합니다. 한 목록에서 제품을 업데이트하면 모든 곳에서 업데이트됩니다.
### Apollo Client가 지원하는 페치 정책은?
| 정책 | 동작 | 사용 사례 |
|--------|----------|----------|
| `cache-first` | 캐시가 있으면 반환, 없으면 페치 | 기본값, 요청 최소화 |
| `cache-and-network` | 캐시 즉시 반환 후 네트워크에서 업데이트 | 즉시 표시와 신선한 데이터 |
| `network-only` | 항상 페치, 캐시 업데이트 | 오래된 데이터가 허용되지 않을 때 |
| `cache-only` | 페치 안 함, 캐시에 없으면 실패 | 오프라인 모드 |
| `no-cache` | 캐시 없이 페치 | 민감한 데이터 |
### 낙관적 업데이트는 어떻게 작동합니까?
클라이언트가 뮤테이션 결과를 예측하고 서버가 응답하기 전에 캐시를 업데이트합니다. 서버가 다른 결과를 반환하면 Apollo는 낙관적 데이터를 실제 응답으로 교체합니다. 뮤테이션이 실패하면 Apollo는 이전 상태로 롤백합니다.
### GraphQL에서 N+1 문제가 발생하는 원인은?
관련 데이터를 개별적으로 가져오는 중첩된 리졸버가 N+1 쿼리를 생성합니다. 50개의 제품과 해당 카테고리를 요청하면 1개의 제품 쿼리와 50개의 카테고리 쿼리가 트리거됩니다. 서버 측 DataLoader가 이를 단일 쿼리로 배치 처리합니다.
### Apollo Client에서 인증 토큰을 어떻게 처리합니까?
권장되는 접근 방식은 [Apollo Link](https://www.apollographql.com/docs/react/api/link/introduction/)를 사용하여 모든 요청에 토큰을 첨부하는 것입니다.
```typescript
// src/apollo/authLink.ts
import { setContext } from '@apollo/client/link/context';
import { getStoredToken } from '../auth/storage';
export const authLink = setContext(async (_, { headers }) => {
const token = await getStoredToken();
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
};
});
```
이 링크를 클라이언트 설정에서 `HttpLink` 앞에 체인합니다.
## React Native에서 GraphQL 디버깅
Apollo는 React Native 개발을 위한 여러 디버깅 옵션을 제공합니다. [VS Code Apollo 확장 프로그램](https://marketplace.visualstudio.com/items?itemName=apollographql.vscode-apollo)에는 캐시 내용과 쿼리 기록을 표시하는 내장 Client DevTools가 포함되어 있습니다.
독립형 디버깅의 경우 Flipper가 커뮤니티 플러그인 `react-native-apollo-devtools`와 통합됩니다. 이 플러그인은 캐시 상태를 검사하고 진행 중인 쿼리를 감시하며 뮤테이션을 리플레이합니다.
React Native Debugger를 통한 네트워크 검사는 "Debug JS Remotely"를 활성화해야 합니다. 그러면 Network 탭에 요청 및 응답 본문이 포함된 GraphQL 페이로드가 표시됩니다.
## 모바일 GraphQL 개발 핵심 요약
- Apollo Client 4.0은 ESM 우선 패키징과 분리된 React export를 통해 번들 크기를 20-30% 줄입니다.
- `cache-and-network` 페치 정책은 느린 연결에서 즉시 표시와 데이터 신선도의 균형을 맞춥니다.
- 낙관적 업데이트는 즉각적인 피드백을 제공하고 `cache.modify`는 불필요한 리페치를 방지합니다.
- Apollo Client 4.0의 타입이 지정된 에러 클래스를 통해 네트워크 장애와 GraphQL 에러에 대한 특정 처리가 가능합니다.
- `apollo3-cache-persist`를 통한 캐시 영속화로 이전에 가져온 데이터에 대한 오프라인 액세스가 가능합니다.
- GraphQL 구독에는 `graphql-ws` 프로토콜과 split link 설정이 필요합니다.
- 면접 질문은 캐싱 전략, 페치 정책, N+1 방지와 같은 모바일 특화 트레이드오프에 초점을 맞춥니다.
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/ko/blog/react-native/react-native-graphql-apollo-client-queries-interview