Aspect-Oriented Programming(AOP)은 Spring에서 횡단 관심사를 처리하기 위한 강력한 도구입니다.

AOP를 활용하면 애플리케이션의 주요 비즈니스 로직을 간결하게 유지하면서도, 반복적인 작업을 쉽게 처리할 수 있습니다. 아래는 Spring AOP를 활용해 구현할 수 있는 몇 가지 유용한 기능과 구현 예제입니다.


1. 로깅 (Logging)

메서드의 입력값, 출력값, 실행 시간을 로깅하는 기능.

구현 예제

@Aspect
@Component
public class LoggingAspect {

    @Around("execution(* com.example..*(..))") // com.example 패키지의 모든 메서드
    public Object logMethodDetails(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Method " + joinPoint.getSignature() + " called with args: " + Arrays.toString(joinPoint.getArgs()));
        Object result = joinPoint.proceed();
        System.out.println("Method " + joinPoint.getSignature() + " returned: " + result);
        return result;
    }
}

2. 보안 검사 (Security Check)

메서드 실행 전 사용자의 인증 및 권한을 검사하는 기능.

구현 예제

@Aspect
@Component
public class SecurityAspect {

    @Before("@annotation(com.example.annotations.SecureAction)") // 특정 어노테이션이 붙은 메서드만
    public void checkSecurity() {
        // 예: 현재 사용자의 권한을 확인
        boolean hasPermission = SecurityContextHolder.getContext().getAuthentication().isAuthenticated();
        if (!hasPermission) {
            throw new SecurityException("User not authorized!");
        }
        System.out.println("User is authorized");
    }
}

3. 트랜잭션 관리 (Custom Transaction Management)

Spring의 기본 트랜잭션 외에, 특정 작업에서 커스텀 트랜잭션 처리를 추가.

구현 예제

@Aspect
@Component
public class TransactionManagementAspect {

    @Around("@annotation(org.springframework.transaction.annotation.Transactional)")
    public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Transaction started");
        try {
            Object result = joinPoint.proceed();
            System.out.println("Transaction committed");
            return result;
        } catch (Exception ex) {
            System.out.println("Transaction rolled back");
            throw ex;
        }
    }
}

4. 캐싱 (Caching)

결과를 캐싱하여 메서드 호출을 최적화.

구현 예제

@Aspect
@Component
public class CachingAspect {

    private final Map<String, Object> cache = new HashMap<>();

    @Around("@annotation(com.example.annotations.Cached)")
    public Object cacheResult(ProceedingJoinPoint joinPoint) throws Throwable {
        String key = joinPoint.getSignature().toString() + Arrays.toString(joinPoint.getArgs());
        if (cache.containsKey(key)) {
            System.out.println("Cache hit for key: " + key);
            return cache.get(key);
        }
        System.out.println("Cache miss for key: " + key);
        Object result = joinPoint.proceed();
        cache.put(key, result);
        return result;
    }
}

5. 예외 처리 (Global Exception Handling)

특정 패키지의 메서드에서 발생한 예외를 중앙에서 처리.

구현 예제

@Aspect
@Component
public class ExceptionHandlingAspect {

    @AfterThrowing(pointcut = "execution(* com.example..*(..))", throwing = "ex")
    public void handleException(JoinPoint joinPoint, Exception ex) {
        System.err.println("Exception in method " + joinPoint.getSignature() + ": " + ex.getMessage());
        // 로그 저장 또는 알림 전송
    }
}

6. 메트릭 수집 (Metrics Collection)

메서드 호출 횟수, 평균 실행 시간을 수집하여 성능 모니터링.

구현 예제

@Aspect
@Component
public class MetricsAspect {

    private final Map<String, Long> executionCount = new HashMap<>();
    private final Map<String, Long> totalTime = new HashMap<>();

    @Around("execution(* com.example..*(..))")
    public Object collectMetrics(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().toString();
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long duration = System.currentTimeMillis() - start;

        executionCount.put(methodName, executionCount.getOrDefault(methodName, 0L) + 1);
        totalTime.put(methodName, totalTime.getOrDefault(methodName, 0L) + duration);

        System.out.println("Method " + methodName + " called " + executionCount.get(methodName) + " times");
        System.out.println("Average execution time: " + (totalTime.get(methodName) / executionCount.get(methodName)) + " ms");

        return result;
    }
}

7. 입력값 검증 (Validation)

메서드 호출 전에 입력값이 유효한지 검증.

구현 예제

@Aspect
@Component
public class ValidationAspect {

    @Before("@annotation(com.example.annotations.ValidateInput)")
    public void validateInputs(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            if (arg == null) {
                throw new IllegalArgumentException("Null value not allowed for arguments!");
            }
        }
        System.out.println("All inputs are valid");
    }
}

여기에는 Spring AOP를 활용하여 더 다양한 유용한 기능을 구현한 예제를 소개합니다. 각 기능은 프로젝트에 따라 확장 가능하며, 비즈니스 요구사항에 따라 활용될 수 있습니다.


8. 요청 데이터 암호화 및 복호화

API 요청 데이터나 응답 데이터를 암호화/복호화하여 보안성을 강화.

구현 예제

@Aspect
@Component
public class EncryptionAspect {

    private static final String SECRET_KEY = "my-secret-key";

    @Around("@annotation(com.example.annotations.EncryptData)")
    public Object encryptAndDecrypt(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        
        // 암호화된 데이터로 변경
        for (int i = 0; i < args.length; i++) {
            if (args[i] instanceof String) {
                args[i] = encrypt((String) args[i]);
            }
        }
        System.out.println("Arguments encrypted: " + Arrays.toString(args));

        // 메서드 실행
        Object result = joinPoint.proceed(args);

        // 복호화된 결과 반환
        if (result instanceof String) {
            result = decrypt((String) result);
            System.out.println("Result decrypted: " + result);
        }

        return result;
    }

    private String encrypt(String data) {
        // 간단한 암호화 로직 (예시)
        return Base64.getEncoder().encodeToString((data + SECRET_KEY).getBytes());
    }

    private String decrypt(String data) {
        // 간단한 복호화 로직 (예시)
        String decoded = new String(Base64.getDecoder().decode(data));
        return decoded.replace(SECRET_KEY, "");
    }
}

9. API 호출 제한 (Rate Limiting)

특정 사용자가 API를 과도하게 호출하지 않도록 제한.

구현 예제

@Aspect
@Component
public class RateLimitingAspect {

    private final Map<String, Integer> userRequestCounts = new HashMap<>();
    private static final int LIMIT = 5; // 요청 제한

    @Before("@annotation(com.example.annotations.RateLimited)")
    public void enforceRateLimit(JoinPoint joinPoint) {
        String userId = getCurrentUserId(); // 사용자 ID 가져오기 (가정)

        userRequestCounts.put(userId, userRequestCounts.getOrDefault(userId, 0) + 1);

        if (userRequestCounts.get(userId) > LIMIT) {
            throw new RuntimeException("Rate limit exceeded for user: " + userId);
        }
        System.out.println("Request allowed for user: " + userId);
    }

    private String getCurrentUserId() {
        // 현재 사용자 ID를 가져오는 로직 (예: 보안 컨텍스트)
        return "user123";
    }
}

10. 메서드 리트라이 (Retry)

실패한 메서드 호출을 일정 횟수까지 재시도.

구현 예제

@Aspect
@Component
public class RetryAspect {

    @Around("@annotation(com.example.annotations.Retryable)")
    public Object retry(ProceedingJoinPoint joinPoint) throws Throwable {
        int maxRetries = 3; // 최대 재시도 횟수
        int attempts = 0;

        while (true) {
            try {
                return joinPoint.proceed(); // 메서드 실행
            } catch (Exception ex) {
                attempts++;
                System.out.println("Attempt " + attempts + " failed");
                if (attempts >= maxRetries) {
                    System.out.println("Max retries reached");
                    throw ex;
                }
            }
        }
    }
}

11. 감사 로깅 (Audit Logging)

중요한 작업(예: 데이터베이스 변경)을 기록하여 감사를 가능하게 함.

구현 예제

@Aspect
@Component
public class AuditLoggingAspect {

    @After("@annotation(com.example.annotations.AuditLog)")
    public void logAuditDetails(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().toShortString();
        String userName = getCurrentUserName(); // 현재 사용자 가져오기 (가정)
        System.out.println("User " + userName + " executed " + methodName);
        // 로그 저장 또는 DB에 기록
    }

    private String getCurrentUserName() {
        // 현재 사용자 이름을 반환 (예: 보안 컨텍스트)
        return "admin";
    }
}

12. 메서드 호출 알림 (Notification)

특정 메서드가 호출되었을 때 알림을 전송.

구현 예제

@Aspect
@Component
public class NotificationAspect {

    @After("@annotation(com.example.annotations.NotifyOnCall)")
    public void sendNotification(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().toShortString();
        System.out.println("Notification: Method " + methodName + " was called");
        // 알림 전송 (예: 이메일, SMS, 슬랙)
    }
}

