Reactive GraphQL — 고급 (DataLoader·Federation·운영)

2026-05-03확률과 통계 마스터 노트

Reactive GraphQL 마스터 노트 시리즈 7편 (마지막). DataLoader가 N+1을 자동 해결하는 메커니즘, Apollo Federation으로 마이크로서비스 GraphQL 통합, Schema Stitching·Subgraph·Gateway 패턴, Caching 전략, Persisted Queries, Observability, 운영 체크리스트까지 — 시리즈 마무리.

이 글은 Reactive GraphQL 마스터 노트 시리즈의 마지막 일곱 번째 편입니다. 1~6편이 토대였다면, 이번엔 고급·운영 — DataLoader·Federation·Caching·운영 모범 사례.

N+1 문제 깊은 해결. 마이크로서비스 GraphQL 통합 (Federation). Caching·Observability. 시리즈 마무리.

처음 고급 주제가 어렵게 느껴지는 이유

처음 이 단원이 어렵게 느껴지는 이유는 두 가지예요. 첫째, DataLoader 동작 원리가 막연합니다. 둘째, Federation이 왜 필요한지 안 보입니다.

해결법은 한 가지예요. "DataLoader = 자동 batching + cache" + "Federation = 마이크로서비스 GraphQL 통합" 두 줄.

DataLoader — N+1 깊은 해결

N+1 문제 복습

{
  users {        # 10 사용자
    posts {      # 각 사용자 게시물 → 10 쿼리
      title
    }
  }
}

11 쿼리 (1 + 10).

@BatchMapping (4편)

@BatchMapping(typeName = "User", field = "posts")
public Map<User, List<Post>> userPosts(List<User> users) { ... }

11 → 2 쿼리. 충분.

DataLoader — 더 강력

@Component
public class PostBatchLoader implements MappedBatchLoader<String, List<Post>> {
    
    @Autowired
    private PostRepository repo;
    
    @Override
    public CompletionStage<Map<String, List<Post>>> load(Set<String> userIds) {
        return repo.findByAuthorIdIn(userIds)
            .collectList()
            .map(posts -> posts.stream()
                .collect(Collectors.groupingBy(Post::getAuthorId)))
            .toFuture();
    }
}
@Configuration
public class GraphQLConfig {
    
    @Bean
    public DataLoaderRegistry dataLoaderRegistry(PostBatchLoader loader) {
        DataLoaderRegistry registry = new DataLoaderRegistry();
        registry.register("posts", DataLoader.newMappedDataLoader(loader));
        return registry;
    }
}
@SchemaMapping(typeName = "User", field = "posts")
public CompletableFuture<List<Post>> posts(User user, DataLoader<String, List<Post>> loader) {
    return loader.load(user.getId());
}

여기서 정말 중요한 시험 함정 — DataLoader = batching + caching + deduplication:

  • Batching — 같은 tick의 요청 묶음
  • Caching — 같은 ID 재요청 시 캐시
  • Deduplication — 중복 ID 자동 제거

@BatchMapping은 단순 batching만. DataLoader는 더 강력.

DataLoader vs BatchMapping

측면 @BatchMapping DataLoader
단순함 단순 복잡
캐시 X 요청 단위 캐시
Deduplication 자동 자동
다중 사용 별도 메서드 같은 Loader 재사용
권장 일반 복잡·재사용

여기서 시험 함정이 하나 있어요. 일반 = @BatchMapping, 복잡 = DataLoader. 둘 다 N+1 해결.

GraphQL Federation — 마이크로서비스 통합

[User Service] (GraphQL)
[Post Service] (GraphQL)
[Comment Service] (GraphQL)
   ↓
[Apollo Gateway] (통합 Schema)
   ↓
[Client]

각 마이크로서비스 = 자기 GraphQL Subgraph. Gateway가 통합.

Subgraph

# user-service/schema.graphqls
type User @key(fields: "id") {
  id: ID!
  name: String!
  email: String!
}

type Query {
  user(id: ID!): User
}
# post-service/schema.graphqls
type Post @key(fields: "id") {
  id: ID!
  title: String!
  authorId: ID!
}

# 다른 서비스 타입 확장
type User @key(fields: "id") {
  id: ID! @external
  posts: [Post!]!     # 이 서비스가 추가
}

type Query {
  post(id: ID!): Post
}

