๐ฑ Spring Events๋ก ์ ํ๋ฆฌ์ผ์ด์ ๋ด ํต์ ๋ง์คํฐํ๊ธฐ! ๐

์๋ , ์น๊ตฌ๋ค! ์ค๋์ ์ ๋ง ์ฌ๋ฏธ์๊ณ ์ ์ฉํ ์ฃผ์ ๋ฅผ ๊ฐ์ง๊ณ ์์ด. ๋ฐ๋ก Spring Events๋ฅผ ํ์ฉํ ์ ํ๋ฆฌ์ผ์ด์ ๋ด ํต์ ์ ๋ํด ๊น์ด ํํค์ณ๋ณผ ๊ฑฐ์ผ. ๐ ์ด ์ฃผ์ ๋ Java ๊ฐ๋ฐ์๋ค์๊ฒ ํนํ ์ค์ํ๋๊น, ์ฐ๋ฆฌ ํจ๊ป ์ฆ๊ฒ๊ฒ ๋ฐฐ์๋ณด์๊ณ !
๋จผ์ , ์ฐ๋ฆฌ๊ฐ ์ ์ด๋ฐ ๊ฑธ ๋ฐฐ์์ผ ํ๋์ง ์์๋ณผ๊น? ๐ค ํ๋์ ์ ํ๋ฆฌ์ผ์ด์ ๋ค์ ์ ์ ๋ ๋ณต์กํด์ง๊ณ ์์ด. ์ฌ๋ฌ ์ปดํฌ๋ํธ๋ค์ด ์๋ก ์ํตํ๋ฉด์ ์ผ์ ์ฒ๋ฆฌํด์ผ ํ๊ฑฐ๋ . ๊ทธ๋ฐ๋ฐ ์ด ์ํต์ ์ด๋ป๊ฒ ํ๋ฉด ๊ฐ์ฅ ํจ์จ์ ์ผ๋ก ํ ์ ์์๊น? ๋ฐ๋ก ์ฌ๊ธฐ์ Spring Events๊ฐ ๋ฑ์ฅํ๋ ๊ฑฐ์ผ!
Spring Events๋ฅผ ์ฌ์ฉํ๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค๋ฅธ ๋ถ๋ถ๋ค์ด ์๋ก ๋์จํ๊ฒ ๊ฒฐํฉ๋๋ฉด์๋ ํจ๊ณผ์ ์ผ๋ก ํต์ ํ ์ ์์ด. ๋ง์น ์ฌ๋ฅ๋ท์์ ๋ค์ํ ์ฌ๋ฅ์ ๊ฐ์ง ์ฌ๋๋ค์ด ์๋ก ์ฐ๊ฒฐ๋๋ ๊ฒ์ฒ๋ผ ๋ง์ด์ผ. ๐
์, ์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก Spring Events์ ์ธ๊ณ๋ก ๋ฐ์ด๋ค์ด๋ณผ ์ค๋น ๋์ด? ์ฐ๋ฆฌ๋ ์ด ์ฌ์ ์ ํตํด ๋ค์๊ณผ ๊ฐ์ ๋ด์ฉ๋ค์ ๋ฐฐ์ฐ๊ฒ ๋ ๊ฑฐ์ผ:
- Spring Events์ ๊ธฐ๋ณธ ๊ฐ๋ ๐ก
- ์ด๋ฒคํธ ๋ฐํ์(Publisher)์ ๊ตฌ๋ ์(Listener) ๋ง๋ค๊ธฐ ๐ญ
- ๋๊ธฐ์ vs ๋น๋๊ธฐ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ โก
- ํธ๋์ญ์ ๋ฐ์ด๋ ์ด๋ฒคํธ ๋ค๋ฃจ๊ธฐ ๐ผ
- Spring Events์ ๊ณ ๊ธ ๊ธฐ๋ฅ๋ค ๐
- ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๋ ๋ฐฉ๋ฒ ๐ ๏ธ
ํฅ๋ฏธ์ง์งํ์ง ์์? ๊ทธ๋ผ ์ด์ ํ๋์ฉ ์์ธํ ์์๋ณด์๊ณ !
๐ Spring Events์ ๊ธฐ๋ณธ ๊ฐ๋
์, ์น๊ตฌ๋ค! ์ด์ Spring Events์ ๊ธฐ๋ณธ ๊ฐ๋ ์ ๋ํด ์์๋ณผ ์ฐจ๋ก์ผ. ๐ค Spring Events๋ ๋ง ๊ทธ๋๋ก Spring ํ๋ ์์ํฌ์์ ์ ๊ณตํ๋ ์ด๋ฒคํธ ๋ฉ์ปค๋์ฆ์ด์ผ. ์ด๊ฑธ ์ด์ฉํ๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ๋ฌ ๋ถ๋ถ๋ค์ด ์๋ก ๋ฉ์์ง๋ฅผ ์ฃผ๊ณ ๋ฐ์ ์ ์์ง.
์ด๋ฒคํธ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ์ด๋ผ๊ณ ๋ค์ด๋ดค์ด? ์ด๊ฑด ํ๋ก๊ทธ๋จ์ ํ๋ฆ์ด ์ด๋ฒคํธ์ ๋ฐ์๊ณผ ์ฒ๋ฆฌ์ ์ํด ๊ฒฐ์ ๋๋ ํ๋ก๊ทธ๋๋ฐ ํจ๋ฌ๋ค์์ด์ผ. Spring Events๋ ์ด ๊ฐ๋ ์ ๋ฐ๋ฅด๊ณ ์์ด.
Spring Events์ ํต์ฌ ๊ตฌ์ฑ ์์๋ค์ ์ดํด๋ณผ๊น?
- ์ด๋ฒคํธ(Event): ๋ฐ์ํ ์ฌ๊ฑด์ ๋ํ๋ด๋ ๊ฐ์ฒด์ผ. ์๋ฅผ ๋ค๋ฉด, "์ฌ์ฉ์๊ฐ ๊ฐ์ ํ๋ค" ๊ฐ์ ์ ๋ณด๋ฅผ ๋ด๊ณ ์์ง.
- ์ด๋ฒคํธ ๋ฐํ์(Event Publisher): ์ด๋ฒคํธ๋ฅผ ๋ง๋ค์ด์ ์์คํ ์ ์๋ฆฌ๋ ์ญํ ์ ํด. ๋ง์น ํํฐ์์ "์ผ์ดํฌ ์๋ฅด์!"๋ผ๊ณ ์ธ์น๋ ์ฌ๋์ด์ผ.
- ์ด๋ฒคํธ ๋ฆฌ์ค๋(Event Listener): ํน์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ ๋ ๋ฐ์ํ๋ ๊ฐ์ฒด์ผ. ์ผ์ดํฌ๋ฅผ ๋จน์ผ๋ฌ ๋ชจ์ฌ๋ ์ฌ๋๋ค์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋ผ.
์ด ์ธ ๊ฐ์ง๋ง ์์ผ๋ฉด ์ฐ๋ฆฌ๋ Spring Events๋ฅผ ์ฌ์ฉํ ์ ์์ด! ๐
๊ทธ๋ฐ๋ฐ ๋ง์ด์ผ, Spring Events๋ฅผ ์ฌ์ฉํ๋ฉด ์ด๋ค ์ฅ์ ์ด ์์๊น? ๐ค
- ๋์จํ ๊ฒฐํฉ(Loose Coupling): ์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํ๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ๋ฌ ๋ถ๋ถ๋ค์ด ์๋ก ์ง์ ์ ์ผ๋ก ์์กดํ์ง ์์๋ ๋ผ. ์ด๊ฑด ๋ง์น ์ฌ๋ฅ๋ท์์ ์ฌ๋ฅ ์ ๊ณต์์ ๊ตฌ๋งค์๊ฐ ์ง์ ๋ง๋์ง ์์๋ ์๋น์ค๋ฅผ ์ฃผ๊ณ ๋ฐ์ ์ ์๋ ๊ฒ๊ณผ ๋น์ทํด.
- ํ์ฅ์ฑ(Scalability): ์๋ก์ด ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ณ ์ถ์ ๋, ๊ธฐ์กด ์ฝ๋๋ฅผ ํฌ๊ฒ ์์ ํ์ง ์๊ณ ๋ ์๋ก์ด ๋ฆฌ์ค๋๋ฅผ ์ถ๊ฐํ ์ ์์ด.
- ๊ด์ฌ์ฌ์ ๋ถ๋ฆฌ(Separation of Concerns): ๊ฐ ์ปดํฌ๋ํธ๋ ์์ ์ ์ฃผ์ ๊ธฐ๋ฅ์๋ง ์ง์คํ ์ ์์ด. ๋ค๋ฅธ ๋ถ๋ถ์ ์ด๋ค ์ํฅ์ ๋ฏธ์น ์ง ๊ฑฑ์ ํ ํ์๊ฐ ์์ง.
- ํ ์คํธ ์ฉ์ด์ฑ(Testability): ์ด๋ฒคํธ ๊ธฐ๋ฐ ์์คํ ์ ๊ฐ ๋ถ๋ถ์ ๋ ๋ฆฝ์ ์ผ๋ก ํ ์คํธํ๊ธฐ ์ฌ์.
์! ๋ฒ์จ๋ถํฐ Spring Events๊ฐ ์ผ๋ง๋ ๊ฐ๋ ฅํ ๋๊ตฌ์ธ์ง ๋๊ปด์ง์ง ์์? ๐ฒ
์ด ๊ทธ๋ฆผ์ ๋ณด๋ฉด Spring Events์ ๊ธฐ๋ณธ ๊ตฌ์กฐ๋ฅผ ํ๋์ ์ดํดํ ์ ์์ด. Event Publisher๊ฐ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํค๋ฉด, ๊ทธ ์ด๋ฒคํธ๋ Spring Application ๋ด์์ ์ ๋ฌ๋์ด Event Listener์๊ฒ ๋์ฐฉํด. ๊ทธ๋ฌ๋ฉด Listener๋ ๊ทธ ์ด๋ฒคํธ์ ๋ฐ์ํ๋ ๊ฑฐ์ง.
์, ์ด์ ์ฐ๋ฆฌ๋ Spring Events์ ๊ธฐ๋ณธ ๊ฐ๋ ์ ์๊ฒ ๋์ด. ๐ ๋ค์ ์น์ ์์๋ ์ด ๊ฐ๋ ์ ์ค์ ๋ก ์ด๋ป๊ฒ ์ฝ๋๋ก ๊ตฌํํ๋์ง ์์๋ณผ ๊ฑฐ์ผ. ์ค๋น๋์ง? ๊ณ์ ๊ฐ๋ณด์๊ณ ! ๐
๐ญ ์ด๋ฒคํธ ๋ฐํ์(Publisher)์ ๊ตฌ๋ ์(Listener) ๋ง๋ค๊ธฐ
์๋ , ์น๊ตฌ๋ค! ์ด์ ์ฐ๋ฆฌ๊ฐ ๋ฐฐ์ด Spring Events์ ๊ฐ๋ ์ ์ค์ ์ฝ๋๋ก ๊ตฌํํด๋ณผ ์๊ฐ์ด์ผ. ๐ ์ฐ๋ฆฌ๋ ์ด๋ฒคํธ ๋ฐํ์(Publisher)์ ๊ตฌ๋ ์(Listener)๋ฅผ ๋ง๋ค์ด๋ณผ ๊ฑฐ์ผ. ๋ง์น ์ฌ๋ฅ๋ท์์ ์ฌ๋ฅ์ ์ ๊ณตํ๋ ์ฌ๋๊ณผ ๊ทธ ์ฌ๋ฅ์ ํ์๋ก ํ๋ ์ฌ๋์ ์ฐ๊ฒฐํ๋ ๊ฒ์ฒ๋ผ ๋ง์ด์ผ!
1. ์ด๋ฒคํธ(Event) ํด๋์ค ๋ง๋ค๊ธฐ
๋จผ์ , ์ฐ๋ฆฌ์ ์ด๋ฒคํธ๋ฅผ ์ ์ํ๋ ํด๋์ค๋ฅผ ๋ง๋ค์ด์ผ ํด. ์ด ํด๋์ค๋ ์ด๋ฒคํธ์ ๊ด๋ จ๋ ์ ๋ณด๋ฅผ ๋ด๊ณ ์์ ๊ฑฐ์ผ.
import org.springframework.context.ApplicationEvent;
public class UserRegisteredEvent extends ApplicationEvent {
private final String username;
public UserRegisteredEvent(Object source, String username) {
super(source);
this.username = username;
}
public String getUsername() {
return username;
}
}
์ฌ๊ธฐ์ UserRegisteredEvent๋ ์ฌ์ฉ์๊ฐ ๋ฑ๋ก๋์ ๋ ๋ฐ์ํ๋ ์ด๋ฒคํธ๋ฅผ ๋ํ๋ด. ์ด ์ด๋ฒคํธ๋ ๋ฑ๋ก๋ ์ฌ์ฉ์์ ์ด๋ฆ์ ๋ด๊ณ ์์ด.
2. ์ด๋ฒคํธ ๋ฐํ์(Publisher) ๋ง๋ค๊ธฐ
๋ค์์ผ๋ก, ์ด๋ฒคํธ๋ฅผ ๋ฐํํ๋ ํด๋์ค๋ฅผ ๋ง๋ค์ด๋ณผ ๊ฑฐ์ผ. ์ด ํด๋์ค๋ ApplicationEventPublisher๋ฅผ ์ฌ์ฉํด์ ์ด๋ฒคํธ๋ฅผ ๋ฐํํด.
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@Component
public class UserService {
private final ApplicationEventPublisher eventPublisher;
public UserService(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void registerUser(String username) {
// ์ฌ์ฉ์ ๋ฑ๋ก ๋ก์ง...
System.out.println("User registered: " + username);
// ์ด๋ฒคํธ ๋ฐํ
eventPublisher.publishEvent(new UserRegisteredEvent(this, username));
}
}
์ด UserService ํด๋์ค๋ ์ฌ์ฉ์๋ฅผ ๋ฑ๋กํ๋ ๋ฉ์๋๋ฅผ ๊ฐ์ง๊ณ ์์ด. ์ฌ์ฉ์๊ฐ ๋ฑ๋ก๋๋ฉด, ์ด ๋ฉ์๋๋ UserRegisteredEvent๋ฅผ ๋ฐํํด. ๋ง์น ์ฌ๋ฅ๋ท์์ ์๋ก์ด ์ฌ๋ฅ์ด ๋ฑ๋ก๋๋ค๊ณ ์๋ฆฌ๋ ๊ฒ๊ณผ ๋น์ทํด!
3. ์ด๋ฒคํธ ๋ฆฌ์ค๋(Listener) ๋ง๋ค๊ธฐ
๋ง์ง๋ง์ผ๋ก, ์ฐ๋ฆฌ์ ์ด๋ฒคํธ๋ฅผ ๋ฃ๊ณ ๋ฐ์ํ ๋ฆฌ์ค๋๋ฅผ ๋ง๋ค์ด๋ณผ ๊ฑฐ์ผ.
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class UserRegistrationListener {
@EventListener
public void handleUserRegisteredEvent(UserRegisteredEvent event) {
System.out.println("Received spring event - user registered: " + event.getUsername());
// ์ฌ๊ธฐ์ ์ถ๊ฐ์ ์ธ ๋ก์ง์ ๋ฃ์ ์ ์์ด. ์๋ฅผ ๋ค๋ฉด:
// - ํ์ ์ด๋ฉ์ผ ๋ณด๋ด๊ธฐ
// - ์ฌ์ฉ์ ํ๋กํ ์ด๊ธฐํํ๊ธฐ
// - ํต๊ณ ์
๋ฐ์ดํธํ๊ธฐ ๋ฑ๋ฑ
}
}
์ด UserRegistrationListener ํด๋์ค๋ @EventListener ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํด์ UserRegisteredEvent๋ฅผ ๋ฃ๊ณ ์์ด. ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด, handleUserRegisteredEvent ๋ฉ์๋๊ฐ ํธ์ถ๋ผ.
์, ์ด์ ์ฐ๋ฆฌ๊ฐ ๋ง๋ ๊ตฌ์กฐ๋ฅผ ์๊ฐํํด๋ณผ๊น?
์ด ๋ค์ด์ด๊ทธ๋จ์ ๋ณด๋ฉด, Spring Events์ ํ๋ฆ์ ์ฝ๊ฒ ์ดํดํ ์ ์์ด:
- UserService๊ฐ UserRegisteredEvent๋ฅผ ๋ฐํํด.
- UserRegisteredEvent๋ Spring Application Context๋ฅผ ํตํด ์ ๋ฌ๋ผ.
- UserRegistrationListener๊ฐ ์ด ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ ์ฒ๋ฆฌํด.
์ด๋ ๊ฒ ํ๋ฉด UserService๋ ๋๊ฐ ์ด ์ด๋ฒคํธ๋ฅผ ๋ฃ๊ณ ์๋์ง ์ ํ์๊ฐ ์์ด. ๊ทธ์ ์ด๋ฒคํธ๋ฅผ ๋ฐํํ๊ธฐ๋ง ํ๋ฉด ๋ผ. ๋ง์ฐฌ๊ฐ์ง๋ก UserRegistrationListener๋ ์ด ์ด๋ฒคํธ๊ฐ ์ด๋์ ์๋์ง ์ ๊ฒฝ ์ธ ํ์๊ฐ ์์ด. ๊ทธ์ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ ์ฒ๋ฆฌํ๊ธฐ๋ง ํ๋ฉด ๋ผ.
์ด๋ฐ ๊ตฌ์กฐ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ ์ ์ฐํ๊ณ ํ์ฅ ๊ฐ๋ฅํ๊ฒ ๋ง๋ค์ด์ค. ์๋ฅผ ๋ค์ด, ๋์ค์ ์ฌ์ฉ์ ๋ฑ๋ก ์ ์ถ๊ฐ์ ์ธ ์์ (์: ํ์ ์ด๋ฉ์ผ ๋ณด๋ด๊ธฐ)์ ํ๊ณ ์ถ๋ค๋ฉด, UserService๋ฅผ ์์ ํ ํ์ ์์ด ์๋ก์ด ๋ฆฌ์ค๋๋ฅผ ์ถ๊ฐํ๊ธฐ๋ง ํ๋ฉด ๋ผ!
์, ์ด์ ์ฐ๋ฆฌ๋ Spring Events์ ๊ธฐ๋ณธ์ ์ธ ๊ตฌํ ๋ฐฉ๋ฒ์ ์๊ฒ ๋์ด. ๐ ๋ค์ ์น์ ์์๋ ๋๊ธฐ์๊ณผ ๋น๋๊ธฐ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ์ ๋ํด ์์๋ณผ ๊ฑฐ์ผ. ๊ณ์ ๋ฐ๋ผ์! ๐
โก ๋๊ธฐ์ vs ๋น๋๊ธฐ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ
์๋ , ์น๊ตฌ๋ค! ์ด๋ฒ์๋ Spring Events์ ์์ฃผ ์ค์ํ ์ฃผ์ ์ธ ๋๊ธฐ์๊ณผ ๋น๋๊ธฐ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ์ ๋ํด ์์๋ณผ ๊ฑฐ์ผ. ๐ ์ด ๊ฐ๋ ์ ์ดํดํ๋ฉด ๋์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋์ฑ ํจ์จ์ ์ผ๋ก ๋ง๋ค ์ ์์ ๊ฑฐ์ผ!
๋๊ธฐ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ
๋จผ์ ๋๊ธฐ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ถํฐ ์ดํด๋ณผ๊น? ๋๊ธฐ์ ์ฒ๋ฆฌ๋ ๊ธฐ๋ณธ์ ์ผ๋ก Spring Events๊ฐ ์๋ํ๋ ๋ฐฉ์์ด์ผ.
๋๊ธฐ์ ์ฒ๋ฆฌ์ ์ฅ๋จ์ ์ ์ดํด๋ณผ๊น?
- ์ฅ์ :
- ์์๊ฐ ๋ณด์ฅ๋ผ. ๋ชจ๋ ๋ฆฌ์ค๋๊ฐ ์์ฐจ์ ์ผ๋ก ์คํ๋๋๊น.
- ํธ๋์ญ์ ๊ด๋ฆฌ๊ฐ ์ฌ์. ๋ชจ๋ ์ฒ๋ฆฌ๊ฐ ๊ฐ์ ํธ๋์ญ์ ๋ด์์ ์ด๋ฃจ์ด์ง๋๊น.
- ์๋ฌ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋จํด. ์ด๋ค ๋ฆฌ์ค๋์์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉด ๋ฐ๋ก ์ ์ ์์ผ๋๊น.
- ๋จ์ :
- ์ฑ๋ฅ ์ด์๊ฐ ์์ ์ ์์ด. ๋ชจ๋ ๋ฆฌ์ค๋๊ฐ ์ฒ๋ฆฌ๋ฅผ ๋ง์น ๋๊น์ง ๊ธฐ๋ค๋ ค์ผ ํ๋๊น.
- ๋ธ๋กํน์ด ๋ฐ์ํด. ์ด๋ฒคํธ ์ฒ๋ฆฌ ์ค์๋ ๋ค๋ฅธ ์์ ์ ํ ์ ์์ผ๋๊น.
์, ์ด์ ๋๊ธฐ์ ์ฒ๋ฆฌ์ ์ฝ๋๋ฅผ ํ๋ฒ ๋ณผ๊น?
@Component
public class SynchronousEventListener {
@EventListener
public void handleUserRegisteredEvent(UserRegisteredEvent event) {
System.out.println("Synchronously handling user registration: " + event.getUsername());
// ์ฌ๊ธฐ์ ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๋ ์์
์ ํ๋ค๊ณ ๊ฐ์ ํด๋ณด์.
try {
Thread.sleep(2000); // 2์ด ๋์ ๋๊ธฐ
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Finished handling user registration for: " + event.getUsername());
}
}
์ด ์ฝ๋์์๋ @EventListener ์ด๋ ธํ ์ด์ ๋ง ์ฌ์ฉํ์ด. ์ด๋ ๊ฒ ํ๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก ๋๊ธฐ์์ผ๋ก ์ฒ๋ฆฌ๋ผ. ์ด๋ฒคํธ ์ฒ๋ฆฌ์ 2์ด๊ฐ ๊ฑธ๋ฆฐ๋ค๊ณ ๊ฐ์ ํ๋๋ฐ, ์ด ๋์ ๋ค๋ฅธ ์์ ์ ๋ธ๋กํน๋ผ.
๋น๋๊ธฐ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ
์ด๋ฒ์ ๋น๋๊ธฐ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ฅผ ์์๋ณผ ์ฐจ๋ก์ผ! ๐
๋น๋๊ธฐ์ ์ฒ๋ฆฌ์ ์ฅ๋จ์ ๋ ์ดํด๋ณผ๊น?
- ์ฅ์ :
- ์ฑ๋ฅ์ด ํฅ์๋ผ. ์ด๋ฒคํธ ์ฒ๋ฆฌ์ ๋ค๋ฅธ ์์ ์ด ๋ณ๋ ฌ๋ก ์คํ๋๋๊น.
- ์๋ต์ฑ์ด ์ข์์ ธ. ์ด๋ฒคํธ ๋ฐํ ํ ๋ฐ๋ก ๋ค์ ์์ ์ผ๋ก ๋์ด๊ฐ ์ ์์ผ๋๊น.
- ํ์ฅ์ฑ์ด ์ข์. ๋ ๋ง์ ๋ฆฌ์ค๋๋ฅผ ์ถ๊ฐํด๋ ์ ์ฒด ์ฑ๋ฅ์ ํฐ ์ํฅ์ ์ฃผ์ง ์์.
- ๋จ์ :
- ์์๊ฐ ๋ณด์ฅ๋์ง ์์. ์ด๋ค ๋ฆฌ์ค๋๊ฐ ๋จผ์ ์คํ๋ ์ง ์ ์ ์์ด.
- ์๋ฌ ์ฒ๋ฆฌ๊ฐ ๋ณต์กํด. ๋น๋๊ธฐ๋ก ์คํ๋๋ ๋ฆฌ์ค๋์์ ๋ฐ์ํ ์๋ฌ๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ง ๊ณ ๋ฏผํด์ผ ํด.
- ํธ๋์ญ์ ๊ด๋ฆฌ๊ฐ ์ด๋ ค์. ๊ฐ ๋ฆฌ์ค๋๊ฐ ๋ณ๋์ ํธ๋์ญ์ ์์ ์คํ๋๋๊น.
์, ์ด์ ๋น๋๊ธฐ์ ์ฒ๋ฆฌ์ ์ฝ๋๋ฅผ ๋ณผ๊น?
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("Async-");
executor.initialize();
return executor;
}
}
@Component
public class AsynchronousEventListener {
@Async
@EventListener
public void handleUserRegisteredEvent(UserRegisteredEvent event) {
System.out.println("Asynchronously handling user registration: " + event.getUsername());
// ์ฌ๊ธฐ์ ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๋ ์์
์ ํ๋ค๊ณ ๊ฐ์ ํด๋ณด์.
try {
Thread.sleep(2000); // 2์ด ๋์ ๋๊ธฐ
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Finished asynchronously handling user registration for: " + event.getUsername());
}
}
์ฌ๊ธฐ์๋ @Async ์ด๋ ธํ ์ด์ ์ ์ถ๊ฐํ์ด. ์ด๋ ๊ฒ ํ๋ฉด ์ด ๋ฆฌ์ค๋๋ ๋ณ๋์ ์ค๋ ๋์์ ๋น๋๊ธฐ์ ์ผ๋ก ์คํ๋ผ. ๊ทธ๋ฆฌ๊ณ @EnableAsync๋ก ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ํ์ฑํํ๊ณ , TaskExecutor๋ฅผ ์ค์ ํด์ ์ค๋ ๋ ํ์ ๊ด๋ฆฌํ๊ณ ์์ด.
๋๊ธฐ์ vs ๋น๋๊ธฐ์: ์ธ์ ๋ฌด์์ ์ฌ์ฉํด์ผ ํ ๊น?
์, ์ด์ ๋ ๋ฐฉ์์ ๋ค ๋ฐฐ์ ์ผ๋ ์ธ์ ์ด๋ค ๋ฐฉ์์ ์ฌ์ฉํด์ผ ํ ์ง ์์๋ณด์!
- ์ด๋ฒคํธ ์ฒ๋ฆฌ ์์๊ฐ ์ค์ํ ๋
- ์ด๋ฒคํธ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ ์ฆ์ ํ์ํ ๋
- ํธ๋์ญ์ ์ผ๊ด์ฑ์ด ์ค์ํ ๋
- ์๋ฌ๋ฅผ ์ฆ์ ์ฒ๋ฆฌํด์ผ ํ ๋
- ์ด๋ฒคํธ ์ฒ๋ฆฌ์ ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆด ๋
- ์ฌ๋ฌ ์ด๋ฒคํธ๋ฅผ ๋ณ๋ ฌ๋ก ์ฒ๋ฆฌํ๊ณ ์ถ์ ๋
- ์ด๋ฒคํธ ๋ฐํ์์ ์๋ต์ฑ์ ๋์ด๊ณ ์ถ์ ๋
- ์ด๋ฒคํธ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ ์ฆ์ ํ์ํ์ง ์์ ๋
์๋ฅผ ๋ค์ด, ์ฌ๋ฅ๋ท์์ ๊ฒฐ์ ์ฒ๋ฆฌ๋ ๋๊ธฐ์์ผ๋ก, ์๋ฆผ ๋ฐ์ก์ ๋น๋๊ธฐ์์ผ๋ก ์ฒ๋ฆฌํ๋ ๊ฒ ์ข์ ๊ฑฐ์ผ. ๊ฒฐ์ ๋ ์ฆ์ ์ฒ๋ฆฌ๋๊ณ ํ์ธ๋์ด์ผ ํ์ง๋ง, ์๋ฆผ์ ์กฐ๊ธ ๋ฆ๊ฒ ๋ฐ์ก๋์ด๋ ํฐ ๋ฌธ์ ๊ฐ ์์ผ๋๊น!
์ด ๊ทธ๋ฆผ์ ๋ณด๋ฉด ๋๊ธฐ์๊ณผ ๋น๋๊ธฐ์์ ์ฐจ์ด๋ฅผ ํ๋์ ์ ์ ์์ด. ๋๊ธฐ์์์๋ ๋ชจ๋ ์ฒ๋ฆฌ๊ฐ ์์ฐจ์ ์ผ๋ก ์ด๋ฃจ์ด์ง์ง๋ง, ๋น๋๊ธฐ์์์๋ ์ด๋ฒคํธ ๋ฐํ ํ ๋ชจ๋ ๋ฆฌ์ค๋๊ฐ ๋์์ ์ฒ๋ฆฌ๋ฅผ ์์ํ๋ ๊ฑธ ๋ณผ ์ ์์ง.
์, ์ด์ ์ฐ๋ฆฌ๋ ๋๊ธฐ์๊ณผ ๋น๋๊ธฐ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ์ ์ฐจ์ด์ ๊ฐ๊ฐ์ ์ฅ๋จ์ , ๊ทธ๋ฆฌ๊ณ ์ธ์ ์ด๋ค ๋ฐฉ์์ ์ฌ์ฉํด์ผ ํ๋์ง ์๊ฒ ๋์ด. ๐ ์ด ์ง์์ ํ์ฉํ๋ฉด ๋์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋์ฑ ํจ์จ์ ์ผ๋ก ๋ง๋ค ์ ์์ ๊ฑฐ์ผ!
๋ค์ ์น์ ์์๋ ํธ๋์ญ์ ๋ฐ์ด๋ ์ด๋ฒคํธ์ ๋ํด ์์๋ณผ ๊ฑฐ์ผ. ์ด๊ฑด ์ ๋ง ์ฌ๋ฏธ์๊ณ ์ ์ฉํ ์ฃผ์ ๋๊น ๊ธฐ๋ํด๋ ์ข์! ๊ณ์ ๋ฐ๋ผ์! ๐
๐ผ ํธ๋์ญ์ ๋ฐ์ด๋ ์ด๋ฒคํธ ๋ค๋ฃจ๊ธฐ
์๋ , ์น๊ตฌ๋ค! ์ด๋ฒ์๋ Spring Events์ ์์ฃผ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ์ธ ํธ๋์ญ์ ๋ฐ์ด๋ ์ด๋ฒคํธ์ ๋ํด ์์๋ณผ ๊ฑฐ์ผ. ๐ ์ด ๊ฐ๋ ์ ์ดํดํ๋ฉด ๋์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์ดํฐ ์ผ๊ด์ฑ์ ๋์ฑ ์ ์ ์งํ ์ ์์ ๊ฑฐ์ผ!
ํธ๋์ญ์ ๋ฐ์ด๋ ์ด๋ฒคํธ๋?
์ด ๊ธฐ๋ฅ์ด ์ ์ค์ํ ๊น? ๐ค ์๋ฅผ ๋ค์ด, ์ฌ์ฉ์ ๋ฑ๋ก ๊ณผ์ ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ์ฅ์ด ์คํจํ๋ฉด ํ์ ์ด๋ฉ์ผ์ ๋ณด๋ด์ง ์์์ผ ํด. ํธ๋์ญ์ ๋ฐ์ด๋ ์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํ๋ฉด ์ด๋ฐ ์ํฉ์ ์ฝ๊ฒ ์ฒ๋ฆฌํ ์ ์์ด!
ํธ๋์ญ์ ๋ฐ์ด๋ ์ด๋ฒคํธ ๊ตฌํํ๊ธฐ
์, ์ด์ ์ฝ๋๋ก ์ด๋ป๊ฒ ๊ตฌํํ๋์ง ๋ณผ๊น?
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
@Component
public class UserService {
private final ApplicationEventPublisher eventPublisher;
public UserService(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
@Transactional
public void registerUser(String username) {
// ์ฌ์ฉ์ ๋ฑ๋ก ๋ก์ง...
System.out.println("User registered: " + username);
// ์ด๋ฒคํธ ๋ฐํ
eventPublisher.publishEvent(new UserRegisteredEvent(this, username));
}
}
@Component
public class TransactionalEventListenerExample {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleUserRegisteredEvent(UserRegisteredEvent event) {
System.out.println("Handling user registration after transaction commit: " + event.getUsername());
// ์ฌ๊ธฐ์ ํ์ ์ด๋ฉ์ผ์ ๋ณด๋ด๊ฑฐ๋ ๋ค๋ฅธ ํ์ ์์
์ ์ํํ ์ ์์ด.
}
}
์ด ์ฝ๋์์ ์ฃผ๋ชฉํด์ผ ํ ์ ์ @TransactionalEventListener ์ด๋ ธํ ์ด์ ์ด์ผ. ์ด ์ด๋ ธํ ์ด์ ์ ํธ๋์ญ์ ์ ํน์ ๋จ๊ณ์์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋๋ก ์ง์ ํด. ์ฌ๊ธฐ์๋ AFTER_COMMIT์ ์ฌ์ฉํ๋๋ฐ, ์ด๋ ํธ๋์ญ์ ์ด ์ฑ๊ณต์ ์ผ๋ก ์ปค๋ฐ๋ ํ์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ค๋ ๋ป์ด์ผ.
ํธ๋์ญ์ ๋จ๊ณ (TransactionPhase)
@TransactionalEventListener๋ ๋ค์๊ณผ ๊ฐ์ ํธ๋์ญ์ ๋จ๊ณ๋ฅผ ์ง์ํด:
- AFTER_COMMIT (๊ธฐ๋ณธ๊ฐ): ํธ๋์ญ์ ์ด ์ฑ๊ณต์ ์ผ๋ก ์ปค๋ฐ๋ ํ
- AFTER_ROLLBACK: ํธ๋์ญ์ ์ด ๋กค๋ฐฑ๋ ํ
- AFTER_COMPLETION: ํธ๋์ญ์ ์ด ์๋ฃ๋ ํ (์ปค๋ฐ ๋๋ ๋กค๋ฐฑ)
- BEFORE_COMMIT: ํธ๋์ญ์ ์ด ์ปค๋ฐ๋๊ธฐ ์ง์
ํธ๋์ญ์ ๋ฐ์ด๋ ์ด๋ฒคํธ์ ์ฅ์
- ๋ฐ์ดํฐ ์ผ๊ด์ฑ ์ ์ง: ํธ๋์ญ์ ์ด ์ฑ๊ณตํ์ ๋๋ง ์ด๋ฒคํธ๊ฐ ์ฒ๋ฆฌ๋๋ฏ๋ก, ๋ฐ์ดํฐ์ ์ผ๊ด์ฑ์ ์ ์งํ ์ ์์ด.
- ์๋ฌ ์ฒ๋ฆฌ ๊ฐ์ํ: ํธ๋์ญ์ ์คํจ ์ ์๋์ผ๋ก ์ด๋ฒคํธ ์ฒ๋ฆฌ๊ฐ ์ทจ์๋๋ฏ๋ก, ๋ณ๋์ ์๋ฌ ์ฒ๋ฆฌ ๋ก์ง์ด ํ์ ์์ด.
- ๋น์ฆ๋์ค ๋ก์ง ๋ถ๋ฆฌ: ์ฃผ์ ๋น์ฆ๋์ค ๋ก์ง๊ณผ ๋ถ๊ฐ์ ์ธ ์์ (์: ์๋ฆผ ๋ฐ์ก)์ ๊น๋ํ๊ฒ ๋ถ๋ฆฌํ ์ ์์ด.
- ์ฑ๋ฅ ์ต์ ํ: AFTER_COMMIT์ ์ฌ์ฉํ๋ฉด ํธ๋์ญ์ ์๋ฃ ํ ๋น๋๊ธฐ์ ์ผ๋ก ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์์ด, ์ ์ฒด ์ฒ๋ฆฌ ์๊ฐ์ ์ค์ผ ์ ์์ด.
์, ์ด์ ํธ๋์ญ์ ๋ฐ์ด๋ ์ด๋ฒคํธ์ ๋์ ๋ฐฉ์์ ์๊ฐํํด๋ณผ๊น?
์ด ๋ค์ด์ด๊ทธ๋จ์์ ๋ณผ ์ ์๋ฏ์ด, ์ด๋ฒคํธ๋ ํธ๋์ญ์ ๋ด์์ ๋ฐํ๋์ง๋ง, ์ค์ ์ฒ๋ฆฌ๋ ํธ๋์ญ์ ์ด ์ปค๋ฐ๋ ํ์ ์ด๋ฃจ์ด์ ธ. ์ด๋ ๊ฒ ํ๋ฉด ํธ๋์ญ์ ์ด ์คํจํ์ ๋ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ ์๋์ผ๋ก ์ทจ์๋๋ ๊ฑฐ์ง!
- ๊ฑฐ๋ ์๋ฃ ํ ํ๋งค์์๊ฒ ์ ์ฐ ์ฒ๋ฆฌ ์ด๋ฒคํธ ๋ฐํ
- ์๋ก์ด ์ฌ๋ฅ ๋ฑ๋ก ํ ๊ด์ฌ ์๋ ์ฌ์ฉ์๋ค์๊ฒ ์๋ฆผ ์ด๋ฒคํธ ๋ฐํ
- ์ฌ์ฉ์ ๋ฆฌ๋ทฐ ์์ฑ ํ ํ๋งค์ ํ์ ์ ๋ฐ์ดํธ ์ด๋ฒคํธ ๋ฐํ
์, ์ด์ ์ฐ๋ฆฌ๋ ํธ๋์ญ์ ๋ฐ์ด๋ ์ด๋ฒคํธ์ ๊ฐ๋ ๊ณผ ์ฌ์ฉ๋ฒ, ๊ทธ๋ฆฌ๊ณ ๊ทธ ์ฅ์ ์ ๋ํด ์์๋ดค์ด. ๐ ์ด ๊ธฐ๋ฅ์ ์ ํ์ฉํ๋ฉด ๋์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ ์ฑ๊ณผ ํ์ฅ์ฑ์ ํ์ธต ๋ ๋์ผ ์ ์์ ๊ฑฐ์ผ!
๋ค์ ์น์ ์์๋ Spring Events์ ๊ณ ๊ธ ๊ธฐ๋ฅ๋ค์ ๋ํด ๋ ์์ธํ ์์๋ณผ ๊ฑฐ์ผ. ๊ณ์ ๋ฐ๋ผ์! ๐
๐ Spring Events์ ๊ณ ๊ธ ๊ธฐ๋ฅ๋ค
์๋ , ์น๊ตฌ๋ค! ์ง๊ธ๊น์ง ์ฐ๋ฆฌ๋ Spring Events์ ๊ธฐ๋ณธ์ ์ธ ์ฌ์ฉ๋ฒ๊ณผ ํธ๋์ญ์ ๋ฐ์ด๋ ์ด๋ฒคํธ์ ๋ํด ์์๋ดค์ด. ์ด์ ๋ ๊น์ด ๋ค์ด๊ฐ์, Spring Events์ ๊ณ ๊ธ ๊ธฐ๋ฅ๋ค์ ์ดํด๋ณผ ๊ฑฐ์ผ. ์ด ๊ธฐ๋ฅ๋ค์ ๋ง์คํฐํ๋ฉด ๋์ ์ ํ๋ฆฌ์ผ์ด์ ์ ํ์ธต ๋ ๊ฐ๋ ฅํด์ง ๊ฑฐ์ผ! ๐
1. ์กฐ๊ฑด๋ถ ์ด๋ฒคํธ ์ฒ๋ฆฌ
๋๋ก๋ ํน์ ์กฐ๊ฑด์์๋ง ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ณ ์ถ์ ๋๊ฐ ์์ด. Spring์ ์ด๋ฅผ ์ํด @EventListener์ condition ์์ฑ์ ์ ๊ณตํด.
@EventListener(condition = "#event.username.startsWith('admin')")
public void handleAdminUserRegisteredEvent(UserRegisteredEvent event) {
System.out.println("Admin user registered: " + event.getUsername());
}
์ด ์ฝ๋๋ ์ฌ์ฉ์ ์ด๋ฆ์ด 'admin'์ผ๋ก ์์ํ ๋๋ง ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํด. ์ด๋ฐ ๊ธฐ๋ฅ์ ์ฌ๋ฅ๋ท์์ ํน๋ณํ ์ ํ์ ์ฌ์ฉ์๋ ์ฌ๋ฅ์ ๋ํด์๋ง ํน์ ์์ ์ ์ํํ๊ณ ์ถ์ ๋ ์ ์ฉํ ๊ฑฐ์ผ.
2. ์ด๋ฒคํธ ์์ ์ง์
์ฌ๋ฌ ๋ฆฌ์ค๋๊ฐ ๊ฐ์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ๋, ์ฒ๋ฆฌ ์์๋ฅผ ์ง์ ํ๊ณ ์ถ์ ์ ์์ด. ์ด๋๋ @Order ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ ์ ์์ด.
@EventListener
@Order(1)
public void handleUserRegisteredEventFirst(UserRegisteredEvent event) {
System.out.println("First listener: " + event.getUsername());
}
@EventListener
@Order(2)
public void handleUserRegisteredEventSecond(UserRegisteredEvent event) {
System.out.println("Second listener: " + event.getUsername());
}
์ด๋ ๊ฒ ํ๋ฉด ์ฒซ ๋ฒ์งธ ๋ฆฌ์ค๋๊ฐ ํญ์ ๋ ๋ฒ์งธ ๋ฆฌ์ค๋๋ณด๋ค ๋จผ์ ์คํ๋ผ. ์ฌ๋ฅ๋ท์์ ์๋ฅผ ๋ค๋ฉด, ์ฌ์ฉ์ ๋ฑ๋ก ํ ๋จผ์ ํ์ ์ด๋ฉ์ผ์ ๋ณด๋ด๊ณ , ๊ทธ ๋ค์์ ์ถ์ฒ ์ฌ๋ฅ ๋ชฉ๋ก์ ์์ฑํ๋ ์์ผ๋ก ์์๋ฅผ ์ ํ ์ ์๊ฒ ์ง?
3. ์ ๋ค๋ฆญ ์ด๋ฒคํธ
๋๋ก๋ ์ฌ๋ฌ ์ ํ์ ์ด๋ฒคํธ๋ฅผ ๋น์ทํ ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌํ๊ณ ์ถ์ ๋๊ฐ ์์ด. ์ด๋ด ๋ ์ ๋ค๋ฆญ ์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํ๋ฉด ์ฝ๋ ์ค๋ณต์ ์ค์ผ ์ ์์ด.
public class EntityCreatedEvent<t> {
private final T entity;
public EntityCreatedEvent(T entity) {
this.entity = entity;
}
public T getEntity() {
return entity;
}
}
@EventListener
public void handleEntityCreatedEvent(EntityCreatedEvent> event) {
System.out.println("Entity created: " + event.getEntity());
}
</t>
์ด ๋ฐฉ์์ ์ฌ์ฉํ๋ฉด ์ฌ์ฉ์ ์์ฑ, ์ฌ๋ฅ ๋ฑ๋ก, ๊ฑฐ๋ ์๋ฃ ๋ฑ ๋ค์ํ "์์ฑ" ์ด๋ฒคํธ๋ฅผ ํ๋์ ๋ฆฌ์ค๋๋ก ์ฒ๋ฆฌํ ์ ์์ด. ์ฝ๋๊ฐ ํจ์ฌ ๊น๋ํด์ง๊ฒ ์ง?
4. ์ด๋ฒคํธ ๋ฐํ ๊ฒฐ๊ณผ ๋ฐํ
๋๋ก๋ ์ด๋ฒคํธ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํ์์๊ฒ ๋๋ ค์ฃผ๊ณ ์ถ์ ๋๊ฐ ์์ด. Spring 4.2๋ถํฐ๋ ์ด๋ฒคํธ ๋ฆฌ์ค๋๊ฐ ๊ฐ์ ๋ฐํํ ์ ์๊ฒ ๋์ด.
@EventListener
public String handleUserRegisteredEvent(UserRegisteredEvent event) {
String welcomeMessage = "Welcome, " + event.getUsername() + "!";
System.out.println(welcomeMessage);
return welcomeMessage;
}
// ์ด๋ฒคํธ ๋ฐํ
List<string> results = (List<string>) eventPublisher.publishEvent(new UserRegisteredEvent(this, "newuser"));
System.out.println("Event processing results: " + results);
</string></string>
์ด ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ฉด ์ด๋ฒคํธ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์ฆ์ ํ์ธํ๊ณ ํ์ฉํ ์ ์์ด. ์ฌ๋ฅ๋ท์์ ์๋ฅผ ๋ค๋ฉด, ์ ์ฌ๋ฅ ๋ฑ๋ก ์ ๊ด๋ จ ํ๊ทธ๋ฅผ ์๋์ผ๋ก ์์ฑํ๊ณ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ๋ก ํ๋ฉด์ ํ์ํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๊ฒ ์ง?
5. ์๋ฌ ํธ๋ค๋ง
์ด๋ฒคํธ ์ฒ๋ฆฌ ์ค ๋ฐ์ํ๋ ์๋ฌ๋ฅผ ์ฐ์ํ๊ฒ ์ฒ๋ฆฌํ๋ ๊ฒ๋ ์ค์ํด. @EventListener์ ์์ธ ์ฒ๋ฆฌ๋ฅผ ์ถ๊ฐํ ์ ์์ด.
@EventListener
public void handleUserRegisteredEvent(UserRegisteredEvent event) {
try {
// ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ก์ง
} catch (Exception e) {
System.err.println("Error processing event for user: " + event.getUsername());
// ์๋ฌ ๋ก๊น
, ์๋ฆผ ๋ฐ์ก ๋ฑ์ ์ถ๊ฐ ์ฒ๋ฆฌ
}
}
๋๋ ์ ์ญ ์๋ฌ ํธ๋ค๋ฌ๋ฅผ ๋ง๋ค์ด ๋ชจ๋ ์ด๋ฒคํธ ์ฒ๋ฆฌ ์๋ฌ๋ฅผ ํ ๊ณณ์์ ๊ด๋ฆฌํ ์๋ ์์ด:
@Component
public class GlobalEventErrorHandler {
@EventListener
public void handleException(Exception exception) {
System.err.println("An error occurred during event processing: " + exception.getMessage());
// ์ถ๊ฐ์ ์ธ ์๋ฌ ์ฒ๋ฆฌ ๋ก์ง
}
}
์ด๋ ๊ฒ ํ๋ฉด ์๊ธฐ์น ๋ชปํ ์๋ฌ๋ก ์ธํด ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ค๋จ๋๋ ๊ฒ์ ๋ฐฉ์งํ ์ ์์ด. ์ฌ๋ฅ๋ท ๊ฐ์ ๋ณต์กํ ์์คํ ์์๋ ์ด๋ฐ ์๋ฌ ์ฒ๋ฆฌ๊ฐ ์ ๋ง ์ค์ํ์ง!
@EventListener(condition = "#event.user.isVIP()")
@Order(Ordered.HIGHEST_PRECEDENCE)
public void handleVIPTalentRegistration(TalentRegisteredEvent event) {
// VIP ์ฌ์ฉ์์ ์ฌ๋ฅ ๋ฑ๋ก ์ฒ๋ฆฌ
}
์, ์ด์ ์ฐ๋ฆฌ๋ Spring Events์ ๊ณ ๊ธ ๊ธฐ๋ฅ๋ค์ ๋ํด ์์๋ดค์ด. ๐ ์ด ๊ธฐ๋ฅ๋ค์ ์ ํ์ฉํ๋ฉด ๋์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋์ฑ ์ ์ฐํ๊ณ , ํ์ฅ ๊ฐ๋ฅํ๋ฉฐ, ๊ด๋ฆฌํ๊ธฐ ์ฌ์์ง ๊ฑฐ์ผ!
๋ค์ ์น์ ์์๋ ์ด ๋ชจ๋ ๊ฐ๋ ๋ค์ ์ข ํฉํด์ ์ค์ ํ๋ก์ ํธ์ ์ด๋ป๊ฒ ์ ์ฉํ ์ ์๋์ง ์์๋ณผ ๊ฑฐ์ผ. ๊ธฐ๋๋์ง ์์? ๊ณ์ ๋ฐ๋ผ์! ๐
๐ ๏ธ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๊ธฐ
์๋ , ์น๊ตฌ๋ค! ๋๋์ด ์ฐ๋ฆฌ๊ฐ ๋ฐฐ์ด ๋ชจ๋ ๊ฒ์ ์ข ํฉํด์ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํด๋ณผ ์๊ฐ์ด ์์ด. ๐ ์ฐ๋ฆฌ๊ฐ ์ง๊ธ๊น์ง ๋ฐฐ์ด Spring Events์ ๋ชจ๋ ๊ฐ๋ ์ ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์ ์ด๋ป๊ฒ ์ ์ฉํ ์ ์๋์ง ์ดํด๋ณด์!
1. ํ๋ก์ ํธ ๊ตฌ์กฐ ์ค๊ณ
๋จผ์ , ์ด๋ฒคํธ ๊ธฐ๋ฐ์ ์ํคํ ์ฒ๋ฅผ ์ ์ฉํ ํ๋ก์ ํธ ๊ตฌ์กฐ๋ฅผ ์ค๊ณํด๋ณผ๊ฒ:
com.talentnet
โโโ config
โ โโโ AsyncConfig.java
โโโ event
โ โโโ TalentRegisteredEvent.java
โ โโโ UserRegisteredEvent.java
โ โโโ TransactionCompletedEvent.java
โโโ listener
โ โโโ TalentEventListener.java
โ โโโ UserEventListener.java
โ โโโ TransactionEventListener.java
โโโ service
โ โโโ TalentService.java
โ โโโ UserService.java
โ โโโ TransactionService.java
โโโ controller
โโโ TalentController.java
โโโ UserController.java
โโโ TransactionController.java
์ด๋ฐ ๊ตฌ์กฐ๋ก ์ค๊ณํ๋ฉด ๊ฐ ๋๋ฉ์ธ(์ฌ๋ฅ, ์ฌ์ฉ์, ๊ฑฐ๋)๋ณ๋ก ์ด๋ฒคํธ์ ๋ฆฌ์ค๋๋ฅผ ๊น๋ํ๊ฒ ๋ถ๋ฆฌํ ์ ์์ด.
2. ์ด๋ฒคํธ ์ ์
๊ฐ ๋๋ฉ์ธ์ ๋ํ ์ด๋ฒคํธ๋ฅผ ์ ์ํด๋ณด์:
public class TalentRegisteredEvent extends ApplicationEvent {
private final String talentId;
private final String userId;
public TalentRegisteredEvent(Object source, String talentId, String userId) {
super(source);
this.talentId = talentId;
this.userId = userId;
}
// getters...
}
public class UserRegisteredEvent extends ApplicationEvent {
private final String userId;
private final String email;
public UserRegisteredEvent(Object source, String userId, String email) {
super(source);
this.userId = userId;
this.email = email;
}
// getters...
}
public class TransactionCompletedEvent extends ApplicationEvent {
private final String transactionId;
private final String buyerId;
private final String sellerId;
private final BigDecimal amount;
public TransactionCompletedEvent(Object source, String transactionId, String buyerId, String sellerId, BigDecimal amount) {
super(source);
this.transactionId = transactionId;
this.buyerId = buyerId;
this.sellerId = sellerId;
this.amount = amount;
}
// getters...
}
3. ์๋น์ค ๊ตฌํ
์ด์ ๊ฐ ์๋น์ค์์ ์ด๋ฒคํธ๋ฅผ ๋ฐํํ๋ ๋ก์ง์ ๊ตฌํํด๋ณด์:
@Service
public class TalentService {
private final ApplicationEventPublisher eventPublisher;
public TalentService(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
@Transactional
public void registerTalent(String talentId, String userId) {
// ์ฌ๋ฅ ๋ฑ๋ก ๋ก์ง...
// ์ด๋ฒคํธ ๋ฐํ
eventPublisher.publishEvent(new TalentRegisteredEvent(this, talentId, userId));
}
}
@Service
public class UserService {
private final ApplicationEventPublisher eventPublisher;
public UserService(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
@Transactional
public void registerUser(String userId, String email) {
// ์ฌ์ฉ์ ๋ฑ๋ก ๋ก์ง...
// ์ด๋ฒคํธ ๋ฐํ
eventPublisher.publishEvent(new UserRegisteredEvent(this, userId, email));
}
}
@Service
public class TransactionService {
private final ApplicationEventPublisher eventPublisher;
public TransactionService(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
@Transactional
public void completeTransaction(String transactionId, String buyerId, String sellerId, BigDecimal amount) {
// ๊ฑฐ๋ ์๋ฃ ๋ก์ง...
// ์ด๋ฒคํธ ๋ฐํ
eventPublisher.publishEvent(new TransactionCompletedEvent(this, transactionId, buyerId, sellerId, amount));
}
}
4. ๋ฆฌ์ค๋ ๊ตฌํ
์ด์ ๊ฐ ์ด๋ฒคํธ์ ๋ํ ๋ฆฌ์ค๋๋ฅผ ๊ตฌํํด๋ณด์:
@Component
public class TalentEventListener {
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleTalentRegistered(TalentRegisteredEvent event) {
System.out.println("์๋ก์ด ์ฌ๋ฅ์ด ๋ฑ๋ก๋์์ต๋๋ค: " + event.getTalentId());
// ์ถ์ฒ ์๊ณ ๋ฆฌ์ฆ ์
๋ฐ์ดํธ
// ๊ด์ฌ ์๋ ์ฌ์ฉ์์๊ฒ ์๋ฆผ ๋ฐ์ก
}
}
@Component
public class UserEventListener {
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleUserRegistered(UserRegisteredEvent event) {
System.out.println("์๋ก์ด ์ฌ์ฉ์๊ฐ ๋ฑ๋ก๋์์ต๋๋ค: " + event.getUserId());
// ํ์ ์ด๋ฉ์ผ ๋ฐ์ก
// ์ด๊ธฐ ์ถ์ฒ ์ฌ๋ฅ ๋ชฉ๋ก ์์ฑ
}
}
@Component
public class TransactionEventListener {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleTransactionCompleted(TransactionCompletedEvent event) {
System.out.println("๊ฑฐ๋๊ฐ ์๋ฃ๋์์ต๋๋ค: " + event.getTransactionId());
// ํ๋งค์์๊ฒ ์ ์ฐ ์ฒ๋ฆฌ
// ๊ตฌ๋งค์์๊ฒ ๋ฆฌ๋ทฐ ์์ฒญ ๋ฐ์ก
// ๊ฑฐ๋ ํต๊ณ ์
๋ฐ์ดํธ
}
}
5. ๋น๋๊ธฐ ์ค์
๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์ํ ์ค์ ์ ์ถ๊ฐํด๋ณด์:
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("TalentNet-");
executor.initialize();
return executor;
}
}
6. ์ปจํธ๋กค๋ฌ ๊ตฌํ
๋ง์ง๋ง์ผ๋ก, ์ด ๋ชจ๋ ๊ฒ์ ์ฌ์ฉํ๋ ์ปจํธ๋กค๋ฌ๋ฅผ ๊ตฌํํด๋ณด์:
@RestController
@RequestMapping("/talents")
public class TalentController {
private final TalentService talentService;
public TalentController(TalentService talentService) {
this.talentService = talentService;
}
@PostMapping
public ResponseEntity> registerTalent(@RequestBody TalentRegistrationRequest request) {
talentService.registerTalent(request.getTalentId(), request.getUserId());
return ResponseEntity.ok().build();
}
}
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@PostMapping
public ResponseEntity> registerUser(@RequestBody UserRegistrationRequest request) {
userService.registerUser(request.getUserId(), request.getEmail());
return ResponseEntity.ok().build();
}
}
@RestController
@RequestMapping("/transactions")
public class TransactionController {
private final TransactionService transactionService;
public TransactionController(TransactionService transactionService) {
this.transactionService = transactionService;
}
@PostMapping("/complete")
public ResponseEntity> completeTransaction(@RequestBody TransactionCompletionRequest request) {
transactionService.completeTransaction(request.getTransactionId(), request.getBuyerId(), request.getSellerId(), request.getAmount());
return ResponseEntity.ok().build();
}
}
- ๊ฐ ๋๋ฉ์ธ(์ฌ๋ฅ, ์ฌ์ฉ์, ๊ฑฐ๋)์ด ๋์จํ๊ฒ ๊ฒฐํฉ๋์ด ์์ด ์ ์ง๋ณด์๊ฐ ์ฌ์.
- ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ํตํด ์ฑ๋ฅ์ด ํฅ์๋๊ณ ํ์ฅ์ฑ์ด ์ข์์ ธ.
- ํธ๋์ญ์ ๋ฐ์ด๋ ์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํด ๋ฐ์ดํฐ ์ผ๊ด์ฑ์ ์ ์งํ ์ ์์ด.
- ์๋ก์ด ๊ธฐ๋ฅ ์ถ๊ฐ๊ฐ ์ฌ์. ์๋ฅผ ๋ค์ด, ์๋ก์ด ์๋ฆผ ์์คํ ์ ์ถ๊ฐํ๊ณ ์ถ๋ค๋ฉด ์๋ก์ด ๋ฆฌ์ค๋๋ง ๋ง๋ค๋ฉด ๋ผ!
์ด ๊ตฌ์กฐ๋ฅผ ๋ฐํ์ผ๋ก ์ฌ๋ฅ๋ท ๊ฐ์ ๋ณต์กํ ํ๋ซํผ์ ํจ๊ณผ์ ์ผ๋ก ๊ตฌํํ ์ ์์ด. ์๋ฅผ ๋ค์ด:
- ์๋ก์ด ์ฌ๋ฅ์ด ๋ฑ๋ก๋๋ฉด, ๊ด์ฌ ์๋ ์ฌ์ฉ์์๊ฒ ์๋์ผ๋ก ์๋ฆผ์ ๋ณด๋ผ ์ ์์ด.
- ์ฌ์ฉ์๊ฐ ๊ฐ์ ํ๋ฉด, ํ์ ์ด๋ฉ์ผ์ ๋ณด๋ด๊ณ ์ด๊ธฐ ์ถ์ฒ ๋ชฉ๋ก์ ์์ฑํ ์ ์์ด.
- ๊ฑฐ๋๊ฐ ์๋ฃ๋๋ฉด, ํ๋งค์์๊ฒ ์ ์ฐ์ ์ฒ๋ฆฌํ๊ณ , ๊ตฌ๋งค์์๊ฒ ๋ฆฌ๋ทฐ ์์ฒญ์ ๋ณด๋ผ ์ ์์ด.
- ๋ชจ๋ ์ด๋ฒคํธ๋ฅผ ๋ก๊น ํด์ ํ๋ซํผ ์ฌ์ฉ ํต๊ณ๋ฅผ ์ค์๊ฐ์ผ๋ก ์ ๋ฐ์ดํธํ ์ ์์ด.
์ด๋ ๊ฒ Spring Events๋ฅผ ํ์ฉํ๋ฉด, ๋ณต์กํ ๋น์ฆ๋์ค ๋ก์ง์ ๊น๋ํ๊ฒ ๋ถ๋ฆฌํ๊ณ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ด. ๋์ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ ์ ๋ ๊ฐ๋ ฅํด์ง๋ ๊ฑธ ๋๋ ์ ์์ ๊ฑฐ์ผ! ๐
์, ์ด์ ์ฐ๋ฆฌ๋ Spring Events์ ๋ชจ๋ ๊ฒ์ ๋ฐฐ์ฐ๊ณ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํด๋ดค์ด. ์ด ์ง์์ ๋ฐํ์ผ๋ก ๋๋ง์ ๋ฉ์ง ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค์ด๋ณด๋ ๊ฑด ์ด๋? ํ์ดํ ! ๐
- ์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์ฌ๋ฅ๋ท์ ์ต์ AI ๊ธฐ์ ๊ณผ ๋ฒ๋ฅ ์ ๊ธฐ๋ฐํ์ฌ ์์ฌ์ ์ง์ ์ฌ์ฐ๊ถ์ ์ ๊ทน์ ์ผ๋ก ๋ณดํธํ๋ฉฐ,
๋ฌด๋จ ์ฌ์ฉ ๋ฐ ์นจํด ํ์์ ๋ํด ๋ฒ์ ๋์์ ํ ๊ถ๋ฆฌ๋ฅผ ๋ณด์ ํฉ๋๋ค.
ยฉ 2025 ์ฌ๋ฅ๋ท | All rights reserved.
๋๊ธ 0๊ฐ