AOP

μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜ 전체에 μ‚¬μš©λ˜λŠ” κΈ°λŠ₯을 μž¬μ‚¬μš© ν•˜λ„λ‘ 지원 ν•˜λŠ”κ²ƒμ΄λ‹€.
μ—¬κΈ°μ„œμ˜ 관점은 기쑴의 μžλ°”λ₯Ό κ°œλ°œν–ˆλ˜ 핡심 λͺ¨λ“ˆμ˜ λŒ€ν•œ 관점 μ—μ„œ λΆ€κ°€κΈ°λŠ₯의 관점 으둜 λ°”κΎΈμ–΄ ν”„λ‘œκ·Έλž˜λ° ν•˜λŠ” 것이닀.

μ΄λŠ” μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ κ°œλ°œμ„ μœ„ν•΄ λΉ„μ§€λ‹ˆμŠ€ 둜직과 인프라 둜직이 μžˆλŠ”λ° 이 λ‘˜μ˜ 차이점은 λ‹€μŒκ³Ό κ°™λ‹€.

  • λΉ„μ§€λ‹ˆμŠ€ 둜직 : λ°μ΄ν„°μ˜ μƒνƒœκ°’μ„ μ‘°μž‘ν•˜λŠ” 둜직
  • 인프라 둜직 : κΆŒν•œ 체크, νŠΈλžœμ μ…˜, λ‘œκΉ… λ“± λΉ„μ§€λ‹ˆμŠ€ 둜직의 싀행을 μœ„ν•œ 기반 둜직

AOP λŠ” 이 인프라 둜직의 쀑볡성을 μ œκ±°ν•˜λŠ”κ²ƒμ— λͺ©ν‘œ λ₯Ό 두고 μžˆλ‹€.

μ΄λŠ” κ΄€μ‹¬μ˜ 뢄리 (Seperation of Concerns) λ₯Ό ν†΅ν•˜μ—¬ 핡심 관심 사항에 집쀑 ν•˜λŠ”κ²ƒμ΄λ‹€.

κΈ°λŠ₯을 핡심 λΉ„μ§€λ‹ˆμŠ€ 둜직과 곡톡 λͺ¨λ“ˆλ‘œ λΆ„λ¦¬ν•˜κ³ , 핡심 λ‘œμ§μ— 영ν–₯을 λ―ΈμΉ˜μ§€ μ•Šκ³  사이사이에 곡톡 λͺ¨λ“ˆμ„ 효과적으둜 잘 λΌμ›Œ 넣도둝 μ½”λ“œλ°–μ—μ„œ μ„€μ •λœλ‹€λŠ” 것이 핡심이닀.

ν”„λ‘œκ·Έλž¨ νŒŒμ•…μ΄ νž˜λ“€κΈ° λ•Œλ¬Έμ— AOP 의 μ‚¬μš©μ΄ λ§Žμ„ κ²½μš°μ—λŠ” μœ μ§€λ³΄μˆ˜μ˜ 관리적인 츑면이 어렀움이 λ§Žλ‹€.

AOP νš‘λ‹¨λΆ„λ¦¬

νŠΉμ§•

μž₯점

  • μ€‘λ³΅λ˜λŠ” μ½”λ“œ 제거
  • 효율적인 μœ μ§€λ³΄μˆ˜
  • 높은 생산성
  • μž¬ν™œμš©μ„± κ·ΉλŒ€ν™”
  • μœ μ—°ν•œ λ³€ν™” 수용

단점

  • AOP κ°€ 많으면 관리가 μ–΄λ ΅λ‹€.