13. 요청 추적 (Request Tracing)

모든 요청을 추적하여 디버깅 및 모니터링.

구현 예제

@Aspect
@Component
public class RequestTracingAspect {

    @Before("execution(* com.example.controller..*(..))") // 컨트롤러의 모든 메서드
    public void traceRequest(JoinPoint joinPoint) {
        System.out.println("Request received for method: " + joinPoint.getSignature());
        System.out.println("Arguments: " + Arrays.toString(joinPoint.getArgs()));
    }
}

14. 데이터 변경 이벤트 처리

데이터 변경 시 추가 작업(예: 캐시 무효화) 수행.

구현 예제

@Aspect
@Component
public class DataChangeAspect {

    @AfterReturning("@annotation(com.example.annotations.OnDataChange)")
    public void handleDataChange(JoinPoint joinPoint) {
        System.out.println("Data change detected in method: " + joinPoint.getSignature());
        // 캐시 무효화, 이벤트 전송 등의 작업 수행
    }
}

15. 성능 문제 감지 (Performance Monitoring)

특정 메서드에서 일정 시간을 초과하는 경우 경고를 출력.

구현 예제

@Aspect
@Component
public class PerformanceMonitoringAspect {

    private static final long WARNING_THRESHOLD = 1000; // 1초

    @Around("@annotation(com.example.annotations.MonitorPerformance)")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long duration = System.currentTimeMillis() - start;

        if (duration > WARNING_THRESHOLD) {
            System.err.println("Performance warning: " + joinPoint.getSignature() + " took " + duration + " ms");
        } else {
            System.out.println(joinPoint.getSignature() + " executed in " + duration + " ms");
        }

        return result;
    }
}

16. 데이터베이스 슬로우 쿼리 탐지

SQL 실행 시간이 긴 경우 경고 로그를 남기거나 알림을 보냅니다.

구현 예제

@Aspect
@Component
public class SlowQueryDetectorAspect {

    private static final long SLOW_QUERY_THRESHOLD = 2000; // 2초

    @Around("execution(* com.example.repository..*(..))") // Repository 계층 감시
    public Object detectSlowQueries(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long duration = System.currentTimeMillis() - start;

        if (duration > SLOW_QUERY_THRESHOLD) {
            System.err.println("Slow query detected: " + joinPoint.getSignature() + " took " + duration + " ms");
        }

        return result;
    }
}

17. 사용자 접근 로그 기록 (Access Logging)

API 호출 시 사용자의 IP 주소, 요청 시간 등을 기록합니다.

구현 예제

@Aspect
@Component
public class AccessLoggingAspect {

    @Before("execution(* com.example.controller..*(..))") // 모든 컨트롤러 메서드
    public void logAccess(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().toShortString();
        String ipAddress = getClientIp(); // 클라이언트 IP 가져오기
        System.out.println("Access log: " + methodName + " called from IP: " + ipAddress);
    }

    private String getClientIp() {
        // HttpServletRequest를 통해 IP 주소 가져오기
        return "127.0.0.1"; // 예제용
    }
}

18. 조건부 실행 (Conditional Execution)

특정 조건에서만 메서드를 실행하거나 무시합니다.

구현 예제

@Aspect
@Component
public class ConditionalExecutionAspect {

    @Around("@annotation(com.example.annotations.ConditionalRun)")
    public Object executeConditionally(ProceedingJoinPoint joinPoint) throws Throwable {
        boolean shouldRun = checkCondition(); // 실행 조건 확인
        if (shouldRun) {
            return joinPoint.proceed(); // 조건이 충족되면 실행
        } else {
            System.out.println("Method " + joinPoint.getSignature() + " skipped due to condition");
            return null; // 실행하지 않음
        }
    }

    private boolean checkCondition() {
        // 조건 로직 (예: 특정 설정값 확인)
        return false;
    }
}

19. 메서드 호출 수 제한

특정 메서드가 지정된 횟수 이상 호출되지 않도록 제한합니다.

구현 예제

@Aspect
@Component
public class MethodCallLimiterAspect {

    private final Map<String, Integer> methodCallCounts = new HashMap<>();
    private static final int MAX_CALLS = 10; // 최대 호출 횟수

    @Before("@annotation(com.example.annotations.LimitedCall)")
    public void limitMethodCalls(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().toShortString();
        int currentCount = methodCallCounts.getOrDefault(methodName, 0);

        if (currentCount >= MAX_CALLS) {
            throw new RuntimeException("Method " + methodName + " has been called too many times");
        }

        methodCallCounts.put(methodName, currentCount + 1);
        System.out.println("Method " + methodName + " called " + (currentCount + 1) + " times");
    }
}

20. 사용자 ID 태깅 (User ID Tagging)

로그나 메트릭에 현재 사용자 ID를 태깅하여 사용자별 모니터링을 제공합니다.

구현 예제

@Aspect
@Component
public class UserIdTaggingAspect {

    @Before("execution(* com.example.service..*(..))") // 서비스 계층 메서드 감시
    public void tagUserId(JoinPoint joinPoint) {
        String userId = getCurrentUserId(); // 현재 사용자 ID 가져오기
        System.out.println("Tagging user ID: " + userId + " for method: " + joinPoint.getSignature());
    }

    private String getCurrentUserId() {
        // 보안 컨텍스트에서 사용자 ID 가져오기
        return "user123"; // 예제용
    }
}

21. 필드 마스킹 (Field Masking)

로그 출력 시 민감한 데이터를 마스킹합니다.

구현 예제

@Aspect
@Component
public class FieldMaskingAspect {

    @Around("execution(* com.example.service..*(..))") // 서비스 계층 메서드 감시
    public Object maskSensitiveFields(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();
        if (result instanceof String) {
            // 민감한 데이터 마스킹 (예: 이메일, 카드 번호)
            result = ((String) result).replaceAll("(?<=.{2}).(?=.*@)", "*");
        }
        return result;
    }
}

22. 실행 환경 확인 (Environment Check)

테스트 환경이나 프로덕션 환경에 따라 다른 동작을 수행.

구현 예제

@Aspect
@Component
public class EnvironmentCheckAspect {

    @Around("@annotation(com.example.annotations.EnvironmentSensitive)")
    public Object checkEnvironment(ProceedingJoinPoint joinPoint) throws Throwable {
        String currentEnv = getCurrentEnvironment(); // 현재 환경 가져오기
        if ("prod".equals(currentEnv)) {
            System.out.println("Executing in production");
        } else {
            System.out.println("Executing in non-production environment");
        }
        return joinPoint.proceed();
    }

    private String getCurrentEnvironment() {
        // Spring Environment에서 현재 프로파일 가져오기
        return "dev"; // 예제용
    }
}

23. 특정 패키지 메서드 호출 차단

특정 패키지의 메서드가 호출되지 않도록 차단합니다.

구현 예제

@Aspect
@Component
public class MethodBlockingAspect {

    @Before("execution(* com.example.unallowed..*(..))") // 금지된 패키지
    public void blockMethodCall(JoinPoint joinPoint) {
        throw new RuntimeException("Method " + joinPoint.getSignature() + " is not allowed to be called");
    }
}

24. 메서드 입력값 변환 (Input Transformation)

메서드 호출 전에 입력값을 변환.

구현 예제

@Aspect
@Component
public class InputTransformationAspect {

    @Around("@annotation(com.example.annotations.TransformInput)")
    public Object transformInput(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();

        for (int i = 0; i < args.length; i++) {
            if (args[i] instanceof String) {
                args[i] = ((String) args[i]).toUpperCase(); // 입력값 대문자 변환
            }
        }
        System.out.println("Transformed arguments: " + Arrays.toString(args));

        return joinPoint.proceed(args);
    }
}

25. 메모리 사용 모니터링 (Memory Usage Monitoring)

메서드 실행 전후로 메모리 사용량을 기록.

구현 예제

@Aspect
@Component
public class MemoryMonitoringAspect {

    @Around("@annotation(com.example.annotations.MonitorMemory)")
    public Object monitorMemoryUsage(ProceedingJoinPoint joinPoint) throws Throwable {
        Runtime runtime = Runtime.getRuntime();
        long beforeMemory = runtime.totalMemory() - runtime.freeMemory();

        Object result = joinPoint.proceed();

        long afterMemory = runtime.totalMemory() - runtime.freeMemory();
        System.out.println("Memory usage for method " + joinPoint.getSignature() + ": " + (afterMemory - beforeMemory) + " bytes");

        return result;
    }
}

26. API 요청 제한 속도 조정 (Dynamic Throttling)

시스템 상태나 사용자 그룹에 따라 요청 속도를 동적으로 제한합니다.

구현 예제

@Aspect
@Component
public class DynamicThrottlingAspect {

    private final Map<String, Long> userLastRequestTime = new HashMap<>();
    private static final long MIN_INTERVAL = 1000; // 최소 요청 간격 (1초)

