스프링 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에서 서버 성능 튜닝 및 최적화 방법을 배워봅시다!
'IT Developer > Spring' 카테고리의 다른 글
Spring 기초 <40. 실전 프로젝트에서 Spring Boot 적용 Best Practices> (0) | 2025.04.25 |
---|---|
Spring 기초 <39. Spring Boot에서 서버 성능 튜닝 및 최적화 방법> (0) | 2025.04.24 |
Spring 기초 <37. Spring Boot에서 멀티 모듈(Multi-Module) 프로젝트 설계> (0) | 2025.04.22 |
Spring 기초 <36. Spring Boot에서 외부 API 호출 (RestTemplate vs WebClient 비교)> (0) | 2025.04.21 |
Spring 기초 <35. Spring에서 커스텀 애너테이션(Custom Annotation) 만들기> (1) | 2025.04.20 |
Spring 기초 <34. Spring Boot 실무 Best Practices 및 최신 트렌드 (Reactive Programming, Cloud Native)> (1) | 2025.04.19 |
Spring 기초 <33. Spring Boot에서 A/B 테스트와 Feature Toggle 적용> (0) | 2025.04.18 |
Spring 기초 <32. Spring Boot에서 OpenTelemetry를 활용한 애플리케이션 모니터링> (0) | 2025.04.17 |