BFF와 API Gateway, 3조건은 같고 축이 다르다

Gateway Pattern — 경계를 어디에 긋는가

ArchitectureGatewayBFFNetwork

난이도: ★★★☆☆
연관 노트: ‘보험/뱅크/증권’은 마이크로서비스가 아니다


핵심 요약

  1. 경계가 다른데 1개로 묶어서 깨진다. 3조건 중 깨지는 건 경계 조건이다.
  2. 단일 진입점은 동일하고, 검문/변환도 문제가 아니다. JWT·PII·외부 스키마 변환을 각각 하는 건 괜찮다 — 게이트웨이 조건이라고 해서 검문/변환을 꼭 1개만 하는 건 아니니까.
  3. 그러나 경계가 다르다. 외부 파트너사는 파트너사 서버 ↔ 내부 서비스 경계고, JWT·rate limit은 브라우저 ↔ 내부 백엔드 경계다.
  4. rate limit과 JWT 검증은 축이 아니라 검문 행위다. 3조건의 “검문+변환”에 들어가는 것이고, “경계를 나누는 축”과는 다른 층이다.
  5. 축을 정하는 건 “무엇을 호출하는가”가 아니라 “왜 반드시 여기를 지나게 만들었는가”다. ← 이 노트의 핵심
  6. 같은 외부 벤더 호출이라도, 화면에 필요한 데이터를 조합하려고 거치면 화면 축(BFF), 규정 준수 때문에 반드시 거쳐야 하면 정책 축(Gateway)이다.

왜 헷갈렸나

오해 1: “API Gateway는 처음 듣는 개념”

대화 초반에 이미 예시로 나왔던 개념이었다(외부 클라이언트 ↔ 내부 마이크로서비스들). “게이트웨이 패턴의 한 사례”로 스쳐 지나갔을 뿐, BFF와 비교해서 뜯어본 적이 없어서 새 개념처럼 느껴졌다.

오해 2: 축과 검문 행위를 같은 층으로 놓음

“rate limit/JWT은 정책 축이고, 외부 벤더 요청은 정책이 아닌 축”이라고 나눴다. 그런데 rate limit과 JWT는 경계에서 수행하는 행위이고, 화면/정책은 경계 자체를 나누는 기준이다. 층이 다른 두 개를 같은 리스트에 올려놓으니 분류가 안 됐다.

오해 3: “외부 요청이면 화면 축이 아니다”

“외부 요청”이라는 사실 자체는 축을 결정하지 않는다. 호출 대상이 아니라 통과를 강제한 이유가 축을 결정한다.

주의할 반대 방향의 오해: “검문/변환이 여러 개면 게이트웨이가 아니다”

이건 오해다. 게이트웨이는 검문·변환을 몇 개 하느냐로 판정하지 않는다. JWT 검증 + PII 필터 + 벤더별 스키마 변환을 한 곳에서 전부 해도 게이트웨이 조건은 깨지지 않는다. 3조건의 “검문+변환”은 개수 제한이 아니라 행위의 존재 여부를 묻는다.

깨지는 건 개수가 아니라 경계다. 검문이 10개라도 경계가 하나면 건강하고, 검문이 2개라도 경계가 둘이면 문제다.

같은 판정이 데이터 쪽에도 그대로 적용된다 — 운영(OLTP) 경계와 분석(OLAP) 경계도 축이 다르므로 한 곳에서 처리하면 안 된다.


메커니즘

게이트웨이 3조건

어떤 컴포넌트가 게이트웨이인지 판정하는 기준.

⚠️ “3조건”이라는 말이 두 곳에서 쓰인다. 여기의 3조건은 “서비스 앞단에 무엇을 세울지”를 판정하고, 마이크로서비스 3조건(독립 배포 / 독립 데이터 / 단일 책임)은 “서비스를 어디서 쪼갤지”를 판정한다. 별개다.

