본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 5장: Federation과 Supergraph
2026년 2월 14일·웹 개발·

5장: Federation과 Supergraph

Apollo Federation 2.0을 활용한 마이크로서비스 GraphQL 아키텍처를 다룹니다. 서브그래프와 슈퍼그래프 개념, 엔티티 참조 리졸버, 주요 디렉티브, 스키마 컴포지션 과정을 학습합니다.

15분670자9개 섹션
graphqlapi-designfrontend
공유
graphql-architecture5 / 11
1234567891011
이전4장: N+1 문제와 DataLoader 최적화다음6장: 실시간 구독(Subscriptions)

학습 목표

  • Federation의 핵심 개념인 서브그래프, 슈퍼그래프, 라우터를 이해한다
  • 엔티티와 참조 리졸버를 구현할 수 있다
  • @key, @external, @provides, @requires 디렉티브의 용도를 파악한다
  • 스키마 컴포지션과 관리형 페더레이션 워크플로를 이해한다

왜 Federation인가

조직이 성장하면 하나의 모놀리식 GraphQL 서버로는 한계에 부딪힙니다.

  • 팀 간 충돌: 여러 팀이 하나의 스키마 파일을 동시에 수정하면 병합 충돌이 빈번해집니다
  • 배포 결합: 작은 변경에도 전체 서비스를 재배포해야 합니다
  • 확장성 제한: 특정 도메인만 스케일 아웃할 수 없습니다

Apollo Federation은 이 문제를 해결합니다. 각 팀이 자신의 도메인에 해당하는 서브그래프(Subgraph) 를 독립적으로 개발하고 배포하며, 라우터(Router) 가 이를 하나의 슈퍼그래프(Supergraph) 로 통합합니다.

클라이언트는 라우터의 단일 엔드포인트만 알면 됩니다. 내부적으로 어떤 서브그래프에서 데이터를 가져오는지는 라우터가 처리합니다.


Federation 2.0 핵심 개념

엔티티(Entity)

엔티티는 여러 서브그래프에 걸쳐 존재하는 공유 타입입니다. @key 디렉티브로 식별자를 지정합니다.

users-subgraph.graphql
graphql
# Users 서브그래프 — User 엔티티의 소유자
type User @key(fields: "id") {
  id: ID!
  name: String!
  email: String!
  avatar: String
}
 
type Query {
  user(id: ID!): User
  me: User
}
reviews-subgraph.graphql
graphql
# Reviews 서브그래프 — User 엔티티를 확장
type User @key(fields: "id") {
  id: ID!
  reviews: [Review!]!    # 이 서브그래프가 추가하는 필드
}
 
type Review @key(fields: "id") {
  id: ID!
  rating: Int!
  body: String!
  author: User!
  product: Product!
}

User 타입이 두 서브그래프에 모두 존재합니다. Users 서브그래프는 name, email, avatar를, Reviews 서브그래프는 reviews를 책임집니다. 라우터가 이를 하나로 합칩니다.

참조 리졸버(Reference Resolver)

서브그래프가 다른 서브그래프의 엔티티를 참조할 때, 해당 엔티티의 데이터를 가져오기 위해 참조 리졸버가 필요합니다.

reference-resolver.ts
typescript
// Users 서브그래프
const resolvers = {
  User: {
    __resolveReference: async (
      reference: { __typename: 'User'; id: string },
      context: Context,
    ) => {
      // 4장에서 배운 DataLoader 사용이 필수
      return context.loaders.userById.load(reference.id);
    },
  },
};

라우터가 Reviews 서브그래프에서 Review.author를 조회한 후, 해당 User의 name이나 email이 필요하면 Users 서브그래프의 참조 리졸버를 호출합니다.

쿼리 실행 흐름

클라이언트가 다음 쿼리를 보내면 라우터가 어떻게 처리하는지 살펴보겠습니다.

federated-query.graphql
graphql
query {
  me {
    name          # Users 서브그래프
    email         # Users 서브그래프
    reviews {     # Reviews 서브그래프
      rating
      body
      product {   # Products 서브그래프
        name
        price
      }
    }
  }
}

주요 디렉티브

Federation 2.0에서 사용하는 핵심 디렉티브를 정리합니다.

@key

엔티티의 식별자를 지정합니다. 복합 키도 가능합니다.

key-directive.graphql
graphql
# 단일 키
type User @key(fields: "id") {
  id: ID!
  name: String!
}
 
# 복합 키
type ProductVariant @key(fields: "productId sku") {
  productId: ID!
  sku: String!
  price: Int!
}
 
# 다중 키 (여러 방법으로 식별 가능)
type User @key(fields: "id") @key(fields: "email") {
  id: ID!
  email: String!
  name: String!
}

