1. ์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํ๋ ์ด์
Spring Boot์์ ์ด๋ฒคํธ๋ฅผ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ํด ๋ค์ด๊ฐ๊ธฐ ์ ์, ์ด๋ฒคํธ๋ฅผ ์ ์จ์ผํ๋์ง, ์ฌ์ฉํ๋ฉด ์ข์ ์ํฉ์ ๋ํด ๋จผ์ ์์๋ณด์. ํ์๊ฐ์ ์ ํ๊ณ ๋๋ฉด ๊ฐ์ ์ถํ ๋ฉ์ธ์ง๋ฅผ ์ ์กํ๋ ๋์์ ์ฟ ํฐ์ ์ ์กํ๋ ์๋น์ค๊ฐ ์๋ค๊ณ ๊ฐ์ ํด๋ณด์๋ค.
@Service
@RequiredArgsConstructor
public class MemberService {
private final AlarmService alarmService;
private final AlimTalkService alimTalkService;
public void register(String name) {
// ํ์๊ฐ์
์ฒ๋ฆฌ ๋ก์ง
System.out.println("ํ์ ์ถ๊ฐ ์๋ฃ");
// ๊ฐ์
์ถํ ํธ์ ์๋ฆผ
alarmService.send(name);
// ์นด์นด์ค ์๋ฆผํก ์ ์ก
alimTalkService.send(name);
}
}
์ฝ๋๋ก ๊ฐ๋ตํ๊ฒ ์์ฑํ๋ฉด ์์ ๊ฐ์ด ์์ฑํ ์ ์๋ค. ํ์ง๋ง ์ด ์ฝ๋์ ๋ช ๊ฐ์ง ๋ฌธ์ ์ ์ด ์๋ค.
1) ๊ฐํ ๊ฒฐํฉ
ํ์ฌ ํ์๊ฐ์ ์๋น์ค์ ํ์ ๊ฐ์ ๋ก์ง๋ฟ๋ง ์๋๋ผ ๊ฐ์ ์ถํ ํธ์ ์๋ฆผ์ ๋ณด๋ด๋ ๋ก์ง, ์นด์นด์ค ์๋ฆผํก ์ ์กํ๋ ๋ก์ง์ด ๋ชจ๋ ์์ฌ์๋ค. ์ด๋ ๊ฒ ์๋ก ๊ฐํ ๊ฒฐํฉ์ผ๋ก ๋ฌถ์ฌ์์ผ๋ฉด ํ์ ์ ์ง๋ณด์๊ฐ ์ด๋ ต๊ณ ์ฝ๋์ ๊ตฌ์กฐ๊ฐ ๋ณต์กํ๋ค.
2) ์ฑ๋ฅ
์ค์ ๋ก ๊ทธ๋ฐ ๊ฒฝ์ฐ๋ ์๊ฒ ์ง๋ง ๊ฐ์ ์ถํ ํธ์ ์๋ฆผ์ ๋ณด๋ด๋๋ฐ 3๋ถ, ์๋ฆผํก์ ๋ณด๋ด๋๋ฐ 3๋ถ์ด ๊ฑธ๋ฆฐ๋ค๊ณ ํ๋ฉด ์ด ํ์๊ฐ์ ์ 6๋ถ์ด ๊ฑธ๋ฆฌ๊ฒ ๋๋ค. ๋ฉ์ธ ์ด๋ฒคํธ์ธ ํ์๊ฐ์ ์ฒ๋ฆฌ ๋ก์ง์ ๋๋ด๋ฉด ์๋ธ ์ด๋ฒคํธ์ธ ๊ฐ์ ์ถํ ๋ฉ์ธ์ง ์ ์ก์ด๋ ๊ฐ์ ์ถํ ์ฟ ํฐ์ ์ ์ก์๋ฃ๊น์ง ๊ธฐ๋ค๋ฆด ํ์๊ฐ ์๋ค.
3) ํธ๋์ญ์
์ ๋ก์ง์์ ๋ง์ฝ ์๋ฆผํก์ ์ ์กํ๋ค ์ค๋ฅ๊ฐ ๋๋ฉด ์ด๋ป๊ฒ๋ ๊น?? ํ ํธ๋์ญ์ ์ผ๋ก ๋ฌถ์ฌ์๊ธฐ ๋๋ฌธ์ ์ด์ ์ ์ฒ๋ฆฌํ ๋ก์ง๋ค๊น์ง ์ ๋ถ ๋กค๋ฐฑ๋๋ค. ์ฐจ๋ผ๋ฆฌ ํ์ ๊ฐ์ ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ๊ณ , ํธ์์๋ฆผ๊ณผ ์๋ฆผํก์ ๋ฐ๋ก ๊ด๋ฆฌํ๋๊ฒ ์ณ์ ๋ฐฉ๋ฒ์ด๋ค.
๋ฐ๋ก ์์ ๊ฐ์ ์ํฉ์ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ ์ค์ ์ด๋ฒคํธ๋ฅผ ์ ์ฉํด์ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ด ์๋ค. ์ด๋ฒคํธ๋ ์์ฑ ์ฃผ์ฒด์ ์ํ๊ฐ ๋ณ๊ฒฝ๋๋ฉด ์ด๋ฒคํธ๋ฅผ ๋ฐ์์์ผ ์ํ๋ ๊ธฐ๋ฅ์ ์คํํด์ ํ์ฒ๋ฆฌ๋ฅผ ๋์์ค๋ค.
2. ์ด๋ฒคํธ์ ์คํ ๋จ๊ณ
- ์์ฑ ์ฃผ์ฒด(์ฃผ๋ก ๋๋ฉ์ธ ๊ฐ์ฒด)์์ ์ด๋ฒคํธ๋ฅผ ๋ฐ์ํ๋ฉด ์ด๋ฒคํธ ๋์คํจ์ฒ์๊ฒ ์ ๋ฌํ๋ค.
- ์ด๋ฒคํธ ๋์คํจ์ฒ๊ฐ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ์ฐ๊ฒฐํด์ค๋ค.
- ์ด๋ฒคํธ ํธ๋ค๋ฌ์์ ์ด๋ฒคํธ์ ๋ด๊ธด ๋ฐ์ดํฐ๋ฅผ ํตํด ์ํ๋ ๊ธฐ๋ฅ์ ์คํํ๋ค.
3.1 ๊ฐํ ๊ฒฐํฉ ํด๊ฒฐ
์ด์ Spring Event๋ฅผ ์ด์ฉํด ๋ฌธ์ ์ ๋ค์ ํ๋ํ๋ ํด๊ฒฐํด๋๊ฐ ๋ณด์.
- ์ด๋ฒคํธ ํด๋์ค ์์ฑ
public class RegisteredEvent {
private String name;
public RegisteredEvent(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
์ด๋ฒคํธ๋ ์ํ๊ฐ ๋ฐ๋ ํ์ ๋ฐ์ํ๊ธฐ ๋๋ฌธ์ ์ด๋ฒคํธ ํด๋์ค์ ์ด๋ฆ์ ๊ณผ๊ฑฐ์์ ๋ก ์ง์ด์ผํ๋ค. ๋ํ, ์ด๋ฒคํธ ํด๋์ค๋ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐ์ดํฐ๋ฅผ ํฌํจํ๋ค.
์์ ์์ ํน์ ํ์์๊ฒ ์๋ฆผ์ ๋ณด๋ด๊ธฐ ์ํด์ ์ ๋ณด๊ฐ ํ์ํ๊ธฐ ๋๋ฌธ์ name์ ํ๋๋ก ๋ฃ์ด์ฃผ์๋ค.
- ๊ธฐ์กด์ MemberService ์์
@Service
@RequiredArgsConstructor
public class MemberService {
// private final AlarmService alarmService;
// private final AlimTalkService alimTalkService;
private final ApplicationEventPublisher publisher;
public void register(String name) {
// ํ์๊ฐ์
์ฒ๋ฆฌ ๋ก์ง
System.out.println("ํ์ ์ถ๊ฐ ์๋ฃ");
// ์ด๋ฒคํธ ๋ฑ๋ก
publisher.publishEvent(new RegisteredEvent(name));
// // ๊ฐ์
์ถํ ํธ์ ์๋ฆผ
// alarmService.send(name);
//
// // ์นด์นด์ค ์๋ฆผํก ์ ์ก
// alimTalkService.send(name);
}
}
์ด๋ฒคํธ๋ฅผ ๋ณด๋ด๋ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ธฐ ์ํด ApplicationEventPublisher๋ฅผ ์ฃผ์ ํด์ฃผ์๊ณ ,
ํ์๊ฐ์ ์ฒ๋ฆฌ๋ฅผ ์๋ฃํ๋ฉด, publishEvent๋ฅผ ์ฌ์ฉํด ์ด๋ฒคํธ๋ฅผ ๋ฐํํด์ค๋ค.
๊ธฐ์กด์ ์์๋ ์๋ฆผ ์๋น์ค์์ ์์กด๊ด๊ณ๋ ์ ๊ฑฐํ์๋ค.
- ์ด๋ฒคํธ ํธ๋ค๋ฌ ๋ฑ๋ก
@Component
@RequiredArgsConstructor
public class SmsEventHandler {
private final AlarmService alarmService;
private final AlimTalkService alimTalkService;
@EventListener
public void sendFCM(RegisteredEvent event) {
System.out.println("[SmsEventHandler - sendFCM]");
alarmService.send(event.getName());
}
@EventListener
public void sendAlimTalk(RegisteredEvent event) {
System.out.println("[SmsEventHandler - sendAlimTalk]");
alimTalkService.send(event.getName());
}
}
@EventListener๋ฅผ ์ฌ์ฉํ๋ฉด ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ก ๋ฑ๋ก์ด ๋๊ณ , ๋งค๊ฐ๋ณ์์ ์ด๋ฒคํธ ํด๋์ค๋ฅผ ์ ์ํ๋ฉด ํด๋น ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ ๋ ์์ ํด์ ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๋ค.
- ํ ์คํธ
@Test
@DisplayName("ํ์ ๊ฐ์
ํ
์คํธ")
void register() {
String name = "dgjinsu";
memberService.register(name);
}
๊ฐ๋จํ๊ฒ ํ ์คํธ์ฝ๋๋ฅผ ์์ฑํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ์ดํด๋ณด๋ฉด ์๋์ ๊ฐ์ด ๋ก๊ทธ๊ฐ ์ฐํ๋๊ฑธ ํ์ธํ ์ ์๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก ๋์ผํ ๋์์ ์ํํ์ง๋ง ์ปดํฌ๋ํธ๊ฐ ๊ฒฐํฉ๋๋ ๋ฎ์ถ ์ ์์๋ค.
3.2 ์ฑ๋ฅ ๋ฌธ์ ํด๊ฒฐ
์๋ฆผ์ ๋ณด๋ด๋ ๊ฐ service ๋ก์ง์ ์์๋ก ์ฐ๋ ๋ ์ฌ๋ฆฝ์ ๊ฑธ์ด์ฃผ์ด ๋ก์ง์ ์ฒ๋ฆฌํ๋๋ฐ ์ค๋ ๊ฑธ๋ฆฌ๋ ์ํฉ์ด๋ผ ๊ฐ์ ํด๋ณด์๋ค.
@Service
public class AlarmService {
public void send(String name) throws InterruptedException {
Thread.sleep(3000); // 3์ด sleep
System.out.println(name + "์๊ฒ push ์๋ฆผ ๋ฐ์ก");
}
}
@Service
public class AlimTalkService {
public void send(String name) throws InterruptedException {
Thread.sleep(3000); // 3์ด sleep
System.out.println(name + "์๊ฒ ์๋ฆผํก ๋ฐ์ก");
}
}
client ์ ์ฅ์์ ํ์๊ฐ์ ์ ์์ฒญํ๊ณ ์ฝ 6์ด ๋ค ์๋ฃ ํ๋ฉด์ ๋ณผ ์ ์๋ค.
@EnableAsync ์ @Async ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํด์ฃผ๋ฉด ํด๊ฒฐ ๊ฐ๋ฅํ๋ค.
@SpringBootApplication
@EnableAsync // ์ถ๊ฐ
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
@Component
@RequiredArgsConstructor
public class SmsEventHandler {
private final AlarmService alarmService;
private final AlimTalkService alimTalkService;
@Async // ์ถ๊ฐ
@EventListener
public void sendFCM(RegisteredEvent event) throws InterruptedException {
System.out.println("[SmsEventHandler - sendFCM]");
alarmService.send(event.getName());
}
@Async // ์ถ๊ฐ
@EventListener
public void sendAlimTalk(RegisteredEvent event) throws InterruptedException {
System.out.println("[SmsEventHandler - sendAlimTalk]");
alimTalkService.send(event.getName());
}
}
๊ฒฐ๊ณผ๋ 2ms๋ก ํธ์ ์๋ฆผ ๋ก์ง๊ณผ ์๋ฆผํก ๋ก์ง์ ์ํฅ์ ๋ฐ์ง ์๋ ๋ชจ์ต์ ํ์ธํ ์ ์๋ค.
3.3 ํธ๋์ญ์ ์ฒ๋ฆฌ
ํธ๋์ญ์ ์ ๋ถ๋ฆฌํด ๋ ๋ฆฝ์ ์ผ๋ก ์คํํ๊ธฐ์ ์์ ํธ๋์ญ์ ์ ๋ํด ๊ฐ๋ณ๊ฒ ์ดํด๋ณด์.
A,B๋ ๊ฐ๊ฐ ํธ๋์ญ์ ์ด๋ ธํ ์ด์ ์ด ๋ถ์ด์๋ค. Aํจ์์์ Bํจ์๋ฅผ ์คํํ๊ณ Bํจ์์์ ์์ธ๊ฐ ๋ฐ์ํ๋ค๋ฉด Aํจ์๋ ๋กค๋ฐฑ์ด ๋ ๊น??
@Service
@RequiredArgsConstructor
@Transactional
public class MemberService {
private final PlatformTransactionManager transactionManager;
private final TestService testService;
public void register(String name) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
testService.a();
// ํธ๋์ญ์
์ปค๋ฐ
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
System.out.println("ํธ๋์ญ์
๋กค๋ฐฑ");
}
}
@Service
public class TestService {
public void a() throws Exception {
Thread.sleep(3000);
throw new Exception("์๋ฌ");
}
}
aํจ์์์ ๋ณ๋๋ก propagation์ ์ค์ ํ์ง ์์๊ธฐ ๋๋ฌธ์ A ํจ์์ ํธ๋์ญ์ ๊ณผ B ํจ์์ ํธ๋์ญ์ ์ ๋์ผํ ์ปจํ ์คํธ๋ฅผ ๊ณต์ ํ๊ฒ ๋๋ค. ๋ฐ๋ผ์ B ํจ์์์ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด A ํจ์์ ํธ๋์ญ์ ๋ ๋กค๋ฐฑ๋๋ค.
ํ์ง๋ง aํจ์๊ฐ @Async๋ก ์คํ๋๋ค๋ฉด ์ํฉ์ด ๋ฌ๋ผ์ง๋ค.
๋น๋๊ธฐ ํธ์ถ์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฉ์๋ ํธ์ถ์ด ๋น๋๊ธฐ์ ์ผ๋ก ์ด๋ฃจ์ด์ง๋ฏ๋ก ํธ๋์ญ์ ์ปจํ ์คํธ๊ฐ ์๋ก ๋ค๋ฅผ ์ ์๋ค.
ํธ๋์ญ์ ๋งค๋์ ๋ ์ฃผ๋ก ํ์ฌ ์ฐ๋ ๋์ ๋ํ ํธ๋์ญ์ ์ปจํ ์คํธ๋ฅผ ๊ด๋ฆฌํ๋ฉฐ, ๋น๋๊ธฐ์ ์ธ ํธ์ถ์ ๋ค๋ฅธ ์ฐ๋ ๋์์ ์ฒ๋ฆฌ๋ ์ ์๋ค.
๋ฐ๋ผ์ A ํจ์์ B ํจ์๊ฐ ์๋ก ๋ค๋ฅธ ์ฐ๋ ๋์์ ์คํ๋ ๊ฒฝ์ฐ, ํธ๋์ญ์ ์ปจํ ์คํธ๊ฐ ๊ณต์ ๋์ง ์์ B์ ์์ธ๊ฐ A์ ํธ๋์ญ์ ๊ฒฐ๊ณผ์ ์ํฅ์ ๋ฏธ์น์ง ์๋๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก @Async๋ฅผ ์ฌ์ฉํด ๋น๋๊ธฐ์ ์ผ๋ก ์๋ฆผ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ๊ฒฝ์ฐ ๋ณ๋ค๋ฅธ ์ค์ ์ ํ์ง ์์๋ ํธ๋์ญ์ ์ด ๋ถ๋ฆฌ๋๋ค.
ํ์ง๋ง ํธ๋์ญ์ ๋ถ๋ฆฌํ๋ค๊ณ ๋ค ํด๊ฒฐ๋๋ ๊ฒ์ ์๋๋ค.
๋์ ์ฝ๋๋ฅผ ์ดํด๋ณด์์ ๋ ํ์๊ฐ์ ์ด ์๋ฃ๋์ผ ์๋ฆผ ๋ก์ง์ด ์คํ๋์ผ ํ๋๋ฐ, ํ์๊ฐ์ ์ด ์คํจํด๋ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํด๋๊ธฐ ๋๋ฌธ์ ์๋ฆผ ๋ก์ง์ด ์คํ๋ ์ ์๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ๋ฐฉ๋ฒ๋ ๋ฌผ๋ก ์๋ค.
์ด๋ถ๋ถ์ ๊ธ์ด ๊ธธ์ด์ ธ ์๋ ํฌ์คํ ์์ ์์ธํ๊ฒ ๋ค๋ค๋ดค์
https://dgjinsu.tistory.com/42
'Spring Boot, JAVA ๐ฑ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Event] ์ค์ ํ๋ก์ ํธ์์ Spring Event๋ฅผ ์ ์ฉํด ๋ณต์กํ ์์กด ๊ด๊ณ ํด์ํ๊ธฐ (0) | 2024.02.23 |
---|---|
[Event] Spring Event ๋์ ์ ํธ๋์ญ์ ์ฒ๋ฆฌ์ ๋ํ ๊ณ ๋ฏผ (1) | 2024.02.22 |
[Redis] Redis๋ก ์ต๊ทผ ๊ฒ์ ๊ธฐ๋ก ๊ด๋ฆฌํ๊ธฐ (0) | 2024.02.02 |
[Redis] Spring boot ์์ Redis๋ก ์ฑ๋ฅ ๊ฐ์ ํ๊ธฐ (0) | 2024.01.08 |
[Spring] AOP ๊ฐ๋ ๋ฐ ์ ์ฉํ๊ธฐ (1) | 2024.01.06 |