목차
Spring Framework AOP (Aspect-Oriented Programming) 상세 가이드
1. AOP (Aspect-Oriented Programming) 개요
AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)는 소프트웨어 개발에서 핵심 로직(Core Logic)과 공통 기능(Cross-Cutting Concerns)을 분리하여 코드의 모듈성을 향상시키는 프로그래밍 패러다임이다. 이를 통해 코드 중복을 줄이고 유지보수를 쉽게 만들 수 있다.
2. AOP 주요 개념
AOP를 이해하기 위해 몇 가지 핵심 개념을 살펴보자.
- Aspect (관점): 공통적인 기능을 모듈화한 객체로, 여러 클래스에서 공통으로 사용되는 기능(예: 로깅, 트랜잭션 관리 등)을 정의한다.
- Join Point (조인 포인트): 실행 중에 Aspect가 적용될 수 있는 지점(메서드 실행, 예외 처리 등)을 의미한다.
- Advice (어드바이스): 특정 Join Point에서 실행될 코드로, Aspect가 수행할 동작을 정의한다.
- Pointcut (포인트컷): 어떤 Join Point에서 Advice를 적용할지 결정하는 표현식이다.
- Weaving (위빙): Advice를 대상 객체(Target Object)에 적용하는 과정으로, 컴파일, 클래스 로딩, 런타임 중에 수행될 수 있다.
- Target Object (대상 객체): AOP의 적용을 받는 객체이다.
- Proxy (프록시): AOP가 적용된 후 생성되는 객체로, 실제 대상 객체의 기능을 확장하여 Advice를 실행한다.
3. 주요 용어 및 어노테이션 설명
Spring AOP에서는 다양한 어노테이션을 제공하여 AOP를 쉽게 적용할 수 있도록 한다.
- @Aspect : 해당 클래스가 AOP의 Aspect(관점)임을 선언하는 어노테이션이다. 이 클래스 내에서 여러 Advice를 정의할 수 있다.
@Aspect
@Component
public class LoggingAspect {
// Advice 정의 가능
}
- @Pointcut : 특정 Join Point를 지정하는 표현식을 정의하는 어노테이션이다. 이를 활용하여 다양한 메서드 실행 패턴을 지정할 수 있다.
@Pointcut("execution(* com.example.service..*(..))")
public void serviceMethods() {}
위 코드는 com.example.service 패키지 내의 모든 메서드 실행을 Pointcut으로 설정한다.
- @Before : 특정 Join Point가 실행되기 전에 Advice를 실행하도록 지정하는 어노테이션이다.
@Before("execution(* com.example.service..*(..))")
public void beforeAdvice() {
System.out.println("메서드 실행 전에 수행됩니다.");
}
위 코드에서 beforeAdvice 메서드는 서비스 메서드 실행 전에 호출된다.
- @After : 특정 Join Point 실행 후 실행될 Advice를 정의하는 어노테이션이다.
@After("execution(* com.example.service..*(..))")
public void afterAdvice() {
System.out.println("메서드 실행 후 수행됩니다.");
}
위 코드에서 afterAdvice 메서드는 서비스 메서드 실행이 끝난 후 실행된다.
- @AfterReturning : 메서드가 정상적으로 실행된 후 실행될 Advice를 정의하는 어노테이션이다.
@AfterReturning(pointcut = "execution(* com.example.service..*(..))", returning = "result")
public void afterReturningAdvice(Object result) {
System.out.println("메서드가 정상적으로 실행된 후 수행됩니다. 결과: " + result);
}
위 코드에서 afterReturningAdvice 메서드는 대상 메서드가 예외 없이 실행된 후 실행되며, 결과 값을 받아서 사용할 수도 있다.
- @AfterThrowing : 예외가 발생했을 때 실행될 Advice를 정의하는 어노테이션이다.
@AfterThrowing(pointcut = "execution(* com.example.service..*(..))", throwing = "ex")
public void afterThrowingAdvice(Exception ex) {
System.out.println("예외가 발생했습니다: " + ex.getMessage());
}
위 코드에서 afterThrowingAdvice 메서드는 대상 메서드에서 예외가 발생했을 때 실행된다.
- @Around : 메서드 실행 전후에 Advice를 실행하도록 지정하는 어노테이션으로, 메서드 실행을 제어할 수도 있다.
@Around("execution(* com.example.service..*(..))")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
System.out.println(joinPoint.getSignature() + " 실행 시간: " + (end - start) + "ms");
return result;
}
aroundAdvice 메서드는 메서드 실행 전후의 시간을 측정하고 실행 시간을 출력한다. 또한 joinPoint.proceed()를 호출하여 실제 대상 메서드를 실행할 수도 있다.
4. AOP의 특징
AOP를 사용하면 다음과 같은 장점을 얻을 수 있다.
- 관심사의 분리 (Separation of Concerns): 핵심 비즈니스 로직과 공통 기능을 분리하여 코드 가독성을 향상시킨다.
- 코드 중복 감소: 로깅, 보안, 트랜잭션 관리 등의 공통 기능을 중앙에서 관리할 수 있어 코드 중복을 줄일 수 있다.
- 유지보수 용이성: 공통 기능이 한 곳에서 관리되므로 유지보수가 쉽다.
- 비침투성 (Non-Intrusiveness): 기존 코드에 최소한의 변경만으로 AOP를 적용할 수 있다.
5. AOP 예제 및 예제 상세 설명
AOP를 실제 프로젝트에 적용하는 방법을 예제와 함께 상세히 설명한다.
5.1. AOP 설정 및 예제 코드
다음은 Spring AOP를 사용하여 서비스 메서드 실행 전에 로그를 남기는 예제이다.
5.1.1. 의존성 추가 (Spring Boot 기준)
먼저, spring-boot-starter-aop 의존성을 추가해야 한다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
5.1.2. 서비스 클래스 생성
비즈니스 로직을 담당하는 UserService 클래스를 만든다.
@Service
public class UserService {
public void getUserById(Long id) {
System.out.println("사용자 ID: " + id);
}
}
5.1.3. AOP Aspect 클래스 생성
이제 LoggingAspect를 만들어 메서드 실행 전에 로그를 출력하도록 한다.
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.UserService.getUserById(..))")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println("[로그] 메서드 실행: " + joinPoint.getSignature().getName());
}
}
5.2 실행 및 결과 확인
Spring Boot 애플리케이션을 실행한 후 UserService의 getUserById() 메서드를 호출하면 다음과 같은 출력이 나타난다.
[로그] 메서드 실행: getUserById
사용자 ID: 1
5.3 @Around 어노테이션을 활용한 실행 시간 측정
메서드 실행 시간을 측정하려면 @Around 어노테이션을 사용할 수 있다.
@Aspect
@Component
public class ExecutionTimeAspect {
@Around("execution(* com.example.service..*(..))")
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
System.out.println(joinPoint.getSignature() + " 실행 시간: " + (end - start) + "ms");
return result;
}
}
위 코드를 적용하면, UserService의 메서드 실행 시간이 자동으로 측정된다.
5.4 예외 처리 예제 (@AfterThrowing 활용)
메서드 실행 중 예외가 발생했을 때 로깅하는 AOP를 추가할 수도 있다.
@Aspect
@Component
public class ExceptionHandlingAspect {
@AfterThrowing(pointcut = "execution(* com.example.service..*(..))", throwing = "ex")
public void logException(JoinPoint joinPoint, Exception ex) {
System.out.println("[예외 발생] 메서드: " + joinPoint.getSignature().getName() + ", 메시지: " + ex.getMessage());
}
}
5.5 트랜잭션 관리와 AOP 적용 예제
AOP는 트랜잭션 관리에도 사용할 수 있다.
@Aspect
@Component
public class TransactionAspect {
@Around("execution(* com.example.service..*(..))")
public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("트랜잭션 시작");
try {
Object result = joinPoint.proceed();
System.out.println("트랜잭션 커밋");
return result;
} catch (Exception ex) {
System.out.println("트랜잭션 롤백");
throw ex;
}
}
}
위 AOP를 적용하면, 메서드 실행 전후로 트랜잭션을 관리할 수 있다.
6. AOP 적용 시 주의사항
AOP 적용 시 몇 가지 고려해야 할 사항이 있다.
- AOP는 Spring 프록시 기반으로 동작하므로, 프록시 객체를 통해서만 Advice가 적용된다.
- 같은 클래스 내에서 직접 메서드를 호출하면 AOP가 적용되지 않는다.
- Spring AOP는 기본적으로 메서드 실행(Join Point)만 지원한다.
- 필드 접근, 생성자 호출 등의 Join Point는 지원하지 않는다.
- @Around Advice는 반드시 joinPoint.proceed()를 호출해야 한다.
- 호출하지 않으면 원래 메서드가 실행되지 않는다.
- Pointcut 표현식의 범위를 정확하게 지정해야 한다.
- 잘못된 설정으로 인해 원치 않는 메서드에 AOP가 적용될 수 있다.
- 과도한 사용을 피하라.
- AOP는 강력한 기능이지만, 남용하면 디버깅이 어려워질 수 있다
- Pointcut 범위를 신중하게 설정하라.
- 불필요한 메서드에도 Advice가 적용되지 않도록 정확한 범위를 지정해야 한다.
- Proxy 기반 한계를 이해하라.
- Spring AOP는 기본적으로 JDK 동적 프록시 또는 CGLIB을 사용하며, final 메서드에는 적용되지 않는다
Spring AOP는 로깅, 보안, 트랜잭션 관리 등 공통 관심사를 쉽게 분리하여 코드의 유지보수성과 확장성을 높여준다.
Spring AOP를 활용하면 코드의 모듈성을 향상시키고 공통 기능을 효율적으로 관리할 수 있다. 적절한 AOP 적용을 통해 유지보수성을 높이고, 개발 생산성을 향상시킬 수 있다.
'이직&취업 > Spring Framework' 카테고리의 다른 글
Spring Bean의 라이프사이클은 어떻게 되나요? (18) | 2025.04.10 |
---|---|
@Component vs @Service vs @Repository vs @Controller: 스프링 어노테이션 차이 (30) | 2025.04.10 |
Spring Framework IoC (Inversion of Control)란? (21) | 2025.03.21 |
Spring Framework DI (의존성 주입) 란? (15) | 2025.03.20 |
Spring 서버에서 HTTP 요청 처리 과정 (13) | 2025.03.19 |