작성일 댓글 남기기

GraphQL의 Federation에 대해서

GraphQL Federation은 여러 개의 마이크로서비스에서 제공하는 여러 개의 GraphQL API를 하나의 통합된 그래프처럼 사용할 수 있게 해주는 아키텍처 패턴입니다. 이를 통해 대규모 애플리케이션에서 서비스 간 분리데이터 통합을 유연하게 처리할 수 있습니다. Apollo Federation이 대표적인 도구이며, 주로 대규모 마이크로서비스 아키텍처에서 GraphQL API를 통합할 때 사용됩니다.

1. Federation이 필요한 이유

전통적인 GraphQL 서버에서는 하나의 서버에서 모든 데이터를 제공하는 방식이 일반적입니다. 하지만 대규모 시스템에서는 여러 서비스가 각각의 데이터 소스를 가지고 있고, 이러한 데이터를 하나의 GraphQL API로 통합해서 사용해야 하는 경우가 많습니다.

Federation은 이러한 문제를 해결하기 위해 설계되었습니다. Federation을 사용하면 각각의 **서브서비스(Subgraph)**가 독립적으로 자신의 데이터를 제공하면서도, Gateway를 통해 모든 데이터를 하나의 통합된 그래프처럼 사용할 수 있습니다.

Federation을 사용하는 이유:

  • 마이크로서비스 아키텍처 지원: 각 서비스는 독립적으로 개발 및 배포되며, GraphQL API도 각 서비스에 맞게 분리되어 있습니다.
  • 데이터 통합: Federation은 여러 서비스에서 제공하는 데이터를 한 곳에서 모아 사용할 수 있게 해줍니다.
  • 확장성: 서비스를 분리하고 독립적으로 확장할 수 있습니다. 서비스 간 의존성이 낮아지고, 각 서비스가 독립적으로 운영됩니다.

2. Federation의 구성 요소

Federation을 구성하는 핵심 요소는 두 가지입니다:

  1. Subgraph (서브그래프): 각 서비스가 자체적으로 제공하는 독립적인 GraphQL API입니다. 각 서비스는 자체적으로 스키마를 정의하고, 해당 데이터를 처리합니다.
  2. Apollo Gateway (게이트웨이): 여러 개의 서브그래프를 하나의 통합된 그래프로 결합하는 역할을 합니다. 클라이언트는 이 게이트웨이를 통해 각각의 서브그래프의 데이터를 요청할 수 있습니다.

그림으로 보면 다음과 같은 구조를 가집니다:

           클라이언트
|
[Apollo Gateway]
/ | \
[Subgraph 1] [Subgraph 2] [Subgraph 3]

게이트웨이가 각 서브그래프에 쿼리를 전달하고, 결과를 클라이언트에 반환하는 방식입니다.


3. Federation의 핵심 기능

1) Key and Extend (키와 확장)

Federation의 핵심 기능 중 하나는 확장성입니다. **@key**와 @extend 디렉티브를 사용해 각 서브그래프의 스키마를 결합할 수 있습니다.

  • @key: 각 서브그래프에서 엔터티를 식별하는 필드를 정의합니다.
  • @extend: 다른 서브그래프에서 정의한 엔터티를 확장할 때 사용합니다.

예시:

User 데이터를 사용자 서비스에서 정의하고, 주문 서비스에서 사용자의 주문 데이터를 확장하려고 할 때, 각 서브그래프는 다음과 같이 정의됩니다.

  1. User 서비스의 Subgraph:
type User @key(fields: "id") {
id: ID!
name: String
email: String
}
  • 여기서 @key(fields: "id")User 타입의 id 필드가 사용자 엔터티를 식별하는 고유 값임을 나타냅니다.
  1. Order 서비스의 Subgraph:
extend type User @key(fields: "id") {
id: ID! @external
orders: [Order]
}

type Order {
id: ID!
product: String
price: Float
}
  • 여기서 **@extend**는 User 타입을 확장하고, @key를 통해 id로 사용자 엔터티를 식별합니다. **@external**은 id 필드가 다른 서브그래프(User 서비스)에서 제공된다는 것을 의미합니다.

2) Entities와 Reference Resolver

게이트웨이가 여러 서브그래프 간에 데이터를 결합할 때, Federation은 entitiesreference resolver를 사용합니다.

  • Entity: 각 서브그래프에서 @key로 정의된 엔터티는 게이트웨이를 통해 통합된 그래프에서 공유됩니다.
  • Reference Resolver: @key로 식별된 엔터티를 다른 서브그래프에서 참조할 수 있도록 리졸버를 제공합니다.

4. Apollo Federation 설정

다음은 Apollo Federation을 실제로 설정하는 과정입니다.

1) 각 서비스의 Subgraph 설정

먼저, 각각의 서브그래프(Subgraph)를 설정해야 합니다. 각 서브그래프는 독립적인 GraphQL 서버로 동작하며, 서로 다른 데이터를 제공합니다.

