[CS] MSA (Microservices Architecture) 학습

dakcoh·2024년 9월 14일

지난번에 레이어드 아키텍처를 간단히 소개했었습니다.

이번에는 더욱 인기가 많아지고 있는 아키텍처인 MSA (Microservices Architecture)에 대해서 학습해보도록 하겠습니다. 요즘 서비스의 다분화와 aws public 클라우드가 대세가 되면서 함께 인기가 높아지는 만큼 확실히 알아보겠습니다.

이번에 주문/결제 서비스를 개발할 예정으로 간단하게 관련된 예시까지 함께 다뤄보도록 하겠습니다.


1. MSA란 무엇인가?

MSA는 간단하게 말하면 하나의 애플리케이션을 독립적으로 배포, 운영할 수 있는 작은 서비스들로 나누는 아키텍처 패턴입니다. 이 각각의 서비스는 자체적으로 실행되며, 각 서비스는 특정 비즈니스 도메인에 맞춘 기능을 담당합니다.

간단하게 정리하자면
각 서비스는 독립적인 비즈니스 로직데이터베이스를 가지며, HTTP REST API나 메시징 시스템을 통해 서로 통신합니다.

특히 MSA는 규모가 큰 모놀리식 아키텍처에서 발생할 수 있는 문제점들(유지 보수의 어려움, 느린 배포 주기 등)을 해결하기 위한 방법론으로 주목받고 있습니다.

MSA 장점과 단점

장점

  • 독립적인 배포: 각 서비스는 다른 서비스와 독립적으로 배포됩니다.
  • 기술 선택 자유: 각 서비스는 자신에게 맞는 기술 스택을 사용할 수 있습니다.
  • 확장성: 필요에 따라 특정 서비스만 스케일 업 할 수 있습니다.

단점

  • 서비스 간 통신 비용: 네트워크를 통한 통신이 필요하므로 성능에 영향을 미칠 수 있습니다.
  • 복잡한 관리: 여러 개의 서비스가 존재하기 때문에 배포 및 관리가 복잡할 수 있습니다.
  • 데이터 일관성: 각 서비스가 자체 데이터베이스를 가지므로 데이터 일관성 유지가 어려울 수 있습니다.

2. MSA 구현하기

이제 간단한 예제를 통해 MSA를 어떻게 구현할 수 있는지 보겠습니다.
예제는 쇼핑몰 시스템을 기반으로, 상품 서비스, 주문 서비스, 결제 서비스 세 가지 서비스로 진행됩니다.

디렉토리 구조

먼저, MSA의 기본적인 디렉토리 구조는 다음과 같습니다.

msa-example/
├── product-service/       => 상품 서비스
│   ├── src/
│   └── pom.xml
├── order-service/         => 주문 서비스
│   ├── src/
│   └── pom.xml
└── payment-service/       => 결제 서비스
    ├── src/
    └── pom.xml
    

각 서비스는 독립적인 Spring Boot 애플리케이션으로 구성되어 있습니다.
src 폴더에는 비즈니스 로직이 들어가며, pom.xml 파일은 Maven을 사용한 의존성 관리 파일입니다.

1. 상품 서비스 (product-service)

상품 서비스를 구현하기 위해, /products API를 통해 상품 목록을 반환하는 간단한 서비스를 작성합니다.

ProductServiceApplication.java

@SpringBootApplication
public class ProductServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProductServiceApplication.class, args);
    }
}

ProductController.java

@RestController
@RequestMapping("/products")
public class ProductController {
    @GetMapping
    public List<String> getProducts() {
        return List.of("Laptop", "Smartphone", "Tablet");
    }
}

2. 결제 서비스 (payment-service)

결제 서비스는 주문에 대한 결제를 처리하는 서비스입니다.
PaymentServiceApplication.java

@SpringBootApplication
public class PaymentServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(PaymentServiceApplication.class, args);
    }
}

PaymentController.java

@RestController
@RequestMapping("/payments")
public class PaymentController {

    @PostMapping
    public String processPayment(@RequestBody Payment payment) {
        return "Payment processed for order ID: " + payment.getOrderId();
    }
}

3. 주문 서비스 (order-service)

주문 서비스는 사용자가 주문을 생성할 수 있는 서비스 입니다.

예시 중 가장 중요한 서비스라고 볼 수있습니다.
즉, 주문을 한다면 상품에 대한 결제를 진행하여 상품/결제 서비스를 호출합니다.

