서동우 튜터님 특강

 

  • 관계형 데이터베이스에서는 연관관계FK(외래키)를 통해 달성
    • 여러 테이블이 FK를 통해 연관관계를 맺고 있다면, 조인을 사용하여 데이터를 연결하고 원하는 정보를 가져올 수 있음

 

  • 그러나 객체 관점에서는? →객체 그래프 탐색으로 인한 n+1 문제 발생

 

 

🤔 RDBMS의 관점과 객체 관점이 맞지 않음

ORM : RDMBS와 객체, 둘의 구조가 달라서 미스매치가 발생하는 것을 해결하기 위해 SQL을 자동으로 생성하고, 객체와 데이터베이스를 연결

 

 

 

 

 

 

이전까지는 ERD설계를 통해 외래키를 설정하고, 외래키 기반으로 연관관계를 설정하여 엔티티를 구현했음

연관관계에서도 종속적인 관계가 있음

  • 영속성 전이(Cascade)가 가능
  • 라이프사이클을 공유
    • ex) 주문 아이템은 주문 없이는 존재할 수 없다
      •  

 

독립적인 관계도 있음

  •  주문아이템-상품 : 상품이 먼저 만들어 진 후 주문이 생성될때 상품을 가져옴 → 생명주기가 다름
  • 주문 - 유저 : 유저가 삭제된다고 해서 주문에 영향이 미치면 안됨

 

 

JPA는 종속적인 관계에 대한 기능을 지원하고 있음

ex) 주문을 생성할때 주문 아이템을 생성함, 주문이 삭제될 때 주문 아이템도 삭제함 등..

 

 

 

 

 

이렇게 도메인이 확장되어 복잡하다면 어떻게 해결할 수 있을까? → DDD 

 

 

 


 

도메인 주도 설계 (DDD)

도메인을 중심으로 소프트웨어를 설계하는 것 

 

 

바운디드 컨텍스트(Bounded Context)

큰 시스템을 여러 개의 작은 컨텍스트로 나누어 각 컨텍스트 내에서 특정 비즈니스 규칙과 데이터 모델이 적용하는 것을 의미

각 컨텍스트는 독립적으로 설계되고 구현 됨. 

각 컨텍스트가 독립적으로 동작하도록 설계 → 전체 시스템의 복잡성을 줄이고 유연성을 높임

컨텍스트 간에는 인터페이스를 통해 상호작용

 

 

패키지구조 - 4계층 구조

presentation - application - domain - infrastructure

: DDD에서 가져가는 기본적인 계층 구조

 

 

모놀리식 → MSA 전환을 위한 구조

 

주문이 공통모듈을 통해 상품을 호출하게 함

그 결과 주문-상품 의존하는 코드가 없음 → 서비스를 따로 분리할 수 있음

 

 

애그리거트(Aggregate)

도메인 모델에서 논리적으로 하나의 단위로 묶여야 하는 객체들의 집합

* 애그리거트 루트 : 애그리커드를 대표하는 객체, 애그리거트의 유일한 진입점(Entry Point) - ex)주문, 유저, 가게..

 

애그리거트는 라이프사이클을 공유함 → 애그리거트를 트랜잭션으로 묶음

 

 

🤔 애그리커드와 루트가 필요한 이유?

  • 데이터 일관성 유지
  • 변경 범위 (트랜잭션) 제어
  • 도메인 모델 명확하게 만들기
  • 엔티티 간 직접 참조 방지
  • 데이터 저장소(Repository)와의 관계 정리

 

데이터 저장소(Respository)와의 관계 정리

애그리거트 단위로 데이터 저장소 관리 - 데이터의 무결성 보장

* OrderRespositoryOrder를 저장, Order 내부의 OrderItem도 함께 저장 (Cascade.Persist) - 영속성 전이
→ 별도의 OrderItemRepository는 만들지 않음

* OrderitemRepository가 따로 있으면 Order 없이 OrderItem이 변경 될 가능성이 있음
→ 데이터가 엉켜서 일관성이 깨질 위험이 있음
public Order(List<OrderItem> orderItems) {
	validateOrderItems(orderItems);
    this.orderItems = orderItems;
}​

Order의 생성은 항상 OrderItems와 함께

