한국어

다양한 글로벌 사용자를 위한 강력하고 유지보수 가능한 API를 구축하기 위한 확장 가능한 GraphQL 스키마 디자인 패턴을 알아보세요. 스키마 스티칭, 페더레이션, 모듈화를 마스터하세요.

GraphQL 스키마 디자인: 글로벌 API를 위한 확장 가능한 패턴

GraphQL은 기존의 REST API에 대한 강력한 대안으로 부상하여, 클라이언트에게 필요한 데이터를 정확하게 요청할 수 있는 유연성을 제공합니다. 그러나 GraphQL API의 복잡성과 범위가 증가함에 따라, 특히 다양한 데이터 요구 사항을 가진 글로벌 사용자를 대상으로 할 때, 신중한 스키마 디자인은 유지보수성, 확장성 및 성능에 매우 중요해집니다. 이 글에서는 글로벌 애플리케이션의 요구 사항을 처리할 수 있는 강력한 API를 구축하는 데 도움이 되는 여러 확장 가능한 GraphQL 스키마 디자인 패턴을 살펴봅니다.

확장 가능한 스키마 디자인의 중요성

잘 설계된 GraphQL 스키마는 성공적인 API의 기반입니다. 이는 클라이언트가 데이터 및 서비스와 상호 작용하는 방식을 결정합니다. 잘못된 스키마 디자인은 다음과 같은 여러 문제를 야기할 수 있습니다.

글로벌 애플리케이션의 경우 이러한 문제들은 더욱 증폭됩니다. 지역마다 데이터 요구 사항, 규제 제약, 성능 기대치가 다를 수 있습니다. 확장 가능한 스키마 디자인은 이러한 과제들을 효과적으로 해결할 수 있게 해줍니다.

확장 가능한 스키마 디자인의 핵심 원칙

특정 패턴을 살펴보기 전에 스키마 디자인을 안내해야 할 몇 가지 핵심 원칙을 설명하겠습니다.

확장 가능한 스키마 디자인 패턴

강력한 GraphQL API를 구축하는 데 사용할 수 있는 몇 가지 확장 가능한 스키마 디자인 패턴은 다음과 같습니다.

1. 스키마 스티칭(Schema Stitching)

스키마 스티칭은 여러 GraphQL API를 단일 통합 스키마로 결합하는 것을 가능하게 합니다. 이는 데이터의 다른 부분을 담당하는 다른 팀이나 서비스가 있을 때 특히 유용합니다. 여러 개의 미니 API를 가지고 '게이트웨이' API를 통해 하나로 합치는 것과 같습니다.

작동 방식:

  1. 각 팀이나 서비스는 자체 스키마를 가진 자체 GraphQL API를 노출합니다.
  2. 중앙 게이트웨이 서비스는 스키마 스티칭 도구(예: Apollo Federation 또는 GraphQL Mesh)를 사용하여 이러한 스키마들을 단일 통합 스키마로 병합합니다.
  3. 클라이언트는 게이트웨이 서비스와 상호 작용하며, 이 서비스는 요청을 적절한 기본 API로 라우팅합니다.

예시:

제품, 사용자, 주문에 대한 별도의 API가 있는 전자상거래 플랫폼을 상상해 보십시오. 각 API에는 자체 스키마가 있습니다.

  
    # Products API
    type Product {
      id: ID!
      name: String!
      price: Float!
    }

    type Query {
      product(id: ID!): Product
    }

    # Users API
    type User {
      id: ID!
      name: String!
      email: String!
    }

    type Query {
      user(id: ID!): User
    }

    # Orders API
    type Order {
      id: ID!
      userId: ID!
      productId: ID!
      quantity: Int!
    }

    type Query {
      order(id: ID!): Order
    }
  

게이트웨이 서비스는 이러한 스키마들을 함께 엮어 통합된 스키마를 만들 수 있습니다.

  
    type Product {
      id: ID!
      name: String!
      price: Float!
    }

    type User {
      id: ID!
      name: String!
      email: String!
    }

    type Order {
      id: ID!
      user: User! @relation(field: "userId")
      product: Product! @relation(field: "productId")
      quantity: Int!
    }

    type Query {
      product(id: ID!): Product
      user(id: ID!): User
      order(id: ID!): Order
    }
  

Order 타입이 이제 별도의 API에 정의되어 있음에도 불구하고 UserProduct에 대한 참조를 포함하는 방식을 주목하십시오. 이는 스키마 스티칭 지시문(이 예에서는 @relation과 같은)을 통해 달성됩니다.

장점:

고려 사항:

2. 스키마 페더레이션(Schema Federation)

스키마 페더레이션은 스키마 스티칭의 한계를 해결하기 위해 설계된 진화된 방식입니다. 이는 GraphQL 스키마를 구성하는 데 더 선언적이고 표준화된 접근 방식을 제공합니다.

작동 방식:

  1. 각 서비스는 GraphQL API를 노출하고 페더레이션 지시문(예: @key, @extends, @external)으로 스키마에 주석을 답니다.
  2. 중앙 게이트웨이 서비스(Apollo Federation 사용)는 이러한 지시문을 사용하여 전체 페더레이션 스키마의 표현인 슈퍼그래프(supergraph)를 구축합니다.
  3. 게이트웨이 서비스는 슈퍼그래프를 사용하여 요청을 적절한 기본 서비스로 라우팅하고 종속성을 해결합니다.

예시:

