[Day 26 | Spring] AOP (Aspect-Oriented Programming) 예제 코드

y♡ding·2024년 11월 18일

데브코스 TIL - Spring

목록 보기
6/46

AOP (Aspect-Oriented Programming)

AOP는 문제를 바라보는 관점을 기준으로 프로그래밍하는 기법입니다. 이를 통해 핵심 관심 사항공통 관심 사항을 분리하여 각각의 역할에 집중할 수 있습니다. 예를 들어, 로깅, 보안, 트랜잭션 관리 등 여러 메서드나 클래스에 걸쳐 반복적으로 발생하는 "공통 관심 사항"을 분리하여 코드를 간결하고 유지보수하기 쉽게 만듭니다.


AOP 주요 개념

  1. 핵심 관심 사항 (Core Concern)

    • 비즈니스 로직이나 애플리케이션의 주요 동작을 구현하는 코드입니다.
    • 예: 데이터베이스 작업, 서비스의 주요 기능 등.
  2. 공통 관심 사항 (Cross-Cutting Concern)

    • 여러 모듈에 공통적으로 사용되는 로직을 의미합니다.
    • 예: 로깅, 보안, 성능 모니터링, 트랜잭션 관리.
  3. 횡단 관심사의 분리 (Separation of Cross-Cutting Concern)

    • 공통 관심 사항을 별도의 모듈로 분리하여 독립적으로 관리함으로써 코드의 응집도를 높이고 중복을 줄입니다.
  4. 위빙 (Weaving)

    • 공통 관심 사항을 핵심 관심 사항에 적용하는 과정입니다.
    • 컴파일 타임, 클래스 로드 타임, 또는 런타임에 수행됩니다.

AOP 구성 요소

  1. Advice (보조 작업)

    • 특정 시점에 실행될 동작을 정의합니다.
    • 종류:
      • @Before: 메서드 실행 이전 실행.
      • @After: 메서드 실행 이후 실행.
      • @Around: 메서드 실행 전/후 실행.
      • @AfterReturning: 정상적으로 메서드 실행 후 동작.
      • @AfterThrowing: 메서드 실행 중 예외 발생 시 동작.
  2. JoinPoint (합류 지점)

    • Advice가 실행될 수 있는 지점을 의미합니다.
    • 예: 특정 메서드 호출, 생성자 호출 등.
  3. Pointcut (지점 설정)

    • Advice가 적용될 JoinPoint를 필터링합니다.
    • 표현식(execution, within, bean)으로 정의됩니다.
  4. Aspect (관점)

    • 여러 Advice와 Pointcut을 조합한 모듈입니다.
    • 공통 관심 사항을 캡슐화합니다.
  5. Target (대상)

    • Advice가 적용될 실제 객체.
  6. Proxy (프록시)

    • AOP가 동작하도록 중간에 생성된 객체.
    • Spring에서는 JDK Dynamic Proxy(인터페이스 기반)와 CGLIB Proxy(클래스 기반)를 지원합니다.

Spring AOP 설정 및 구현

1. Spring AOP 의존성 추가

Maven Repository에서 spring-boot-starter-aop를 추가합니다.

2. AOP 활성화

  • application.properties에서 AOP 관련 설정:
    spring.aop.auto=true
    spring.aop.proxy-target-class=true
    • proxy-target-class=true: CGLIB 프록시 사용.
    • proxy-target-class=false: JDK Dynamic Proxy 사용.

3. 사용할 인터페이스 및 클래스 구현

// 인터페이스
public interface Target {
    String sayEcho( String name );
}

// 클래스
import org.springframework.stereotype.Service;

@Service( "target" )
public class TargetService implements Target {

    // 핵심 사항
    @Override
    public String sayEcho(String name) {
        //System.out.println( "전처리" );
        System.out.println( "sayEcho(String name) 호출" );
        //System.out.println( "후처리" );
        return "Hi " + name;
    }
}

4. Pointcut과 Advice 구현

  • execution 표현식을 사용하여 특정 메서드 패턴을 설정
  • 전처리/후처리 로직 추가
package org.example.config;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.Configuration;

@Configuration
@Aspect
public class BasicAdvice1 {

    @Pointcut( "execution(public * say*(..))" )
    public void myTarget() {
    }

    @Around( "myTarget()" )
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        Object obj = null;

        System.out.println( "전처리 구간" );

        // 핵심 사항 실행
        obj = joinPoint.proceed();

        System.out.println( "후처리 구간" );

        return obj;
    }
}

메인 클래스

package org.example;

import org.example.model.Target;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class Di02Application implements CommandLineRunner {

    @Autowired
    private ApplicationContext ctx;

    public static void main(String[] args) {
        SpringApplication.run(Di02Application.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        Target target = ctx.getBean("target", Target.class);
        System.out.println(target.sayEcho("홍길동"));
    }
}

성능 측정 예제 (@Around 사용)

특정 메서드의 실행 속도를 측정하는 AOP 구현:

@Aspect
@Configuration
public class PerformanceAdvice {
    @Pointcut("execution(public * say*(..))")
    public void myTarget() {}

    @Around("myTarget()")
    public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.nanoTime();
        Object result = joinPoint.proceed(); // 핵심 로직 실행
        long endTime = System.nanoTime();
        System.out.println("메서드 실행 시간: " + (endTime - startTime) + "ns");
        return result;
    }
}

실습 예제: 특정 메서드만 AOP 적용

  • 목표:
    WriteAction의 실행 전처리와 ListAction의 실행 후처리를 적용.
@Aspect
@Configuration
public class BasicAdvice {
    @Before("bean(*write*)")
    public void before(JoinPoint joinPoint) {
        System.out.println("전처리: " + joinPoint.getSignature().getName());
    }

    @After("bean(*list*)")
    public void after(JoinPoint joinPoint) {
        System.out.println("후처리: " + joinPoint.getSignature().getName());
    }
}
  • 실행 클래스:
@SpringBootApplication
public class AopApplication implements CommandLineRunner {
    @Autowired
    private ApplicationContext ctx;

    public static void main(String[] args) {
        SpringApplication.run(AopApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        WriteAction writeAction = ctx.getBean("write", WriteAction.class);
        writeAction.execute(); // 전처리 적용

        ListAction listAction = ctx.getBean("list", ListAction.class);
        listAction.execute(); // 후처리 적용
    }
}

AOP의 주요 활용 사례

  1. 로깅: 메서드 호출 이력 및 결과를 기록.
  2. 트랜잭션 관리: 데이터베이스 트랜잭션의 자동 관리.
  3. 보안: 권한 검사와 접근 제어.
  4. 성능 모니터링: 메서드의 실행 시간 측정.

AOP는 핵심 로직에 영향을 주지 않으면서 공통된 요구 사항을 처리할 수 있어, 코드의 재사용성과 유지보수성을 크게 향상시킵니다.

0개의 댓글