public void validateOrderItems(List<OrderItem> orderItems) {
	if(orderItem == null || orderItems.isEmpty()) {
    	throw new IllegalArgumentException("주문 항목은 비어 있을 수 없습니다.");
	}
    if (orderItems.size() > MAX_ORDER_ITEMS) { // OrderItem 갯수가 10개를 초과하면 Order 자체를 만들면 안됨
    	throw new IllegalArgumentException("주문 상품 수가 " + MAX_ORDER_ITEMS + "개를 초과하였습니다.");
    }
}

 

 

 


 

 

 

'Back-End > DDD' 카테고리의 다른 글

DIP (Dependency Inversion Principle, 의존성 역전 원칙)  (0) 2025.03.15
아키텍처  (0) 2025.03.15
도메인  (0) 2025.03.14

'Back-End > Spring' 카테고리의 다른 글

[Spring] Page 대신 PagedModel  (0) 2025.04.26
API 테스트를 통한 문서 자동화  (1) 2025.04.13
스케줄러 서비스 분리  (0) 2025.04.04
Feign Client LoadBalancer 의존성 누락  (1) 2025.03.21

✅ 문제 상황

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'deliveryAssignmentScheduler' defined in file [/Users/t2024-m0206/Documents/git/chill/mono-repo/com.sparta.logistics.delivery-service/build/classes/java/main/com/sparta/logistics/delivery_service/application/service/DeliveryAssignmentScheduler.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'deliveryService' defined in file [/Users/t2024-m0206/Documents/git/chill/mono-repo/com.sparta.logistics.delivery-service/build/classes/java/main/com/sparta/logistics/delivery_service/application/service/DeliveryService.class]: Unsatisfied dependency expressed through constructor parameter 1: Error creating bean with name 'deliveryRouteService' defined in file [/Users/t2024-m0206/Documents/git/chill/mono-repo/com.sparta.logistics.delivery-service/build/classes/java/main/com/sparta/logistics/delivery_service/application/service/DeliveryRouteService.class]: Unsatisfied dependency expressed through constructor parameter 1: Error creating bean with name 'com.sparta.logistics.delivery_service.infrastructure.client.HubRouteClient': FactoryBean threw exception on object creation

 

HubRouteClient 빈 생성 과정에서 문제 발생

 

 

✅  원인 분석

배송서비스에는 다른 서비스들과 달리 스케줄러가 포함되어 있음

주기적으로 Feign Client를 통해 다른 서비스를 호출하는 메서드를 실행함

@Scheduled(fixedRate = 60000) //1분
    public void runAssignmentScheduler() {
        if(shutdown) return;

        try {
            deliveryService.assignPendingDeliveries();
            deliveryRouteService.assignPendingDeliveries();
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

 

Feign Client는 서비스 이름으로 API를 호출, 이 서비스 이름을 실제 서버 주소로 변환하기 위해 로드밸런서 필요

@FeignClient(name = "hub-service")
public interface HubRouteClient {
    @GetMapping("/api/hub-routes")
    List<HubRouteListResponseDto> getHubRouteList(@RequestParam UUID startHubId,
                                                 @RequestParam UUID endHubId);
}

 

✅ 문제 해결 시도

 

@Lazy 어노테이션 사용

DeliveryRouteService가 DeliveryService를 의존하고, DeliveryService가 또 다른 클라이언트들을 의존하는 방식에서 순환 의존성이 있을 수 있다고 생각함

→ @Lazy 어노테이션을 사용해 의존성을 지연로딩

빈의 초기화를 애플리케이션 시작 시점이 아닌 실제 사용되는 시점까지 지연 가능

@Service
@RequiredArgsConstructor
public class DeliveryRouteService {
    private final DeliveryRouteRepository deliveryRouteRepository;
    
    @Lazy // 지연 로딩 적용
    private final HubRouteClient hubRouteClient;
    
    @Lazy // 지연 로딩 적용
    private final DeliveryManagerClient deliveryManagerClient;
    
    ...
}

 

✅ 문제 원인

Caused by: java.lang.IllegalStateException: No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?

 

 

 

 

 

 

✅ 해결 방법

로드밸런서 의존성 명시적으로 추가

 

 

 

🤔 왜 로드밸런싱을 추가했어야 했는가?

 

배운점

중요한 기능은 항상 명시적으로 의존성 선언하기

전이적 의존성에 의존하지 않기

@Lazy는 순환 참조 문제에는 효과적이나, 의존성 누락 문제는 해결할 수 없음

 

스케줄러 사용 시 주의점:

  • 스케줄러는 애플리케이션 초기화 단계에서 의존성 문제를 빠르게 드러냄
  • 외부 서비스 호출을 포함하는 스케줄러 사용 시 관련 의존성 신중히 검토
  • @Lazy로 초기화를 지연시켜도 스케줄러 작동 시점에는 모든 의존성이 필요함

 

### 중요한 발견: 순환 참조가 아닌 의존성 누락

처음에는 순환 참조 문제로 의심하여 @Lazy 어노테이션을 적용했으나, 로드밸런서 의존성을 추가한 후에는 @Lazy 어노테이션 없이도 정상적으로 작동했습니다. 이는 실제 문제가 순환 참조가 아니라 순수하게 의존성 누락이었음을 의미합니다.

이는 오류 메시지를 정확히 해석하는 것의 중요성을 보여줍니다. "Unsatisfied dependency" 오류는 순환 참조 문제일 수도 있지만, 이 경우에는 의존성 누락을 나타내는 것이었습니다. 결국 명확한 오류 메시지 해석과 근본 원인 분석이 문제 해결의 핵심이었습니다.

 

 

 

 

Spring Cloud 2020.0.0 이후 버전부터는 spring-cloud-starter-netflix-eureka-client가 자동으로 spring-cloud-starter-loadbalancer를 포함

 

근데? 나는? 

Caused by: java.lang.IllegalStateException: No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?

 

Spring Cloud Feign 클라이언트를 사용할 때 로드밸런싱 기능이 필요한데 관련 의존성이 누락됐다고한다..

 

Feign Client는 @FeignClient(name="service-name") 어노테이션을 통해 서비스 이름으로 요청을 보냄

로드밸런서는 이 서비스의 이름을 실제 서비스 인스턴스 URL로 변환하고 여러 인스턴스 간의 요청을 분산시킴.

 

Spring Cloud Feign → LoadBalancer → Service Discovery

 

@Scheduled 어노테이션은 Spring의 TaskScheduler 호출

Spring Cloud 환경에서 Feign 클라이언트를 주기적으로 호출

 

 

스케줄러가 다른 서비스를 호출하는 Feign Client를 자동으로 실행함

스케줄러: 자동으로 주기적인 서비스 호출 → 애플리케이션 시작 시점부터 로드밸런서가 필요

 

시스템 초기화 순서:

  • 애플리케이션 시작 → 스케줄러 초기화 → TaskScheduler 빈 생성 → Feign Client 초기화 → 로드밸런서 필요
  • 이 흐름에서 로드밸런서가 없으면 Feign Client 초기화가 실패하고, 결국 애플리케이션 시작이 실패합니다.

일반 API 컨트롤러에서는 사용자가 요청할 때만 이런 호출이 발생하므로, 애플리케이션 시작 시점에 로드밸런서 의존성이 확인되지 않을 수 있습니다. 하지만 스케줄러는 애플리케이션 시작 시점에 초기화되므로, 로드밸런서 의존성이 즉시 확인됩니다.

결론적으로, 스케줄러가 직접 로드밸런서를 필요로 하는 것이 아니라, 스케줄러가 호출하는 코드(Feign Client)가 로드밸런서를 필요로 합니다. 그리고 스케줄러는 애플리케이션 시작 시점에 이 의존성을 즉시 드러나게 만드는 역할을 한 것입니다.

 

'Back-End > Spring' 카테고리의 다른 글

[Spring] Page 대신 PagedModel  (0) 2025.04.26
API 테스트를 통한 문서 자동화  (1) 2025.04.13
스케줄러 서비스 분리  (0) 2025.04.04
Custom exception  (0) 2025.03.26
고수준 모듈은 저수준 모듈에 의존하면 안 되고,
저수준 모듈이 고수준 모듈에 의존하도록 해야한다.
→ 둘 다 추상화(인터페이스)에 의존해야 한다.

 

  • 고수준 모듈 : 핵심 비지니스 로직 담당
  • 저수준 모듈 : 세부적인 구현 담당

 

"주문이 완료되면 사용자에게 알람을 보내는 시스템"

 

❌ DIP를 지키지 않은 코드

OrderService(고수준)가 EmailNotification(저수준)에 직접 의존

만약 문자메세지(SMS) 알림을 추가하려면? - OrderService 내부 코드 수정 → OCP 위반

 // 저수준 모듈
 class EmailNotification {
 	public void sendEmail(String message) {
    	System.out.println("이메일전송: " + message);
    }
 }
// 고수준 모듈
class OrderService {
	private EmailNotification emailNotification; // 직접 의존
    
    public OrderService() {
    	this.emailNotification = new EmailNotification();
    }
    
    public void completeOrder() {
    	System.out.println("주문 완료!");
        emailNotification.sendEmail("주문이 완료되었습니다");
    }
 }

 

 

✅ DIP를 적용한 코드

인터페이스(추상화)를 사용하여 의존성 역전

OrderService(고수준) → Notification 인터페이스(추상화)에만 의존

EmailNotification, SmsNotification을 자유롭게 교체 가능 → OCP 만족

새로운 알림 방식 추가 시 OrderService 수정 불필요

// 추상화(인터페이스)
public interface Notification {
	void send(String message);
}
// 저수준 모듈1
pulbic class EmailNotification implements Notification {
	pulbic void send(String message) {
    	System.out.println("이메일발송: " + message);
    }
}

// 저수준 모듈2
public class SmsNotification implements Notification {
	public void send(String message) {
    	System.out.println("문자발송: " + message);
    }
}

 

 

 


 

 

'가격 할인 계산' 입장에서는 어떻게 구현했는지 중요하지 않음

'고객정보와 구매 정보에 룰을 적용해서 할인 금액을 구한다' 만 중요함

// 룰을 이용해 할인 금액 계산
public interface RuleDiscounter {
	Money applyRules(Customer customer, List<OrderLine> orderLines);
}
public class CalculateDiscountService {
	
    private RuleDiscounter ruleDiscounter;
    
    public CalculateDiscountService(RuleDiscounter ruleDiscounter) {
    	this.ruleDiscounter = ruleDiscounter;
    }
    
    public Money calculateDiscount(List<OrderLine> orderLines, String customerId) {
    	Customer customer = findCustomer(customerId);
        
        return ruleDiscount.applyRules(customer, orderLines);
   	}

'가격 할인 계산'은 구현 기술인 Drools에 의존하지 않고

'룰을 이용한 할인 금액 계산'을 추상화한 RuleDiscounter 인터페이스에 의존

// RuleDiscounter를 상속받아 구현한 할인 규칙 적용 구현체
public class DroolsRuleDiscounter implements RuleDiscounter {
	
    ...
    
    @Override
    public Money applyRules(Customer customer, List<OrderLine> orderLines) {
    	KieSession kSession = kContainer.newKieSession("discountSession");
        ..
        return money.toImmutableMoney();
    }
 }

 

 

 

DIP를 적용하면 저수준 모듈이 고수준 모듈에 의존

 

 

* DIP 주의사항

저수준 모듈에서 인터페이스를 추출하는 경우가 있음

DIP를 적용할 때 하위 기능을 추상화한 인터페이스는 고수준 모듈 관점에서 도출해야 함

 

 

 

이메일 알람을 SMS으로 변경하거나 Jpa 대신 MyBatis를 구현 기술로 변경하고 싶다면

응용 영역과 도메인 영역은 수정 없이 구현체만 바꾸면 됨

'Back-End > DDD' 카테고리의 다른 글

도메인 주도 설계의 필요성  (0) 2025.03.27
아키텍처  (0) 2025.03.15
도메인  (0) 2025.03.14

 

이전까지 프로젝트 할 때는 
Contoller / Service / Entity / Repository / Dto 
이런 패키지 구조로 진행했음

도메인 중심 설계 (DDD, Domain-Driven Design)에 기반한 구조는
Presentation / Application(Service) / Domain / InfraStructure
이렇게 구성됨

이전에는 Service에서 비지니스 로직을 수행했는데 DDD 계층 구조에서는 Service에서는 로직을 직접 수행하지 않고 Domain 을 조합하거나 도메인 모델에 로직 수행을 위임함
핵심 로직은 도메인

 


Service : 로직을 직접 구현하지 않고 도매인 객체를 조합하는 역할

@Service
@RequiredArgsConstructor
public class OrderService {

    private final OrderRepository orderRepository;
    private final ProductRepository productRepository;
    private final MemberRepository memberRepository;

    public Long createOrder(OrderRequest orderRequest) {
        // 1. 주문 정보를 생성
        Member member = memberRepository.findById(orderRequest.getMemberId())
                .orElseThrow(() -> new IllegalArgumentException("회원 정보를 찾을 수 없습니다."));
        
        Product product = productRepository.findById(orderRequest.getProductId())
                .orElseThrow(() -> new IllegalArgumentException("상품 정보를 찾을 수 없습니다."));

        Order order = new Order(member, product, orderRequest.getQuantity());
        
        orderRepository.save(order);

        return order.getId();
    }
}​

 

 


Domain : 핵심 비지니스 로직

@Getter
public class Order {
    private final Long id;
    private final Member member;
    private final Product product;
    private int quantity;
    private OrderStatus status;

    public Order(Member member, Product product, int quantity) {
        validateStock(product, quantity);
        this.member = member;
        this.product = product;
        this.quantity = quantity;
        this.status = OrderStatus.PENDING;
    }

    private void validateStock(Product product, int quantity) {
        if (product.getStock() < quantity) {
            throw new IllegalStateException("재고가 부족합니다.");
        }
    }

    public void completeOrder() {
        if (this.status != OrderStatus.PLACED) {
            throw new IllegalStateException("주문이 완료될 수 없습니다.");
        }
        this.status = OrderStatus.COMPLETED;
    }

    public void cancel() {
        if (this.status != OrderStatus.PENDING) {
            throw new IllegalStateException("이미 처리된 주문은 취소할 수 없습니다.");
        }
        this.status = OrderStatus.CANCELED;
    }
}

 

'Back-End > DDD' 카테고리의 다른 글

도메인 주도 설계의 필요성  (0) 2025.03.27
DIP (Dependency Inversion Principle, 의존성 역전 원칙)  (0) 2025.03.15
도메인  (0) 2025.03.14

 도메인 모델 패턴 / 도출 

 

아키텍처 구성

  • 표현(Presantation)  : 사용자 요청 처리, 사용자에게 정보 보여줌
  • 응용(Application) : 사용자가 요청한 기능 실행, 업무 로직 직접 구현x, 도메인 계층을 조합해 기능 실행
  • 도메인(Domain) : 시스템이 제공할 도메인 규칙 구현
  • 인프라스트럭처(Infrastructure) : 데이터베이스나 메시징시스템과 같은 외부 시스템 연동 처리

 

도메인

시스템이 제공할 도메인 규칙 구현

 

온라인 서점 서비스의 주문 도메인을 만든다고 할 때,

'출고 전 배송지를 변경할 수 있다' , '주문 취소는 배송 전에만 할 수 있다' 라는 규칙을 구현한 코드가 도메인 계층에 위치

public class Order {
	private OrderState state;
    private ShippingInfo shippingInfo;
    
    public void changeShippingInfo(ShippingInfo newShippingInfo) {
    	if(!isShippingChangeable()) {
        	throw new IllegalStateException("cant' change shippingn int " + state);
        }
        	this.shippingInfo = newShippingInfo;
    }
    
    private boolean isShippingChangeable() {
    	return state == OrderState.PAYMENT_WAITING ||
        	state == OrderState.PREPARING;
    }
 }
 
 
 public enum OrderState {
 	PAYMENT_WAITING, PREPARING, SHIPPED, DELIVERING, DELIVERY_COMPLETED;
 }

배송지 변경 가능 여부를 판단하는 기능 - 주문과 관련된 중요 업무 규칙을 주문 도메인 모델인 Order나 OrderState에 구현

 

핵심 규칙을 구현한 코드는 도메인 모델(도메인 계층의 객체 모델)에 위치

▶ 규칙이 바뀌거나 규칙을 확장해야 할 때 다른 코드에 영향을 덜 주고 변경 내역을 모델에 반영할 수 있게 됨

 

 

 


 

 엔티티 

엔티티의 가장 큰 특징은 식별자를 가진다는 것

식별자는 엔티티 객체마다 고유해서 각 엔티티는 서로 다른 식별자를 

밸류 타입

ShippingInfo 클래스의 receiverName / reveiverPhoneNumber 필드는 다른 데이터를 담고 있지만 개념적으로는 받는 사람을 의미

즉 두 필드는 실제로 하나의 개념을 표현하고 있음

 

밸류타입은 개념적으로 완전한 하나를 표현할 때 사용

 

 

price와 amounts 는 '돈'을 의미 - Money타입

// 밸류타입 사용 전
public class OrderLine {
	private Product product;
    priavte int price;
    private int quantity;
    private int amounts;
    
    public OrderLine(Product product, int price int quantity) {
    	this.product = product;
        this.price = price;
        this.quantity = quantity;
        this.amounts = calculateAmounts();
    }
    
    private int calculateAmounts() {
    	return price * quantity;
    }
    ..
  }
// 밸류타입 사용 후 - 코드 가독성 향상
public class OrderLine {
	private Product product;
    priavte Money price;
    private int quantity;
    private Money amounst;
    
    public OrderLine(Product product, Money price int quantity) {
    	this.product = product;
        this.price = price;
        this.quantity = quantity;
        this.amounts = caluculateAmounts();
    }
    
    private int calculateAmounts() {
    	return price.multiply(quantity);
    }
    ..
  }

 

또한 밸류타입은 밸류 타입을 위한 기능을 추가할 수 있음

public class Money {
	private int value;
    
    ... 
    
    public Money add(Money money) {
    	return new Money(this.value + money.value);
    }
    
    // 정수 타입 연산이 아니라 돈 계산이라는 의므로 코드를 작성 할 수 있음
    public Money multiply(int multiplier) {
    	return new Money(value * multiplier);
    }
  }

 

 

식별자를 위한 밸류 타입을 사용하여 필드의 의미가 잘 드러나도록 할 수 있음

 

 

 

 

'Back-End > DDD' 카테고리의 다른 글

도메인 주도 설계의 필요성  (0) 2025.03.27
DIP (Dependency Inversion Principle, 의존성 역전 원칙)  (0) 2025.03.15
아키텍처  (0) 2025.03.15

 

 RabbitMQ 

  • 메세지 브로커 - 데이터(메세지)를 프로듀서로부터 컨슈머에게 전달하는 중간 매개체
  • 메세지를 큐(queue)에 저장하고 필요할 때 적절한 수신자에게 전달
  • 비동기 처리 - 시스템의 응답성을 높임

 

✅ 큐에 대한 순서는 보장되지만 컨슈머에 대한 순서 보장은 안될 수 있다는 것을 염두해야 함

 

 

도커를 사용하여 RabbitMQ 설치

docker run -d --name rabbitmq -p5672:5672 -p 15672:15672 --restart=unless-stopped rabbitmq:management

 

RabbitMQ 대시보드

http://localhost:15672

'Back-End > Large-Scale Streaming' 카테고리의 다른 글

JMeter로 성능 테스트  (0) 2025.04.22
[대규모 스트림 처리] 분산 트랜잭션  (0) 2025.03.10
대규모 시스템에서는 데이터의 일관성을 유지하는 것이 중요
이를 위해 분산 트랜잭션, 이벤트 소싱, CQRS(Command Query Responsibility Segregation) 등의 기법을 사용

 

 2PC (Two-Phase Commit) 

  • 준비 단계 (Prepare Phase) : 각 참여 노드는 트랜잭션 준비 상태르 확인, 준비 완료를 마스터 노드에 알림
  • 커밋 단계 (Commit Phase) : 마스터 노드는 모든 참여 노드가 준비되었음을 확인 후 트랜잭션을 커밋하도록 지시. 준비가 완료되지 않은 노드가 있다면 롤백

모든 단계가 한 트랜잭션

 

 사가 패턴 (Saga Pattern) 

 

트랜잭션을 여러 단계로 나누어 처리

각 단계가 독립적으로 커밋

각 단계가 성공적으로 완료되면 다음 단계로 넘어가고 실패 시 보상 트랜잭션을 실행하여 상태를 롤백 

 

'Back-End > Large-Scale Streaming' 카테고리의 다른 글

JMeter로 성능 테스트  (0) 2025.04.22
[대규모 스트림 처리] RabbitMQ  (0) 2025.03.11

+ Recent posts