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가지 경우.