API 게이트웨이와 BFF의 핵심 차이점 이해하기
Ethan Miller
Product Engineer · Leapcell

서론
현대 소프트웨어 아키텍처, 특히 마이크로서비스의 광범위한 채택으로 인해 서비스의 복잡한 웹을 관리하고 프론트엔드와 백엔드 간의 통신을 최적화하는 것이 매우 중요해졌습니다. 이 문제에 대한 논의에서 자주 등장하는 두 가지 아키텍처 패턴은 API 게이트웨이와 Backend for Frontend(BFF)입니다. 둘 다 중개자 역할을 하는 것처럼 보이지만, 목적, 기능 및 의도된 사용 사례는 근본적으로 다릅니다. 이러한 차이를 잘못 이해하면 최적이 아닌 아키텍처 선택, 복잡성 증가, 개발 속도 저하로 이어질 수 있습니다. 이 글은 Kong과 같은 도구로 대표되는 API 게이트웨이와 BFF 패턴 간의 근본적인 차이를 명확히 하고, 잘 설계된 시스템에서 둘 다 왜 중요한지에 대한 이유를 다양한 이유로 설명하는 것을 목표로 합니다.
중간 지점 해부
핵심 차이점을 자세히 알아보기 전에 관련된 핵심 용어에 대한 명확한 이해를 확립해 보겠습니다.
API 게이트웨이: API 게이트웨이는 정의된 일련의 마이크로서비스에 대한 단일 진입점 역할을 하는 서버입니다. 여러 클라이언트가 요구하는 공통 기능을 집계하고 적절한 백엔드 서비스로 요청을 전달합니다. 요청이 개별 서비스에 도달하기도 전에 횡단 관심사를 처리하는 마이크로서비스의 "교통 경찰"이라고 생각하십시오.
Backend for Frontend (BFF): BFF 패턴은 특정 프론트엔드 애플리케이션 또는 사용자 경험을 위해 전용 백엔드 서비스를 만드는 것을 포함합니다. 모든 클라이언트에 서비스를 제공하는 단일 모놀리식 API 대신, 각 프론트엔드(예: 웹 앱, iOS 앱, Android 앱)는 자체 맞춤형 백엔드를 갖게 됩니다. 그런 다음 이 백엔드는 다양한 다운스트림 마이크로서비스에 대한 호출을 조정하고 데이터를 변환 및 집계하여 해당 프론트엔드의 특정 요구 사항에 맞춥니다.
API 게이트웨이: 인프라 계층
API 게이트웨이는 주로 인프라 수준에서 작동합니다. 핵심 책임은 광범위하고 종종 일반적이며 클라이언트에 관계없이 모든 들어오는 요청에 적용됩니다.
원칙:
- 중앙 집중식 요청 처리: 모든 외부 요청은 게이트웨이를 통해 들어옵니다.
 - 횡단 관심사: 인증, 권한 부여, 속도 제한, 트래픽 관리, 라우팅, 로드 밸런싱, 캐싱 및 분석을 처리합니다.
 - 서비스 검색: 올바른 마이크로서비스 인스턴스를 찾아 요청을 라우팅합니다.
 - 프로토콜 번역: 다른 프로토콜(예: REST에서 gRPC로)을 변환할 수 있습니다.
 
