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
+168
View File
@@ -0,0 +1,168 @@
# AOP 사용 기준
## 목적
AOP는 비즈니스 로직을 숨기는 우회 수단이 아니라,
여러 타입과 객체를 가로지르는 **cross-cutting concern** 을 모듈화할 때만 사용한다.
## 공식 의미
- Spring AOP는 OOP를 보완하는 방식이다.
- AOP의 핵심 단위는 class가 아니라 aspect다.
- Spring AOP는 proxy-based다.
- proxy-based AOP에서는 proxy를 통과하는 외부 호출만 advice가 적용된다.
- self-invocation은 기본적으로 advice가 적용되지 않는다.
- Spring은 AOP의 대표적 용도로 declarative transaction 같은 cross-cutting concern을 든다.
- pointcut은 더 작은 named pointcut으로 조합하는 것이 권장된다.
- 대부분의 경우 static pointcut이 충분하고 더 낫다.
## 기본 규칙
### 1. AOP는 cross-cutting concern에만 사용
다음은 AOP 후보가 된다.
- 공통 로깅
- 메트릭/트레이싱
- 권한 체크의 반복 경계
- 재시도/타이밍 측정
- 공통 감사(audit)
- 선언적 트랜잭션
다음은 AOP로 풀지 않는다.
- 핵심 비즈니스 흐름
- 상태 전이 정책
- 도메인 규칙
- 복잡한 분기 로직
- 외부 API orchestration
### 2. 기본값은 “명시적 코드”, 예외적으로 AOP
같은 기능을 명시적 코드로 더 잘 읽을 수 있으면 AOP를 쓰지 않는다.
기본:
- use case/service 안에서 명시적으로 보이는 흐름 우선
- 반복되는 횡단 관심사만 AOP로 추출
### 3. proxy-based 한계를 항상 전제로 한다
Spring AOP는 proxy 기반이므로 다음을 전제로 설계한다.
- proxy를 통과하는 외부 호출만 interception
- self-invocation은 적용되지 않음
- “같은 클래스 안에서 호출되면 aspect가 붙겠지”를 금지
### 4. self-invocation 해결을 위해 AOP 남용 금지
self-invocation 문제를 해결하려고 다음을 기본 금지한다.
- 자기 자신 proxy 주입
- `AopContext.currentProxy()` 의존
- 구현을 proxy semantics에 강하게 묶는 설계
기본 대응:
- 경계를 다시 분리
- 클래스를 분리
- 더 명시적인 구조로 변경
### 5. AOP는 경계가 뚜렷한 곳에만 적용
좋은 적용 지점:
- service/use-case public method
- controller 경계
- repository 경계
- 명시된 package/bean naming convention
지양:
- 너무 넓은 전체 패키지
- “일단 다 잡고 보자” 식 표현식
- private/internal 세부 구현까지 얽는 pointcut
### 6. pointcut은 작고 이름 있게 조합
공식 권장대로 pointcut은 작은 named pointcut을 조합해 만든다.
기본:
- package 범위 pointcut
- role 기반 pointcut
- public method pointcut
- bean naming 기반 pointcut
를 분리하고 조합한다.
### 7. 대부분 static pointcut 우선
동적 조건보다 static pointcut이 충분하면 static 쪽을 우선한다.
성능/이해도/예측 가능성이 더 좋다.
### 8. `@Around`는 최소화
`@Around`는 가장 강력하지만 가장 위험하다.
반환값/예외/호출 자체를 제어할 수 있으므로 꼭 필요할 때만 쓴다.
기본 우선순위:
- 단순 전처리 -> `@Before`
- 정상 반환 후 후처리 -> `@AfterReturning`
- 예외 기록/번역 -> `@AfterThrowing`
- 무조건 정리 -> `@After`
- 호출 제어/타이밍/재시도 등 정말 필요할 때만 `@Around`
### 9. advice 안에서 비즈니스 의미를 새로 만들지 않는다
advice는 보조 concern을 수행해야 한다.
금지:
- 상태 전이 결정
- 비즈니스 실패를 성공처럼 바꾸기
- 핵심 정책 우회
- controller/service가 해야 할 결정을 aspect에서 대신하기
### 10. 예외를 숨기지 않는다
AOP에서 예외를 잡더라도 기본은:
- 기록
- 문맥 추가
- 그대로 전파
중 하나다.
금지:
- 예외 삼키기
- 정상값으로 은폐
- 실패를 조용히 무시
### 11. 트랜잭션 대체 수단으로 일반 AOP를 남용하지 않는다
선언적 트랜잭션은 Spring이 제공하는 표준 메커니즘을 우선 사용한다.
일반 custom aspect로 transaction semantics를 흉내 내지 않는다.
### 12. AOP는 observability / policy enforcement에 더 적합
프로젝트에서 AOP는 아래 유형에 더 적합하다.
- 실행 시간 측정
- 공통 로깅
- 감사 기록
- annotation 기반 정책 강제
- 공통 예외 기록
복잡한 use case orchestration에는 부적합하다.
### 13. pointcut 범위는 문서화 가능해야 한다
pointcut을 보고 아래를 설명할 수 있어야 한다.
- 어디에 적용되는가
- 왜 거기에만 적용되는가
- 새 코드가 추가되면 어떤 naming/package 규칙으로 포함되는가
설명하기 어려우면 범위가 너무 넓거나 모호한 것이다.
### 14. bean naming / package convention을 설계와 함께 쓴다
Spring 공식 문서가 bean PCD나 package 기반 pointcut 예시를 드는 것처럼,
AOP를 쓸 거면 package 구조나 bean naming convention이 일정해야 한다.
즉:
- `*Service`
- `..application..`
- `..infrastructure..`
같은 규칙은 pointcut과 함께 관리한다.
## 프로젝트 기준 요약
- AOP는 cross-cutting concern에만 사용
- 기본값은 명시적 코드
- Spring AOP는 proxy-based라는 점을 전제로 설계
- self-invocation 기대 금지
- pointcut은 작고 이름 있게 조합
- 대부분 static pointcut 우선
- `@Around` 최소화
- advice에서 비즈니스 의미를 만들지 않음
- 예외를 숨기지 않음