동일한 전자상거래 예시를 사용하여 페더레이션된 스키마는 다음과 같을 수 있습니다.

  
    # Products API
    type Product @key(fields: "id") {
      id: ID!
      name: String!
      price: Float!
    }

    type Query {
      product(id: ID!): Product
    }

    # Users API
    type User @key(fields: "id") {
      id: ID!
      name: String!
      email: String!
    }

    type Query {
      user(id: ID!): User
    }

    # Orders API
    type Order {
      id: ID!
      userId: ID!
      productId: ID!
      quantity: Int!
      user: User! @requires(fields: "userId")
      product: Product! @requires(fields: "productId")
    }

    extend type Query {
      order(id: ID!): Order
    }
  

페더레이션 지시문의 사용에 주목하십시오.

장점:

고려 사항:

3. 모듈식 스키마 디자인

모듈식 스키마 디자인은 크고 모놀리식인 스키마를 더 작고 관리하기 쉬운 모듈로 나누는 것을 포함합니다. 이를 통해 페더레이션 스키마에 의존하지 않고도 API의 개별 부분을 더 쉽게 이해, 수정 및 재사용할 수 있습니다.

작동 방식:

  1. 스키마 내의 논리적 경계(예: 사용자, 제품, 주문)를 식별합니다.
  2. 각 경계에 대해 별도의 모듈을 만들고 해당 경계와 관련된 타입, 쿼리 및 뮤테이션을 정의합니다.
  3. GraphQL 서버 구현에 따라 가져오기/내보내기 메커니즘을 사용하여 모듈을 단일 통합 스키마로 결합합니다.

예시 (JavaScript/Node.js 사용):

각 모듈에 대해 별도의 파일을 만듭니다.

  
    // users.graphql
    type User {
      id: ID!
      name: String!
      email: String!
    }

    type Query {
      user(id: ID!): User
    }

    // products.graphql
    type Product {
      id: ID!
      name: String!
      price: Float!
    }

    type Query {
      product(id: ID!): Product
    }
  

그런 다음 기본 스키마 파일에서 결합합니다.

  
    // schema.js
    const { makeExecutableSchema } = require('graphql-tools');
    const { typeDefs: userTypeDefs, resolvers: userResolvers } = require('./users');
    const { typeDefs: productTypeDefs, resolvers: productResolvers } = require('./products');

    const typeDefs = [
      userTypeDefs,
      productTypeDefs,
      ""
    ];

    const resolvers = {
      Query: {
        ...userResolvers.Query,
        ...productResolvers.Query,
      }
    };

    const schema = makeExecutableSchema({
      typeDefs,
      resolvers,
    });

    module.exports = schema;
  

장점:

고려 사항:

4. 인터페이스 및 유니언 타입

인터페이스 및 유니언 타입을 사용하면 여러 구체적인 타입으로 구현될 수 있는 추상 타입을 정의할 수 있습니다. 이는 다형성 데이터, 즉 컨텍스트에 따라 다른 형태를 취할 수 있는 데이터를 나타내는 데 유용합니다.

작동 방식:

예시:

  
    interface Node {
      id: ID!
    }

    type User implements Node {
      id: ID!
      name: String!
      email: String!
    }

    type Product implements Node {
      id: ID!
      name: String!
      price: Float!
    }

    union SearchResult = User | Product

    type Query {
      node(id: ID!): Node
      search(query: String!): [SearchResult!]!
    }
  

이 예에서 UserProduct는 모두 공통 id 필드를 정의하는 Node 인터페이스를 구현합니다. SearchResult 유니언 타입은 User 또는 Product일 수 있는 검색 결과를 나타냅니다. 클라이언트는 `search` 필드를 쿼리한 다음 `__typename` 필드를 사용하여 어떤 유형의 결과를 받았는지 확인할 수 있습니다.

장점:

고려 사항:

5. 커넥션 패턴

커넥션 패턴은 GraphQL API에서 페이지네이션을 구현하는 표준적인 방법입니다. 대규모 데이터 목록을 청크 단위로 검색하는 일관되고 효율적인 방법을 제공합니다.

작동 방식:

예시:

  
    type User {
      id: ID!
      name: String!
      email: String!
    }

    type UserEdge {
      node: User!
      cursor: String!
    }

    type UserConnection {
      edges: [UserEdge!]!
      pageInfo: PageInfo!
    }

    type PageInfo {
      hasNextPage: Boolean!
      hasPreviousPage: Boolean!
      startCursor: String
      endCursor: String
    }

    type Query {
      users(first: Int, after: String, last: Int, before: String): UserConnection!
    }
  

장점:

고려 사항:

글로벌 고려 사항

글로벌 사용자를 위한 GraphQL 스키마를 디자인할 때 다음 추가 요소를 고려하십시오.

예를 들어, 제품 설명 필드를 고려해 보겠습니다.


type Product {
 id: ID!
 name: String!
 description(language: String = "en"): String!
}

이를 통해 클라이언트는 특정 언어로 설명을 요청할 수 있습니다. 언어가 지정되지 않은 경우 영어(`en`)가 기본값으로 사용됩니다.

결론

확장 가능한 스키마 디자인은 글로벌 애플리케이션의 요구 사항을 처리할 수 있는 강력하고 유지보수 가능한 GraphQL API를 구축하는 데 필수적입니다. 이 글에서 설명한 원칙을 따르고 적절한 디자인 패턴을 사용함으로써, 이해하고, 수정하고, 확장하기 쉬우면서도 뛰어난 성능과 확장성을 제공하는 API를 만들 수 있습니다. 스키마를 모듈화하고, 구성하고, 추상화하며, 글로벌 사용자의 특정 요구를 고려하는 것을 기억하십시오.

이러한 패턴을 채택함으로써 GraphQL의 잠재력을 최대한 발휘하고 앞으로 수년간 애플리케이션을 구동할 수 있는 API를 구축할 수 있습니다.