1. 경계(boundary)      : 서로 다른 신뢰 영역 사이에 있는가
2. 단일 진입점          : 그 경계를 넘는 모든 트래픽이 반드시 여기를 지나는가
3. 검문 + 변환          : 통과 여부를 검증하고, 형태를 바꾸는가

BFF도, API Gateway도 3조건을 모두 만족한다. 그래서 3조건만으로는 둘을 구분할 수 없다.

구분 기준: 3조건을 “어느 단위”로 적용했는가

조건BFFAPI Gateway
경계특정 화면(앱 하나) ↔ 여러 백엔드모든 외부 소비자 ↔ 전체 서비스군
단일 진입점”그 화면” 전용 진입점 — 앱마다 여러 개 존재 가능 (웹BFF, 모바일BFF)조직 전체 트래픽이 통과하는 진짜 하나
검문+변환변환 목적 = UI가 필요한 모양으로 조합(aggregation)검문 목적 = 인증/rate limit/라우팅 등 인프라 정책 적용
변경 이유UI 요구사항 — “이 화면에 이 데이터 조합이 필요해짐”인프라·보안 정책 — “인증 방식 교체”, “계약 등급 신설”
소유 팀프론트엔드 팀플랫폼·인프라 팀

한 줄로: BFF는 3조건을 “화면 단위”로 적용한 것, API Gateway는 “전사 인프라 정책 단위”로 적용한 것.

축을 판정하는 질문

같은 코드를 보고도 축이 갈린다. 판정 기준은 하나다.

        "왜 반드시 여기를 지나게 만들었는가?"
                       │
        ┌──────────────┴──────────────┐
        ▼                             ▼
 "이 화면에 필요한 데이터를      "규정/보안 때문에 모든
  조합해야 하니까"               트래픽이 거쳐야 하니까"
        │                             │
        ▼                             ▼
    화면 축 = BFF              정책 축 = API Gateway
   (UI 바뀌면 변경)            (정책 바뀌면 변경)

“외부 서드파티 벤더를 호출한다”는 사실은 이 판정에 아무 정보도 주지 않는다. 호출 대상은 축을 결정하지 않는다.


안티패턴: 두 경계를 한 파일에 묶기

한 API Route에 rate limit + JWT 검증을 넣고 웹·모바일·파트너사 요청을 전부 받는 구조.

  경계 A — 브라우저 ↔ 내부 백엔드 (화면 축)
[웹앱]     ─┐
[모바일앱]  ─┤
            ├──▶ [ API Route 하나 ]  ◀── 두 경계가 뒤섞임
[파트너사]  ─┘
  경계 B — 파트너사 서버 ↔ 내부 서비스 (정책 축)

  ※ 파트너사 요청은 브라우저에서 오지 않는다.
    출발지가 다르므로 신뢰 영역도 다르다.

3조건 중 깨지는 것: 경계. 단일 진입점은 오히려 더 잘 뭉쳐지고, 검문+변환도 형식상 성립한다. 문제는 경계가 2개인데 1개로 묶었다는 것.

터지는 지점:

증상원인
화면 문구 하나 고치려고 배포했는데 파트너사 트래픽이 같이 내려감서로 다른 변경 주기가 한 배포 단위에 묶임
프론트팀 PR에 플랫폼팀 인증 정책 diff가 섞임소유 팀이 충돌
이 라우트 하나 죽으면 전 채널 정지장애 범위가 두 경계의 합집합이 됨 (SPOF)

분리 방향

[웹앱]     ──▶ [ 웹 BFF ]     ──┐
[모바일앱] ──▶ [ 모바일 BFF ]  ──┼──▶ [ 내부 서비스들 ]
                               │
[파트너사] ──▶ [ API Gateway ] ─┘
                 인증/rate limit

BFF는 소비자마다 하나씩(N개 허용), Gateway는 정책 경계에 하나. 각자 자기 축의 변경만 흡수한다.


다음에 이 상황을 만나면