    @Before("@annotation(com.example.annotations.Throttled)")
    public void enforceThrottling(JoinPoint joinPoint) {
        String userId = getCurrentUserId(); // 사용자 ID 가져오기
        long currentTime = System.currentTimeMillis();

        if (userLastRequestTime.containsKey(userId)) {
            long lastRequestTime = userLastRequestTime.get(userId);
            if (currentTime - lastRequestTime < MIN_INTERVAL) {
                throw new RuntimeException("Too many requests for user: " + userId);
            }
        }

        userLastRequestTime.put(userId, currentTime);
    }

    private String getCurrentUserId() {
        // 예: 보안 컨텍스트에서 사용자 ID 가져오기
        return "user123"; // 예제용
    }
}

27. 컨트롤러 응답 변환 (Response Transformation)

컨트롤러의 반환값을 동적으로 수정하여 사용자 맞춤 데이터를 반환합니다.

구현 예제

@Aspect
@Component
public class ResponseTransformationAspect {

    @AfterReturning(pointcut = "execution(* com.example.controller..*(..))", returning = "response")
    public Object transformResponse(JoinPoint joinPoint, Object response) {
        if (response instanceof String) {
            return ((String) response).toUpperCase(); // 응답을 대문자로 변환
        }
        return response;
    }
}

28. 실시간 메트릭 수집 (Real-time Metrics Collection)

메서드 실행 데이터를 외부 모니터링 시스템으로 전송합니다.

구현 예제

@Aspect
@Component
public class RealTimeMetricsAspect {

    @AfterReturning("execution(* com.example.service..*(..))")
    public void sendMetricsToSystem(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().toShortString();
        System.out.println("Sending execution metrics for: " + methodName);
        // 외부 시스템에 메트릭 전송 (예: Prometheus, DataDog)
    }
}

29. 사용자별 데이터 필터링

사용자 권한에 따라 반환되는 데이터를 필터링합니다.

구현 예제

@Aspect
@Component
public class DataFilteringAspect {

    @Around("execution(* com.example.service..*(..)) && @annotation(com.example.annotations.FilteredData)")
    public Object filterDataForUser(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();

        // 사용자 권한에 따른 데이터 필터링
        if (result instanceof List) {
            List<?> list = (List<?>) result;
            return list.stream()
                    .filter(item -> userHasAccess(item)) // 사용자 접근 권한 필터
                    .toList();
        }
        return result;
    }

    private boolean userHasAccess(Object item) {
        // 접근 권한 로직
        return true; // 예제용
    }
}

30. 메서드 파라미터 로그 제외 (Sensitive Logging)

특정 파라미터를 로그에서 제외하여 민감 정보를 보호합니다.

구현 예제

@Aspect
@Component
public class SensitiveLoggingAspect {

    @Around("execution(* com.example.service..*(..))")
    public Object excludeSensitiveParams(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        String methodName = joinPoint.getSignature().toShortString();

        // 민감 데이터 마스킹
        Object[] sanitizedArgs = Arrays.stream(args)
                .map(arg -> arg instanceof String && isSensitive(arg) ? "****" : arg)
                .toArray();

        System.out.println("Method " + methodName + " called with args: " + Arrays.toString(sanitizedArgs));
        return joinPoint.proceed();
    }

    private boolean isSensitive(Object arg) {
        // 민감 정보 판별 (예: 비밀번호, 카드 번호)
        return arg.toString().matches(".*\\d{4}-\\d{4}-\\d{4}-\\d{4}.*"); // 카드 번호 예시
    }
}

31. 백그라운드 태스크 실행 (Background Task Execution)

메서드 실행을 비동기로 처리하여 응답 속도를 높입니다.

구현 예제

@Aspect
@Component
public class BackgroundTaskAspect {

    private final ExecutorService executorService = Executors.newCachedThreadPool();

    @Around("@annotation(com.example.annotations.BackgroundTask)")
    public void executeInBackground(ProceedingJoinPoint joinPoint) {
        executorService.submit(() -> {
            try {
                joinPoint.proceed();
                System.out.println("Executed in background: " + joinPoint.getSignature());
            } catch (Throwable e) {
                e.printStackTrace();
            }
        });
    }
}

32. 디버그 모드 활성화 (Debug Mode Toggle)

애플리케이션의 디버그 모드에서만 특정 로직을 실행합니다.

구현 예제

@Aspect
@Component
public class DebugModeAspect {

    private static final boolean DEBUG_MODE = true; // 설정에 따라 변경 가능

    @Around("@annotation(com.example.annotations.DebugOnly)")
    public Object executeInDebugMode(ProceedingJoinPoint joinPoint) throws Throwable {
        if (DEBUG_MODE) {
            System.out.println("Debug mode enabled for method: " + joinPoint.getSignature());
            return joinPoint.proceed();
        } else {
            System.out.println("Debug mode skipped for method: " + joinPoint.getSignature());
            return null; // 디버그 모드가 아니면 실행 생략
        }
    }
}

33. 캐시 삭제 후처리 (Post Cache Eviction Handling)

캐시 삭제 후 추가 작업을 처리합니다.

구현 예제

@Aspect
@Component
public class PostCacheEvictionAspect {

    @After("@annotation(org.springframework.cache.annotation.CacheEvict)")
    public void handleCacheEviction(JoinPoint joinPoint) {
        System.out.println("Cache evicted for method: " + joinPoint.getSignature());
        // 캐시 삭제 후 작업 (예: 로깅, 알림)
    }
}

34. 데이터 통합 검증 (Unified Data Validation)

모든 데이터 입력에 대해 통합적으로 검증을 수행합니다.

구현 예제

@Aspect
@Component
public class UnifiedValidationAspect {

    @Before("execution(* com.example.controller..*(..))")
    public void validateData(JoinPoint joinPoint) {
        for (Object arg : joinPoint.getArgs()) {
            if (!isValid(arg)) {
                throw new IllegalArgumentException("Invalid argument: " + arg);
            }
        }
        System.out.println("All arguments are valid for method: " + joinPoint.getSignature());
    }

    private boolean isValid(Object arg) {
        // 데이터 유효성 검사 로직
        return arg != null; // 예제용
    }
}

35. 조건부 트랜잭션 관리 (Conditional Transaction Management)

특정 조건에서만 트랜잭션을 활성화합니다.

구현 예제

@Aspect
@Component
public class ConditionalTransactionAspect {

    @Around("@annotation(org.springframework.transaction.annotation.Transactional)")
    public Object manageTransactionConditionally(ProceedingJoinPoint joinPoint) throws Throwable {
        if (shouldActivateTransaction()) {
            System.out.println("Transaction started for: " + joinPoint.getSignature());
            Object result = joinPoint.proceed();
            System.out.println("Transaction committed for: " + joinPoint.getSignature());
            return result;
        } else {
            System.out.println("Transaction skipped for: " + joinPoint.getSignature());
            return joinPoint.proceed();
        }
    }

    private boolean shouldActivateTransaction() {
        // 트랜잭션 활성화 조건
        return true; // 예제용
    }
}

36. 데이터 변경 알림 (Change Notification)

특정 데이터가 변경되었을 때 관리자에게 알림을 전송합니다.

구현 예제

@Aspect
@Component
public class ChangeNotificationAspect {

    @AfterReturning("@annotation(com.example.annotations.NotifyChange)")
    public void notifyOnChange(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().toShortString();
        System.out.println("Data change detected in method: " + methodName);
        sendNotification("Data has been changed in " + methodName);
    }

    private void sendNotification(String message) {
        // 이메일 또는 SMS 전송 로직
        System.out.println("Notification sent: " + message);
    }
}

37. 캐시 프리로드 (Cache Preloading)

애플리케이션 시작 시 특정 데이터를 캐시에 미리 로드합니다.

구현 예제

@Aspect
@Component
public class CachePreloadingAspect {

    @PostConstruct // 애플리케이션 시작 시 호출
    public void preloadCache() {
        System.out.println("Preloading cache...");
        // 캐시 초기화 로직
        loadCache("key1", "value1");
        loadCache("key2", "value2");
    }

    private void loadCache(String key, String value) {
        // 캐시에 데이터 로드
        System.out.println("Cached: " + key + " -> " + value);
    }
}

38. 특정 메서드 비활성화 (Method Deactivation)

관리자가 지정한 메서드를 비활성화하여 호출되지 않도록 합니다.

구현 예제

@Aspect
@Component
public class MethodDeactivationAspect {

    private static final Set<String> deactivatedMethods = Set.of("com.example.service.MyService.deactivatedMethod");

    @Before("execution(* com.example..*(..))")
    public void blockDeactivatedMethods(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().toString();
        if (deactivatedMethods.contains(methodName)) {
            throw new RuntimeException("Method " + methodName + " is currently deactivated");
        }
    }
}

39. API 요청 경로 추적 (Request Path Tracing)

모든 HTTP 요청의 경로를 추적하여 디버깅 정보를 출력합니다.

구현 예제

@Aspect
@Component
public class RequestPathTracingAspect {

