Files
2nd/10_Wiki/Topics/Architecture/Aspect-Oriented Programming (AOP).md
T
koriweb d8a80f6272 chore(wiki): dangling 링크 canonical 정규화 (768파일/1200건)
이름만 다른(표기 변형) [[위키링크]]를 대상 문서의 canonical 제목으로 치환해
끊겼던 1,200개 링크를 연결. 제목/파일명 정규화 일치만 적용하고 별칭 매칭은
과병합 위험으로 제외(애매성 가드). 원본은 _link_reconcile_backup/ 에 백업.
도구: Datacollect/scripts/link_reconcile_apply.mjs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:24:15 +09:00

7.2 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-aspect-oriented-programming-aop Aspect Oriented Programming (AOP) 10_Wiki/Topics verified self
AOP
Aspect Programming
Cross-cutting Concerns
none A 0.9 applied
architecture
paradigm
spring
decorator
2026-05-10 pending
language framework
Java / TypeScript / Python Spring AOP / AspectJ / NestJS

Aspect-Oriented Programming (AOP)

매 한 줄

"매 cross-cutting concern을 separate aspect로 modularize.". 1997년 Gregor Kiczales (Xerox PARC) 의 AspectJ 가 시초. 2026 현재 Spring AOP 6.x, NestJS interceptor, Python decorator 가 주류 — logging/transaction/security 같이 매 객체 가로지르는 logic을 매 한 곳에 모음.

매 핵심

매 용어

  • Aspect: 매 cross-cutting concern 의 modular 단위 (e.g. LoggingAspect).
  • Join Point: 매 program execution 의 한 지점 (method call, field access).
  • Pointcut: 매 join point 의 selection expression.
  • Advice: 매 pointcut 매칭 시 실행할 code (@Before, @After, @Around).
  • Weaving: 매 aspect 와 base code 결합 — compile-time / load-time / runtime.

매 cross-cutting concerns

  • Logging / tracing
  • Transaction management
  • Security / authorization
  • Caching
  • Performance monitoring
  • Error handling / retry
  • Audit trail

매 응용

  1. Spring @Transactional — DB transaction 자동 commit/rollback.
  2. NestJS @UseInterceptors — request/response transform.
  3. Python @functools.lru_cache — pure caching aspect.
  4. OpenTelemetry auto-instrumentation — runtime byte-code weaving.

💻 패턴

Spring AOP — @Around aspect

@Aspect
@Component
public class LoggingAspect {

    @Around("execution(* com.example.service.*.*(..))")
    public Object logExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            Object result = pjp.proceed();
            long elapsed = System.currentTimeMillis() - start;
            log.info("{} took {} ms", pjp.getSignature(), elapsed);
            return result;
        } catch (Throwable t) {
            log.error("{} threw {}", pjp.getSignature(), t.getMessage());
            throw t;
        }
    }
}

Spring — @Transactional declarative TX

@Service
public class OrderService {

    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public Order placeOrder(OrderRequest req) {
        Order order = orderRepo.save(new Order(req));
        inventoryRepo.decrement(req.getItems());
        // Any RuntimeException → automatic rollback via AOP proxy
        return order;
    }
}

NestJS — Interceptor (AOP-style)

import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable, tap } from 'rxjs';

@Injectable()
export class TimingInterceptor implements NestInterceptor {
  intercept(ctx: ExecutionContext, next: CallHandler): Observable<unknown> {
    const start = Date.now();
    return next.handle().pipe(
      tap(() => console.log(`${ctx.getHandler().name} took ${Date.now() - start}ms`)),
    );
  }
}

@Controller('orders')
@UseInterceptors(TimingInterceptor)
export class OrdersController { /* ... */ }

Python — Decorator as aspect

import functools, time, logging

def timed(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        try:
            return func(*args, **kwargs)
        finally:
            elapsed = time.perf_counter() - start
            logging.info(f"{func.__name__} took {elapsed*1000:.1f}ms")
    return wrapper

@timed
def expensive_calc(n: int) -> int:
    return sum(i * i for i in range(n))

Python — Class-based retry aspect

def retry(times: int = 3, on: tuple = (Exception,)):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            last = None
            for attempt in range(times):
                try:
                    return func(*args, **kwargs)
                except on as e:
                    last = e
                    time.sleep(2 ** attempt)
            raise last
        return wrapper
    return decorator

@retry(times=5, on=(httpx.HTTPError,))
def fetch_remote(url: str) -> dict:
    return httpx.get(url).json()

AspectJ pointcut DSL

@Pointcut("execution(public * com.example.repo.*.find*(..))")
public void anyFinder() {}

@Pointcut("@annotation(com.example.Audited)")
public void auditedMethod() {}

@Before("anyFinder() && auditedMethod()")
public void audit(JoinPoint jp) {
    auditService.log(jp.getSignature().toShortString(), Instant.now());
}

TypeScript — method decorator

function Cached(ttlMs: number): MethodDecorator {
  const cache = new Map<string, { value: unknown; exp: number }>();
  return (_t, _k, desc: PropertyDescriptor) => {
    const original = desc.value;
    desc.value = function (...args: unknown[]) {
      const key = JSON.stringify(args);
      const entry = cache.get(key);
      if (entry && entry.exp > Date.now()) return entry.value;
      const value = original.apply(this, args);
      cache.set(key, { value, exp: Date.now() + ttlMs });
      return value;
    };
  };
}

class Pricing {
  @Cached(60_000)
  fetchRate(currency: string) { /* ... */ }
}

매 결정 기준

상황 Approach
Java enterprise, declarative TX Spring AOP (proxy-based)
매 method 매칭 + field access weaving AspectJ (compile/load-time)
Node.js HTTP layer NestJS Interceptor / Guard
Python script / function-level Decorator
Cross-language tracing OpenTelemetry auto-instrumentation

기본값: framework-native (Spring AOP / NestJS interceptor / decorator). 매 raw AspectJ 는 매 매우 specific 한 경우만.

🔗 Graph

🤖 LLM 활용

언제: cross-cutting concern (logging/TX/security/caching) 이 매 여러 service 에 반복, declarative style 선호, framework 가 AOP 지원. 언제 X: 매 한두 곳만 쓰는 logic (직접 호출이 명확), 매 magic 회피 culture, 매 debug 어려움 감수 못할 때.

안티패턴

  • Aspect 안 business logic: 매 aspect 는 매 cross-cutting 만 — 매 domain rule 넣지 X.
  • 너무 broad pointcut: execution(* *.*(..)) — 매 unintended weaving.
  • Self-invocation 의 proxy bypass: 매 같은 class 안 method call 은 매 proxy 통과 안 함 (Spring).
  • Order 의존: 매 multiple aspect 매 ordering 명시 X 면 매 비결정적.
  • Aspect explosion: 매 100+ aspect → 매 control flow 추적 불가.

🧪 검증 / 중복

  • Verified (Spring Framework 6.x docs, AspectJ programming guide, NestJS docs).
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — AOP full content with Spring/NestJS/Python patterns