Backend For Frontend (a.k.a. BFF)

이종경·2025년 5월 4일
0

BFF의 개념과 등장 배경

개념

Backend For Frontend(BFF)는 말 그대로 프론트엔드를 위한 백엔드를 의미합니다. 하나의 공용 API 서버 대신 각각의 클라이언트 환경에 특화된 별도의 백엔드 서비스(API)를 두는 아키텍처 패턴입니다. BFF는 프론트엔드와 백엔드 서비스들 사이에 위치한 중간 계층으로, 해당 UI에 필요한 데이터와 기능만을 모아 맞춤형 API를 제공합니다. 예를 들어 모바일 앱과 웹 사이트 각각에 그들의 요구에 최적화된 전용 백엔드(BFF)가 존재하고, 이 BFF가 내부의 여러 서비스들과 통신하여 클라이언트에 꼭 맞는 응답을 만들어줍니다. 이를 통해 프론트엔드 개발자는 불필요한 데이터나 로직 처리 없이 필요한 것만 전달받고, 백엔드 개발자는 UI 요구 사항에 따라 API를 빠르게 수정하거나 확장할 수 있어 협업 효율이 높아집니다

bff

등장 배경

BFF 패턴이 등장하게 된 배경에는 클라이언트 종류의 다양화마이크로서비스 아키텍처의 확산이 있습니다. 과거에는 웹/모바일 등 여러 클라이언트를 하나의 범용 백엔드 API로 처리하는 접근이 흔했습니다. 그러나 모바일 환경은 화면 크기, 네트워크 제약 등이 데스크톱 웹과 달라 동일한 API로 두 종류를 모두 만족시키기 어렵다는 문제가 곧 드러났습니다. 실제로 모바일 사용자는 더 적은 데이터 전송과 신속한 응답을 원하지만, 데스크톱 웹 앱은 비교적 많은 정보를 한 번에 받아볼 수 있습니다. 하나의 API로 모바일과 웹 양쪽의 요구사항을 절충해야 하면 어느 쪽에도 완벽히 최적화되지 못하는 문제가 발생합니다.

한편, 모놀리식 백엔드를 마이크로서비스(MSA)로 전환하면서 프론트엔드에서는 한 화면을 완성하기 위해 여러 서비스로부터 데이터를 호출하고 직접 조합하는 부담이 커졌습니다. 다시 말해 백엔드가 잘게 분리될수록 그 복잡성이 프론트엔드로 전가되는 양상이 나타난 것입니다. 원래 훌륭한 API 디자인 원칙 중 하나는 복잡성은 API 제공자가 흡수하고, 소비자인 프론트엔드는 단순하게 유지한다”는 것인데, 마이크로서비스 환경에서는 이 원칙이 지켜지기 어려워졌습니다.

이러한 문제를 해결하기 위해 클라이언트와 백엔드 서비스들 사이에 새로운 계층을 추가하는 아이디어가 나왔습니다.

2013년경 SoundCloud에서는 하나의 공용 API로 모든 클라이언트(사내 제품 및 서드파티)를 지원하던 방식을 버리고, 클라이언트별 전용 API 서비스를 도입하기 시작했는데 이를 “Backends for Frontends (BFF)”라 명명했습니다. BFF를 도입하면 모바일과 웹 각각 자신만의 백엔드(BFF)를 통해 필요한 데이터만 제공받을 수 있으므로, 앞서 언급한 클라이언트별 차이를 효과적으로 관리할 수 있게 되었습니다.

다음 그림은 BFF 패턴을 적용한 전용 API 구조를 보여줍니다. 모바일 앱과 데스크톱 웹에 각기 별도의 BFF 서버를 두고, 각 BFF가 자신이 담당하는 클라이언트에 필요한 데이터만을 여러 내부 서비스에서 취합하여 제공합니다. 이렇게 클라이언트별로 백엔드가 분리되면 모바일과 웹이 서로 영향을 주지 않고 각자 최적화된 API를 가질 수 있어 유연성이 높아집니다.

bff structure


