next.js와 Apollo GraphQL을 이용한 웹 서비스 개발하기(3)

Image for post

지난 시간 index.js페이지 외에 추가적으로 유저 데이터 리스트를 보여주는 users/index.js 페이지와 유저 하나의 데이터를 보여주는 users/[id].js페이지를 개발해보았습니다.

이번 시간에는 별도의 추가기능을 개발하기보다는 서버에 graphql 요청을 보내는 다양한 페이지의 apollo-client를 별도의 파일로 분리하고 React hooks를 사용해 일관되게 관리하도록 해보겠습니다.

Github Source Code

next.js 공식 github repository에는 다양한 경우에 대한 examples code를 제공하고 있습니다. 이번에 참고할 코드는 with-apollo 예제입니다.

먼저 ApolloClient 인스턴스를 새로 생성해주는 apolloClient.js 파일을 lib directory 하위에 생성합니다.

ApolloClient 객체를 만들어주는 createApolloClient 함수와

여기에 추가적인 initialState를 설정한 뒤 Singleton pattern으로 방어코드를 포함하여 생성된 apolloClient 단일 객체를 반환해주는 initializeApollo 함수,

initialState에 따라 apolloClient를 초기화해주는 useApollo hook

세가지 함수를 생성하고, 각 페이지에서는 이 함수들을 이용해 동일한 ApolloClient 인스턴스를 사용하도록 합니다.

추가적으로 apollo-client API를 사용함으로써 간편하게 캐시와 credentials 인증 스킴도 설정이 가능합니다.

이제 users/index.js파일을 수정해보겠습니다.

// users/index.js...export defualt function Users() {
...
useEffect( () => { ( async () => { const response = await fetch('http://localhost:3000/api/graphql', { method: 'POST', headers: { 'Content-type': 'applocation/json', }, body: JSON.stringify({ query: '{ users { id name color } }' }), }); const { data } = await response.json(); console.log( data ); setUsers( data.users ); } )(); }, []);...
}

기존에 작성되어던 코드는 apollo-client를 사용하지 않고 fetch API를 이용해 POST method로 쿼리를 작성해서 사용하고 있었습니다.

이제 apolloClient를 사용하기 위해 lib/apolloClient.js 파일을 가져와 useApollo hook을 사용해보겠습니다.

// users/index.js
import initializeApollo from '../../lib/apolloClient';
import { USERS_LIST } from '../../gql/user';
...export defualt function Users() {
const apolloClient = useApollo();
const [ users, setUsers ] = useState([]); ... useEffect( () => { ( async () => { const { loading, error, data } = await apolloClient.query({ query: USERS_LIST, }); console.log( data ); setUsers( data.users ); } )(); }, []);...
}

functional component 내부에서 useApollo를 통해 apolloClient를 생성하고 생성된 apolloClient로 USERS_LIST query를 요청했습니다.

이제 매 페이지마다 새로운 apolloClient 객체를 생성하지 않아도 되고 fetch API보다 훨씬 간편하게 graphql 통신을 할 수 있게 되었습니다.

‘/gql/user.js’파일에서 import해온 USERS_LIST query의 경우

// gql/user.jsimport gql from 'graphql-tag';export const USERS_LIST = gql`  query users {    users {      id      name      color    }  }`;

다음과 같이 ‘graphql-tag’를 사용해 작성했고 재사용이 가능하도록 외부로 추출하였습니다. 유사한 방식으로 특정 user의 디테일 정보를 가져오는 ‘USER_DETAIL’ query도 작성합니다.

// gql/user.js...
export const USER_DETAIL = gql`
query user( $userId: String! ) { user( id: $userId ) { id name color } }`;

이제 작성된 USER_DETAIL query와 useApollo hook을 사용해 users/[id].js파일도 리팩토링해보겠습니다. users/index.js파일과 방식은 같으며 기존과 같이 특정 user를 식별할 userId값만 parameter로 함께 넘겨줍니다.

// users/[id].js...import { useApollo } from '../../lib/apolloClient';import { USER_DETAIL } from '../../gql/user';function UserDetail() {
const apolloClient = useApollo();
... ( async function () { const { query: { id: userId } } = router; if ( !userId ) { return; } const { loading, error, data } = await apolloClient.query({ query: USER_DETAIL, variables: { userId }, }); console.log('loading:', loading); console.log('error:', error); console.log('data:', data); setUser( data.user );})();...

원래 apollo-client API를 사용해서 새로운 ApolloClient 객체를 생성하던 코드를 제거하고 대신 UserDetail function 내부에 useApollo를 이용해 apolloClient를 전달받습니다. 이후에는 USER_DETAIL query를 이용해 user data를 요청했습니다.

이렇게 useApollo hook을 만들어 각 페이지에 적용시켜보고 향후 중복될 가능성이 큰 query들을 가독성있게 네이밍하여 외부로 추출해보았습니다. 아직은 두 종류의 페이지와 두 종류의 query밖에 없지만 향후 기능과 페이지를 확장하게 되면 더이상 새로운 apolloClient 객체를 생성하거나 기존에 작성했던 query를 다시 작성하지 않아도 됩니다. 🙂

Github Source Code

woohyun

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store