안녕하딤니카?
날씨가 좋은데 omg 송화가루 진심... 난리도 아닙니다
눈이 3자가 돼서 고생 중이니 여러분들은 알레르기약 꼭 드시고 이런 일 없길 바랍니다

저는 쭉 프로젝트를 한 프로젝트 안에서 빌드하는 모놀리식 방식으로 많이 작업했는데
프로젝트 덩치가 쫌이라도 커지니 클래스 찾기도 힘들고 진짜 너무 햄들었어요
제가 이런데 다른 사람들은 오죽할까
그래서 마이크로 서비스 아키텍처 방식을 공부해보자~ 하고 요즘 공부 중입니다
그 내용을 올리려고 합니다 (중간중간 다른 글도 올릴 거긴 하지만...
암튼 이번 글에서는 SOA와 MSA 방식의 차이점, MSA 방식에서 필요한 도구(Eureka, Spring Cloud Gateway)를 정리해 보겠습니다
그럼 시~~~작
◎ 이 글은 Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA) 강의를 수강 후 정리 목적으로 작성했습니다.
1. 파이프 아키텍처 - 모놀리식 vs SOA vs MSA
애플리케이션 아키텍처에는 대표적으로 세 가지 방식이 있습니다: 모놀리식(Monolithic), SOA(Service-Oriented Architecture), 그리고 MSA(Microservice Architecture)입니다. 각각의 방식은 시스템을 어떻게 나누고 구성할지, 그리고 서비스 간 통신을 어떻게 처리할지에 대한 전략이 다릅니다.
1-1. 모놀리식 아키텍처 (Monolithic Architecture)
모놀리식(Monolithic) 가장 전통적인 애플리케이션 구조로, 하나의 코드 베이스를 사용하여 여러 비즈니스 기능을 수행하는 전통적인 소프트웨어 개발 모델입니다. 즉, 사용자 인증, 주문 처리, 결제 시스템 등 모든 기능이 하나의 프로젝트, 하나의 애플리케이션으로 배포됩니다.
이 방식의 장점은 배포가 간편하고, 로컬 개발 환경 구성도 쉬우며, 초기 개발 속도가 빠르다는 것입니다. 하지만 규모가 커질수록 다음과 같은 문제점이 발생합니다.
- 일부 코드 수정 시 전체 애플리케이션을 다시 배포해야 함
- 특정 기능만 확장하거나 유지보수하기 어려움
- 개발자 간 충돌이 잦아지고 협업이 어려워짐
이러한 단점을 보완하기 위해 SOA와 MSA가 등장하게 되었습니다.
서비스 지향 아키텍처(SOA)와 마이크로서비스 아키텍처(MSA)는 모두 시스템을 구성하는 방식에 관한 개념입니다. 이 두 가지 방식은 시스템의 구조와 서비스 간의 통신 방법에서 본질적인 차이를 가집니다.
1-2. SOA (Service-Oriented Architecture)
서비스 지향 아키텍처(SOA, Service-Oriented Architecture)는 서비스라는 소프트웨어 구성 요소를 사용해 비즈니스 애플리케이션을 생성하는 소프트웨어 개발 방식입니다. 각 서비스는 비즈니스 기능을 제공하며, 플랫폼과 언어를 넘나들며 서로 통신할 수 있습니다. 개발자는 SOA를 사용해 서로 다른 시스템 내의 서비스를 재사용하거나 독립적인 여러 서비스를 결합하여 복잡한 태스크를 수행합니다.
이 방식에서 각 서비스는 보통 XML이나 SOAP 기반의 프로토콜을 통해 통신합니다. 다양한 서비스들을 하나의 통합된 아키텍처 내에서 관리하려면 ESB(Enterprise Service Bus)라는 중앙 허브 역할을 하는 컴포넌트가 필요합니다. 이 ESB는 서비스 간의 메시지 전달, 포맷 변환, 로깅 등 다양한 기능을 담당합니다.
그러나 이 방식은 ESB가 병목 지점이 되기 쉽고, 서비스 간 결합도가 높아 유지보수나 확장이 어려운 단점이 있습니다.
1-3. MSA (Microservice Architecture)
MSA는 최근 많은 기업에서 채택하고 있는 분산 시스템 구조로, 하나의 애플리케이션을 여러 개의 독립적인 작은 서비스로 분할하는 아키텍처입니다.

