[Event 기반 설계] Transactional Outbox 패턴이 필요한 이유 : 프로젝트 적용 사례

2024. 9. 26. 00:21·Side Project/Study Together

개요

이번 포스트에서는 프로젝트를 진행하며 이벤트 기반 설계를 도입한 이유와 그 과정에서 Transactional Outbox Pattern의 필요성을 느낀 배경, 그리고 구현 내용을 정리하고자 합니다.


서론

웹 애플리케이션 서비스에서 회원가입 시, 사용자가 입력한 이메일이 유효한지 확인하는 이메일 인증 절차는 필수적입니다. 이는 사용자와 서비스 모두에게 중요한 과정입니다.

 

먼저, 사용자가 이메일을 정확히 입력했는지 확인하는 것은 필수입니다. 오타나 잘못된 이메일 주소로 인해 중요한 알림을 받지 못할 수 있기 때문입니다. 예를 들어, 비밀번호를 잊었을 때 새 비밀번호를 이메일로 전송해야 하는 경우, 올바른 이메일 주소가 필요합니다.

 

서비스 운영 측면에서도 이메일의 유효성은 중요합니다. 광고나 공지 사항을 보낼 때, 정확한 이메일 정보는 효과적인 마케팅과 원활한 사용자 소통을 가능하게 하기 때문입니다.

 

그런데, 이메일 인증 프로세스가 시스템 구조 내에서 제대로 설계되지 않으면 확장성, 유지보수성, 그리고 시스템 안정성 측면 등 다양한 문제가 발생할 수 있습니다.

 

현재 제 애플리케이션의 이메일 인증 프로세스를 개선하고자 이벤트 기반 아키텍처(Event-Driven Architecture)를 도입하여 회원가입과 이메일 전송 기능을 분리해 보았습니다. 또한, 다양한 이벤트의 확장성을 위해 이벤트 추상화 작업도 함께 진행했습니다.


기존 회원가입 로직의 문제점

✔️ 비즈니스 요구사항 : 회원가입 후 이메일 인증 프로세스

 

1. 회원가입 : 사용자가 회원가입을 완료한다.

2. 이메일 발송 : 사용자가 회원가입시 입력한 이메일로 인증 메일을 발송한다.

3. 이메일 인증 : 이메일에 포함된 링크를 클릭해 인증 완료한다.

 

 

✔️ 기존 구조의 문제점: 회원가입 로직 내에서 이메일 전송

@Service
public class MemberService {

    @Autowired
    private NotificationService notificationService;
    @Autowired
    private MemberRepository memberRepository;
	
    @Transactional
    public void registerUser(String userId, String userEmail) {
        
    	// 회원가입 로직 생략
        
        // 이메일 전송
    	notificationService.sendEmail();
   }
  
}

 

기존 구조는 회원가입 로직 내에 이메일 전송 메서드가 존재합니다. 이러한 구조는 아래와 같이 세 가지 관점에서 바라봤을 때 향후 문제가 발생할 여지가 있습니다.

 

1. 비즈니스 확장성

2. 시스템 안정성

3. UX


비즈니스 확장성 관점

훗날 서비스가 운영되는 과정에서 비즈니스 요구사항이 추가 되는 경우를 가정해 보겠습니다.

 

✅ case 1: 사용자가 회원가입하면 이메일 외에도 SMS, 카카오톡 등 다양한 알림을 추가해야 할 수 있다.

✅ case 2: 회원가입 시 관리자에게 SMS 알림이 필요할 수 있다.

 

우선, 이메일 전송 기능을 알림 기능(NotificationService)이라고 칭하겠습니다.

 

현재는 이 알림 기능이 Member 도메인과 강하게 결합된 구조입니다.

 

강결합이란, Member 도메인과 알림 기능이 높은 의존성을 가지는 것을 의미합니다. 보통 한 도메인이 다른 도메인의 구체적인 구현 세부 사항을 알거나, 해당 동작에 직접적으로 의존하는 경우 강한 결합을 가지고 있다고 말합니다.

 

