마이크로서비스 설계 패턴 핵심 요약

2025. 5. 10. 22:00·System Architecture

마이크로 서비스

일반적으로 프로젝트를 진행하게 되면 하나의 서버에 모든 서비스 코드가 들어가게 됩니다. 이런 방식을 모놀리식 아키텍쳐라고 부릅니다. 모놀리식 아키텍쳐는 개발에는 용이하지만 이후에 서비스가 성공하여 규모가 매우 커지게 되면 여러 문제점이 발생할 수 있습니다.

  • 유연성 저하 : 간단한 수정 사항에도 전체 애플리케이션을 다시 빌드하고 배포해야 합니다.
  • 확장성 한계 : 특정 기능에만 부하가 증가해도 해당 기능만 확장하는 것이 아닌 전체 애플리케이션을 확장해야 합니다.
  • 기술 선택의 제약 : 전체 애플리케이션을 하나의 기술 스택으로 구현해야 하기 때문에 기술 도입이나 부분적인 기술 변경에 제약이 있습니다.
  • 장애의 영향 범위 확대 : 특정 기능의 장애가 전체 애플리케이션의 장애로 이어질 수 있습니다.

이런 문제점으로 인해 마이크로 서비스 아키텍쳐가 등장하게 되었습니다. 마이크로 서비스는 각 서비스 별로 따로 배포하여 운영하는 방식입니다. 마이크로 서비스 아키텍쳐에는 모놀리식 아키텍쳐의 단점을 해결해줍니다.

  • 유연성 향상 : 서비스별로 독립적인 배포가 가능하여 배포 주기가 짧아지고 새로운 기능을 신속하게 출시할 수 있습니다.
  • 유연한 확장성 : 특정 서비스에 부하가 증가하면 해당 서비스만 선택적으로 확장할 수 있습니다.
  • 기술 다양성 : 각 서비스별로 알맞은 최적의 기술 스택을 선택하여 개발할 수 있습니다.
  • 회복 탄력성 : 특정 서비스의 장애가 전체 애플리케이션에 미치는 영향을 최소화 할 수 있습니다.

하지만 마이크로 서비스는 단점도 존재합니다. 설계가 복잡해지고 관리해야 될 서비스의 수가 늘어나는 등 여러 문제점이 있습니다. 이런 문제점들을 해결하기 위해 여러 설계 패턴이 등장했습니다. 이 글에선 그 중 Database Per Service Pattern, API Gateway Pattern, BFF Pattern, CQRS Pattern, Event Sourcing Pattern, Saga Pattern, Sidecar Pattern, Circuit Breaker Pattern 총 8가지를 소개하려고 합니다.

Database Per Service Pattern

Database Per Service Pattern은 각 마이크로 서비스가 각자의 데이터 베이스를 갖도록 하는 설계 패턴입니다. 이를 통해 각각의 서비스에서 데이터 격리와 자율성을 보장할 수 있습니다. 만약 다른 마이크로 서비스의 데이터가 필요한 경우 API를 통해 접근하거나 조작할 수 있습니다. 이는 각 서비스별로 책임을 명확하게 분리하고 각 서비스 간에 느슨한 결합을 강화시켜 줍니다. 또한 각 서비스 별로 최적의 데이터 베이스를 선택할 수 있습니다.

다만, 모놀리식 아키텍쳐에서 마이크로 서비스로 마이그레이션 할 때 적용하기 어렵습니다. 모놀리식 아키텍쳐에서는 서비스 간의 경계 구분이 명확하지 않을 때가 많아 다른 서비스의 데이터를 접근할 수 있기 때문입니다.

 

장점

  • 각 서비스 별로 데이터 격리, 확장성, 자율성을 확보할 수 있습니다.
  • 다른 서비스에 영향을 주지 않고 독립적으로 데이터 모델(Schema)을 발전시킬 수 있습니다.
  • 각 마이크로 서비스 요구사항에 알맞은 데이터 베이스를 선택적으로 도입할 수 있습니다.