Gateway

# Apollo Router 또는 Apollo Gateway
subgraphs:
  - name: users
    routing_url: http://user-service/graphql
  - name: posts
    routing_url: http://post-service/graphql
# 클라이언트 쿼리 (통합)
{
  user(id: "1") {        # User Service
    name
    posts {              # Post Service (자동 라우팅)
      title
    }
  }
}

여기서 정말 중요한 시험 함정 — Federation = 모놀리식 GraphQL 회피. 각 팀이 자기 GraphQL 운영. Gateway가 통합. Apollo·DGS (Netflix) 도구.

Spring GraphQL Federation 지원

implementation 'com.apollographql.federation:federation-graphql-java-support:5.x'
@Bean
public RuntimeWiringConfigurer federation() {
    return wiringBuilder -> 
        Federation.transform(...)
            .resolveEntityType(...)
            .build();
}

Caching 전략

Per-Request Cache

@QueryMapping
public Mono<User> user(@Argument String id, GraphQLContext ctx) {
    Map<String, User> cache = ctx.get("userCache");
    if (cache == null) {
        cache = new HashMap<>();
        ctx.put("userCache", cache);
    }
    
    if (cache.containsKey(id)) {
        return Mono.just(cache.get(id));
    }
    
    return userRepo.findById(id)
        .doOnSuccess(u -> cache.put(id, u));
}

요청 단위 캐시. DataLoader가 더 우아.

Application-Level Cache (Redis)

@QueryMapping
public Mono<User> user(@Argument String id) {
    return cacheService.get("user:" + id, User.class)
        .switchIfEmpty(
            userRepo.findById(id)
                .doOnSuccess(u -> cacheService.put("user:" + id, u, Duration.ofMinutes(5)))
        );
}

Reactive Redis 시리즈 4편 패턴.

CDN Cache (Persisted Queries 시)

GET /graphql?queryId=abc&variables=...
   ↓ CDN cache (각 queryId별)
   ↓ 미스 시 서버

POST 쿼리는 CDN cache 어려움. GET + Persisted Queries = 가능.

Persisted Queries

1. 클라이언트 빌드 시 모든 쿼리를 ID로 등록
2. 런타임에 ID + 변수만 전송
3. 서버는 등록된 ID에서 쿼리 lookup
# 클라이언트
POST /graphql
{
  "queryId": "GetUser_abc123",
  "variables": { "id": "1" }
}

# 서버
{ user(id: "1") { name } }   // 등록된 쿼리

여기서 정말 중요한 시험 함정 — Persisted Queries = 보안 + 성능:

  • 등록 안 된 쿼리 거부 (보안)
  • GET + 캐시 가능 (성능)
  • 페이로드 크기 ↓

Apollo·Relay 자동 지원.

Observability

Metrics

management:
  endpoints:
    web:
      exposure:
        include: prometheus,metrics

자동 메트릭:

  • spring.graphql.request (지연·횟수)
  • 쿼리별 분리

Logging

@Bean
public RuntimeWiringConfigurer loggingInstrumentation() {
    return wiringBuilder -> wiringBuilder.instrumentation(
        new SimpleInstrumentation() {
            @Override
            public InstrumentationContext<ExecutionResult> beginExecution(...) {
                log.info("Query: {}", parameters.getQuery());
                return super.beginExecution(parameters, state);
            }
        }
    );
}

쿼리 로그 + 지연.

Tracing — Apollo Tracing·OpenTelemetry

@Bean
public Instrumentation tracingInstrumentation(OpenTelemetry otel) {
    return GraphQLTracingInstrumentation.builder(otel).build();
}

분산 추적·각 Resolver 시간.

Schema Versioning

# Deprecated 표시
type User {
  id: ID!
  name: String!
  username: String! @deprecated(reason: "Use 'name' instead")
}

GraphQL = 추가는 호환·삭제는 deprecation.

여기서 시험 함정이 하나 있어요. REST의 v1·v2 같은 versioning 거의 X. 단일 스키마·필드 추가/deprecate. 사용 메트릭 보고 deprecation 안전.

Schema Stitching (옛 방식)

Federation 이전. 여러 GraphQL 서버 → 단일 Gateway. 수동 통합.

여기서 시험 함정이 하나 있어요. Federation이 신규 표준. Schema Stitching는 deprecated. Apollo 권장 = Federation.