BFF 목적

  1. 클라이언트 맞춤형 데이터 제공
    • 클라이언트가 필요한 데이터만 전달함으로써 성능 개선
    • 클라이언트에서의 복잡한 데이터 처리 로직 감소
  2. 클라이언트와 백엔드의 결합도 감소
    • 백엔드 서비스의 변경사항으로부터 클라이언트 보호
    • 각 프론트엔드에서 필요한 API를 독립적으로 관리 가능
  3. 성능 및 효율성 향상
    • 여러 API 호출을 하나의 BFF 계층에서 병합하여 네트워크 비용을 절약
    • 캐싱, 데이터 가공, 인증 및 권한 관리 등의 공통 작업을 처리하여 프론트엔드의 부담 완화
  4. 독립적 개발 및 배포
    • 각 프론트엔드 전용 BFF를 통해 독립적인 배포 사이클 제공
    • 서비스의 배포 주기 단축, 지속적 배포(CD) 활성화 가능

BFF 구조

BFF는 시스템 전체 구조에서 프론트엔드와 내부 백엔드 서비스들 사이에 위치합니다. 클라이언트(프론트엔드)가 BFF에 요청을 보내면, BFF는 내부의 여러 서비스들과 통신하여 데이터를 취합 및 가공한 후, 클라이언트에 꼭 맞는 형태로 응답을 반환합니다. 이러한 BFF 서버는 보안상 내부 네트워크 안에서 동작하는 서버이며, 프론트엔드 팀이 직접 관리하는 경우가 많습니다. 이를 통해 프론트엔드 요구사항 변화에 따른 API 수정이 수월해지고, 프론트엔드 앱과 BFF를 동일한 주기로 배포하는 것도 가능합니다.

BFF 구조 종류

  1. 다중 클라이언트 ↔ BFF ↔ MSA Server
  2. 다중 클라이언트 ↔ 클라이언트별 BFF ↔ MSA Server
  3. 클라이언트 ↔ 기능별 BFF ↔ MSA Server
    • 네이버를 예를 들면, Auth BFF, Blog BFF, Pay BFF 등

BFF 동작 흐름

일반적인 BFF 동작 흐름은 아래와 같습니다.

  1. 프론트엔드에서 BFF의 API 엔드포인트를 호출합니다. 이때 BFF는 REST API 형태이든 GraphQL API 형태이든 상관 없으며, 클라이언트가 필요한 데이터를 요청합니다.
  2. BFF 서버가 요청을 수신하면, 관련된 백엔드 마이크로서비스들(또는 기존 모놀리식 서버)의 API를 필요한 만큼 호출합니다.
  3. BFF는 각 서비스로부터 받은 데이터를 취합하고 변환하여 프론트엔드가 필요로 하는 형태로 가공합니다. 불필요한 필드는 제거하고, 여러 서비스의 응답을 하나로 합치거나, 프론트엔드에서 쓰기 좋도록 포멧을 변경하는 작업을 합니다.
  4. 최종적으로 BFF는 가공된 맞춤 응답 데이터를 클라이언트에 반환합니다. 프론트엔드는 하나의 응답으로 필요한 정보를 모두 얻을 수 있고, 추가적인 데이터 조합 로직을 프론트엔드에 구현할 필요가 없어집니다.

프론트엔드는 BFF를 마치 백엔드처럼 투명하게 호출하고, BFF는 백엔드들의 복잡한 세부사항을 감추는 인터페이스로 동작합니다. 예를 들어, 인증/인가가 필요한 경우 BFF에서 토큰을 검사한 뒤 내부 서비스 호출에 포함시키고, 오류가 발생하면 BFF가 이를 적절히 처리하여 프론트엔드에 이해하기 쉬운 오류 메시지로 변환하는 식입니다. 요약하면, BFF는 프론트엔드 친화적인 API 게이트웨이 역할을 하며, 데이터 집계, 포맷 변환, 인증 처리 등을 전담합니다.

BFF의 장단점

BFF 패턴을 도입하면 얻을 수 있는 주요 이점은 다음과 같습니다.

