172 lines
3.6 KiB
Markdown
172 lines
3.6 KiB
Markdown
# AOP 예시
|
|
|
|
## 좋은 예시 1: 실행 시간 측정
|
|
|
|
```java
|
|
@Aspect
|
|
@Component
|
|
public class TimingAspect {
|
|
|
|
@Pointcut("execution(public * com.project.auth.application..*(..))")
|
|
public void applicationOperation() {}
|
|
|
|
@Around("applicationOperation()")
|
|
public Object measure(ProceedingJoinPoint joinPoint) throws Throwable {
|
|
long start = System.nanoTime();
|
|
try {
|
|
return joinPoint.proceed();
|
|
} finally {
|
|
long elapsed = System.nanoTime() - start;
|
|
log.info("method={} elapsedNanos={}", joinPoint.getSignature(), elapsed);
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**왜 좋은가:**
|
|
|
|
- 횡단 관심사인 timing만 다룬다
|
|
- 비즈니스 로직을 바꾸지 않는다
|
|
- pointcut이 이름 있는 작은 단위다
|
|
|
|
## 좋은 예시 2: 예외 기록
|
|
|
|
```java
|
|
@Aspect
|
|
@Component
|
|
public class ExceptionLoggingAspect {
|
|
|
|
@Pointcut("execution(public * com.project.auth.application..*(..))")
|
|
public void applicationOperation() {}
|
|
|
|
@AfterThrowing(pointcut = "applicationOperation()", throwing = "exception")
|
|
public void logFailure(Exception exception) {
|
|
log.error("application failure", exception);
|
|
}
|
|
}
|
|
```
|
|
|
|
**왜 좋은가:**
|
|
|
|
- 예외를 숨기지 않고 기록만 한다
|
|
- 비즈니스 의미를 변경하지 않는다
|
|
|
|
## 좋은 예시 3: annotation 기반 감사
|
|
|
|
```java
|
|
@Target(ElementType.METHOD)
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
public @interface Audited {
|
|
String action();
|
|
}
|
|
|
|
@Aspect
|
|
@Component
|
|
public class AuditAspect {
|
|
|
|
@Around("@annotation(audited)")
|
|
public Object audit(ProceedingJoinPoint joinPoint, Audited audited) throws Throwable {
|
|
Object result = joinPoint.proceed();
|
|
auditLog.record(audited.action(), joinPoint.getSignature().toShortString());
|
|
return result;
|
|
}
|
|
}
|
|
```
|
|
|
|
**왜 좋은가:**
|
|
|
|
- annotation으로 의도를 드러낸다
|
|
- 횡단 concern만 수행한다
|
|
- service 흐름을 숨기지 않는다
|
|
|
|
## 좋은 예시 4: named pointcut 조합
|
|
|
|
```java
|
|
@Aspect
|
|
@Component
|
|
public class CommonPointcuts {
|
|
|
|
@Pointcut("execution(public * *(..))")
|
|
public void publicMethod() {}
|
|
|
|
@Pointcut("within(com.project.auth.application..*)")
|
|
public void inApplicationLayer() {}
|
|
|
|
@Pointcut("publicMethod() && inApplicationLayer()")
|
|
public void applicationPublicOperation() {}
|
|
}
|
|
```
|
|
|
|
**왜 좋은가:**
|
|
|
|
- 작은 pointcut을 조합한다
|
|
- 범위를 읽고 설명하기 쉽다
|
|
|
|
## 나쁜 예시 1: 핵심 비즈니스 로직을 AOP로 이동
|
|
|
|
```java
|
|
@Around("execution(* ..LoginService.login(..))")
|
|
public Object issueTokenAndSaveAudit(ProceedingJoinPoint joinPoint) throws Throwable {
|
|
...
|
|
}
|
|
```
|
|
|
|
**문제:**
|
|
|
|
- 핵심 use case 흐름이 숨는다
|
|
- 코드 추적이 어려워진다
|
|
- 서비스가 해야 할 결정을 aspect가 가져간다
|
|
|
|
## 나쁜 예시 2: self-invocation 기대
|
|
|
|
```java
|
|
@Service
|
|
public class SampleService {
|
|
|
|
public void foo() {
|
|
this.bar(); // aspect 기대
|
|
}
|
|
|
|
public void bar() {
|
|
...
|
|
}
|
|
}
|
|
```
|
|
|
|
**문제:**
|
|
|
|
- proxy를 통과하지 않아 advice가 적용되지 않을 수 있다
|
|
|
|
## 나쁜 예시 3: 너무 넓은 pointcut
|
|
|
|
```java
|
|
@Before("execution(* *(..))")
|
|
public void logEverything() {
|
|
...
|
|
}
|
|
```
|
|
|
|
**문제:**
|
|
|
|
- 범위가 지나치게 넓다
|
|
- 성능/디버깅/예측 가능성 모두 나빠질 수 있다
|
|
- 어떤 코드가 영향을 받는지 설명하기 어렵다
|
|
|
|
## 나쁜 예시 4: @Around로 예외 숨김
|
|
|
|
```java
|
|
@Around("execution(* ..*(..))")
|
|
public Object swallow(ProceedingJoinPoint joinPoint) {
|
|
try {
|
|
return joinPoint.proceed();
|
|
} catch (Throwable ex) {
|
|
return null;
|
|
}
|
|
}
|
|
```
|
|
|
|
**문제:**
|
|
|
|
- 실패를 정상값처럼 숨긴다
|
|
- 디버깅과 계약을 깨뜨린다
|