1️⃣ GraphQL이란?
GraphQL은 Facebook이 개발한 데이터 쿼리 언어이자 API를 설계하는 방식입니다. 클라이언트가 필요한 데이터만 요청하고 받을 수 있게 설계되었는 클라이언트 중심 데이터 요청 프로토콜을 일컫습니다. REST API의 대안으로 많이 사용됩니다.
GraphQL을 사용함으로써 얻을 수 있는 이점으로는,
- 단일 엔드포인트사용으로 유연한 처리방식
- Over-Fetching과 Under-Fetching 방지를 통한 UX향상
- 데이터 구조를 정의, 즉 스키마를 우선 정의하여 Server 엔지니어와 Client 엔지니어의 DX향상
등이 있습니다.
개인적으로 Relay에 GraphQL에 대한 설명이 잘 나와있다고 생각합니다.
추가적으로, GraphQL의 단점에 대한 아티클 공유
6년 만에 GraphQL을 그만둔 이유 | GeekNews
2️⃣ GraphQL을 사용하게 된 이유
이전 프로젝트에서는 REST API를 사용하였고, API의 잦은 변경으로 인하여 발생하는 비용때문에 REST API의 한계를 느끼고 있었습니다.
이전 팀의 구성은 서버 백엔드 엔지니어 5명과 클라이언트 프론트엔드 엔지니어 3명으로 구성된 팀이었습니다.
이전 프로젝트는 디자이너, PM등 다른 직군이 없는 엔지니어로만 이루어진 팀이었고, 이에따라 다양한 업무를 할당받으며 팀의 부족한 부분을 채우기 위해 노력하였습니다.
이로인해 우리 팀은 ‘엔지니어링 관점’에서 ‘목적 조직’보다는 ‘기능 조직’으로서 협업하기 시작하였습니다. 너무나 많은 기능을 감당하기 위해서 였습니다. 클라이언트와 서버의 활발한 교류가 이루어지지 않았습니다.
이러한 사일로 현상을 최대한 극복하기 위해서 우리팀은 API명세를 확실히 정의하고, 공유하였습니다. 하지만 프로젝트가 커짐에 따라 단순히 명세를 정의하는 것으로 모든 문제를 해결할 순 없었습니다.
특히나 프로젝트의 복잡성이 커지면서, 단순한 API 명세의 변경은 다른 사이드 이펙트를 발생시켰습니다. 이에 따라 우리는 REST API의 Under-Fetching, Over-Fetching을 지켜볼 수 밖에 없었구요.
이런 REST API의 문제를 효과적으로 해결해줄 수 있는 프로토콜 GraphQL의 필요성을 느꼈고, 경험해보고자 하였습니다.
3️⃣ Supabase를 사용하게 된 이유
이번 사이드 프로젝트는 프론트엔드 엔지니어 2명으로 구성되어있습니다. 백엔드의 부재를 최소화 할 수 있는 방식으로 많은 기능을 제공하는 백엔드 서비스를 사용할 필요성을 느꼈습니다.
그 중 Supabase를 고른 이유는 관계형 데이터 베이스(RDB)인 PostgreSQL을 사용하고, GraphQL을 지원한다는 점이었습니다.
RDB와 GraphQL의 상호 관계는 장점이 있습니다. DB에서의 테이블과 같은 표현이 결국 GraphQL의 엔티티로 그대로 매칭되기 때문입니다. SQL구문을 하나도 알지 못하지만, Supabase를 통해 편하게 테이블을 구성하고 이를 그대로 GraphQL의 Typescript Type으로 가져올 수 있었습니다.
특히 Supabase에서는 pg-graphql을 이용하여 PostgreSQL을 GraphQL로 확장시켜 제공합니다. 기존 Supabase 프로젝트의 기본 URL에 /graphql/v1 을 붙인 endPoint를 사용하여 쉽게 GraphQL을 사용할 수 있습니다.
4️⃣ Apollo를 사용하게 된 이유
결론적으로 낮은 러닝커브 때문이었습니다.
우선 GraphQL을 사용하는데 있어서 널리 알려진 Client Fetch 라이브러리로 Apollo와 Relay가 있습니다. 이 두 도구를 표면적으로 살펴보았을 때, 느낀점은 다음과 같습니다.
- Apollo는 사용자 친화적입니다. 기존 Tanstack Query의 사용법과 같아 러닝커브가 적습니다.
- Relay는 초기설정이 복잡하고 러닝커브가 높습니다. 하지만 복잡한 데이터 요구사항을 모두 수행할 수 있는 강력한 도구입니다.
저희 팀은 GraphQL을 처음 사용하고 학습하기 때문에 러닝커브가 낮은 Apollo를 사용하기로 하였습니다. 프로젝트를 설계해 나가는데 발생하는 새로운 문제 해결 수단으로 Relay를 선택해보면 좋겠네요.
5️⃣ Apollo Client와 Supabase 설정하기
Supabase는 위 Project Dashboard를 통해 쉽게 테이블을 생성하고 데이터를 가공할 수 있습니다.
Supabase의 Project를 만들었다면, 코드를 작성할 준비가 되었습니다.
Supabase는 Apollo와 함께 사용할 수 있는 설명을 제공합니다. ⤵️
☑️ Codegen.ts 설정
우선 codegen을 설치합니다.
npm i graphql
npm i -D typescript @graphql-codegen/cli
저희 프로젝트에서는 Apollo Server를 사용하지 않습니다. 따라서 위 설명에 적혀있는 GraphQL endPoint를 다르게 설정해주었습니다.
import type { CodegenConfig } from "@graphql-codegen/cli";
import { addTypenameSelectionDocumentTransform } from "@graphql-codegen/client-preset";
import dotenv from "dotenv";
dotenv.config();
const GRAPHQL_SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL + "/graphql/v1";
const SUPABASE_KEY = import.meta.env.VITE_SUPABASE_KEY;
const config: CodegenConfig = {
schema: [
{
[GRAPHQL_SUPABASE_URL]: {
headers: {
apikey: SUPABASE_KEY,
Authorization: `Bearer ${SUPABASE_KEY}`,
},
},
},
], // Using the local endpoint, update if needed
documents: "src/**/*.tsx",
overwrite: true,
ignoreNoDocuments: true,
generates: {
"src/gql/": {
preset: "client",
documentTransforms: [addTypenameSelectionDocumentTransform],
plugins: [],
config: {
scalars: {
UUID: "string",
Date: "string",
Time: "string",
Datetime: "string",
JSON: "string",
BigInt: "string",
BigFloat: "string",
Opaque: "any",
},
},
},
},
};
export default config;
VITE 환경의 프로젝트였기에, 환경변수 Prefix로 VITE_를 사용하였습니다. 또한 dotenv를 통해 config파일에서도 환경변수를 읽을 수 있게끔 하였습니다.
추가적으로 Supabase의 endPoint에 접근할 수 있게끔 Auth 설정을 함께 해주었습니다.
// package.json
"script" :{
//...
"codegen": "graphql-codegen --config codegen.ts"
script를 추가후 npm run codegen 을 통해 Supabase의 GraphQL Type을 generate 할 수 있습니다.
생성되는 폴더 구조는 다음과 같습니다.
src/gql/
├── fragment-masking.ts
├── gql.ts
├── graphql.ts
└── index.ts
☑️ Apollo Client 설정
npm install @apollo/client graphql
위 명령어로 Apollo Client 설치 후, Apollo Client 설정을 해줍니다.
이 역시 With Apollo에서 잘 확인할 수 있지만, 설정을 조금 바꿔줍니다.
기본적으로 Supabase Clients는 불필요합니다. Apollo Server를 통해 resolver를 변경해주지 않을 것이기 때문입니다.
기본 설정에서 몇가지를 변경해줍니다.
import { ApolloClient, InMemoryCache, createHttpLink, defaultDataIdFromObject } from "@apollo/client/index.js";
import { setContext } from "@apollo/client/link/context";
import { relayStylePagination } from "@apollo/client/utilities";
~~import supabase from './supabase'~~ // 삭제해줍니다.
//...
const httpLink = createHttpLink({
// URL을 Supabase의 Graphql endPoint로 변경
uri: `${import.meta.env.VITE_SUPABASE_URL}/graphql/v1`,
});
const authLink = setContext(async (_, { headers }) => {
// Auth 설정을 Supabase의 Key값으로 변경
const SUPABASE_KEY = import.meta.env.VITE_SUPABASE_KEY;
return {
headers: {
...headers,
apikey: SUPABASE_KEY,
Authorization: `Bearer ${SUPABASE_KEY}`,
},
};
});
const apolloClient = new ApolloClient({
link: authLink.concat(httpLink),
cache,
});
export default apolloClient;
마지막으로 Apollo Client로 프로젝트를 감싸줍시다.
import { ApolloProvider } from "@apollo/client/index.js";
import apolloClient from "src/apollo/client";
//...
export default function App() {
return (
<ApolloProvider client={apolloClient}>
<Outlet />
</ApolloProvider>
);
}
이렇게 프로젝트를 위한 Apollo 설정과 Supabase 설정을 마쳤습니다. 이제 본격적으로 Apollo Client 코드를 작성해보도록 하겠습니다.
이어서 Apollo Client를 이용하여 Supabase 데이터를 CRUD 로직을 만들어보겠습니다.
'Front End > React' 카테고리의 다른 글
Paint 이전 Macro Task가 실행될 가능성과 React의 useEffect (1) | 2024.11.28 |
---|---|
[Apollo Basic] Apollo Client를 이용하여 Supabase GraphQL 요청 보내기 (2) | 2024.11.27 |
크루루 서비스의 PopOverMenu 컴포넌트 개선기(1), createPortal과 합성 이벤트 문제 해결 (1) | 2024.11.23 |
[React] useLayoutEffect와 useEffect (0) | 2024.11.22 |
Tanstack Query의 useQuery 캐싱 매커니즘 분석하기 (0) | 2024.11.21 |