ํฐ์คํ ๋ฆฌ ๋ทฐ
๐ฅ AOP๋ฅผ ํ์ฉํด ์ค๋ณต์ฝ๋๋ฅผ ์ ๊ฑฐํด๋ณด์! (feat. AOP๋?๐ค)
junojuno 2024. 2. 8. 01:49๐ Overview
๋๋ ํฌ๋ฏผ(ํฌ์ฅ์ ๋ฏผ์กฑ) ํ๋ก์ ํธ์์ ์ฌ์ฅ๋ ์๋ฒ์์ ๊ฐ๊ฒ ๋๋ฉ์ธ๊ณผ ๋ฉ๋ด ๋๋ฉ์ธ์ ๋งก์๋ค.
ํฌ์ฅ์ ๋ฏผ์กฑ ํ๋ก์ ํธ์์์ ํน์ด์ ์ด๋ผ๊ณ ํ๋ค๋ฉด, ๊ณ ๊ฐํ๊ณผ ์ฌ์ฅํ์ด ๊ฐ๊ฐ ์๋ฒ๋ฅผ ๋๋๊ณ , ์๋ฒ๋ฅผ ๋๋ด์ผ๋ DB ๋ํ ๊ฐ๊ฐ ๋๋ ์งํ์ ํ๋ค.
๊ทธ๋ฌ๊ธฐ ๋๋ฌธ์, ํด๋น ์ฌ์ฅ ์๋ฒ์ DB์ ๊ณ ๊ฐ ์๋ฒ์ DB์ ์ ํฉ์ฑ์ ๋ง์ถ๋ ๊ฒ์ด ํต์ฌ์ด์๊ณ ํญ์ ์ฌ์ฅ ์๋ฒ์์ ์์์ด ์์ฑ๋๊ฑฐ๋ ๋ณ๊ฒฝ๋ ๋ ๋ง๋ค ๊ณ ๊ฐํ ์๋ฒ๋ก ์ ๋ณด๋ฅผ ์ ์กํด ์ฃผ์ด์ผ ํ๋ค.
๋ฐ๋ผ์ ์๋์ ๊ฐ์ด InfoSender ๋น์ ํตํด์ RestTemplate์ ํตํด ์ ๋ณด๋ฅผ ๊ณ ๊ฐํ ์๋ฒ๋ก ์ ๋ฌํ๋ค.
@Component
@RequiredArgsConstructor
public class InfoSender {
private final RestTemplate restTemplate;
private static final String URL_PREFIX = "http://52.78.144.166:8080/api/v1"; // ๊ณ ๊ฐํ url
public void send(Store store) {
StoreInfo body = StoreInfo.from(store);
HttpEntity<StoreInfo> storeInfoHttpEntity = new HttpEntity<>(body);
ResponseEntity<String> response = restTemplate.exchange(URL_PREFIX + "/stores", POST, storeInfoHttpEntity, String.class);
validateResponse(response);
}
public void send(Menu menu) {
MenuInfo body = MenuInfo.from(menu);
HttpEntity<MenuInfo> menuInfoHttpEntity = new HttpEntity<>(body);
ResponseEntity<String> response = restTemplate.exchange(URL_PREFIX + "/menus", POST, menuInfoHttpEntity, String.class);
validateResponse(response);
}
public void send(Menu menu, OptionGroup optionGroup) {
OptionGroupInfoToSend body = OptionGroupInfoToSend.from(optionGroup, menu);
HttpEntity<OptionGroupInfoToSend> optionGroupInfoToSendHttpEntity = new HttpEntity<>(body);
ResponseEntity<String> response = restTemplate.exchange(URL_PREFIX + "/options", POST, optionGroupInfoToSendHttpEntity, String.class);
validateResponse(response);
}
๊ทธ๋ฆฌ๊ณ ํด๋น ๋น์ ์ฃผ์ ๋ฐ์์ Service ๋ฉ์๋ ๋ง๋ค ๋ณ๊ฒฝ์ด ์ผ์ด๋ ๋๋ง๋ค ํด๋น ์ํฐํฐ ์ ๋ณด๋ฅผ ๊ณ ๊ฐํ ์๋ฒ๋ก ๋ณด๋ด ์ฃผ์ด์ผ ํ๋๋ฐ,
๊ทธ๋ ๋ค ๋ณด๋ Cross Cutting Concern, ํก๋จ ๊ด์ฌ์ฌ๊ฐ ๋ค์๊ณผ ๊ฐ์ด ๋ฐ์ํ๊ฒ ๋์๋ค.
์ด๋ ๊ฒ ๋๋ฉด ๋ฌธ์ ์ ์ด
1. InfoSender ๋น์ ์์กดํ๊ณ ์์ด์ผ ํ๊ธฐ ๋๋ฌธ์ InfoSender์ StoreService ๊ฐ์ ๊ฐํ ๊ฒฐํฉ์ด ๋ฐ์ํ๋ค.
2. ๋ฉ์๋๊ฐ ๋ ๊ฐ์ง ์ผ์ ํ๊ณ ์๊ณ , ์ฝ๋์ ์ค๋ณต์ด ๋ฐ์ํ๋ค.
์ด๋ฅผ Spring์์ ์ ๊ณตํ๋ Spring AOP๋ฅผ ์ฌ์ฉํด ํด๊ฒฐ ํ ์ ์์๋ค.
๐ฅ AOP๋?
AOP(Aspect Oriented Programming)์ด๋, ๊ด์ ์งํฅ ํ๋ก๊ทธ๋๋ฐ์ด๋ค.
Aspect๋ ๋ฌด์์ผ๊น?
๊ทธ๊ฒ ์ ์ AOP๋ฅผ ํตํด์ ํก๋จ ๊ด์ฌ์ฌ๋ฅผ ์ฒ๋ฆฌํ ์ ์๋ค๊ณ ํ๋๋ฐ ์ด๋ป๊ฒ ๊ทธ๊ฒ ๊ฐ๋ฅํ ๊ฒ์ผ๊น?
์ ํ๋ฆฌ์ผ์ด์ ์ ํฌ๊ฒ ํต์ฌ ๊ธฐ๋ฅ๊ณผ ๋ถ๊ฐ ๊ธฐ๋ฅ์ผ๋ก ๋๋๋ค.
ํต์ฌ๊ธฐ๋ฅ์ ํด๋น ๊ฐ์ฒด๊ฐ ์ ๊ณตํ๋ ๊ณ ์ ์ ๊ธฐ๋ฅ์ ๋งํ๋ค. ์๋ฅผ ๋ค์ด ์์ StoreService์์ ํต์ฌ ๊ธฐ๋ฅ์ Store์ ๋ํ ์ ๋ณด๋ฅผ ์ ์ฅํ๊ณ , ๋ณ๊ฒฝํ๋ ๊ฒ์ด๋ค.
๋ถ๊ฐ ๊ธฐ๋ฅ์ ํต์ฌ ๊ธฐ๋ฅ์ ๋ณด์กฐํ๊ธฐ ์ํด ์ ๊ณต๋๋ ๊ธฐ๋ฅ์ผ๋ก ๋ก๊ทธ ์ถ์ , ํธ๋์ญ์ ์ ์๋ก ๋ค ์ ์๋๋ฐ, ์์์ ํด๋น ๋ณ๊ฒฝ๋ ์ํฐํฐ ์ ๋ณด๋ฅผ ๊ณ ๊ฐ ์๋ฒ์ ๋ณด๋ด๋ ๊ฒ์ ๋ถ๊ฐ ๊ธฐ๋ฅ์ด๋ผ๊ณ ๋ณผ ์ ์๋ค.
์ด๋ ๊ฒ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ๋ชจ๋ํ ํด์ ์ด๋์ ์ ์ฉํ ์ง๋ฅผ ์ ํํ๋ ๊ธฐ๋ฅ์ ํฉํด ํ๋์ ๋ชจ๋๋ก ๋ง๋ ๊ฒ์ Aspect๋ผ๊ณ ํ๋ค.
์ด๋ ๊ฒ ๋ถ๊ฐ๊ธฐ๋ฅ๊ณผ ํต์ฌ ๊ธฐ๋ฅ์ ๋ถ๋ฆฌํจ์ผ๋ก์ AOP๋ฅผ ์ฌ์ฉํ๋ฉด ์ ๋ง ๋ฉ์๋๊ฐ ํ๋์ ์ผ๋ง ํ ์ ์๋๋ก, ๋ ๊ฐ์ฒด ์งํฅ์ ์ผ๋ก ์ฝ๋ฉ์ด ๊ฐ๋ฅํด์ง๋ค.
๐ฅ Spring์์๋ AOP๋ฅผ ์ด๋ป๊ฒ ๊ตฌํํ๋๊ฐ?
ํก๋จ๊ด์ฌ์ฌ, cross cutting concern์ ์ฒ๋ฆฌํ๊ธฐ ์ํด์ AOP๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์๋ ์ธ ๊ฐ์ง๊ฐ ์๋ค.
1. ์ปดํ์ผ ์์ ์ .class ํ์ผ๋ก ๋ณํ ๋ ๋ ๋ฐ์ดํธ์ฝ๋๋ฅผ ๋ง๋ถํ AOP๋ฅผ ์ ์ฉํ ์ ์๋ค.
2. ํด๋์ค ๋ก๋์ ๋ก๋ ๋ ๋ ์ฝ๋๋ฅผ ๋ง๋ถํ AOP๋ฅผ ์ ์ฉํ ์ ์๋ค.
3. ๋ฐํ์ ์์ ์ ์์ด์ AOP๋ฅผ ์ ์ฉํ ์ ์๋๋ฐ, ํ๋ก์๋ฅผ ์ฌ์ฉํด AOP๋ฅผ ์ ์ฉํ ์ ์๋ค.
์ฒซ๋ฒ์งธ, ๋ ๋ฒ์งธ ๋ฐฉ์, ์ฆ ์ปดํ์ผ ์์ ๊ณผ ํด๋์ค ๋ก๋์ ๋ก๋ ๋๋ ์์ ์ AOP๋ฅผ ์ ์ฉํ๋ ๋ฐฉ์์ AOP์ ๋ํ ๊ฑฐ์ ๋ชจ๋ ๊ธฐ๋ฅ์ ์ง์ํ๋ AspectJ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด ๊ตฌํํ ์ ์์ง๋ง, ์๋นํ ํ์ต๋๋ ๋ง๊ณ ๋ณต์กํ๋ค๊ณ ํ๋ค.
๊ทธ๋์ Spring์์๋ AOP๋ฅผ ํ๋ก์๋ฅผ ์ฌ์ฉํด ๋ฐํ์์ ์ ์ฉํ๋ ๋ฐฉ์์ ํํ๋ค.
๊ทธ๋ผ ์คํ๋ง์์๋ AOP๋ฅผ ์ ์ฉํ ๋์์ ํ๋ก์๋ฅผ ํตํด ์ด๋ป๊ฒ ๊ตฌํํ ์ ์์๊น?
์ด๊ฒ์ ๋ํด์ ์ค๋ช ํ๊ธฐ์ํด ๋จผ์ ์ฉ์ด์ ๋ฆฌ๋ฅผ ํ์๋ฉด, AOP๋ฅผ ์ ์ฉํ ๋์์ธ Joinpoint, ์ ์ฉํ ๋ถ๊ฐ ๋ก์ง์ธ Advice, ์ ์ฉ ์์น๋ฅผ ์ค์ ํ๋ Pointcut, ํ๋์ Advice์ ํ๋์ Pointcut์ ํฉ์น Advisor๊ฐ ์๋ค.
Pointcut์ ํด๋นํ๋ ๋ฉ์๋๊ฐ ์๋ ํด๋์ค๋ฅผ ๋น ๋ฑ๋ก ์ CGLIB ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด ๋์ ๋น์ ํ๋ก์๋ก ๋ฑ๋กํด Advice๋ฅผ ์ ์ฉํ๋ ๋ฐฉ์์ผ๋ก ํ๋ก์ ํจํด์ ํตํด AOP๋ฅผ ๋ฐํ์์ ์คํํ ์ ์๋ค.
๊ทธ๋ผ PointCut์ผ๋ก ์ค์ ๋ ๋์์ Proxy์ ์ฉ์ ์ด๋ป๊ฒ ํ๋๋?
๋น ํ ์ฒ๋ฆฌ๊ธฐ(Bean Post Processor)๋ฅผ ํตํด ๋ค๋ฅธ ์ค์ ์์ด Bean์ผ๋ก ๋ฑ๋ก ๋ ๊ฒ์ ํ๋ก์ ๊ฐ์ฒด๋ก ๋ฐ๊ฟ ์ ์๋๋ฐ, @Aspect ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํด Pointcut๊ณผ Advice๋ฅผ ์ค์ ํ๋ฉด, AnnotationAwareAspectJAutoProxyCreator๊ฐ ์๋์ผ๋ก ํ๋ก์๋ฅผ ๋น์ผ๋ก ๋ฑ๋ก ์์ผ ์ค๋ค.
์ฌ๋ฌ ๋ถ๊ฐ๊ธฐ๋ฅ๊ณผ ์ ์ฉ์์น Pointcut์ ํฉ์น Aspect๋ฅผ ์ค์ ํ๋ฉด, ์๋์ผ๋ก ํ๋ก์๋ฅผ ๋ง๋ค์ด AOP๋ฅผ ๋ฐํ์์ ์ค์ ํด ์ฃผ๋ ๊ฒ์ด Spring์์ ์ ๊ณตํ๋ AOP ๊ธฐ๋ฅ์ด๋ค.
๐ฅ AOP๋ฅผ ์ ์ฉํด๋ณด์!
๋จผ์ , ์ฒ์ ์ StoreService์์ InfoSender๋ฅผ ๋ถ๋ฆฌํด ๋ณด์.
๊ทธ๋ฌ๊ธฐ ์ํด์ Aspect๋ฅผ ์ค์ ํด ์ฃผ์ด์ผ ํ๋๋ฐ, Aspect์ Pointcut ์ค์ ํ๋๋ฐ ์์ด ์๋์ ๊ฐ์ด @TransmitData ์ด๋ ธํ ์ด์ ์ ์์ฑํด ์ด๋ ธํ ์ด์ ๋์์ผ๋ก Pointcut์ ์ค์ ํด ์ฃผ์๋ค.
๊ทธ ์ด์ ๋ StoreService, MenuService์ AOP๋ฅผ ์ค์ ํ๊ณ ์ถ์๋ฐ, ๋ชจ๋ ํด๋์ค์ ๋ฉ์๋์ ์ ์ฉํ๋๊ฒ ์๋๋ผ ์ด๋ค ๋ฉ์๋๋ ๊ณ ๊ฐํ์ ์ ์ก๊ธฐ๋ฅ์ด ํ์ํ๊ณ , ์ด๋ค ๋ฉ์๋๋ ํ์ ์์๊ธฐ ๋๋ฌธ์ ์ฌ์ฉํ๊ธฐ ๊ฐํธํ ์ด๋ ธํ ์ด์ ๊ธฐ๋ฐ์ผ๋ก Pointcut์ ์ค์ ํ๋ค.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TransmitData {
}
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class DataTransferAspect {
private final InfoSender infoSender;
@Pointcut("@annotation(com.ray.pominowner.store.service.aop.TransmitData)")
private void toTransmitDataAnnotation() {}
@AfterReturning(pointcut = "toTransmitDataAnnotation()", returning = "store")
public void transmitData(Store store) {
infoSender.send(store);
log.info("store info sent");
}
@AfterReturning(pointcut = "toTransmitDataAnnotation()", returning = "menu")
public void transmitData(Menu menu) {
infoSender.send(menu);
log.info("menu info sent");
}
@AfterReturning(pointcut = "toTransmitDataAnnotation()", returning = "optionInfo")
public void transmitData(OptionGroupInfoToSend optionInfo) {
infoSender.send(optionInfo);
log.info("option info sent");
}
}
โ ์ @Around ๋์ @AfterReturning์ ์ฌ์ฉํ๋๊ฐ?
๋ ์์ ๊ฐ์ด @Around ๋์ @AfterReturning์ ์ฌ์ฉํ ์ด์ ๋, ProceedingJoinPoint์ proceed() ๋ฉ์๋ ํธ์ถ์ ํด์ผํ ํ์์ฑ์ ๋ชป๋๊ผ๋๊ฒ์ด ๊ฐ์ฅ ์ปธ๊ณ , @AfterReturning์ ์ฌ์ฉํ๋๊ฒ์ด target๋ฉ์๋์ ๋ฉ์๋๊ฐ return๋๊ณ ๋์ํ๋ ๊ธฐ๋ฅ์ด๋ผ๋ ๊ฒ์ด ๋ช ํํ๊ฒ ๋ณด์๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋ฆฌ๊ณ return๊ฐ์ ์ฌ์ฉํด ๋ฐ๋ก ๊ณ ๊ฐํ ์๋ฒ์ ๋ณด๋ด๋ฉด ๋์๊ธฐ ๋๋ฌธ์, @AfterReturning์ ์ฌ์ฉํ๋ค.
โ ์ ํ๋์ advice ๋ฉ์๋์์ ์ฒ๋ฆฌํ ๊ฒ ์๋๋ผ ๋ฉ์๋๋ฅผ ๊ฐ๊ฐ ๋ง๋ค์๋๊ฐ?
returning์์ ์ค์ ํ ๊ฐ๊ณผ ๋์ผํ ํ๋ผ๋ฏธํฐ๊ฐ ์์ ๋, Object๋ก ํ๋ผ๋ฏธํฐ๋ฅผ ๋ฐ์ instanceof ๋ฅผ ํตํด ํจํด ๋งค์นญ ํ ํด๋น ํ์ ์ ๋ง๋ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค๋ฉด ๋ฉ์๋ ์ธ ๊ฐ๊ฐ ์๋ ํ๋๋ก ๋๋๋ ๋ฌธ์ ์๋ค.
๊ทธ ์ด์ ๋, ๊ฐ๋ ์ฑ์ด ๋จ์ด์ง๊ณ ์ง์ ๋ถํด์ง๊ธฐ ๋๋ฌธ์ด๋ค.
์๋ฐ 21์ switch ๋ฌธ์ ํตํ ํจํด๋งค์นญ์ ์ฌ์ฉํ ์ ์๋ค๋ฉด ๋ฆฌํํ ๋ง์ ํ ์ ์๊ฒ ์ง๋ง, ์๋ฐ 17๋ฒ์ ์ ์ฌ์ฉํ๊ณ ์๊ธฐ๋ ํ๊ณ , @AfterReturning ์ฌ์ฉ์ return ๊ฐ์ ๋ฐ๋ ๊ฒ์ ์์ด์ ํ์ ์ด ๋ค๋ฅด๋ฉด AOP ์ ์ฉ์ด ๋ฌด์ ๋๊ธฐ ๋๋ฌธ์ ๊ฐ๊ฐ ๋ฉ์๋๋ฅผ ๋ง๋ค์ด ์ฃผ์ด ๊ฐ๋ ์ฑ์ ๋ํ๊ณ ์ญํ ์ ๋ถ๋ฆฌ ์ํฌ ์ ์์๋ค.
์์ ๊ฐ์ด Aspect๋ฅผ ์ค์ ํ๊ณ , ๋์์ด ๋๋ Service ์ฝ๋๋ฅผ ์๋์ ๊ฐ์ด ๋ฆฌํํ ๋ง ํด ์ฃผ์๋ค.
๋ฌผ๋ก Entity๋ฅผ ๋ฐํํด ์ฃผ์ด์ผ ํ๋ค๋ ์ ์ผ๋ก Controller์ ์ฝ๋๋ ์ฝ๊ฐ ์์ ๋๊ธด ํ์ง๋ง ํฐ ๋ฌธ์ ๊ฐ ๋์ง ์์๋ค.
์ด๋ ๊ฒ AOP๋ฅผ ํตํด ํต์ฌ ๋ก์ง๊ณผ ๊ณตํต ๋ก์ง์ ๋ถ๋ฆฌ ์ํด์ผ๋ก์ ๊ฐ์ฒด์งํฅ์ ์ด๊ณ ๊ฐ๋ ์ฑ ๋์ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์๋ค!
Reference
https://m.yes24.com/Goods/Detail/17350624
'Programming > Spring & Hibernate' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
SpringBoot๋ฅผ command line์ ํตํด์ ์คํํ๊ธฐ (0) | 2022.07.25 |
---|---|
Hibernate Advanced Mappings Overview (0) | 2021.03.04 |
Hibernate CRUD features: Create, Read, Update and Delete (0) | 2021.03.02 |
Hibernate Configuration with Annotations (0) | 2021.02.26 |
Hibernate Overview & Setting Up Development Environment (0) | 2021.02.26 |
- Total
- Today
- Yesterday