현재 회원가입 로직에서는 알림 기능(NotificationService)의 메서드를 직접적으로 호출하고 있습니다. 이러한 경우, 비즈니스 요구사항이 늘어나거나 변경이 되면 확장이 까다로워집니다.

 

예를 들어, 회원가입 시 SMS 전송도 추가적으로 필요하다는 비즈니스 요구사항이 생길 경우입니다.

@Service
public class MemberService {
	
    @Autowired
    private NotificationService notificationService;
    @Autowired
    private MemberRepository memberRepository;
	
    @Transactional
    public void registerUser(String userId, String userEmail) {
        
        // 회원가입 로직
        Member member = new Member(...);
        memberRepository.save(member);
      
        notificationService.sendEmail("email@google.com");
        // SMS 전송 메서드 추가
        notificationService.sendSMS("010-1234-2121");
    }
}

 

문자 전송 메소드가 회원 가입 로직에 추가되었습니다. 지금은 비즈니스 요구사항 추가가 겨우 문자 전송 하나뿐이지만, 만약 지속적으로 추가사항이 생긴다면 어떻게 될까요?

 

마찬가지로, 회원가입 로직에 지속적으로 요구사항 관련 메서드들이 추가됩니다. 즉, 새로운 알림 수단이 추가될 때마다 기존 MemberService 클래스에 새로운 메서드와 로직을 추가해야 합니다.

 

알림 전송 방식이나 로직이 변경되면, MemberService와 같은 핵심 도메인 서비스에도 변경이 필요하므로 수정 작업도 동반하게 됩니다.

 

더불어, 현재 구조는 MemberSerivce를 테스트할 때 알림 전송 기능까지 함께 테스트해야 합니다. 그러므로 단위 테스트마저 복잡해집니다.

 

 

여기서 중요한 개념은 "관심사의 분리"입니다. 회원가입과 알림 기능은 관심사가 다릅니다. 이는 곧 두 기능의 변경 시점이 다르다는 것을 의미합니다. 구체적으로 말하면, 두 기능이 각각의 변화와 요구 사항에 따라 독립적으로 수정되어야 한다는 것입니다.

 

회원가입 기능은 Member 객체 생성과 저장에 초점을 맞추는 반면, 이메일 알림 기능은 사용자에게 필요한 정보를 전달하는 역할을 합니다. 이 두 기능을 하나의 흐름으로 처리하게 되면, 한쪽의 변경 사항이 다른 쪽에 영향을 미쳐 유지보수성이 떨어지게 됩니다.

 

관심사를 분리하면 회원가입 로직을 수정할 때 알림 기능에 영향을 주지 않고, 반대로 알림 요구사항이 변경되더라도 회원가입 기능을 수정할 필요가 없습니다.

 


 

시스템 안정성(장애 전파 격리)

 

현재 구조에서는 회원가입 중 알림 기능에 장애가 발생하면 회원가입에도 영향을 미치게 됩니다. 이는 시스템 안정성과 직결되며, 시스템의 안정성을 위협할 수 있는 위험 요소입니다.

 

시스템 안정성이란 시스템이 예기치 않은 상황이나 오류가 발생하더라도 시스템이 정상적으로 동작을 유지하거나, 최소한의 기능만으로 신속히 복구할 수 있는 능력을 말합니다.

 

시스템 안정성은 비즈니스에서 매우 중요합니다. 시스템이 불안정하면 사용자 경험이 저하되고, 데이터 무결성에도 문제가 발생할 수 있으며, 결국 운영 비용 증가와 같은 부정적인 결과로 이어질 수 있습니다.

 

자세한 이해를 위해 코드를 설명해 보겠습니다.

@Service
public class MemberService {

    @Autowired
    private NotificationService notificationService;
    @Autowired
    private MemberRepository memberRepository;
	
    @Transactional
    public void registerUser(String userId, String userEmail) {
        
        // 회원가입 로직
        Member member = new Member(...);
        memberRepository.save(member);
      
        notificationService.sendEmail("email@google.com");
    }
}

 

