API 게이트웨이
고친 사람 github-actions[bot]
API 게이트웨이는 여러 서비스로 들어갈 요청을 한 곳에서 받아 알맞은 서비스로 넘겨 줍니다. 클라이언트는 서비스마다 다른 주소를 찾지 않고 이 한 곳에만 요청합니다. 넘기기 전에 요청을 검사하는 일도 여기서 대신 맡습니다.
쉽고 빠른 이해
API 게이트웨이는 안쪽 서비스들 앞에 서는 하나뿐인 입구입니다. 게이트웨이를 부르는 쪽이 클라이언트입니다. 웹 브라우저든 모바일 앱이든 안쪽 서비스를 쓰려면 이 입구를 지납니다.
쇼핑 앱의 주문 화면이 주문·상품·리뷰 서비스 셋을 불러야 해도, 앱은 게이트웨이 주소 하나만 알면 됩니다.
서비스를 여러 벌로 쪼개면 주소도 그만큼 늘어납니다. 입구가 없으면 앱이 그 주소를 전부 들고 있어야 합니다. 안쪽에서 서비스를 나누거나 합칠 때마다 앱까지 고쳐 다시 배포해야 합니다.
- 게이트웨이가 요청을 받아 보낸 쪽이 누구인지 확인합니다
- 요청 경로를 보고 어느 서비스가 맡을지 고릅니다
- 그 서비스의 응답을 받아 클라이언트에게 돌려줍니다
대가는 모든 요청이 한 곳을 지난다는 것입니다. 게이트웨이가 멈추면 안쪽 서비스가 멀쩡해도 요청이 들어가지 못합니다. 서비스를 하나 늘릴 때마다 게이트웨이 설정을 먼저 고쳐야 합니다.
상세
서비스 앞에 세우는 하나뿐인 입구
API(Application Programming Interface)는 프로그램이 다른 프로그램을 부르는 약속입니다. 서버가 바깥에 열어 둔 주소와 요청 모양이 곧 그 서버의 API 입니다.
API 게이트웨이는 그런 API 여럿을 뒤에 감추고 바깥 요청을 혼자 받는 서버입니다. 쇼핑 앱의 주문 화면 하나를 그리려면 주문 서비스와 상품 서비스와 리뷰 서비스를 불러야 합니다. 앱은 셋의 주소를 몰라도 됩니다. 게이트웨이 한 곳에 요청하면 됩니다.
flowchart TD
W["웹 앱"] --> G["게이트웨이"]
M["모바일 앱"] --> G
G --> O["주문 서비스"]
G --> P["상품 서비스"]
G --> R["리뷰 서비스"]
웹 앱과 모바일 앱이 게이트웨이 한 대에 요청합니다. 게이트웨이가 그 요청을 주문·상품·리뷰 서비스로 나눠 보냅니다. 클라이언트 쪽에서 보이는 주소는 게이트웨이 하나뿐입니다.
이렇게 앞에 입구를 하나 세우기로 한 결정이 이 패턴입니다. 서비스를 여러 벌로 쪼개 따로 배포하는 마이크로서비스 구조에서 자연히 따라옵니다. 서비스를 쪼개면 만드는 쪽은 편해집니다. 그 편함의 값을 부르는 쪽인 클라이언트가 대신 치르지 않게 막는 것이 이 패턴입니다.
입구가 없을 때 클라이언트가 지는 짐
입구가 없으면 클라이언트가 안쪽 지도를 통째로 알아야 합니다. 서비스가 열 개면 주소도 열 개입니다. 그 열 개가 앱 코드 안에 박힙니다.
안쪽에서 서비스 하나를 둘로 가르면 앱도 같이 고쳐 다시 배포해야 합니다. 모바일 앱은 사용자가 업데이트를 받아야 바뀌므로 이 배포가 특히 무겁습니다.
flowchart TD
subgraph N["입구가 없을 때"]
W["웹 앱"] --> O["주문 서비스"]
W --> P["상품 서비스"]
W --> R["리뷰 서비스"]
M["모바일 앱"] --> O
M --> P
M --> R
end
클라이언트가 둘이고 서비스가 셋이면 알고 있어야 할 길이 여섯 줄기입니다. 앞 절의 그림에서는 같은 것이 클라이언트마다 한 줄기였습니다. 서비스가 늘면 이 줄기가 클라이언트 수만큼 같이 늘어납니다.
요청을 걸러 내는 규칙도 흩어집니다. 토큰이 올바른지 확인하는 인증, 한 사람이 너무 자주 부르는 것을 막는 속도 제한 같은 검사는 모든 서비스에 똑같이 필요합니다. 서비스마다 따로 만들면 같은 규칙이 열 벌이 됩니다. 한 곳만 고치면 그때부터 규칙이 갈립니다.
왕복도 쌓입니다. 화면 하나를 그리려고 서비스 셋을 각각 부르면 네트워크를 세 번 오갑니다. 응답 하나하나는 짧습니다. 그래도 이동 통신망은 한 번 오가는 데 시간이 걸립니다. 그 횟수가 그대로 화면이 뜨는 시간이 됩니다.
요청 하나가 게이트웨이 안에서 거치는 단계
게이트웨이는 받은 요청을 바로 넘기지 않습니다. 넘겨도 되는 요청인지 먼저 확인합니다. 통과한 것만 안쪽으로 보냅니다.
flowchart TD
A["요청이 들어온다"] --> B{"토큰이 올바른가"}
B -->|아니다| C["401 Unauthorized 로 돌려보낸다"]
B -->|그렇다| D{"허용한 요청 수 안인가"}
D -->|아니다| E["429 Too Many Requests 로 돌려보낸다"]
D -->|그렇다| F["경로를 보고 맡을 서비스를 고른다"]
F --> G["안쪽 서비스로 넘기고 응답을 돌려준다"]
앞의 두 갈림에서 걸린 요청은 안쪽으로 가지 않습니다. 게이트웨이가 그 자체로 응답을 만들어 돌려보냅니다. 토큰이 틀렸다는 뜻의 상태 코드가 401, 너무 자주 불렀다는 뜻의 상태 코드가 429 입니다. 걸릴 요청이 안쪽 서비스까지 갔다가 되돌아 나오는 왕복을 이 단계에서 없앱니다.
통과한 요청은 경로를 보고 갈 곳이 정해집니다. 게이트웨이는 어떤 경로를 어느 서비스가 맡는지를 적은 표를 들고 있습니다.
| 들어온 경로 | 넘기는 서비스 |
|---|---|
/orders/… |
주문 서비스 |
/products/… |
상품 서비스 |
/reviews/… |
리뷰 서비스 |
이 표만 고치면 안쪽 구조를 바꿔도 바깥 주소는 그대로입니다. 주문 서비스를 둘로 갈라도 클라이언트는
여전히 /orders/ 로 요청합니다.
여러 서비스의 응답을 하나로 모으기
게이트웨이는 요청 하나를 여러 서비스에 나눠 보내고 돌아온 응답을 합쳐 줄 수도 있습니다. 앞 절에서 말한 왕복 문제를 줄이는 방법입니다.
sequenceDiagram
participant 앱
participant 게이트웨이
participant 주문 as 주문 서비스
participant 상품 as 상품 서비스
앱->>게이트웨이: 주문 화면에 필요한 것을 한 번에 요청
게이트웨이->>주문: 주문 내역을 달라
게이트웨이->>상품: 주문에 담긴 상품 정보를 달라
주문-->>게이트웨이: 주문 내역
상품-->>게이트웨이: 상품 정보
게이트웨이-->>앱: 둘을 합쳐 한 번에 돌려준다
앱이 보기에는 요청 한 번에 화면에 필요한 것이 다 돌아옵니다. 여러 번 오가던 왕복이 게이트웨이와 서비스 사이의 짧은 통신으로 옮겨 간 것입니다.
화면마다 필요한 모양이 다르면 게이트웨이를 클라이언트 종류별로 따로 두기도 합니다. 웹용과 모바일용을 가르는 이 변종을 BFF(Backends for Frontends)라고 부릅니다.
flowchart TD
W["웹 앱"] --> WB["웹용 게이트웨이"]
M["모바일 앱"] --> MB["모바일용 게이트웨이"]
subgraph S["안쪽 서비스"]
O["주문 서비스"]
P["상품 서비스"]
R["리뷰 서비스"]
end
WB --> S
MB --> S
입구가 둘로 갈려도 뒤에 서는 서비스는 그대로입니다. 갈라 두는 이유는 클라이언트 종류마다 응답 모양을 따로 맞춰 주기 위해서입니다.
리버스 프록시·로드 밸런서와 가르는 선
앞단에 서서 요청을 넘겨 주는 중개자는 여럿입니다. 이름이 겹쳐 보이지만 요청을 얼마나 깊이 들여다보고 넘기느냐가 다릅니다.
| 중개자 | 무엇을 보고 넘기나 | 요청에 손대나 |
|---|---|---|
| 로드 밸런서 | 같은 일을 하는 서버 여럿 중 덜 바쁜 쪽 | 대개 안 댑니다 |
| 리버스 프록시 | 요청 경로나 호스트 이름 | 헤더를 고쳐 넘깁니다 |
| API 게이트웨이 | 경로에 더해 요청한 사람과 요청 횟수 | 거르고 합치고 바꿉니다 |
두 망 사이에서 요청을 받아 건네주는 중개자를 게이트웨이라고 부릅니다. API 게이트웨이는 거기에 검사와 합치기를 얹은 것입니다.
표의 셋은 서로 다른 제품이 아닙니다. nginx 같은 프로그램 하나가 설정에 따라 셋을 다 맡기도 합니다. 이름이 가리키는 것은 제품이 아니라 그 앞단이 맡기로 한 역할입니다.
한 곳으로 모은 대가
모든 요청이 한 곳을 지나므로 그 한 곳이 멈추면 전체가 멈춥니다. 안쪽 서비스가 전부 멀쩡해도 바깥에서 아무것도 들어가지 못합니다. 이렇게 하나가 멈추면 전체가 멈추는 곳이 단일 장애점입니다. 그래서 게이트웨이는 대개 여러 대를 세우고 그 앞에 다시 부하를 나누는 장비를 둡니다.
거쳐 가는 곳이 하나 늘어난 만큼 응답도 늦어집니다. 연결을 하나 더 맺고 요청을 한 번 더 고치는 시간이 모든 요청에 붙습니다.
배포에도 걸림돌이 생깁니다. 서비스를 새로 내거나 경로를 바꾸려면 게이트웨이 설정을 먼저 고쳐야 합니다. 모든 팀이 같은 설정을 함께 쓰므로 순서를 기다리게 됩니다. 따로 배포하려고 쪼갠 서비스가 한 곳 때문에 다시 같이 움직이게 되면 분산 모놀리스로 가는 길입니다.
게이트웨이에 얹는 일이 늘어나는 것도 대가입니다. 응답을 합치고 모양을 바꾸는 코드를 계속 넣다 보면 업무 규칙이 안쪽 서비스가 아니라 게이트웨이에 쌓입니다. 그러면 게이트웨이 자체가 손봐야 할 또 하나의 애플리케이션이 됩니다.
세울 때와 안 세울 때
서비스가 두셋뿐이고 부르는 클라이언트도 하나라면 이 패턴은 과합니다. 경로를 보고 넘기는 리버스 프록시 설정 몇 줄이면 같은 일이 끝납니다.
값이 커지는 쪽은 클라이언트가 여러 종류일 때입니다. 웹과 모바일 앱과 바깥 회사에 열어 준 창구가 저마다 다른 모양을 원하면, 그 차이를 받아 주는 곳이 한 군데 있는 편이 서비스마다 갈래를 만드는 것보다 다루기 쉽습니다.
안쪽 서비스끼리 주고받는 요청은 대개 게이트웨이를 지나지 않습니다. 이 패턴은 바깥에서 들어오는 요청을 맡습니다. 서비스 사이의 통신은 서비스 메시가 맡습니다.
컨테이너 여럿을 굴려 주는 도구가 쿠버네티스입니다. 거기에는 바깥 요청을 안으로 들이는 인그레스라는 앞단이 이미 있습니다. 경로를 보고 넘기는 일까지는 그것이 맡습니다.
flowchart TD
A{"바깥에서 들어오는 요청인가"} -->|아니다| B["서비스 메시가 맡는다"]
A -->|그렇다| C{"부르는 클라이언트가 여러 종류인가"}
C -->|아니다| D["리버스 프록시 설정으로 충분하다"]
C -->|그렇다| E["API 게이트웨이를 세운다"]
갈림 둘을 차례로 따라가면 세울지 말지가 정해집니다. 쿠버네티스를 쓰고 있다면 인그레스가 이미 맡은 일을 뺀 나머지가 게이트웨이가 할 일입니다.
관련 항목
API 게이트웨이와 같은 앞단에 서는 중개자
게이트웨이 · 리버스 프록시 · 로드 밸런서 · 프록시 · 인그레스 · 서비스 메시 · 사이드카 · 쿠버네티스
API 게이트웨이가 요청을 넘기며 맡는 기능
인증 · 인가 · 속도 제한 · 스로틀링 · TLS 종료 · 요청 라우팅 · 응답 캐싱 · 요청 집계 · 서킷 브레이커
API 게이트웨이를 앞에 세우는 아키텍처
마이크로서비스 · 모놀리식 아키텍처 · BFF · 서비스 디스커버리 · 분산 모놀리스
API 게이트웨이를 세우면 따라오는 위험
단일 장애점 · 병목 · 지연 시간 · 고가용성 · 구성 드리프트
API 게이트웨이 노릇을 맡는 소프트웨어
nginx · Envoy · Kong · Traefik · Amazon API Gateway · Spring Cloud Gateway
API 게이트웨이가 넘겨 주는 요청의 규격
다른 이름: API gateway · API Gateway