ํฝ๋ธ์ฝ์ ์งํํ๋ฉด์ ํ๋์ ๊ธฐ๋ฅ์ ์ฌ๋ฌ ๋ถ๊ฐ ๊ธฐ๋ฅ์ด ๋ฐ๋ผ์ค๊ฒ ๋์๋ค.
์: ์ฝ์ ๋ฑ๋ก โ DB ์ ์ฅ โ ๊ด๋ฆฌ์์๊ฒ ๋ฉ์ผ ๋ฐ์ก
์ด์ฒ๋ผ ํ๋์ ์ก์ ์ ์ฌ๋ฌ ์ฑ ์์ด ๋ธ๋ ค๋ถ๊ธฐ ์์ํ๋ฉด ๊ฐ์ฒด์งํฅ 5์์น ์ค ํ๋์ธ ๋จ์ผ ์ฑ ์ ์์น(SRP)์ด ์ฝ๊ฒ ๊นจ์ง๋ค.
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด Spring Event๋ฅผ ๋์ ํ๊ณ , ์ ์ฉ ๋ฐฉ๋ฒ๊ณผ ์ค์ ์ ์ฉ๊ธฐ(ํฝ๋ธ์ฝ ์์ )๋ฅผ ์๋์ ์ ๋ฆฌํด๋ดค๋ค.
Spring Event๋ ์ปดํฌ๋ํธ ๊ฐ์ ๋์จํ ์ฐ๊ฒฐ(loose coupling)์ ๊ฐ๋ฅํ๊ฒ ํด์ฃผ๋ ๊ตฌ์กฐ๋ค.
๋ด๋ถ์ ์ผ๋ก๋ Observer ํจํด์ผ๋ก ๊ตฌ์ฑ๋ผ์๋ค.
๋๊ตฐ๊ฐ ์ด๋ค ์ด๋ฒคํธ๋ฅผ ๋ฐํํ๋ฉด,
๊ด์ฌ ์๋ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ์ค๋๋ก์ ๊ทธ ์ด๋ฒคํธ๋ฅผ ๋ฐ์ ์ฒ๋ฆฌ.
์ญํ | ์ค๋ช |
---|---|
Publisher | ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํค๋ ์ชฝ |
Event | ์ ๋ฌ๋๋ ๋ฐ์ดํฐ ๊ฐ์ฒด |
Listener | ์ด๋ฒคํธ๋ฅผ ๊ตฌ๋ ํ๊ณ ์ฒ๋ฆฌํ๋ ์ชฝ |
public class UserRegisteredEvent {
private final String email;
public UserRegisteredEvent(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
}
@Component
@RequiredArgsConstructor
public class UserService {
private final ApplicationEventPublisher publisher;
public void registerUser(String email) {
// ์ฌ์ฉ์ ๋ฑ๋ก ๋ก์ง ...
// ์ด๋ฒคํธ ๋ฐํ
publisher.publishEvent(new UserRegisteredEvent(email));
}
}
@Component
public class WelcomeEmailListener {
@EventListener(UserRegisteredEvent.class)
public void handleUserRegistered(UserRegisteredEvent event) {
System.out.println("ํ์ ๋ฉ์ผ ์ ์ก ๋์: " + event.getEmail());
// ๋ฉ์ผ ์ ์ก ๋ก์ง ...
}
}
ํฝ๋ธ์ฝ์์๋ ์ฝ์๋ฅผ ์น์ธํ ๋ ๋ค์ 3๊ฐ์ง ๊ธฐ๋ฅ์ด ํ ๋ฒ์ ์คํ๋๋ค.
์ด๋ 1~2๋ฒ์ ํธ๋์ญ์ ์์์ ์์ ํ๊ฒ ์คํ๋์ด์ผ ํ๊ณ , 3๋ฒ์ ๋ถ๊ฐ๊ธฐ๋ฅ์ด๋ฏ๋ก ์ฑ๊ณต/์คํจ๊ฐ ํธ๋์ญ์ ์์ ์ ์ํฅ์ด ์์ด์ผํ๋ ์กฐ๊ฑด์ด ์์๋ค.
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class ConfirmPrePostService {
private final ApplicationEventPublisher eventPublisher;
private final GetPrePostService getPrePostService;
private final ConfirmPrePostValidator confirmPrePostValidator;
public void confirm(UpdatePrePostStatusDto dto) {
log.info("Confirm pre post: {}", dto);
PrePost prePost = getPrePostService.getPrePost(dto.prePostId());
confirmPrePostValidator.validateStatus(prePost.getStatus());
prePost.updateStatus(dto.status(), dto.updatedBy());
eventPublisher.publishEvent(ConfirmPrePostStatus.confirmPrePost(prePost, dto.updatedBy()));
}
@TransactionalEventListener(classes = ConfirmPrePostStatus.class, phase = TransactionPhase.BEFORE_COMMIT)
public void registerPost(ConfirmPrePostStatus event) {
log.info("EVENT - Register post: {}", event);
String geohash = geoHashUtil.encode(event.latitude(), event.longitude());
MultipartFile img = fileCodecUtil.decodeToMultipartFile(geohash, event.img());
s3Util.uploadFile(event.type() + "-" + geohash, img);
Post post = new Post(
geohash,
event.name(),
event.latitude(),
event.longitude(),
event.type(),
event.requester(),
event.noImg(),
event.confirmedBy()
);
postRepository.save(post);
}
์ฌ๊ธฐ์ @TransactionalEventListener์ด๋ ธํ ์ด์ ๊ณผ phase = TransactionPhase.BEFORE_COMMIT ๋๊ฐ๋ฅผ ์ ๋ด์ผํ๋ค.
@Slf4j
@Service
@RequiredArgsConstructor
public class ConfirmPrePostMailService {
private final JavaMailSender javaMailSender;
private final ConfirmPrePostTemplate confirmPrePostTemplate;
@Value("${spring.mail.username}")
private String from;
@TransactionalEventListener(classes = ConfirmPrePostStatus.class, phase = TransactionPhase.AFTER_COMMIT)
public void confirmPrePost(ConfirmPrePostStatus event) {
log.info("EVENT - Sending confirm prePost mail: {}", event);
String html = confirmPrePostTemplate.build(event);
MimeMessage message = javaMailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setTo(event.requester().getEmail());
helper.setSubject("์ฝ์ ๋ฑ๋ก ์น์ธ");
helper.setFrom(from);
helper.setText(html, true);
helper.addInline("img", confirmPrePostTemplate.bindImg(event.img()));
} catch (MessagingException e) {
log.error("sendConfirmPrePostMail Exception", e);
throw new MessagingBuildException("ConfirmPrePostMail");
}
javaMailSender.send(message);
}
}
์ฌ๊ธฐ์ ์ค์ํ๊ฒ ๋ด์ผํ๋ ๊ฑด phase = TransactionPhase.AFTER_COMMIT์ด๋ค.
๊ธฐ์กด์๋ ํ๋์ ์ฝ์ ์น์ธ Class์์ ์น์ธ ์ฒ๋ฆฌ + ๋ฑ๋ก + ๋ฉ์ผ ์ ์ก์ ๋ชจ๋ ์ฒ๋ฆฌํ๋ค.
์ด ๋ฐฉ์์ ํด๋์ค๊ฐ 3๊ฐ์ง ์ฑ
์์ ๋์์ ๊ฐ์ง๊ฒ ๋์ด ์ ์ง๋ณด์์ ์ถ๊ฐ ๊ธฐ๋ฅ์ ๋ถ์ด๊ธฐ๊ฐ ์ด๋ ค์ ๋ค.
Spring Event๋ฅผ ๋์ ํ ํ์๋
โ๏ธ ์๋ก์ด ํ์ฒ๋ฆฌ๋ฅผ ๋ถ์ผ ๋๋ ๊ธฐ์กด ํด๋์ค ์์ ์์ด
@TransactionalEventListener๋ง ์ถ๊ฐํ๋ฉด ๋!
์ด ์ฒ๋ผ Spring Event๋ฅผ ๋์ ํ ์ ์ง๋ณด์์ ๊ธฐ๋ฅ ์ถ๊ฐ ์ ์ด์ ์ ํจ๊นจ ๊ฐ์ ธ๊ฐ๊ฒ ๋์๋ค.