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
+133
View File
@@ -0,0 +1,133 @@
# Integration Idempotency 예시
## 좋은 예시
### 예시 1. provider 공식 header를 adapter에서 설정한다
```java
@Service
@RequiredArgsConstructor
class StripePaymentClient {
private final RestClient restClient;
ChargeResult createCharge(CreateChargeCommand command) {
return restClient.post()
.uri("/v1/payment_intents")
.header("Idempotency-Key", command.providerIdempotencyKey())
.body(StripeCreateChargeRequest.from(command))
.retrieve()
.body(ChargeResult.class);
}
}
```
**좋은 이유:**
- provider 공식 idempotency header를 adapter 경계에서 설정한다
- application/domain이 HTTP 헤더 이름을 몰라도 된다
- 같은 command 재전송 시 같은 key를 쓸 수 있다.
### 예시 2. 내부 command id와 provider key를 분리해 관리한다
```java
public record OutboundCallKey(
String outboundCommandId,
String provider,
String operation,
String providerIdempotencyKey
) {
}
public record OutboundFingerprint(
String requestDigest
) {
}
```
**좋은 이유:**
- 내부 추적 키와 provider 전송 키가 분리된다
- provider별 operation scope 차이를 표현하기 쉽다
- fingerprint 충돌 검사를 붙이기 좋다
### 예시 3. timeout 후 같은 key로 재전송한다
```java
try {
return paypalCaptureClient.capture(command);
} catch (ExternalTimeoutException ex) {
return paypalCaptureClient.capture(command.withSameProviderIdempotencyKey());
}
```
**좋은 이유:**
- side effect 재시도 시 새 key를 만들지 않는다
- 같은 요청 의도에 같은 provider key를 재사용한다
- PayPal/Stripe 문서 취지와 맞는다.
### 예시 4. 같은 key 동시 송신을 막는다
```java
if (!outboundIdempotencyCoordinator.tryAcquire(command.provider(), command.operation(), command.providerIdempotencyKey())) {
throw new DuplicateOutboundCallInProgressException();
}
```
**좋은 이유:**
- 같은 key 두 번 동시 전송을 줄인다
- PayPal이 설명한 concurrent duplicate 문제를 완화할 수 있다.
## 나쁜 예시
### 예시 1. timeout 후 새 key로 다시 보낸다
```java
try {
return stripeClient.createCharge(command.withNewProviderIdempotencyKey());
} catch (ExternalTimeoutException ex) {
return stripeClient.createCharge(command.withNewProviderIdempotencyKey());
}
```
**나쁜 이유:**
- 같은 외부 side effect 요청이 새 요청으로 처리될 수 있다
- 중복 생성/중복 결제 위험이 커진다
### 예시 2. 같은 key를 다른 operation에 재사용한다
```java
String key = "7f6d...";
authorizePayment(key);
capturePayment(key);
```
**나쁜 이유:**
- provider마다 operation scope가 다를 수 있다
- PayPal은 API call type 단위 고유성을 요구한다.
### 예시 3. provider key에 이메일을 넣는다
```java
String providerIdempotencyKey = request.email() + ":" + request.orderId();
```
**나쁜 이유:**
- PII가 key에 섞인다
- Stripe도 민감정보를 key로 쓰지 말라고 권고한다.
### 예시 4. replay semantics를 무시하고 항상 “새 성공”으로 해석한다
```java
return new PaymentCapturedResult(true, true);
```
**나쁜 이유:**
- provider가 이전 결과 재생인지 최신 상태 조회인지 구분하지 못한다
- 내부 감사/운영 추적이 부정확해진다