intro
์คํ๋ง ์
๋ฌธ - ์ฝ๋๋ก ๋ฐฐ์ฐ๋ ์คํ๋ง ๋ถํธ, ์น MVC, DB ์ ๊ทผ ๊ธฐ์ ์ ๋ฃ๊ณ ๋์ ๊ฐ์ธ์ ์ผ๋ก AOP์ ๋ํด ์ถ๊ฐ๋ก ์ ๋ฆฌํด ๋ณด์๋ค.
ํด๋น ๊ฐ์์ ๋์จ ์ฝ๋ ๋ถ์ ์ธ์๋ ์ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ๋ ์ดํด๋ณด์๋ค.
AOP (Aspect Oriented Programming)๋?
AOP๋ Aspect Oriented Programming์ ์ฝ์๋ก ๊ด์ ์งํฅ ํ๋ก๊ทธ๋๋ฐ์ด๋ผ๊ณ ๋ถ๋ฆฐ๋ค. ๊ด์ ์งํฅ์ ์ด๋ค ๋ก์ง์ ๊ธฐ์ค์ผ๋ก ํต์ฌ์ ์ธ ๊ด์ , ๋ถ๊ฐ์ ์ธ ๊ด์ ์ผ๋ก ๋๋์ด์ ๋ณด๊ณ ๊ทธ ๊ด์ ์ ๊ธฐ์ค์ผ๋ก ๋ชจ๋ํ ํ๊ฒ ๋ค๋ ๊ฒ์ด๋ค.
- ๋ชจ๋ํ : ์ด๋ค ๊ณตํต๋ ๋ก์ง์ด๋ ๊ธฐ๋ฅ์ ํ๋์ ๋จ์๋ก ๋ฌถ๋ ๊ฒ
์๋ฅผ ๋ค์ด ํต์ฌ์ ์ธ ๊ด์ ์ ๋น์ฆ๋์ค ๋ก์ง์ด ๋ ์ ์๊ณ , ๋ถ๊ฐ์ ์ธ ๊ด์ ์ ํต์ฌ ๋ก์ง์ ์คํํ๊ธฐ ์ํด ํํด์ง๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ, ๋ก๊น , ํ์ผ ์ ์ถ๋ ฅ ๋ฑ์ด ๋ ์ ์๋ค.
AOP๋ ํฉ์ด์ง ๊ด์ฌ์ฌ(Crosscutting Concerns)๋ฅผ ๋ชจ๋ํ ํ ์ ์๋ ํ๋ก๊ทธ๋๋ฐ ๊ธฐ๋ฒ์ด๋ค
[๊ทธ๋ฆผ 1]๊ณผ ๊ฐ์ด ํด๋์ค A, B, C์์ ๊ณตํต์ ์ผ๋ก ๋ํ๋๋ ์๊น ๋ธ๋ก์ ์ค๋ณต๋๋ ๋ฉ์๋, ํ๋, ์ฝ๋ ๋ฑ์ด๋ค. ์ด๋ ์๋ฅผ ๋ค์ด ํด๋์ค A์ ์ฃผํฉ์ ๋ธ๋ก ๋ถ๋ถ์ ์์ ํด์ผ ํ๋ค๋ฉด ํด๋์ค B, C์ ์ฃผํฉ์ ๋ถ๋ถ๋ ์ผ์ผ์ด ์ฐพ์ ์์ ํด์ผ ํ๋ค. ์ด๋ SOLID์์น์ ์๋ฐฐํ๋ฉฐ ์ ์ง๋ณด์๋ฅผ ์ด๋ ต๊ฒ ๋ง๋ ๋ค. ์ด๋ฐ ์์ผ๋ก ์์ค ์ฝ๋์์์ ๊ณ์ ๋ฐ๋ณตํด์ ์ฌ์ฉ๋๋ ๋ถ๋ถ๋ค์ **ํฉ์ด์ง ๊ด์ฌ์ฌ(Crosscutting Concerns)**๋ผ๊ณ ํ๋ค.
๊ฒฐ๊ตญ AOP์์ ๊ฐ ๊ด์ ์ ๊ธฐ์ค์ผ๋ก ๋ก์ง์ ๋ชจ๋ํํ๋ค๋ ๊ฒ์ ํฉ์ด์ง ๊ด์ฌ์ฌ๋ฅผ ๋ชจ๋ํํ๊ฒ ๋ค๋ ์๋ฏธ๋ค. [๊ทธ๋ฆผ 1]๊ณผ ๊ฐ์ด ์ฃผํฉ์, ํ๋์, ๋นจ๊ฐ์ ๋ธ๋ก์ฒ๋ผ ๋ชจ๋ํ ์์ผ๋๊ณ ์ด๋์ ์ ์ฉ์ํฌ์ง๋ง ์ ์ํด์ฃผ๋ฉด ๋๋ ๊ฒ์ด๋ค. ์ด๋ ๋ชจ๋ํ ์์ผ๋์ ๋ธ๋ญ์ Aspect๋ผ๊ณ ํ๋ค.
๊ณตํต ๊ด์ฌ ์ฌํญ(cross-cutting concern) vs ํต์ฌ ๊ด์ฌ ์ฌํญ(core concern) ๋ถ๋ฆฌ
AOP ๊ด๋ จ ์ฉ์ด
- Aspect : ํฉ์ด์ง ๊ด์ฌ์ฌ๋ฅผ ๋ชจ๋ํ ํ ๊ฒ.
- Target : Aspect๋ฅผ ์ ์ฉํ๋ ๊ณณ. ํด๋์ค, ๋ฉ์๋ ๋ฑ..
- Advice : ์ค์ง์ ์ผ๋ก ์ด๋ค ์ผ์ ํด์ผ ํ ์ง์ ๋ํ ๊ฒ, ์ค์ง์ ์ธ ๋ถ๊ฐ๊ธฐ๋ฅ์ ๋ด์ ๊ตฌํ์ฒด
- Join Point : Advice๊ฐ ์ ์ฉ๋ ์์น ํน์ ๋ผ์ด๋ค ์ ์๋ ์์ . ๋ฉ์๋ ์ง์ ์์ , ์์ฑ์ ํธ์ค ์์ , ํ๋์์ ๊บผ๋ด์ฌ ์์ ๋ฑ ๋ผ์ด๋ค ์์ ์ ์๋ฏธ. ์ฐธ๊ณ ๋ก ์คํ๋ง์์ Join Point๋ ์ธ์ ๋ ๋ฉ์๋ ์คํ ์์ ์ ์๋ฏธ ํ๋ค.
- Point Cut : Join Point์ ์์ธํ ์คํ์ ์ ์ํ ๊ฒ. "A๋ ๋ฉ์๋์ ์ง์ ์์ ์ ํธ์ถํ ๊ฒ"์ฒ๋ผ ๊ตฌ์ฒด์ ์ผ๋ก Advice๊ฐ ์คํ๋ ์์ ์ ์ ํจ.
AOP ์ ์ฉ ๋ฐฉ๋ฒ
- ์ปดํ์ผ ํ์ ์ ์ฉ
- ์ปดํ์ผ ์์ ์ ๋ฐ์ดํธ ์ฝ๋๋ฅผ ์กฐ์ํ์ฌ AOP๊ฐ ์ ์ฉ๋ ๋ฐ์ดํธ ์ฝ๋๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ.
- ๋ก๋ ํ์ ์ ์ฉ
- ์์ํ๊ฒ ์ปดํ์ผํ ๋ค, ํด๋์ค๋ฅผ ๋ก๋ฉํ๋ ์์ ์ ํด๋์ค ์ ๋ณด๋ฅผ ๋ณ๊ฒฝํ๋ ๋ฐฉ๋ฒ
- ๋ฐํ์ ์ ์ฉ
- ์คํ๋ง AOP๊ฐ ์ฃผ๋ก ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ. A๋ผ๋ ํด๋์ค ํ์ ์ Bean์ ๋ง๋ค ๋ A ํ์ ์ Proxy Bean์ ๋ง๋ค์ด Proxy Bean์ด Aspect ์ฝ๋๋ฅผ ์ถ๊ฐํ์ฌ ๋์ํ๋ ๋ฐฉ๋ฒ.
์คํ๋ง AOP
- ์คํ๋ง์์ ์ ๊ณตํ๋ ์คํ๋ง AOP๋ ํ๋ก์ ๊ธฐ๋ฐ์ AOP ๊ตฌํ์ฒด์ด๋ค.
- ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ์ ๊ทผ ์ ์ด ๋ฐ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ธฐ ์ํด์์ด๋ค.
- ์คํ๋ง AOP๋ ์คํ๋ง Bean์๋ง ์ ์ฉํ ์ ์๋ค.
- ๋ชจ๋ AOP ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ๊ฒ์ด ๋ชฉ์ ์ด ์๋, ์ค๋ณต ์ฝ๋, ํ๋ก์ ํด๋์ค ์์ฑ์ ๋ฒ๊ฑฐ๋ก์ ๋ฑ ํํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ์๋ฃจ์ ์ ์ ๊ณตํ๋ ๊ฒ์ด ๋ชฉ์ ์ด๋ค.
- ์คํ๋ง AOP๋ ์์ ์๋ฐ๋ก ๊ตฌํ๋์๊ธฐ ๋๋ฌธ์ ํน๋ณํ ์ปดํ์ผ ๊ณผ์ ์ด ํ์ํ์ง ์๋ค.
- ํ๋ก์ ํจํด
ํ๋ก์ ํจํด์์๋ interface๊ฐ ์กด์ฌํ๊ณ Client๋ ์ด interface ํ์ ์ผ๋ก Proxy ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ค. Proxy ๊ฐ์ฒด๋ ๊ธฐ์กด์ ํ๊ฒ ๊ฐ์ฒด(Real Subject)๋ฅผ ์ฐธ์กฐํ๋ค. Proxy ๊ฐ์ฒด์ ๊ธฐ์กด์ ํ๊ฒ ๊ฐ์ฒด์ ํ์ ์ ๊ฐ๊ณ , Proxy๋ ์๋ ํ ์ผ์ ๊ฐ์ง๊ณ ์๋ Real Subject๋ฅผ ๊ฐ์ธ์ Client์ ์์ฒญ์ ์ฒ๋ฆฌํ๋ค.
์คํ๋ง AOP ๊ตฌํ
maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
gradle
implementation 'org.springframework.boot:spring-boot-starter-aop'
์คํ๋ง ์ ๋ฌธ - ์ฝ๋๋ก ๋ฐฐ์ฐ๋ ์คํ๋ง ๋ถํธ, ์น MVC, DB ์ ๊ทผ ๊ธฐ์ ์ ๋์จ ์ฝ๋๋ก AOP๋ฅผ ๋จผ์ ๋ถ์ํด๋ณธ๋ค.
1. ์๊ฐ ์ธก์ AOP ๋ฑ๋ก
package hello.hellospring.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
// AOP ์ ์ฉ, ์คํ๋ง ๋น ๋ฑ๋ก
@Aspect
@Component
public class TimeTraceAop {
// ๊ณตํต๊ด์ฌ์ฌํญ์ ์ ์ฉํ ๊ณณ(hello.hellospring ํจํค์ง ํ์ ๋ชจ๋) ํ๊ฒํ
@Around("execution(* hello.hellospring..*(..))")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis(); // ์์ ์๊ฐ
System.out.println("START: " + joinPoint.toString());
try {
return joinPoint.proceed();
} finally {
long finish = System.currentTimeMillis(); // ์ข
๋ฃ ์๊ฐ
long timeMs = finish - start; // ํธ์ถ ์๊ฐ
System.out.println("END: " + joinPoint.toString() + " " + timeMs + "ms");
}
}
}
์คํ๋ง ๋น์ ๋ฑ๋กํ๋ ๋ฐฉ๋ฒ์ ์ปดํฌ๋ํธ ์ค์บ ๋ฐฉ์๊ณผ SpringConfig์ ์ง์ ๋ฑ๋กํด์ฃผ๋ ๋ฐฉ์ 2๊ฐ์ง๊ฐ ์๋ค.
์ฌ๊ธฐ์๋ ๊ฐ๋จํ๊ฒ ์ปดํฌ๋ํธ ์ค์บ ๋ฐฉ์์ธ @Component ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํ๋ค. ํ์ง๋ง TimeTraceAop ๊ฐ์ ๊ฒฝ์ฐ๋ ๊ณตํต ๊ด์ฌ ์ฌํญ์ด๊ธฐ ๋๋ฌธ์ SpringConfig ํ์ผ์ ์ง์ ๋น์ผ๋ก ๋ฑ๋กํด์ฃผ๋ ๊ฒ์ ์ถ์ฒํ๋ค.
๋ฉ์๋ ํธ์ถํ ๋๋ง๋ค ์ธํฐ์ ํธ๊ฐ ๊ฑธ๋ ค์ ๋์ํ๋ ๋ฐฉ์์
2. ์คํ ๊ฒฐ๊ณผ
1) localhost:8080 ์คํ
2) ํ์ ๋ชฉ๋ก ํด๋ฆญ
3) ํ์ ๊ฐ์ ํด๋ฆญ
4) id ์ ๋ ฅ ํ ๋ฑ๋ก
3. ํด๊ฒฐ
- ํ์๊ฐ์ , ํ์ ์กฐํ๋ฑ ํต์ฌ ๊ด์ฌ์ฌํญ๊ณผ ์๊ฐ์ ์ธก์ ํ๋ ๊ณตํต ๊ด์ฌ ์ฌํญ์ ๋ถ๋ฆฌํ๋ค.
- ์๊ฐ์ ์ธก์ ํ๋ ๋ก์ง์ ๋ณ๋์ ๊ณตํต ๋ก์ง์ผ๋ก ๋ง๋ค์๋ค.
- ํต์ฌ ๊ด์ฌ ์ฌํญ์ ๊น๋ํ๊ฒ ์ ์งํ ์ ์๋ค.
- ๋ณ๊ฒฝ์ด ํ์ํ๋ฉด ์ด ๋ก์ง๋ง ๋ณ๊ฒฝํ๋ฉด ๋๋ค.
- ์ํ๋ ์ ์ฉ ๋์์ ์ ํํ ์ ์๋ค.(ํ๊ฒํ )
2. ์คํ๋ง์ AOP ๋์ ๋ฐฉ์ ์ค๋ช
AOP๋ Proxy๋ฅผ ํธ์ถํ๋ค!
1. AOP ์ ์ฉ ์ ์์กด๊ด๊ณ
2. AOP ์ ์ฉ ํ ์์กด๊ด๊ณ
1) AOP๋ฅผ ์ ์ฉํ๋ฉด Proxy(๊ฐ์ง memberService)๋ฅผ ๋ง๋ค์ด๋ธ๋ค.
2) ์ค์ ๋ก๋ memberController๊ฐ memberService๋ผ๋ Proxy(๊ฐ์ง memberService)๋ฅผ ํธ์ถํ๋ค.
3) joinPoint.proceed()๊ฐ ํธ์ถ๋๋ฉด ์ค์ memberService๊ฐ ํธ์ถ๋๋ค.
<Proxy ์ฃผ์ ์ฝ์์์ ์ถ๋ ฅํด์ ํ์ธํ๊ธฐ>
@Controller
public class MemberController {
// private final MemberService memberService = new MemberService();
// ์คํ๋ง ์ปจํ
์ด๋์ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก์ ํด๋๊ณ ๊ฐ์ ธ๋ค ์ฐ๋ ๋ฐฉ์(๊ฐ์ MemberService๋ฅผ ๊ณต์ ํ๋๋ก)
private final MemberService memberService;
// ํ์ ์ปจํธ๋กค๋ฌ & ํ์ ์๋น์ค ์ฐ๊ฒฐ
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
// memberController๊ฐ Proxy(๊ฐ์ง memberService)๋ฅผ ํธ์ถํจ์ ํ์ธ
System.out.println("memberService = " + memberService.getClass());
...
}
์๋์ ๊ฐ์ด ์ค์ MemberService๊ฐ ์๋ ๋ค์ ๋ฌด์์ธ๊ฐ ๋ถ์ Proxy๊ฐ ์ฃผ์ ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
3. AOP ์ ์ฉ ์ ์ ์ฒด ๊ทธ๋ฆผ
4. AOP ์ ์ฉ ํ ์ ์ฒด ๊ทธ๋ฆผ
Spring AOP๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ์์ ๊ฐ์ด ์์กด์ฑ์ ์ถ๊ฐํด์ค์ผ ํ๋ค.
์ด์ ๋ค๋ฅธ ์ฝ๋๋ก AOP๋ฅผ ๋ถ์ํด ๋ณธ๋ค.
์ง๊ธ๋ถํฐ ๊ตฌํํ ๊ธฐ๋ฅ์ ํ๊ฒ ๋ฉ์๋์ ์คํ ์๊ฐ์ ์ธก์ ํด์ฃผ๋ ๋ก์ง์ด๋ค.
@Component
@Aspect
public class PerfAspect {
@Around("execution(* com.example..*.EventService.*(..))")
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object reVal = pjp.proceed();
System.out.println(System.currentTimeMillis() - begin);
return reVal;
}
}
์คํ๋ง AOP๋ Bean์์๋ง ๋์ํ๋ค๊ณ ํ๋ค. ๋ฐ๋ผ์ @Component ์ด๋ ธํ ์ด์ ๋ฑ์ ํตํด ์คํ๋ง Bean์ผ๋ก ๋ฑ๋กํด์ค ๋ค ์ฌ์ฉํด์ผ ํ๋ค. @Aspect ์ด๋ ธํ ์ด์ ์ ๋ถ์ด๋ฉด ํด๋น ํด๋์ค๊ฐ Aspect๋ผ๋ ๊ฒ์ ๋ช ์ํด์ค๋ค.
logPerf() ๋ฉ์๋๋ @Around ์ด๋ ธํ ์ด์ ์ execution์ ํตํด Advice๋ฅผ ์ ์ฉํ ๋ฒ์๋ฅผ ์ง์ ํด์ค ์ ์๋ค. ์์ ์ฝ๋๋ฅผ ์์๋ก ๋ค์๋ฉด com.example ๋ฐ์ ๋ชจ๋ ํด๋์ค์ ์ ์ฉํ๊ณ , EventService ๋ฐ์ ๋ชจ๋ ๋ฉ์๋์ ์ ์ฉํ๋ผ๋ ๋ป์ด๋ค.
์์ ๊ฐ์ ๊ฒฝ๋ก ์ง์ ๋ฐฉ์์ด ์๋ ํน์ ์ด๋ ธํ ์ด์ ์ด ๋ถ์ ํฌ์ธํธ์ ํด๋น Aspect๋ฅผ ์คํํ ์๋ ์๋ค.
@Component
@Aspect
public class PerfAspect {
@Around("@annotation(PerfLogging)")
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object retVal = pjp.proceed();
System.out.println(System.currentTimeMillis() - begin);
return retVal;
}
}
์์ ๊ฐ์ด @Around ์ด๋ ธํ ์ด์ ์ @annotation(PerfLogging)์ฒ๋ผ ์ ์ฉ๋ ์ด๋ ธํ ์ด์ ์ ๋ช ์ํ ์ ์๋ค. ๊ทธ๋ผ ํด๋น ๋ฉ์๋๋ฅผ ์ ์ฉ์ํฌ ํน์ ๋ฉ์๋์ @PerfLogging ์ด๋ ธํ ์ด์ ์ ๋ถ์ฌ์ฃผ๊ธฐ๋ง ํ๋ฉด logPerf() ๊ธฐ๋ฅ์ด ๋์ํ๋ค.
ํน์ ํน์ Bean ์ ์ฒด์ ํด๋น ๊ธฐ๋ฅ์ ์ ์ฉ์ํฌ ์๋ ์๋ค.
// Aspect ์ ์@Component
@Aspect
public class PerfAspect {
// ๋น์ด ๊ฐ์ง๊ณ ์๋ ๋ชจ๋ ํผ๋ธ๋ฆญ ๋ฉ์๋@Around("bean(simpleServiceEvent)")
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object retVal = pjp.proceed();
System.out.println(System.currentTimeMillis() - begin);
return retVal;
}
}
์์ ๊ฐ์ด @Around ์ด๋ ธํ ์ด์ ์ bean(simpleServcieEvent)์ฒ๋ผ ์ ์ฉ๋ ๋น์ ๋ช ์ํ ์ ์๋ค. ๊ทธ๋ผ ํด๋น ๋น์ด ๊ฐ์ง๊ณ ์๋ ๋ชจ๋ public ๋ฉ์๋์ ํด๋น ๊ธฐ๋ฅ์ด ์ ์ฉ๋๋ค.
@Around ์ธ์ ํ๊ฒ ๋ฉ์๋์ Aspect ์คํ ์์ ์ ์ง์ ํ ์ ์๋ ์ด๋ ธํ ์ด์ ์ ๋ค์๊ณผ ๊ฐ์ ๊ฒ๋ค์ด ์๋ค.
- @Before : Advice ํ๊ฒ ๋ฉ์๋๊ฐ ํธ์ถ๋๊ธฐ ์ ์ Advice ๊ธฐ๋ฅ ์ํ
- @After : ํ๊ฒ ๋ฉ์๋์ ๊ฒฐ๊ณผ์ ๊ด๊ณ์์ด ํ๊ฒ ๋ฉ์๋๊ณผ ์๋ฃ๋๋ฉด Advice ๊ธฐ๋ฅ ์ํ
- @AfterRunning : ํ๊ฒ ๋ฉ์๋๊ฐ ์ฑ๊ณต์ ์ผ๋ก ๊ฒฐ๊ณผ๊ฐ์ ๋ฐํ ํ ํ์ Advice ๊ธฐ๋ฅ ์ํ
- @AfterThrowing : ํ๊ฒ ๋ฉ์๋๊ฐ ์ํ ์ค ์์ธ๋ฅผ ๋์ง๋ฉด Advice ๊ธฐ๋ฅ ์ํ
- @Around : Advice๊ฐ ํ๊ฒ ๋ฉ์๋๋ฅผ ๊ฐ์ธ ํ๊ฒ ๋ฉ์๋ ํธ์ถ ์ , ํ์ Advice ๊ธฐ๋ฅ ์ํ