
회사에서 내가 구축한 영상 이해 데이터 플랫폼의 연장으로, 브랜드 대시보드 프로젝트를 진행하면서 GraphQL과 Apollo Federation을 본격적으로 다뤘다.
그 과정에서 Federation 환경의 GraphQL 서버를 개발하며 느낀 것들을 담아봤다
GraphQL에 대한 오해
GraphQL은 흔히 overfetching(불필요한 데이터까지 가져오는 문제)과 underfetching(한 번에 필요한 데이터를 다 못 가져오는 문제)을 해결한다고 설명된다.
처음에 내가 잘못 생각했던 부분은, “각 요청마다 딱 맞는 스키마를 계속 추가해야 한다”라고 생각한 것이다.
즉, 상황별로 최적화된 쿼리와 응답을 위해 스키마를 세분화·확장하는 방식이 맞다고 여겼다.
설계를 하면서는 내가 정말 잘 하고 있다고 생각했고, 깔끔하다고 느끼기까지 했다.
하지만 리더의 리뷰를 받으며 “기존 거 최대한 그대로 쓸 수 있게 고민해 봐라” 라는 말을 듣고, 이유도 들을 필요 없이 이건 GraphQL 철학과 맞지 않다는 걸 바로 깨닫게 되었다.
내가 설계했던 방식대로 하면 스키마와 쿼리가 기하급수적으로 늘어나 관리가 불가능해진다.
GraphQL의 진짜 장점은 스키마를 계속해서 늘리는 것이 아니라 (어떻게 보면 맞기도 하다.),
하나의 스키마에서 클라이언트가 원하는 필드를 선택할 수 있다는 점에 있다는 걸 뒤늦게 이해했다.
쿼리 단순화
처음에는 대시보드 기능을 위해 쿼리를 세 개나 두려고 했다.
하지만 분석해보니 세 번째 쿼리는 두 번째 쿼리의 부분집합이었다.
# 원래 생각했던 구조
query getBrandVideos { ... }
query getBrandVideosStatDetail { ... }
query getBrandVideosDailyStat { ... } # 사실상 부분집합
결국 두 번째와 세 번째 쿼리를 합쳐, 총 두 개의 쿼리만으로 충분히 커버할 수 있었다.
이로 인해 개발/관리할 쿼리가 두 개가 되면서 편해지기도 했고, 코드 자체도 로직이 효율적으로 합쳐지며 깔끔해졌다.
GraphQL의 장점을 잘 살린 사례가 아닐까..? 싶다.
Federation 구조와 제약
이 작업에서는 총 3개의 서버가 상호작용을 하는데, 그 중 두 개는 아래와 같다.
- info 서버: 원본 데이터베이스의 내용을 GraphQL로 그대로 노출. 서비스의 일반적인 View의 응답 담당
- brand 서버: 브랜드 관련 기능(가공, 확장, 통계 등) 담당
회사의 모든 서버는 Apollo Federation으로 묶였고, gateway가 각 subgraph의 스키마를 조합해 클라이언트에 제공한다.
여기서 중요한 점은 federation의 위임 방식이다.
예를 들어, brands 필드는 gateway가 해당 스키마를 정의한 subgraph(여기서는 info 서버)에 요청을 넘긴다.
즉, 내 brand 서버에서는 brands 내부 데이터를 직접 필터링하거나 가공할 수 없다.
내 서버에서 특정 조건에 맞는 브랜드만 필터링해 반환하고 싶었는데,
brands 필드의 제어권은 info 서버에 있기 때문에 brand 서버에서 할 수 있는 방법은 새로운 필드를 정의하는 것뿐이었다.
그래서 filteredBrands라는 스키마를 별도로 구성하였다.
# 예시: 특정 ID에 맞는 브랜드만 필터링
query getFilteredBrands {
filteredBrandsById(id: "b1") {
id
name
sentiment
}
}
여기서도 GraphQL이 정말 유연하다는 것을 느꼈는데, 저 filteredBrands 스키마는 한 영상 내에서 ‘언급된 브랜드’에 대한 데이터만 필터링해 보여주는 필드였다.
“영상 정보”의 일환인데, 일반적으로는 다른 영상 정보에서 필요한 필드를 지정하고, filteredBrands와 합쳐서 응답을 보내주는 방식으로 구성하게 될 것이다.
하지만 GraphQL Federation에서는, 특정 필드만 지정하는 게 아니고 기존 영상 정보와 ‘결합’되도록 구성할 수 있었다.
extend type Video @key(fields: "videoId") {
videoId: ID! @external
filteredBrands(brandId: String): [Brand]
}
이렇게 구성하면, 클라이언트는 갑자기 영상 내 다른 필드가 필요할 때에도 서버 수정 없이 바로 응답을 받을 수 있고, 영상 내 새로운 필드가 추가되어도 일일이 반영할 필요가 없다.
Lessons Learned
- GraphQL 철학에 대한 이해
- Overfetching/underfetching 문제는 필드 선택권으로 해결된다.
- 요청마다 최적화된 스키마를 새로 정의하는 방식은 오히려 관리 지옥으로 이어진다.
- GraphQL Federation 환경에 대한 이해
- Federation에서는 각 subgraph가 맡은 필드에 다른 서버가 개입할 수 없다.
- 따라서 가능한 한 기존 스키마의 조합으로 쿼리를 설계하는 게 건강하다.
- 불가피하게 새로운 스키마를 정의해야 한다면, 기존 스키마를 확장하는 방식으로 유연하게 설계할 수 있다.
'Learnings' 카테고리의 다른 글
| 오픈서치(엘라스틱서치) 인덱스와 매핑, 그리고 리인덱싱 (0) | 2025.09.05 |
|---|---|
| MongoDB로 동시성과 상태 관리하기 (0) | 2025.09.04 |