우이삭 프로젝트에서 Member 프로필을 수정하는 경우에 Event를 사용했습니다.
그 이유는 updateMemberProfile메서드의 경우, 회원의 profile을 수정하는 책임을 갖습니다. Transaction이 커밋된 후의 Cache를 무효화하는 로직은 updateMemberProfile에서 하는 것이 아닌 다른 객체의 책임이라고 생각했습니다. 하나의 객체에서 두개의 책임을 갖는 것은 복잡성을 높인다고 생각하여 Event를 사용하여 분리하였고, Event에 대해 학습한것을 정리하는 목적이 있습니다.
@Transactional
@CacheEvict(value = {CacheConstants.MEMBER_PROFILE, CacheConstants.COUPLE_PROFILE}, key = "#memberId")
public void updateMemberProfile(List<MultipartFile> profileImages, MemberProfileEditServiceRequest serviceRequest, Long memberId) {
Member findMember = validateMemberId(memberId);
String oldProfileImageUrl = findMember.getImageUrl();
String profileImageUrl = updateProfileImage(profileImages, oldProfileImageUrl);
updateMemberProfile(profileImageUrl, serviceRequest, findMember);
Events.raise(new MemberUpdatedEvent(findMember));
}
@Profile("!test")
@Component
@RequiredArgsConstructor
public class MemberCreatedEventListener {
private final CacheManager cacheManager;
@Async
@TransactionalEventListener(value = MemberUpdatedEvent.class, phase = TransactionPhase.AFTER_COMMIT)
public void handleAfterMemberUpdateInvalidateCache(MemberUpdatedEvent event) {
Member member = event.member();
Objects.requireNonNull(cacheManager.getCache(CacheConstants.USER_DETAILS)).evict(member.getEmail());
}
}
Spring Framework의 Event 시스템은 애플리케이션 내에서 객체 간의 느슨한 결합을 유지하면서 메시지를 전달할 수 있도록 돕는 메커니즘입니다. 애플리케이션의 구성 요소가 서로 직접적인 참조 없이 이벤트를 발생시키고 해당 이벤트에 반응할 수 있게 해줍니다. 이를 통해 모듈 간의 독립성을 유지하면서도 기능적 협업을 가능하게 합니다.
이벤트는 특정 상황이 발생했을 때 이를 알리기 위한 객체입니다. 일반적으로, Spring에서 이벤트는 ApplicationEvent 클래스를 상속하거나 POJO(Plain Old Java Object)로도 표현될 수 있습니다.
public class MyEvent {
private String message;
public MyEvent(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
@Component
public class MyEventPublisher {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void publish(String message) {
MyEvent event = new MyEvent(message);
eventPublisher.publishEvent(event);
}
}
@Component
public class MyEventListener {
@EventListener
public void handleMyEvent(MyEvent event) {
System.out.println("Received event: " + event.getMessage());
}
}
참고) EventListener의 매개변수에 해당하는 클래스 타입의 이벤트가 발생했을 때, EventListener가 작동한다.
@Configuration
@EnableAsync
public class AsyncConfig {
}
@Component
public class MyAsyncEventListener {
@Async
@EventListener
public void handleMyEvent(MyEvent event) {
System.out.println("Received event asynchronously: " + event.getMessage());
}
}
@Component
public class MyTransactionalEventListener {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleMyEvent(MyEvent event) {
System.out.println("Received event after transaction commit: " + event.getMessage());
}
}
이벤트 시스템을 통해 모듈 간의 의존성을 최소화하면서 메시지를 전달할 수 있습니다. 퍼블리셔는 리스너에 대해 알 필요가 없고, 리스너 역시 퍼블리셔에 대해 알 필요가 없습니다.
동기적으로 이벤트를 처리할 수도 있고, 비동기적으로 처리할 수도 있어 애플리케이션의 성능과 확장성을 향상시킬 수 있습니다.
트랜잭션과 이벤트를 연계하여 트랜잭션 커밋 후 또는 트랜잭션 롤백 후 이벤트를 처리하는 등, 비즈니스 로직의 복잡성을 쉽게 관리할 수 있습니다.
새로운 기능을 추가할 때 기존 코드의 수정 없이 리스너를 추가하여 확장할 수 있어 유지보수성이 뛰어납니다.