InterfaceObject 를 알게된 맥락
팀에서 신규로 도입하는 언어와 프레임워크로 새로운 서버를 구축하는 작업을 진행하고 있다. 신규 서버의 Client - Server 간 통신을 GraphQL 로 하는 것으로 확정이 되었다.
이미 팀에서 Apollo Federation (Federation 1) 을 사용중이어서 새로운 Subgraph 를 하나 추가하는 방향으로 진행했는데 subgraph 를 새로 올리는 과정에서 한가지 문제를 만나게 된다. 문제는 interface 타입을 다른 subgraph 에 resolving 하도록 할 때 발생한다.
새로운 서브그래프에서 기존에 정의된 서브그래프에서 정의한 타입들을 사용해야 하는 경우가 있다.
resolving 로직을 중복으로 구현하지 않으려면 graphql federation 의 directive 를 적절히 사용해서 resolving 의 책임을 다른 subgraph 로 넘겨야 한다.
일반적인 type 에서는 문제가 없었는데, interface 를 다른 subgraph 에서 resolving 하려고 하니 문제가 하나 있었다. Federation 1 에서는 구체화된 type resolving 을 한 뒤에 다른 subgraph 로 넘겨야 한다는 것이다. 결국 다른 서버에서 관리하는 타입을 책임이 없는 서브그래프에서도 알고있어야 한다.
극단적으로 User / Post / Comment 가 전부 별도의 subgraph 로 구성된 예시로 확인해보자.
Federation1 schema
# User subgraph schema type User @key(fields: "id") { id: ID! name: String! posts: [Post!]! } type Post @key(fields: "id") { id: ID! }
# Post subgraph schema type Post @key(fields: "id") { id: ID! content: String! author: User! comments: [Comment!]! } type User @key(fields: "id") { id: ID! } interface Comment @key(fields: "id") { id: ID! } type NormalComment implements Comment @key(fields: "id") { id: ID! } type EmojiComment implements Comment @key(fields: "id") { id: ID! }
# Comment subgraph schema interface Comment @key(fields: "id") { id: ID! content: String! author: User! } type NormalComment implements Comment @key(fields: "id") { id: ID! content: String! author: User! } type EmojiComment implements Comment @key(fields: "id") { id: ID! content: String! author: User! emoji: String! } type User @key(fields: "id") { id: ID! }
V1 의 경우 Post 하위의 Comments 를 Comment Subgraph 로 resolving 요청을 할 때 구체타입을 알아야 하기 때문에 Post Subgraph 에도 Comment interface 를 구체화하는 type 들이 전부 명시되어야 한다.
분리된 subgraph 를 구성했는데, 결국 type 을 알기위해 어느정도의 조회가 필요한 이상한 상황이 된다.
Federation2 schema
# User subgraph schema type User @key(fields: "id") { id: ID! name: String! posts: [Post!]! } type Post @key(fields: "id") { id: ID! }
# Post subgraph schema type Post @key(fields: "id") { id: ID! content: String! author: User! comments: [Comment!]! } type User @key(fields: "id") { id: ID! } interface Comment @key(fields: "id") @interfaceObject { id: ID! }
# Comment subgraph schema interface Comment @key(fields: "id") { id: ID! content: String! author: User! } type NormalComment implements Comment @key(fields: "id") { id: ID! content: String! author: User! } type EmojiComment implements Comment @key(fields: "id") { id: ID! content: String! author: User! emoji: String! } type User @key(fields: "id") { id: ID! }
Federation1 과의 차이점은 @interfaceObject 디렉티브가 interface 에 붙은점과 Comment interface 를 구체화한 타입이 스키마에서 사라진 점이다.
Federation2 에서는 @interfaceObject 디렉티브를 통해서 TypeResolving 을 하지 않은 상태로 다른 subgraph 로 resolving 에 대한 책임을 넘길 수 있다.
다만 Federation2 에서도 2.3 버전부터 도입된 디렉티브이기 때문에 버전을 맞춰야 한다.