구현 (Kong을 사용한 개념적):
UserService, ProductService, OrderService와 같은 여러 마이크로서비스가 있다고 가정해 보겠습니다. API 게이트웨이가 없으면 프론트엔드는 각 서비스의 IP 주소와 포트를 알아야 하고 공통 관심사를 수동으로 처리해야 합니다.
Kong을 사용하면 들어오는 요청을 서비스에 매핑하는 라우트를 정의할 수 있습니다.
# kong.yml _format_version: "2.1" services: - name: user-service url: http://user-service:8080 routes: - name: user-route paths: - /users strip_path: true - name: product-service url: http://product-service:8081 routes: - name: product-route paths: - /products strip_path: true plugins: - name: jwt # 예: JWT 인증을 전역적으로 적용 service: user-service config: claims_to_verify: ["exp"]
이 예에서 Kong은 /users를 user-service로, /products를 product-service로 라우팅하는 것을 처리합니다. 또한 JWT 인증과 같은 플러그인을 모든 서비스 또는 라우트에 보편적으로 적용하여 개별 마이크로서비스에서 이 책임을 덜어줄 수 있습니다.
적용 시나리오:
- 마이크로서비스 외부 노출: 외부 클라이언트에 대한 단일의 안전한 진입점을 제공합니다.
 - API 버전 관리: 서비스의 다른 버전에 대한 요청을 라우팅할 수 있습니다.
 - 보안 적용: 중앙 집중식 인증, 권한 부여 및 위협 방지.
 - 모니터링 및 분석: API 사용량 및 성능에 대한 메트릭을 수집합니다.
 
BFF: 클라이언트 맞춤형 계층
반면에 BFF 패턴은 특정 클라이언트에 대한 경험을 최적화하는 데 중점을 둔 디자인 패턴입니다. 인프라 중심이 아니라 클라이언트 중심입니다.
원칙:
- 클라이언트별 API: 각 프론트엔드는 요구 사항에 정확하게 맞춤화된 API를 받습니다.
 - 데이터 집계 및 변환: 여러 다운스트림 서비스의 데이터를 단일의 최적화된 응답으로 집계하여 클라이언트에 제공합니다.
 - 클라이언트 측 복잡성 감소: 프론트엔드에서 데이터 조작 및 오케스트레이션을 해제합니다.
 - 독립적 배포: BFF는 다른 BFF 및 핵심 마이크로서비스에 영향을 주지 않고 독립적으로 발전하고 배포될 수 있습니다.
 
구현 (웹 UI용 Node.js에 대한 개념적):
웹 애플리케이션이 사용자 프로필과 함께 최근 주문 및 즐겨찾는 제품을 표시해야 하는 시나리오를 생각해 보세요. 이 데이터는 UserService, OrderService, ProductService에서 옵니다.
// web-bff/src/index.js const express = require('express'); const axios = require('axios'); const app = express(); const port = 3001; // 이것들이 내부 마이크로서비스 URL이라고 가정합니다. 내부적으로 API 게이트웨이를 통해 라우팅될 수 있습니다. const USER_SERVICE_URL = 'http://user-service:8080'; const ORDER_SERVICE_URL = 'http://order-service:8082'; const PRODUCT_SERVICE_URL = 'http://product-service:8081'; app.get('/api/web/user-dashboard/:userId', async (req, res) => { try { const userId = req.params.userId; // 사용자 세부 정보 가져오기 const userRes = await axios.get(`${USER_SERVICE_URL}/users/${userId}`); const userData = userRes.data; // 사용자의 최근 주문 가져오기 const ordersRes = await axios.get(`${ORDER_SERVICE_URL}/orders?userId=${userId}&limit=5`); const recentOrders = ordersRes.data; // 사용자의 즐겨찾는 제품 가져오기 (제품 ID가 사용자 데이터에 있다고 가정) const productIds = userData.favoriteProductIds || []; const productPromises = productIds.map(productId => axios.get(`${PRODUCT_SERVICE_URL}/products/${productId}`) ); const productResponses = await Promise.all(productPromises); const favoriteProducts = productResponses.map(response => response.data); // 단일 웹 클라이언트 보기에 대한 데이터 집계 및 변환 const dashboardData = { user: { id: userData.id, name: userData.name, email: userData.email, }, recentOrders: recentOrders.map(order => ({ orderId: order.id, totalAmount: order.total, status: order.status })), favoriteProducts: favoriteProducts.map(product => ({ productId: product.id, name: product.name, price: product.price })) }; res.json(dashboardData); } catch (error) { console.error('사용자 대시보드 데이터 가져오기 오류:', error.message); res.status(500).json({ message: '대시보드 데이터 검색에 실패했습니다.' }); } }); app.listen(port, () => { console.log(`웹 BFF가 http://localhost:${port}에서 수신 대기 중입니다.`); });
여기서 web-bff의 /api/web/user-dashboard/:userId 엔드포인트는 여러 마이크로서비스에서 데이터를 집계하고 웹 인터페이스에 맞게 응답을 맞춤 설정하여 프론트엔드에서 보일러플레이트 코드를 줄이고 네트워크 호출을 최적화합니다.
적용 시나리오:
- 다양한 클라이언트 유형: 현저히 다른 클라이언트 애플리케이션(예: 모바일, 웹, 스마트 TV)이 있어 고유한 데이터 구조 또는 세부 정보 수준이 필요한 경우.
 - 복잡한 UI 데이터 집계: 단일 UI 화면이 많은 백엔드 서비스에서 데이터를 필요로 하여 프론트엔드가 여러 순차적인 API 호출을 하는 것을 방지하는 경우.
 - 진화하는 프론트엔드: 프론트엔드가 다른 클라이언트나 핵심 백엔드 서비스에 영향을 주지 않고 더 빠르게 반복하고 배포할 수 있습니다.
 - 클라이언트별 보안: 각 클라이언트의 특정 권한에 맞게 세분화된 액세스 제어.
 
