마이크로서비스 Microservices (2) API 게이트웨이

⏱ 5 분

이전 포스트에서 마이크로서비스 아키텍처의 개념을 살펴봤습니다. 이번 포스트에서는 마이크로서비스의 중요한 요소 중 하나인 API 게이트웨이(API Gateway)를 알아보겠습니다.

서비스 직접 호출하기

먼저 대표적인 쇼핑몰 아마존의 모바일 애플리케이션을 예로 들어보겠습니다.

https://www.nginx.com/blog/building-microservices-using-an-api-gateway/

위 화면은 모바일에서 본 아마존 제품 상세 화면입니다. 간단해보이지만, 실제로 이 안에는 여러 가지 정보와 기능이 담겨있습니다.

  • 상품 정보
  • 쇼핑 카트에 담긴 상품 수
  • 주문 내역
  • 고객 리뷰
  • 수량이 얼마 안남았으면 경고 보여주기
  • 배송 옵션
  • 다른 사람들이 해당 상품과 같이 구매한 상품 추천
  • 구매 옵션

모놀리식 애플리케이션(monolithic application)이라면 한 번의 API 호출로 모든 정보를 가져올 겁니다. REST 요청을 로드 밸런서가 받아서 애플리케이션 인스턴스 중 하나에 전달하고 애플리케이션은 여러 테이블에서 데이터를 가져와 응답을 보내줄 겁니다.

하지만 마이크로서비스 아키텍처에서는 어떨까요? 각 기능이 서비스로 나뉘어있기 때문에 각 서비스마다 호출이 필요합니다. 예를 들면 다음과 같이 서비스가 나뉘어 있을 겁니다.

  • 상품 정보, 구매 옵션 -> Catalog Service
  • 쇼핑 카트에 담긴 상품 수 -> Shopping Cart Service
  • 주문 내역 -> Order Service
  • 고객 리뷰 -> Review Service
  • 수량이 얼마 안남았으면 경고 보여주기 -> Inventory Service
  • 배송 옵션 -> Shipping Service
  • 다른 사람들이 해당 상품과 같이 구매한 상품 추천 -> Recommendation Service

한 번의 호출로 충분했던 모놀리스와 달리, 하나의 페이지를 보여주기 위해 이렇게 많이 서비스를 호출하는 것은 비합리적입니다.

https://www.nginx.com/blog/building-microservices-using-an-api-gateway/

  • 요청이 많이 일어나면서 네트워크 데이터를 많이 소모합니다. 특히나 모바일 환경에서는 더 중요합니다.
  • 각 서비스의 API 가 웹 친화적(web-friendly)이지 않을 수 있습니다. HTTP 가 아닌 Thrift binary RPC 나 AMQP 메시징 프로토콜을 사용할 수도 있습니다. 이럴 경우 클라이언트에서 직접 호출하기 어렵습니다.
  • 서비스의 리팩토링이 어려워집니다. 하나의 서비스가 커져서 여러 서비스로 나누거나, 반대로 여러 서비스를 하나의 서비스로 합칠 때 호출단까지 같이 수정해야하기 때문에 작업이 어렵습니다.

API 게이트웨이 사용하기

이런 문제를 해결하기 위해 서비스들의 엔드포인트를 하나로 묶을 수 있는 API 게이트웨이가 필요합니다. 각 서비스를 직접 호출하지않고 모든 요청이 API 게이트웨이를 통하게 만드는 것입니다. 예제에서 API 게이트웨이는 하나의 요청에 여러 서비스를 호출한 후 하나의 결과로 취합해서 보내줄 겁니다.

https://www.nginx.com/blog/building-microservices-using-an-api-gateway/

기능과 장점

이러면 API 게이트웨이에서 모든 요청을 볼 수 있기 때문에 한 곳에서 다양한 일을 할 수 있습니다.

  • 요청에 따라 필요한 서비스로 라우팅합니다.
  • 모든 서비스의 API 를 노출하는 대신 필요한 API 만을 노출해서 캡슐화할 수 있습니다.
  • 클라이언트 별로 다른 API 를 제공할 수 있습니다.
  • 하나의 요청에 필요한 서비스를 각각 호출해 결과를 모아서 응답할 수 있습니다.
  • 내부에서 사용하는 프로토콜이 다를 경우 외부에는 웹 친화적인 프로토콜(HTTP, WebSocket 등)으로 변환해줍니다.
  • 클라이언트와의 통신을 줄일 수 있고, 클라이언트의 코드도 단순해집니다.
  • 권한 인증, 모니터링, 로드 밸런싱, 캐싱, 과금을 위한 측정 등을 한 곳에서 할 수 있습니다.