단점

  • 마이크로 서비스 사이의 데이터 일관성을 유지하기 어렵습니다.
  • 여러 서비스의 데이터를 필요로 하는 복잡한 쿼리를 처리하기 어렵습니다.
  • 여러 서비스에 걸친 비즈니스 트랜잭션을 관리하기 어렵습니다.

API Gateway Pattern

API Gateway Pattern은 클라이언트가 각 서비스의 api를 직접 호출하는 것이 아닌 앞단의 API Gateway를 사용하도록 하는 설계 패턴입니다. 이는 마이크로 서비스에서 클라이언트가 여러 서비스와 통신하며 여러번 통신해야 되는 문제를 API Gateway와만 통신하는 방식으로 해결해줍니다. 예를 들어 OrderService와 CustomService 두 곳과 통신해야 되는 기능을 "api/order-detail"이라는 API만 호출하면 API Gateway가 요청을 받아 직접 OrderService와 CustomService의 API를 호출한 뒤 그 결과를 종합해 클라이언트에게 반환해줍니다. 즉, API Gateway가 통신을 단순화해주는 단일 진입점 역할을 해줍니다. 또한 API Gateway에서 인증, 인가, 캐싱 등 여러 서비스의 공통 기능을 처리해주는 역할도 수행합니다.

 

장점

  • 클라이언트는 여러 마이크로 서비스와 통신할 필요 없이 하나의 엔드포인트와 통신하여 왕복 요청 수를 줄여줍니다.
  • 인증, 인가, 캐싱과 같은 횡단 관심사를 한 곳에서 처리해줍니다.

단점

  • 설계에 따라 API Gateway가 SPOF 지점이 될 수 있습니다.
  • API Gateway가 성능 병목의 지점이 될 수 있습니다.

BFF Pattern

BFF Pattern은 웹이나 모바일 애플리케이션 등 서로 다른 프론트엔드 클라이언트마다 별도의 백엔드 서비스를 만드는 것입니다. 이는 웹이나 모바일 애플리케이션마다 필요로 하는 API가 다르거나 같은 기능의 API라도 요구하는 API 스펙이 다를 수 있는 문제점을 해결하기 위해 나온 설계 패턴입니다. 각 프론트엔드의 요청을 처리하는 별도의 BFF 서버를 두고 각 BFF 서버가 각 서비스의 API를 호출하여 해당 프론트엔드에게 최적화된 API 요청을 전달해줍니다.

 

장점

  • 각 프론트엔드에게 최적화된 경험을 제공해줍니다.
  • 클라이언트와 서비스 계층의 독립적인 진화와 확장을 가능하게 해줍니다.

단점

  • 여러 백엔드를 관리 및 유지보수해야 하기 때문에 복잡성이 증가합니다.

CQRS Pattern

CQRS Pattern은 읽기와 쓰기를 별도의 데이터베이스에 분리하여 관리하는 방식입니다. 이를 통해 Write DB는 상태 변경을 처리하고 영속화하는 것에, Read DB는 복잡한 쿼리와 집계를 최적화하는 것에 집중할 수 있습니다.

CQRS 패턴은 보통 pub/sub 패턴으로 동작합니다. 먼저 마이크로 서비스가 상태 변화를 유발하는 쓰기 작업을 실행하면 도메인 이벤트를 발행합니다. 그러면 이벤트 리스너가 이 도메인 이벤트를 받아 Read DB를 갱신합니다. 이후에 클라이언트 애플리케이션이 데이터를 요청하면 Read DB에서 데이터를 읽어 이 요청을 처리합니다.

 

장점

  • Write DB는 대량의 쓰기 요청을 처리하고 Read DB는 다수의 동시 쿼리를 처리하도록 독립적으로 확장이 가능합니다.
    • Read DB의 경우 비정규화, Materialized View, 캐싱 등을 적용하는 방식으로 읽기 작업의 속도를 향상시킬 수 있습니다.

단점

  • Read DB와 Write DB 간의 데이터 정합성을 유지하기 어렵습니다.
  • 최종 일관성 모델을 사용하기 때문에 강한 일관성이 요구되는 서비스에는 부적합합니다.