현재 registerUser() 메서드는 @Transactional 어노테이션을 사용하여 회원가입 기능의 원자성을 보장하고 있습니다.

 

이로 인해 회원가입 중 데이터가 성공적으로 영속화되더라도, 알림 기능에서 오류가 발생하면 전체 트랜잭션이 rollback 됩니다. 따라서 회원가입 전체가 실패하는 방식으로 동작합니다.

 

이러한 구조에서는 사용자는 회원가입을 다시 시도해야 하며, 알림 기능의 장애가 장기화될 경우 회원가입이 지속적으로 실패할 수 있습니다.

 

이 상황에서 엔지니어는 시스템 안정성을 보장하기 위해 장애 전파를 격리해야 합니다. 즉, 이메일 전송 기능과 회원가입 로직이 상호 영향을 미치지 않도록 기능을 분리하는 것이 필요합니다.


UX 관점

이메일 발송 과정에서 딜레이가 발생하면 회원가입 API 응답이 지연될 수 있습니다. 이러한 지연이 길어질 경우, 사용자는 불편함을 느끼게 되며, 심각한 경우 서비스에 대한 신뢰도가 떨어질 수 있습니다. 딜레이는 사용자 경험에 중요한 영향을 미치므로, 이를 최소화하는 것은 매우 중요합니다.


Event-Driven Architecture 도입하기

✔️ Event-Driven Architecture 선택 이유

 

기존 구조의 문제점을 개선하기 위한 세 가지 방안을 모색해 보았습니다.

 

1. Notification API 만들기

2. 비동기

3. 이벤트 기반 구조(Event-Driven Architecture )

 

 

(1) 첫 번째, Notification API의 문제

 

: 새로운 Notification API를 생성하고, 회원가입이 완료되면 클라이언트가 별도로 Notification API를 호출하는 방법입니다.

 

이는 API 수준에서 두 관심사를 분리하는 방법입니다. Member 도메인과 Notification 기능을 분리할 수 있는 방법으로 고안했습니다.

 

그러나, 이 방식은 신뢰성 문제를 야기할 수 있습니다. 클라이언트가 알림 요청의 주체가 되는 경우, 악의적인 사용자가 의도적으로 API를 호출한다면 서버는 이를 방지할 수 없으며, 결국 보안 취약점으로 이어질 수 있습니다.

 

물론 Notification API 호출 시 내부 로직에서 사용자의 이메일 인증 여부를 확인하여, 이메일 인증이 되지 않은 사용에게만 이메일을 전송되도록 구현할 수도 있습니다. 그러나 이는 Notification 기능이 Member 도메인과 다시 결합되는 구조로 현재 방향성에 맞지 않습니다.

 

 

(2) 두 번째, 비동기 방식의 한계

 

: 스프링의 @Async 어노테이션을 사용하여 비동기 메서드를 구현하는 방법입니다.

@Service
public class NotificationService {

    // 비동기 방식으로 알림을 전송
    @Async
    public void sendEmail(String email) {
    	//...
    }
}

@Service
public class MemberService {

    @Autowired
    private NotificationService notificationService;
    @Autowired
    private MemberRepository memberRepository;
	
    @Transactional
    public void registerUser(String userId, String userEmail) {
        
        // 회원가입 로직
        Member member = new Member(...);
        memberRepository.save(member);
      
        notificationService.sendEmail("email@google.com");
    }
}

 

이는 회원가입 로직과 Notification(알림) 기능의 동작 흐름을 분리하기 위한 방법입니다. 또한, 알림 기능의 장애가 회원가입 로직에 영향을 미치지 않게 됩니다. 그러나 여전히 회원가입 로직과 알림 전송 로직이 논리적으로 결합된 한계가 존재합니다.

 

 

(3) 이벤트 기반 구조 선택

