GraphQL vs REST, 보이스루 API를 고민하다 ③

보이스루 CTO
2022-09-26

들어가며

안녕하세요. 보이스루에서 CTO를 맡고 있는 윤승현입니다.
지난 2편에서는 Data fetching 관점에서 GraphQL과 REST API의 큰 차이를 확인하며 GraphQL API 기반 개발 플로우를 소개했습니다.
또, 결제 Admin system 개발 상황을 가정해 기존 REST API 방법에서 고민되는 지점을 설명하며 
보이스루가 얻은 GraphQL 적용 장점과 개발 프로세스 효율화를 정리했습니다.

‘미디어 로컬라이제이션’이라는 보이스루의 궁극적 목표에 기여하는 테크팀의 API 이야기, 드디어 마지막 세 번째 시간입니다.

Spring Boot GraphQL 적용


보이스루의 BE 개발 스택은 Kotlin으로 Spring Boot를 사용하고 있고, FE는 TypeScript로 React를 사용하고 있습니다.
저희는 Spring Boot에 GraphQL을 도입하는 과정에서 Spring 프로젝트인 ‘Spring GraphQL와
Expedia의 GraphQL Kotlin, Netflix의 Spring DGS’ 이렇게 3가지 Framework를 레퍼런스로 고민했는데요.

1. Spring GraphQL


  • Spring 정식 지원 
  • Schema, Mapping 코드 직접 작성

처음 고민했던 Framework는 Spring GraphQL이었습니다.
Spring에서 정식으로 개발하고 있던 framework이기에 사용을 고민했습니다.
Spring GraphQL은 GraphQL schema 파일과 schema와 연결되는 코드를 모두 작성해줘야 합니다.


사용 과정에서 연결되는 코드는 작성해야 하고, Schema는 변경을 누락시켜 반대로 해야 하는 케이스가 발생해 사용하기 까다로웠습니다.

2. Netflix Spring DGSL

Netflix에서 domain들을 엮어 GraphQL을 제공하기 위해 만든 Framework입니다. 

GraphQL이 커지면서 필요한 federation 기능을 제공하고 있고, Netflix에서 관리해 확장성도 있다고 판단했습니다.


Spring GraphQL을 사용하면서 schema와 구현하기 위한 연결 코드를 모두 관리하는 일이 무척 까다로웠기에 관리 주체를 하나로 설정하고 싶었습니다.


Spring DGS는 Schema first를 따르고 있습니다. Schema를 생성하고 그에 따른 코드를 Generate하는 과정으로 개발이 진행됩니다. 

최종적으로 전달되는 Interface인 Schema를 설정하고 구현하는 일은 Interface First 디자인의 성격을 띠고 있습니다.

3. Kotlin GraphQL


  • Expedia 개발
  • Code First


Expedia에서 Open source로 운영하고 있는 Framework입니다. Netflix의 Spring DGS와 다르게 Code First를 따르고 있습니다.
Code로 원하는 schema를 구현하고, 이에 해당하는 Schema를 생성하여 GraphQL을 운영하는 형식입니다.


Schema First의 경우 schema를 이용하여 generate된 코드를 가지고 개발을 진행하게 됩니다.
실제 운영에 들어가면 generate된 코드로는 한계가 발생하고 generate된 코드를 변경하게 됩니다.


결국 schema와 코드를 각각 관리하는 이슈가 발생하는데요.
Spring GraphQL을 사용하면서 각각 관리하는 이슈는 또 잦은 실수를 낳았습니다.
Schema를 변경하고 구현한 Code를 변경하지 않아 실수가 발생하고 반대로도 이슈가 많이 생겼습니다.


저희는 코드를 기점으로 schema를 관리하기로 했습니다.
개발자가 실제로 GraphQL을 구현하는 code를 작성하면서 어떤 형태로 schema가 전달될지 예상할 수 있기에,
코드 개발과 schema 개발을 별도로 하는 데에서 오는 실수 포인트를 줄일 수 있었습니다.

