IT Developer/Spring

Spring 기초 <38. Spring Boot에서 CQRS 패턴 적용 및 활용법>

TEMA_ 2025. 4. 23. 13:32
반응형

스프링 Spring

38. Spring Boot에서 CQRS 패턴 적용 및 활용법

 

안녕하세요! 태마입니다.

Spring 기초 강좌입니다.

 

강좌의 경우 

1. 주제 간단 정리

2. 상세 주제 정리

으로 이루어져 있습니다.

 

스프링 Spring

포스팅 시작하겠습니다 :)

 


 

1. 주제 간단 정리

 

1. CQRS(Command Query Responsibility Segregation) 패턴이란?

CQRS(Command Query Responsibility Segregation)는 명령(Command)과 조회(Query)를 분리하는 소프트웨어 아키텍처 패턴
데이터 변경(Write)과 조회(Read)를 독립적으로 처리하여 성능과 확장성을 개선

📌 CQRS 패턴의 핵심 개념

개념설명
Command(명령) 데이터를 변경하는 작업 (예: INSERT, UPDATE, DELETE)
Query(조회) 데이터를 조회하는 작업 (예: SELECT)
책임 분리 Command와 Query를 분리하여 최적화 가능
이벤트 소싱과 결합 가능 데이터 변경을 이벤트 형태로 저장하여 변경 이력을 추적

📌 CQRS 패턴을 활용하면 "데이터 변경과 조회 성능을 각각 최적화 가능"


2. CQRS 패턴을 적용하는 이유

기존의 전통적인 CRUD 방식에서는 하나의 서비스가 모든 작업을 처리하지만, CQRS 패턴은 이를 분리하여 더 효율적으로 운영 가능

📌 CQRS 패턴 적용의 주요 장점

장점설명
읽기/쓰기 성능 최적화 조회 요청과 변경 요청을 독립적으로 처리
확장성(Scalability) 증가 조회(Read)와 변경(Write) 로직을 별도 확장 가능
데이터 정합성 유지 이벤트 소싱(Event Sourcing)과 결합하여 변경 이력을 관리
복잡한 비즈니스 로직 적용 가능 Command와 Query 로직을 개별적으로 최적화

📌 CQRS 패턴을 활용하면 "대규모 시스템에서 확장성과 성능을 극대화 가능"


✅ 여기까지 CQRS 패턴의 개념과 필요성을 배웠습니다!
👉 "그렇다면, Spring Boot에서 CQRS 패턴을 어떻게 적용할까?"
✅ 2부에서 Spring Boot에서 CQRS 패턴을 실제로 적용하는 방법을 배워봅시다!

 

반응형

 


 

2. 상세 주제 정리

 

1. CQRS 패턴을 적용한 프로젝트 구조

CQRS 패턴을 적용할 때, Command와 Query를 별도의 계층으로 분리하는 것이 핵심

📌 Spring Boot CQRS 프로젝트 구조 예시

cqrs-example/
 ├── domain/
 │   ├── entity/        # JPA 엔티티(Entity)
 │   ├── repository/    # Command용 Repository (데이터 저장)
 │   ├── projection/    # Query용 Projection (데이터 조회)
 │
 ├── command/
 │   ├── controller/    # Command API (POST, PUT, DELETE)
 │   ├── service/       # Command Service
 │   ├── handler/       # Command Handler (CQRS 적용)
 │
 ├── query/
 │   ├── controller/    # Query API (GET)
 │   ├── service/       # Query Service
 │   ├── repository/    # Query Repository (별도 DB 또는 캐싱 활용)

Command와 Query의 데이터를 별도로 관리하여 성능 최적화 가능

📌 CQRS 패턴을 활용하면 "데이터 변경과 조회를 분리하여 효율적인 운영 가능"


2. Command(쓰기) 계층 구현

Command 계층에서는 데이터 변경을 담당하며, Command Handler를 통해 명령을 처리함

📌 1️⃣ 엔티티(Entity) 정의 (Order.java)

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String product;
    private int quantity;
    
    public Order(String product, int quantity) {
        this.product = product;
        this.quantity = quantity;
    }
}

JPA를 활용하여 주문(Order) 데이터를 저장하는 엔티티 생성

📌 2️⃣ Command Handler 구현 (OrderCommandHandler.java)

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderCommandHandler {

    private final OrderRepository orderRepository;

    public OrderCommandHandler(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    @Transactional
    public Order createOrder(String product, int quantity) {
        Order order = new Order(product, quantity);
        return orderRepository.save(order);
    }
}

Command Handler는 데이터를 변경하는 비즈니스 로직을 담당

📌 3️⃣ Command API 컨트롤러 구현 (OrderCommandController.java)

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/orders")
public class OrderCommandController {

    private final OrderCommandHandler orderCommandHandler;

    public OrderCommandController(OrderCommandHandler orderCommandHandler) {
        this.orderCommandHandler = orderCommandHandler;
    }

    @PostMapping
    public Order createOrder(@RequestParam String product, @RequestParam int quantity) {
        return orderCommandHandler.createOrder(product, quantity);
    }
}

POST 요청을 통해 새로운 주문을 생성하는 API 구현

📌 Command 계층을 활용하면 "데이터 변경 작업을 독립적으로 관리 가능"


3. Query(읽기) 계층 구현

Query 계층에서는 데이터 조회를 담당하며, 별도의 Query Projection을 사용할 수 있음

📌 1️⃣ Projection(조회 전용 DTO) 정의 (OrderView.java)

public record OrderView(Long id, String product, int quantity) { }

JPA Entity가 아닌 별도의 DTO를 활용하여 조회 성능 최적화 가능

📌 2️⃣ Query Repository 구현 (OrderQueryRepository.java)

import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;

public interface OrderQueryRepository extends JpaRepository<Order, Long> {
    List<OrderView> findAllProjectedBy();
}

프로젝션을 활용하여 조회 성능을 최적화

📌 3️⃣ Query API 컨트롤러 구현 (OrderQueryController.java)

import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/orders")
public class OrderQueryController {

    private final OrderQueryRepository orderQueryRepository;

    public OrderQueryController(OrderQueryRepository orderQueryRepository) {
        this.orderQueryRepository = orderQueryRepository;
    }

    @GetMapping
    public List<OrderView> getOrders() {
        return orderQueryRepository.findAllProjectedBy();
    }
}

조회 요청을 처리하는 API 구현

📌 Query 계층을 활용하면 "조회 전용 데이터베이스나 캐시를 활용하여 성능을 최적화 가능"


✅ 여기까지 Spring Boot에서 CQRS 패턴을 적용하는 방법을 배웠습니다!
👉 "그렇다면, Spring Boot에서 서버 성능을 튜닝하고 최적화하는 방법은?"
✅ 다음 회차에서 Spring Boot에서 서버 성능 튜닝 및 최적화 방법을 배워봅시다!

 

 

반응형