이벤트 기반 구조(Event-Driven Architecture)란, 시스템의 구성 요소가 이벤트를 통해 상호작용하고 통신하는 패턴입니다. 이 구조에서는 시스템 내의 구성 요소들이 서로 직접 호출하지 않습니다. 대신, 이벤트를 발생시키고 해당 이벤트를 처리하는 방식으로 통신합니다.

 

이벤트 기반 구조는 위에서 살펴본 두 가지 방법의 문제를 해결하면서 확장성과 시스템 안정성, 관심사 분리를 모두 충족시킬 수 있습니다.

 

✅ 시스템의 안정성

회원가입 로직과 알림 로직이 분리되어 있어, 알림 전송이 실패하거나 지연되더라도 회원가입에는 영향을 미치지 않습니다. 이로 인해 시스템의 안정성이 높아지고, 각각의 기능이 독립적으로 동작할 수 있는 기반을 마련합니다.

 

✅ 동작 흐름 분리

회원가입 로직은 성공 후 이벤트를 발행하는 역할만 하고, 알림 전송 등 부가적인 작업들은 비동기적으로 처리됩니다. 이를 통해 시스템이 더 빠르게 응답하고, 메인 로직의 복잡성이 줄어듭니다.

 

✅ 논리적인 개념(관심사) 분리

이벤트 기반 구조는 각 기능을 독립된 책임 단위로 나눕니다. 회원가입은 사용자 등록에만 집중하고, 알림 전송은 이벤트 리스너에서 처리됩니다. 이를 통해 관심사를 명확히 분리하여 코드의 유지보수성과 확장성이 강화됩니다.


Event-Driven Architecture 구현하기

✔️ 구현 전략 선택하기

 

이벤트 기반 구조를 구현하는 방법은 여러 가지가 있으며, 시스템의 요구사항과 목적에 따라 적절한 방법을 선택할 수 있습니다. 예를 들어, 서버 내 이벤트 큐, Apache Kafka, RabbitMQ, Redis Pub/Sub 등이 대표적인 구현 방법입니다. 또한, 이벤트 스토리지를 사용하는 Outbox Pattern도 사용할 수 있습니다.

 

서버 내 이벤트 큐(In-Memory Event Queue)는 이벤트를 메모리 내 큐에 저장하고 이를 소비하는 방식입니다. 구현이 간단하고, 메모리 기반으로 성능이 빠르나 스케일링이 어렵고 서버를 재시작하면 큐의 데이터가 사라질 수 있습니다.

 

Redis의 Pub/Sub 기능도 메시지 발행과 구독에 사용할 수 있습니다. 이 또한 메모리 기반의 데이터 저장소로 매우 빠른 성능을 제공하나, 메시지의 내구성을 보장하지 않으며 확장성 문제가 발생할 수 있습니다.

*(Redis AOF 활용하여 영속성을 보장하는 방법도 있으나, 해당 포스트에선 다루지 않겠습니다.)

 

 

대체로 이벤트 기반 구조를 가진 서비스에서는 외부 메시지 브로커를 사용해 이벤트를 발행하고, 관련 작업을 비동기적으로 처리합니다. 이러한 메시지 브로커에는 Kafka, RabbitMQ 등이 존재합니다.

 

그러나, 단순히 메시지 브로커에만 의존하여 이벤트를 발행하고 처리하기엔 한계가 존재합니다.

 

현재 회원가입 로직을 예로 들어보겠습니다. 사용자가 회원가입을 수행하면 다음과 같은 과정이 진행됩니다.

1. Member 객체 생성 및 DB 영속화 : DB 트랜잭션이 발생합니다.

2. 메시지 브로커에 회원가입 완료 메시지 발행 : 회원가입 완료 이벤트가 메시지 브로커에 발행됩니다.

 

이 과정에서 발생할 수 있는 문제는 다음과 같습니다.


 

" DB 영속화 성공, 메시지 브로커에 이벤트 발행 실패 "

Member 객체가 DB에 성공적으로 저장되었지만, 메시지 브로커에 이벤트 발행이 실패하거나 네트워크 문제가 발생하면, 메시지 브로커는 이벤트를 받지 못하게 됩니다. 이로 인해 회원가입 처리가 완료되었음에도 불구하고 다른 시스템(예: 이메일 전송 시스템)은 이를 인식하지 못할 수 있습니다.

 

