AOP (Aspect-Oriented Programming)

πŸ” κ°œμš”

  • AOPλŠ” λ‘œκΉ…Β·νŠΈλžœμž­μ…˜Β·λ³΄μ•ˆ 같은 νš‘λ‹¨ 관심사λ₯Ό ν•œκ³³(Aspect)으둜 λͺ¨μ•„ 핡심 λΉ„μ¦ˆλ‹ˆμŠ€ 둜직(μ£Όμš” 관심사) μ—μ„œ λΆ„λ¦¬ν•˜λŠ” 기법이닀.
  • Spring AOPλŠ” ν”„λ‘μ‹œ 기반으둜 β€˜λ©”μ„œλ“œ 싀행’ 지점(join point)에 Adviceλ₯Ό μ‚½μž…ν•΄ λ™μž‘ν•œλ‹€.
  • λ‚΄λΆ€ 호좜(Self-invocation)Β·final/private λ©”μ„œλ“œ λ“± ν”„λ‘μ‹œ ν•œκ³„λ₯Ό μ΄ν•΄ν•˜κ³  섀계해야 ν•œλ‹€.

κ°œλ… & λ°°κ²½

OOP(객체 μ§€ν–₯ ν”„λ‘œκ·Έλž˜λ°) μ—μ„œ λͺ¨λ“ˆν™”μ˜ 핡심 λ‹¨μœ„λŠ” ν΄λž˜μŠ€μ΄μ§€λ§Œ, AOPμ—μ„œ λͺ¨λ“ˆν™”μ˜ 핡심 λ‹¨μœ„λŠ” **Aspect(관점)**이닀.

  • νš‘λ‹¨ 관심사(Cross-cutting concerns) : λ‘œκΉ…, νŠΈλžœμž­μ…˜, λ³΄μ•ˆ, λͺ¨λ‹ˆν„°λ§ λ“± μ—¬λŸ¬ λͺ¨λ“ˆμ— 걸쳐 λ°˜λ³΅λ˜λŠ” κΈ°λŠ₯
  • μ£Όμš” 관심사(Core concerns) : μ„œλΉ„μŠ€ 고유의 λΉ„μ¦ˆλ‹ˆμŠ€ 둜직

β†’ 반볡 μ½”λ“œλ₯Ό κ±·μ–΄λ‚΄κ³ , 정책을 μΌκ΄€λ˜κ³  μž¬μ‚¬μš© κ°€λŠ₯ν•˜κ²Œ λ§Œλ“ λ‹€.

  • νš‘λ‹¨ 관심사와 μ£Όμš” 관심사λ₯Ό λΆ„λ¦¬ν•˜μ—¬ κ΄€μ λ³„λ‘œ 각각 κΈ°λŠ₯을 λͺ¨λ“ˆν™” ν•  수 μžˆλ‹€.
  • 섀정을 μΆ”κ°€ν•˜μ—¬ Weaving ν•œλ‹€.

κ²°λ‘ 

AOP λŠ” μŠ€ν”„λ§ 만의 μš©μ–΄λŠ” μ•„λ‹ˆλ©°, νš‘λ‹¨ 관심사(κ³΅ν†΅μ˜ κΈ°λŠ₯을 μ—¬λŸ¬ μ½”λ“œμ— 걸쳐 반볡적으둜 적용)λ₯Ό λͺ¨λ“ˆν™” ν•˜λŠ” κ°œλ…μ„ λ§ν•œλ‹€.

β†’ Spring μ—μ„œλŠ” AOPλ₯Ό κ΅¬ν˜„ν•˜κΈ° μœ„ν•΄ Proxy νŒ¨ν„΄μ„ μ‚¬μš©ν•œλ‹€.


🧩 핡심 μš©μ–΄