@external

다른 서브그래프에서 정의된 필드를 참조할 때 사용합니다.

external-directive.graphql
graphql
# Reviews 서브그래프
type Product @key(fields: "id") {
  id: ID!
  averageRating: Float! @requires(fields: "reviewCount totalRating")
  reviewCount: Int! @external
  totalRating: Int! @external
}

@provides

서브그래프가 엔티티의 특정 필드를 함께 제공할 수 있음을 선언합니다. 라우터가 추가 서브그래프 호출을 줄이는 최적화에 사용됩니다.

provides-directive.graphql
graphql
# Reviews 서브그래프
type Review @key(fields: "id") {
  id: ID!
  body: String!
  author: User! @provides(fields: "name")
}
 
# Reviews 서브그래프가 User.name을 직접 제공할 수 있음
type User @key(fields: "id") {
  id: ID!
  name: String! @external
}

이 경우 Review.author.name만 필요한 쿼리에서는 라우터가 Users 서브그래프를 호출하지 않아도 됩니다.

@requires

필드 해결에 다른 서브그래프의 필드가 필요함을 선언합니다.

requires-directive.graphql
graphql
# Shipping 서브그래프
type Product @key(fields: "id") {
  id: ID!
  weight: Float! @external
  dimensions: String! @external
  shippingCost: Float! @requires(fields: "weight dimensions")
}

shippingCost를 계산하려면 Products 서브그래프의 weight와 dimensions가 필요합니다. 라우터가 자동으로 Products 서브그래프에서 이 데이터를 먼저 가져옵니다.


서브그래프 서버 구현

Federation을 지원하는 서브그래프 서버를 구현합니다.

subgraph-server.ts
typescript
import { ApolloServer } from '@apollo/server';
import { buildSubgraphSchema } from '@apollo/subgraph';
import { parse } from 'graphql';
import { readFileSync } from 'fs';
 
const typeDefs = parse(readFileSync('./schema.graphql', 'utf8'));
 
const resolvers = {
  Query: {
    user: async (_parent: unknown, args: { id: string }, context: Context) => {
      return context.dataSources.users.findById(args.id);
    },
    me: async (_parent: unknown, _args: unknown, context: Context) => {
      if (!context.currentUser) return null;
      return context.dataSources.users.findById(context.currentUser.id);
    },
  },
  User: {
    __resolveReference: async (
      ref: { __typename: 'User'; id: string },
      context: Context,
    ) => {
      return context.loaders.userById.load(ref.id);
    },
  },
};
 
const server = new ApolloServer({
  schema: buildSubgraphSchema({ typeDefs, resolvers }),
});
Info

buildSubgraphSchema는 일반 ApolloServer에서 사용하는 typeDefs + resolvers 대신 Federation 사양에 맞는 스키마를 생성합니다. _entities 쿼리와 _service 쿼리가 자동으로 추가됩니다.


스키마 컴포지션

여러 서브그래프의 스키마를 하나의 슈퍼그래프로 합치는 과정이 스키마 컴포지션(Schema Composition) 입니다.

로컬 컴포지션

개발 환경에서 Rover CLI를 사용하여 로컬에서 컴포지션을 수행할 수 있습니다.

supergraph.yaml
yaml
federation_version: =2.9
subgraphs:
  users:
    routing_url: http://localhost:4001/graphql
    schema:
      file: ./subgraphs/users/schema.graphql
  products:
    routing_url: http://localhost:4002/graphql
    schema:
      file: ./subgraphs/products/schema.graphql
  reviews:
    routing_url: http://localhost:4003/graphql
    schema:
      file: ./subgraphs/reviews/schema.graphql
composition-commands.sh
bash
# Rover CLI로 슈퍼그래프 스키마 생성
rover supergraph compose --config ./supergraph.yaml > supergraph.graphql
 
# 컴포지션 검증 (CI에서 사용)
rover supergraph compose --config ./supergraph.yaml --dry-run

관리형 페더레이션(Managed Federation)

프로덕션 환경에서는 Apollo Studio를 통한 관리형 페더레이션을 권장합니다.

managed-federation.sh
bash
# 서브그래프 스키마 변경 검증 (CI에서 실행)
rover subgraph check my-graph@production \
  --name users \
  --schema ./schema.graphql
 
# 서브그래프 스키마 배포 (CD에서 실행)
rover subgraph publish my-graph@production \
  --name users \
  --schema ./schema.graphql \
  --routing-url https://users.internal:4001/graphql
Warning

rover subgraph check는 Breaking Change를 감지합니다. 기존 클라이언트가 사용 중인 필드를 삭제하거나 타입을 변경하면 경고를 발생시킵니다. 이 단계를 CI 파이프라인에 포함시켜 안전한 스키마 진화를 보장해야 합니다.