Event Sourcing Pattern

Event Sourcing Pattern은 엔티티의 상태를 DB에서 직접 수정하는 방식이 아닌 엔티티의 상태를 이벤트로 캡쳐하여 DB에 저장하는 방식입니다. 주로 CQRS 패턴과 함께 사용됩니다.

Event Sourcing Pattern에서는 이벤트 저장소(Event Store)에 시스템에서 발생한 모든 이벤트를 저장합니다. 그러면 메시지 큐에서 이벤트 저장소에 저장된 이벤트들을 다른 시스템 구성 요소로 전파해줍니다. 이 이벤트를 통해 Materialized View와 같은 기술을 사용하는 Read DB에서 엔티티의 현재 상태를 반영하고 실시간으로 정보에 접근이 가능하도록 해줍니다. 만약 이후에 클라이언트가 특정 시점의 상태를 요구하면 이벤트 저장소에 저장된 이벤트를 리플레이하는 방식으로 상태를 재구성할 수 있습니다.

 

장점

  • 이벤트 저장소에 단순히 이벤트를 추가하기 때문에 대량의 쓰기 작업을 효율적으로 처리할 수 있습니다.
  • 이벤트 저장소에 모든 상태 변경 이력을 유지하고 있기 때문에 디버깅, 트러블 슈팅 등을 위한 기록으로 활용할 수 있습니다.
  • 새로운 기능이나 동작이 추가되도 그 영향을 최소한으로 줄여줍니다. 이는 새로운 이벤트가 도입되도 기존의 이벤트는 수정할 필요가 없기 때문입니다.

단점

  • 이벤트 저장소와 Read DB간의 일관성과 무결성 보장이 어렵습니다.
  • 이벤트 버저닝, 스키마 변경, long running 프로세스 관리 등 시스템의 복잡성이 증가합니다.

Saga Pattern

Saga Pattern은 비즈니스 트랜잭션이 여러 서비스에 걸쳐 있을 때 트랜잭션의 일관성과 무결성을 보장해주기 위해 나온 설계 패턴입니다. Saga Pattern에서 하나의 비즈니스 트랜잭션은 여러 개의 로컬 트랜잭션으로 나누어지고 각 트랜잭션을 각각의 서비스에서 처리해줍니다. 각 서비스가 자신의 로컬 트랜잭션을 처리한 뒤 이벤트를 발행하고 이 이벤트는 다음 서비스에 전달되어 해당 서비스에서 자신의 트랜잭션을 처리합니다. 이 과정은 비즈니스 트랜잭션이 완료될때까지 반복되는데 만약 중간에 어떤 로컬 트랜잭션이 실패하면 보상 트랜잭션을 실행하여 이전에 완료한 작업을 취소하는 방식으로 시스템이 일관된 상태를 유지할 수 있도록 해줍니다.

Saga Pattern은 2종류로 구분할 수 있습니다. 각각 Orchestration-Based Saga와 Choreography-Based Saga라고 부릅니다.

Orchestration-Based Saga

Orchestration-Based Saga는 중앙의 orchestrator에서 Saga의 각 단계를 조율하고 결정을 내리는 방식입니다. 이 방식은 Saga의 workflow가 orchestrator에 명시되어 있어 전체 과정을 시각화하기 쉽습니다.

Choreography-Based Saga

Choreography-Based Saga는 Saga에 참여하는 각 서비스가 직접 이벤트를 발행하고 구독하며 다른 서비스와 통신하는 방식입니다. 각 서비스는 자율적으로 동작하면서 수신된 이벤트에 기반하여 결정을 내립니다. 서비스의 로컬 트랜잭션이 완료되면 다음 단계로 넘어가는 이벤트를 발행하여 다음 서비스를 호출하고 만약 트랜잭션이 실패하면 보상 트랜잭션을 발행하여 롤백을 수행합니다.

 