장점

  • 클라이언트 맞춤형 API 제공
    • 각 프론트엔드에 특화된 API를 설계하여 오버페칭/언더페칭을 줄이고 불필요한 데이터 전송이나 다중 호출을 최소화할 수 있습니다. 그 결과 API 호출 횟수가 감소하고 응답 지연이 줄어 UX를 향상할 수 있습니다.
  • 프론트엔드와 백엔드의 분리
    • BFF 레이어가 양측을 분리시켜 줌으로써, 백엔드 서비스 교체나 변경이 프론트엔드에 영향을 덜 주게 됩니다. 각 클라이언트 전용 API가 있으므로 특정 클라이언트를 위한 수정이 다른 클라이언트에 파급되지 않습니다. 또한 프론트엔드 팀이 요구에 맞게 BFF를 직접 조정할 수 있어 개발 속도가 향상됩니다.
  • 디바이스 최적화
    • 서로 다른 디바이스의 능력과 제약에 맞게 최적화된 응답을 제공할 수 있습니다. 예를 들어 모바일 BFF는 저용량 이미지나 압축된 데이터 등 데이터 효율을 우선하고, 데스크톱 BFF는 한 번에 고해상도 이미지와 상세한 정보를 추가적으로 전달하여 콘텐츠 제공에 초점을 맞출 수 있습니다.

단점

  • 중복 구현 및 코드 중복
    • 클라이언트별로 BFF를 여러 개 운영하면 비슷한 로직이 중복될 수 있습니다. 예를 들어 공통으로 필요한 데이터 가공이나 인증, 캐싱 로직을 각 BFF마다 따로 구현해야 할 수 있어 코드 중복과 일관성 관리 문제가 생길 수 있습니다.
  • 리소스 및 운영 부담
    • 클라이언트마다 별도 백엔드를 둔다면, 그만큼 서버 자원과 인프라가 더 필요합니다. 여러 BFF 서비스를 각각 유지하려면 호스팅 비용도 증가합니다.
  • 과설계 위험
    • 모든 프로젝트에 BFF가 정답은 아닙니다. 클라이언트가 하나뿐이거나 백엔드가 이미 충분히 단순한 경우, BFF를 추가하면 복잡성만 늘어나고 실익은 적을 수 있습니다

BFF 적용 사례

BFF 패턴의 효과를 이해하기 위해 프론트엔드 코드 관점에서 모놀리식 구조와 BFF 구조를 비교해보겠습니다. 예를 들어, 어떤 서비스에서 사용자 프로필 화면에 최근 주문 내역을 함께 표시해야 한다고 가정해보겠습니다.

모놀리식 구조

BFF가 없던 기존 구조에서는 프론트엔드가 필요한 정보를 각기 다른 서비스 API들로부터 직접 받아와 클라이언트 측에서 데이터를 조합해야 했습니다. 사용자 정보와 주문 정보를 한 화면에 보여주려면, 프론트엔드 코드에서 두 개 이상의 API를 호출하고 그 결과를 합쳐서 사용해야 합니다. 아래는 이러한 상황에서의 예시 코드입니다:

// BFF 없이 두 개의 API를 호출하여 데이터 통합
useEffect(() => {
  async function loadData() {
    // 1. 사용자 정보 API 호출
    const userRes = await fetch('/api/user/123');
    const userData = await userRes.json();
    // 2. 주문 목록 API 호출
    const ordersRes = await fetch(`/api/orders?userId=123`);
    const ordersData = await ordersRes.json();
    // 3. 데이터 합성: 사용자 객체에 최근 주문 목록 추가
    userData.recentOrders = ordersData.slice(0, 5);
    setUserProfile(userData);
  }
  loadData();
}, []);

위 코드에서 프론트엔드는 사용자 정보(/api/user/123)와 주문 목록(/api/orders?userId=123) 두 엔드포인트를 순차적으로 호출한 뒤, 응답 받은 데이터를 자바스크립트 단에서 합쳐서 사용하고 있습니다. 이렇듯 BFF가 없는 경우 프론트엔드에 상당한 비즈니스 로직이 포함되며, 필요한 데이터가 늘어날수록 이러한 통합 작업도 복잡해집니다.

BFF 구조

BFF를 도입한 후에는 프론트엔드가 복잡한 로직을 수행할 필요 없이 단일 BFF 엔드포인트 호출로 통합된 데이터를 받을 수 있습니다. 위와 동일한 기능을 BFF 패턴을 사용하여 구현한 예시는 다음과 같습니다.

