BFF Backend For Frontend 패턴¶
한 줄 요약: BFF는 "특정 화면 하나를 위해 범용 API 앞에 세운 전용 서버"라는 역할(패턴)의 이름이다. 프레임워크 이름이 아니다. Express, FastAPI, Next.js 무엇으로든 만들 수 있다.
📍 만난 상황¶
- 2026-11-10 13:22 — 아키텍처 설명 초안을 쓰고 있었다. "기능 조사 → ERD → 로컬 PG → Next.js → FusionDB 판단" 순서였다.
- 직전에 피드백을 들었다. "pg에서 메타데이터를 뽑아 sqlway에 쿼리를 날린다"는 표현이 틀렸고, 실제로는 대시보드 서버가 dhub2-manager를 거친다는 내용이었다.
- 그 설명에 나온 BFF라는 말이 왜 나왔는지, "Next.js가 BFF인 거야?"라고 물었다.
- BFF를 도구 이름처럼 받아들인 것이다. 실제로는 서버가 맡은 역할을 가리킨다. → 그날 일지
🧩 쉽게 말하면¶
큰 도매시장과 식당의 구매 담당자를 떠올리면 된다.
도매시장(범용 API)은 여러 식당에 똑같이 판다. 박스 단위로만 팔고, 출입증이 있어야 들어간다.
구매 담당자(BFF)는 우리 식당만을 위해 일한다. 출입증을 들고 시장에 가서, 오늘 메뉴에 맞는 만큼 사서 손질해 온다.
담당자가 없으면 주방장(화면)이 직접 시장에 가야 한다. 출입증을 주방장마다 나눠 줘야 하고, 매번 박스를 뜯어 필요한 것만 골라야 한다.
📖 제대로 알기¶
정의¶
BFF(Backend For Frontend: 프론트엔드를 위한 백엔드)는 화면 하나(또는 앱 하나)만 상대하는 전용 서버를 두는 설계 방식이다. - 범용 백엔드는 그대로 둔다. 여러 서비스가 함께 쓰는 API이기 때문이다. - 화면 사정에 맞춘 일은 BFF가 맡는다. - 웹·모바일처럼 화면이 여러 종류면, 화면마다 BFF를 하나씩 두기도 한다.
패턴과 도구는 다르다¶
- 패턴(pattern): 자주 쓰이는 설계 방식에 붙인 이름. "무슨 역할을 하는 서버인가"를 말한다. 예: BFF, 프록시, 작업 큐.
- 도구(프레임워크·라이브러리): 그 역할을 실제로 만드는 재료. 예: Next.js, Express, FastAPI.
- 그래서 "Next.js가 BFF다"보다 "Next.js의 서버 기능으로 BFF를 구현했다"가 정확하다.
BFF가 맡는 일¶
| 브라우저가 범용 API를 직접 부르면 | BFF가 대신 하는 일 |
|---|---|
| 플랫폼 토큰이 브라우저에 노출된다 | 토큰은 서버에만 두고 대신 호출한다 |
| 브라우저가 SQL을 만들어 보내야 한다 | 조건 값만 받고, SQL은 정해진 틀로 서버가 만든다 |
| 즐겨찾기·제안·문의를 저장할 곳이 없다 | 앱 전용 DB(PG)에 쓴다 |
| 로그인·관리자 확인을 할 곳이 없다 | 세션과 역할을 확인한다 |
| 화면 하나에 여러 번 호출해서 조합해야 한다 | 여러 결과를 화면에 딱 맞는 모양으로 합쳐 한 번에 준다 |
관련 용어¶
- 범용 API: 특정 화면이 아니라 여러 서비스를 위한 API. 여기서는 dhub2-manager다.
- 프록시(대신 요청해 주는 서버): BFF는 프록시 역할을 포함하지만, 조합·저장·권한 확인까지 하므로 더 넓다.
- API 게이트웨이: 모든 서비스 앞에 서는 공용 입구. 인증·속도 제한·경로 나누기를 한다. 특정 화면을 위한 맞춤 일은 하지 않는다.
⚙️ 어떻게 동작하나¶
미리보기 화면 하나를 여는 경우를 따라가 본다.
- 브라우저가 BFF에 조건만 보낸다. 예: 데이터셋, 기간, 지역.
- BFF가 세션 쿠키로 로그인한 사람인지 확인한다.
- BFF가 대시보드 PG에서 데이터셋 정보(표 이름, 위치 단위 등)를 읽는다.
- BFF가 그 정보로 SQL을 정해진 틀에 맞춰 만든다.
- BFF가 자기만 가진 토큰을 붙여 dhub2-manager에 보낸다. manager는 sqlway를 거쳐 ClickHouse에서 실행한다.
- BFF가 PG의 요약과 플랫폼 결과를 한 응답으로 합쳐 브라우저에 준다.
flowchart LR
subgraph APP["Next.js 앱 (컨테이너 하나)"]
UI["화면<br/>브라우저에서 도는 React"]
BFF["서버 코드<br/>BFF 역할"]
end
UI -->|"① 조건만"| BFF
BFF -->|"③ 데이터셋 정보"| PG[("대시보드 PG")]
BFF -->|"⑤ SQL + 토큰"| M["dhub2-manager<br/>범용 API"]
M --> CH[("ClickHouse")]
Next.js 앱 하나 안에 화면과 서버 코드가 함께 있다. 그중 서버 코드가 BFF 역할을 한다. Next.js 전체가 BFF인 것은 아니다.
왜 이렇게 설계할까? 범용 API를 화면에 맞춰 고치면, 그 API를 쓰는 다른 서비스가 영향을 받는다. 화면 전용 일은 화면 전용 서버에 두는 편이 서로 덜 엉킨다.
🧪 예시¶
-
예시 1 · 일상에 빗대어: 식당 메뉴판에 "오늘의 정식"이 있다. 밥 1, 국 1, 반찬 3이다. 손님이 주방·창고·냉장고 3곳에 따로 주문하지 않는다. 직원 한 명이 3곳에서 모아 쟁반 하나에 담아 온다. 손님 쪽 요청은 3번이 아니라 1번이다. 이 직원이 BFF다.
-
예시 2 · 실제로는: 데이터셋 요약 화면용 API 하나다(이름은 예시용).
// app/api/datasets/[id]/summary/route.ts
export async function GET(req: Request, { params }) {
const user = await requireSession(req); // ①
const ds = await getDataset((await params).id); // ②
const sql = buildCountSql(ds, parseFilter(req)); // ③
const count = await platformQuery(sql); // ④
const fav = await isFavorite(user.id, ds.id); // ⑤
return Response.json({ title: ds.title, count, fav }); // ⑥
}
⚖️ 비교 · 흔한 오해¶
| 구분 | 누구를 위해 | 주로 하는 일 |
|---|---|---|
| 범용 백엔드 | 여러 서비스 | 데이터 보관, 권한 판정, 쿼리 실행 |
| API 게이트웨이 | 모든 서비스 공용 | 인증, 속도 제한, 경로 나누기 |
| BFF | 화면 하나 | 조합, 토큰 숨기기, 앱 전용 저장 |
- 헷갈렸던 점: "Next.js가 BFF인가?" — 설명에서 "Next.js로 만든 서버가 BFF 역할을 한다"는 말이 한 문장에 붙어 나온다. 그래서 둘이 같은 것처럼 들리기 쉽다. 하지만 BFF는 "무슨 역할인가", Next.js는 "무엇으로 만들었나"에 대한 답이다. 질문의 층이 다르다.
- 오해: "BFF면 별도 서버를 따로 띄워야 한다" — 따로 띄워도 되고, Next.js처럼 화면과 한 프로그램에 넣어도 된다. 작은 과제에서는 한 프로젝트, 한 컨테이너가 관리하기 쉽다.
- 오해: "백엔드가 이미 있으니 BFF는 중복이다" — BFF는 데이터 보관·권한 판정을 다시 만들지 않는다. 범용 백엔드가 못 하거나, 해서는 안 되는 화면 맞춤 일만 한다.
- 오해: "BFF는 프록시와 같다" — 프록시는 요청을 그대로 넘긴다. BFF는 여러 곳을 조합하고, 자기 DB에 쓰기도 한다.
🛠️ 실무에서는¶
- 쓰면 좋을 때
- 브라우저에 두면 안 되는 비밀값(토큰)이 있다.
- 범용 API가 화면에 비해 너무 잘게 나뉘어, 화면 하나에 호출이 여러 번 필요하다.
- 앱에만 필요한 기록(즐겨찾기, 문의)을 저장할 곳이 필요하다.
- 피해야 할 것
- BFF에 업무 규칙이 계속 쌓이는 것. 여러 서비스가 함께 써야 할 규칙은 범용 백엔드로 올린다.
- BFF가 범용 API 기능을 다시 구현하는 것. 권한 판정을 두 곳에서 하면 둘이 어긋난다.
- 체크리스트
- 토큰·비밀번호가 서버 코드에만 있나.
- 브라우저가 SQL이나 표 이름을 직접 보내지 않나.
- 관리자 API는 서버에서 역할을 확인하나.
- 문제가 생기면 먼저 볼 것: 오류가 BFF에서 났는지, 뒤의 범용 API에서 났는지부터 가른다. BFF 로그에 "플랫폼 응답 코드"를 남겨 두면 빠르다.
❓ 자주 묻는 질문¶
- Q. 설명할 때는 어떻게 말하면 되나? A. "프론트와 백엔드를 Next.js 하나로 만들었다. Next.js의 서버 기능이 dhub2-manager 앞에서 BFF 역할을 한다."
- Q. 나중에 모바일 앱이 생기면? A. 모바일 전용 BFF를 하나 더 두는 방식이 BFF 패턴의 원래 모습이다. 화면마다 필요한 모양이 다르기 때문이다.
- Q. BFF를 FastAPI로 따로 만들었으면 뭐가 달라지나? A. 하는 일은 같다. 프로그램이 둘로 나뉘어 배포·로그인 연결(CORS, 토큰 전달)을 따로 맞춰야 한다.
🔭 더 깊이 보기¶
- API 게이트웨이와 BFF를 함께 쓰면 어떤 모양이 되나? 서비스가 늘어날 때 입구를 어떻게 나누는지 보인다.
- BFF에서 여러 API를 동시에 부르려면?
Promise.all같은 병렬 호출로 응답 시간을 줄이는 방법이다. - 관련 주제: 프론트엔드와 백엔드 서버 구조 — Next.js가 화면과 서버를 함께 맡는 원리
- 관련 주제: 메타데이터 카탈로그 기반 조회 — BFF가 PG 정보로 SQL을 만드는 흐름
- 관련 주제: OIDC 로그인 흐름 — BFF가 로그인까지 맡는 이유
📚 참고 자료¶
- Backends for Frontends 패턴 (Microsoft Learn 한국어) — 언제 쓰고, 무엇을 조심하는지 · 링크
- Backends For Frontends (영문, Sam Newman) — 이 패턴을 널리 알린 글. 화면마다 BFF를 두는 이유 · 링크
- Next.js Docs (영문) — Route Handlers 항목에서 서버 API를 만드는 법 · 링크
🗓️ 이력¶
- 2026-11-10: BFF라는 말이 왜 나왔는지, Next.js가 BFF인지 물음 → 일지