Apollo Router 설정

Apollo Router는 Rust로 작성된 고성능 GraphQL 게이트웨이입니다.

router.yaml
yaml
supergraph:
  listen: 0.0.0.0:4000
 
# 헤더 전파 설정
headers:
  all:
    request:
      - propagate:
          matching: "authorization|x-request-id|x-trace-id"
 
# CORS 설정
cors:
  origins:
    - https://app.example.com
  allow_headers:
    - Content-Type
    - Authorization
 
# 텔레메트리
telemetry:
  instrumentation:
    spans:
      mode: spec_compliant
  exporters:
    tracing:
      otlp:
        enabled: true
        endpoint: http://otel-collector:4317
 
# 쿼리 제한
limits:
  max_depth: 15
  max_height: 200
  max_aliases: 30

Federation 설계 원칙

효과적인 Federation 아키텍처를 위한 핵심 원칙을 정리합니다.

엔티티 소유권을 명확히 하라: 각 엔티티에는 하나의 "소유자" 서브그래프가 있어야 합니다. 소유자가 핵심 필드를 정의하고, 다른 서브그래프는 필드를 확장합니다.

서브그래프 경계는 도메인을 따르라: 기술적 레이어(auth, logging)가 아닌 비즈니스 도메인(사용자, 상품, 주문)으로 서브그래프를 나누어야 합니다.

공유 타입을 남용하지 마라: 모든 타입을 엔티티로 만들면 서브그래프 간 결합도가 높아집니다. 진정으로 여러 도메인에 걸친 타입만 엔티티로 정의합니다.

참조 리졸버에서 DataLoader를 사용하라: 4장에서 강조했듯이, 참조 리졸버에서 DataLoader 없이는 N+1 문제가 서브그래프 간 네트워크 호출 수준으로 증폭됩니다.


정리

이번 장에서는 Apollo Federation 2.0을 활용한 분산 GraphQL 아키텍처를 학습했습니다. 서브그래프와 슈퍼그래프의 개념, 엔티티와 참조 리졸버의 동작 원리, @key, @external, @provides, @requires 디렉티브의 용도를 파악했습니다. 스키마 컴포지션과 관리형 페더레이션을 통한 안전한 스키마 진화 방법도 살펴보았습니다.

Federation은 대규모 조직에서 GraphQL의 장점을 극대화하는 아키텍처 패턴입니다. 각 팀이 독립적으로 개발하면서도 클라이언트에게는 하나의 통합된 그래프를 제공할 수 있습니다.

다음 장 미리보기

6장에서는 GraphQL의 세 번째 루트 타입인 Subscription을 다룹니다. WebSocket 기반 실시간 데이터 구독의 구현, PubSub 패턴, Redis를 활용한 확장, 그리고 Federation 환경에서의 구독 한계와 대안 패턴을 살펴보겠습니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#graphql#api-design#frontend

관련 글

웹 개발

6장: 실시간 구독(Subscriptions)

GraphQL Subscriptions의 프로토콜과 구현을 다룹니다. WebSocket 전송, PubSub 패턴, Redis를 활용한 확장, 필터링 전략, 그리고 Federation 환경에서의 한계와 SSE 등 대안 패턴을 살펴봅니다.

2026년 2월 16일·13분
웹 개발

4장: N+1 문제와 DataLoader 최적화

GraphQL에서 가장 흔한 성능 문제인 N+1 문제의 원인을 분석하고, DataLoader를 통한 배칭과 캐싱 최적화를 구현합니다. 쿼리 복잡도 분석과 깊이 제한 전략도 다룹니다.

2026년 2월 12일·13분
웹 개발

7장: 인증과 인가

GraphQL API에서의 인증과 인가 전략을 다룹니다. JWT 인증 미들웨어, 컨텍스트 기반 사용자 주입, 리졸버 수준 인가, 디렉티브 기반 선언적 인가, RBAC/ABAC 모델을 학습합니다.

2026년 2월 18일·13분
이전 글4장: N+1 문제와 DataLoader 최적화
다음 글6장: 실시간 구독(Subscriptions)

댓글

목차

약 15분 남음
  • 학습 목표
  • 왜 Federation인가
  • Federation 2.0 핵심 개념
    • 엔티티(Entity)
    • 참조 리졸버(Reference Resolver)
    • 쿼리 실행 흐름
  • 주요 디렉티브
    • @key
    • @external
    • @provides
    • @requires
  • 서브그래프 서버 구현
  • 스키마 컴포지션
    • 로컬 컴포지션
    • 관리형 페더레이션(Managed Federation)
  • Apollo Router 설정
  • Federation 설계 원칙
  • 정리
    • 다음 장 미리보기