Front-end) 프로젝트 아토믹 디자인 패턴 적용
이전 포스트 Front-end) 프로젝트 설계 및 기초 세팅쇼핑몰 기능 구상1. 메인 화면HeaderLogo카테고리캐릭터만화게임개인커스텀문의간편 메뉴북마크찜장바구니로그인메인 화면 상품 목록진행중인
dmltn3426.tistory.com
✅ Intro
- GraphQL을 간단하게 공부하고 이것의 장단점 대해 간략하게 설명해보자.
- GraphQL을 적용하고 설계해 보자.
- 커스텀 Hook useGraphQL 만들어 보자
- GraphQL을 왜 쇼핑몰 프로젝트에 적용할 생각을 하였는지.
🔍 GrapQL이란 무엇인가?
기존의 Rest-API가 가지고 있던 한계점을 극복하기 위해 개발된 쿼리 언어이다.
- Over-Fething 이슈 해결
- REST API를 이용하면 백엔드(BE)가 URL의 각 리소스에 사용할 수있는 데이터를 정의하는 반면, 프론트엔드(FE)는 리소스의 일부만 필요하더라도 항상 리소스의 모든 정보를 요청해야 한다.
- GraphQL을 사용하면 필요한 필드만 명시적으로 요청할 수 있기 때문에 해당 문제를 근본적으로 해결한다고 할 수 있다.
- Under-Fetching 이슈 해결
- 일반적으로 REST API에서는 특정 데이터를 사용하려면 여러 엔드포인트에 액세스하여 데이터를 받아와야 한다.
- 첫번째 - /student/<id> 엔드 포인트
두번째 - 학생의 정보를 가져오기 위해서 /student/<id>/info 엔드 포인트
세번째 - 학생의 친구 목록을 가져오기 위한 /student/<id>/friends 엔드 포인트
- 첫번째 - /student/<id> 엔드 포인트
- GraphQL은 단일 쿼리에 구체적인 데이터 요구 사항을 적어 GraphQL 서버에 보내기만 하면 된다.
- 일반적으로 REST API에서는 특정 데이터를 사용하려면 여러 엔드포인트에 액세스하여 데이터를 받아와야 한다.
React) Apollo Client GraphQL 사용해보기 - 1
※ GraphQL을 들어가기 전에GraphQl이 무엇인지와 이의 필요성에 대해 공부해 보고, React와 Apollo Client를 함께 사용 하는 방법을 Hook기반의 케이스로 공부하고 실습해봤던 내용을 소개하려고 한다. 먼
dmltn3426.tistory.com
❓쇼핑몰 프로젝트에서 GraphQL를 써보았을 때 장점과 단점은 무엇이였는지?
아직 API 프로토콜 현황을 보면 아직까지도 REST를 가장 널리 사용하는 것을 확인 할 수 있는거 처럼 GraphQL을 프로젝트에 적용시키는 것은 처음이여서 적용시키는 과정에서 Back/Front End 둘 다 어려운 과정을 겪었다. 쇼핑몰 프로젝트에 GraphQL을 적용하면서 Front-End적으로 장점과 단점을 확인 할 수도 있었다.
장점
- Apollo client를 설정하면 하나의 EndPoint를 가지고 있어서 유지보수에 용이했었다.
- 쇼핑몰에서 카테고리를 원하면 쿼리문을 통해서 내가 원하는 Response Parameter를 지정하여 가져올 수 있었다.
- 원하는 데이터를 가져올 수 있어서 서버를 여러번 호출해서 리소스를 낭비 시키지 않아도 된다.
- 쇼핑몰 아이템 데이터를 가져올 때에는 데이터를 한번에 반환하는 방식으로 원하는 데이터를 최적화하기가 용이하다.
단점
- GraphQL은 하나의 URL로 처리하기에, HTTP에서 제공하는 캐싱 전략을 그대로 사용하는 것이 불가능해서 장점이자 단점이라고도 생각한다.
- 파일 업로드를 처리할 때 문제점이 발생하였다.
어려웠던 점
위에서 장점과 단점을 간단하게 프로젝트를 진행하면서 겪었던 일을 나열해 보았다. 그 중에서도 사용자의 시점에서 생각했을 때 리뷰 작성을 하게 된다면 리뷰 등록은 실시간으로 중간 저장이 필요하다고 생각을 하게 되었는데 그렇게 되면 GraphQL을 호출하여 삭제, (임시)등록 혹은 수정, (임시)등록을 고려해 볼 필요가 있다고 생각하게 되었고 이 부분에서 Back-End와도 많은 상의를 나눠보고 결론을 지어 이중 Mutation을 이용하여 동시에 호출하여 임시 저장버튼을 만드는것으로 합의하여 설계하였다. 이 부분에서는 아직 미숙한 부분이 보이기도 했지만 타협점을 찾고 잘 진행했다.
결론
이러듯이 GraphQL은 Front-End가 장점만 본다면 매우 매력적으로 보일 지 모르겠지만 사용자의 입장을 고려하여서 설계를 진행하게 되면 고려해야 될게 많아서 아직까지는 REST-API를 선호하는 것이 아닌가 싶다.
쇼핑몰 GraphQL과 Apollo Client 적용