OrderServiceApplication.java

@SpringBootApplication
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

OrderController.java

@RestController
@RequestMapping("/orders")
public class OrderController {
	// 상품 서비스 URL
    private static final String PRODUCT_SERVICE_URL = "http://localhost:8081/products";
	// 결제 서비스 URL
    private static final String PAYMENT_SERVICE_URL = "http://localhost:8082/payments";

    private final RestTemplate restTemplate;
	
    public OrderController(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @PostMapping
    public String createOrder(@RequestBody Order order) {
        // 1. 상품 서비스에서 상품 정보 조회
        List<String> products = restTemplate.getForObject(PRODUCT_SERVICE_URL, List.class);
        
		// 상품이 없다면 반환
        if (!products.contains(order.getProductName())) {
            return "Product not found!";
        }

        // 2. 결제 서비스 호출
        processPayment(order);

        return "Order created for " + order.getProductName();
    }

    private void processPayment(Order order) {
    	// 주문 상품을 저장합니다.
        Payment payment = new Payment();
        payment.setOrderId(order.getOrderId());
        payment.setAmount(order.getAmount());

        restTemplate.postForObject(PAYMENT_SERVICE_URL, payment, String.class);
    }
}

주문 서비스는 상품 서비스를 호출하여 상품이 존재하는지 확인하고, 결제 서비스를 호출하여 결제를 처리합니다. 이와 같은 방식으로 서비스 간의 통신을 처리합니다.


3. 독립적인 배포와 관리

이제 각 서비스가 독립적으로 배포될 수 있도록 Docker와 CI/CD 도구들을 사용한 배포 방법을 살펴보겠습니다.

Docker 사용하기

각 서비스를 Docker 컨테이너로 감싸서 독립적인 배포 환경을 만들 수 있습니다. 각 서비스에 대한 Dockerfile을 작성하여 Docker 이미지를 만들고 실행할 수 있습니다.

Dockerfile 예시 (각 서비스에 대해 작성)

# Base image
FROM openjdk:11-jre

# Copy the jar file
COPY target/product-service.jar /app/product-service.jar

# Run the application
CMD ["java", "-jar", "/app/product-service.jar"]

Dockerfile을 각 서비스에 대해 작성하고,
docker build 명령어를 통해 Docker 이미지를 생성한 후,
docker run 명령어로 각 서비스를 실행할 수 있습니다.

Jenkins나 GitLab CI/CD 활용하기

MSA를 실제로 운영 환경에 배포하려면 JenkinsGitLab CI/CD를 사용하여 자동화된 배포 파이프라인을 설정할 수 있습니다.

.gitlab-ci.yml 예시

stages:
  - build
  - deploy

build:
  stage: build
  script:
    - mvn clean install
    - docker build -t product-service .

deploy:
  stage: deploy
  script:
    - docker run -d -p 8080:8080 product-service

.gitlab-ci.yml 파일을 사용하면, 코드 변경이 있을 때마다 자동으로 빌드하고, Docker 이미지를 생성하여 배포할 수 있습니다.

Docker Compose로 서비스 연결하기

Docker Compose를 사용하여 여러 마이크로서비스를 함께 실행할 수 있습니다. docker-compose.yml 파일을 작성하여 각 서비스를 연결할 수 있습니다.

docker-compose.yml 예시

version: '3'

services:
  product-service:
    build: ./product-service
    ports:
      - "8080:8080"

  order-service:
    build: ./order-service
    ports:
      - "8081:8081"

  payment-service:
    build: ./payment-service
    ports:
      - "8082:8082"

이렇게 하면, docker-compose up 명령어로 모든 서비스를 동시에 실행할 수 있습니다.


마무리

Microservices Architecture (MSA)는 독립적이고 분리된 서비스로 시스템을 구축할 수 있는 아키텍처입니다.

각 서비스는 독립적으로 배포되고 관리되며,
Docker, Jenkins, GitLab CI/CD와 같은 도구들을 사용하여 효율적으로 운영할 수 있습니다.

이 글에서는 Java와 Spring Boot를 사용하여 예제와 함께 간단한 MSA 시스템을 설명했습니다.

향후 github주문/결제 서비스를 개발 해볼 예정입니다.
감사합니다.

profile
포기하기 금지

0개의 댓글