Apollo Studio·Apollo Sandbox

- Schema 시각화
- 사용 메트릭
- 운영 로그·쿼리
- 팀 협업

GraphQL 운영 SaaS. 무료 티어 있음.

Subscription 운영

수만 동시 구독 시:
  - WebSocket 연결 = OS 한계
  - Pub/Sub 백엔드 (Redis·Kafka)
  - Sticky Session 또는 Pub/Sub
  - 모니터링 — 활성 구독 수

3편 (Subscription) 참조.

운영 체크리스트

✓ N+1 해결 — @BatchMapping 또는 DataLoader
✓ Depth Limit (10~15) + Complexity Limit
✓ Rate Limit (클라이언트별)
✓ Persisted Queries (운영)
✓ Introspection OFF (운영)
✓ HTTPS (TLS)
✓ JWT·OAuth2 인증
✓ @PreAuthorize 메서드 보안
✓ 필드 레벨 보안 (민감 정보)
✓ 캐싱 — Redis·CDN
✓ Federation (마이크로서비스)
✓ Apollo Studio 또는 자체 모니터링
✓ Subscription Pub/Sub 백엔드
✓ Schema deprecation 점진 전환
✓ 부하 테스트 (Apollo 도구·k6 등)
✓ 부분 응답 + 에러 처리
✓ Logging·Tracing (OpenTelemetry)

REST·gRPC와 함께 사용

[Client]
   ↓ GraphQL (외부)
[BFF (Backend for Frontend)]
   ↓ gRPC (내부 마이크로서비스)
[User Service·Order Service·...]

여기서 정말 중요한 시험 함정 — GraphQL = BFF 위치 적합. 외부 API = 클라이언트 친화. 내부 = gRPC·REST. 각자 자리.

시리즈 마무리 — 7편 종합

1편부터 7편까지의 흐름:

주제 한 줄
1 기본 개념 Schema·SDL·REST 비교
2 Query·Mutation Variables·Alias·Fragment
3 Subscription WebSocket·Sinks·Pub/Sub
4 Spring 5 어노테이션·@BatchMapping
5 Reactive Mono/Flux·R2DBC
6 Security·Testing JWT·@GraphQlTest·Complexity
7 고급 DataLoader·Federation·운영

GraphQL의 거의 모든 운영 패턴을 한 번에 통찰할 토대.

시험 직전 한 번 더 — 자주 헷갈리는 함정 모음

여기까지가 7편의 핵심입니다. 시험 직전 또는 실무에서 헷갈릴 때 다시 펼쳐 볼 수 있게 압축 노트로 마무리할게요.

  • DataLoader = batching + caching + deduplication
  • @BatchMapping = 단순 batching만
  • 일반 = @BatchMapping, 복잡 = DataLoader
  • GraphQL Federation = 마이크로서비스 GraphQL 통합
  • Subgraph + Gateway (Apollo·DGS·Apollo Router)
  • @key(fields: "id") + @external 다른 서비스 타입 확장
  • Spring GraphQL Federation 지원 라이브러리
  • Caching — Per-Request·Application (Redis)·CDN (Persisted)
  • Persisted Queries = 등록된 ID만 + GET 캐시 가능
  • 보안 + 성능 + 페이로드 ↓
  • Observability — Spring metrics·Logging Instrumentation·OpenTelemetry
  • Apollo Studio·Apollo Sandbox = 운영 SaaS
  • Schema Versioning = deprecation (REST v1/v2 X)
  • Schema Stitching → Federation (신규 표준)
  • Subscription 운영 — Pub/Sub·Sticky Session·모니터링
  • GraphQL = BFF 위치 적합 (외부 API)
  • 내부 = gRPC·REST
  • 운영 체크 — N+1·Depth·Rate·Persisted·Introspection·TLS·인증·캐시·Federation·모니터링·deprecation·부하 테스트

시리즈 다른 편 (시리즈 마지막)

공식 문서: DataLoader / Apollo Federation / Spring GraphQL Reference 에서 더 깊이.

Reactive GraphQL 마스터 시리즈는 여기서 마무리. 1편부터 7편까지의 흐름이 머리에 남으면 GraphQL의 거의 모든 운영 패턴을 손에 잡고 시작할 토대가 됩니다.

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!