Apollo Client란?
Apollo Client는 GraphQL API와의 통신을 간편하게 도와주는 클라이언트 라이브러리이다.
- 쿼리(Query)와 뮤테이션(Mutation) 요청: 클라이언트에서 원하는 데이터를 서버에 요청하거나 변경 작업 수행
- 캐싱: 서버에서 가져온 데이터를 클라이언트 측에 캐싱하여 네트워크 요청을 최소화
- 서버 상태 관리: 애플리케이션의 로컬 상태와 서버 상태를 통합적으로 관리
REST API와의 차이점은 이전 포스팅을 참고!
REST API와 GraphQL의 차이점 알아보기
REST API vs GraphQL API개발에서 데이터를 전달하고 처리하는 방법은 매우 중요하다.REST API는 오랜 시간 동안 표준으로 자리 잡아 왔고 GraphQL API의 등장 이후로 해당 방식을 채택하는 기업이 늘고 있
ramveloper.tistory.com
Apollo Client 기본 설정
https://www.apollographql.com/docs/react/integrations/react-native
Integrating with React Native
Search Apollo content (Cmd+K or /)
www.apollographql.com
apollo/client 설치
npm install @apollo/client graphql
Apollo Client를 설정하려면 다음과 같이 GraphQL 서버의 URL과 캐시 설정이 필요하다.
// apolloClient.ts
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: 'https://spacex-production.up.railway.app/graphql',
cache: new InMemoryCache(),
});
export default client;
ApolloProvider를 사용해 Apollo Client를 애플리케이션 전체에서 사용할 수 있도록 설정한다.
// App.tsx
import React from 'react';
import { ApolloProvider } from '@apollo/client';
import client from './src/apolloClient';
import MainScreen from './src/MainScreen';
const App = () => {
return (
<ApolloProvider client={client}>
<MainScreen />
</ApolloProvider>
);
};
export default App;
GraphQL 요청 처리
GraphQL 쿼리를 정의하려면 Apollo Client에서 제공하는 gql 태그를 사용한다.
예시 코드는 SpaceX API에서 과거 로켓 발사 정보를 가져오는 쿼리이다.
페이지네이션을 구현하기 위해 limit, offset 변수를 추가해 주었다.
(https://studio.apollographql.com/public/SpaceX-pxxbxen/variant/current/home 참고)
import { gql } from '@apollo/client';
const GET_LAUNCHES = gql`
query GetAllLaunches($limit: Int!, $offset: Int!) {
launches(limit: $limit, offset: $offset) {
id
mission_id
mission_name
launch_date_utc
launch_success
rocket {
rocket_name
rocket_type
}
launch_site {
site_name_long
}
links {
flickr_images
video_link
mission_patch
}
details
}
}
`;
React 컴포넌트에서 데이터를 가져오려면 Apollo Client의 useQuery 훅을 사용한다.
이 훅은 로딩 상태와 에러, 데이터를 반환한다.
import { useQuery } from '@apollo/client';
const useAllLaunches = (limit: number, offset: number) => {
return useQuery<LaunchesPastData>(GET_ALL_LAUNCHES, {
variables: {limit, offset},
fetchPolicy: 'cache-and-network',
});
};
fetchPolicy
'cache-and-network'로 설정하여 Apollo Client가 먼저 캐시에서 데이터를 반환하고 네트워크 요청으로 최신 데이터를 가져와 빠른 응답과 최신 데이터 보장이 가능하도록 했다.
이 외의 apollo client에서 지원되는 fetch policy의 정보는 다음과 같다.
반환 데이터 타입
Typescript를 사용하기 때문에 데이터 타입은 LaunchesPastData 인터페이스를 생성 및 정의해 반환 데이터 구조를 안전하게 관리하도록 했다.
fetchMore와 updateQuery 활용
import React, {useCallback, useEffect, useState} from 'react';
import {
FlatList,
ListRenderItem,
ListRenderItemInfo,
StyleSheet,
View,
} from 'react-native';
import {Props, ScreenName} from 'views';
import {Launch} from 'models';
import {LaunchesAPI} from 'network/LaunchesAPI';
import {Colors} from 'styles';
import LaunchItem from './LaunchItem';
import Loading from 'views/commons/Loading';
const PAGE_SIZE = 50;
const MainScreen = ({
navigation,
}: Props<ScreenName.MainScreen>): React.ReactElement => {
const [launches, setLaunches] = useState<Launch[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(false);
const {data, loading, fetchMore} = LaunchesAPI.useAllLaunches(PAGE_SIZE, 0);
const loadMoreData = useCallback(async () => {
if (loading) {
return;
}
setIsLoading(true);
try {
await fetchMore({
variables: {
offset: launches.length,
limit: PAGE_SIZE,
},
updateQuery: (prev, {fetchMoreResult}) => {
if (!fetchMoreResult) {
return prev;
}
const newLaunches = fetchMoreResult.launches.filter(
newLaunch =>
!prev.launches.some(prevLaunch => prevLaunch.id === newLaunch.id),
);
return {
launches: [...prev.launches, ...newLaunches],
};
},
});
} finally {
setIsLoading(false);
}
}, [launches.length, loading, fetchMore]);
const appendData = useCallback(() => {
if (data && data.launches) {
setLaunches(prevLaunches => [...prevLaunches, ...data.launches]);
}
}, [data]);
useEffect(() => {
appendData();
}, [appendData]);
// ...
return (
<View style={styles.container}>
<FlatList
keyExtractor={(item, index) => `${item.id}_${index}`}
style={styles.list}
data={launches}
numColumns={2}
renderItem={renderLaunchItem}
onEndReached={loadMoreData}
onEndReachedThreshold={1}
/>
{(loading || isLoading) && <Loading />}
</View>
);
};
const styles = StyleSheet.create({
// ...
});
export default MainScreen;
- fetchMore: fetchMore는 Apollo Client의 기능으로, 기존 쿼리의 변수 값을 변경하거나 추가 데이터를 요청하여 기존 결과와 병합할 수 있도록 도와준다. 위 코드에서는 새로운 데이터를 요청하고 기존 데이터에 결합하여 페이지네이션 기능에 활용했다.
- updateQuery: fetchMore가 반환한 데이터를 기존 Apollo 캐시에 저장된 데이터와 병합하는 방식을 정의한다. 위 코드에서 중복 데이터를 제거하고 새로운 데이터를 기존 데이터 뒤에 추가하는 로직이 포함되어 있다.
화면
FlatList의 numColumns를 활용하여 Grid 형식으로 리스트를 보여주었다.
최신순으로 보여주고 싶었지만 현재 SpaceX의 GraphQL API들이 관리가 이루어지지 않는지 order와 sort변수가 적용되지 않아 부득이하게 과거 날짜부터 보여지도록 설정했다.
REST API의 Over-fetching과 Under-fetching 문제가 해소되어 클라이언트 단에서 유연하게 데이터의 형태를 정의할 수 있어 활용도가 매우 높은 것 같다!😄
'개발 > React & React Native' 카테고리의 다른 글
[React Native] 리액트 네이티브 최신 버전 New Architecture 설정 끄기 (1) | 2024.11.18 |
---|---|
[React Native] react-native-skia 를 활용해 토스 복권 긁기 따라해보기 (2) | 2024.11.15 |
[React] 리액트 라인차트 (Line Chart) 컴포넌트 만들기 (1) | 2024.11.08 |
[React] 리액트 원형 프로그래스 (Circle Progress Bar) 컴포넌트 만들기/예제 (1) | 2024.11.04 |
[React Native] React Native의 New Architecture (JSI, JavaScript Interface) (5) | 2024.10.31 |