마이크로서비스 아키텍처(MSA)에 대해 알아봅시다!

이재원·2025년 5월 14일
0

아키텍처

목록 보기
4/4

모놀리식 아키텍처

마이크로서비스를 이해하기에 앞서 모놀리식 아키텍처를 이해할 필요가 있습니다. 그래야 마이크로서비스가 도입된 배경과 필요성, 그리고 언제 마이그레이션을 진행하는게 좋을지를 판단할 수 있게 되기 때문이죠.

이해를 돕기 위해 모놀리식이라는 단어의 어원을 한번 알아보면, 모놀리식(Monolithic)은 그리스어 "monos"(단일)와 "lithos"(돌)에서 유래되었으며, 이는 "하나의 돌"이라는 의미를 갖습니다. 즉, 모든 기능들을 하나의 소프트웨어(애플리케이션)에 통합되어 있는 구조를 말하는 것이죠.

모놀리식 아키텍처의 정의를 AWS에서 다음과 같이 정의합니다.

AWS
모놀리식 아키텍처는 하나의 코드 베이스를 사용하여 여러 비즈니스 기능을 수행하는 전통적인 소프트웨어 개발 모델입니다.

이제 모놀리식 아키텍처가 무엇을 뜻하는지 어느정도 이해 됐을 겁니다. 그럼 이제 장단점에 대해 비교해 봅시다.

장점

  • 손쉬운 배포: 실행 파일 또는 디렉토리가 하나여서 배포가 더 쉽습니다.
  • 개발: 하나의 코드 베이스로 애플리케이션을 구축하여 개발이 더 쉽습니다.
  • 성능: 동일한 프로세스 내에서 모든 컴포넌트가 동작하기 때문에, 서비스 간 통신에 따른 네트워크 지연이 없습니다.
  • 테스트 간소화: 통합된 환경에서 테스트가 가능하므로, 별도의 모킹(mocking) 없이 단위 및 통합 테스트를 수행하기 쉽습니다.
  • 간편한 디버깅: 모든 코드가 하나의 프로젝트에 포함되어 있기 때문에, 전체 흐름을 추적하고 디버깅하는데 용이합니다.

단점

  • 느린 개발 속도: 코드베이스가 커질수록 의존성이 복잡해지고, 개발 및 변경 작업이 느려질 수 있습니다.
  • 확장성: 부분적인 확장이 불가능하며, 부하가 큰 기능 하나 때문에 전체 시스템을 확장해야 하는 비효율일 발생합니다.
  • 안정성: 하나의 기능에서 발생한 오류가 전체 애플리케이션 장애로 이어질 가능성이 높습니다.
  • 기술 채택의 장벽: 전체 시스템에 영향을 줄 수 있기 때문에, 특정 모듈에 새로운 기능이나 언어를 도입하기가 어렵습니다.
  • 유연성 부족: 팀이 커지고 도메인이 복잡해질수록 기능 분리가 어려워지고, 병렬 개발이 제한됩니다.
  • 배포: 일부 코드만 수정해도 전체 애플리케이션을 다시 빌드하고 배포해야 하므로, 배포 주기가 길어질 수 있습니다.

장단점에서 알 수 있듯이 규모가 작은 경우에는 모놀리식으로 진행하는 것이 더 효율적입니다. 하지만 규모가 커지고 확장이 어려워지면 장점보다 단점이 더 커집니다.

이러한 단점을 개선하고자 도입된 것이 마이크로서비스 아키텍처입니다.

실제로 넷플릭스, 아마존, 우버와 같은 기업들이 MSA를 도입하면서 성공적인 비즈니스를 이끌어낸 사례로 손꼽힙니다.

마이크로서비스 아키텍처(MSA)

마이크로서비스 아키텍처(MSA, Microservices Architecture)는 현대 소프트웨어 시스템의 설계 방식 중 하나로, 대규모 서비스를 작은 단위의 독립적인 서비스들로 나누어 개발, 배포, 운영하는 아키텍처입니다.

각 기업들의 문서에서는 마이크로서비스 아키텍처에 대해 아래와 같이 정의합니다.

Ret Hat
마이크로서비스란 독립적인 서비스들의 모음이 경량화 API를 통해 통신하는 애플리케이션 아키텍처의 한 유형을 말합니다.

Atlassian
간단하게 마이크로서비스라고도 하는 마이크로서비스 아키텍처는 독립적으로 배포 가능한 일련의 서비스를 이용하는 아키텍처 방법입니다. 이러한 서비스에는 특정한 목표를 가진 자체 비즈니스 로직 및 데이터베이스가 있습니다. 업데이트, 테스트, 배포 및 확장은 각 서비스 내에서 이루어집니다. 마이크로서비스는 주요 비즈니스, 도메인별 문제를 별도의 독립적인 코드 베이스로 분리합니다. 마이크로서비스는 복잡성을 줄여주지는 않지만, 작업이 서로 독립적으로 작동하고 전체에 기여하는 더 작은 프로세스로 분리하여 복잡성을 눈으로 볼 수 있고 관리하기 쉽도록 만듭니다.