장점

  • 비즈니스의 트랜잭션을 여러 개의 로컬 트랜잭션으로 나눠 시스템이 일관된 상태를 유지할 수 있게 해줍니다.
  • 가 서비스가 독립적으로 트랜잭션을 수행하여 확장성과 성능이 향상됩니다.

단점

  • 여러 서비스에 걸친 트랜잭션의 흐름을 관리하므로 복잡해집니다.
  • 최종 일관성 모델에 의존하므로 모든 트랜잭션이 완료되기 전까지 일시적으로 불일치한 상태가 발생할 수 있습니다.
  • 외부 시스템이나 복구할 수 없는 작업이 포함되는 등 보상 시스템의 설계 및 구현이 어려울 수 있습니다.

Sidecar Pattern

Sidecar Pattern은 도메인 기능과 운영 기능을 각각의 컨테이너로 분리하여 배포하는 설계 패턴입니다. 이를 통해 각각의 서비스에서 로깅, 모니터링, 설정 관리, 캐싱과 같은 운영 기능을 매번 새로 구현하는 일을 방지할 수 있게 해줍니다.

위의 사진은 핵심 로직을 포함한 비즈니스 요구 사항을 처리하는 애플리케이션 컨테이너와 애플리케이션 컨테이너를 보조하는 사이드카 컨테이너를 따로 배포한 뒤 사이드카 컨테이너에서는 깃허브에서 설정 정보를 가져와 Volume에 세팅하고 애플리케이션 컨테이너에서 Volume을 사용해 설정 정보를 활용하는 모습입니다.

 

장점

  • 핵심 애플리케이션 로직과 운영 기능을 명확하게 분리할 수 있습니다.
  • 운영 기능을 사이드카 컨테이너로 캡슐화하여 같은 사이드카 컨테이너를 여러 마이크로 서비스에서 재사용 할 수 있습니다.
  • 핵심 애플리케이션에 영향을 주지 않고 운영 기능을 추가하거나 수정할 수 있는 유연성을 제공해줍니다.

단점

  • 각 마이크로 서비스마다 여러 개의 컨테이너를 관리해야 하므로 운영 오버헤드가 증가합니다.
  • 모든 애플리케이션 컨테이너마다 사이드카가 동작하므로 메모리 및 CPU와 같은 자원의 소모량이 증가합니다.

Circuit Breaker Pattern

Circuit Breaker Pattern은 서비스간의 통신 장애가 발생했을 때 해당 통신을 차단하여 장애가 전체 애플리케이션에 전파되지 않도록 막아주는 설계 패턴입니다. 마이크로 서비스에서 서비스 간의 통신은 네트워크 불안정, 타임 아웃 등 여러 이유로 실패가 발생할 수 있습니다. 이럴 때 보통은 재시도를 하지만 연속적으로 실패가 발생하면 이러한 재시도는 시간과 자원의 낭비를 발생시킬 수 있습니다. 그렇기에 Circuit Breaker Pattern에서는 서비스간 통신 사이에 프록시를 도입하여 특정 시간 내에 발생하는 실패 횟수를 모니터링하고 실패 횟수가 일정 임계치를 초과하면 요청이 통과하지 못하도록 차단시킵니다.

이런 서킷 브레이커는 3개의 상태를 가집니다.

Closed State

서킷 브레이커는 초기에 Closed 상태를 가집니다. Closed 상태에서는 정상적으로 통신을 하며 프록시는 실패 횟수를 모니터링 합니다. 만약 실패 횟수가 임계치를 초과하게 되면 서킷 브레이커는 Open 상태로 전환됩니다.

Open State

Open 상태에서는 서킷 브레이커가 해당 통신을 완전히 차단 시킵니다. Open 상태에서 클라이언트는 "서비스 이용 불가"와 같은 실패 응답이나 Default 응답을 받습니다. Open 상태는 정해진 타임아웃 기간 동안 유지되며 타임아웃이 끝나면 Half-Open 상태로 전환됩니다.

Half-Open State

