BFF(프런트엔드용 백엔드) 패턴 이해: 간단한 가이드
마이크로서비스 아키텍처에서 우리 시스템은 사용자 서비스, 주문 서비스, 제품 서비스 등 수십 개의 작고 집중적인 서비스로 분류됩니다.
그러나 이 정보를 사용자에게 표시하는 경우 장치마다 요구 사항이 매우 다릅니다. 고속 데스크톱 컴퓨터의 웹 브라우저에는 테이블, 사이드바, 그래프로 가득 찬 풍부한 대시보드가 필요합니다. 느린 셀룰러 네트워크의 모바일 앱은 대역폭과 배터리를 절약하기 위해 간단하고 가벼운 레이아웃을 원합니다. 스마트워치 앱에는 한 줄의 텍스트만 필요할 수 있습니다.
이러한 프런트엔드가 모두 동일한 백엔드 API를 쿼리하는 경우 누군가 타협해야 합니다. 모바일 앱은 막대한 양의 쓸모 없는 데이터를 다운로드해야 하거나, 웹 앱은 필요한 모든 것을 가져오기 위해 수십 개의 별도 네트워크 요청을 해야 합니다.
이것이 바로 BFF(Backend for Frontend) 패턴으로 해결된 문제입니다. 이 가이드에서는 이 패턴을 간단한 단어로 설명하고 실제 비유를 살펴보고 이를 표준 API 게이트웨이와 비교하고 실용적인 코드 구현을 안내합니다.
실제 비유: 레스토랑 메뉴
세 가지 매우 다른 유형의 식사를 제공하는 레스토랑을 상상해 보십시오.
- 자세한 재료 목록이 포함된 전체 5코스 시식 메뉴를 원하는 음식 평론가.
- 기차에서 미리 포장된 빠른 간식을 먹고 싶은 바쁜 통근자.
- 매운 재료 없이 소량으로 간단한 어린이 식사를 원하는 아이.
레스토랑에 세 가지 옵션을 모두 자세히 나열한 단일 메뉴만 있다면 엄청난 일이 될 것입니다. 통근자는 5가지 코스 요리법을 읽는 데 시간을 낭비할 것이고, 아이의 부모는 간단한 음식 옵션을 찾는 데 어려움을 겪을 것입니다.
대신 레스토랑에서는 시식 메뉴, 간편 포장 메뉴, 어린이 메뉴 등 세 가지 맞춤 메뉴를 인쇄합니다.
각 메뉴는 동일한 주방(마이크로서비스)에서 가져오지만 해당 고객(클라이언트)을 위해 특별히 선택 항목의 형식과 크기를 지정합니다.
이 시나리오에서는:
- 주방은 마이크로서비스(사용자, 카탈로그, 결제)를 나타냅니다.
- 맞춤 메뉴는 귀하의 절친(웹 절친, 모바일 절친, 시계 절친)입니다.
- 식사객은 프런트엔드(데스크톱 브라우저, 모바일 앱, 스마트워치)입니다.
문제: “모든 용도에 적용 가능한” API
마이크로서비스가 처음 대중화되었을 때 많은 팀에서는 모든 프런트엔드 클라이언트를 처리하기 위해 단일 공유 API 게이트웨이를 구축했습니다.
단일 진입점이 훌륭하지만 공유 API에는 여러 가지 확장 병목 현상이 발생합니다.
- 모바일용 페이로드 블로트: 데스크톱 웹 앱에는 사용자의 주문 내역, 청구서 수신 주소, 프로필 사진, 적립 포인트가 필요합니다. 모바일 앱에는 ‘마지막 주문: 배송됨’만 표시하면 됩니다. 공유 API를 사용하면 모바일 앱이 전체 프로필 페이로드를 다운로드하므로 귀중한 데이터가 낭비되고 로드 시간이 느려집니다.
- API 병목 현상: 단일 팀이 공유 게이트웨이의 병목 현상이 됩니다. iOS 팀이 작은 레이아웃 필드를 변경하려는 경우 공유 게이트웨이 팀이 새 버전을 배포할 때까지 기다려야 하므로 개발 주기가 느려집니다.
- 다양한 보안 요구 사항: 웹 브라우저에는 XSS(Cross-Site Scripting)를 방지하기 위해 쿠키 기반 세션이 필요할 수 있는 반면, 모바일 앱은 토큰 기반 OAuth 헤더를 선호합니다. 단일 서버에서 두 가지를 모두 처리하면 복잡하고 지저분한 코드가 생성됩니다.
해결책: BFF 패턴
BFF 패턴은 모든 장치에 대해 하나의 거대한 게이트웨이를 만드는 대신 각 프런트엔드 애플리케이션에 대해 하나의 전용 백엔드 서버를 구축하는 것을 옹호합니다.
당신은 다음을 갖게 될 것입니다 :
- 웹 BFF: 데스크톱 브라우저의 요청을 처리합니다. 전체 프로필, 제품 카탈로그 및 자세한 결제 정보를 집계합니다.
- 모바일 BFF: iOS 및 Android 앱의 요청을 처리합니다. 데이터를 집계하고, 불필요한 필드를 필터링하고, 최종 응답을 압축하여 빠른 성능을 보장합니다.
API 게이트웨이와 BFF: 차이점은 무엇인가요?
이 두 패턴은 모두 클라이언트와 마이크로서비스 사이에 있기 때문에 혼동하는 것이 일반적입니다. 차이점은 다음과 같습니다.
| 기능 | 일반 API 게이트웨이 | 프론트엔드용 백엔드(BFF) |
|---|---|---|
| 게이트웨이 수 | 일반적으로 전체 시스템에 하나입니다. | 여러(클라이언트 장치 유형별로 하나씩). |
| 책임 | 높은 수준의 라우팅, 속도 제한 및 글로벌 보안. | 특정 프런트엔드에 대한 데이터 집계 및 페이로드 조정 |
| 소유권 | 전담 백엔드/플랫폼 인프라 팀이 관리합니다. | 해당 앱을 빌드하는 프런트엔드 팀이 관리합니다. |
| 맞춤화 | 낮은. 변경 사항은 모든 클라이언트에 영향을 미칩니다. | 높은. 변경 사항은 하나의 클라이언트 애플리케이션에만 영향을 미칩니다. |
실용적인 구현(Node.js/Express)
이것이 실제로 어떻게 작동하는지 이해하기 위해 간단한 Node.js 예제를 작성해 보겠습니다.
내부적으로 실행되는 두 개의 마이크로서비스가 있다고 가정해 보세요.
- 사용자 서비스(기본 사용자 프로필 정보 반환)
- 주문 서비스(자세한 내용이 포함된 주문 목록 반환)
우리는 데스크톱 사이트와 모바일 앱을 다르게 제공하기 위해 웹 BFF와 모바일 BFF를 구축하고 싶습니다.
1. 공유 마이크로서비스 모의
먼저, 두 가지 기본 마이크로서비스에 대한 모의 데이터는 다음과 같습니다.
// Internal User Service Response
const userProfile = {
id: 42,
username: "dev_coder",
email: "coder@ghaznix.com",
avatarUrl: "https://ghaznix.com/avatars/42.png",
preferences: { theme: "light", newsletter: true }
};
// Internal Order Service Response
const orderHistory = [
{ id: "ORD-99", date: "2026-06-25", items: ["Laptop", "Mouse"], status: "Shipped", tax: 15.00, total: 1215.00 },
{ id: "ORD-88", date: "2026-05-12", items: ["Keyboard"], status: "Delivered", tax: 5.00, total: 105.00 }
];
2. 웹 BFF(완전한 세부 페이로드 반환)
Web BFF는 데스크탑 화면에 표시할 공간이 충분하기 때문에 모든 필드를 집계합니다.
const express = require('express');
const webBff = express();
webBff.get('/dashboard', (req, res) => {
// Web needs everything: profile + full order details + settings
const responsePayload = {
user: {
username: userProfile.username,
email: userProfile.email,
avatar: userProfile.avatarUrl,
theme: userProfile.preferences.theme
},
orders: orderHistory // Send all order details, taxes, and items
};
res.json(responsePayload);
});
webBff.listen(3001, () => console.log('Web BFF running on port 3001'));
3. 모바일 BFF(최소화되고 집계된 페이로드 반환)
모바일 BFF는 불필요한 필드(예: 이메일, 설정, 세금)를 필터링하고 주문 항목을 집계하여 대역폭을 절약합니다.
const express = require('express');
const mobileBff = express();
mobileBff.get('/dashboard', (req, res) => {
// Mobile only wants: username, avatar, and summary of the latest order
const latestOrder = orderHistory[0];
const responsePayload = {
user: {
username: userProfile.username,
avatar: userProfile.avatarUrl
},
latestOrderStatus: {
orderId: latestOrder.id,
status: latestOrder.status,
date: latestOrder.date,
itemCount: latestOrder.items.length // Send count instead of array list
}
};
res.json(responsePayload);
});
mobileBff.listen(3002, () => console.log('Mobile BFF running on port 3002'));
페이로드 크기 비교
- 웹 BFF 응답 크기: 중첩된 구성, 기본 설정, 전체 주문 목록, 세금, 항목 등을 포함합니다(약 400바이트).
- 모바일 BFF 응답 크기: 핵심을 나타내는 6개의 키-값 쌍만 포함합니다. (약 120바이트—네트워크 크기 70% 감소!)
BFF 패턴의 장점과 단점
BFF 패턴은 매우 효과적이지만 다음과 같은 장단점이 있습니다.
| 어드밴티지(프로) | 단점(단점) |
|---|---|
| 최적화된 클라이언트 성능: 클라이언트는 필요한 정확한 데이터만 로드하므로 배터리 소모와 메모리 사용량이 줄어듭니다. | 코드 중복: 여러 BFF 코드베이스에서 유사한 데이터 가져오기 논리를 작성하게 될 수도 있습니다. |
| 빠른 릴리스 주기: 모바일 프런트엔드 팀은 웹 개발자와 협력할 필요 없이 BFF 서버를 업데이트할 수 있습니다. | 서버 수 증가: 이제 하나의 API 게이트웨이를 관리하는 대신 여러 BFF 서비스를 배포하고 관리해야 합니다. |
| 단순화된 프런트엔드 코드: 클라이언트는 복잡한 정렬, 필터링 또는 병합 논리를 처리할 필요가 없습니다. 단지 수신한 JSON을 표시할 뿐입니다. | 보안 관리: SSL/TLS 인증, 속도 제한 규칙 및 방화벽은 여러 게이트웨이에서 관리되어야 합니다. |
결론
BFF(프런트엔드용 백엔드) 패턴은 웹, 모바일 및 IoT 장치와 같은 여러 클라이언트 유형을 제공하는 시스템을 위한 강력한 아키텍처입니다. 각 특정 프런트엔드에 대한 맞춤형 게이트웨이를 생성하면 클라이언트 개발을 분리하고 페이로드 크기를 최소화하며 더 빠르고 응답성이 뛰어난 사용자 경험을 제공할 수 있습니다.
시스템에 단일 웹 애플리케이션만 있는 경우 공유 API 게이트웨이로 충분합니다. 그러나 웹 애플리케이션과 함께 모바일 앱이나 특수 장치 경험을 구축하기 시작하는 순간 BFF를 구현하는 것이 프런트엔드 및 백엔드 아키텍처를 깔끔하고 최적화되며 독립적으로 유지하는 가장 좋은 방법입니다.