[Spring] AOP (Aspect Oriented Programming)
in SPRING
SpringSecurity - AOP (Aspect Oriented Programming)
AOP
AOP๋ Aspect Oriented Programming์ ์ฝ์๋ก ๊ด์ ์งํฅ ํ๋ก๊ทธ๋๋ฐ์ด๋ผ๊ณ ๋ถ๋ฆฐ๋ค. ๊ด์ ์งํฅ์ ์ฝ๊ฒ ๋งํด ์ด๋ค ๋ก์ง์ ๊ธฐ์ค์ผ๋ก ํต์ฌ์ ์ธ ๊ด์ , ๋ถ๊ฐ์ ์ธ ๊ด์ ์ผ๋ก ๋๋์ด์ ๋ณด๊ณ ๊ทธ ๊ด์ ์ ๊ธฐ์ค์ผ๋ก ๊ฐ๊ฐ ๋ชจ๋ํํ๊ฒ ๋ค๋ ๊ฒ์ด๋ค. ์ฌ๊ธฐ์ ๋ชจ๋ํ๋ ์ด๋ค ๊ณตํต๋ ๋ก์ง์ด๋ ๊ธฐ๋ฅ์ ํ๋์ ๋จ์๋ก ๋ฌถ๋ ๊ฒ์ ๋งํ๋ค.
Spring์ ํต์ฌ ๊ฐ๋ ์ค ํ๋์ธ DI๊ฐ ์ ํ๋ฆฌ์ผ์ด์ ๋ชจ๋๋ค ๊ฐ์ ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถฐ์ค๋ค๋ฉด, AOP๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฒด์ ๊ฑธ์ณ ์ฌ์ฉ๋๋ ๊ธฐ๋ฅ์ ์ฌ์ฌ์ฉํ๋๋ก ์ง์ํ๋ ๊ฒ์ด๋ค.
์๋ก๋ค์ด ํต์ฌ์ ์ธ ๊ด์ ์ ๊ฒฐ๊ตญ ์ฐ๋ฆฌ๊ฐ ์ ์ฉํ๊ณ ์ ํ๋ ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง์ด ๋๋ค. ๋ํ ๋ถ๊ฐ์ ์ธ ๊ด์ ์ ํต์ฌ ๋ก์ง์ ์คํํ๊ธฐ ์ํด์ ํํด์ง๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ, ๋ก๊น , ํ์ผ ์ ์ถ๋ ฅ ๋ฑ์ ์๋ก ๋ค ์ ์๋ค.
- OOP : ๋น์ง๋์ค ๋ก์ง์ ๋ชจ๋ํ
- ๋ชจ๋ํ์ ํต์ฌ ๋จ์๋ ๋น์ง๋์ค ๋ก์ง
- AOP : ์ธํ๋ผ ํน์ ๋ถ๊ฐ๊ธฐ๋ฅ์ ๋ชจ๋ํ
- ๋ํ์ ์ : ๋ก๊น , ํธ๋์ญ์ , ๋ณด์ ๋ฑ
- ๊ฐ๊ฐ์ ๋ชจ๋๋ค์ ์ฃผ ๋ชฉ์ ์ธ์ ํ์ํ ๋ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ๋ค
AOP์์ ๊ฐ ๊ด์ ์ ๊ธฐ์ค์ผ๋ก ๋ก์ง์ ๋ชจ๋ํํ๋ค๋ ๊ฒ์ ์ฝ๋๋ค์ ๋ถ๋ถ์ ์ผ๋ก ๋๋์ด์ ๋ชจ๋ํํ๊ฒ ๋ค๋ ์๋ฏธ๋ค. ์ด๋, ์์ค ์ฝ๋์์์ ๋ค๋ฅธ ๋ถ๋ถ์ ๊ณ์ ๋ฐ๋ณตํด์ ์ฐ๋ ์ฝ๋๋ค์ ๋ฐ๊ฒฌํ ์ ์๋ ๋ฐ ์ด๊ฒ์ ํฉ์ด์ง ๊ด์ฌ์ฌ (Crosscutting Concerns)๋ผ ๋ถ๋ฅธ๋ค.
์์ ๊ฐ์ด ํฉ์ด์ง ๊ด์ฌ์ฌ๋ฅผ Aspect๋ก ๋ชจ๋ํํ๊ณ ํต์ฌ์ ์ธ ๋น์ฆ๋์ค ๋ก์ง์์ ๋ถ๋ฆฌํ์ฌ ์ฌ์ฌ์ฉํ๊ฒ ๋ค๋ ๊ฒ์ด AOP์ ์ทจ์ง๋ค.
AOP ์ฃผ์ ๊ฐ๋
Aspect : ์์์ ์ค๋ช ํ ํฉ์ด์ง ๊ด์ฌ์ฌ๋ฅผ ๋ชจ๋ํ ํ ๊ฒ. ์ฃผ๋ก ๋ถ๊ฐ๊ธฐ๋ฅ ๋ชจ๋์ Aspect๋ผ๊ณ ๋ถ๋ฅด๋ฉฐ ํต์ฌ๊ธฐ๋ฅ์ ๋ถ๊ฐ๋์ด ์๋ฏธ๋ฅผ ๊ฐ๋ ํน๋ณํ ๋ชจ๋์ด๋ผ ์๊ฐํ๋ฉด ๋๋ค.
Target : Aspect๋ฅผ ์ ์ฉํ๋ ๊ณณ (ํด๋์ค, ๋ฉ์๋ .. )
Advice : ์ค์ง์ ์ผ๋ก ์ด๋ค ์ผ์ ํด์ผํ ์ง์ ๋ํ ๊ฒ, ์ค์ง์ ์ธ ๋ถ๊ฐ๊ธฐ๋ฅ์ ๋ด์ ๊ตฌํ์ฒด
Advice์ ๊ฒฝ์ฐ ํ๊ฒ ์คํ์ ํธ์ ์ข ์๋์ง ์๊ธฐ ๋๋ฌธ์ ์์ํ๊ฒ ๋ถ๊ฐ๊ธฐ๋ฅ์๋ง ์ง์คํ ์ ์๋ค.
Advice๋ Aspect๊ฐ โ๋ฌด์์โ, โ์ธ์ โ ํ ์ง๋ฅผ ์๋ฏธํ๊ณ ์๋ค.
JointPoint : Advice๊ฐ ์ ์ฉ๋ ์์น, ๋ผ์ด๋ค ์ ์๋ ์ง์ . ๋ฉ์๋ ์ง์ ์ง์ , ์์ฑ์ ํธ์ถ ์์ , ํ๋์์ ๊ฐ์ ๊บผ๋ด์ฌ ๋ ๋ฑ ๋ค์ํ ์์ ์ ์ ์ฉ๊ฐ๋ฅ , Spring์์๋ ๋ฉ์๋ ์กฐ์ธํฌ์ธํธ๋ง ์ ๊ณต
PointCut : JointPoint์ ์์ธํ ์คํ์ ์ ์ํ ๊ฒ. โA๋ ๋ฉ์๋์ ์ง์ ์์ ์ ํธ์ถํ ๊ฒโ๊ณผ ๊ฐ์ด ๋์ฑ ๊ตฌ์ฒด์ ์ผ๋ก Advice๊ฐ ์คํ๋ ์ง์ ์ ์ ํ ์ ์์ (๋ถ๊ฐ๊ธฐ๋ฅ์ด ์ ์ฉ๋ ๋์(๋ฉ์๋)๋ฅผ ์ ์ ํ๋ ๋ฐฉ๋ฒ์ ์๊ธฐํ๋ค.) ์ฆ, ์ด๋๋ฐ์ด์ค๋ฅผ ์ ์ฉํ ์กฐ์ธํฌ์ธํธ๋ฅผ ์ ๋ณํ๋ ๊ธฐ๋ฅ์ ์ ์ํ ๋ชจ๋์ ์๊ธฐํ๋ค.
(์ด๋๋ฐ์ด์ค์ value๋ก ๋ค์ด๊ฐ ๋ฌธ์์ด์ ํฌ์ธํธ์ปท ํํ์์ด๋ผ๊ณ ํ๋ค.)
- ํฌ์ธํธ์ปท ํํ์์ 2๊ฐ์ง๋ก ๋๋ ์ง๋๋ฐ,
execution
์ ์ง์ ์๋ผ๊ณ ๋ถ๋ฅธ๋ค. (* com.hojin2world..*.EventService.*(..)))
๋ ํ๊ฒ ๋ช ์ธ๋ผ๊ณ ํ๋ค.
- ํฌ์ธํธ์ปท ํํ์์ 2๊ฐ์ง๋ก ๋๋ ์ง๋๋ฐ,
Proxy : ํ๊ฒ์ ๊ฐ์ธ์ ํ๊ฒ์ ์์ฒญ์ ๋์ ๋ฐ์์ฃผ๋ ๋ฉํ(Wrapping) ์ค๋ธ์ ํธ์ด๋ค. ํธ์ถ์ (ํด๋ผ์ด์ธํธ)์์ ํ๊ฒ์ ํธ์ถํ๊ฒ ๋๋ฉด ํ๊ฒ์ด ์๋ ํ๊ฒ์ ๊ฐ์ธ๊ณ ์๋ Proxy๊ฐ ํธ์ถ๋์ด, ํ๊ฒ ๋ฉ์๋ ์คํ์ ์ ์ ์ฒ๋ฆฌ, ํ๊ฒ ๋ฉ์๋ ์คํ ํ, ํ์ฒ๋ฆฌ๋ฅผ ์คํ์ํค๋๋ก ๊ตฌ์ฑ๋์ด์๋ค.
(AOP์์ ํ๋ก์๋ ํธ์ถ์ ๊ฐ๋ก์ฑ ํ, ์ด๋๋ฐ์ด์ค์ ๋ฑ๋ก๋ ๊ธฐ๋ฅ์ ์ํ ํ ํ๊ฒ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.)
AOP ํน์ง
- ํ๋ก์ ํจํด ๊ธฐ๋ฐ์ AOP ๊ตฌํ์ฒด, ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ฐ๋ ์ด์ ๋ ์ ๊ทผ ์ ์ด ๋ฐ ๋ถ๊ฐ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ธฐ ์ํด์์
- ์คํ๋ง ๋น์๋ง AOP๋ฅผ ์ ์ฉ ๊ฐ๋ฅ
- ๋ชจ๋ AOP ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ๊ฒ์ด ์๋ ์คํ๋ง IoC์ ์ฐ๋ํ์ฌ ์ํฐํ๋ผ์ด์ฆ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๊ฐ์ฅ ํํ ๋ฌธ์ (์ค๋ณต์ฝ๋, ํ๋ก์ ํด๋์ค ์์ฑ์ ๋ฒ๊ฑฐ๋ก์, ๊ฐ์ฒด๋ค ๊ฐ ๊ด๊ณ ๋ณต์ก๋ ์ฆ๊ฐ โฆ)์ ๋ํ ํด๊ฒฐ์ฑ ์ ์ง์ํ๋ ๊ฒ์ด ๋ชฉ์
AOP์ ์ฅ์
- ์ดํ๋ฆฌ์ผ์ด์ ์ ์ฒด์ ํฉ์ด์ง ๊ณตํต ๊ธฐ๋ฅ์ด ํ๋์ ์ฅ์์์ ๊ด๋ฆฌ๋๋ค๋ ์
- ๋ค๋ฅธ ์๋น์ค ๋ชจ๋๋ค์ด ๋ณธ์ธ์ ๋ชฉ์ ์๋ง ์ถฉ์คํ๊ณ ๊ทธ์ธ ์ฌํญ๋ค์ ์ ๊ฒฝ์ฐ์ง ์์๋ ๋๋ค๋ ์
์คํ๋ง AOP : @AOP
์คํ๋ง @AOP๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ๋ค์๊ณผ ๊ฐ์ ์์กด์ฑ์ ์ถ๊ฐํด์ผ ํ๋ค.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
๋ค์์๋ ์๋์ ๊ฐ์ด @Aspect ์ด๋ ธํ ์ด์ ์ ๋ถ์ฌ ์ด ํด๋์ค๊ฐ Aspect๋ฅผ ๋ํ๋ด๋ ํด๋์ค๋ผ๋ ๊ฒ์ ๋ช ์ํ๊ณ @Component๋ฅผ ๋ถ์ฌ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํ๋ค.
@Component
@Aspect
public class PerfAspect {
// Around -> ์ด๋๋ฐ์ด์ค (Advice๋ Aspect๊ฐ "๋ฌด์์", "์ธ์ " ํ ์ง๋ฅผ ์๋ฏธํ๊ณ ์๋ค.)
// ์ฌ๊ธฐ์ "๋ฌด์"์ logPerf() ๋ฉ์๋๋ฅผ ๋ํ๋ธ๋ค.
/* ๊ทธ๋ฆฌ๊ณ "์ธ์ "๋ @Around๊ฐ ๋๋๋ฐ , ์ด ์ธ์ ๋ผ๋ ์์ ์ ๊ฒฝ์ฐ @Around๋ง ์กด์ฌํ์ง ์๊ณ ์ด 5๊ฐ์ง์
ํ์
์ด ์กด์ฌํ๋ค. @Before, @After ... */
// execution -> ํฌ์ธํธ์ปท ์ง์ ์
// *๋ฆฌํด ํ์
์ ๋ํ๋ธ๋ค. ํ์ฌ : ๋ชจ๋ ํ์
๋ฆฌํด ๊ฐ๋ฅ
// com.hojin2world..*.EventService.* ํ๊ฒ์ด ๋๋ ๋ฉ์๋ ์ง์
// (..) -> ์ธ์(argument)ํ์
ํ์ฌ : ๋ชจ๋ ํ์
์ธ์ ํ์ฉ
@Around("execution(* com.hojin2world..*.EventService.*(..))")
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable{
long begin = System.currentTimeMillis();
Object retVal = pjp.proceed(); // ๋ฉ์๋ ํธ์ถ ์์ฒด๋ฅผ ๊ฐ์
System.out.println(System.currentTimeMillis() - begin);
return retVal;
}
}
@Around ์ด๋ ธํ ์ด์ ์ ํ๊ฒ ๋ฉ์๋๋ฅผ ๊ฐ์ธ์ ํน์ Advice๋ฅผ ์คํํ๋ค๋ ์๋ฏธ์ด๋ค. ์ ์ฝ๋์ Advice๋ ํ๊ฒ ๋ฉ์๋๊ฐ ์คํ๋ ์๊ฐ์ ์ธก์ ํ๊ธฐ ์ํ ๋ก์ง์ ๊ตฌํํ์๋ค. ์ถ๊ฐ์ ์ผ๋ก execution(* com.hojin2world..*.EventService.*(..))๊ฐ ์๋ฏธํ๋ ๋ฐ๋ com.hojin2world ์๋์ ํจํค์ง ๊ฒฝ๋ก์ EventService ๊ฐ์ฒด์ ๋ชจ๋ ๋ฉ์๋์ ์ด Aspect๋ฅผ ์ ์ฉํ๊ฒ ๋ค๋ ์๋ฏธ๋ค.
public interface EventService {
void createEvent();
void publishEvent();
void deleteEvent();
}
@Component
public class SimpleEventService implements EventService {
@Override
public void createEvent() {
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("Created an event");
}
@Override
public void publishEvent() {
try {
Thread.sleep(1000);
} catch (InterruptedException e){
e.printStackTrace();;
}
System.out.println("Published an event");
}
public void deleteEvent() {
System.out.println("Delete an event");
}
}
@Service
public class AppRunner implements ApplicationRunner {
@Autowired
EventService eventService;
@Override
public void run(ApplicationArguments args) throws Exception {
eventService.createEvent();
eventService.publishEvent();
eventService.deleteEvent();
}
}
Created an event
1003
Published an event
1000
Delete an event
0
๋ํ ๊ฒฝ๋ก์ง์ ๋ฐฉ์๋ง๊ณ ํน์ ์ด๋ ธํ ์ด์ ์ด ๋ถ์ ํฌ์ธํธ์ ํด๋น Aspect๋ฅผ ์คํํ ์ ์๋ ๊ธฐ๋ฅ๋ ์ ๊ณตํ๋ค.
@Component
@Aspect
public class PerfAspect {
@Around("@annotation(PerLogging)")
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable{
long begin = System.currentTimeMillis();
Object retVal = pjp.proceed(); // ๋ฉ์๋ ํธ์ถ ์์ฒด๋ฅผ ๊ฐ์
System.out.println(System.currentTimeMillis() - begin);
return retVal;
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface PerLogging {
}
@Component
public class SimpleEventService implements EventService {
@PerLogging
@Override
public void createEvent() {
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("Created an event");
}
@Override
public void publishEvent() {
try {
Thread.sleep(1000);
} catch (InterruptedException e){
e.printStackTrace();;
}
System.out.println("Published an event");
}
@PerLogging
@Override
public void deleteEvent() {
System.out.println("Delete an event");
}
}
Created an event
1003
Published an event
Delete an event
0
์ ์ถ๋ ฅ ๊ฒฐ๊ณผ์์ @PerLogging ์ด๋ ธํ ์ด์ ์ด ๋ถ์ ๋ฉ์๋๋ง Aspect๊ฐ ์ ์ฉ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก ์คํ๋ง ๋น์ ๋ชจ๋ ๋ฉ์๋์ ์ ์ฉํ ์ ์๋ ๊ธฐ๋ฅ๋ ์ ๊ณตํ๋ค.
@Component
@Aspect
public class PerfAspect {
@Around("bean(simpleEventService)")
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable{
long begin = System.currentTimeMillis();
Object retVal = pjp.proceed(); // ๋ฉ์๋ ํธ์ถ ์์ฒด๋ฅผ ๊ฐ์
System.out.println(System.currentTimeMillis() - begin);
return retVal;
}
}
@Component
public class SimpleEventService implements EventService {
@Override
public void createEvent() {
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("Created an event");
}
@Override
public void publishEvent() {
try {
Thread.sleep(1000);
} catch (InterruptedException e){
e.printStackTrace();;
}
System.out.println("Published an event");
}
@Override
public void deleteEvent() {
System.out.println("Delete an event");
}
}
@Service
public class AppRunner implements ApplicationRunner {
@Autowired
EventService eventService;
@Override
public void run(ApplicationArguments args) throws Exception {
eventService.createEvent();
eventService.publishEvent();
eventService.deleteEvent();
}
}
Created an event
1002
Published an event
1001
Delete an event
0
์ ์ถ๋ ฅ๊ฒฐ๊ณผ๋ก SimpleEventService์ ๋ชจ๋ ๋ฉ์๋์ ํด๋น Aspect๊ฐ ์ถ๊ฐ๋ ๊ฒ์ ์ ์ ์๋ค.
์ด ๋ฐ์๋ @Around ์ธ์ ํ๊ฒ ๋ฉ์๋์ Aspect ์คํ ์์ ์ ์ง์ ํ ์ ์๋ ์ด๋ ธํ ์ด์ ์ด ์๋ค.
@Before (์ด์ ) : ์ด๋๋ฐ์ด์ค ํ๊ฒ ๋ฉ์๋๊ฐ ํธ์ถ๋๊ธฐ ์ ์ ์ด๋๋ฐ์ด์ค ๊ธฐ๋ฅ์ ์ํ
- @After (์ดํ) : ํ๊ฒ ๋ฉ์๋์ ๊ฒฐ๊ณผ์ ๊ด๊ณ์์ด(์ฆ ์ฑ๊ณต, ์์ธ ๊ด๊ณ์์ด) ํ๊ฒ ๋ฉ์๋๊ฐ ์๋ฃ ๋๋ฉด ์ด๋๋ฐ์ด์ค ๊ธฐ๋ฅ์ ์ํ
- @AfterReturning (์ ์์ ๋ฐํ ์ดํ)ํ๊ฒ ๋ฉ์๋๊ฐ ์ฑ๊ณต์ ์ผ๋ก ๊ฒฐ๊ณผ๊ฐ์ ๋ฐํ ํ์ ์ด๋๋ฐ์ด์ค ๊ธฐ๋ฅ์ ์ํ
- @AfterThrowing (์์ธ ๋ฐ์ ์ดํ) : ํ๊ฒ ๋ฉ์๋๊ฐ ์ํ ์ค ์์ธ๋ฅผ ๋์ง๊ฒ ๋๋ฉด ์ด๋๋ฐ์ด์ค ๊ธฐ๋ฅ์ ์ํ
- @Around (๋ฉ์๋ ์คํ ์ ํ) : ์ด๋๋ฐ์ด์ค๊ฐ ํ๊ฒ ๋ฉ์๋๋ฅผ ๊ฐ์ธ์ ํ๊ฒ ๋ฉ์๋ ํธ์ถ์ ๊ณผ ํ์ ์ด๋๋ฐ์ด์ค ๊ธฐ๋ฅ์ ์ํ
- @Around์ ๊ฒฝ์ฐ ๋ฐ๋์ proceed() ๋ฉ์๋๊ฐ ํธ์ถ๋์ด์ผ ํ๋ค.
- proceed() ๋ฉ์๋๋ ํ๊ฒ ๋ฉ์๋๋ฅผ ์ง์นญํ๊ธฐ ๋๋ฌธ์ proceed ๋ฉ์๋๋ฅผ ์คํ์์ผ์ผ๋ง ํ๊ฒ ๋ฉ์๋๊ฐ ์ํ์ด ๋๋ค๋๊ฒ์ ์์ง ๋ง์.
//์๋ฅผ ๋ค์ด ํ๊ฒ ๋ฉ์๋์ ์ด์ ์์ ์์๋ง ์ด๋๋ฐ์ด์ค ๋ฉ์๋๋ฅผ ์ํํ๊ณ ์ถ๋ค๋ฉด,
@Before("ํฌ์ธํธ์ปท ํํ์")
public void ์ด๋๋ฐ์ด์ค๋ฉ์๋() {
....
}
reference
https://engkimbs.tistory.com/746
https://jojoldu.tistory.com/71