개요
현재 토이 프로젝트에 간단하게 CQRS 아키텍처를 도입해보고 있다. 따라서 쓰기DB, 읽기DB 총 2개의 데이터베이스가 존재하고 쓰기 요청이 오는 경우 쓰기DB에 데이터를 기록함과 동시에 SQS에 이벤트를 발송하고 있다. 그리고 읽기DB에서는 해당 SQS를 컨슈밍하고 있다가 변경 이벤트가 들어오면 내부적으로 동기화를 진행하는 형태이다.
위 요구사항에 따라 엔티티에 변경사항이 생긴다면 감지한 후 이벤트를 발생해줘야하기 때문에 관련 이벤트 리스너를 찾아봤는데, Hibernate의 EventListener를 활용하면 손쉽게 가능하다는것을 알게 되었다. JPA에서 이벤트 리스너를 제공해주지만 Hibernate의 기능을 사용해야 좀 더 세분화하여 활용할 수 있다고 하여 사용하게 됐다. (참고: https://www.youtube.com/watch?v=fg5xbs59Lro)
PostInsertEventListener
Insert쿼리가 나간 후 발생되는 리스너이다.
@Component
@RequiredArgsConstructor
@Slf4j
public class CustomPostInsterEventListener implements PostInsertEventListener {
@Override
public void onPostInsert(PostInsertEvent event) {
log.info("Emit user insert event");
publishRelatedEntity(event.getEntity());
}
@Override
public boolean requiresPostCommitHanding(EntityPersister persister) {
return false;
}
private void publishRelatedEntity(Object entity) {
try {
publishEventForUser(entity);
} catch (Exception e) {
log.error(String.valueOf(e));
}
}
private void publishEventForUser(Object entity) throws JsonProcessingException {
if (!(entity instanceof User)) {
return;
}
...
}
}
onPostInsert()와 requiresPostCommitHaning() 총 2개의 메소드를 오버라이드 해야한다. onPostInsert() 쪽에 이벤트가 들어오는데 getEntity() 메소드를 통해 어떠한 엔티티가 변경되었는지 파악할 수 있다. publishRelatedEntity(), publishEventForEvent()는 나중에 여러개의 엔티티가 들어왔을때 해당 엔티티에 맞게 작업을 처리해주기 위해 작성한 코드이다.
PostUpdateEventListener
Update쿼리가 나간 후 발생되는 리스너이다.
@Component
@RequiredArgsConstructor
@Slf4j
public class CustomPostUpdateEventListener implements PostUpdateEventListener {
@Override
public void onPostUpdate(PostUpdateEvent event) {
log.info("Emit user update event");
publishRelatedEntity(event.getEntity());
}
@Override
public boolean requiresPostCommitHanding(EntityPersister persister) {
return false;
}
...
}
onPostUpdate()와 requiresPostCommitHanding() 총 2개의 메소드를 오버라이드 해야한다. Insert와 같이 onPostUpdate에 이벤트가 들어온다.
PostDeleteEventListener
Delete쿼리가 나간 후 발생되는 리스너이다.
@Component
@RequiredArgsConstructor
@Slf4j
public class CustomPostDeleteEventListener implements PostDeleteEventListener {
@Override
public void onPostDelete(PostDeleteEvent event) {
log.info("Emit user delete event");
publishRelatedEntity(event.getEntity());
}
@Override
public boolean requiresPostCommitHanding(EntityPersister persister) {
return false;
}
...
}
onPostDelete()와 requiredPostCommitHanding() 총 2개의 메소드를 오버라이드 해야한다. 삭제 또한 마찬가지로 onPostDelete에 이벤트가 들어온다.
EntityManagerFactory
이제 위에서 생성해준 리스너를 실제로 이벤트 리스너에 등록해줘야 한다.
@Component
@RequiredArgsConstructor
@Slf4j
public class HibernateListener {
private final EntityManagerFactory entityManagerFactory;
private final CustomPostInsterEventListener customPostInsterEventListener;
private final CustomPostUpdateEventListener customPostUpdateEventListener;
private final CustomPostDeleteEventListener customPostDeleteEventListener;
@PostConstruct
private void init() {
log.info("Initializing HibernateListener");
SessionFactoryImpl sessionFactory = entityManagerFactory.unwrap(SessionFactoryImpl.class);
EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
registry.getEventListenerGroup(EventType.POST_INSERT).appendListener(customPostInsterEventListener);
registry.getEventListenerGroup(EventType.POST_UPDATE).appendListener(customPostUpdateEventListener);
registry.getEventListenerGroup(EventType.POST_DELETE).appendListener(customPostDeleteEventListener);
}
}
코드를 보면 알겠지만 getEventListenerGroup에서 EventType별로 특정 동작 시 어떠한 이벤트 리스너를 등록할 지 정해줄 수 있다. 위에서 우리가 생성한 리스너를 appendListener() 메소드를 통해 등록해주면 정상적으로 동작하는 모습을 확인할 수 있다.
참고로 본 포스팅에서는 Post~ 리스너들만 다뤘지만 Pre~, PostCommit~ 등 여러가지의 이벤트 리스너들이 존재하므로 각 요구사항에 맞게 활용하면 된다.
'Coding > Java Spring' 카테고리의 다른 글
스프링 시큐리티 Authentication 아키텍처 (0) | 2023.06.19 |
---|---|
스프링 시큐리티 아키텍처 (0) | 2023.06.19 |
Spring AWS SNS + SQS로 이벤트 처리하는 방법 (0) | 2022.01.18 |
Spring Interceptor를 활용하여 JWT인증 구현하기 (0) | 2022.01.03 |
Spring Data Redis로 레디스 연동하는 방법 (0) | 2021.10.19 |