React Native와 GraphQL 2026: Apollo Client, 쿼리 및 면접 질문
Apollo Client 4.0을 사용하여 React Native에 GraphQL을 통합하는 방법을 알아봅니다. 쿼리, 뮤테이션, 캐싱 전략, 모바일 GraphQL 개발 면접 질문을 다룹니다.

GraphQL은 유연한 데이터 페칭과 효율적인 네트워크 사용이 필요한 React Native 애플리케이션에서 선호되는 API 레이어로 자리 잡았습니다. 2025년 후반에 출시된 Apollo Client 4.0은 모바일 환경을 위해 특별히 설계된 20-30% 작은 번들과 개선된 TypeScript 지원을 제공하는 경량 아키텍처를 도입했습니다.
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 프로젝트 모두에서 작동합니다.
# Installation
npm install @apollo/client graphql클라이언트 초기화에는 네트워크 요청을 위한 HttpLink와 로컬 상태 관리를 위한 InMemoryCache가 필요합니다.
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 레이아웃의 루트 레벨에 배치합니다.
import { ApolloProvider } from '@apollo/client/react';
import { apolloClient } from './src/apollo/client';
import { RootNavigator } from './src/navigation';
export default function App() {
return (
<ApolloProvider client={apolloClient}>
<RootNavigator />
</ApolloProvider>
);
}Apollo Client 4.0은 메인 진입점이 아닌 @apollo/client/react에서 React 훅을 import합니다. 이 변경으로 코어 클라이언트가 React가 아닌 컨텍스트에서 실행될 때 번들 크기가 줄어듭니다.
타입 안전한 GraphQL 쿼리 작성하기
GraphQL Code Generator는 스키마에서 TypeScript 타입을 생성하여 런타임 타입 불일치를 제거합니다. 클라이언트 플러그인과 함께 설치합니다.
# Install codegen tools
npm install -D @graphql-codegen/cli @graphql-codegen/client-presetcodegen 설정은 스키마를 지정하고 출력 위치를 지정합니다.
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를 도입했습니다.
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 <Text>Error: {error.message}</Text>;
}
const loadMore = () => {
fetchMore({
variables: { offset: data?.products.length ?? 0 },
});
};
return (
<FlatList
data={data?.products ?? []}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <ProductCard product={item} />}
onEndReached={loadMore}
onEndReachedThreshold={0.5}
ListFooterComponent={loading ? <ActivityIndicator /> : null}
/>
);
}cache-and-network 페치 정책은 서버에서 새로고침하는 동안 캐시된 데이터를 즉시 표시합니다. 이 패턴은 모바일 네트워크에서 체감 지연 시간을 줄입니다.
React Native 면접 준비가 되셨나요?
인터랙티브 시뮬레이터, flashcards, 기술 테스트로 연습하세요.
뮤테이션과 낙관적 업데이트
뮤테이션은 서버 데이터를 수정하고 로컬 캐시를 업데이트합니다. 낙관적 응답은 서버가 변경을 확인하기 전에 즉각적인 피드백을 제공합니다.
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() 메서드를 통해 타입 안전한 에러 처리가 가능합니다.
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 (
<View>
<Text>Connection failed. Check your internet.</Text>
<Button title="Retry" onPress={onRetry} />
</View>
);
}
// GraphQL validation or resolver errors
if (GraphQLErrors.is(error)) {
const firstError = error.graphQLErrors[0];
if (firstError?.extensions?.code === 'UNAUTHENTICATED') {
return <Text>Session expired. Please log in again.</Text>;
}
return <Text>Request failed: {firstError?.message}</Text>;
}
return <Text>Something went wrong</Text>;
}네트워크 에러와 GraphQL 에러를 구분하면 다른 복구 전략을 사용할 수 있습니다. 일시적인 장애에는 재시도, 인증 문제에는 리다이렉트를 수행합니다.
캐시 영속화를 통한 오프라인 지원
모바일 앱은 이전에 가져온 데이터에 대한 오프라인 액세스가 필요합니다. apollo3-cache-persist 라이브러리는 캐시를 AsyncStorage에 직렬화합니다.
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 프로토콜을 지원합니다.
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를 사용하여 모든 요청에 토큰을 첨부하는 것입니다.
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 확장 프로그램에는 캐시 내용과 쿼리 기록을 표시하는 내장 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 방지와 같은 모바일 특화 트레이드오프에 초점을 맞춥니다.
연습을 시작하세요!
면접 시뮬레이터와 기술 테스트로 지식을 테스트하세요.
React Native 코드의 버그를 찾을 수 있나요
실제 코드 한 조각, 숨은 버그 하나, 하루 한 번. 계정 없이 바로 도전할 수 있습니다.

작성자
Anthony Fillion-MailletSharpSkill 창업자
10년 이상 풀스택 개발을 해왔습니다. SharpSkill을 운영하며 이곳에 게시되는 모든 내용에 책임을 집니다.
2026년 8월 20일 업데이트
태그
공유
관련 기사

Flutter vs React Native 성능 비교 2026: 벤치마크와 면접 질문
Flutter 3.38과 React Native 0.82의 성능을 철저히 비교합니다. Impeller 엔진, 새로운 아키텍처, 프레임 레이트 벤치마크, 메모리 사용량, 채용 면접에서 자주 나오는 질문들을 다룹니다.

React Native 0.85 (2026): 새로운 애니메이션 백엔드, 엄격한 TypeScript API 및 면접 질문
React Native 0.85의 공유 애니메이션 백엔드, 포스트 브리지 아키텍처, Metro TLS에 대해 코드 예제와 면접 질문을 통해 심층 분석합니다.

React Native 0.84의 Hermes V1: 성능 최적화, 프리컴파일 바이트코드, 기술 면접 가이드
Hermes V1이 React Native 0.84의 기본 JavaScript 엔진으로 채택되어 바이트코드 프리컴파일, Hades 가비지 컬렉터, 메모리 최적화를 통해 획기적인 성능 향상을 제공합니다. 기술 면접에서 자주 출제되는 Hermes 내부 구조를 상세히 다룹니다.