- api/gql 폴더 안에 mutation를 관리할 폴더인 mutations와 query를 관리할 폴더인 queries를 만들고 client.ts를 통해서 GraphQL API와 연결하는 http link 생성하고 http link와 InMemoryCache의 새 인스턴스를 전달하여 Apollo Client를 인스턴스화를 해주는 파일을 만들어주는 작업을 밑에 코드와 같이 해준다.
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client';
// Apollo Client 인스턴스를 GraphQL API와 연결하는 http Link생성한다.
const httpLink = createHttpLink({
uri: '{GraphQL 서버}',
});
// httpLink 및 InMemoryCache의 새 인스턴스를 전달하여 ApolloClient를 인스턴스화한다.
export const client = new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
});
- src/index.tsx에 코드에서 기존에 App을 호출하는 부분에 ApolloProvider를 감싸주고 Apollo Client를 인스턴스한 client를 props로 설정해준다.
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<StyledEngineProvider injectFirst>
<ApolloProvider client={client}>
<CookiesProvider>
<BrowserRouter>
<RecoilRoot>
<App />
</RecoilRoot>
</BrowserRouter>
</CookiesProvider>
</ApolloProvider>
</StyledEngineProvider>,
);
이렇게 설계 단계를 지난 후에 Apollo Client를 Front에 적용시키면서 느꼈던 점을 간단히 설명해 보자.
개발을 진행하면서 Apollo Client를 적용시키면서 느꼈던 점
※ Apollo Client를 공부하고 프로젝트에 직접 적용시키면서 Apollo Client Caching의 작동 원리를 공식문서와 함께 중점적으로 파악해 보았고 아래 내용을 보았다.
캐시를 Apollo Client에 보내면, Apollo Client가 쿼리 응답을 받을 때 마다, 자체 캐시 안에서 분리된 별개의 엔트리에 자동적으로 데이터를 저장한다.
처음으로 쿼리할 때 데이터를 가져오는 흐름은 다음과 같다.

동일한 객체에 대해 실행될 때마다 흐름은 다음과 같다.

출처 : Apollo Client 공식 문서
GraphQL Custom Hook으로 만들어 보자
이렇게 유지보수나 최적화적인면에서 GraphQL을 이용하는 것이 유리해 보여 사용했다. 하지만 GraphQL에서 useQuery나 useMutation과 같음 Hook을 이용해서 사용할 때 마다 Loading이나 Error처리를 계속해서 해줘야하는데 이것을 한번에 처리하여 재활용성을 높이도록하고 나중에 유지보수를 하더라도 파일 하나만 수정하여 유지보수의 효율성을 높이도록 하고 싶어서 Custom Hook인 useGraphQL.tsx을 만들었다.
import { useEffect } from 'react';
import { DocumentNode } from 'graphql/language';
import { LazyQueryHookOptions, MutationHookOptions, useLazyQuery, useMutation } from '@apollo/client';
import { useRecoilState } from 'recoil';
import loadingAtom from '@recoil/atoms/loadingAtom';
interface graphQLProps<T> {
query: DocumentNode;
request?: T;
type: 'query' | 'mutation';
option?: MutationHookOptions | LazyQueryHookOptions | T;
}
/*
*
* query는 GraphQL의 쿼리를 받는 props이다.
* request는 필터링 혹은 variables에 필요한 request 값을 받는 값을 담아주는 props이다.
* type은 mutation을 호출하는지 query를 호출하는지 타입을 정해주기 위한 props이다.
* option은 headers에 들어갈 데이터들의 option을 담는 props이다.
*/
const useGraphQL = <T,>({ query, request, type, option }: graphQLProps<T>) => {
const [loadingGlobal, setLoading] = useRecoilState(loadingAtom);
const selectType = () => {
if (type === 'mutation') {
return useMutation(query, {
context: {
headers: { ...option },
fetchOptions: {
credentials: 'include',
},
},
variables: {
request: { ...request },
},
});
}
return useLazyQuery(query, {
context: {
headers: { ...option },
},
variables: {
request: { ...request },
},
});
};
const [refetch, { data, loading, error }] = selectType();
const modifyLoading = (state: boolean) => {
if (state) setLoading(loadingGlobal + 1);
else setLoading(loadingGlobal - 1 < 0 ? 0 : loadingGlobal - 1);
};
useEffect(() => {
if (loading) modifyLoading(true);
else modifyLoading(false);
}, [loading]);
useEffect(() => {
if (error) alert(error.message);
});
return { data, refetch };
};
export default useGraphQL;
selectType을 통해서 값을 받으면 refetch와 data, loading, error이 갱신되고 loadingGlobal이라는 recoil을 통해서 전역으로 loading의 상태를 관리해준다. 그리고 useEffect를 통해서 loading값과 error 값을 변경한다.
※ 그렇다면 recoil을 통해 loading을 전역으로 관리하기만 하고 화면을 나타내는 부분은 어떻게 하면 좋을까?
그 부분은 Loading페이지를 만들어서 전역으로 관리하는 loading 상태 값을 통해 Layout 페이지에 적용을 시켜서 loading 중이면 화면을 나타내주면 해결된다.
✅결론
- GraphQL은 하나의 URL로 처리하기에 유지보수 하기에는 용이하지만, HTTP에서 제공하는 캐싱 전략을 그대로 사용하는 것이 불가능해서 장점이자 단점을 가지고 있기도 하다.
- 아직 REST처럼 대중적이지 않고 설계 단계에서 어려움이 있을 수 있다.
- 데이터 리소스 낭비가 적다.
- 프로젝트에서 useGraphQL이라는 커스텀 Hook을 만들어서 유지보수에 더욱 편의성을 높였다.
참고
Caching in Apollo Client
Search Apollo content (Cmd+K or /)
www.apollographql.com
출처: Api 프로토콜 빈도 현황
2023년 API 프로토콜 현황 | GeekNews
Postman이 4만명의 개발자 대상 조사를 통해 정리한 API 프로토콜 트렌드와 장/단점REST, WebHooks, GraphQL, SOAP, WebSocket, gRPC 등REST아직 가장 널리 사용. 지난 2년간 92% 에서 86%로 감소단순성, 확장성 및 웹
news.hada.io