    @Before("execution(* org.springframework.web.bind.annotation.RestController..*(..))")
    public void traceRequestPath(JoinPoint joinPoint) {
        String path = joinPoint.getSignature().toShortString();
        System.out.println("Request received for path: " + path);
    }
}

40. 메서드 병렬 실행 (Parallel Execution)

특정 메서드를 병렬로 실행하여 성능을 높입니다.

구현 예제

@Aspect
@Component
public class ParallelExecutionAspect {

    private final ExecutorService executor = Executors.newFixedThreadPool(10);

    @Around("@annotation(com.example.annotations.ParallelExecution)")
    public Object executeInParallel(ProceedingJoinPoint joinPoint) {
        executor.submit(() -> {
            try {
                joinPoint.proceed();
                System.out.println("Executed in parallel: " + joinPoint.getSignature());
            } catch (Throwable e) {
                e.printStackTrace();
            }
        });
        return null; // 병렬 실행이므로 결과를 즉시 반환
    }
}

41. 결과 데이터 포맷팅 (Result Formatting)

메서드 반환값을 JSON이나 XML 등 특정 형식으로 변환합니다.

구현 예제

@Aspect
@Component
public class ResultFormattingAspect {

    @AfterReturning(pointcut = "@annotation(com.example.annotations.FormatResult)", returning = "result")
    public Object formatResult(JoinPoint joinPoint, Object result) {
        if (result != null) {
            // JSON 형식으로 변환
            String jsonResult = toJson(result);
            System.out.println("Formatted result: " + jsonResult);
            return jsonResult;
        }
        return result;
    }

    private String toJson(Object result) {
        // 간단한 JSON 변환 로직
        return "{\"data\": \"" + result.toString() + "\"}";
    }
}

42. 실행 결과 캐싱 (Result Caching)

메서드 실행 결과를 캐싱하여 동일한 요청에 대해 재사용합니다.

구현 예제

@Aspect
@Component
public class ResultCachingAspect {

    private final Map<String, Object> cache = new HashMap<>();

    @Around("@annotation(com.example.annotations.Cacheable)")
    public Object cacheResult(ProceedingJoinPoint joinPoint) throws Throwable {
        String key = generateCacheKey(joinPoint);

        if (cache.containsKey(key)) {
            System.out.println("Cache hit for key: " + key);
            return cache.get(key);
        }

        Object result = joinPoint.proceed();
        cache.put(key, result);
        System.out.println("Cache saved for key: " + key);
        return result;
    }

    private String generateCacheKey(ProceedingJoinPoint joinPoint) {
        return joinPoint.getSignature().toString() + Arrays.toString(joinPoint.getArgs());
    }
}

43. 요청의 IP 블랙리스트 처리 (IP Blacklisting)

특정 IP 주소에서의 요청을 차단합니다.

구현 예제

@Aspect
@Component
public class IPBlacklistAspect {

    private static final Set<String> blacklistedIPs = Set.of("192.168.0.1", "10.0.0.1");

    @Before("execution(* com.example.controller..*(..))")
    public void blockBlacklistedIPs() {
        String clientIp = getClientIP();
        if (blacklistedIPs.contains(clientIp)) {
            throw new RuntimeException("Access denied for IP: " + clientIp);
        }
        System.out.println("Access allowed for IP: " + clientIp);
    }

    private String getClientIP() {
        // 실제로 HttpServletRequest에서 IP를 가져오는 로직을 사용
        return "192.168.0.1"; // 예제용
    }
}

44. 요청별 세션 데이터 초기화 (Session Initialization)

HTTP 요청마다 세션 데이터를 초기화하거나 설정합니다.

구현 예제

@Aspect
@Component
public class SessionInitializationAspect {

    @Before("execution(* com.example.controller..*(..))")
    public void initializeSessionData() {
        System.out.println("Initializing session data...");
        // 세션 데이터 초기화 로직
    }
}

45. 서비스 계층 장애 감지 (Service Failure Detection)

특정 서비스 계층에서의 장애 발생률을 모니터링하고 알림을 전송합니다.

구현 예제

@Aspect
@Component
public class FailureDetectionAspect {

    private final Map<String, Integer> failureCount = new HashMap<>();
    private static final int FAILURE_THRESHOLD = 3;

    @AfterThrowing(pointcut = "execution(* com.example.service..*(..))", throwing = "ex")
    public void detectFailures(JoinPoint joinPoint, Throwable ex) {
        String methodName = joinPoint.getSignature().toString();
        failureCount.put(methodName, failureCount.getOrDefault(methodName, 0) + 1);

        if (failureCount.get(methodName) >= FAILURE_THRESHOLD) {
            System.err.println("Failure threshold exceeded for method: " + methodName);
            sendAlert(methodName);
        }
    }

    private void sendAlert(String methodName) {
        // 알림 전송 로직 (예: 이메일, Slack)
        System.out.println("Alert sent for method: " + methodName);
    }
}

여기에는 Spring AOP를 활용해 추가로 구현할 수 있는 더 많은 유용한 기능들을 소개합니다. 다양한 사례와 아이디어로 활용할 수 있습니다.


46. 데이터베이스 페일오버 처리 (Database Failover Handling)

주 데이터베이스 장애 발생 시 대체 데이터베이스로 자동 전환.

구현 예제

@Aspect
@Component
public class DatabaseFailoverAspect {

    private boolean primaryDatabaseDown = false;

    @Around("execution(* com.example.repository..*(..))")
    public Object handleFailover(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            if (!primaryDatabaseDown) {
                System.out.println("Using primary database...");
                return joinPoint.proceed();
            }
        } catch (Exception ex) {
            System.err.println("Primary database failed. Switching to backup database...");
            primaryDatabaseDown = true;
            return useBackupDatabase(joinPoint);
        }
        return useBackupDatabase(joinPoint);
    }

    private Object useBackupDatabase(ProceedingJoinPoint joinPoint) throws Throwable {
        // 여기서 backup 데이터베이스를 사용하는 로직 추가
        System.out.println("Executing with backup database");
        return joinPoint.proceed(); // 예제에서는 그냥 진행
    }
}

47. 사용자 활동 기록 (User Activity Logging)

사용자가 수행한 모든 주요 작업을 기록합니다.

구현 예제

@Aspect
@Component
public class UserActivityLoggingAspect {

    @AfterReturning("execution(* com.example.controller..*(..))")
    public void logUserActivity(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().toShortString();
        String userId = getCurrentUserId();
        System.out.println("User " + userId + " performed action: " + methodName);
        saveActivityToDatabase(userId, methodName);
    }

    private String getCurrentUserId() {
        // 보안 컨텍스트에서 현재 사용자 ID 가져오기
        return "user123"; // 예제용
    }

    private void saveActivityToDatabase(String userId, String action) {
        // 데이터베이스에 사용자 활동 저장 로직
        System.out.println("Activity saved: User=" + userId + ", Action=" + action);
    }
}

48. API 응답 압축 (Response Compression)

컨트롤러의 모든 응답을 GZIP으로 압축하여 클라이언트로 전송합니다.

구현 예제

@Aspect
@Component
public class ResponseCompressionAspect {

    @Around("execution(* com.example.controller..*(..))")
    public Object compressResponse(ProceedingJoinPoint joinPoint) throws Throwable {
        Object response = joinPoint.proceed();

        if (response instanceof String) {
            String compressed = compress((String) response);
            System.out.println("Response compressed");
            return compressed;
        }
        return response;
    }

    private String compress(String data) {
        // 간단한 GZIP 압축 로직
        return Base64.getEncoder().encodeToString(data.getBytes());
    }
}

49. 요청 프로파일링 (Request Profiling)

요청의 시작 시간, 끝 시간, 실행 시간 등을 기록하여 성능 분석.

구현 예제

@Aspect
@Component
public class RequestProfilingAspect {

    @Around("execution(* com.example.controller..*(..))")
    public Object profileRequest(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object response = joinPoint.proceed();
        long end = System.currentTimeMillis();

        System.out.println("Method " + joinPoint.getSignature() + " executed in " + (end - start) + " ms");
        return response;
    }
}

50. 서비스 호출 패턴 감지 (Service Call Pattern Detection)

특정 서비스가 지나치게 빈번히 호출되는 경우 경고.

구현 예제

@Aspect
@Component
public class ServiceCallPatternAspect {

    private final Map<String, Long> callTimestamps = new HashMap<>();
    private static final long THRESHOLD = 1000; // 1초 이내 반복 호출 경고

    @Before("execution(* com.example.service..*(..))")
    public void detectRapidCalls(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().toShortString();
        long currentTime = System.currentTimeMillis();

        if (callTimestamps.containsKey(methodName)) {
            long lastCall = callTimestamps.get(methodName);
            if (currentTime - lastCall < THRESHOLD) {
                System.err.println("Warning: Method " + methodName + " is being called too frequently!");
            }
        }

        callTimestamps.put(methodName, currentTime);
    }
}