Google Cloud
마이크로서비스 아키텍처(주로 마이크로서비스라고도 함)란 애플리케이션 개발을 위한 아키텍처 스타일을 의미합니다. 마이크로서비스를 사용하면 대규모 애플리케이션을 각각 담당 영역을 가진 소규모의 독립적인 구성요소로 구분할 수 있습니다. 마이크로서비스 기반 애플리케이션은 단일 사용자 요청을 처리하기 위해 여러 내부 마이크로서비스를 호출하여 응답을 작성할 수 있습니다.

각 문서들에서 말하는 정의들을 종합해서 다시 정의내려보면,

하나의 애플리케이션을 작고 자율적인 서비스들의 집합으로 나누어 설계하는 방식이다. 각 서비스는 하나의 특정 비지니스 기능을 담당하며, 독립적으로 개발, 배포, 확장이 가능한 아키텍처이다.

그럼 이제 MSA의 장단점을 알아보겠습니다.

장점

  • 애질리티 - 배포가 잦은 소규모 팀에서 애자일 방식으로 작업하기 좋습니다.
  • 유연한 확장: 부하가 큰 서비스만 따로 확장 가능합니다.(예: 결제 서비스만 3배 확장)
  • 독립적/지속적 배포 가능: 서비스별로 팀을 나누고, 팀단위 배포가 가능해지며, 배포를 위해 다른 팀을 신경쓰지 않아도 됩니다.
  • 높은 유지 관리성 및 테스트 편의성: 팀에서 새로운 기능을 실험해 보고 문제가 발생하면 롤백할 수 있습니다.
  • 기술 유연성: 서비스마다 기술 스택이나 도구 사용이 가능해집니다.
  • 높은 안정성: 전체 애플리케이션이 중단될 위험이 없어집니다.

단점

  • 인프라 비용: 서비스가 많아질수록 각 서비스마다 서버, 컨테이너, 네트워크 리소스가 필요하므로, 초기 인프라 구축 및 운영 비용이 증가합니다.
  • 조직 오버헤드 추가: 서비스별로 팀이 분리되고 책임이 나뉘기 때문에, 협업과 커뮤니케이션이 많아지고 조직 운영의 복잡도가 높아집니다
  • 디버깅 문제: 기능이 여러 서비스에 걸쳐 분산되어 있기 때문에, 단일 트랜잭션을 추적하거나 오류를 디버깅하는 데 어려움이 있습니다.
  • 명확한 소유권 부족: 팀이 많아질수록 팀에서 어떤 서비스를 활용할 수 있는지, 그리고 지원을 받으려면 누구에게 문의해야 하는지 파악하기가 어려워집니다.

MSA에서 클라이언트와 상호작용 하는 방법

MSA는 APIGateway를 통해 클라이언트로부터 API를 호출받습니다. 그리고 APIGateway는 클라이언트가 호출한 API를 각 서비스에 알맞게 매핑해줍니다. 이러한 과정을 통해 각 서비스들은 상호작용하며 클라이언트는 통합된 하나의 결과를 받게 됩니다.

이러한 구조로 작동되기 때문에 DB역시 각 서비스들마다 독립적으로 관리됩니다. 그럼 어떻게 DB에 있는 데이터들을 각 서비스들이 알 수 있는지 의문이 생길 수 있는데요, DB에 있는 데이터 역시 API 호출을 통해 상호작용하게 됩니다. 즉, 독립적인 서비스들은 독립적인 DB를 소유하고 관리하며, 상호작용은 오로지 API를 통해서만 하는 것이 핵심입니다.

이러한 내용에 대한 이해를 바탕으로 APIGateway가 무엇인지 정의내려보면,

클라이언트의 모든 요청을 받아서 적절한 MSA로 라우팅 해주는 중앙 관문

이라고 정의할 수 있겠습니다.

API Gateway는 경우에 따라 인증/인가, 로깅 및 모니터링, 요청 결과 조합(BFF: Backend For Frontend), 속도 제한 및 캐싱 역할도 함께 수행합니다.

다음은 실제로 사용하는 오픈소스 API Gateway입니다.

  • Kong: 경량화된 API Gateway, 플러그인 기반
  • NGINX: 리버스 프록시 기반으로 커스터마이징 쉬움
  • Zuul(Netflix): 자바/스프링 진영에서 사용
  • AWS(APIGateway): AWS에서 제공하는 관리형 서비스
  • Spring Cloud Gateway: 스프링 생태계에서 가장 많이 사용

코드로 이해 해보기

지금까지 정리한 MSA에 대한 설명을 바탕으로 간단한 예제 코드를 작성해 보겠습니다.

Model(DB)

/// Model Class
class User {
    public Long id;
    public String name;

    public User(Long id, String name) {
        this.id = id;
        this.name = name;
    }
}

class Order {
    public Long userId;
    public Long orderId;
    public String product;

    public Order(Long userId, Long orderId, String product) {
        this.orderId = orderId;
        this.userId = userId;
        this.product = product;
    }
}

먼저 모델을 정의해 줍니다. 실제에서 각 모델들은 DB라고 생각하면 됩니다.

Service

