마이크로서비스 아키텍처(MSA)를 설계할 때, 확장성과 유지보수성을 위해 각 구성 요소의 역할을 명확히 하는 것이 중요합니다. 이번 글에서는 API Gateway, Eureka(Discovery Server), Load Balancer(LB), 그리고 클라이언트가 각각 어떤 목적을 가지고 있으며, 이들이 어떻게 상호 작용하여 확장성 있는 시스템을 구성하는지 정리해보겠습니다.
주소 추상화 및 라우팅:
클라이언트가 직접 각 서비스의 포트나 주소를 알 필요 없이, http://api-gateway/ 등의 단일 엔드포인트로 접근할 수 있도록 합니다.
예를 들어, ProductService의 실제 주소가 http://localhost:9091이라 하더라도, Gateway에서 http://product-service로 매핑해주면, Front-end 개발자분들은 포트 변경이나 인스턴스 스케일아웃에 대해 신경 쓸 필요가 없습니다.
부가 기능 제공:
인증/인가: 잘못된 요청은 내부 서비스로 전달되지 않고 Gateway에서 바로 401/403 응답 처리
캐싱: 빈번하지만 변경이 적은 데이터를 Gateway에서 캐싱하여 응답 속도 개선
로깅, 모니터링 등 추가 기능을 제공하여 서비스의 안정성을 높입니다.
서비스 등록 및 상태 모니터링:
각 서비스는 자신이 어느 포트에서 실행되고 있는지, 그리고 정상적인 상태(UP/DOWN)를 Eureka에 등록합니다.
Eureka 서버는 등록된 서비스들의 정보를 실시간으로 관리하며, 다른 서비스가 요청 시 Eureka를 통해 최신 정보를 받아올 수 있습니다.
동적 서비스 검색:
목적:
FeignClient는 MSA에서 마이크로서비스 간의 HTTP 요청을 보다 쉽게 처리할 수 있도록 해주는 선언적(Declarative) REST 클라이언트입니다.예제:
@FeignClient(name = "product-service")
public interface ProductClient {
@GetMapping("/products/{id}")
ProductResponse getProductById(@PathVariable("id") Long id);
}
name = "product-service"를 설정하면, product-service의 인스턴스를 Eureka에서 자동으로 조회하여 로드밸런싱을 수행합니다.목적:
product-service 인스턴스) 사이에 요청을 분산 기존 Spring Cloud에서 FeignClient는 기본적으로 Ribbon을 사용하여 로드밸런싱을 수행했습니다.
@FeignClient(name = "product-service")
public interface ProductClient {
@GetMapping("/products/{id}")
ProductResponse getProductById(@PathVariable("id") Long id);
}
✔️ Ribbon이 Eureka와 연동되어 자동으로 서비스 인스턴스를 조회하고 로드밸런싱을 수행했습니다.
Ribbon이 Spring Cloud에서 제거되었으며, 대신 Spring Cloud LoadBalancer가 기본 로드밸런서로 적용되었습니다.
spring:
cloud:
loadbalancer:
ribbon:
enabled: false
# true => Ribbon 활성화
✔️ Ribbon을 비활성화하고, Spring Cloud LoadBalancer를 기본적으로 사용하도록 변경되었습니다.
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
✔️ @LoadBalanced를 붙이면 RestTemplate이 Eureka에서 서비스 인스턴스를 조회하여 로드밸런싱을 수행합니다.
참고: API Gateway도 간단한 로드밸런싱 기능을 제공할 수 있으나, Gateway의 본래 목적은 클라이언트 요청 라우팅과 인증/인가, 캐싱 등 부가 기능에 집중하는 것입니다. 따라서, 본격적인 부하 분산은 전용 LB가 맡는 것이 좋습니다.
MSA 환경에서 API Gateway, Eureka(Discovery Server), Load Balancer는 각자의 목적에 맞게 역할을 수행하며, 이를 적절히 활용하면 확장성과 유지보수성이 뛰어난 시스템을 구축할 수 있습니다.
이를 통해 안정적이고 확장 가능한 MSA 아키텍처를 설계할 수 있습니다.