51. 테스트 환경 모드 처리 (Test Mode Handling)

테스트 환경에서는 특정 동작을 스킵하거나 대체 동작을 실행합니다.

구현 예제

@Aspect
@Component
public class TestModeAspect {

    private static final boolean TEST_MODE = true;

    @Around("execution(* com.example.service..*(..)) && @annotation(com.example.annotations.TestModeOnly)")
    public Object handleTestMode(ProceedingJoinPoint joinPoint) throws Throwable {
        if (TEST_MODE) {
            System.out.println("Test mode active, skipping: " + joinPoint.getSignature());
            return null; // 테스트 환경에서는 동작 생략
        }
        return joinPoint.proceed();
    }
}

52. 서비스 사용량 제한 (Quota Management)

사용자가 지정된 서비스 사용량을 초과하지 않도록 제한합니다.

구현 예제

@Aspect
@Component
public class QuotaManagementAspect {

    private final Map<String, Integer> userQuotas = new HashMap<>();
    private static final int MAX_QUOTA = 100;

    @Before("execution(* com.example.service..*(..)) && @annotation(com.example.annotations.QuotaLimited)")
    public void enforceQuota(JoinPoint joinPoint) {
        String userId = getCurrentUserId();
        int currentQuota = userQuotas.getOrDefault(userId, 0);

        if (currentQuota >= MAX_QUOTA) {
            throw new RuntimeException("Quota exceeded for user: " + userId);
        }

        userQuotas.put(userId, currentQuota + 1);
        System.out.println("Quota used by " + userId + ": " + (currentQuota + 1));
    }

    private String getCurrentUserId() {
        // 현재 사용자 ID 가져오기 로직
        return "user123"; // 예제용
    }
}

53. 요청 실행 순서 추적 (Execution Order Tracking)

다단계 요청 실행 흐름을 추적하여 디버깅 정보를 제공합니다.

구현 예제

@Aspect
@Component
public class ExecutionOrderTrackingAspect {

    private int sequence = 0;

    @Before("execution(* com.example.service..*(..))")
    public void trackExecutionOrder(JoinPoint joinPoint) {
        sequence++;
        System.out.println("Execution order " + sequence + ": " + joinPoint.getSignature());
    }
}

54. 비동기 오류 처리 (Async Error Handling)

비동기로 실행되는 메서드에서 발생하는 예외를 중앙에서 처리합니다.

구현 예제

@Aspect
@Component
public class AsyncErrorHandlingAspect {

    @AfterThrowing(pointcut = "execution(* com.example.service..*(..)) && @annotation(org.springframework.scheduling.annotation.Async)", throwing = "ex")
    public void handleAsyncError(JoinPoint joinPoint, Throwable ex) {
        System.err.println("Async error in method: " + joinPoint.getSignature() + ", error: " + ex.getMessage());
        sendErrorNotification(joinPoint.getSignature().toString(), ex);
    }

    private void sendErrorNotification(String method, Throwable ex) {
        // 오류 알림 로직 (예: 이메일, Slack)
        System.out.println("Error notification sent for method: " + method);
    }
}

55. 비밀 유지 계약(NDA) 로깅

특정 메서드가 호출될 때, 실행된 동작이 NDA(Non-Disclosure Agreement)에 따라 로그에 포함되지 않도록 처리.

구현 예제

@Aspect
@Component
public class NDALoggingAspect {

    @Around("@annotation(com.example.annotations.NDAProtected)")
    public Object handleNDALogging(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            System.out.println("NDA-protected method called: " + joinPoint.getSignature());
            return joinPoint.proceed();
        } finally {
            System.out.println("Execution of NDA-protected method completed. No details logged.");
        }
    }
}

56. 컨트롤러 입력값 스니핑 방지 (Input Sniffing Prevention)

사용자의 입력값에 악성 코드가 포함되어 있는지 탐지하고 차단합니다.

구현 예제

@Aspect
@Component
public class InputValidationAspect {

    @Before("execution(* com.example.controller..*(..))")
    public void validateInput(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            if (arg instanceof String && containsMaliciousCode((String) arg)) {
                throw new IllegalArgumentException("Malicious input detected: " + arg);
            }
        }
    }

    private boolean containsMaliciousCode(String input) {
        // 간단한 악성 코드 탐지 로직
        return input.contains("<script>") || input.contains("DROP TABLE");
    }
}

57. 애플리케이션 상태 체크 (Application Health Monitoring)

특정 메서드가 호출될 때 애플리케이션의 상태를 체크하고 필요시 알림을 전송.

구현 예제

@Aspect
@Component
public class ApplicationHealthAspect {

    @Before("execution(* com.example.service..*(..)) && @annotation(com.example.annotations.HealthCheck)")
    public void checkApplicationHealth() {
        if (!isApplicationHealthy()) {
            System.err.println("Application health check failed!");
            sendHealthAlert();
        } else {
            System.out.println("Application is healthy.");
        }
    }

    private boolean isApplicationHealthy() {
        // 간단한 상태 체크 로직 (예: 메모리 사용량, 디스크 상태 등)
        return true; // 예제용
    }

    private void sendHealthAlert() {
        // 건강 상태 알림 전송 로직
        System.out.println("Health alert sent to monitoring system.");
    }
}

58. 데이터 변경 감사 로그 작성 (Audit Logging with Details)

데이터베이스에서 데이터가 변경되었을 때 변경 전후의 값을 감사 로그로 작성.

구현 예제

@Aspect
@Component
public class AuditDetailAspect {

    @Around("@annotation(com.example.annotations.AuditChanges)")
    public Object logChanges(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Capturing audit log for method: " + joinPoint.getSignature());
        Object[] args = joinPoint.getArgs();

        // 변경 전 데이터 가져오기
        Object beforeChange = fetchCurrentData(args);

        Object result = joinPoint.proceed();

        // 변경 후 데이터 가져오기
        Object afterChange = fetchCurrentData(args);

        // 감사 로그 작성
        logAuditDetails(beforeChange, afterChange);

        return result;
    }

    private Object fetchCurrentData(Object[] args) {
        // 현재 데이터를 가져오는 로직 (예: ID로 조회)
        return "Sample Data"; // 예제용
    }

    private void logAuditDetails(Object before, Object after) {
        System.out.println("Audit Log -> Before: " + before + ", After: " + after);
    }
}

59. 외부 API 호출 자동 재시도 (External API Retry Mechanism)

외부 API 호출이 실패할 경우 일정 횟수까지 재시도합니다.

구현 예제

@Aspect
@Component
public class ExternalAPIRetryAspect {

    @Around("@annotation(com.example.annotations.RetryOnFailure)")
    public Object retryAPIRequest(ProceedingJoinPoint joinPoint) throws Throwable {
        int maxRetries = 3;
        int attempts = 0;

        while (true) {
            try {
                return joinPoint.proceed();
            } catch (Exception ex) {
                attempts++;
                System.err.println("API call failed. Attempt " + attempts);
                if (attempts >= maxRetries) {
                    throw ex;
                }
            }
        }
    }
}

60. 비밀번호 유효성 검사 (Password Strength Validation)

사용자가 입력한 비밀번호가 규칙에 맞는지 검증.

구현 예제

@Aspect
@Component
public class PasswordValidationAspect {

    @Before("@annotation(com.example.annotations.ValidatePassword)")
    public void validatePassword(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            if (arg instanceof String && !isValidPassword((String) arg)) {
                throw new IllegalArgumentException("Invalid password: " + arg);
            }
        }
    }

    private boolean isValidPassword(String password) {
        // 비밀번호 강도 검사 (예: 최소 길이, 특수 문자 포함 여부 등)
        return password.length() >= 8 && password.matches(".*[!@#$%^&*()].*");
    }
}

61. 지연 초기화 (Lazy Initialization)

특정 메서드의 결과를 최초 호출 시 초기화하고 캐싱하여 재사용.

구현 예제

@Aspect
@Component
public class LazyInitializationAspect {

    private final Map<String, Object> cache = new HashMap<>();

    @Around("@annotation(com.example.annotations.LazyInit)")
    public Object handleLazyInit(ProceedingJoinPoint joinPoint) throws Throwable {
        String key = joinPoint.getSignature().toString();

        if (cache.containsKey(key)) {
            System.out.println("Returning cached result for: " + key);
            return cache.get(key);
        }

        Object result = joinPoint.proceed();
        cache.put(key, result);
        System.out.println("Result cached for: " + key);
        return result;
    }
}

62. 사용자 시간대 기반 데이터 처리 (Time Zone Based Processing)

사용자의 시간대에 맞춰 데이터를 처리.

구현 예제

@Aspect
@Component
public class TimeZoneProcessingAspect {

    @Around("@annotation(com.example.annotations.TimeZoneAware)")
    public Object adjustForTimeZone(ProceedingJoinPoint joinPoint) throws Throwable {
        String userTimeZone = getUserTimeZone();
        System.out.println("Adjusting data for user time zone: " + userTimeZone);

        // 시간대 조정 로직
        Object result = joinPoint.proceed();
        return adjustResultForTimeZone(result, userTimeZone);
    }