각 마이크로서비스는 고유한 기능을 담당하며, 독립적으로 배포, 확장, 유지보수가 가능합니다. 이를 위해 마이크로서비스는 다른 시스템도 액세스 하고 사용하는 중앙 집중식 데이터에 대한 원격 액세스 대신 필요한 모든 데이터에 대한 로컬 액세스 권한을 갖습니다. 이는 성능과 민첩성 측면에서 마이크로서비스가 보완하는 데이터 중복을 만들어냅니다.
각 서비스는 RESTful API를 통해 통신하며, JSON과 HTTP 기반으로 구성됩니다. 또한, 각 서비스가 별도의 데이터베이스를 가질 수도 있고, Polyglot Persistence(각 서비스마다 서로 다른 저장소 사용)도 가능합니다. 서비스 간의 연결을 위해 API Gateway를 도입하고, 서비스 등록/탐색에는 Eureka 같은 디스커버리 서버를 사용합니다.
마이크로서비스 아키텍처 스타일은 최신 클라우드 컴퓨팅 환경에 가장 적합합니다. 최신 클라우드 컴퓨팅 환경은 모든 종속 항목과 함께 코드를 패키징 하는 독립 소프트웨어 단위인 컨테이너에서 작동합니다.
1-4. 아키텍처 정리
| 항목 | 모놀리식 | SOA | MSA |
| 통신방식 | 내부 메소드 호출 | SOAP / XML | REST / JSON |
| 중심 허브 | 없음 | ESB | API Gateway |
| 확장성 | 낮음 | 보통 | 높음 (서비스 단위 확장) |
| 결합도 | 매우 높음 | 높음 | 낮음 (독립성 보장) |
| 운영 복잡도 | 낮음 | 보통 | 상대적으로 높음 |
결론적으로, MSA는 빠르게 변화하는 요구사항에 맞춰 개별 서비스를 빠르게 개발하고 배포할 수 있는 장점이 있어, 클라우드 환경에 특히 잘 어울리는 구조입니다.
2. 서비스 디스커버리의 핵심 - Eureka
2-1. Eukeka란?
Eureka(유레카)는 Netflix에서 개발한 오픈 소스 서비스 디스커버리 도구입니다. 마이크로서비스 환경에서는 서비스 인스턴스가 고정된 주소(IP, 포트)를 가지지 않고 유동적으로 바뀌는 경우가 많기 때문에, 각 서비스가 어디에 존재하는지를 알아내는 기능이 필수적입니다. 유레카는 이를 해결하기 위해 도입됩니다. 유레카에는 두 가지 구성 요소가 있습니다.
- Eureka Server : 서비스 등록소 역할을 하며, 각 클라이언트 인스턴스의 위치 정보를 관리합니다.
- Eureka Client : 개별 서비스가 자신을 Eureka 서버에 등록하고, 다른 서비스의 위치를 조회할 수 있게 해줍니다.
2-2. Eukeka의 주요 개념 및 동작 방식
- 서비스 디스커버리 (Service Discovery) : MSA 환경에서는 여러 개의 서비스가 분산되어 실행됩니다. 유레카는 각 서비스의 위치(IP, PORt)를 중앙 서버에 등록하고, 다른 서비스가 이를 찾아서 통신할 수 있도록 도와줍니다.
- 동적 서비스 등록 및 해제 : 서비스가 시작되면 유레카 서버에 자동으로 등록(Registration)되고, 종료되면 자동으로 해제(Deregistration)됩니다. 서비스의 상태(Health Check)를 지속적으로 모니터링하여 비정상적인 서비스는 제거합니다.
- 로드 밸런싱 및 장애 허용성 : 유레카는 여러 인스턴스 중 하나를 선택해 요청을 전달함으로써 로드 밸런싱을 제공합니다. 특정 인스턴스가 장애를 겪어도 대체 인스턴스를 활용하여 서비스를 지속할 수 있습니다.
이렇게 유레카의 주요 개념을 알고 있으면 좋고, 이제 유레카가 어떻게 동작하는지 정리해 보겠습니다.
- 각 서비스는 Eureka Client로 동작하며, 부팅 시 자신의 인스턴스 정보를 Eureka Server에 등록합니다.
- 다른 서비스와 통신하고 싶을 때, 서비스의 고정 IP나 포트를 사용하는 대신, Eureka 서버를 통해 해당 서비스의 실제 인스턴스 위치를 조회합니다.
- Eureka 서버는 일정 주기(기본 30초)로 heartbeat 신호를 받아 클라이언트가 살아 있는지 확인합니다.
- heartbeat 신호가 일정 시간 이상 수신되지 않으면, 해당 인스턴스를 registry에서 제거합니다.
간단한 구성 예시입니다. 보통 유레카 서버를 따로 두고, 각 서비스를 클라이언트로 등록해 두면 됩니다.
// 유레카 서버로 등록할 프로젝트의 application.yml
// Eureka Server 의존성을 Maven이나 Gradle에 추가
server:
port: 8761
spring:
application:
name: eurekaserver
eureka:
client:
register-with-eureka: false
fetch-registry: false
// 유레카 서버로 등록할 프로젝트의 Application.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
그리고 유레카 클라이언트로 등록할 서비스 프로젝트에는 아래와 같이 설정하면 됩니다.
// 유레카 클라이언트로 등록할 프로젝트의 application.yml
// Eureka Client 의존성을 Maven이나 Gradle에 추가
server:
port: 0 # 0번이라고 입력해두면 랜덤으로 포트번호를 지정함, 포트 지정은 자유롭게
spring:
application:
name: eureka-service
eureka:
instance:
instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://127.0.0.1:8761/eureka
// 유레카 클라이언트로 등록할 프로젝트의 Application.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@SpringBootApplication
@EnableDiscoveryClient
public class ServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
}
이를 통해 각 서비스는 “주소를 외우는 것”이 아니라 “서비스 이름”만 알고 있으면 통신이 가능합니다. 예를 들어, Gateway가 http://eureka-service로 호출하면 Eureka가 이를 실제 인스턴스 주소로 매핑해 주는 방식입니다.
3. API Gateway 도입 - Zuul vs Spring Cloud Gateway
3-1. 왜 API Gateway가 필요한가?
마이크로서비스 구조에서는 수많은 서비스가 존재하고, 클라이언트는 여러 개의 서비스를 호출해야 할 수도 있습니다. 이때 각 서비스에 직접 접근하면 다음과 같은 문제가 발생합니다.
- 모든 서비스의 주소를 클라이언트가 알아야 함
- 공통 인증/로깅/모니터링 기능을 각 서비스에 중복 구현해야 함
- 보안상 외부에 노출되지 않아야 할 서비스까지 노출될 수 있음
이러한 문제를 해결하기 위해 API Gateway가 등장합니다. API Gateway는 모든 외부 요청의 진입점을 하나로 통합해 주는 역할을 하며, 공통 기능을 한 곳에서 처리할 수 있게 해줍니다.
또한 Gateway는 단순 라우팅뿐만 아니라 로드 밸런싱(Load Balancing) 기능도 제공합니다. 예를 들어 동일한 User Service가 여러 인스턴스로 구성되어 있는 경우, API Gateway는 클라이언트 요청을 특정 인스턴스 하나가 아닌, 여러 인스턴스에 분산 처리할 수 있습니다. 이처럼 부하를 균등하게 나누는 것을 '로드 밸런싱'이라 하고, 이를 수행하는 역할을 '로드 밸런서'라고 부릅니다.
Spring Cloud Gateway는 Eureka와 연동해 로드 밸런싱을 수행할 수 있도록 내장된 LoadBalancer 기능을 제공합니다. 기본적으로는 Round-Robin 방식으로 인스턴스를 순차적으로 돌며 요청을 분산시키고, 이를 통해 서비스의 가용성과 응답 속도를 유지할 수 있습니다.
하지만, 실무 환경에서는 이러한 기본적인 로드 밸런싱만으로는 충분하지 않을 수 있습니다. 예를 들어 다음과 같은 복잡한 요구사항이 있을 수 있습니다.
- 특정 사용자 그룹에 따라 트래픽 분리
- 트래픽 급증 시 자동으로 인스턴스 확장 또는 축소
- 서비스 간의 보안 강화
- 장애 발생 시 자동으로 요청 차단 또는 다른 인스턴스로 우회
이런 고급 시나리오에서는 Spring Cloud Gateway와 Eureka만으로는 기능이 부족할 수 있으며, 이러한 이유로 Istio, Linkerd 같은 Service Mesh를 사용하는 경우가 많습니다.
3-2. Service Mesh란?
Service Mesh는 서비스 간 통신을 제어하고 보안, 로깅, 모니터링, 서킷 브레이커 등의 기능을 일관되게 적용할 수 있게 해주는 인프라 계층입니다.
- 각 서비스 앞에 프록시(사이드카 패턴)를 두어 트래픽을 감시 및 제어
- 라우팅 정책, 보안 설정, 인증/인가, TLS 암호화 등 다양한 기능 제공
- 로드 밸런싱도 더욱 세밀하게 제어 가능 (예: 트래픽 비율, 조건부 라우팅 등)
즉, Spring Cloud Gateway는 기본적인 라우팅과 로드 밸런싱을 제공하지만, 서비스 전반의 통신 제어와 정책 관리를 위해서는 Service Mesh를 함께 고려하는 것이 실무적인 설계에서 많이 활용됩니다.
3-3. API Gateway의 종류 1 - Netflix Zuul
Zuul은 Netflix에서 개발한 오픈소스 API Gateway 솔루션으로, Spring Cloud에서 기본 지원되던 Gateway였습니다. Zuul은 Servlet 기반의 동기 방식으로 동작하며, 다양한 필터 체인을 이용해 라우팅, 인증, 로깅 등을 처리할 수 있습니다.
하지만, Zuul은 구조상 Netty 기반 비동기 처리에 비해 성능이 떨어지고, Spring Boot 2.4 이후부터는 유지보수 단계로 넘어가서 더 이상의 패치가 멈췄습니다. Spring에서도 Deprecated 되어 Spring Boot 최신 버전에서는 Zuul은 사용할 수 없습니다.
이후 새로운 Gateway 솔루션으로 Spring Cloud Gateway가 등장하게 되었습니다.
3-4. API Gateway의 종류 2 - Spring Cloud Gateway
Spring Cloud Gateway는 Spring WebFlux 기반으로 동작하는 API Gateway로, Netty를 기반으로 하여 비동기/논블로킹 I/O를 처리합니다. Spring Cloud Gateway는 비동기 방식을 통해 수많은 요청을 빠르게 처리할 수 있으며 다른 Spring Cloud 기반 기술들과 통합이 잘 되어 있어 다양한 기술적 연계가 가능합니다.
- 비동기 기반으로 고성능 처리 가능 (Netty 기반)
- YAML 설정만으로 라우팅 간단 설정
- Predicate, Filter 등을 통해 정교한 조건 처리 가능
- 기본적으로 Load Balancer(Eureka와 연동) 기능 포함
3-5. Zuul → Spring Cloud Gateway 전환 이유 정리
| 항목 | Zuul | Spring Cloud Gateway |
| 기반 기술 | Servlet (blocking) | WebFlux + Netty (non-blocking) |
| 성능 | 낮음 | 높음 |
| 유지보수 | 중단됨 | 현재 Spring Cloud 기본 지원 |
| 확장성 | 필터 체인 강제 구현 | 함수형 DSL로 유연함 |
이러한 이유로 대부분의 Spring Cloud 기반 프로젝트에서는 Zuul 대신 Spring Cloud Gateway를 도입하고 있으며, 저 또한 MSA 방식 프로젝트 구현 실습에서는 Spring Cloud Gateway를 사용하여 서비스 라우팅과 Eureka 연동을 진행하고 있습니다.
4. 정리
프로젝트의 성격마다 아키텍처를 선택해서 사용하면 좋지만, 아무래도 요즘같이 프로젝트의 크기가 커지고, 개발자마다 사용하는 환경이나 언어가 다르다면 MSA 방식을 채택해서 개발을 진행하면 좋을 것 같습니다. 각 아키텍처마다의 장점이 있으니~
글의 길이가 길어져서 Spring Cloud Gateway 세팅 방법이나 필터 구현 예시는 넣지 못했는데, 빠른 시일 내로 정리해서 글을 올리도록 해보겠습니다~ 그때까지 안녕히!
그럼 20000
🍀
좋아하는 것을 계속 좋아하세요!
반드시 행복해집니다
백엔드 개발자가 되고 싶어서 열심히 헤딩 중인 재영입니다 :-)
[Github] https://github.com/chujaeyeong
[E-mail] chujy1224@gmail.com