BFF
고친 사람 github-actions[bot]
BFF 는 화면 종류마다 그 화면만 상대하는 서버를 하나씩 세웁니다. 웹 화면에는 웹용 서버가, 모바일 앱에는 모바일용 서버가 붙습니다. 이 서버가 뒤쪽 여러 서비스에 대신 물어봅니다. 받은 답은 화면이 바로 쓸 모양으로 묶어 줍니다.
쉽고 빠른 이해
BFF 는 화면 종류 하나를 위해 일하는 전용 서버입니다. 웹 화면에 하나, 모바일 앱에 하나를 둡니다. 모바일 앱의 주문 페이지가 주문 상태와 상품 이름과 리뷰 수를 원하면, 모바일용 서버가 세 서비스에 물어 그 세 값만 한 번에 돌려줍니다.
모든 화면이 공용 서버 하나를 같이 쓰면 서로 발목을 잡습니다. 웹은 많이 받고 싶고 모바일은 적게 받고 싶습니다. 한쪽에 맞추면 다른 쪽이 필요 없는 값을 받거나 여러 번 다시 물어야 합니다.
순서는 이렇습니다.
- 화면이 자기 전용 서버에 한 번 요청합니다
- 전용 서버가 뒤쪽 서비스 여럿을 부릅니다
- 받은 답을 화면에 맞게 줄이고 합쳐서 돌려줍니다
대가가 있습니다. 화면 종류만큼 서버가 늘어납니다. 비슷한 코드도 서버마다 따로 생깁니다.
그래서 화면 종류가 하나뿐이거나 여러 화면이 거의 같은 값을 쓰면 BFF 는 필요 없습니다. 공용 서버 하나로 충분합니다.
상세
이 절은 먼저 모든 화면이 공용 서버 하나를 같이 쓸 때 무엇이 문제인지 봅니다. 그다음 화면 종류마다 서버를 하나씩 두면 그 문제가 어떻게 풀리는지를 그림과 코드로 따라갑니다. 끝으로 이 설계가 치르는 값과 쓰지 않을 때를 짚습니다.
예로는 쇼핑몰 하나를 끝까지 씁니다. 이 쇼핑몰에는 웹 화면과 모바일 앱이 있습니다. 뒤에는 주문 서비스, 상품 서비스, 리뷰 서비스가 서 있습니다.
이 문서에서 「화면 종류」는 웹 화면이나 모바일 앱처럼 사용자가 쓰는 프로그램 하나를 말합니다. 「페이지」는 그 안의 한 장입니다. 주문 내역을 보여 주는 장이 그 예입니다.
BFF 라는 이름
BFF 는 Backend for Frontend 의 줄임말입니다. 우리말로는 프론트엔드를 위한 백엔드라고 풉니다.
프론트엔드와 백엔드는 짝을 이루는 말입니다. 프론트엔드는 사용자가 직접 보는 화면 쪽 코드입니다. 백엔드는 그 화면에 데이터를 대 주는 서버 쪽 코드입니다.
이름에는 화면 종류 하나를 위해 서버를 둔다는 뜻이 담겨 있습니다. 화면 종류마다 하나씩 생기므로 복수형으로 Backends for Frontends 라고 쓰기도 합니다. 두 표기는 같은 것을 가리킵니다.
공용 API 하나로 버틸 때
BFF 가 없으면 모든 화면이 공용 서버 하나의 API(Application Programming Interface)를 같이 부릅니다. API 는 프로그램이 다른 프로그램을 부를 때 지키는 약속입니다. 이 문서에서는 서버가 밖에 열어 둔 요청 주소와 응답 모양을 가리킵니다.
공용 API 는 어느 화면에도 치우치지 않게 만듭니다. 주문 하나를 물으면 주문에 관한 값을 전부 돌려줍니다. 웹 화면은 그 값을 거의 다 씁니다. 모바일 앱은 그중 셋만 씁니다.
모바일 앱은 쓰지도 않을 값까지 받아 옵니다. 필요 없는 값까지 받아 오는 일을 오버페칭이라고 부릅니다. 모바일 망은 데이터 요금이 들어서 이 낭비가 눈에 띕니다.
반대 방향의 문제도 있습니다. 주문 페이지 하나를 그리려면 주문 API, 상품 API, 리뷰 API 를 차례로 불러야 합니다. 한 번으로 모자라 여러 번 부르는 일을 언더페칭이라고 부릅니다. 앱과 서버 사이를 오가는 횟수가 늘어난 만큼 화면이 늦게 뜹니다.
고칠 때도 걸립니다. 모바일 화면에 값 하나를 더하려면 공용 API 를 고쳐야 합니다. 그 API 는 웹도 쓰므로 웹 팀과 맞춰야 합니다. 두 팀의 일정이 서로 묶입니다.
화면마다 서버를 하나씩
BFF 는 이 공용 API 대신 화면 종류별 서버를 하나씩 세웁니다. 웹 화면은 웹용 BFF 만 부릅니다. 모바일 앱은 모바일용 BFF 만 부릅니다. 뒤쪽 서비스는 바뀌지 않습니다.
flowchart TD
W["웹 화면"] --> WB["웹용 BFF"]
M["모바일 앱"] --> MB["모바일용 BFF"]
subgraph S["뒤쪽 서비스"]
O["주문 서비스"]
P["상품 서비스"]
R["리뷰 서비스"]
end
WB --> S
MB --> S
두 BFF 는 같은 서비스를 부릅니다. 다른 것은 돌려주는 모양입니다. 웹용 BFF 는 웹 화면이 원하는 대로, 모바일용 BFF 는 모바일 화면이 원하는 대로 답을 만듭니다.
이런 구조는 마이크로서비스에서 자주 보입니다. 마이크로서비스는 기능마다 작은 서비스를 따로 두고 그 여럿으로 시스템을 짓는 방식입니다. 서비스가 잘게 쪼개질수록 페이지 하나가 불러야 할 곳이 늘어나서 모아 줄 서버가 아쉬워집니다.
왕복이 옮겨 가는 곳
BFF 를 두면 앱이 서버와 오가는 횟수가 한 번으로 줍니다. 여러 서비스를 부르는 일은 BFF 가 서버들 사이에서 대신 합니다. 아래 그림은 모바일 앱의 주문 페이지 한 장을 그리는 순서입니다.
sequenceDiagram
participant 앱 as 모바일 앱
participant B as 모바일용 BFF
participant 주문 as 주문 서비스
participant 상품 as 상품 서비스
participant 리뷰 as 리뷰 서비스
앱->>B: 주문 페이지를 달라
B->>주문: 주문 상태와 상품 번호
주문-->>B: 배송 중 · 상품 7
B->>상품: 상품 7의 이름
상품-->>B: 티셔츠
B->>리뷰: 상품 7의 리뷰 수
리뷰-->>B: 12
B-->>앱: 화면에 쓸 값만 한 번에
Note over 앱,B: 모바일 망은 한 번만 건넌다
앱과 BFF 사이에는 요청과 응답이 한 번씩만 오갑니다. 나머지 호출은 BFF 와 서비스 사이에서 일어납니다. 서버끼리는 대개 같은 데이터센터 안에 있어서 이 왕복은 모바일 망을 건너는 왕복보다 훨씬 짧습니다.
코드로 본 BFF
모바일용 BFF 안의 주문 페이지 처리 하나를 자바 코드로 보겠습니다. order·product·review 는 각각 주문·상품·리뷰 서비스를 부르는 객체입니다. OrderPage 는 앱에 돌려줄 세 값을 담는 묶음입니다.
item 에는 주문된 상품의 번호가 담깁니다. 각 줄 오른쪽 주석이 그 호출이 돌려준 값입니다.
OrderPage orderPage(long id) {
var state = order.state(id); // 배송 중
var item = order.item(id); // 7
var name = product.name(item); // 티셔츠
var count = review.count(item); // 12
return new OrderPage(state, name, count);
}
이 함수는 세 서비스를 불러 값 셋을 모읍니다. 주문 서비스는 주문에 관한 값을 많이 갖고 있습니다. 이 함수는 그중 상태와 상품 번호만 꺼냅니다. 앱이 받는 응답에는 상태, 이름, 리뷰 수 세 값만 들어 있습니다.
서로 기다릴 필요가 없는 호출은 동시에 보내기도 합니다. 위 코드에서 상품 이름과 리뷰 수는 상품 번호만 있으면 따로 물을 수 있습니다. 둘을 함께 보내면 응답이 빨라집니다.
BFF 가 맡는 일
BFF 가 하는 일은 대개 아래 셋으로 모입니다. 셋 모두 화면 종류 하나의 사정에 맞추는 일입니다.
| 일 | 무엇을 하나 | 쇼핑몰 예 |
|---|---|---|
| 모으기 | 여러 서비스를 불러 한 응답으로 합친다 | 주문 · 상품 · 리뷰를 한 번에 |
| 다듬기 | 화면이 안 쓰는 값을 빼고 모양을 바꾼다 | 상품 설명 전문을 빼고 이름만 |
| 버티기 | 한 서비스가 죽어도 나머지로 답한다 | 리뷰 서비스가 안 되면 리뷰 수만 비운다 |
셋째가 놓치기 쉽습니다. 서비스 하나가 응답하지 않았다고 페이지 전체를 실패로 돌리면 사용자는 아무것도 못 봅니다. 이렇게 일부만 실패한 상태를 부분 실패라고 부릅니다. BFF 는 그 값만 비워 두고 나머지를 돌려줍니다. 화면은 빈 값이 와도 깨지지 않게 만듭니다.
응답이 늦는 서비스를 끝없이 기다리지 않도록 타임아웃도 BFF 가 겁니다. 정해 둔 시간이 지나면 그 호출을 포기하고 그 값만 비웁니다.
누가 BFF 를 맡나
BFF 는 대개 그 화면을 만드는 팀이 맡습니다. 모바일 팀이 모바일용 BFF 를, 웹 팀이 웹용 BFF 를 고치고 배포합니다. 화면에 값 하나를 더할 때 다른 팀과 일정을 맞추지 않아도 됩니다.
얼마나 잘게 나눌지에 대한 경험칙이 하나 있습니다. 사용자가 겪는 경험 하나에 BFF 하나를 둡니다. 여기서 경험 하나는 페이지 한 장이 아니라 웹이나 모바일 앱 같은 화면 종류 하나입니다. 안드로이드 앱과 아이폰 앱이 같은 화면을 보이고 같은 팀이 맡으면 BFF 하나를 같이 써도 됩니다.
API 게이트웨이와 가르는 선
API 게이트웨이도 화면과 서비스 사이에 섭니다. API 게이트웨이는 모든 요청이 지나는 단일 입구입니다. 화면 종류와 상관없이 모든 요청에 걸 일을 한곳에서 처리합니다. 요청한 사람이 누구인지 확인하는 인증, 한 사용자가 너무 자주 부르지 못하게 막는 속도 제한이 그런 일입니다.
BFF 는 화면 종류마다 하나씩 있습니다. 그리고 그 화면만의 사정을 담습니다. 그래서 BFF 를 화면 종류별로 갈라 둔 API 게이트웨이로 보기도 합니다. 둘을 함께 쓰면 게이트웨이가 맨 앞에서 인증을 합니다. 그 뒤에서 BFF 가 화면별 응답을 만듭니다.
치르는 값
BFF 를 두면 서버가 화면 종류만큼 늘어납니다. 서버마다 배포하고 지켜봐야 할 일도 같이 늘어납니다.
비슷한 코드가 BFF 마다 따로 생깁니다. 주문과 상품을 합치는 코드가 웹용과 모바일용에 둘 다 들어갑니다. 공통 라이브러리로 빼면 중복은 줄어듭니다. 대신 두 BFF 가 그 라이브러리에 함께 매여 따로 고치기 어려워집니다.
요청이 지나는 단계도 하나 늘어납니다. 앱과 서비스 사이에 서버 하나가 더 끼므로 그만큼 지연이 붙습니다. 앞에서 본 왕복 절약이 이 비용보다 커야 남는 장사입니다.
비즈니스 로직이 BFF 로 새기 쉽습니다. 비즈니스 로직은 할인 계산이나 재고 확인처럼 화면과 상관없이 지켜야 하는 업무 규칙입니다. 이런 규칙이 BFF 에 들어가면 웹과 모바일이 서로 다르게 계산하게 됩니다. 그래서 업무 규칙은 뒤쪽 서비스에 둡니다. BFF 는 모으고 다듬는 일만 맡습니다.
쓰지 않을 때
화면이 웹 하나뿐이면 BFF 를 따로 둘 까닭이 약합니다. 공용 API 를 그 화면에 맞추면 되기 때문입니다. 여러 화면이 거의 같은 값을 쓸 때도 공용 API 하나로 충분합니다.
화면이 원하는 값을 스스로 고르게 하는 방법도 있습니다. GraphQL은 화면 쪽 코드가 받고 싶은 필드를 요청에 적어 보내는 질의 언어입니다. 화면마다 서버를 두는 대신 서버 하나가 화면마다 다른 모양을 내줍니다. 두 방법을 섞어 BFF 안에서 GraphQL 을 쓰기도 합니다.
관련 항목
BFF 가 속하는 아키텍처 양식
마이크로서비스 · 모놀리스 · 소프트웨어 아키텍처 · 아키텍처 양식 · 서비스 지향 아키텍처 · 분산 모놀리스
BFF 와 함께 앞단에 서는 중개자
API 게이트웨이 · 게이트웨이 · 리버스 프록시 · 로드 밸런서 · 프록시 · 서비스 메시
BFF 가 응답을 맞춰 주는 클라이언트
프론트엔드 · 클라이언트 · 브라우저 · 모바일 개발 · 모바일 앱 · 단일 페이지 애플리케이션
BFF 가 뒤에서 부르는 서버 쪽 구성 요소
백엔드 · API · 엔드포인트 · REST · gRPC · 서비스 디스커버리
BFF 가 응답을 모으며 쓰는 기법
요청 집계 · 타임아웃 · 재시도 · 서킷 브레이커 · 부분 실패 · 우아한 성능 저하 · 캐싱
BFF 를 대신할 수 있는 다른 수단
GraphQL · GraphQL 페더레이션 · API 조합 · 범용 API
BFF 가 풀거나 새로 낳는 문제
오버페칭 · 언더페칭 · 왕복 시간 · 지연 · 코드 중복 · 비즈니스 로직
BFF 앞의 게이트웨이가 맡는 기능
인증 · 인가 · 속도 제한 · TLS 종료 · 요청 라우팅
BFF 를 맡는 팀의 구조 원칙
다른 이름: Backend for Frontend · Backends for Frontends · Backend for Frontends · 프론트엔드를 위한 백엔드 · BFF 패턴