μš©μ–΄μ„€λͺ…
Aspect곡톡 κΈ°λŠ₯ λͺ¨λ“ˆ (ex. λ‘œκΉ…, νŠΈλžœμž­μ…˜)
Adviceμ‹€μ œ μˆ˜ν–‰λ˜λŠ” μ½”λ“œ (Before/After/AfterReturning/AfterThrowing/Around)
Join PointAOPλ₯Ό μ μš©ν•  수 μžˆλŠ” 지점 (ex. λ©”μ„œλ“œ μ‹€ν–‰ μ‹œμ )
PointcutAdviceλ₯Ό μ μš©ν•  지점 μ§€μ • (Join Point 쑰건 μ •μ˜)
WeavingAdviceλ₯Ό μ‹€μ œ 객체에 μ—°κ²°ν•˜λŠ” κ³Όμ • (Spring은 λŸ°νƒ€μž„ Weaving)

βœ… AOP 적용 방식

  • ν”„λ‘μ‹œ 기반:
    • μΈν„°νŽ˜μ΄μŠ€κ°€ 있으면 JDK 동적 ν”„λ‘μ‹œ, μ—†μœΌλ©΄ CGLIB μ‚¬μš©
  • μ§€μ›ν•˜λŠ” Join Point: λ©”μ„œλ“œ μ‹€ν–‰
  • ν”„λ‘μ‹œ ν•œκ³„:
    • λ‚΄λΆ€ 호좜(self-invocation) 은 ν”„λ‘μ‹œ 우회 β†’ AOP 미적용
    • final/private λ©”μ„œλ“œ, μƒμ„±μž, ν•„λ“œ μ ‘κ·Ό 등은 적용 λΆˆκ°€
  • μ—¬λŸ¬ Aspect의 μˆœμ„œλŠ” @Order ν˜Ήμ€ Ordered둜 μ œμ–΄

Spring AOPλŠ” λ©”μ„œλ“œ μ‹€ν–‰ μ‹œμ μ˜ Join Point만 지원


🧰 μ£Όμš” μ–΄λ…Έν…Œμ΄μ…˜

@Aspect            // Aspect μ •μ˜ 클래슀
@Before            // λ©”μ„œλ“œ μ‹€ν–‰ μ „ μ‹€ν–‰
@AfterReturning    // 정상 λ°˜ν™˜ ν›„ μ‹€ν–‰
@AfterThrowing     // μ˜ˆμ™Έ λ°œμƒ μ‹œ μ‹€ν–‰
@After             // λ©”μ„œλ“œ μ’…λ£Œ ν›„ (성곡/μ‹€νŒ¨ 무관)
@Around            // λ©”μ„œλ“œ μ‹€ν–‰ μ „/ν›„ λͺ¨λ‘ μ œμ–΄
@Pointcut          // 포인트컷 μ •μ˜

🎯 포인트컷 ν‘œν˜„μ‹ μ˜ˆμ‹œ

ν‘œν˜„μ‹μ˜λ―Έ
execution(* com.example..*.*(..))λͺ¨λ“  λ©”μ„œλ“œ
execution(* *..Service.*(..))Service 클래슀 λ‚΄ λͺ¨λ“  λ©”μ„œλ“œ
within(com.example.controller..*)νŠΉμ • νŒ¨ν‚€μ§€ ν•˜μœ„ 클래슀 전체

πŸ”§ μ˜ˆμ‹œ

(A) 간단 λ‘œκΉ…

@Aspect
@Component
public class LoggingAspect {
 
    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("호좜된 λ©”μ„œλ“œ: " + joinPoint.getSignature().getName());
    }
}

(B) μ‹€ν–‰ μ‹œκ°„ μΈ‘μ •(@Around)

@Aspect
@Component
public class TimingAspect {
  @Around("execution(* com.example..*Service.*(..))")
  public Object measure(ProceedingJoinPoint pjp) throws Throwable {
    long start = System.nanoTime();
    try {
      return pjp.proceed();
    } finally {
      long elapsedMs = (System.nanoTime() - start) / 1_000_000;
      System.out.println(pjp.getSignature() + " took " + elapsedMs + "ms");
    }
  }
}