단점

하나의 엔트리 포인트를 갖는 것은 장점이자 단점입니다.

  • API 게이트웨이에서 하는 역할이 많고, 게이트웨이에 장애가 나면 서비스 전체가 사용이 불가능합니다.
  • 각 서비스의 API 를 수정하면 API 게이트웨이를 함께 수정해야 합니다. 이는 개발 과정에서 병목(bottleneck)이 되어 개발 과정일 지연시킬 수 있습니다.
  • API 게이트웨이 또한 개발하고 유지보수해야 할 대상입니다.

구현 시 고려사항

비동기, 반응형 프로그래밍

API 게이트웨이는 많은 요청을 처리하기 위해 비동기(asynchronous), 논블락킹(nonblocking) I/O 기반으로 설계되어야 합니다. 한 요청을 받아 서비스를 호출할 때 해당 서비스가 지연되거나 응답이 없는 경우, 동기(synchronous), 블락킹(blocking) 모델이라면 전체 서비스에 지연이 생길 겁니다. JVM 위에서 동작하는 NIO 기반 프레임워크는 Netty, Vert.x, Spring Reactor 등이 있습니다. JVM 을 사용하지 않는 환경에서는 대표적으로 Node.js 가 있습니다.

API 를 비동기로 구축하다보면 흔히 ‘콜백 지옥’이라 부르는 얽히고설켜 복잡한 콜백 함수들을 다루게 됩니다. 이럴 땐 반응형 프로그래밍 모델(Reactive programming model)을 고려해보는 것이 좋습니다. 반응형 프로그래밍은 데이터를 다루는 방식이 다릅니다. 기존 프로그램처럼 필요한 데이터를 당겨오는(pull) 방식이 아니라 데이터 변화가 발생했을 때 새로운 데이터를 보내주는(push) 방식입니다.

리액티브 방식은 스칼라(Scala)의 Future, 자바 8의 CompletableFuture, 자바스크립트의 Promise 등을 이용해 구현할 수 있습니다. 그리고 RxJava[1], RxJS 와 같은 ReactiveX 를 사용하는 것도 좋은 방법입니다.

프로세스 간 통신

마이크로서비스에서 분산된 서비스가 서로 통신하기 위해서는 프로세스 간 통신(inter-process communication; IPC) 매커니즘이 필요합니다. 각 서비스들이 사용하는 방식이 다를 경우 API 게이트웨이는 다양한 방식을 지원해야 합니다.

  • 비동기 메시징 기반(asynchronous, messaging‑based mechanism) : JMS(Java Message Service), AMQP(Advanced Message Queuing Protocol) 등
  • 동기 방식(synchronous mechanism) : HTTP, Apache Thrift

서비스 디스커버리

API 게이트웨이는 각 서비스를 호출하기 위해 IP 주소와 포트를 알고 있어야 합니다. 기존 환경에서는 이러한 서버의 위치가 고정이라 문제가 없지만, 클라우드 기반에서는 각 서비스가 동적으로 할당된 서버에 배포되면서 해당 서비스의 위치를 파악하는 것이 어려워졌습니다. 이렇게 해당 서비스의 위치를 찾는 기술을 서비스 디스커버리(Service Discovery)라고 합니다. API 게이트웨이는 서버 사이드, 혹은 클라이언트 사이드 기준으로 서비스 디스커버리를 구현할 수 있습니다.

부분적인 장애 대처

마이크로서비스에서 각 서비스는 독립적으로 배포되기 때문에 부분별로 장애가 발생할 수 있습니다. API 게이트웨이는 각 서비스를 호출하면서 해당 서비스에서 장애가 났을 경우 사용자가 경험을 해치지 않도록 매끄럽게 처리해야 합니다. 에러를 던져줄 수도, 에러 화면으로 라우팅할 수도, 또는 기본값이나 캐시된 값을 보여줄 수도 있습니다.

참고


  1. 1.RxJava 는 넷플릭스(Netflix)에서는 REST 기반의 API 호출 횟수와 서비스의 전반적인 성능을 개선하면서 .NET 환경의 리액티브 확장 라이브러리(Rx)를 JVM 에 포팅하여 만들었다.