우아콘 2022 API Gateway 회고: Pattern과 Framework를 구분하는 기준

반응형

2022년에는 MSA architecture라면 API Gateway server가 필연적으로 필요하다고 생각했다. 우아콘의 ‘API Gateway Framework는 사실 그리 많이 필요하지 않아요’ 세션을 고른 것도 그 믿음을 확인해 보고 싶어서였다.

세션을 보고 남은 가장 큰 배움은 API Gateway Pattern과 API Gateway 제품·framework를 같은 것으로 보면 안 된다는 점이었다. 다만 당시 메모에는 “단순 routing은 어떠한 경우에도 사용하면 안 된다”처럼 너무 단정적인 문장도 있었다. 지금은 gateway가 해결할 client·edge 문제가 실제로 있는지, 그리고 그 책임을 어디까지 둘지를 먼저 묻는 편이 정확하다고 본다.

API Gateway Pattern은 무엇인가

API Gateway는 client가 내부 service를 직접 알지 않도록 앞단에 둔 논리적 경계다. 요청을 적절한 service로 전달할 수도 있고, 여러 응답을 조합하거나 client에 필요한 형태로 바꿀 수도 있다.

Spring Cloud Gateway나 과거의 Netflix Zuul 같은 framework는 이 pattern을 구현하는 선택지다. framework를 설치했다고 presentation boundary가 자동으로 설계되는 것도 아니고, 직접 code를 작성했다고 좋은 gateway가 되는 것도 아니다.

구분하면 다음과 같다.

구분 답해야 할 질문
Pattern client와 내부 service 사이에 어떤 경계가 필요한가
Responsibility routing, 인증, rate limit, 조합, 변환 중 무엇을 맡길 것인가
Implementation managed gateway, framework, custom application 중 무엇으로 만들 것인가

단순 routing은 정말 나쁜가

그렇지 않다. routing 자체는 gateway의 정상적인 책임이다. public endpoint를 한곳에 모으고 TLS termination, authentication, rate limiting, observability를 공통 적용하려는 요구라면 routing gateway가 충분한 가치를 낼 수 있다.

문제는 내부 service API를 거의 그대로 외부에 노출하면서 소유권과 보안 경계까지 해결됐다고 착각할 때 생긴다.

  • client가 내부 service 구조와 배포 변화에 직접 묶인다
  • mobile client가 여러 service를 조합하며 business flow를 떠맡는다
  • 내부 field가 필요 이상으로 외부 계약에 고정된다
  • gateway의 인증만 믿고 resource 소유권 검사를 빠뜨릴 수 있다
  • timeout과 부분 실패의 책임이 어느 component에 있는지 흐려진다

따라서 “routing을 금지한다”가 아니라 외부 contract가 내부 topology를 그대로 복사하지 않는지, 각 endpoint의 owner가 분명한지를 확인해야 한다.

BFF와 응답 조합이 필요한 때

BFF(Backend for Frontend)는 web, iOS, Android처럼 client별 요구와 release cadence가 크게 다를 때 각 client에 맞는 backend boundary를 두는 방식이다. mobile 화면 하나를 그리기 위해 여러 service를 순서대로 호출해야 하고, app release 없이는 조합 logic을 고치기 어렵다면 BFF가 책임을 가져가는 편이 나을 수 있다.

gateway aggregation도 client round trip과 불필요한 data를 줄일 수 있다. 대신 server 내부 fan-out이 생기므로 다음을 함께 설계해야 한다.

  • downstream별 timeout과 전체 deadline
  • 일부 service 실패 시 partial response 여부
  • retry가 중복 요청이나 부하 증폭을 만들지 않는지
  • response cache와 data freshness 기준
  • trace를 통해 느린 downstream을 찾을 수 있는지

Reactive나 non-blocking I/O를 쓴다는 사실만으로 end-to-end latency가 좋아지지는 않는다. workload가 I/O 중심인지, connection과 thread가 실제 병목인지 측정한 뒤 선택해야 한다.

인증과 인가는 다른 경계다

gateway에서 token을 검증하면 각 service가 모든 인증 protocol을 반복 구현하지 않아도 된다. 그러나 “이 사용자가 로그인했다”와 “이 사용자가 이 주문을 조회할 수 있다”는 다른 질문이다.

OWASP가 Broken Object Level Authorization으로 분류하는 문제는 object ID를 받은 API가 요청자에게 그 object 권한이 있는지 확인하지 않을 때 발생한다. gateway가 인증을 끝냈더라도 resource를 소유한 service에서 object-level authorization을 강제해야 한다. GraphQL을 gateway에 둔다고 이 문제가 사라지는 것도 아니다.

gateway는 곧 Single Point of Failure인가

논리적으로 요청이 모이는 choke point인 것은 맞지만, 반드시 단일 process나 단일 instance일 필요는 없다. 여러 availability zone에 replica를 두고 load balancer, health check, autoscaling, timeout과 circuit breaker를 갖추면 물리적인 단일 장애점은 줄일 수 있다.

다만 중앙 경계의 장애 영향 범위가 큰 것은 변하지 않는다. config 배포, certificate, route rule, authentication provider 변경을 작은 blast radius로 검증할 수 있어야 한다. 관측성과 운영 역량이 없다면 gateway가 하나 늘어나는 것만으로도 관리 부담이 된다.

service 간 east-west traffic까지 같은 gateway에 몰아넣는 것도 피한다. 외부 client와 service 사이의 north-south traffic, 내부 service 사이의 통신과 service mesh는 문제와 운영 주체가 다르다.

도입 전에 확인할 질문

  1. 여러 client가 공통으로 필요로 하는 edge 책임이 있는가
  2. 외부 API 계약과 내부 service 계약을 분리해야 하는가
  3. client 대신 조합해야 할 호출이 실제로 있는가
  4. 인증 이후 resource-level 인가를 누가 보장하는가
  5. timeout, retry, partial failure의 owner가 정해졌는가
  6. gateway 장애와 잘못된 route 배포를 견딜 수 있는가
  7. framework를 도입하는 비용보다 해결되는 문제가 큰가

이 질문에 구체적으로 답할 수 없다면 MSA diagram의 빈칸을 채우기 위해 gateway를 먼저 만들 필요는 없다. 반대로 명확한 edge 요구가 있다면 managed product든 framework든 직접 구현이든 운영 조건에 맞춰 고르면 된다.

당시에는 “Gateway 측에서 내부와 외부 사이의 통제탑 역할을 직접 설계해야 한다”는 깨달음을 적었다. 지금 덧붙인 결론은 한 걸음 더 현실적이다. pattern은 책임을 설명하고, framework는 그 책임을 구현하는 수단이다. 둘을 구분하되 routing 자체를 악으로 만들지는 말자.

변경 비용과 domain boundary를 다룬 우아콘 아키텍처 세션 회고, event로 system 경계를 나눈 회원 시스템 이벤트 아키텍처 회고와 함께 보면 연결된다.

참고 자료

반응형
KEEP READING
카테고리 전체 보기 →

댓글