// BFF를 통해 하나의 엔드포인트에서 통합 데이터 받기
useEffect(() => {
  async function loadData() {
    // BFF 서버가 통합된 사용자+주문 데이터를 제공
    const res = await fetch('/api/bff/user-dashboard/123');
    const dashboardData = await res.json();
    setUserProfile(dashboardData);
  }
  loadData();
}, []);

위 코드에서는 프론트엔드가 BFF에 해당하는 단일 엔드포인트(/api/bff/user-dashboard/123)만 호출하면 됩니다. BFF 서버가 내부적으로 사용자 서비스와 주문 서비스 등 여러 곳에서 데이터를 가져와 대시보드에 필요한 형태로 가공한 뒤 응답했기 때문에, 프론트엔드에서는 추가적인 처리 없이 결과를 바로 활용할 수 있습니다. 이처럼 BFF를 활용하면 프론트엔드 코드가 훨씬 단순해지고 본연의 화면 구성에 집중할 수 있게 됩니다.


BFF 의의

BFF의 의의는 프론트엔드와 백엔드의 역할 분리 및 협업 향상 측면에서도 강조됩니다. 프론트엔드 팀은 자신들의 BFF를 통해 백엔드 구현 세부사항에 얽매이지 않고 사용자 경험 개선에 집중할 수 있고, 백엔드 팀은 코어 서비스 로직에 집중하면서도 각 클라이언트별 요구사항을 BFF 레이어를 통해 유연하게 수용할 수 있습니다. 그 결과 개발 효율성시스템 품질 모두 향상되는 효과를 얻습니다.

또한 BFF는 마이크로서비스로 분산된 백엔드 시스템과 최종 사용자 사이를 효율적으로 이어주는 일종의 다리 역할을 합니다. 백엔드가 여러 마이크로서비스로 쪼개어져 있더라도, 사용자에게는 BFF를 통해 하나로 통합된 일관된 인터페이스를 제공할 수 있으므로 분산 시스템의 복잡성을 숨기고 안정적인 서비스를 제공할 수 있습니다. 오늘날 데이터 통합 문제를 해결하기 위해 GraphQL 등의 기술이 각광받고 있지만, BFF 패턴은 팀 경계와 서비스 책임을 명확히 반영하면서도 비교적 단순한 방식으로 이러한 문제를 해결한다는 점에서 여전히 유용한 접근입니다. 실제로 API Gateway와 BFF를 조합하여 사용하거나, 요구에 따라 REST, gRPC, GraphQL 등 다양한 API 스타일을 BFF 레이어에 적용하는 등 발전적인 활용도 이루어지고 있습니다.

마지막으로, BFF를 운용할 때의 유의사항도 짚고 넘어가야 합니다. BFF에 지나치게 많은 역할과 로직을 몰아넣으면 여러 BFF 간에 중복된 비즈니스 기능이 구현되거나 유지보수가 어려워지는 문제가 생길 수 있습니다. 따라서 공통 비즈니스 로직은 기존의 마이크로서비스들이 담당하고, BFF는 클라이언트별 프레젠테이션 계층의 조율에 집중하는 경량화된 레이어로 유지하는 것이 바람직합니다. 이러한 균형을 잘 맞춘다면 BFF는 앞으로도 다양한 클라이언트 환경에서 일관된 사용자 경험효율적인 개발 프로세스를 실현하는 데 기여하는 중요한 아키텍처 패턴으로서 의의를 가질 것입니다.

참고
MS - Backends for Frontends pattern
AWS- Backends for Frontends Pattern
Backend-for-Frontend (BFF) Architecture
Backend for frontend (BFF) pattern— why do you need to know it?
Service Architecture at SoundCloud — Part 1: Backends for Frontends
WebFlux와 코루틴으로 BFF(Backend For Frontend) 구현하기
API 수정 요청? 이제 그만! BFF로 프론트엔드에 책임 떠넘기기 🙅‍♂️💼

profile
작은 성취들이 모여 큰 결과를 만든다고 믿으며, 꾸준함을 바탕으로 개발 역량을 키워가고 있습니다

0개의 댓글