Half-Open 상태에서는 제한된 수의 요청만 대상 서비스로 전달됩니다. 만약 이 요청들이 성공하면 문제가 해결된 것으로 간주하고 다시 Closed 상태로 전환됩니다. 반면에 이 요청들이 실패하면 다시 Open 상태로 전환되어 타임아웃 기간동안 통신을 차단시킵니다.

 

장점

  • 과도한 재시도를 방지하여 시스템의 회복성을 향상시킵니다.
  • fail-fast를 통해 문제를 빠르게 감지합니다.
  • Open 상태에서 클라이언트에게 Default 응답을 제공하여 기능이 완전히 중지되지 않고 점진적으로 저하되는 형태를 취할 수 있습니다.
  • 상태 변화를 통해 간접적으로 서비스의 상태를 모니터링 할 수 있습니다.

단점

  • 적절한 실패 임계치와 타임 아웃을 설정해줘야 합니다.
  • 서킷 브레이커 기능을 외부 라이브러리를 통해 구현하게 될 경우 시스템 복잡성이 증가합니다.

정리

마이크로 서비스 아키텍쳐에서 발생하는 문제점을 해결하기 위한 8가지 설계 패턴을 설명했습니다. 하지만 코드에 정답은 없다라는 말이 있듯이 각 패턴 별로 장점과 단점이 존재합니다. 서비스의 성격이나 리소스 등을 고려하여 알맞은 방법을 선택하는 것이 최선인 것 같습니다. 이런 마이크로 서비스에 대한 코드 예시를 제공하는 FTGO 애플리케이션도 있으니 이런 자료들을 참고하며 알맞은 선택을 할 수 있으면 좋을 것 같습니다.

참고자료

A Crash Course on Microservices Design Patterns

 

A Crash Course on Microservices Design Patterns

Microservices architecture has gained popularity for its ability to improve the flexibility, testability, and scalability of software systems.

blog.bytebytego.com

 

'System Architecture' 카테고리의 다른 글

엔지니어링의 트레이드오프: 실제 환경에서의 최종적 일관성 (Eventual Consistency)  (0) 2025.05.29
Slack은 어떻게 하루 수십억 개의 메시지를 처리할까  (0) 2025.05.22
당신이 알아야 할 시스템 설계의 핵심 개념 20가지 - 2. 캐싱  (0) 2025.05.01
당신이 알아야 할 시스템 설계의 핵심 개념 20가지 - 1. 로드 밸런싱  (0) 2025.05.01
당신이 알아야 할 시스템 설계의 핵심 개념 20가지 - 0. 개론  (1) 2025.05.01
'System Architecture' 카테고리의 다른 글
  • 엔지니어링의 트레이드오프: 실제 환경에서의 최종적 일관성 (Eventual Consistency)
  • Slack은 어떻게 하루 수십억 개의 메시지를 처리할까
  • 당신이 알아야 할 시스템 설계의 핵심 개념 20가지 - 2. 캐싱
  • 당신이 알아야 할 시스템 설계의 핵심 개념 20가지 - 1. 로드 밸런싱
ggio
ggio
개발 공부를 하며 배운 내용을 기록합니다.
  • ggio
    기록을 하자
    ggio
  • 전체
    오늘
    어제
    • 분류 전체보기 (41)
      • SW마에스트로 (5)
      • System Architecture (8)
      • Algorithm (15)
      • Side Tech Notes (7)
      • CS (5)
      • 취준 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    다중화
    리트코드
    지리적 분산
    코테
    프로액터 패턴
    leetcode
    비관락
    Algorithm
    ha 아키텍처
    분산락
    fail back
    리액터 패턴
    fail over
    소마
    부트캠프
    Programming
    메시지 큐
    시스템 설계
    시스템 아키텍쳐
    3PC
    코딩테스트
    멀티 코어
    객체지향
    소프트웨어 마에스트로
    SW마에스트로
    at-least-once
    알고리즘
    토스 NEXT
    프로그래밍
    매일메일
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
ggio
마이크로서비스 설계 패턴 핵심 요약
상단으로

티스토리툴바