좔후에 ν•™μŠ΅ν•  Filter 와 Interceptor 도 이와 λΉ„μŠ·ν•œ κ°œλ…

β†’ κ³΅ν†΅μ˜ 관심사λ₯Ό λΆ„λ¦¬ν•˜μ—¬ μ²˜λ¦¬ν•œλ‹€λŠ” μ μ—μ„œ λΉ„μŠ·ν•˜μ§€λ§Œ, λ™μž‘ μ‹œμ κ³Ό 적용 λŒ€μƒμ— 차이가 있음 μ°Έκ³  : Spring AOP vs Filter vs Interceptor

κ΅¬λΆ„μ μš© λ ˆμ΄μ–΄μ£Ό μ‚¬μš©μ²˜νŠΉμ§•
Filterμ„œλΈ”λ¦Ώ μ»¨ν…Œμ΄λ„ˆκ³΅ν†΅ λ³΄μ•ˆ/인코딩/λ‘œκΉ…URL λ‹¨μœ„, μŠ€ν”„λ§ μ™ΈλΆ€ 계측
InterceptorμŠ€ν”„λ§ MVC컨트둀러 μ „/ν›„ μ²˜λ¦¬ν•Έλ“€λŸ¬ μ „ν›„, λ·° λ Œλ” μ „
AOPμŠ€ν”„λ§ 빈 λ ˆλ²¨μ„œλΉ„μŠ€/리포지토리 λ‹¨λ©”μ„œλ“œ μ‹€ν–‰ λ‹¨μœ„, ν”„λ‘μ‹œ 기반

μ…‹ λͺ¨λ‘ β€œκ³΅ν†΅ 관심사 뢄리” λͺ©μ μ΄μ§€λ§Œ 적용 μ‹œμ κ³Ό λ²”μœ„κ°€ 닀름.


⚠️ μ£Όμ˜ν•  점

  • λ‚΄λΆ€ 호좜: 같은 객체 λ‚΄λΆ€μ—μ„œ this.method() 호좜 μ‹œ AOP 미적용 β†’ ꡬ쑰 뢄리/자기참쑰 μ£Όμž…(@Lazy self) λ“±μœΌλ‘œ 우회
  • ν”„λ‘μ‹œ λΆˆκ°€ λŒ€μƒ: final 클래슀/λ©”μ„œλ“œ, private λ©”μ„œλ“œ, μƒμ„±μž
  • @Transactional도 ν”„λ‘μ‹œ 기반이라 λ‚΄λΆ€ ν˜ΈμΆœμ—μ„  λ™μž‘ μ•ˆ 함
  • μ„±λŠ₯: κ³Όλ„ν•œ 포인트컷/μ–΄λΌμš΄λ“œλŠ” μ˜€λ²„ν—€λ“œ β†’ λ²”μœ„ μ΅œμ†Œν™”, ν•„μš” μ§€μ λ§Œ 적용
  • ν…ŒμŠ€νŠΈ: @SpringBootTestμ—μ„œ Aspect ν™œμ„±ν™”, λ‹¨μœ„ ν…ŒμŠ€νŠΈλŠ” 포인트컷 μ΅œμ†Œν™”λ‘œ μž¬ν˜„μ„± 확보

πŸ”— κ΄€λ ¨

  • IoC β€” μ»¨ν…Œμ΄λ„ˆκ°€ 객체 생λͺ…μ£ΌκΈ°/연결을 관리
  • DI β€” μ˜μ‘΄μ„± 연결을 μ£Όμž…μœΌλ‘œ ν•΄κ²°
  • external-configuration β€” ν™˜κ²½/ν”„λ‘œν•„ 기반 μ„€μ •
  • Spring AOP vs Filter vs Interceptor - 각 κΈ°λŠ₯ νŠΉμ„± 비ꡐ