" DB 영속화 실패, 메시지 브로커에 이벤트 발행 성공 "

DB에 Member 객체를 영속화하는 과정에서 실패해 rollback이 발생했지만, 이벤트가 메시지 브로커에 발행된다면, 회원 정보가 저장되지 않은 상태에서 메시지 소비자(Consumer) 모듈이 회원가입 완료 메시지를 받아 잘못된 작업을 수행할 수 있습니다.


 

쉽게 말해, 발행되어야 할 메시지가 발행되지 않을 수 있고, 발행되지 않아야 할 메시지가 발행되는 경우가 발생할 수 있는 것입니다.

 

이는 DB와 메시지 브로커가 서로 다른 시스템이기 때문에 원자성을 보장하기 어려워 발생하는 문제입니다. 이러한 데이터 일관성 문제 해결을 위해 Transactional Outbox Pattern을 애플리케이션에 도입했습니다. 


Transactional Outbox Pattern

'Outbox'는 애플리케이션에서 보내야 하는 '메시지의 임시 보관함'을 의미하는 것으로, 편지를 보내기 전 우체통에 넣는 과정과 유사합니다. 편지를 친구에게 보내고 싶다고 상상해 볼 때 우리는 바로 친구에게 보내는 게 아니라 먼저 우체통에 넣어둡니다. 나중에 우체국에서 그 편지를 가져가서 친구에게 보내줄 수 있습니다. 이때 우체통은 편지를 안전하게 보관해 두는 역할을 합니다.

 

Outbox도 이와 비슷한 일을 합니다. 애플리케이션에서 발생하는 이벤트를 다른 모듈에게 알려야 할 때, 바로 보내지 않고 Outbox에 먼저 저장을 합니다. 그러면 나중에 Outbox에서 메시지를 꺼내어 다른 모듈이 처리할 수 있습니다. 이를 통해 메시지가 안전하게 저장되고, 만약 문제가 생기더라도 Outbox에 저장된 메시지를 다시 처리함으로써 결과적으로 메시지 처리를 보장할 수 있습니다.

 

Transactional Outbox Pattern이란, 데이터 업데이트와 메시지(이벤트) 저장을 하나의 트랜잭션으로 묶어 데이터와 메시지 간의 일관성을 보장하는 설계 패턴입니다. 

 

이 패턴은 다음과 같이 동작합니다. 사용자가 회원가입 시, 애플리케이션은 먼저 DB에 회원 정보를 저장합니다. 동시에, 이 회원가입 이벤트를 Outbox에 저장합니다. 이 두 작업이 하나의 트랜잭션으로 묶여 있어, 만약 회원 정보 저장이 성공적으로 이루어지더라도 Outbox에 메시지를 저장하는 데 문제가 발생하면, 전체 트랜잭션이 롤백되어 회원가입이 실패하게 됩니다. 반대로, 회원 정보 저장이 실패하면 Outbox에 메시지가 저장되지 않도록 합니다.

 

이렇게 함으로써, 데이터베이스와 메시지 발행 간의 일관성을 유지할 수 있습니다.

 

 


이벤트 처리(소비) 구현 방법

Outbox 테이블에 저장된 이벤트를 처리하는 방법에는 대표적으로 Polling과 Transaction Log Tailing 두 가지가 존재합니다. 각 방식의 장단점을 살펴본 후, 현재 제 애플리케이션에 어떤 선택이 적합할지 고민해 보겠습니다.

 

✔️ Polling

 

Outbox 테이블에 주기적으로 polling 하여 이벤트를 가져오는 방식입니다. 단순한 구조로 구현 난이도가 낮지만, 지속적 polling이 데이터베이스 부하를 발생할 수 있습니다.

 

 

✔️  Transaction Log Tailing

 

