BOLD9

2022.03.02

by Aiden

GraphQL schema design

:Considerations for front-end

Index

Problems of schema based database

:Considerations for front-end

Circular Imports Hell

두 타입이 서로 참조 하고 있을 때 잠재적인 문제가 발생

Circular Imports Hell

Why?

 악의적인 공격이 들어 올 때 무한 순회 필드에 대해 치명적인 장애가 발생

Circular Imports Hell

우리 서비스 사례

Circular Imports Hell

:Tip

순회 참조를 피할 수 있으면 좋지만 그럴 수 없는 케이스라면 graphql-depth-limit 사용하는 것을 추천한다. graphql-depth-limit 는 복잡한 쿼리 사용 시 필드의 깊이를 제한 할 수 있는 툴이다.

Deeply nested object

재고 이동 테이블 회사/브랜드 컬럼을 가져 왔다. 재고 이동을 한 회사 이름이 필요 할 뿐인데 6 depth 데이터를 참조해야 한다.

Deeply nested object

불필요한 쿼리 실행

GraphQL 리졸버는 루트 필드부터 쿼리의 마지막 depth의 필드까지 연쇄적으로 호출된다. 따라서 ORM을 사용하여 데이터를 쿼리하는 우리 케이스 같은 경우, DB 쿼리 실행 또한 불필요하게 많이 될 것이다.

의도되지 않은 자유

앞서 본 예시 코드에서 회사 이름을 얻기 위해 깊은 데이터 쿼리로 클라이언트에서 알아서 회사 이름을 가져 올 수 있었다. 하지만 이것은 어쩌다 걸린 케이스일 수 있다.  "재고이동" 이라는 데이터 모델을 생각 했을 때 전혀 예측할 수 없는 모델에서 "회사 이름" 을 가져 왔다.  이런 의문이 들 수 있다.

재고 이동을 한 회사 이름인데 왜 상품 데이터 모델의 회사 이름을 가져오지?

나아가 로직에 대한 빠른 이해를 방해하는 요소가 될 수 있다.

Deeply nested object

:Tip

graphql-query-complexity 를 이용하면 쿼리의 복잡성을 분석하고 제어할 수 있다.

Server / Client data mismatch

클라이언트에서 데이터는 디비에서 가져 오는 것은 맞지만 데이터 모델은 전혀 다르다. 

Client

DB

Server / Client data mismatch

데이터 모델을 만들기 위해 추가적인 가공 작업이 필요하다.

Client

DB

+

Server / Client data mismatch

디비 모델이 곧 GraphQL 스키마 모델이 되다 보니 DB 구조, 컬럼명 변경이 일어나면 클라이언트의 변경 또한 필수적이다.

Case

B: 디비 송장 테이블에 printedAt 컬럼 추가 했으니 송장 리스트에서 출력 일자 컬럼  createdAt를 printedAt으로 바꿔주세요.

F: 난 그냥 출력일자를 받으면 되는건데..

Inconsistent naming / type

비즈니스 모델과 디비 모델은 전혀 다르다. 하지만 디비 모델의 컬럼명과 타입을 따라가기 때문에 클라이언트에서 혼란이 생긴다.

Inconsistent naming / type

Case 1

입고 query arguments 는 입고 데이터 타입을 따로 정의하고 결과 타입은 디비 모델의 타입을 사용하여 문제가 생김

Inconsistent naming / type

Case 1

입고 query arguments 는 입고 데이터 타입을 따로 정의하고 결과 타입은 디비 모델의 타입을 사용하여 문제가 생김

디비 모델의 데이터 타입은 입고와 출고의 타입이 합쳐진 타입이다. 하지만 클라이언트에서 입고 데이터는 입고 유형만 가져야한다. 입고 타입을 TransactionType에 맞추려다 보니 에러발생, 이를 해결하기 위해 타입가드나 부정확한 타입 지정등 추가 코드 작성을 하여 타입 문제를 해결한다.

Inconsistent naming / type

Case 2

Seller는 User 모델에서는 Nullable 타입이지만, "재고" 모델에서는 Required 타입니다.

Inconsistent naming / type

Case 2

Seller는 User 모델에서는 Nullable 타입이지만, "재고" 모델에서는 Required 타입니다.

유저는 초기 진입 시 소속된 셀러가 없어 null 이 될수 있다. 재고 데이터는 셀러의 재고를 가져오기 때문에 null이 될 수 없는 값이다. 재고 데이터에서도 null 타입의 seller 데이터를 받고 있다.

Reference

GraphQL schema design

By Seungwoo Hong

GraphQL schema design

  • 250