    private String getUserTimeZone() {
        // 사용자의 시간대 가져오는 로직 (예: 요청 헤더에서 추출)
        return "Asia/Seoul"; // 예제용
    }

    private Object adjustResultForTimeZone(Object result, String timeZone) {
        // 시간대에 맞게 데이터를 조정하는 로직
        return result; // 예제용
    }
}

63. API 요청별 실행 우선순위 처리 (Priority-Based Execution)

API 요청에 우선순위를 부여하여 중요한 요청을 먼저 처리.

구현 예제

@Aspect
@Component
public class PriorityExecutionAspect {

    @Around("@annotation(com.example.annotations.Priority)")
    public Object prioritizeRequest(ProceedingJoinPoint joinPoint) throws Throwable {
        String priority = getPriorityFromRequest();
        System.out.println("Handling request with priority: " + priority);

        // 우선순위에 따라 처리 로직 추가
        return joinPoint.proceed();
    }

    private String getPriorityFromRequest() {
        // 요청에서 우선순위를 추출 (예: 요청 헤더 또는 파라미터)
        return "HIGH"; // 예제용
    }
}

64. 요청 응답 시간 로깅 (Request-Response Time Logging)

요청 처리의 전체 시간을 측정하고 로깅.

구현 예제

@Aspect
@Component
public class RequestResponseTimeAspect {

    @Around("execution(* com.example.controller..*(..))")
    public Object logRequestResponseTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long end = System.currentTimeMillis();

        System.out.println("Request to " + joinPoint.getSignature() + " completed in " + (end - start) + " ms");
        return result;
    }
}

65. API 요청 크기 제한 (Request Size Limiting)

API 요청의 데이터 크기를 제한하여 과도한 데이터 전송 방지.

구현 예제

@Aspect
@Component
public class RequestSizeLimitingAspect {

    private static final int MAX_SIZE = 1024 * 1024; // 1MB

    @Before("execution(* com.example.controller..*(..))")
    public void limitRequestSize(JoinPoint joinPoint) {
        for (Object arg : joinPoint.getArgs()) {
            if (arg instanceof String && ((String) arg).length() > MAX_SIZE) {
                throw new IllegalArgumentException("Request size exceeds the allowed limit.");
            }
        }
    }
}

66. 요청 리플레이 방지 (Request Replay Protection)

특정 요청이 중복해서 처리되지 않도록 방지.

구현 예제

@Aspect
@Component
public class ReplayProtectionAspect {

    private final Set<String> processedRequests = Collections.synchronizedSet(new HashSet<>());

    @Before("@annotation(com.example.annotations.ReplayProtected)")
    public void preventReplay(JoinPoint joinPoint) {
        String requestId = extractRequestId();
        if (processedRequests.contains(requestId)) {
            throw new IllegalStateException("Duplicate request detected: " + requestId);
        }
        processedRequests.add(requestId);
    }

    private String extractRequestId() {
        // 요청 ID 추출 로직 (예: 헤더에서 가져오기)
        return UUID.randomUUID().toString(); // 예제용
    }
}

67. 사용자별 데이터 처리 속도 제한 (Rate Limit per User)

사용자별 데이터 처리 속도를 제한.

구현 예제

@Aspect
@Component
public class UserRateLimitAspect {

    private final Map<String, Long> userLastRequestTime = new ConcurrentHashMap<>();
    private static final long MIN_INTERVAL = 2000; // 2초 간격

    @Before("execution(* com.example.controller..*(..))")
    public void enforceUserRateLimit() {
        String userId = getCurrentUserId();
        long now = System.currentTimeMillis();

        if (userLastRequestTime.containsKey(userId)) {
            long lastRequestTime = userLastRequestTime.get(userId);
            if (now - lastRequestTime < MIN_INTERVAL) {
                throw new RuntimeException("User " + userId + " is sending requests too quickly.");
            }
        }

        userLastRequestTime.put(userId, now);
    }

    private String getCurrentUserId() {
        // 사용자 ID 가져오기 로직
        return "user123"; // 예제용
    }
}

68. 메서드 호출 통계 (Method Invocation Statistics)

특정 메서드가 얼마나 자주 호출되는지 기록하고 분석.

구현 예제

@Aspect
@Component
public class MethodInvocationStatisticsAspect {

    private final Map<String, Integer> methodCallCounts = new ConcurrentHashMap<>();

    @After("execution(* com.example.service..*(..))")
    public void trackMethodCalls(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().toShortString();
        methodCallCounts.put(methodName, methodCallCounts.getOrDefault(methodName, 0) + 1);
        System.out.println("Method " + methodName + " has been called " + methodCallCounts.get(methodName) + " times.");
    }
}

69. 특정 필드 제거 후 로깅 (Exclude Sensitive Fields)

로그 출력 시 민감한 데이터를 제거합니다.

구현 예제

@Aspect
@Component
public class SensitiveFieldRemovalAspect {

    @Around("execution(* com.example.service..*(..))")
    public Object removeSensitiveFieldsFromLog(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();

        if (result instanceof Map) {
            Map<String, Object> data = (Map<String, Object>) result;
            data.remove("password");
            data.remove("creditCardNumber");
        }

        return result;
    }
}

70. API 사용량 모니터링 (API Usage Monitoring)

API 호출 사용량을 추적하여 과도한 요청에 대한 경고를 제공합니다.

구현 예제

@Aspect
@Component
public class ApiUsageMonitoringAspect {

    private final Map<String, Integer> apiUsageCounts = new ConcurrentHashMap<>();
    private static final int MAX_USAGE_LIMIT = 100;

    @After("execution(* com.example.controller..*(..))")
    public void monitorApiUsage(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().toShortString();
        apiUsageCounts.put(methodName, apiUsageCounts.getOrDefault(methodName, 0) + 1);

        if (apiUsageCounts.get(methodName) > MAX_USAGE_LIMIT) {
            System.err.println("Warning: API usage limit exceeded for " + methodName);
        }
    }
}

71. 요청 응답 디버깅 (Request-Response Debugging)

요청 및 응답 데이터를 캡처하고 디버깅 로그를 출력.

구현 예제

@Aspect
@Component
public class RequestResponseDebuggingAspect {

    @Around("execution(* com.example.controller..*(..))")
    public Object logRequestAndResponse(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Request to: " + joinPoint.getSignature());
        System.out.println("Arguments: " + Arrays.toString(joinPoint.getArgs()));

        Object response = joinPoint.proceed();

        System.out.println("Response from: " + joinPoint.getSignature());
        System.out.println("Response Data: " + response);
        return response;
    }
}

72. 오래 실행되는 요청 탐지 (Long-Running Request Detection)

특정 시간 이상 실행되는 요청을 탐지하여 경고.

구현 예제

@Aspect
@Component
public class LongRunningRequestAspect {

    private static final long WARNING_THRESHOLD = 3000; // 3초

    @Around("execution(* com.example.service..*(..))")
    public Object detectLongRunningRequests(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long duration = System.currentTimeMillis() - start;

        if (duration > WARNING_THRESHOLD) {
            System.err.println("Warning: Long-running request detected in " + joinPoint.getSignature() + " (Duration: " + duration + " ms)");
        }

        return result;
    }
}

73. 메서드 실행 제한 시간 초과 경고 (Timeout Warning)

특정 메서드가 지정된 시간 내에 완료되지 않으면 경고를 출력.

구현 예제

@Aspect
@Component
public class MethodTimeoutWarningAspect {

    private static final long TIMEOUT_THRESHOLD = 2000; // 2초

    @Around("execution(* com.example.service..*(..))")
    public Object monitorMethodTimeout(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long duration = System.currentTimeMillis() - start;

        if (duration > TIMEOUT_THRESHOLD) {
            System.err.println("Warning: Method " + joinPoint.getSignature() + " took too long (" + duration + " ms)");
        }

        return result;
    }
}

74. 데이터 사본 생성 및 복원 (Backup and Restore Data)

데이터 변경 작업 전에 사본을 생성하고, 실패 시 복원.

구현 예제

@Aspect
@Component
public class DataBackupAspect {

    private final Map<String, Object> backupData = new ConcurrentHashMap<>();

    @Around("@annotation(com.example.annotations.BackupRestore)")
    public Object handleBackupAndRestore(ProceedingJoinPoint joinPoint) throws Throwable {
        String key = joinPoint.getSignature().toString();

        try {
            backupData.put(key, createBackup());
            return joinPoint.proceed();
        } catch (Exception ex) {
            restoreBackup(key);
            throw ex;
        }
    }

    private Object createBackup() {
        // 데이터 백업 로직
        System.out.println("Backup created");
        return "BackupData"; // 예제용
    }

    private void restoreBackup(String key) {
        // 데이터 복원 로직
        System.out.println("Restoring backup for key: " + key);
    }
}