예시: User 서비스
const { ApolloServer, gql } = require('apollo-server');
const { buildFederatedSchema } = require('@apollo/federation');

const typeDefs = gql`
type User @key(fields: "id") {
id: ID!
name: String
email: String
}

type Query {
users: [User]
}
`;

const resolvers = {
Query: {
users() {
return [
{ id: '1', name: 'John Doe', email: '[email protected]' },
{ id: '2', name: 'Jane Doe', email: '[email protected]' }
];
}
}
};

const server = new ApolloServer({
schema: buildFederatedSchema([{ typeDefs, resolvers }])
});

server.listen({ port: 4001 }).then(({ url }) => {
console.log(`User service running at ${url}`);
});
예시: Order 서비스
const { ApolloServer, gql } = require('apollo-server');
const { buildFederatedSchema } = require('@apollo/federation');

const typeDefs = gql`
extend type User @key(fields: "id") {
id: ID! @external
orders: [Order]
}

type Order {
id: ID!
product: String
price: Float
}

type Query {
orders: [Order]
}
`;

const resolvers = {
User: {
orders(user) {
return [
{ id: '1', product: 'Laptop', price: 1000.0 },
{ id: '2', product: 'Phone', price: 500.0 }
];
}
},
Query: {
orders() {
return [
{ id: '1', product: 'Laptop', price: 1000.0 },
{ id: '2', product: 'Phone', price: 500.0 }
];
}
}
};

const server = new ApolloServer({
schema: buildFederatedSchema([{ typeDefs, resolvers }])
});

server.listen({ port: 4002 }).then(({ url }) => {
console.log(`Order service running at ${url}`);
});

2) 게이트웨이 설정

이제 Apollo Gateway를 설정하여 각각의 서브그래프를 하나의 통합된 GraphQL API로 연결합니다.

Apollo Gateway 설정:
javascript코드 복사const { ApolloServer } = require('apollo-server');
const { ApolloGateway } = require('@apollo/gateway');

const gateway = new ApolloGateway({
  serviceList: [
    { name: 'users', url: 'http://localhost:4001' },
    { name: 'orders', url: 'http://localhost:4002' }
  ]
});

const server = new ApolloServer({
  gateway,
  subscriptions: false
});

server.listen({ port: 4000 }).then(({ url }) => {
  console.log(`Gateway running at ${url}`);
});

게이트웨이는 각 서브그래프(User 서비스와 Order 서비스)를 연결하고, 클라이언트로부터 요청을 받습니다.

3) 클라이언트에서 쿼리 실행

이제 클라이언트는 게이트웨이를 통해 통합된 GraphQL API를 사용할 수 있습니다.

query {
users {
id
name
orders {
product
price
}
}
}

이 쿼리는 users 필드를 통해 사용자 목록을 가져오고, 각 사용자의 주문 데이터를 가져옵니다. 게이트웨이가 각 서비스에 쿼리를 분배하고, 최종적으로 클라이언트에 데이터를 반환합니다.


5. Federation의 장점

  • 마이크로서비스 아키텍처와의 자연스러운 통합: 각 서비스는 독립적으로 관리되며, 이를 통합하여 하나의 그래프로 제공할 수 있습니다.
  • 확장성: 개별 서비스는 독립적으로 개발 및 배포될 수 있어, 시스템 전체의 확장성이 향상됩니다.
  • 일관된 GraphQL API 제공: 클라이언트는 여러 개의 서비스로 나뉘어 있는 데이터를 하나의 통합된 그래프에서 쿼리할 수 있습니다.

6. Federation의 단점 및 고려사항

  • 복잡성 증가: 서비스가 많아질수록 Federation 구조가 복잡해질 수 있습니다. 각 서브그래프 간의 의존성을 관리하고, 데이터를 효율적으로 결합해야 합니다.
  • 추가적인 네트워크 호출: 게이트웨이가 각 서브그래프에 쿼리를 전달하고 데이터를 모으는 과정에서 네트워크 호출이 증가할 수 있습니다. 이는 성능에 영향을 줄 수 있으므로 최적화가 필요합니다.
  • 추가적인 인프라 관리: 게이트웨이와 각 서브그래프를 배포하고 모니터링하는 추가적인 인프라 관리가 필요합니다.

결론

GraphQL Federation은 여러 마이크로서비스를 통합하여 하나의 일관된 GraphQL API를 제공할 수 있는 강력한 아키텍처 패턴입니다. 특히 대규모 시스템에서 서비스 간의 의존성을 줄이고, 독립적으로 개발 및 배포할 수 있는 환경을 제공합니다.

Federation을 효과적으로 사용하려면 각 서브그래프 간의 관계를 명확하게 정의하고, 게이트웨이를 통해 통합된 데이터 흐름을 관리해야 합니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다