ADR-009 PartnerAuditLog 별도 테이블 + 필터 레벨 비동기 기록

  • 상태: 채택
  • 날짜: 2026-07-03
  • 근거: PRD FR-8 / Success Metrics(커버리지 100%) / AS-IS McpAuditLogAsyncRecorder

맥락

Partner의 등록·수정 활동 감사 로그를 어디에·어떻게 남길지 결정한다. mcp에 비동기 기록기 패턴이 존재하나, 등록 요청은 기존 여러 컨트롤러에 분산돼 있고 파트너/비파트너 구분이 필요하다.

결정

  1. 별도 테이블 partner_audit_logmcp_audit_logs 미공유(도메인 격리, ADR-001).
  2. 필터 레벨 기록PartnerApiKeyAuthenticationFilter가 체인 완료 후(응답 status·latency 확보) PartnerActivityRecorder.record(...) 호출. 필터는 모든 파트너 요청의 단일 통과점 → 구조적 100% 커버리지.
  3. 비동기·비실패PartnerActivityRecorder interface(domain) / AsyncPartnerActivityRecorder(infra, @Async("partnerAuditExecutor")). 적재 실패는 요청을 실패시키지 않고 WARN 로그만(mcp 패턴).
  4. 전용 스레드풀 — 등록 요청 P95(300ms)에 감사 I/O가 끼지 않게 partnerAuditExecutor 분리(신규 PartnerAsyncConfig, 기존 AsyncConfig.kt 미수정 → Single Writer 보호).

근거

  • 필터는 등록/수정/조회 모든 파트너 요청을 통과 → AOP·컨트롤러 산개 대비 커버리지·구분이 구조적으로 해결.
  • mcp가 AOP를 회피한 이력(CGLIB proxy 우회) + 등록 API 다컨트롤러 분산 → AOP 미채택.
  • 도메인이 interface(PartnerActivityRecorder)를 정의하고 infra가 구현 → 필터(infra)는 domain interface에만 의존(레이어 규칙 준수).

대안 (미채택)

  • mcp_audit_logs 공유: 도메인 격리 위반(ADR-001), PRD FR-8 별도 테이블 명시.
  • AOP(@Around) 컨트롤러 부착: 등록 API 산개 + 파트너 구분 로직 필요, mcp의 CGLIB 이슈.
  • 동기 기록: 등록 트랜잭션에 감사 I/O 결합 → P95 악화·감사 실패가 등록 실패로 전파.

영향

  • partner_audit_log: partnerId·userId·method·path·status_code·latency_ms·ip·userAgent·called_at. 인덱스 (partner_id, called_at).
  • 커버리지 측정: access log 등록 건수 vs partner_audit_log 적재 건수 대조 → 누락 0건(Success Metric).
  • 감사 조회는 QueryDSL Custom Repository(findBy(partnerId, from~to, paged)).