Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 29 | 30 | 31 |
Tags
- 자바 문제풀이
- springboot
- Segmentation with Paging
- Allocation of Physical Memory
- linux
- 스프링
- 스프링부트
- Effective Access Time
- 2단계 Page Table
- 다단계 페이지 테이블
- 코드스테이츠 백엔드 과정 39기
- 메모리의 불연속적 할당
- 운영체제
- Inverted Page Table
- 자바 알고리즘
- CS
- 알고리즘
- 프로세스 동기화
- Shared Page
- spring
- 프로세스 할당
- 프로세스 불연속 할당
- 리눅스
- Page Table의 구현
- 메모리 관리
- 웹개발
- annotation
- 웹 프로그래밍
- jpa
- 문제풀이
Archives
- Today
- Total
GrowMe
[Spring] 이벤트를 처리하는 방법 본문
Event를 처리하는 방법
# Event
# 이벤트 처리하기
# ApplicationEvert
# ApplicationEventPublisher
# ApplicationListener
# 이벤트 발행
# ApplicationListener
*Event란?
'이벤트 발생' 이라는 의미는 '회원 정보 저장', '회원 정보 업데이트' 등의 어떤 기능이 처리됨을 의미합니다. 이처럼, 어떠한 기능을 이벤트로 정의하고, 그 이벤트가 발생할 때 어떠한 추가적인 처리를 하도록 구현을 할 상황이 필요하곤 합니다. 아래와 같이, 회원가입 후 축하 메시지 전송 및 쿠폰 전송하는 기능있다고 가정할 때, 아래의 코드는 몇 가지 문제점이 존재합니다.
@Service
@Transactional
public class RegisterService {
@Autowired
ApplicationEventPublisher publisher;
public void register(String name) {
// 회원가입 처리 로직
System.out.println("회원 추가 완료");
// 가입 축하 메세지 전송
System.out.println(name + "님에게 가입 축하 메세지를 전송했습니다.");
// 가입 축하 쿠폰 발급
System.out.println(name + "님에게 쿠폰을 전송했습니다.");
}
}
- 강한 결합도
회원가입 서비스에 가입에 대한 기능 뿐만 아니라, 메시지 전송 기능 & 쿠폰 발급 기능이 모두 섞여 있습니다. 이처럼 결합도가 강할 경우, 추후에 유지보수가 어려워지고, 코드의 복잡도도 올라가게 될 것입니다. - 트랜잭션
메시지 전송 중 실패 시, 저장되었던 회원 정보까지 모두 롤백이 되기에 이는 적절치 못한 처리라고 할 수 있습니다.
따라서, 회원 가입 / 축하 메시지, 쿠폰발급 <- 이렇게 나눠서 처리를 해주는 것이 필요합니다. - 성능
위 코드처럼 처리할 경우, 회원가입 요청 후, 메시지 전송, 쿠폰 발급이 완료될 때까지 회원가입 처리가 지연됩니다. 즉, 메시지 전송, 쿠폰발급하는데 1분씩 소요된다고하면, 고객이 회원가입 완료 화면을 보는데까지 2분이상 기다려야 된다는 것입니다.
-> 이와 같은 문제를 해결하기위해, 원하는 기능을 이벤트로 정의하고 본기능과 분리하여 처리해봅시다.
*이벤트 처리 방법
1. 이벤트 클래스 만들기
@Getter
public class PostEvent extends ApplicationEvent {
private final Member savedmember;
public PostEvent(Object obj, Member savedmember){
super(obj);
this.savedmember = savedmember;
}
- ApplicationEvert를 상속받아야 하나의 이벤트로 인식된다.
- 생성자로 이벤트를 발생시킨 주체 클래스(서비스)를 필수로 입력받아야 하며, 이벤트 발생시 생겨난 데이터(savedmember)를 전달받아 이벤트 후처리를 할 EventLister에서 접근할 수 있도록 가능하다.
- 전달받은 주체 클래스(서비스)는 super()을 통해 ApplicationEvent 클래스로 넘겨주어야 한다.
2. 서비스에서 이벤트를 정의하고, 발행하기
@Slf4j
@Transactional
@Service
public class MemberService {
private final MemberRepository memberRepository;
private final ApplicationEventPublisher applicationEventPublisher;
public MemberService(MemberRepository memberRepository, ApplicationEventPublisher applicationEventPublisher) {
this.memberRepository = memberRepository;
this.applicationEventPublisher = applicationEventPublisher;
}
@Transactional
public Member createMember(Member member) {
verifyExistsEmail(member.getEmail());
Member savedMember = memberRepository.save(member);
PostEvent postEvent = new PostEvent(this, savedMember);
applicationEventPublisher.publishEvent(postEvent);
return savedMember;
}
}
- 이벤트 발행 기능을 사용하기 위해, ApplicationEventPublisher 을 주입받는다.
- 위에서 만든 PostEvent 클래스를 생성(이벤트 발생 주체인 MemberServic를 넘겨주고, 생성된 데이터 savedMember도 넘겨준다)하여 이벤트를 정의한다.
- ApplicationEventPublisher의 publishEvent를 통해 정의한 이벤트를 발행한다.
3. 발행한 이벤트를 받아서 처리할 이벤트 핸들러 만들기
@EnableAsync
@RequiredArgsConstructor
@Configuration
@Component
public class EventLister implements ApplicationListener<PostEvent> {
private final EmailSender emailSender;
private final MemberService memberService;
@Async
// @EventListener
public void onApplicationEvent(PostEvent postEvent) {
try {
// 이메일을 보낸다
emailSender.sendEmail("any email message");
} catch (Exception e) {
// 이메일 발송 실패 시, 저장했던 회원 정보를 삭제한다
log.error("MailSendException happened: ", e);
memberService.deleteMember(postEvent.getSavedmember().getMemberId());
throw new RuntimeException(e);
};
}
}
- 이벤트 핸들러는 빈으로 등록이 되어야 작동을 한다. (@Component 추가)
- 이벤트 리스너로서 작동하기 위해, ApplicationListener<정의한 이벤트>을 상속받아야 한다.
- onApplicationEvent 메서드를 오버라이드하여, 2번에서 발행한 이벤트를 넘겨받는다.
- PostEvent를 정의할 때 넘겨받았던 savedmember 데이터를 다시 넘겨받아, 가공하여 deleteMember 등 후처리 가능.
- @EventLister : 스프링 4.2 이후부터 추가된 애너테이션으로, 이것을 사용하면 ApplicationListerner을 상속받지 않아도 되며, 정의했던 PostEvent에서도 ApplicationEvert을 상속받지 않아도 되고, super()로 이벤트 발생 주체 클래스(서비스)도 안넘겨줘도 된다. 대신 리스너의 핸들러 메서드명은 onApplicationEvent여야 한다.
- @Async : 비동기적으로 이벤트 핸들러를 처리하고 싶을때 붙여준다. @EnableAsync를 리스너 클래스에 @Configuration과 함께 붙여줘야한다. 이렇게하면, 멀티쓰레드로 작동하여, 회원정보 저장 -> 회원가입 완료 ->
이메일 발송 -> 이메일 발송완료, 즉 Service 계층에서 한 트랜잭션으로 묶여있어도 분리되어 동작한다.
'About Spring' 카테고리의 다른 글
[API 문서화] Swagger의 기본 사용법 (0) | 2022.08.30 |
---|---|
[API 문서화] Spring Rest Docs를 통한 API 문서화 (0) | 2022.08.19 |
[JPA] 엔티티 간 연관관계 매핑 방법 (2) | 2022.07.12 |
SpringBoot에서 JPA 사용하기 (0) | 2022.06.30 |
[Spring] Mapper와 MapStruct에 대해 알아보자 (0) | 2022.06.28 |
Comments