데이터베이스의 트랜잭션 로그(Transaction Log)를 모니터링하여 Outbox 테이블에 기록된 이벤트를 실시간으로 감지하고, 소비하는 방식입니다.

Debezium과 같은 CDC(Change Data Capture) 도구를 활용하여, 데이터베이스의 변경 사항을 감지하는 방식으로 구현됩니다.

 

Transaction Log Tailing 방식은 트랜잭션 로그에서 실제 변경된 데이터만을 감지하므로 불필요한 데이터베이스 조회 부하가 없습니다.

 

CDC 도구(Debezium, Maxwell) 같은 외부 시스템을 도입해야 하며, 이에 대한 학습과 설정이 필요합니다. 이로 인해 초기 설정 비용이 높아질 수 있습니다. 이로 인해 외부 시스템 의존성이 생기며, 시스템 관리 복잡성이 증가합니다.


 

각 장단점을 비교한 후, 현재 애플리케이션 규모에서는 지속적으로 polling을 사용하는 것이 큰 부하를 일으키지 않을 것으로 판단했습니다. 이를 바탕으로 polling 방식을 선택하였으며, 추후 성능 평가 및 모니터링을 통해 이 방식이 미치는 영향을 면밀히 분석하고 필요시 조정할 계획입니다.

 


Outbox Pattern 활용 흐름

지금까지 설명한 내용들을 바탕으로 Outbox Pattern이 애플리케이션에서 어떤 흐름으로 동작하는지 조금 더 자세히 살펴보겠습니다.

1. 회원가입 후 Outbox 테이블에 이벤트 저장

 

회원가입 로직이 완료되면, 데이터베이스 트랜잭션 내에서 회원 정보뿐만 아니라 이벤트도 함께 Outbox 테이블에 기록합니다.

 

만약 회원가입 중 에러가 발생하여 트랜잭션이 롤백되면, Outbox 테이블에 기록된 이벤트도 함께 롤백되기 때문에 잘못된 데이터가 메시지로 발행되는 것을 방지할 수 있습니다

 

2. 별도의 EventConsumer가 일정 주기로 polling 하여 Outbox 테이블에서 처리되지 않은 이벤트를 가져와 처리

 

데이터베이스에 저장된 이벤트는 별도의 EventConsumer가 이를 처리합니다. 이 Consumer는 일정 주기로 Outbox 테이블을 polling 합니다

 

Consumer가 성공적으로 처리하면 해당 이벤트는 Outbox 테이블에서 삭제됩니다.

 

만약 메시지 전송에 실패하더라도 Outbox 테이블에 이벤트가 남아 있으므로, 재시도를 통해 안정적인 메시지 발행이 가능합니다. 이는 시스템의 신뢰성과 내결함성을 높여줄 뿐만 아니라, 결과적으로 일관성을 유지하는 데에도 기여합니다. Outbox에 저장된 이벤트를 통해 메시지가 안정적으로 처리되므로, 결국 시스템 전체의 일관성을 확보할 수 있는 것입니다. 

 


 

✔️ 구현 코드

 

Outbox 테이블

CREATE TABLE `MEMBER_SIGN_UP_EVENT` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `email` varchar(255) NOT NULL,
  `auth_key` varchar(255) NOT NULL,
   PRIMARY KEY (`id`)
)

 

회원가입 이벤트를 저장하는 Outbox 테이블을 생성합니다.

 

MemberService 비즈니스 로직

@Service
public class MemberService {

    @Autowired
    private MemberSignUpEventRepository memberSignUpEventRepository;
	
    @Transactional
    public void registerUser(String userId, String userEmail) {
        // 회원가입 로직
        
        // outbox 테이블에 이벤트 저장
        memberSignUpEventRepository.save(event);
    }
}

 

서비스 레이어의 로직에서는 회원가입, 이벤트 저장을 한 트랜잭션으로 묶어 이벤트 발행을 보장합니다.


결과 및 성과

이벤트 기반 구조와 Transactional Outbox Pattern을 도입함으로써 다음과 같은 결과를 얻었습니다:

 