/// UserService: 사용자 서비스(마이크로서비스)
class UserService {
    private Map<Long, User> userDB = new HashMap<>();

    public UserService() {
        // 모의DB 초기화
        userDB.put(1L, new User(1L, "river"));
        userDB.put(2L, new User(2L, "bob"));
    }

    public User getUserById(Long userId) {
        System.out.println("UserService getUserById called");
        return userDB.get(userId);
    }
}

/// OrderService: 주문 서비스(마이크로서비스)
class OrderService {
    private List<Order> orderDB = new ArrayList<>();

    public OrderService() {
        // 모의 DB 초기화
        orderDB.add(new Order(1L, 10L, "iPhone15"));
        orderDB.add(new Order(2L, 20L, "iPhone15 pro"));
        orderDB.add(new Order(3L, 20L, "iPhone15 pro max"));
    }

    public List<Order> getOrdersByUserId(Long userId) {
        System.out.println("UserService getOrdersByUserId called");
        List<Order> result = new ArrayList<>();
        for (Order order : orderDB) {
            if (order.userId.equals(userId)) {
                result.add(order);
            }
        }
        return result;
    }
}

각 서비스들은 관련 DB(UserService - User, OrderService - Order)를 초기화해주고, 각 서비스와 관련된 비지니스 로직을 작성해 주면 됩니다.

실제에서 각 서비스들은 앞서 설명한대로 팀 단위로 이루어지며, 독립적으로 프로젝트를 생성하여 배포 및 관리합니다.

API Gateway

///  APIGateway: 클라이언트 요청을 받아 각 마이크로서비스에 위임
class APIGateway {
    private UserService userService;
    private OrderService orderService;

    public APIGateway(UserService userService, OrderService orderService) {
        this.userService = userService;
        this.orderService = orderService;
    }

    // 클라이언트 요청: 유저의 주문 조회
    public void getUserOrders(Long userId) {
        System.out.println("[APIGateway] 요청 시작: getUserOrders");

        // (1) 유저 정보 조회(내부적으로 HTTP API 호출이라고 가정)
        User user = userService.getUserById(userId);
        if (user == null) {
            System.out.println("유저를 찾을 수 없습니다.");
            return;
        }

        // (2) 주문 정보 조회(마찬가지로 HTTP 요청)
        List<Order> orders = orderService.getOrdersByUserId(userId);

        // (3) 결과 조합 및 응답
        System.out.println("사용자 이름: " + user.name);
        System.out.println("주문 목록:");
        for (Order order : orders) {
            System.out.println("- " + order.product);
        }
    }
}

클라이언트가 호출한 API를 적절한 서비스로 연결해주는 API Gateway입니다.

클라이언트는 userId를 통해 사용자와 주문정보를 조회합니다. 예제를 간단히 하고자 getUserOrders 메서드 내에서 유저 조회와 주문 정보 조회를 함께 작성했습니다.

public class MicroserviceExample {
    public static void main(String[] args) {
        UserService userService = new UserService();
        OrderService orderService = new OrderService();

        APIGateway apiGateway = new APIGateway(userService, orderService);
        apiGateway.getUserOrders(1L); // 클라이언트가 API 호출
    }
}

/* 출력 예시
[APIGateway] 요청 시작: getUserOrders
UserService getUserById called
UserService getOrdersByUserId called
사용자 이름: river
주문 목록:
- iPhone15
*/

이제 메인 메서드에서 각 서비스들을 생성해주고 APIGateway를 통해 호출해 주면 됩니다.

apiGateway.getUserOrders(1L); 이 부분이 실제 구조에서는 API를 호출하는 부분이라 생각하면 됩니다.

마무리

오늘은 마이크로서비스 아키텍처(MSA)에 대해 정리해보았습니다.

아직은 MSA를 직접 적용할 만큼의 대규모 프로젝트를 경험해보지는 못했지만,

이러한 개념들을 미리 이해하고 있는 것만으로도 앞으로 프로젝트를 진행하면서 마주하게 될 여러 문제들에 대해 더 유연하게 접근할 수 있으리라 생각합니다.

MSA가 강조하는 철학과 설계 원칙은 개발뿐만 아니라 협업, 나아가 삶의 태도에도 적용해볼 수 있는 인사이트를 주는 것 같습니다.

다음 글에서는, MSA에서 자주 발생하는 동기 호출 문제를 어떻게 해결할 수 있을지,

그리고 그 대안으로 자주 언급되는 이벤트 기반 아키텍처(Event-driven Architecture)에 대해 공부하고 정리해보겠습니다.

출처

https://www.redhat.com/ko/topics/microservices/what-are-microservices#%EB%8D%94%EC%9A%B1-%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EA%B0%9C%EB%B0%9C-%EB%B0%A9%EC%8B%9D
https://www.atlassian.com/ko/microservices/microservices-architecture/microservices-vs-monolith
https://metanetglobal.com/bbs/board.php?bo_table=tech&wr_id=137
https://learn.microsoft.com/ko-kr/dotnet/architecture/microservices/architect-microservice-container-applications/direct-client-to-microservice-communication-versus-the-api-gateway-pattern

profile
20학번 새내기^^(였음..)

0개의 댓글