4. Typescript Apollo Client Generation

FE에서는 TypeScript로 React를 사용하여 개발하고 있는데요. 
FE에서 GraphQL을 사용할 때 불편한 점은 GraphQL로 정의한 Graph가 Nested 구조로 Return된다는 점입니다.


REST로 페이지마다 API를 작성하는 경우, Graph의 성격을 띠는 데이터를 flatten하여 내려보낼 수 있습니다. 
FE는 페이지마다 API에 해당되는 Interface를 작성하여 사용하면 되는데요. 
GraphQL의 Graph가 복잡하면 Nested 구조 또한 복잡해집니다.


export const GQL_RELATE_TASK = gql`
 query relateTask($uuid: UUID!) {
   taskByUuid(taskUuid: $uuid) {
     work {
       operation {
         job {
           project {
             jobs {
               uuid
               name
             }
           }
         }
       }
     }
   }
 }
`;

gql.example


export type relateJob = {
   readonly taskByUuid: {
     readonly work: {
       readonly operation: {
         readonly job: {
           readonly project: {
             readonly jobs: ReadonlyArray<{
               readonly uuid: string;
               readonly name: string;
             }>;
           }
       };
     };
   };
 };

nested structure.example


export type relateJobs = {
   readonly jobs: ReadonlyArray<{
       readonly uuid: string;
       readonly name: string;
   }>;
}

REST api.example


GraphQL을 사용할 때마다 대응하는 Nested Interface를 작성하고 해당 타입을 명시하는 일은 어려운 일입니다. 
초기 GraphQL를 도입할 때 장벽이 되었던 부분도 이 부분이었습니다.


GraphQL를 개발하는 Apollo는 많은 Tool을 개발하고 있습니다. 그중 하나가 Apollo Client입니다.


Apollo Client는 GraphQL을 사용하는 client단의 프로그램입니다. 
Apollo의 중요 기능 중 하나는 정의된 GQL을 인식하여 해당되는 interface를 자동으로 만들어주는 부분인데요. 
Apollo Client의 Code generation을 이용하면 위 복잡한 Nested Interface를 자동으로 만들 수 있습니다.


이에 따라 FE의 개발 플로우는 ‘페이지 분석 → GQL 작성 → Code generate → 사용’ 플로우로 진행됩니다.

GraphQL 정리

보이스루의 Server Architecture는 DDD와 MSA 패턴을 채택하고 있습니다.


DDD와 MSA구조는 서비스의 안정적인 확장과 서비스 운용의 안정성을 가져왔습니다. 
다만 도메인별로 서비스가 분리되다 보니 통합 정보 쿼리 개발에 이슈가 발생했습니다.


DDD를 통해 도메인간 관심사를 분리하는 장점이 통합 쿼리를 지원하면서 줄어들었는데요.


CQRS 패턴을 적용해 조회용 도메인이라는 도메인을 만드는 것도 하나의 방법입니다. 
하지만 늘어나는 요구 사항에 맞춰 지속해서 조회용 도메인을 추가 개발해야 하고, 
조회용 도메인의 근간이 되는 도메인들과의 싱크를 맞추는 작업도 지속해야 합니다.


보이스루는 이번에 GraphQL을 사용하면서 이 문제를 많이 해결하고 배웠습니다. 
도메인 간 연결을 GraphQL로 상당수 구현해낼 수 있었습니다.


DDD와 MSA를 사용하는 회사라면 GraphQL 도입을 고민해보면 좋을 것 같습니다.


더 읽어보면 좋을 자료들

https://velog.io/%40heoseungyeon/MSA-%EC%99%80-DDD-2-DDD%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-MSA 

https://martinfowler.com/articles/microservice-trade-offs.html 

https://youtu.be/mJMzV6GCmPw 

https://youtu.be/BnS6343GTkY

Graphql Vs Rest
Domain Driven Design
Spring
Microservice Architecture
Tech