06 — Backend-for-Frontend Pattern
한 줄 답: BFF(Backend-for-Frontend)는 *“클라이언트마다 그에 최적화된 백엔드 한 층을 둔다”*는 패턴 (Sam Newman, 2015). GraphQL은 그 BFF 한 층이 N개 클라이언트를 동시에 받는 자리에 가장 자연스럽다 — 클라이언트가 각자 다른 모양을 한 쿼리로 요청할 수 있기 때문이다.
Why — 왜 BFF 패턴이 등장했나
2010년대 초, 회사들은 같은 백엔드에 모바일·웹·TV·watch 등 다양한 클라이언트를 붙이기 시작했다. 처음엔 공통 REST API 하나로 다 처리하려 했지만, 곧 모순이 드러났다.
| 문제 | 원인 |
|---|---|
| 모바일은 작은 응답이 필요 (셀룰러·배터리) | 웹은 큰 응답이 효율적 (한 번에 받기) |
| TV는 큰 이미지·간결한 메타 필요 | 모바일은 작은 이미지·풍부한 메타 |
| 모바일 release는 느림 (app store) | 웹은 즉시 배포 |
| 모바일 API는 backwards compat 강제 | 웹은 자유롭게 깰 수 있음 |
→ 한 백엔드가 모든 클라이언트를 만족시키려면 어느 한 쪽이 unfair해진다. SoundCloud 엔지니어들이 이 문제를 해결하려 만든 패턴이 BFF.
Sam Newman의 정의 (2015)
“A separate backend for each user experience.” — 모바일 BFF, 웹 BFF, TV BFF — 각각 그 클라이언트만을 위한 백엔드.
각 BFF는 자기 클라이언트의 요구만 본다. 백엔드 서비스는 순수 비즈니스 로직만 다룬다.
How — GraphQL이 BFF 자리에 자연스러운 이유
1) N개 BFF → 1개 GraphQL BFF의 진화
전통 BFF는 클라이언트 종류만큼 백엔드 인스턴스를 만든다. 운영 비용이 N배. GraphQL은 이 문제를 한 BFF로 흡수할 수 있다.
각 클라이언트가 자기 쿼리를 보낸다. 모바일은 작은 모양, 웹은 풍부한 모양 — 같은 endpoint에서 각자 다른 응답. 운영 비용이 1배.
2) “각 클라이언트가 자기 쿼리를 갖는다”는 사고
# 모바일 — 작은 모양
query MobileFeed {
feed(first: 10) {
title
thumbnailSmall
}
}
# 웹 — 풍부한 모양
query WebFeed {
feed(first: 20) {
title
subtitle
thumbnailLarge
author { name avatar }
commentCount
likeCount
}
}
# TV — 또 다른 모양
query TVFeed {
feed(first: 5) {
title
posterImage
durationLabel
}
}같은 feed 노드를 세 가지 모양으로 본다. 서버 스키마는 한 벌. 클라이언트만 각자의 쿼리를 가진다.
→ 이게 GraphQL의 client-specific query 패턴. BFF의 철학과 정확히 일치.
3) GraphQL + Federation — team-specific BFF
회사가 커지면 팀이 백엔드를 나눠 가짐. GraphQL Federation은 subgraph로 이를 표현한다.
- 각 팀은 자기 subgraph를 독립 배포
- Gateway가 subgraph들을 하나의 그래프로 합침
- 클라이언트는 팀 경계를 모르고 한 그래프로 쿼리
→ BFF + 마이크로서비스의 결합. 자세한 건 06-federation 챕터.
4) BFF의 책임 — 데이터 결합 + 응답 적응
BFF 한 층이 하는 일:
| 책임 | GraphQL이 어떻게 도움 |
|---|---|
| 여러 서비스에서 데이터 모음 (aggregation) | resolver가 자연스럽게 multiple backend 호출 |
| 클라이언트별 응답 모양 적응 | client query가 자동 적응 |
| 인증·인가 통합 | context 미들웨어 |
| 응답 캐싱 | persisted query + normalized cache |
| 클라이언트 버전 관리 | 필드 deprecate로 점진 진화 |
→ GraphQL이 각 책임을 모두 1급 시민으로 다룬다. REST/RPC BFF로 같은 일을 하려면 코드를 더 짜야 함.
What — 실제 사례
Netflix — Federation BFF의 정점
- 2010년대 초: 자체 BFF 프레임워크 Falcor 사용 (GraphQL의 사촌)
- 2018~: GraphQL Federation으로 이동 — Studio Edge
- 현재: 모든 클라이언트(TV·모바일·웹)가 하나의 federated graph를 본다
- 내부 서비스 간: gRPC
GitHub — 듀얼 API + 내부 BFF
- 외부 개발자용: REST v3, GraphQL v4 둘 다
- 내부 web app: GraphQL을 BFF처럼 사용
- 모바일 (iOS/Android): GraphQL을 직접 사용
→ “GraphQL을 내부 BFF에 쓰고, REST를 외부 노출에 유지”의 모범 사례.
Airbnb — REST BFF → GraphQL BFF 전환
- 2013: REST BFF (단일 endpoint per screen)
- 2018: GraphQL BFF — 한 endpoint, 클라이언트별 쿼리
- 결과: 프론트엔드 팀이 백엔드 변경 없이 새 화면 추가
Shopify — Storefront API
- 외부 개발자가 Shopify 데이터를 각자의 화면에 맞게 가져갈 수 있도록 GraphQL Storefront API를 공개
- 한 쇼피파이 가게가 모바일 앱·웹·POS를 동시에 가짐 — GraphQL이 각 화면 모양에 맞게 응답
- BFF 패턴이 고객사까지 확장된 형태
한국 사례 — 카카오·네이버
- 카카오: GraphQL을 모바일 BFF로 도입 (카카오톡 일부 영역, 다음 검색)
- 네이버: Smart-Editor·페이 등에서 GraphQL BFF 사용
- 공통: 내부 서비스는 gRPC/REST, 클라이언트엔 GraphQL
What-if — BFF에 GraphQL을 안 쓰면
1) 클라이언트별 REST endpoint를 만들고 있음
→ /mobile/feed, /web/feed, /tv/feed — 화면 추가마다 endpoint도 추가. N×M 폭발.
대응: GraphQL 한 endpoint에서 client query로 분기.
2) 모든 클라이언트가 공통 REST를 쓰고 있음
→ 모바일이 큰 응답을 받아야 하거나, 웹이 N번 호출을 해야 함. 어느 한 쪽이 손해. 대응: BFF 도입. GraphQL이 가장 자연스러움.
3) 클라이언트마다 완전 다른 BFF를 운영
→ 운영 비용 N배. 공통 로직 중복. 변경 시 N곳 동기화. 대응: GraphQL 한 BFF + client-specific query로 흡수.
4) BFF 한 층 안에서 서비스를 직접 import
→ BFF가 비즈니스 로직까지 잠식. 마이크로서비스 경계 깨짐. 대응: BFF는 얇은 aggregation만. 비즈니스는 백엔드 서비스에 둠.
5) GraphQL BFF의 권한을 클라이언트에 위임
→ “클라이언트가 쿼리를 만든다”는 자유를 권한 우회로 악용.
대응: persisted query + field-level authorization (자세히는 07-security-governance).
Insight — 흥미로운 이야기
”BFF는 GraphQL과 동시에 등장했다”
Sam Newman이 BFF 패턴을 처음 글로 정리한 게 2015년 (SoundCloud 사례 기반). GraphQL이 공개된 게 2015년. 우연이 아니다 — 같은 시기, 같은 산업 문제를 다른 각도에서 본 두 답.
Sam Newman의 글은 *“클라이언트마다 백엔드 하나”*를 제안했고, GraphQL은 *“한 백엔드가 클라이언트마다 다른 모양을 줄 수 있다면?”*을 제안했다. 둘이 합쳐지는 데 5년이 걸렸다.
”Facebook의 BFF — Relay와 GraphQL의 짝”
Facebook은 GraphQL과 함께 Relay라는 클라이언트 라이브러리를 공개했다. Relay의 핵심 아이디어 — 각 UI 컴포넌트가 자기 데이터 의존성을 fragment로 선언. 컴파일러가 그 fragment들을 한 쿼리로 묶어 BFF에 보냄.
→ “컴포넌트 = BFF의 client query 단위”라는 사고. BFF 패턴을 프레임워크 레벨로 끌어올린 것.
”Falcor — 잊혀진 사촌”
Netflix가 2014년 만든 Falcor는 GraphQL과 거의 같은 발상을 했다 — 클라이언트 주도 모양 선택, BFF 역할. 그러나 Falcor는 path-based(JSON path)였고 GraphQL은 graph-based. 산업은 GraphQL을 택했고 Falcor는 2018년 Netflix 내부에서도 GraphQL로 대체됐다.
→ 비슷한 문제 인식은 흔하지만, 맞는 추상화를 찾는 게 어렵다. GraphQL이 그 자리를 차지했다.
”MFE와 GraphQL Federation”
마이크로프런트엔드(MFE)는 프런트엔드를 팀별로 쪼개는 패턴. GraphQL Federation은 백엔드를 팀별로 쪼개는 패턴. 두 패턴은 데칼코마니다 — 클라이언트 BFF가 한 그래프를 보고, 백엔드 팀이 그 그래프의 부분을 담당. 조직 구조가 API 구조에 그대로 투영된다 (Conway’s Law의 21세기 버전).
요약 + 다이어그램
BFF는 클라이언트 종류만큼 백엔드를 둔다는 패턴이고, GraphQL은 한 BFF가 N 클라이언트를 동시에 받는 자리에 가장 자연스럽다. 클라이언트가 각자 다른 모양을 한 쿼리로 요청할 수 있어, 전통 BFF의 N 인스턴스 운영 비용을 1로 줄인다. Federation을 더하면 팀 경계까지 그래프에 반영된다 — Conway’s Law의 GraphQL 버전.
다음 문서:
07-when-not-to-use-graphql.mdx— 그래도 안 쓰는 게 나은 5가지 경우.