✅ 시스템 안정성 향상: 이벤트 기반 구조로 인해 회원가입과 알림 기능이 독립적으로 동작하며, 알림 전송 실패가 회원가입에 영향을 미치지 않게 되었습니다.

 

✅ 유지보수성 및 확장성 강화: 각 기능이 독립된 책임 단위로 나뉘어, 새로운 기능 추가나 수정이 용이해졌습니다.

 

✅ 데이터 일관성 보장: Transactional Outbox Pattern을 통해 데이터베이스와 메시지 발행 간의 일관성을 보장하며, 시스템의 신뢰성을 높였습니다. Outbox Pattern을 통해 이벤트 테이블에 저장된 메시지의 소비에 문제가 생기더라도, 결과적 일관성을 보장할 수 있게 되었습니다.

 


다음 포스트 예고: 확장성을 고려한 이벤트 추상화

 

현재 구현 방식은 회원가입 이벤트에 구체적으로 맞춰져 있습니다. 하지만 앞으로 회원가입뿐만 아니라, 다른 도메인 이벤트 발행에 대한 요구사항이 생길 가능성이 큽니다. 예를 들어, Member의 패스워드가 변경되면, 보안 강화를 위해 이메일을 통해 개인 정보가 변경됐다는 알림을 전송해야 할 수 있습니다.

 

이와 같이 다양한 도메인 이벤트가 계속해서 추가되면, 이벤트 발행 서비스와 이벤트 저장소를 매번 새롭게 구현해야 할 것입니다. 따라서, 특정 도메인 이벤트에 종속되지 않는 추상화된 이벤트 구조를 설계하는 것이 중요합니다. 

 

다음 포스트에선 이러한 확장성을 고려한 이벤트 추상화에 대해서 다뤄보겠습니다.

'Side Project > Study Together' 카테고리의 다른 글

[동시성 제어] DB 락 vs Redis vs 비동기  (1) 2025.02.04
[Test Code] Repository 단위 테스트로 다져가는 도메인 중심 설계의 기초  (4) 2024.10.26
[Event 기반 설계] 다양한 이벤트 확장 : 이벤트 추상화와 동적 매핑 구현 방법  (2) 2024.09.26
[JPA] 일대다(OneToMany) 단방향 매핑의 성능 이슈  (1) 2024.09.25
[JPA] JPA 적용 이유, 의존성 주입으로 유연한 Repository 설계  (1) 2024.09.25
'Side Project/Study Together' 카테고리의 다른 글
  • [Test Code] Repository 단위 테스트로 다져가는 도메인 중심 설계의 기초
  • [Event 기반 설계] 다양한 이벤트 확장 : 이벤트 추상화와 동적 매핑 구현 방법
  • [JPA] 일대다(OneToMany) 단방향 매핑의 성능 이슈
  • [JPA] JPA 적용 이유, 의존성 주입으로 유연한 Repository 설계
우니wooni
우니wooni
  • 우니wooni
    woonDev
    우니wooni
  • 전체
    오늘
    어제
    • 전체
      • Programming
        • Java,Back-end
        • Spring,JPA
        • OS
        • Network
        • 기술 면접 대비
      • Books
        • MySQL 8.0
      • Side Project
        • Study Together
      • Life
        • 회고
        • 일상 이야기
  • 블로그 메뉴

    • 홈
    • GitHub
  • 링크

    • GitHub
  • 공지사항

  • 인기 글

  • 태그

    레디스
    JPA
    익명 클래스
    Pessimistic lock
    hibernate
    영속성 컨텍스트
    람다
    이벤트 기반 구조
    Spring
    비관적 락
    Java
    동시성 제어
    디자인 패턴
    스프링
    Persistence Context
    Optimistic Lock
    redis
    낙관적 락
    추상화
    event-driven architecture
    비동기
    동시성
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
우니wooni
[Event 기반 설계] Transactional Outbox 패턴이 필요한 이유 : 프로젝트 적용 사례
상단으로

티스토리툴바