init: 클린 기반 auth 서버 설계

This commit is contained in:
DongHyeonka
2026-07-24 14:30:18 +09:00
parent 471db0203d
commit 8a1ac1e769
3642 changed files with 275893 additions and 1 deletions
+171
View File
@@ -0,0 +1,171 @@
# 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;
}
}
```
**문제:**
- 실패를 정상값처럼 숨긴다
- 디버깅과 계약을 깨뜨린다