
AOP(Aspect Oriented Programming)๋ ๊ด์ ์งํฅ ํ๋ก๊ทธ๋๋ฐ์ผ๋ก, ๊ธฐ์กด OOP(๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ)์ ํ๊ณ๋ฅผ ๊ทน๋ณตํ๊ธฐ ์ํด ๋ฑ์ฅํ ํจ๋ฌ๋ค์์
๋๋ค.
์ฃผ์ ๋ก์ง๊ณผ ๋ฐ๋ณต์ ์ผ๋ก ์ฌ์ฉ๋๋ ๋ถ๊ฐ ๋ก์ง์ ๋ถ๋ฆฌํ์ฌ ํจ์จ์ ์ธ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๊ฒ ๋์์ค๋๋ค.
ํต์ฌ ๊ด์ฌ ์ฌํญ(Core Concern):
ํ๋ก๊ทธ๋จ์ ๋ณธ์ง์ ์ธ ๊ธฐ๋ฅ๊ณผ ๋ก์ง์
๋๋ค. ์๋ฅผ ๋ค์ด, ์ผํ๋ชฐ ํ๋ก๊ทธ๋จ์ด๋ผ๋ฉด "์ํ ์ฃผ๋ฌธ", "๊ฒฐ์ ์ฒ๋ฆฌ"์ ๊ฐ์ ๊ธฐ๋ฅ์ด ํต์ฌ ๊ด์ฌ ์ฌํญ์
๋๋ค.
๊ณตํต ๊ด์ฌ ์ฌํญ(Cross-cutting Concern):
์ฌ๋ฌ ๊ธฐ๋ฅ์์ ๋ฐ๋ณต์ ์ผ๋ก ์ฌ์ฉ๋๋ ๋ถ๊ฐ์ ์ธ ๋ก์ง์
๋๋ค. ์๋ฅผ ๋ค์ด "๋ก๊ทธ ๊ธฐ๋ก", "ํธ๋์ญ์
์ฒ๋ฆฌ", "๋ณด์ ๊ฒ์ฌ" ๋ฑ๊ณผ ๊ฐ์ ๋ถ๋ถ์
๋๋ค.
๐ ์ฌ๋ฌ ๋ชจ๋์ ์ค๋ณต๋๋ ์ฝ๋๊ฐ ์๊ธธ ์ํ์ด ์์ต๋๋ค.
OOP(๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ)์์๋ ํด๋์ค์ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํด ๊ธฐ๋ฅ์ ๋ชจ๋ํํฉ๋๋ค. ํ์ง๋ง, ๊ณตํต ๊ด์ฌ ์ฌํญ์ ๊ด๋ฆฌํ๋ ๋ฐ์๋ ํ๊ณ๊ฐ ์์ต๋๋ค.
๋ฌธ์ ์ ์์
AOP๋ ํต์ฌ ๋ก์ง๊ณผ ๋ถ๊ฐ ๋ก์ง์ ๋ถ๋ฆฌํ์ฌ ๊ณตํต ๊ด์ฌ ์ฌํญ์ ๋ชจ๋(Aspect)๋ก ๊ด๋ฆฌํฉ๋๋ค.
์ด๋ฅผ ํตํด ํต์ฌ ๊ธฐ๋ฅ์ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์๋์ผ๋ก ์ ์ฉํ ์ ์์ต๋๋ค.
AOP ์ ์ฉ ์์
AOP(Aspect Oriented Programming)๋ ๊ด์ ์งํฅ ํ๋ก๊ทธ๋๋ฐ์ผ๋ก, ํ๋ก๊ทธ๋จ ๋ด ํต์ฌ ๊ธฐ๋ฅ๊ณผ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ๋ช ํํ ๋ถ๋ฆฌํด ์ฝ๋์ ์ค๋ณต์ ์ค์ด๊ณ ์ ์ง๋ณด์๋ฅผ ์ฝ๊ฒ ๋ง๋ญ๋๋ค.
ํต์ฌ ๊ธฐ๋ฅ(Core Functionality):
ํ๋ก๊ทธ๋จ์ ์ฃผ๋ ๋ก์ง๊ณผ ๋น์ฆ๋์ค ๋ชฉํ๋ฅผ ๋ด๋นํฉ๋๋ค.
์) ์ฃผ๋ฌธ ์ฒ๋ฆฌ, ๊ฒฐ์ ์์คํ
๋ถ๊ฐ ๊ธฐ๋ฅ(Auxiliary Functionality):
์ฌ๋ฌ ๊ธฐ๋ฅ์ ๊ฑธ์ณ ๋ฐ๋ณต์ ์ผ๋ก ํ์ํ ๋ณด์กฐ ๋ก์ง์
๋๋ค.
์) ๋ก๊ทธ ๊ธฐ๋ก, ๋ณด์ ๊ฒ์ฌ, ํธ๋์ญ์
๊ด๋ฆฌ
๋ฌธ์ ์์
AOP๋ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ๋ ๋ฆฝ๋ ๋ชจ๋(Aspect)๋ก ์ ์ํ์ฌ ํต์ฌ ๊ธฐ๋ฅ์ ์๋์ผ๋ก ์ ์ฉํ ์ ์๊ฒ ํฉ๋๋ค.
AOP ์ ์ฉ ์์
System.currentTimeMillis()๋ฅผ ์ฌ์ฉํ๊ฑฐ๋StopWatch๋ฅผ ํ์ฉํด ์๊ฐ์ ์ธก์ ํฉ๋๋ค.try-catch ๋ธ๋ก์ ์์ฑํด์ผ ํ๋๋ฐ, ์ด๋ ์ฝ๋๊ฐ ๊ธธ๊ณ ๋ณต์กํด์ง๋ ์์ธ์ด ๋ฉ๋๋ค.DataAccessException๊ณผ ๊ฐ์ ํ์คํ๋ ์์ธ ๊ณ์ธต์ ์ ๊ณตํฉ๋๋ค.Hibernate์ JDBC์ ๋๊ธฐํ ๋ฌธ์ ํด๊ฒฐ:
Hibernate์ JDBC๋ฅผ ํจ๊ป ์ฌ์ฉํ ๊ฒฝ์ฐ DB ๋๊ธฐํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. AOP๋ฅผ ํตํด ์ด ๋ฌธ์ ๋ฅผ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์์์ ๋ฝ(lock) ์ค์ :
์ฌ๋ฌ ์ค๋ ๋๊ฐ ๋์์ ์ ๊ทผํ ์ ์๋ ๋ฉ์๋์ ๋ฝ์ ์ค์ ํ์ฌ ์ค๋ ๋ ์์ ์ฑ์ ๋ณด์ฅํฉ๋๋ค.
๋ฐ๋๋ฝ ๋ฐฉ์ง:
PessimisticLockingFailureException๊ณผ ๊ฐ์ ์์ธ๋ฅผ AOP๋ก ํฌ์ฐฉํด ๋ฐ๋๋ฝ์ด ๋ฐ์ํ์ง ์๋๋ก ๋์ํฉ๋๋ค.
๋ก๊ทธ, ์ธ์ฆ, ๊ถํ ๊ด๋ฆฌ:
๋ชจ๋ ๋ฉ์๋์ ๋ก๊ทธ๋ ์ธ์ฆ ์ฝ๋๋ฅผ ์ง์ ์์ฑํ์ง ์๊ณ , AOP๋ฅผ ํตํด ์ค์์์ ํ ๋ฒ์ ๊ด๋ฆฌํฉ๋๋ค.
์ ์:
Advice๋ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ๊ตฌ์ฒด์ ์ธ ๋ก์ง์
๋๋ค. ํน์ ์์ (์: ๋ฉ์๋ ์คํ ์ /ํ)์ ์ํ๋ Aspect์ ๋์์ ์ ์ํฉ๋๋ค.
์์:
๋ฉ์๋ ์คํ ์ ์ ๋ก๊ทธ๋ฅผ ๊ธฐ๋กํ๊ฑฐ๋, ๋ฉ์๋๊ฐ ๋๋ ํ ํธ๋์ญ์
์ ์ข
๋ฃํ๋ ์ฝ๋๊ฐ Advice์ ํฌํจ๋ฉ๋๋ค.
Advice ์ ํ:
์ ์:
JoinPoint๋ Aspect๊ฐ ์ ์ฉ๋ ์ ์๋ ์์ ์
๋๋ค.
์์:
๋ง์ฝ ํน์ API์ ๋ฉ์๋๊ฐ ํธ์ถ๋ ๋๋ง๋ค ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๋ ค๋ฉด, ์ด ๋ฉ์๋์ ํธ์ถ ์ง์ ์ด JoinPoint๊ฐ ๋ฉ๋๋ค.
์ ์:
Pointcut์ ๊ณตํต ๊ด์ฌ ์ฌํญ์ ์ ์ฉํ JoinPoint๋ฅผ ์ ํํ๋ ๊ท์น์
๋๋ค.
Pointcut ํํ์:
execution(* com.example.service.*.*(..))์ ์:
์ฌ๋ฌ ๊ฐ์ฒด์ ๊ฑธ์ณ ๊ณตํต์ผ๋ก ์ ์ฉ๋๋ ๋ถ๊ฐ ๊ธฐ๋ฅ(๊ณตํต ๊ด์ฌ ์ฌํญ)์
๋๋ค.
๊ตฌ์ฑ:
Aspect = Advice + Pointcutํน์ง:
์ ์:
Advisor๋ Advice์ Pointcut์ ์กฐํฉ์ผ๋ก, Spring AOP์์ ์ฌ์ฉํ๋ ํน๋ณํ ์ฉ์ด์
๋๋ค.
์์:
ํน์ ๋ฉ์๋์๋ง ์ ์ฉ๋๋ Advisor๋ฅผ ์ค์ ํด, ๋ก๊ทธ ๊ธฐ๋ก์ด๋ ํธ๋์ญ์
๊ด๋ฆฌ๋ฅผ ์ธ๋ฐํ๊ฒ ์กฐ์ ํ ์ ์์ต๋๋ค.
์ ์:
Weaving์ Advice๋ฅผ ํน์ Pointcut์ ์ฝ์
ํ๋ ๊ณผ์ ์
๋๋ค.
์ข ๋ฅ:
์์:
๋ก๊ทธ ๊ธฐ๋ก์ ์ํด ๋ฉ์๋ ์คํ ์ ํ์ ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๋ ๋ถ๊ฐ ๊ธฐ๋ฅ์ด Weaving์ ํตํด ์ ์ฉ๋ฉ๋๋ค. ์ด ๊ณผ์ ์์ ํต์ฌ ๋ก์ง์ ์ํฅ์ ์ฃผ์ง ์์ต๋๋ค.
| ์ฉ์ด | ์ค๋ช |
|---|---|
| Target | ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ฉํ ํต์ฌ ๊ธฐ๋ฅ์ ๋ด๊ณ ์๋ ๊ฐ์ฒด |
| Advice | ํน์ ์์ ์ ์ ์ฉ๋ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ํ ๋ชจ๋ |
| JoinPoint | AOP๊ฐ ์ ์ฉ๋ ์ ์๋ ์์ (์: ๋ฉ์๋ ํธ์ถ, ํ๋ ์ ๊ทผ ๋ฑ) |
| Pointcut | ํน์ JoinPoint๋ฅผ ์ ํํ๋ ๊ธฐ์ค (์ ๊ท ํํ์ ๋ฑ) |
| Aspect | Advice์ Pointcut์ ์กฐํฉ์ผ๋ก ๊ณตํต ๊ธฐ๋ฅ์ ์ ์ํ ๋ชจ๋ |
| Advisor | Advice์ Pointcut์ ์ธ๋ถ ์ค์ ์ ํฌํจํ ์กฐํฉ |
| Weaving | ํต์ฌ ๊ธฐ๋ฅ์ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์๋์ผ๋ก ์ฝ์ ํ๋ ๊ณผ์ |
| ํํ์ | ์ค๋ช |
|---|---|
* | 0๊ฐ ์ด์์ ๋ฌธ์๋ฅผ ์๋ฏธํฉ๋๋ค. ์ (.)์ ํฌํจํ์ง ์๋ ๋ชจ๋ ๋ฌธ์์ ์ผ์นํฉ๋๋ค. |
.. | 0๊ฐ ์ด์์ ํจํค์ง๋ ํ๋ผ๋ฏธํฐ๋ฅผ ์๋ฏธํฉ๋๋ค. ์ (.)์ ํฌํจํ ๊ณ์ธต ๊ตฌ์กฐ๊น์ง ํฌํจํฉ๋๋ค. |
+ | ํน์ ํด๋์ค์ ์๋ธ ํด๋์ค๋ ์๋ธ ์ธํฐํ์ด์ค๋ฅผ ํฌํจํฉ๋๋ค. ํ์ ํด๋์ค๋ ๊ตฌํ์ฒด๋ฅผ ๋ชจ๋ ๋งค์นญํฉ๋๋ค. |
com.example.*: com.example ํจํค์ง ๋ด ๋ชจ๋ ํด๋์คcom.example..*: com.example์ ๊ทธ ํ์ ํจํค์ง์ ๋ชจ๋ ํด๋์ค UserService+: UserService์ ๊ทธ ํ์ ํด๋์ค ํฌํจ| ํํ์ | ์ค๋ช |
|---|---|
! | ํด๋น ์กฐ๊ฑด์ ํฌํจ๋์ง ์๋ ๋ชจ๋ JoinPoint๋ฅผ ์ ํํฉ๋๋ค. |
&& | ๋ ์กฐ๊ฑด์ ๋ชจ๋ ๋ง์กฑํ๋ JoinPoint๋ฅผ ์ง์ ํฉ๋๋ค. |
| ` |
!execution(* get*(..)): get์ผ๋ก ์์ํ๋ ๋ฉ์๋๋ฅผ ์ ์ธํ ๋ชจ๋ ๋ฉ์๋ execution(* set*(..)) && within(com.example..*): set์ผ๋ก ์์ํ๋ ๋ฉ์๋ ์ค com.example ํจํค์ง ๋ด์์๋ง ์ ์ฉ execution(* get*(..)) || execution(* set*(..)): get์ด๋ set์ผ๋ก ์์ํ๋ ๋ฉ์๋ ์ค ํ๋๋ผ๋ ํด๋นํ๋ฉด JoinPoint๋ก ์ง์
||์ฌ์ฉ ์ ์์ฌํญ: AOP ํํ์์์๋ ์ฌ๋ฌ ์กฐ๊ฑด์ ํจ๊ป ์ฌ์ฉํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ดget๋๋set์ผ๋ก ์์ํ๋ ๋ฉ์๋ ๋ ์ค ํ๋๋ผ๋ ๋ง์กฑํ๋ฉด ํด๋น JoinPoint๊ฐ ์ ํ๋ฉ๋๋ค. ์ด๋ฅผ ํ์ฉํด ์ฌ๋ฌ ๋ฉ์๋ ํจํด์ ๋์์ ๋งค์นญํ ์ ์์ต๋๋ค.
| ํํ์ | ์ค๋ช |
|---|---|
get*(..) | get์ผ๋ก ์์ํ๋ ๋ชจ๋ ๋ฉ์๋. ํ๋ผ๋ฏธํฐ์ ๊ฐ์๋ ํ์
์ ์๊ด์์ด ๋งค์นญ๋ฉ๋๋ค. |
* get*(..) | ๋ฆฌํด ํ์
์ด ๋ฌด์์ด๋ ์๊ด์์ด get์ผ๋ก ์์ํ๋ ๋ฉ์๋ |
execution(public * *(..)): public ์ ๊ทผ ์ ์ด์๋ฅผ ๊ฐ์ง ๋ชจ๋ ๋ฉ์๋ execution(* set*(..)): set์ผ๋ก ์์ํ๋ ๋ชจ๋ ๋ฉ์๋ execution(* com.example.service.UserService.*(..)): UserService ํด๋์ค ๋ด ๋ชจ๋ ๋ฉ์๋| ํํ์ | ์ค๋ช |
|---|---|
com.example.* | com.example ํจํค์ง์ ๋ชจ๋ ํด๋์ค |
com.example..* | com.example ํจํค์ง์ ๊ทธ ํ์ ํจํค์ง์ ๋ชจ๋ ํด๋์ค |
MemberService+ | MemberService์ ํ์ ํด๋์ค๋ ์ธํฐํ์ด์ค๋ฅผ ํฌํจํฉ๋๋ค. |
!MemberService | MemberService ํด๋์ค๋ ์ ์ธํ๊ณ ๋๋จธ์ง ๋ชจ๋ ํด๋์ค |
com.project.*: project ํจํค์ง์ ๋ชจ๋ ํด๋์ค com.project..*: project ํจํค์ง์ ํ์ ํจํค์ง์ ๋ชจ๋ ํด๋์ค UserService+: UserService์ ๊ทธ ํ์ ํด๋์ค ํฌํจ| ํํ์ | ์ค๋ช |
|---|---|
* addUser(..) | ๋งค๊ฐ๋ณ์์ ๊ฐ์์ ํ์
์๊ด์์ด addUser ๋ฉ์๋ |
* addUser(String) | 1๊ฐ์ String ํ์
๋งค๊ฐ๋ณ์๋ฅผ ๊ฐ์ง addUser ๋ฉ์๋ |
* addUser(*, ..) | ์ฒซ ๋ฒ์งธ ๋งค๊ฐ๋ณ์ ์ดํ๋ ์ด๋ค ํ์ ์ด๋ ํ์ฉ |
| ํํ์ | ์ค๋ช |
|---|---|
new(..) | ๋ชจ๋ ์์ฑ์๋ฅผ ์ง์ ํฉ๋๋ค. |
UserService new(*) | UserService์ ๋ชจ๋ ์์ฑ์๋ฅผ ์ง์ ํฉ๋๋ค. |
@Around("execution(* com.example.service.*.*(..))")
public Object logMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Method started: " + joinPoint.getSignature());
Object result = joinPoint.proceed();
System.out.println("Method ended: " + joinPoint.getSignature());
return result;
}
com.example.service ํจํค์ง์ ๋ชจ๋ ๋ฉ์๋ ์คํ ์ ํ์ ๋ก๊ทธ๋ฅผ ๊ธฐ๋กํ๋ AOP ์์ ์
๋๋ค.@Before("execution(* com.example.dao.*.*(..))")
public void beginTransaction() {
System.out.println("Transaction started");
}
com.example.dao ํจํค์ง์ ๋ชจ๋ ๋ฉ์๋ ์คํ ์ ์ ํธ๋์ญ์
์ ์์ํฉ๋๋ค.||, && ํ์ฉ)@Before("execution(* get*(..)) || execution(* set*(..))")
public void logGetterAndSetter() {
System.out.println("Getter or Setter method invoked");
}
get ๋๋ set์ผ๋ก ์์ํ๋ ๋ฉ์๋ ์ค ํ๋๋ผ๋ ์คํ๋ ๋ ๋ก๊ทธ๋ฅผ ๋จ๊น๋๋ค.OOP๋ ํ๋ก๊ทธ๋จ์ ๊ฐ์ฒด ๋จ์๋ก ๋๋์ด ๊ธฐ๋ฅ์ ๋ชจ๋ํํ๋ ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์์ ๋๋ค. ๊ฐ๊ฐ์ ๊ฐ์ฒด๋ Primary Concern(ํต์ฌ ๊ด์ฌ ์ฌํญ)์ ์ค์ฌ์ผ๋ก ๋์ํ๋ฉฐ, ์ฃผ์ ๋น์ฆ๋์ค ๋ก์ง์ ๋ด๋นํฉ๋๋ค.
OOP์์๋ Cross-Cutting Concern์ด ์ฌ๋ฌ ๊ฐ์ฒด์ ๊ฑธ์ณ ์ค๋ณต๋ฉ๋๋ค. ์ด๋ก ์ธํด ์ฝ๋๊ฐ ๋ณต์กํด์ง๊ณ ์ ์ง๋ณด์๊ฐ ์ด๋ ค์์ง ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ๊ฐ ๋ฉ์๋์ ๋ก๊ทธ๋ฅผ ๊ธฐ๋กํ๋ ์ฝ๋๊ฐ ์ค๋ณต๋๊ฑฐ๋, ๋ณด์ ๊ฒ์ฆ์ ์ถ๊ฐํ๋ ์ฝ๋๊ฐ ์ฌ๋ฌ ๊ณณ์ ๋ฐ๋ณต๋ฉ๋๋ค.
AOP๋ OOP์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ฑ์ฅํ ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์์ ๋๋ค. ํต์ฌ ๋ก์ง(Primary Concern)๊ณผ ๋ถ๊ฐ ๋ก์ง(Cross-Cutting Concern)์ ๋ถ๋ฆฌํ์ฌ, ํ์ํ ๋๋ง ๋ถ๊ฐ ๊ธฐ๋ฅ์ ํต์ฌ ๋ก์ง์ ์ ์ฉํฉ๋๋ค.
Point-Cut: ๋ถ๊ฐ ๋ก์ง์ด ์ ์ฉ๋ ํน์ ์ง์ ์ ์ ํํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ํน์ ํด๋์ค์ ๋ฉ์๋ ์คํ ์, ํน์ ํจํด์ ๋ฉ์๋๊ฐ ํธ์ถ๋ ๋ ๋ฑ.
Advice: Point-Cut์์ ์ด๋ค ์์ ์ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์คํํ ์ง๋ฅผ ์ ์ํฉ๋๋ค. ์ด๋ Before(๋ฉ์๋ ์คํ ์ ), After(๋ฉ์๋ ์คํ ํ), ๋๋ Around(๋ฉ์๋ ์คํ ์ ํ)๋ฅผ ์ ํํ ์ ์์ต๋๋ค.
Weaving: ๋ถ๊ฐ ๋ก์ง(Aspect)์ ํต์ฌ ๋ก์ง์ ๊ฒฐํฉํ๋ ๊ณผ์ ์ ๋๋ค. ์ด ๊ณผ์ ์์ ํต์ฌ ๋ก์ง์๋ ์ํฅ์ ์ฃผ์ง ์์ผ๋ฉด์๋ ๋ถ๊ฐ ๊ธฐ๋ฅ์ด ์ ์ฉ๋ฉ๋๋ค.
Spring AOP์ ํต์ฌ์ ํ๋ก์(Proxy) ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋ ๊ฒ์ ๋๋ค. ํ๋ก์๋ฅผ ํตํด Target ๊ฐ์ฒด์ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ฉํ๊ณ , ์ด๋ฌํ ๊ธฐ๋ฅ์ ์คํ ์๊ฐ(Runtime)์ ๋์ ์ผ๋ก ์ถ๊ฐ๋ฉ๋๋ค.
Spring AOP์์ Proxy๋ Target ๊ฐ์ฒด์ ๋ํ ํธ์ถ์ ๊ฐ๋ก์ฑ๋(Intercept) ์ญํ ์ ํฉ๋๋ค. ์ฆ, ๋ฉ์๋๊ฐ ํธ์ถ๋๋ฉด Proxy๊ฐ ํธ์ถ์ ๋ฐ์ผ๋ฉฐ, ๋ถ๊ฐ ๊ธฐ๋ฅ์ ๋จผ์ ์คํํ ๋ค Target์ ์ค์ ๋ฉ์๋๋ฅผ ํธ์ถํฉ๋๋ค.
Spring AOP๋ ๋ฉ์๋ JoinPoint๋ง์ ์ง์ํฉ๋๋ค. ์ฆ, Spring AOP์์๋ ๋ฉ์๋ ํธ์ถ ์์ ์๋ง ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ฉํ ์ ์์ผ๋ฉฐ, ๋ค๋ฅธ ์์ (์: ํ๋ ์ ๊ทผ, ๊ฐ์ฒด ์์ฑ ๋ฑ)์ ์ง์๋์ง ์์ต๋๋ค.
JoinPoint๋ AOP์์ ๋ถ๊ฐ ๊ธฐ๋ฅ์ด ์ ์ฉ๋ ์ ์๋ ํน์ ์์ ์ ์๋ฏธํฉ๋๋ค. Spring AOP์์๋ ๋ฉ์๋ ์คํ ์์ ๋ง JoinPoint๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
Spring AOP๋ ํ๋ก์ ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋ฉฐ, ํต์ฌ ๋ก์ง๊ณผ ๋ถ๊ฐ ๋ก์ง์ ๋ถ๋ฆฌํด ์ ์ง๋ณด์์ฑ์ ๋์ด๋ ๋ฐ ๋งค์ฐ ์ ์ฉํฉ๋๋ค. ๋ํ ๋ฉ์๋ ํธ์ถ ์์ ์์๋ง ๋์ํ๋ฏ๋ก, ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง์ ์ํฅ์ ์ฃผ์ง ์์ผ๋ฉด์๋ ํ์ํ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ฉํ ์ ์์ต๋๋ค.
Spring AOP๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์๋ ์ธ ๊ฐ์ง ์ฃผ์ ๋ฐฉ์์ด ์์ต๋๋ค: POJO ๊ธฐ๋ฐ ๊ตฌํ, Spring API ๊ธฐ๋ฐ ๊ตฌํ, ์ด๋ ธํ ์ด์ ๊ธฐ๋ฐ ๊ตฌํ. ๊ฐ ๋ฐฉ์์ ํน์ฑ๊ณผ ์ฅ๋จ์ ์ ์ดํด๋ณด๊ณ , ์ด๋ค ์ํฉ์์ ๊ฐ์ฅ ์ ํฉํ์ง ์์๋ณด๊ฒ ์ต๋๋ค.
POJO(Plain Old Java Object)๋ ์์ํ ์๋ฐ ๊ฐ์ฒด๋ก, ํน๋ณํ ์์์ด๋ ์ธํฐํ์ด์ค ๊ตฌํ ์์ด ์ฌ์ฉํ ์ ์๋ ๊ฐ์ฒด์ ๋๋ค. POJO ๊ธฐ๋ฐ์ AOP ๊ตฌํ์ ์๋ฐ ํด๋์ค์ ์ข ์๋์ง ์๊ณ ์์ํ ์๋ฐ ์ฝ๋๋ก AOP๋ฅผ ๊ตฌํํ๋ ๋ฐฉ์์ ๋๋ค.
public class MyService {
public void myMethod() {
System.out.println("Executing my method...");
}
}
public class MyServiceProxy extends MyService {
@Override
public void myMethod() {
// ๋ถ๊ฐ ๊ธฐ๋ฅ: ๋ก๊ทธ ์ถ๋ ฅ
System.out.println("Logging before method execution.");
super.myMethod();
// ๋ถ๊ฐ ๊ธฐ๋ฅ: ๋ก๊ทธ ์ถ๋ ฅ
System.out.println("Logging after method execution.");
}
}
์ด ์ฝ๋๋ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํด ๋ฉ์๋ ์คํ ์ ํ๋ก ๋ก๊ทธ๋ฅผ ์ถ๋ ฅํ๋ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ฉํ ์์์ ๋๋ค.
Spring AOP๋ Spring์ด ์ ๊ณตํ๋ API๋ฅผ ์ฌ์ฉํ์ฌ AOP๋ฅผ ๊ตฌํํ๋ ๋ฐฉ์์ผ๋ก, POJO๋ณด๋ค ๋ ๊ฐํธํ๊ณ ๊ฐ๋ ฅํ๊ฒ AOP๋ฅผ ๊ตฌํํ ์ ์์ต๋๋ค. Spring์์๋ ProxyFactoryBean ๋ฑ์ ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์๋์ผ๋ก ๊ด๋ฆฌํ๊ณ , ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ฉํ ์ ์๊ฒ ํฉ๋๋ค.
ProxyFactoryBean proxyFactory = new ProxyFactoryBean();
proxyFactory.setTarget(new MyService());
proxyFactory.addAdvice(new LoggingAdvice());
MyService proxy = (MyService) proxyFactory.getObject();
proxy.myMethod();
์ด ์ฝ๋์์๋ Spring์ ProxyFactoryBean์ ์ฌ์ฉํด ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ฉํ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์์ฑํ ํ, ํด๋น ํ๋ก์๋ฅผ ์ฌ์ฉํด ๋ฉ์๋๋ฅผ ํธ์ถํฉ๋๋ค.
Spring 2.0 ์ดํ๋ถํฐ๋ ์ด๋ ธํ ์ด์ ๊ธฐ๋ฐ์ AOP ๊ตฌํ์ด ๊ฐ๋ฅํด์ก์ต๋๋ค. ์ด๋ ธํ ์ด์ ์ ์ด์ฉํ AOP๋ AspectJ ์คํ์ผ์ ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ AOP๋ฅผ ์ ์ฉํ๋ ๋ฐฉ์์ ๋๋ค. ์ค์ ํ์ผ์ด๋ Bean์ ์์ฑํ ํ์ ์์ด ๊ฐ๋จํ๊ฒ AOP๋ฅผ ์ ์ฉํ ์ ์์ต๋๋ค.
@Aspect, @Before, @After, @Around ๋ฑ์ ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ์ฌ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ๋ฉ์๋์ ์ ์ฉํฉ๋๋ค.@Aspect
public class LoggingAspect {
@Before("execution(* MyService.*(..))")
public void logBefore() {
System.out.println("Logging before method execution");
}
@After("execution(* MyService.*(..))")
public void logAfter() {
System.out.println("Logging after method execution");
}
}
์ด ์ฝ๋์์๋ ์ด๋ ธํ ์ด์ ์ ํตํด ๋ฉ์๋ ์คํ ์ ํ์ ๋ก๊ทธ ๊ธฐ๋ก ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ฉํฉ๋๋ค.
| ๊ตฌํ ๋ฐฉ์ | ์ค๋ช | ์ฅ์ | ๋จ์ |
|---|---|---|---|
| POJO ๊ธฐ๋ฐ AOP ๊ตฌํ | ์์ ์๋ฐ ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ํ๋ก์๋ฅผ ์๋์ผ๋ก ์์ฑํด AOP ๊ตฌํ | ํ๋ ์์ํฌ ๋ ๋ฆฝ์ , ์ ์ฐ์ฑ | ์ค์ ๊ณผ ๊ตฌํ์ด ๋ณต์กํจ |
| Spring API ๊ธฐ๋ฐ AOP ๊ตฌํ | Spring์ด ์ ๊ณตํ๋ ProxyFactoryBean ๋ฑ์ ์ฌ์ฉํด AOP ๊ตฌํ | Spring์ ๊ฐ๋ ฅํ API ์ง์, ๊ฐํธํ ์ค์ | Spring ํ๋ ์์ํฌ์ ์ข ์๋จ |
| Annotation ๊ธฐ๋ฐ AOP ๊ตฌํ | @AspectJ ์คํ์ผ์ ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํด AOP ์ ์ฉ | ์ฝ๋ ๊ฐ๊ฒฐ์ฑ, ์ค์ ํ์ผ ์์กด์ฑ ๊ฐ์ | ๋ณต์กํ ๋ก์ง ๊ตฌํ ์ ์ ์ฐ์ฑ ๋ถ์กฑ |
Spring AOP๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด XML Schema๋ฅผ ์ด์ฉํด AOP ๊ด๋ จ ํ๊ทธ๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก ๋ค์์คํ์ด์ค์ ์คํค๋ง๋ฅผ ์ถ๊ฐํ๋ ๊ณผ์ ์ด ํ์ํฉ๋๋ค.
xmlns:aop: AOP ๊ด๋ จ ํ๊ทธ๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก AOP ๋ค์์คํ์ด์ค๋ฅผ ์ ์ธํฉ๋๋ค.xsi:schemaLocation: AOP ์คํค๋ง ํ์ผ ๊ฒฝ๋ก๋ฅผ ์ง์ ํ์ฌ Spring์ด AOP ์ค์ ์ ํ์ฑํ ์ ์๊ฒ ํฉ๋๋ค.<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
์ค๋ช
:
์ด ์ค์ ์ XML์์ AOP ๊ด๋ จ ํ๊ทธ๋ฅผ ํ์ฑํํ๊ณ , AOP ๊ธฐ๋ฅ์ ์ค์ ํ ์ ์๋๋ก ๋ค์์คํ์ด์ค์ ์คํค๋ง๋ฅผ ์ ์ธํฉ๋๋ค.
AOP ์ค์ ์ Advice, Aspect, Pointcut์ ์ ์ํ์ฌ ํน์ ๋ฉ์๋์ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ฉํ๋ ๊ณผ์ ์ผ๋ก ์ด๋ฃจ์ด์ง๋๋ค. ์ด ์ค์ ์ ํตํด ๋ฉ์๋ ์คํ ์ ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๊ฑฐ๋ ํธ๋์ญ์ ์ฒ๋ฆฌ๋ฅผ ์ถ๊ฐํ ์ ์์ต๋๋ค.
<bean> ํ๊ทธ: loggingAdvice๋ผ๋ ์ด๋ฆ์ผ๋ก ๋ก๊ทธ ๊ธฐ๋ก ๊ธฐ๋ฅ์ ํ๋ Advice๋ฅผ ์ ์ํฉ๋๋ค. LoggingAdvice ํด๋์ค๋ฅผ ํตํด ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ฒ๋ฆฌํฉ๋๋ค.<aop:aspect> ํ๊ทธ: loggingAspect๋ผ๋ Aspect๋ฅผ ์ ์ํ๊ณ , ํน์ ๋ฉ์๋์ ๋ถ๊ฐ ๊ธฐ๋ฅ์ด ์ ์ฉ๋ ์ง์ ์ ์ค์ ํฉ๋๋ค. publicMethods๋ผ๋ Pointcut์ ์ ์ํ์ฌ ํน์ ํจํค์ง์ public ๋ฉ์๋์๋ง ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ฉํฉ๋๋ค.<bean id="loggingAdvice"
class="com.example.aop.LoggingAdvice" />
<aop:config>
<aop:aspect id="loggingAspect" ref="loggingAdvice">
<aop:pointcut id="publicMethods"
expression="execution(public * com.example.service..*.*(..))" />
<aop:before method="logBefore" pointcut-ref="publicMethods" />
</aop:aspect>
</aop:config>
์ค๋ช :
com.example.service ํจํค์ง ๋ด์ public ๋ฉ์๋๊ฐ ์คํ๋ ๋๋ง๋ค ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๋ ๋ถ๊ฐ ๊ธฐ๋ฅ์ด ์ ์ฉ๋ฉ๋๋ค. logBefore ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ๋ฉ์๋ ์คํ ์ ์ ๋ก๊ทธ ๊ธฐ๋ก์ ์ถ๊ฐํฉ๋๋ค.AOP์์ ์ฌ์ฉํ๋ ์ฃผ์ ํ๊ทธ๋ฅผ ์ ์ํ์ฌ Aspect, Pointcut, Advice๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค. ๊ฐ ํ๊ทธ๋ AOP ๊ธฐ๋ฅ์ ๊ตฌํํ๋ ๋ฐ ์ค์ํ ์ญํ ์ ํฉ๋๋ค.
<aop:config>: AOP ์ค์ ์ ๋ฃจํธ ํ๊ทธ๋ก AOP ์ค์ ์ ๋ฌถ์ด์ ๊ด๋ฆฌํฉ๋๋ค.<aop:aspect>: Aspect๋ฅผ ์ ์ํ๋ฉฐ, ํน์ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ฉํ Advice๋ฅผ ์ค์ ํฉ๋๋ค.<aop:pointcut>: ๋ถ๊ฐ ๊ธฐ๋ฅ์ด ์ ์ฉ๋ ๋ฉ์๋๋ ํด๋์ค์ ์ง์ ์ ์ ์ํฉ๋๋ค.<aop:before>: ๋ฉ์๋ ์คํ ์ ์ ์คํ๋ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ํฉ๋๋ค.<aop:after-returning>: ๋ฉ์๋๊ฐ ์ ์์ ์ผ๋ก ์คํ๋ ํ์ ์ ์ฉํ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ํฉ๋๋ค.<aop:after-throwing>: ๋ฉ์๋ ์คํ ์ค ์์ธ๊ฐ ๋ฐ์ํ์ ๋ ์ ์ฉ๋ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ํฉ๋๋ค.<aop:after>: ๋ฉ์๋๊ฐ ์ ์ ์คํ ๋๋ ์์ธ ๋ฐ์๊ณผ ๊ด๊ณ์์ด ์คํ ํ์ ์ ์ฉ๋ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ํฉ๋๋ค.<aop:around>: ๋ฉ์๋ ์ ํ ๋ชจ๋์ ์ ์ฉ๋ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ํฉ๋๋ค.Aspect๋ ์ฌ๋ฌ ๋ถ๊ฐ ๊ธฐ๋ฅ(Advice)์ ํ๋๋ก ๋ฌถ์ด ๊ด๋ฆฌํ๋ฉฐ, Pointcut์ ์ฌ์ฉํด ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ด๋ค ๋ฉ์๋์ ์ ์ฉํ ์ง ์ ์ํฉ๋๋ค.
<aop:aspect> ํ๊ทธ: loggingAspect๋ผ๋ Aspect๋ฅผ ์ ์ํ๊ณ , ๋ก๊ทธ ๊ธฐ๋ฅ์ ํ๋ Advice๋ฅผ ์ฐธ์กฐํฉ๋๋ค. ์ด Aspect๋ publicMethods๋ผ๋ Pointcut์ ํตํด ํน์ ๋ฉ์๋์ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ฉํฉ๋๋ค.ref ์์ฑ: Aspect์์ ์ฐธ์กฐํ ๋ถ๊ฐ ๊ธฐ๋ฅ(Advice)์ bean์ผ๋ก ์ฐ๊ฒฐํฉ๋๋ค.id ์์ฑ: Aspect ํ๊ทธ์ ์๋ณ์๋ก ์ฌ์ฉ๋๋ฉฐ, ์ฌ๋ฌ ๊ฐ์ Pointcut์ ํฌํจํ ์ ์์ต๋๋ค.<aop:config>
<aop:aspect id="loggingAspect" ref="loggingAdvice">
<aop:pointcut id="publicMethods"
expression="execution(public * com.example.service..*.*(..))" />
<aop:before method="logBefore" pointcut-ref="publicMethods" />
</aop:aspect>
</aop:config>
์ค๋ช :
loggingAspect๋ผ๋ Aspect๋ฅผ ํตํด public ๋ฉ์๋์ ๋ก๊ทธ ๊ธฐ๋ก์ ์ ์ฉํ๋ ๊ฒ์
๋๋ค. Pointcut์ ํตํด ์ง์ ๋ public ๋ฉ์๋์์๋ง ๋ถ๊ฐ ๊ธฐ๋ฅ์ด ๋์ํฉ๋๋ค.Pointcut์ ๋ถ๊ฐ ๊ธฐ๋ฅ์ด ์ ์ฉ๋ ์ง์ ์ ์ ์ํฉ๋๋ค. ํน์ ํจํค์ง, ํด๋์ค, ๋ฉ์๋ ๋ฑ์ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ฉํ ์ ์์ผ๋ฉฐ, AOP ํํ์์ ํตํด ์ํ๋ ๋๋ก ์ค์ ํ ์ ์์ต๋๋ค.
<aop:pointcut>: AOP ํํ์์ ์ฌ์ฉํด ๋ถ๊ฐ ๊ธฐ๋ฅ์ด ์ ์ฉ๋ ์ง์ ์ ์ ์ํฉ๋๋ค. execution(public * com.example.service..*.*(..)) ํํ์์ ์ฌ์ฉํ์ฌ public ๋ฉ์๋์๋ง ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ฉํฉ๋๋ค.<aop:pointcut id="publicMethods"
expression="execution(public * com.example.service..*.*(..))" />
์ค๋ช :
com.example.service ํจํค์ง ๋ด public ๋ฉ์๋๋ฅผ ๋์์ผ๋ก AOP ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ฉํ๋ Pointcut์ ์ ์ํ ์ค์ ์
๋๋ค. ์ด๋ฅผ ํตํด ํด๋น ๋ฉ์๋์๋ง ๋ถ๊ฐ ๊ธฐ๋ฅ์ด ์ ์ฉ๋ฉ๋๋ค.Before Advice๋ ๋ฉ์๋ ์คํ ์ ์ ํน์ ๊ณตํต ๊ธฐ๋ฅ์ ์ํํฉ๋๋ค.
์ฃผ์: ๋ง์ฝ Target ๋ฉ์๋์์ Exception(์์ธ)์ด ๋ฐ์ํ๋ฉด, Before Advice๋ ์คํ๋์ง ์์ต๋๋ค.
public class LoggingAdvice {
public void logBefore() {
System.out.println("๋ฉ์๋ ์คํ ์ ๋ก๊ทธ ๊ธฐ๋ก");
}
}
์ค๋ช :
After Returning Advice๋ ๋ฉ์๋๊ฐ ์ ์์ ์ผ๋ก ์คํ๋ ํ ๊ณตํต ๊ธฐ๋ฅ์ ์ ์ฉํฉ๋๋ค. ๋ฉ์๋๊ฐ ์ ์์ ์ผ๋ก ์๋ฃ๋ ํ์๋ง ์คํ๋ฉ๋๋ค.
public class ResultLoggingAdvice {
public void logAfterReturning(Object returnValue) {
System.out.println("๋ฉ์๋๊ฐ ๋ฐํํ ๊ฐ: " + returnValue);
}
}
์ค๋ช :
<aop:aspect id="resultAspect" ref="resultLoggingAdvice">
<aop:pointcut id="serviceMethods" expression="execution(* com.example.service..*(..))" />
<aop:after-returning method="logAfterReturning" pointcut-ref="serviceMethods" returning="resultValue" />
</aop:aspect>
After Throwing Advice๋ ๋ฉ์๋ ์คํ ์ค ์์ธ๊ฐ ๋ฐ์ํ์ ๋ ๊ณตํต ๊ธฐ๋ฅ์ ์ํํฉ๋๋ค. ๋ฉ์๋ ์คํ ์ค ๋ฐ์ํ Exception์ ๋ฐ์์ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
public class ExceptionLoggingAdvice {
public void logAfterThrowing(Exception ex) {
System.out.println("์์ธ ๋ฐ์: " + ex.getMessage());
}
}
์ค๋ช :
<aop:aspect id="exceptionAspect" ref="exceptionLoggingAdvice">
<aop:pointcut id="serviceMethods" expression="execution(* com.example.service..*(..))" />
<aop:after-throwing method="logAfterThrowing" pointcut-ref="serviceMethods" throwing="ex" />
</aop:aspect>
After Advice๋ ๋ฉ์๋๊ฐ ์ ์์ ์ผ๋ก ์คํ๋๋ , ์์ธ๊ฐ ๋ฐ์ํ๋ ์๊ด์์ด ์คํ๋ฉ๋๋ค. Java์ finally ๋ธ๋ก๊ณผ ์ ์ฌํ๊ฒ ๋์ํฉ๋๋ค.
public class AfterMethodAdvice {
public void afterMethod() {
System.out.println("๋ฉ์๋ ์คํ ํ ๋ฌด์กฐ๊ฑด ์คํ๋๋ ๋ก์ง");
}
}
์ค๋ช :
<aop:aspect id="afterAspect" ref="afterMethodAdvice">
<aop:pointcut id="serviceMethods" expression="execution(* com.example.service..*(..))" />
<aop:after method="afterMethod" pointcut-ref="serviceMethods" />
</aop:aspect>
Around Advice๋ ๋ฉ์๋ ์คํ ์ ๊ณผ ํ์ ๋ชจ๋ ์คํ๋๋ Advice์ ๋๋ค. ProceedingJoinPoint๋ฅผ ์ฌ์ฉํ์ฌ ๋ฉ์๋ ์คํ ์์ฒด๋ฅผ ์ ์ดํ ์ ์์ต๋๋ค.
public class TimingAdvice {
public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed(); // ์ค์ ๋ฉ์๋ ์คํ
long endTime = System.currentTimeMillis();
System.out.println("๋ฉ์๋ ์คํ ์๊ฐ: " + (endTime - startTime) + "ms");
return result;
}
}
์ค๋ช :
<aop:aspect id="timingAspect" ref="timingAdvice">
<aop:pointcut id="serviceMethods" expression="execution(* com.example.service..*(..))" />
<aop:around method="aroundMethod" pointcut-ref="serviceMethods" />
</aop:aspect>
JoinPoint๋ Spring AOP์์ ์ค์ํ ๊ฐ๋ ์ผ๋ก, ๋์ ๊ฐ์ฒด(Target Object)์ ๋ํ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ ๊ฐ์ฒด์ ๋๋ค. Aspect๊ฐ ์ ์ฉ๋๋ ๋ฉ์๋์ ์คํ ์์ ์ ํด๋น ๋ฉ์๋์ ๊ด๋ จ๋ ๋ค์ํ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ผ๋ฉฐ, ์ด๋ฅผ ํตํด AOP Advice์์ ๋ฉ์๋ ์คํ์ ์ ์ดํ๊ฑฐ๋, ์ ๋ณด๋ฅผ ๋ก๊น ํ๋ ๋ฑ์ ์์ ์ ํ ์ ์์ต๋๋ค.
JoinPoint๋ org.aspectj.lang ํจํค์ง์ ํฌํจ๋์ด ์์ผ๋ฉฐ, AOP์์ ์ฌ์ฉํ๋ Advice ๋ฉ์๋์ ์ฒซ ๋ฒ์งธ ์ธ์๋ก ์ ๋ฌ๋ฉ๋๋ค. ์ด ๊ฐ์ฒด๋ฅผ ํตํด ๋ฉ์๋ ํธ์ถ๊ณผ ๊ด๋ จ๋ ์ฌ๋ฌ ์ค์ํ ์ ๋ณด๋ฅผ ์ป์ ์ ์์ต๋๋ค.
JoinPoint ํด๋์ค๋ ๋ค์ํ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ฉฐ, ์ด๋ฅผ ํตํด ๋์ ๋ฉ์๋์ ์ ๋ณด๋ฅผ ์กฐํํ ์ ์์ต๋๋ค. ๊ฐ ๋ฉ์๋์ ๊ธฐ๋ฅ์ ์๋์์ ์์ธํ ์ค๋ช ํ๊ฒ ์ต๋๋ค:
UserService ํด๋์ค์ ๋ฉ์๋๋ผ๋ฉด, getTarget() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด UserService ๊ฐ์ฒด๊ฐ ๋ฐํ๋ฉ๋๋ค.saveUser(String name, int age)๋ผ๋ ๋ฉ์๋๊ฐ ํธ์ถ๋์๋ค๋ฉด, getArgs() ๋ฉ์๋๋ ["name", 25]์ ๊ฐ์ ์ธ์ ๋ฐฐ์ด์ ๋ฐํํฉ๋๋ค.UserService ํด๋์ค์ createUser(String name) ๋ฉ์๋๊ฐ ํธ์ถ๋์๋ค๋ฉด, getSignature()๋ ํด๋น ๋ฉ์๋์ ์ด๋ฆ๊ณผ ์๊ทธ๋์ฒ ์ ๋ณด๋ฅผ ๋ฐํํฉ๋๋ค.getSignature().getName()์ ํธ์ถํ๋ฉด "createUser"๊ฐ ๋ฐํ๋ฉ๋๋ค.getSignature().getName()์ ํธ์ถํ๋ฉด, ๋์ ๋ฉ์๋์ ์ด๋ฆ(์: "createUser")์ ๋ฐํํฉ๋๋ค.public void createUser(String name)๋ผ๋ฉด, toLongString()์ public void createUser(String)๊ณผ ๊ฐ์ ๊ฐ์ ๋ฐํํฉ๋๋ค. ์ด๋ฅผ ํตํด ๋ฉ์๋์ ์ ์ฒด์ ์ธ ๊ตฌ์กฐ๋ฅผ ์ฝ๊ฒ ํ์
ํ ์ ์์ต๋๋ค.public void createUser(String name)๋ผ๋ฉด, toShortString()์ createUser()์ฒ๋ผ ์ถ์ฝ๋ ๋ฉ์๋ ์ ๋ณด๋ฅผ ๋ฐํํฉ๋๋ค.AOP์์ JoinPoint๋ ๋ฉ์๋ ์คํ ์ ๋ณด๋ฅผ ํ์ฉํ์ฌ ๋ค์ํ ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์๊ฒ ํด์ค๋๋ค. ์๋ฅผ ๋ค์ด, ๋ก๊ทธ ๊ธฐ๋ก, ๋ฉ์๋ ์คํ ์๊ฐ ์ธก์ , ๋ฉ์๋ ์ธ์ ๊ฐ ํ์ธ ๋ฐ ๊ฒ์ฆ ๋ฑ์ ์์ ์ ๋งค์ฐ ์ ์ฉํ๊ฒ ์ฌ์ฉ๋ฉ๋๋ค.
@Aspect
public class LoggingAspect {
// Before Advice: ๋ฉ์๋๊ฐ ํธ์ถ๋๊ธฐ ์ ์ ์คํ
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("๋ฉ์๋ ์คํ ์ : " + joinPoint.getSignature().getName());
System.out.println("๋ฉ์๋ ์ธ์: " + Arrays.toString(joinPoint.getArgs()));
}
// After Returning Advice: ๋ฉ์๋๊ฐ ์ ์์ ์ผ๋ก ์คํ๋ ํ ์คํ
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
System.out.println("๋ฉ์๋ ์คํ ํ: " + joinPoint.getSignature().getName());
System.out.println("๋ฉ์๋ ๊ฒฐ๊ณผ: " + result);
}
}
getSignature()๋ฅผ ์ด์ฉํด ๋ฉ์๋์ ์ด๋ฆ์ ์ถ๋ ฅํ๊ณ , getArgs()๋ฅผ ์ฌ์ฉํ์ฌ ๋ฉ์๋์ ์ ๋ฌ๋ ์ธ์ ๊ฐ์ ํ์ธํฉ๋๋ค.์ด๋ฌํ ๋ฐฉ์์ผ๋ก JoinPoint๋ฅผ ํ์ฉํ์ฌ ๋ฉ์๋ ์คํ๊ณผ ๊ด๋ จ๋ ์ฌ๋ฌ ์ ๋ณด๋ฅผ ๋ค๋ฃจ๊ณ , ๋ค์ํ ๋ฐฉ์์ผ๋ก ์ ํ๋ฆฌ์ผ์ด์ ์ ๋์์ ์ ์ดํ๊ฑฐ๋ ๋ชจ๋ํฐ๋งํ ์ ์์ต๋๋ค.
1๏ธโฃ ์ค์ ํ์ผ์ ๋ฐ๋์ ์ถ๊ฐํด์ผ ํ๋ ํญ๋ชฉ
Spring์์ @Aspect๋ฅผ ์ฌ์ฉํ์ฌ AOP๋ฅผ ๊ตฌํํ๋ ค๋ฉด ์ค์ ํ์ผ์ ๋ค์ ํญ๋ชฉ์ ๋ฐ๋์ ์ถ๊ฐํด์ผ ํฉ๋๋ค.
<aop:aspectj-autoproxy />
์ด ์ค์ ์ Spring์ด AspectJ ๊ธฐ๋ฐ AOP ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์๋์ผ๋ก ์์ฑํ ์ ์๋๋ก ๋์์ค๋๋ค. ์ด๋ก ์ธํด, AOP ์ค์ ์ด ์๋์ผ๋ก ์ ์ฉ๋์ด ๋ค์ํ Aspect๊ฐ ๋์ํ ์ ์์ต๋๋ค.
2๏ธโฃ Aspect Class๋ฅผ ์ผ๋ก ๋ฑ๋ก
Aspect Class๋ ๋ฐ๋์ Spring์ Bean์ผ๋ก ๋ฑ๋ก๋์ด์ผ ํฉ๋๋ค. ์ด๋ฅผ ํตํด Spring์ด ํด๋น Aspect๋ฅผ ๊ด๋ฆฌํ๊ณ Advice๋ฅผ ์ ์ฉํ ์ ์์ต๋๋ค.
์ด์ @Aspect์ ๋ค์ํ ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ ์ผ๋ฐ์ ์ธ ์์๋ฅผ ์ค๋ช ํด ๋ณด๊ฒ ์ต๋๋ค.
@Aspect
public class LoggingAspect {
// Pointcut: ํน์ ๋ฉ์๋๋ฅผ ๋์์ผ๋ก ํ๋ ์กฐ๊ฑด ์ ์
@Pointcut("execution(public * com.example.service.*.*(..))")
public void logForServiceMethods() {}
// Around Advice: ๋ฉ์๋ ์คํ ์ ํ์ ์ ์ฉ๋๋ Advice
@Around("logForServiceMethods()")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
// ๋ฉ์๋ ์คํ ์ ์์ : ์คํ ์์ ์๊ฐ ๊ธฐ๋ก
long startTime = System.currentTimeMillis();
System.out.println("๋ฉ์๋ ์์: " + joinPoint.getSignature());
// ์ค์ ๋ฉ์๋ ์คํ
Object result = joinPoint.proceed(); // Target ๋ฉ์๋ ์คํ
// ๋ฉ์๋ ์คํ ํ ์์ : ์คํ ์๋ฃ ์๊ฐ ๊ธฐ๋ก ๋ฐ ์คํ ์๊ฐ ์ถ๋ ฅ
long endTime = System.currentTimeMillis();
System.out.println("๋ฉ์๋ ์ข
๋ฃ: " + joinPoint.getSignature());
System.out.println("์คํ ์๊ฐ: " + (endTime - startTime) + "ms");
return result;
}
}
@Aspect: LoggingAspect ํด๋์ค๋ AOP Aspect์์ ์ ์ธํฉ๋๋ค. ์ด ํด๋์ค์๋ Advice์ Pointcut์ด ํฌํจ๋์ด ์์ต๋๋ค.
@Pointcut:
"execution(public * com.example.service.*.*(..))"๋ Pointcut ํํ์์
๋๋ค. ์ด๋ com.example.service ํจํค์ง์ ๋ชจ๋ public ๋ฉ์๋๋ฅผ ๋์์ผ๋ก ํฉ๋๋ค.@Around("logForServiceMethods()"):
ProceedingJoinPoint: