마이크로서비스 아키텍처

마이크로서비스 아키텍처란?

마이크로서비스 아키텍처Microservice Architecture는 애플리케이션을 작은 독립적인 서비스들로 분리하여 개발하고 운영하는 방식입니다. 각 마이크로서비스는 독립적으로 배포, 확장, 유지보수가 가능하며, 특정 비즈니스 기능을 수행하는 데 초점을 맞춥니다. 이러한 아키텍처는 서비스 간 느슨한 결합과 독립성을 바탕으로 애플리케이션의 유연성과 확장성을 크게 향상시킬 수 있으며, 대규모 시스템에서 특히 유리합니다.

마이크로서비스 아키텍처의 주요 특징

  • 작은 크기의 독립적 서비스: 마이크로서비스는 하나의 비즈니스 기능에만 집중된 작은 서비스로 설계됩니다.
  • 독립적인 배포: 각 서비스는 독립적으로 배포 및 업데이트가 가능하여, 전체 시스템에 영향을 주지 않고 새로운 기능을 릴리스하거나 수정할 수 있습니다.
  • 다양한 기술 스택 허용: 각 마이크로서비스는 독립적으로 개발되기 때문에, 다양한 프로그래밍 언어나 기술 스택을 사용하여 최적의 성능을 낼 수 있습니다.
  • 장애 격리: 서비스 중 하나에 문제가 생겨도, 다른 서비스에 영향을 주지 않도록 고립되어 있습니다.
  • 독립적 확장: 특정 서비스에 부하가 집중될 경우 해당 서비스만 확장하여 리소스를 최적화할 수 있습니다.

마이크로서비스 아키텍처의 장점

  • 유연한 개발 및 배포: 독립적인 서비스 구조로 인해 개발 팀들이 동시에 다양한 기능을 개발하고 배포할 수 있습니다.
  • 확장성: 각 마이크로서비스는 독립적으로 확장 가능하여 특정 서비스에만 자원을 집중할 수 있습니다.
  • 장애 격리: 한 서비스의 장애가 전체 시스템에 영향을 미치지 않기 때문에 시스템 안정성이 높습니다.
  • 유지보수성: 작은 코드베이스로 구성된 각 서비스는 유지보수가 쉬우며, 변화가 빠른 비즈니스 환경에 유연하게 대처할 수 있습니다.
  • 다양한 기술 사용 가능: 각 마이크로서비스는 최적의 기술을 사용할 수 있어, 서비스에 맞는 기술 선택이 가능합니다.

마이크로서비스 아키텍처의 단점

  • 복잡성 증가: 많은 서비스가 존재할 경우, 각 서비스 간 통신, 데이터 동기화, 배포 관리 등의 복잡성이 증가할 수 있습니다.
  • 네트워크 통신: 서비스 간에 API 통신이 빈번히 발생하므로, 네트워크 성능 저하나 통신 장애가 발생할 가능성이 있으며, 이를 해결하기 위한 추가적인 기술이 필요합니다.
  • 데이터 일관성 문제: 각 서비스가 독립적인 데이터베이스를 사용하기 때문에, 데이터 일관성을 유지하는 것이 어렵습니다.

주요 구성 요소

API 게이트웨이

API 게이트웨이는 클라이언트와 마이크로서비스 간의 요청을 관리하는 역할을 합니다. 클라이언트가 여러 마이크로서비스에 직접 접근하는 대신, API 게이트웨이를 통해 모든 요청을 전달하고, 게이트웨이가 적절한 서비스로 라우팅합니다.

서비스 디스커버리

서비스 디스커버리는 동적으로 변경되는 마이크로서비스의 네트워크 주소를 추적하고, 서비스 간 통신을 원활히 하기 위한 메커니즘입니다. 이는 서비스가 중단되거나 재배포될 때, 새로 할당된 네트워크 주소를 다른 서비스들이 알 수 있게 도와줍니다.

데이터 분리

마이크로서비스 아키텍처에서는 서비스 간의 결합을 낮추기 위해 각 서비스는 자신만의 데이터 저장소를 가집니다. 이는 데이터베이스 스키마 변경 시 다른 서비스에 영향을 주지 않고, 독립적인 데이터 관리를 가능하게 합니다.

메시지 브로커

마이크로서비스 간 비동기 통신을 위해 메시지 브로커(Kafka, RabbitMQ 등)를 사용하여 서비스 간 데이터를 교환할 수 있습니다. 이를 통해 서비스 간 결합도를 줄이고, 성능을 향상시킬 수 있습니다.

마이크로서비스 통신 예시

도서 관리 시스템에서 사용자 관리, 도서 관리, 대출 관리 등은 각각 독립적인 마이크로서비스로 운영될 수 있습니다. 이러한 서비스는 서로 독립적으로 배포 및 확장 가능하며, 서로 필요한 데이터를 요청할 수 있습니다.