μ£Όμš”κ°œλ…

  • Aspect
    • μ—¬λŸ¬ 객체에 κ³΅ν†΅μœΌλ‘œ μ μš©λ˜λŠ” νš‘λ‹¨ 관심 λͺ¨λ“ˆμ„ μ •μ˜
  • Joinpoint
    • Advice λ₯Ό 적용 κ°€λŠ₯ν•œ μ§€μ μœΌλ‘œ Method λ₯Ό 호좜
    • ν•„λ“œ κ°’ 변경이 ν•΄λ‹Ή
    • Spring μ—μ„œλŠ” Method 호좜만 지원
  • Interceptor
    • Interceptor Chain λ°©μ‹μ˜ AOP νˆ΄μ—μ„œ μ‚¬μš©λ˜λŠ” μš©μ–΄λ‘œμ„œ 주둜 ν•œκ°œμ˜ 호좜 Method λ₯Ό κ°€μ§€λŠ” Advice
  • Advice
    • μ–Έμ œ, μ–΄λ–€ 곡톡 관심 κΈ°λŠ₯을 핡심 관심 λͺ¨λ“ˆμ— μ μš©ν•  지λ₯Ό μ •μ˜
  • Weaving
    • Advice λ₯Ό 핡심 관심 λͺ¨λ“ˆμ— μ μš©ν•˜λŠ” 것을 μ •μ˜
  • Pointcut
    • νš‘λ‹¨ 관심 λͺ¨λ“ˆμ΄ 적용될 λ©”μ†Œλ“œλ₯Ό μ„ μ •ν•˜λŠ” λ°©λ²•μœΌλ‘œ μŠ€ν”„λ§μ—μ„œλŠ” μ •κ·œ ν‘œν˜„μ‹μ΄λ‚˜ Aspect J 문법을 톡해 μ •μ˜

Advice 의 μ’…λ₯˜

  • Before
    • λŒ€μƒ 객체의 Method 호좜 μ „ μ‹€ν–‰
  • After Returning
    • λŒ€μƒ 객체가 Exception 없이 μ •μƒμ μœΌλ‘œ μ‹€ν–‰λœ ν›„ μ‹€ν–‰
  • After Throwing
    • λŒ€μƒ 객체의 Exception κ³Ό 상관없이 μ‹€ν–‰
    • finally 의 κΈ°λŠ₯κ³Ό λΉ„μŠ·ν•¨
  • Around
    • λŒ€μƒ 객체의 λ©”μ„œλ“œ 호좜 μ „, ν›„ λ˜λŠ” Exception λ°œμƒ μ‹œμ μ— μ‹€ν–‰

적용

Weaving 방식

Advice λ₯Ό Weaving ν•˜λŠ” λ°©μ‹μ—λŠ” 세가지 방식이 μ‘΄μž¬ν•œλ‹€.

  1. μ»΄νŒŒμΌμ‹œμ— Weaving ν•˜κΈ°
  2. 클래슀 λ‘œλ”© μ‹œμ— Weaving ν•˜κΈ°
  3. λŸ°νƒ€μž„μ‹œμ— Weaving ν•˜κΈ°

Spring AOP λž€?

Spring AOP

μŠ€ν”„λ§μ€ 자체적으둜 ν”„λ‘μ‹œ 기반의 AOP λ₯Ό μ§€μ›ν•˜κ³  μžˆλ‹€.

μŠ€ν”„λ§ AOP λŠ” λ©”μ„œλ“œ 호좜 JoinPoint 만 μ§€μ›ν•œλ‹€.

μŠ€ν”„λ§μ€ 3가지 λ°©μ‹μ˜ AOP λ₯Ό μ§€μ›ν•œλ‹€.

  1. XML μŠ€ν‚€λ§ˆ 기반의 POJO 클래슀λ₯Ό μ΄μš©ν•œ AOPκ΅¬ν˜„
  2. AspectJ μ—μ„œ μ •μ˜ν•œ @Aspect μ–΄λ…Έν…Œμ΄μ…˜ 기반의 AOP κ΅¬ν˜„
  3. μŠ€ν”„λ§ API λ₯Ό μ΄μš©ν•œ AOP κ΅¬ν˜„

