Spring AOP는 스프링 프레임워크에서 제공하는 모듈로, 애플리케이션의 공통 관심사(ex: 로깅, 보안, 트랜잭션 관리 등)를 핵심 비즈니스 로직과 분리하여 관리할 수 있게 해준다. 이를 통해 코드 중복을 줄이고 유지보수를 용이하게 만들 수 있다.
@Aspect 어노테이션을 사용해 정의하며, 여러 Adivce를 포함할 수 있다.public void registerUser(String username, String email) {
// 로깅
long start = System.currentTimeMillis();
log.info("사용자 등록 시작 - username: {}", username);
// registerUser 에서 유저 입력 유효성 체크를 담당하는 파트를 담는다
if (username == null || email == null) {
throw new IllegalArgumentException("입력값이 비어 있습니다.");
}
// 이메일 검증
if (!email.endsWith("@example.com")) {
throw new SecurityException("허용되지 않은 이메일 도메인입니다.");
}
// 핵심 로직
log.info("DB에 사용자 저장 로직 실행");
// 로깅
long end = System.currentTimeMillis();
log.info("사용자 등록 완료 - username: {} 걸린 시간: {}", username, end - start);
}
public void registerUser(String username, String email) {
// 로깅
long start = System.currentTimeMillis();
logStartExecutionTime(username);
// registerUser 에서 유저 입력 유효성 체크를 담당하는 파트를 담는다
validateUserInput(username, email);
// 핵심 로직
log.info("DB에 사용자 저장 로직 실행");
// 로깅
long end = System.currentTimeMillis();
logEndExecutionTime(username, end-start);
}
private void logStartExecutionTime(String username) {
log.info("사용자 등록 시작 - username: {}", username);
}
private void logEndExecutionTime(String username, long executionTime) {
log.info("사용자 등록 완료 - username: {} {}", username, executionTime);
}
private void validateUserInput(String username, String email) {
// 유효성 검사
if (username == null || email == null) {
throw new IllegalArgumentException("입력값이 비어 있습니다.");
}
// 이메일 검증
if (!email.endsWith("@example.com")) {
throw new SecurityException("허용되지 않은 이메일 도메인입니다.");
}
}
registerUser메서드 내에서 이 부수 기능들을 직접 호출하고 있다.@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
}
@EnableAspectJAutoProxy를 사용하여 AspectJ 기반 AOP 프록시를 활성화하여, 애스팩트를 자동으로 적용할 수 있게 해준다.@Aspect
@Component
public class LogExecutionTimeAspect {
private static final Logger log = LoggerFactory.getLogger("LogExecutionTimeAspect");
@Around("@annotation(com.inspire12.likelionbackend.module.core.aop.aspect.LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
String username = (args.length > 0 && args[0] instanceof String) ? (String) args[0]: "unknown";
log.info("사용자 등록 시작 - username: {}", username);
long startTime = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
long endTime = System.currentTimeMillis();
log.info("사용자 등록 완료 - username: {} {}", username, endTime - startTime);
return proceed;
}
}
@Aspect
@Component
public class UserInputValidateAspect {
@Pointcut("@annotation(com.inspire12.likelionbackend.module.core.aop.aspect.UserInputValidate)")
public void userInputValidatePointcut() {}
@Around("userInputValidatePointcut()")
public Object userInputValidate(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
String username = args[0].toString();
String email = args[1].toString();
if (username == null || email == null) {
throw new IllegalArgumentException("입력값이 비어 있습니다.");
}
// 이메일 검증
if (!email.endsWith("@example.com")) {
throw new SecurityException("허용되지 않은 이메일 도메인입니다.");
}
return joinPoint.proceed();
}
}
joinPoint.proceed()은 AOP 로직 실행 중 실제 메서드 호출 시점을 결정할 수 있도록 해준다.joinPoint.getArgs()은 메서드 호출 시 전달된 인자들을 가져오는 역할을 한다.@LogExecutionTime
@UserInputValidate
public void registerUser(String username, String email) { // 변경됨
log.info("DB에 사용자 저장 로직 실행");
}