75. HTTP 요청의 헤더 검증 (Request Header Validation)

HTTP 요청 헤더에 필수 정보가 포함되어 있는지 검증.

구현 예제

@Aspect
@Component
public class RequestHeaderValidationAspect {

    @Before("execution(* com.example.controller..*(..))")
    public void validateHeaders() {
        String requiredHeader = getHeader("X-Required-Header");
        if (requiredHeader == null || requiredHeader.isEmpty()) {
            throw new IllegalArgumentException("Missing required header: X-Required-Header");
        }
    }

    private String getHeader(String headerName) {
        // 요청 헤더 가져오기 (HttpServletRequest에서)
        return "ValidHeader"; // 예제용
    }
}

76. 데이터 일관성 확인 (Data Consistency Check)

메서드 호출 후 데이터베이스의 상태를 확인하여 데이터 일관성을 유지.

구현 예제

@Aspect
@Component
public class DataConsistencyAspect {

    @After("execution(* com.example.repository..*(..))")
    public void checkDataConsistency() {
        if (!isDataConsistent()) {
            System.err.println("Data inconsistency detected!");
            handleInconsistency();
        }
    }

    private boolean isDataConsistent() {
        // 데이터 일관성 확인 로직
        return true; // 예제용
    }

    private void handleInconsistency() {
        // 데이터 불일치 처리 로직
        System.out.println("Handling data inconsistency...");
    }
}

77. 동적 기능 활성화 (Dynamic Feature Toggle)

외부 설정값에 따라 메서드 실행 여부를 동적으로 제어.

구현 예제

@Aspect
@Component
public class FeatureToggleAspect {

    @Around("@annotation(com.example.annotations.FeatureToggle)")
    public Object handleFeatureToggle(ProceedingJoinPoint joinPoint) throws Throwable {
        String featureName = getFeatureName(joinPoint);
        if (isFeatureEnabled(featureName)) {
            System.out.println("Feature " + featureName + " is enabled");
            return joinPoint.proceed();
        } else {
            System.out.println("Feature " + featureName + " is disabled");
            return null;
        }
    }

    private String getFeatureName(ProceedingJoinPoint joinPoint) {
        // 어노테이션에서 기능 이름 추출
        return "NewFeature"; // 예제용
    }

    private boolean isFeatureEnabled(String featureName) {
        // 외부 설정값으로 기능 활성화 여부 결정
        return true; // 예제용
    }
}

78. API 응답 캐시 무효화 (Invalidate Cache on Response)

특정 조건에서 API 응답 캐시를 무효화.

구현 예제

@Aspect
@Component
public class CacheInvalidationAspect {

    @After("@annotation(com.example.annotations.InvalidateCache)")
    public void invalidateCache() {
        System.out.println("Invalidating cache for the current operation...");
        // 캐시 무효화 로직
    }
}

79. 메서드 호출 흐름 추적 (Call Flow Tracing)

메서드 호출 흐름을 추적하여 디버깅.

구현 예제

@Aspect
@Component
public class CallFlowTracingAspect {

    private final ThreadLocal<Stack<String>> callStack = ThreadLocal.withInitial(Stack::new);

    @Before("execution(* com.example..*(..))")
    public void beforeMethod(JoinPoint joinPoint) {
        callStack.get().push(joinPoint.getSignature().toShortString());
        System.out.println("Entering: " + joinPoint.getSignature());
    }

    @After("execution(* com.example..*(..))")
    public void afterMethod(JoinPoint joinPoint) {
        System.out.println("Exiting: " + callStack.get().pop());
    }
}

80. 트랜잭션 롤백 후 처리 (Post Rollback Handling)

트랜잭션이 롤백된 후 추가 작업을 수행.

구현 예제

@Aspect
@Component
public class RollbackHandlingAspect {

    @AfterThrowing(pointcut = "execution(* com.example.service..*(..)) && @annotation(org.springframework.transaction.annotation.Transactional)", throwing = "ex")
    public void handleRollback(Throwable ex) {
        System.err.println("Transaction rolled back due to: " + ex.getMessage());
        // 롤백 후 작업 (예: 알림 전송, 복구 작업)
    }
}

81. 사용자 활동의 세부 로그 생성 (Detailed User Activity Logging)

사용자가 수행한 작업의 세부 로그를 생성하여 분석.

구현 예제

@Aspect
@Component
public class DetailedUserActivityAspect {

    @After("execution(* com.example.controller..*(..))")
    public void logDetailedUserActivity(JoinPoint joinPoint) {
        String userId = getCurrentUserId();
        String methodName = joinPoint.getSignature().toShortString();
        System.out.println("User " + userId + " accessed " + methodName);
        saveDetailedActivityLog(userId, methodName);
    }

    private void saveDetailedActivityLog(String userId, String action) {
        // 사용자 활동 로그 저장 로직
        System.out.println("Activity saved: User=" + userId + ", Action=" + action);
    }

    private String getCurrentUserId() {
        return "user123"; // 예제용
    }
}

82. 글로벌 메서드 실행 모니터링 (Global Method Execution Monitoring)

애플리케이션 내에서 모든 메서드 실행을 모니터링하여 통계 생성.

구현 예제

@Aspect
@Component
public class GlobalMethodMonitoringAspect {

    private final Map<String, Long> executionCounts = new ConcurrentHashMap<>();
    private final Map<String, Long> executionDurations = new ConcurrentHashMap<>();

    @Around("execution(* com.example..*(..))")
    public Object monitorMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().toShortString();
        long start = System.currentTimeMillis();

        Object result = joinPoint.proceed();

        long duration = System.currentTimeMillis() - start;
        executionCounts.put(methodName, executionCounts.getOrDefault(methodName, 0L) + 1);
        executionDurations.put(methodName, executionDurations.getOrDefault(methodName, 0L) + duration);

        System.out.println("Executed " + methodName + " in " + duration + " ms");
        return result;
    }

    public void printStatistics() {
        executionCounts.forEach((method, count) -> {
            long totalDuration = executionDurations.get(method);
            System.out.println("Method: " + method + ", Count: " + count + ", Avg Time: " + (totalDuration / count) + " ms");
        });
    }
}

83. 비동기 메서드 병렬 실행 제한 (Async Method Parallel Execution Limit)

비동기로 실행되는 메서드의 병렬 실행 개수를 제한.

구현 예제

@Aspect
@Component
public class AsyncParallelExecutionLimitAspect {

    private final Semaphore semaphore = new Semaphore(5); // 최대 병렬 실행 개수 제한

    @Around("@annotation(org.springframework.scheduling.annotation.Async)")
    public Object limitAsyncExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        if (!semaphore.tryAcquire()) {
            throw new RuntimeException("Too many parallel executions. Try again later.");
        }
        try {
            return joinPoint.proceed();
        } finally {
            semaphore.release();
        }
    }
}

84. 동적 서비스 변경 (Dynamic Service Switching)

특정 조건에 따라 서비스 인스턴스를 동적으로 변경.

구현 예제

@Aspect
@Component
public class DynamicServiceSwitchingAspect {

    @Around("execution(* com.example.service..*(..))")
    public Object switchService(ProceedingJoinPoint joinPoint) throws Throwable {
        if (shouldUseAlternativeService()) {
            System.out.println("Using alternative service for: " + joinPoint.getSignature());
            return useAlternativeService(joinPoint);
        }
        return joinPoint.proceed();
    }

    private boolean shouldUseAlternativeService() {
        // 서비스 변경 조건
        return Math.random() > 0.5; // 예제용
    }

    private Object useAlternativeService(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Alternative service logic executed.");
        // 실제로 대체 서비스 호출 로직 작성
        return null; // 예제용
    }
}

85. 데이터 조회 실패 대체 데이터 제공 (Fallback Data Provider)

데이터 조회 실패 시 기본값 또는 캐시 데이터를 제공.

구현 예제

@Aspect
@Component
public class FallbackDataAspect {

    @Around("execution(* com.example.repository..*(..))")
    public Object provideFallbackData(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            return joinPoint.proceed();
        } catch (Exception ex) {
            System.err.println("Failed to fetch data. Providing fallback.");
            return getFallbackData(joinPoint);
        }
    }

    private Object getFallbackData(ProceedingJoinPoint joinPoint) {
        // 대체 데이터 제공 로직
        return "FallbackData"; // 예제용
    }
}

86. 사용자 지정 요청 추적 ID 생성 (Custom Request Trace ID)

각 요청에 고유한 추적 ID를 생성하여 로깅과 디버깅에 활용.

구현 예제

@Aspect
@Component
public class RequestTraceIdAspect {

    private final ThreadLocal<String> traceId = new ThreadLocal<>();

    @Before("execution(* com.example.controller..*(..))")
    public void generateTraceId() {
        traceId.set(UUID.randomUUID().toString());
        System.out.println("Generated Trace ID: " + traceId.get());
    }

    @After("execution(* com.example.controller..*(..))")
    public void clearTraceId() {
        System.out.println("Clearing Trace ID: " + traceId.get());
        traceId.remove();
    }