API Route에 새 책임을 추가하려 할 때 → 축을 먼저 판정한다.

  • 이 라우트를 통과하도록 강제한 이유가 뭔가? (호출 대상이 아니라 이유)
  • 그 이유가 기존 책임의 이유와 같은 축인가? 다르면 파일을 나눈다
  • 추가하려는 게 축인가 검문 행위인가? rate limit/JWT/스키마 검증은 행위 — 축을 새로 만들지 않는다
  • 이 라우트가 죽으면 몇 개 채널이 멈추나? 두 개 이상이면 경계를 합쳐놓은 것
  • 이 파일을 고칠 때 다른 팀 리뷰가 필요한가? 필요하면 소유가 갈려 있다는 신호

Next.js 특유의 함정: app/api/*는 서버 전용 번들이라 벤더 API 키가 클라이언트로 노출되지 않는다 — 이건 Build 레이어의 보장이다. “게이트웨이는 Network 문제”라고 단정하면 이 층을 놓친다.


커넥팅 닷

← 선행 개념 (이걸 알아야 이해된다)

  • ‘보험/뱅크/증권’은 마이크로서비스가 아니다 — API Gateway가 “여러 서비스 앞단”에 서는 이유는 그 뒤에 독립 배포되는 서비스들이 있기 때문. 마이크로서비스 없이는 Gateway의 존재 이유가 절반 사라진다
  • 왜 서버는 이전 요청을 기억하지 않는가 — 게이트웨이가 요청을 자유롭게 라우팅·재시도할 수 있는 건 각 요청이 독립적이기 때문
  • 신뢰 경계(trust boundary) — 3조건 중 1번. “신뢰할 수 없는 입력이 신뢰 영역으로 들어오는 지점”이 어디인지 못 그리면 게이트웨이를 어디 둘지 정할 수 없다

→ 확장 개념 (여기서 더 나아가면)

  • Sidecar / Service Mesh (Envoy, Istio) — 게이트웨이의 검문 로직을 각 서비스 옆 프록시로 밀어낸 형태. “정책을 앞단 하나에 모을까, 모든 서비스 옆에 붙일까”의 트레이드오프
  • Edge Middleware — 게이트웨이를 CDN 엣지까지 끌어올린 것. 오리진 도달 전에 검문하면 지연이 줄지만 실행 환경이 제약된다
  • API Composition vs CQRS Read Model — BFF의 aggregation을 요청 시점에 할지, 미리 조합된 읽기 모델을 만들어둘지

↔ 같은 원리가 적용되는 곳

  • ‘쓰기도 하고 읽기도 하는데’ 죽은 코드다 — 경계를 선언하지 않으면 책임이 스며든다는 같은 문제. 저긴 데이터 소유, 여긴 트래픽 소유
  • 섹션 사이 구분선, 컴포넌트 대신 플래그로 — “무엇이 함께 바뀌는가”로 단위를 정하는 동일한 판단. 축이 다르면 분리한다
  • Single Responsibility Principle의 정확한 정의 — “하나의 일만 한다”가 아니라 **“변경 이유가 하나”**다. 화면 축과 정책 축이 한 파일에 있으면 변경 이유가 둘이라 SRP 위반

부록: “단일 진입점”의 함정

3조건의 “단일 진입점”을 물리적으로 파일 하나로 오해하기 쉽다. 실제 의미는 “그 경계를 넘는 트래픽이 우회로 없이 여기를 지난다”이다.

그래서 BFF는 여러 개 있어도 각각이 단일 진입점이다. 웹BFF와 모바일BFF는 서로 다른 경계를 담당하므로, 각자 자기 경계의 유일한 통로다.

반대로 위 안티패턴은 파일이 하나인데 단일 진입점이 아니다 — 두 경계의 통로를 겸하고 있어서, 어느 경계 기준으로도 “그 경계만의 진입점”이 아니다. 파일 개수와 진입점 개수는 무관하다.


참고