// 대출 서비스가 도서 관리 서비스와 통신하는 예시
public class BorrowService
{
    private readonly IBookService _bookService;
    private readonly INotificationService _notificationService;
    public BorrowService(IBookService bookService, INotificationService notificationService)
    {
        _bookService = bookService;
        _notificationService = notificationService;
    }
    public void BorrowBook(int bookId, int userId)
    {
        var book = _bookService.GetBookById(bookId);
        if (!book.IsAvailable)
        {
            throw new InvalidOperationException("Book is not available for borrowing.");
        }
        // 도서 대출 처리
        _bookService.MarkAsBorrowed(bookId, userId);
        // 대출 알림 전송
        _notificationService.SendBorrowNotification(userId, book.Title);
    }
}

이 코드에서 BorrowServiceBookServiceNotificationService와 통신하여 도서 대출을 처리하고, 사용자에게 알림을 전송합니다.

데이터 분리

마이크로서비스에서 데이터 분리는 매우 중요한 개념입니다. 각 서비스는 자신의 데이터베이스를 관리하며, 다른 서비스와 데이터를 공유하지 않습니다. 이는 서비스 간의 결합도를 낮추고, 독립적인 확장 및 배포가 가능하게 만듭니다.

services:
  book-service:
    image: book-service
    environment:
      - DATABASE_URL=mysql://user:password@mysql/book_db
  user-service:
    image: user-service
    environment:
      - DATABASE_URL=mongodb://user:password@mongo/user_db
  borrow-service:
    image: borrow-service
    environment:
      - DATABASE_URL=postgresql://user:password@postgres/borrow_db

위의 예시는 각 서비스가 서로 다른 데이터베이스를 사용하여 독립적으로 관리되는 것을 보여줍니다.

결론

마이크로서비스 아키텍처는 대규모 시스템에서 확장성, 유연성, 유지보수성을 제공하는 강력한 설계 방식입니다. 서비스 간 독립성과 데이터 분리를 통해 복잡한 시스템을 효율적으로 관리할 수 있으며, 여러 기술을 활용하여 서비스의 성능을 최적화할 수 있습니다.

심화 학습

비동기 작업

비동기 작업 큐는 서비스 간 비동기적으로 작업을 처리할 때 유용합니다. 마이크로서비스는 종종 비동기 작업 큐를 사용하여 요청을 대기열에 저장한 후, 워커가 이를 처리하게 합니다.

public class BorrowService
{
    private Queue<int> _borrowQueue = new Queue<int>();
    public void AddToQueue(int bookId)
    {
        _borrowQueue.Enqueue(bookId);
        Console.WriteLine($"Book {bookId} added to borrow queue.");
    }
    public void ProcessQueue()
    {
        while (_borrowQueue.Count > 0)
        {
            var bookId = _borrowQueue.Dequeue();
            ProcessBorrow(bookId);
        }
    }
    private void ProcessBorrow(int bookId)
    {
        // 도서 대출 처리 로직
        Console.WriteLine($"Processing borrow for book {bookId}.");
    }
}

데이터 일관성 유지 전략

마이크로서비스에서는 데이터베이스가 분리되어 있어 데이터 일관성을 유지하기 어려울 수 있습니다. 이를 해결하기 위한 방법으로 사가 패턴Saga Pattern과 사후 일관성Eventual Consistency이 자주 사용됩니다.

사가 패턴

사가 패턴Saga Pattern은 분산된 트랜잭션을 관리하기 위한 패턴입니다. 각 트랜잭션이 독립적으로 실행되며, 트랜잭션이 실패할 경우 이전 트랜잭션을 보상하는 방식으로 처리됩니다.

public class SagaOrchestrator
{
    private List<Action> _compensationTasks = new List<Action>();
    public void ExecuteSaga()
    {
        try
        {
            PerformStep1();
            _compensationTasks.Add(() => CompensateStep1());
            PerformStep2();
            _compensationTasks.Add(() => CompensateStep2());
            PerformStep3();
        }
        catch (Exception)
        {
            Console.WriteLine("Saga failed, performing compensation.");
            foreach (var task in _compensationTasks)
            {
                task();
            }
        }
    }
    private void PerformStep1() { /* Step 1 logic */ }
    private void PerformStep2() { /* Step 2 logic */ }
    private void PerformStep3() { /* Step 3 logic */ }
    private void CompensateStep1() { /* Compensation for step 1 */ }
    private void CompensateStep2() { /* Compensation for step 2 */ }
}

서비스 간 통신 방법

마이크로서비스 간 통신은 주로 REST API, gRPC, 메시지 큐를 사용합니다. REST API는 가장 일반적이며, gRPC는 고성능이 요구되는 곳에서 유용하고, 메시지 큐는 비동기 작업에서 많이 사용됩니다.

public class BorrowController : ControllerBase
{
    private readonly HttpClient _httpClient;
    public BorrowController(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }
    [HttpPost("borrow")]
    public async Task<IActionResult> BorrowBook(int bookId)
    {
        var response = await _httpClient.GetAsync($"http://authservice/validate?bookId={bookId}");
        
        if (response.IsSuccessStatusCode)
        {
            return Ok("Borrowed successfully.");
        }
        return BadRequest("Borrow failed.");
    }
}

위의 예시는 BorrowControllerauthservice와 REST API를 통해 통신하여 도서 대출을 처리하는 예시입니다.