ν”„λ‘μ‹œ 객체λ₯Ό μƒμ„±ν•˜λŠ” 방식은 λŒ€μƒ 객체가 μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜κ³  μžˆλŠ”μ§€ 여뢀에 λ”°λΌμ„œ 2가지 λ°©μ‹μœΌλ‘œ λ‚˜λ‰©λ‹ˆλ‹€.

  • JDK Dynamic Proxy
    • μžλ°”μ˜ λ¦¬ν”Œλ ‰μ…˜ API λ₯Ό μ œκ³΅ν•˜λŠ” java.lang.reflect.Proxy λ₯Ό μ΄μš©ν•˜μ—¬ ν”„λ‘μ‹œ 객체λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
    • μΈν„°νŽ˜μ΄μŠ€λ₯Ό 기반으둜 ν”„λ‘μ‹œ 객체λ₯Ό μƒμ„±ν•˜κΈ° λ•Œλ¬Έμ— μΈν„°νŽ˜μ΄μŠ€μ— μ •μ˜λ˜μ§€ μ•ŠλŠ” λ©”μ„œλ“œμ— λŒ€ν•΄μ„œλŠ” AOP κ°€ μ μš©λ˜μ§€ μ•ŠλŠ” 점에 μœ μ˜ν•΄μ•Ό ν•œλ‹€.
  • CGLIB (Code Generation Library)
    • λŒ€μƒ 객체가 μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜κ³  μžˆμ§€ μ•Šκ³  λ°”λ‘œ 클래슀둜 μ‚¬μš©ν•  λ•Œ ν”„λ‘μ‹œ 객체λ₯Ό 생성
    • λŒ€μƒ 클래슀λ₯Ό λ°”λ‘œ 상속 λ°›μ•„ ν”„λ‘μ‹œλ₯Ό κ΅¬ν˜„ν•œλ‹€.
    • ν΄λž˜μŠ€κ°€ final 인 κ²½μš°μ—λŠ” ν”„λ‘μ‹œλ₯Ό 생성할 수 μ—†λ‹€.

Spring Boot AOP 적용

package com.square.common.advice;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Order(0)
public class LoggerAdvice {
  
  private static final Logger logger = LoggerFactory.getLogger(LoggerAdvice.class);

  @Before("execution(* com.square..controller.*Controller.*(..))")
  public void logServiceAccessBefore(JoinPoint joinPoint) throws Throwable{
    logger.info("Logger Advice Before");
  }

  @After("execution(* com.square..controller.*Controller.*(..))")
  public void logServiceAccessAfter(JoinPoint joinPoint) throws Throwable{
    logger.info("Logger Advice After");
  }

  @AfterReturning(pointcut="execution(* com.square..controller.*Controller.*(..))", returning="str")
  public void logServiceAccessAfterReturning(JoinPoint joinPoint, Object str) throws Throwable{
    logger.info("Returning is " + str);

    logger.info("Logger Advice AfterReturning");
  }

  @Pointcut("execution(* com.square..controller.*Controller.*(..))")
  public void logServiceAccessPointcut() {}

  @Before("logServiceAccessPointcut()")
  public void logServiceAccessPointcutBefore(JoinPoint joinPoint) {
    logger.info("[Pointcut] Logger Advice Before");
  }
  
  @After("logServiceAccessPointcut()")
  public void logServiceAccessPointcutAfter(JoinPoint joinPoint) {
    logger.info("[Pointcut] Logger Advice After");
  }
  
  @AfterReturning(value="logServiceAccessPointcut()", returning="str")
  public void logServiceAccessPointcutAfterReturning(JoinPoint joinPoint, Object str) {
    logger.info("[Pointcut] Returning is " + str);

    logger.info("[Pointcut] Logger Advice AfterReturning");
  }

  @Around("execution(* com.square..controller.*Controller.*(..))")
  public Object logServiceAccessPointcutAround(ProceedingJoinPoint processedJoinPoint) throws Throwable {
    logger.info("[Pointcut] Logger Advice Around Before");

    Object obj = processedJoinPoint.proceed();

    logger.info("[Pointcut] Logger Advice Around After");

    return obj;
  }
}

AOP 적용 λŒ€μƒ νŽ˜μ΄μ§€λ₯Ό ν˜ΈμΆœν•˜λ©΄ λ‹€μŒκ³Ό 같이 λ‘œκ·Έκ°€ λ‚¨λŠ”λ‹€.

[Pointcut] Logger Advice Around Before
Logger Advice Before
[Pointcut] Logger Advice Before
##
# 컨트둀러 둜직 처리
##
[Pointcut] Logger Advice Around After
Logger Advice After
[Pointcut] Logger Advice After
Returning is index
Logger Advice AfterReturning
[Pointcut] Returning is index
[Pointcut] Logger Advice AfterReturning