일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 1차원 DP
- 2차원 dp
- 99클럽
- @BeforeAll
- @BeforeEach
- @Builder
- @Entity
- @GeneratedValue
- @GenericGenerator
- @NoargsConstructor
- @Query
- @Table
- @Transactional
- Actions
- Amazon EFS
- amazon fsx
- Android Studio
- ANSI SQL
- ApplicationEvent
- assertThat
- async/await
- AVG
- AWS
- Azure
- bind
- builder
- button
- c++
- c++ builder
- c03
- Today
- Total
기록
[JpaTest] CQRS와 서비스 레이어에서의 트랜잭션 관리 본문
CQRS 개념
CQRS(Command Query Responsibility Segregation)는 소프트웨어 아키텍처 스타일 중 하나로, 시스템의 명령(Command)과 조회(Query)를 분리하여 각자의 책임을 명확히 정의합니다. 이는 대규모 애플리케이션에서 복잡성을 줄이고 성능을 최적화하기 위한 강력한 설계 방식으로, 다음과 같은 특징이 있습니다:
- 명령(Command): 데이터 변경 작업을 처리하며, 데이터베이스에 새로운 데이터를 삽입하거나 기존 데이터를 수정 및 삭제하는 역할을 합니다. 이러한 작업은 주로 비동기적으로 처리되며, 트랜잭션을 통해 데이터의 일관성을 보장합니다.
- 조회(Query): 데이터를 읽는 작업으로, 시스템 상태를 변경하지 않고 필요한 정보를 클라이언트에게 반환합니다. 조회 작업은 일반적으로 고성능이 요구되며 읽기 전용 트랜잭션에서 수행됩니다.
CQRS는 명령과 조회를 물리적으로 분리함으로써 성능 병목을 최소화하고, 데이터 모델을 각 작업에 최적화할 수 있는 유연성을 제공합니다.
읽기 전용 트랜잭션과 기본 설정
@Transactional(readOnly = true) 어노테이션은 JPA와 같은 ORM 도구를 사용할 때 읽기 전용 작업의 성능을 최적화하는 데 중요한 역할을 합니다. 이 설정은 다음과 같은 이유로 기본값으로 사용됩니다:
- 성능 향상: 읽기 전용 트랜잭션은 플러시(flush) 호출을 방지하여 데이터베이스 리소스를 절약하고 처리 속도를 증가시킵니다. 예를 들어, 대규모 전자상거래 플랫폼에서는 상품 조회와 검색 요청이 전체 트래픽의 80% 이상을 차지하는 경우가 일반적입니다. 이러한 읽기 중심 시스템에서는 readOnly 설정이 응답 시간을 단축시키는 데 효과적입니다.
- 데이터 무결성 보장: 읽기 전용 트랜잭션은 데이터 변경 작업이 발생하지 않도록 보장하여 실수로 데이터베이스 상태가 변경되는 상황을 방지합니다.
쓰기 작업과 트랜잭션 관리
쓰기 작업은 데이터베이스 상태를 변경해야 하므로, 별도의 @Transactional 어노테이션이 필요합니다. 이는 다음과 같은 이유에서 중요합니다:
- 읽기 전용 트랜잭션에서는 데이터 변경이 불가능하며, 변경된 데이터를 데이터베이스에 영속적으로 저장하거나 커밋하는 과정이 필요하기 때문입니다.
- 쓰기 작업은 종종 여러 테이블에 걸쳐 일관된 상태 변경을 요구하며, 이를 위해 트랜잭션 경계 내에서 처리해야 합니다.
서비스 레이어에서의 트랜잭션 관리
다음은 CQRS 원칙을 준수하며 서비스 레이어에서 읽기 및 쓰기 작업을 관리하는 예제 코드입니다:
@Transactional(readOnly = true)
@RequiredArgsConstructor
@Service
public class ProductService {
private final ProductRepository productRepository;
private final ProductNumberFactory productNumberFactory;
// 읽기 작업: readOnly 트랜잭션 적용
public List<ProductResponse> getSellingProducts() {
List<Product> products = productRepository.findAllBySellingStatusIn(ProductSellingStatus.forDisplay());
return products.stream().map(ProductResponse::of)
.collect(Collectors.toList());
}
// 쓰기 작업: 기본 트랜잭션(readOnly=false)
@Transactional
public ProductResponse createProduct(ProductCreateServiceRequest request) {
String nextProductNumber = productNumberFactory.createNextProductNumber();
Product product = request.toEntity(nextProductNumber);
Product savedProduct = productRepository.save(product);
return ProductResponse.builder()
.id(savedProduct.getId())
.productNumber(nextProductNumber)
.type(savedProduct.getType())
.price(savedProduct.getPrice())
.sellingStatus(savedProduct.getSellingStatus())
.name(savedProduct.getName())
.build();
}
}
CQRS와 트랜잭션 관리의 중요성
서비스 레이어에서 @Transactional(readOnly = true)를 기본값으로 설정하고 쓰기 작업에만 명시적으로 트랜잭션을 추가하는 접근 방식은 다음과 같은 이점을 제공합니다:
- 책임 분리: CQRS의 원칙에 따라 명령과 조회의 책임을 분리하면, 각 작업의 역할과 성능 요구사항에 최적화된 설계를 구현할 수 있습니다.
- 성능 최적화: 읽기 작업에서 불필요한 데이터베이스 작업(예: 플러시 호출)을 제거하여 리소스를 효율적으로 사용합니다.
- 안정성 향상: 읽기 작업 중 데이터 변경을 방지함으로써 안정성을 보장합니다.
- 확장성 강화: 읽기와 쓰기 작업을 물리적으로 분리하면 트래픽 급증 시 성능 병목 현상을 완화하고 확장성을 향상시킬 수 있습니다.
결론
CQRS는 명령과 조회를 분리하여 성능과 유지보수성을 동시에 향상시키는 강력한 설계 패턴입니다. 서비스 레이어에서 읽기 작업을 위한 @Transactional(readOnly = true)를 기본값으로 설정하고, 쓰기 작업에 별도의 트랜잭션을 명시적으로 정의하는 것은 CQRS 원칙을 실천하는 접근 방식입니다.
'교육 > 강의' 카테고리의 다른 글
[테스트전략] 각 테스트는 하나의 목적만 가진다 (0) | 2025.02.09 |
---|---|
[Mock] 스프링 환경에서 외부 시스템 테스트하기 (0) | 2025.02.06 |
[JpaTest] Spring Bean Validation 활용 가이드 (0) | 2025.02.04 |
[JpaTest] Spring Layered Architecture와 Layer 테스트 전략 (0) | 2025.02.03 |
[JpaTest] Layered Architecture vs Hexagonal Architecture (0) | 2025.01.26 |