결론부터 말하자면, 전혀 없다.
DB 스키마로부터 GraphQL Type 을 자동으로 만들어 낼 수는 있다. 이는 GraphQL Type 을 작성하는 노력을 줄이고, 하나의 스키마로부터 동일한 개념을 생성하겠다는데 목적이 있다.
그런데 잘 생각해보자. 이게 옳은 것인가?
서버에서 DB 스키마를 변환한 내용으로, Query
를 정의해 API 엔드포인트를 열어두었다고 생각해보자.
DB 스키마가 잘 정규화되지 않은, 아래 형태의 도큐먼트가 있다고 가정해보자
{
"restaurant": {
"ja": {
"name": "string",
"street": "string"
},
"fr": {
"name": "string",
"street": "string"
}
}
}
언어별로 필드가 있고, 내부적으로 데이터를 가지고 있다. 이게 클라이언트에 어떻게 노출이 될까?
# DB 로부터 정의된 GraphQL 타입 스키마
type Restaurant {
ja: RestaurantJa
fr: RestaurantFr
}
type RestaurantJa {
name: String
street: String
}
type RestaurantFr {
name: String
street: String
}
# 클라이언트에 노출하는 쿼리
type Query {
home(first: Int): [Restaurant]
}
클라이언트가 일본어로 된 레스토랑 정보를 받으려면 어떻게 쿼리를 날려야 할까?
query home {
restaurant {
ja {
name,
strret
}
}
}
이게 정말로 GraphQL 철학에 맞는다고 생각하는가?
클라이언트는 자신이 필요한 데이터를 모델링하고, 서버에 선언적으로 요청하면 된다. DB 스키마와 전혀 별개로 작성할 수 있어야 한다는 뜻이다. 아래 쿼리를 보자.
query home(lang: "jr") {
restaurant {
name,
street
}
}
---
# 서버에서 클라이언트와 통신할 인터페이스 정의
type Restaurant {
name: String
street: String
}
# 클라이언트에 노출하는 쿼리
type Query {
home($lang: Language): [Restaurant]
}
# 해당 쿼리를 처리할 함수
const resolver = ({ lang }, projection) => Restaurant.find({}).select({
[lang]: true,
...projection,
})
느낌이 어떤가? 아직도 DB 스키마를 클라이언트까지 엮는게 좋다고 생각하는가? 이렇게 강한 의존성은 끊고 추상화위에서 쿼리를 작성해야 한다.
다시 한번 말하지만, GraphQL 서버는 어떤 DB 를 사용하는가에 전혀 영향을 받지 않는다. 클라이언트가 DB 스키마 모델의 정보를 알게 되는 것 자체가 이상하다는 뜻이다.
서버에서는 각 필드를 어떻게 처리할지, resolver
를 선언하면서 비즈니스 로직을 처리하면 된다.
비즈니스 로직에서는 적절하게 DB 를 접근하여 데이터를 가져오거나 수정하면 된다.
각 레이어 사이의 의존성을 끊고, 자신의 역할에 충실하라. 편하게 가려다 철학을 위배하는 상황을 만들고, Bad Practice 로 남을 수 있으니 말이다.