    public String getTraceId() {
        return traceId.get();
    }
}

87. 사용자 세션 기반 접근 제어 (Session-Based Access Control)

사용자 세션 상태에 따라 특정 API 접근을 제한.

구현 예제

@Aspect
@Component
public class SessionAccessControlAspect {

    @Before("@annotation(com.example.annotations.SessionRestricted)")
    public void restrictAccessBasedOnSession() {
        if (!isSessionActive()) {
            throw new IllegalStateException("User session is not active.");
        }
    }

    private boolean isSessionActive() {
        // 세션 상태 확인 로직
        return true; // 예제용
    }
}

88. 캐시 업데이트 시 알림 전송 (Notify on Cache Update)

캐시가 업데이트될 때 관리자나 모니터링 시스템에 알림을 전송.

구현 예제

@Aspect
@Component
public class CacheUpdateNotificationAspect {

    @After("@annotation(org.springframework.cache.annotation.CachePut)")
    public void notifyCacheUpdate() {
        System.out.println("Cache updated. Sending notification...");
        sendCacheUpdateNotification();
    }

    private void sendCacheUpdateNotification() {
        // 알림 전송 로직 (예: 이메일, 슬랙)
        System.out.println("Notification sent.");
    }
}

89. 사용자별 트랜잭션 추적 (User-Specific Transaction Tracking)

각 사용자별로 트랜잭션 실행 내역을 추적.

구현 예제

@Aspect
@Component
public class UserTransactionTrackingAspect {

    @AfterReturning("@annotation(org.springframework.transaction.annotation.Transactional)")
    public void trackTransaction(JoinPoint joinPoint) {
        String userId = getCurrentUserId();
        String methodName = joinPoint.getSignature().toShortString();
        System.out.println("Transaction completed by user: " + userId + " on method: " + methodName);
        saveTransactionLog(userId, methodName);
    }

    private String getCurrentUserId() {
        // 사용자 ID 가져오기 로직
        return "user123"; // 예제용
    }

    private void saveTransactionLog(String userId, String methodName) {
        // 트랜잭션 로그 저장
        System.out.println("Transaction log saved for user: " + userId + ", method: " + methodName);
    }
}

90. 커스텀 메서드 리턴값 변환 (Custom Return Value Transformation)

특정 메서드의 반환값을 동적으로 변환.

구현 예제

@Aspect
@Component
public class ReturnValueTransformationAspect {

    @AfterReturning(pointcut = "execution(* com.example.service..*(..))", returning = "result")
    public Object transformReturnValue(JoinPoint joinPoint, Object result) {
        if (result instanceof String) {
            return ((String) result).toUpperCase();
        }
        return result;
    }
}

91. 조건부 로깅 (Conditional Logging)

특정 조건을 만족할 때만 메서드 실행을 로깅합니다.

구현 예제

@Aspect
@Component
public class ConditionalLoggingAspect {

    @Around("execution(* com.example.service..*(..)) && @annotation(com.example.annotations.ConditionalLog)")
    public Object logConditionally(ProceedingJoinPoint joinPoint) throws Throwable {
        if (shouldLog(joinPoint)) {
            System.out.println("Executing method: " + joinPoint.getSignature());
        }
        return joinPoint.proceed();
    }

    private boolean shouldLog(ProceedingJoinPoint joinPoint) {
        // 조건 정의 (예: 특정 파라미터 값, 현재 환경 등)
        return true; // 예제에서는 항상 로깅
    }
}

92. 데이터 입력 이력 관리 (Data Input History Tracking)

모든 데이터 입력 내역을 저장하여 추적 가능하도록 만듭니다.

구현 예제

@Aspect
@Component
public class DataInputHistoryAspect {

    @After("execution(* com.example.service..*(..))")
    public void trackInputHistory(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            saveInputHistory(arg);
        }
    }

    private void saveInputHistory(Object input) {
        // 데이터 입력 이력 저장 로직
        System.out.println("Input history saved: " + input);
    }
}

93. 메서드 호출 순서 보장 (Ensure Method Invocation Order)

특정 메서드 호출 순서를 보장하여 의도한 흐름을 유지합니다.

구현 예제

@Aspect
@Component
public class MethodInvocationOrderAspect {

    private final Queue<String> expectedOrder = new LinkedList<>(List.of(
        "firstMethod", "secondMethod", "thirdMethod"
    ));

    @Before("execution(* com.example.service..*(..))")
    public void ensureOrder(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        if (!expectedOrder.isEmpty() && !expectedOrder.peek().equals(methodName)) {
            throw new IllegalStateException("Method " + methodName + " called out of order.");
        }
        expectedOrder.poll();
    }
}

94. 특정 파라미터 기반 접근 제어 (Parameter-Based Access Control)

메서드 파라미터 값에 따라 실행 여부를 제어.

구현 예제

@Aspect
@Component
public class ParameterBasedAccessControlAspect {

    @Before("execution(* com.example.service..*(..)) && args(param,..)")
    public void restrictAccess(Object param) {
        if (!hasAccess(param)) {
            throw new SecurityException("Access denied for parameter: " + param);
        }
    }

    private boolean hasAccess(Object param) {
        // 접근 권한 확인 로직 (예: 특정 값 허용 여부)
        return !"restricted".equals(param);
    }
}

95. 요청 데이터 변환 (Request Data Transformation)

요청 데이터를 변환하여 메서드가 처리하기 쉽게 조정.

구현 예제

@Aspect
@Component
public class RequestDataTransformationAspect {

    @Around("execution(* com.example.controller..*(..))")
    public Object transformRequestData(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            if (args[i] instanceof String) {
                args[i] = preprocessData((String) args[i]);
            }
        }
        return joinPoint.proceed(args);
    }

    private String preprocessData(String data) {
        // 데이터 전처리 로직 (예: 대문자 변환)
        return data.trim().toUpperCase();
    }
}

96. 트랜잭션 내 특정 작업 감시 (Monitor Specific Actions in Transactions)

트랜잭션 내부에서 특정 작업이 발생하는지 모니터링.

구현 예제

@Aspect
@Component
public class TransactionMonitoringAspect {

    @Around("@annotation(org.springframework.transaction.annotation.Transactional)")
    public Object monitorTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Transaction started: " + joinPoint.getSignature());
        try {
            return joinPoint.proceed();
        } finally {
            System.out.println("Transaction completed: " + joinPoint.getSignature());
        }
    }
}

97. 동적 메서드 실행 제한 (Dynamic Execution Blocking)

환경이나 조건에 따라 메서드 실행을 차단.

구현 예제

@Aspect
@Component
public class DynamicExecutionBlockingAspect {

    @Before("execution(* com.example.service..*(..)) && @annotation(com.example.annotations.DynamicBlock)")
    public void blockExecution(JoinPoint joinPoint) {
        if (shouldBlockExecution()) {
            throw new IllegalStateException("Method execution blocked: " + joinPoint.getSignature());
        }
    }

    private boolean shouldBlockExecution() {
        // 차단 조건 (예: 특정 환경, 설정값 등)
        return false; // 예제에서는 차단하지 않음
    }
}

98. HTTP 응답 데이터 필터링 (Response Data Filtering)

API 응답에서 민감한 정보를 필터링.

구현 예제

@Aspect
@Component
public class ResponseDataFilteringAspect {

    @Around("execution(* com.example.controller..*(..))")
    public Object filterResponseData(ProceedingJoinPoint joinPoint) throws Throwable {
        Object response = joinPoint.proceed();

        if (response instanceof Map) {
            Map<String, Object> data = (Map<String, Object>) response;
            data.remove("password");
            data.remove("ssn"); // 민감 데이터 제거
        }

        return response;
    }
}

99. 대규모 데이터 처리 모니터링 (Large Data Processing Monitoring)

대규모 데이터 처리 시 성능을 추적하고 경고.

구현 예제

@Aspect
@Component
public class LargeDataProcessingMonitoringAspect {

    private static final int LARGE_DATA_THRESHOLD = 1000;

    @Around("execution(* com.example.service..*(..))")
    public Object monitorLargeDataProcessing(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();

        for (Object arg : args) {
            if (arg instanceof List && ((List<?>) arg).size() > LARGE_DATA_THRESHOLD) {
                System.err.println("Warning: Large data processing detected in method: " + joinPoint.getSignature());
            }
        }

        return joinPoint.proceed();
    }
}

100. 요청 ID 전파 (Request ID Propagation)

요청 ID를 메서드 호출 체인 전반에 전파하여 추적 가능.

구현 예제

@Aspect
@Component
public class RequestIdPropagationAspect {

    private static final ThreadLocal<String> requestId = ThreadLocal.withInitial(() -> UUID.randomUUID().toString());

    @Before("execution(* com.example.controller..*(..))")
    public void propagateRequestId() {
        System.out.println("Propagating Request ID: " + requestId.get());
    }

    public static String getRequestId() {
        return requestId.get();
    }
}