근본적인 차이점
근본적인 차이점은 책임 범위와 추진력에 있습니다.
- 
API 게이트웨이: 서비스 중심입니다. 이는 귀하의 백엔드 서비스를 집합적으로 상호 작용하기 위한 안정적이고 안전하며 성능 좋은 진입점을 제공하는 데 중점을 둡니다. 관심사는 대부분 수평적(횡단)이며 인프라 관련입니다. 특정 프론트엔드의 특정 표시 요구 사항을 알거나 신경 쓰지 않습니다. 목표는 효율적이고 안전한 API 노출입니다.
 - 
BFF: 클라이언트 중심입니다. 이는 특정 프론트엔드 애플리케이션과의 상호 작용을 최적화하는 데 중점을 둡니다. 관심사는 수직적(클라이언트별)이며 애플리케이션 관련입니다. 관련 클라이언트의 UI/UX 요구 사항을 이해하고 데이터 집계 및 변환을 수행하여 클라이언트 측 복잡성 및 네트워크 호출을 줄이는 전문 어댑터 역할을 합니다.
 
둘 다 사용할 수 있고 종종 사용해야 합니다. API 게이트웨이는 모든 BFF(및 잠재적으로 다른 직접 마이크로서비스) 앞에 위치하여 일반적인 인증 및 속도 제한과 같은 첫 번째 계층의 공통 관심사를 처리할 수 있습니다. 그런 다음 요청은 적절한 BFF로 전달되고, 이는 더 깊은 마이크로서비스에 대한 호출을 조정합니다.
결론
API 게이트웨이와 BFF는 마이크로서비스 아키텍처에서 중요한 중개자 역할을 하지만, 목적이 다릅니다. API 게이트웨이는 전체 시스템에 대한 공통 진입점과 횡단 관심사를 관리하는 강력하고 공유된 인프라 계층입니다. Frontend를 위한 Backend는 개별 프론트엔드 애플리케이션에 최적화된 API 경험을 세심하게 제작하는 전문화된 클라이언트 주도 계층입니다. 이 핵심 차이를 이해하는 것은 다양한 클라이언트의 요구를 효과적으로 충족하는 확장 가능하고 유지 관리 가능하며 성능 좋은 시스템을 설계하는 데 중요합니다. 이들은 대안이 아니라 복원력 있고 적응력 있는 애플리케이션을 구축하기 위해 협력하는 상호 보완적인 패턴입니다.