Next.js에서 Apollo Client 사용하기
Apollo Client
@apollo/client 라이브러리를 이용합니다. Apollo는 GraphQL 서버에서 필요한 정확한 데이터를 쉽게 query할 수 있는 GraplQL 클라이언트입니다. 데이터를 가져오고 변조하는 것 이외에도, Apollo는 query와 그 결과를 분석하여 클라이언트 사이드 캐시를 구성하며 이를 통해 추가 query와 mutation이 실행될 때 최신 상태로 유지되게 합니다.
Next.js에서 Apollo Client 사용하기
SSR을 할 수 있는 Next.js 프레임워크에서 Apollo Client를 사용하는 방법이 여러가지 있다. next-with-apollo라는 라이브러리가 유명하지만 아쉽게도 최신 스펙인 getServerSideProps
나 getStaticProps
를 정식으로 지원하고 있지 않다. 따라서 이번 글에서는 라이브러리 없이 구현한 예제들을 소개하고자한다.
/lib/apolloClient.ts
우리가 사용할 Apollo Client 객체를 생성하는 파일이다. Next.js에서는 SSR 과정과 CSR 과정이 나누어져있기 때문에 이 부분까지 고려해서 짜야한다.
import { useMemo } from 'react'
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client'
import merge from 'deepmerge'
let apolloClient: ApolloClient<NormalizedCacheObject> = null
function createApolloClient() {
return new ApolloClient({
ssrMode: typeof window === 'undefined',
link: new HttpLink({
uri: 'https://nextjs-graphql-with-prisma-simple.vercel.app/api', // 서버 URL (상대 주소가 아닌 절대 주소를 써야한다.)
credentials: 'same-origin', // `credentials`나 `headers`같은 추가적 fetch() 옵션
}),
cache: new InMemoryCache(),
})
}
export function initializeApollo(initialState = null) {
const _apolloClient = apolloClient ?? createApolloClient()
// Next.js에서 Apollo Client를 이용해 데이터를 가져오는 함수가 있다면, 초기 상태값이 여기에서 합쳐진다.
if (initialState) {
// 클라이언트에서의 받은 데이터인 현재 캐시 데이터를 가져온다.
const existingCache = _apolloClient.extract()
// 현재 캐시와 SSR 메소드인 getStaticProps/getServerSideProps로 부터 받은 데이터를 합친다.
const data = merge(initialState, existingCache)
// 합쳐진 데이터를 저장한다.
_apolloClient.cache.restore(data)
}
// SSG(Server Side Generation)와 SSR(Server Side Rendering)은 항상 새로운 Apollo Client를 생성한다.
if (typeof window === 'undefined') return _apolloClient
// 클라이언트의 Apollo Client는 한 번만 생성한다.
if (!apolloClient) apolloClient = _apolloClient
return _apolloClient
}
export function useApollo(initialState) {
const store = useMemo(() => initializeApollo(initialState), [initialState])
return store
}
/pages/_app.js
우리가 Apollo Client를 사용하고자 하는 최상위 페이지(/page/~)에 있는 SSR 메소드(getStaticProps/getServerSideProps
) 안의 내용을 넣어줍니다. 해당 Page 컴포넌트가 로드될 때, 서버의 SSR 메소드에서 본인 혹은 자식 컴포넌트들에서 필요한 qurey
를 가져옵니다. qurey
의 값을 받았을 때, Apollo Client의 store는 완전히 초기화됩니다. 결과적으로 브라우져에서 가져온 데이터와 hydrate Apollo를 가진 초기 HTML를 제공할 수 있게 됩니다.
import { ApolloProvider } from '@apollo/client'
import { useApollo } from '../lib/apolloClient'
export default function App({ Component, pageProps }) {
const apolloClient = useApollo(pageProps.initialApolloState)
return (
<ApolloProvider client={apolloClient}>
<Component {...pageProps} />
</ApolloProvider>
)
}
/pages/index.tsx
페이지 컴포넌트의 예시로 컴포넌트 내의 Query 메소드와 SSR 메소드를 주의깊게 확인해야합니다.
import {
ALL_POSTS_QUERY,
} from '@/~'
import {
allPostsQueryVars,
} from '@/~'
import { initializeApollo } from '../lib/apolloClient'
const Home = () => (
...
// SSR에서 사용한 query와 같은 query(ALL_POSTS_QUERY)를 사용
const { loading, error, data } = useQuery(ALL_POSTS_QUERY, {
variables: allPostsQueryVars,
})
)
export const getServerSideProps: GetServerSideProps<{}, {}> = async (ctx) => {
/*
1. 투표 등 실시간성 데이터가 계속 업데이트 되어야하는 경우 getStaticProps와 revalidate를 이용
2. 일반적인 경우 getServerSideProps를 이용
*/
// browser단의 context(headers)를 SSR에 넘기는 과정
const apolloClient = initializeApollo(null, ctx)
await apolloClient.query({
query: ALL_POSTS_QUERY,
variables: allPostsQueryVars,
})
return {
props: {
initialApolloState: apolloClient.cache.extract(),
},
}
}
export default Home
'Web > Next.js' 카테고리의 다른 글
React 개발이 이렇게 쉬웠나? (Feat. Next.js) (1) | 2020.12.03 |
---|---|
Next.js의 기본 구조 정리 (0) | 2020.11.22 |
create-react-app 와 next.js 어떤 걸 써야 할까? (0) | 2020.09.15 |
[Next.js] 공유된 컴포넌트 사용하기 (0) | 2020.01.21 |
[Next.js] 페이지 간 이동하기 (0) | 2020.01.20 |
댓글
이 글 공유하기
다른 글
-
React 개발이 이렇게 쉬웠나? (Feat. Next.js)
React 개발이 이렇게 쉬웠나? (Feat. Next.js)
2020.12.03 -
Next.js의 기본 구조 정리
Next.js의 기본 구조 정리
2020.11.22 -
create-react-app 와 next.js 어떤 걸 써야 할까?
create-react-app 와 next.js 어떤 걸 써야 할까?
2020.09.15 -
[Next.js] 공유된 컴포넌트 사용하기
[Next.js] 공유된 컴포넌트 사용하기
2020.01.21