Files
llm-wiki/wiki/projects/ca-tmpl/privacy-file-domain-modeling.md
T

135 lines
10 KiB
Markdown

---
title: ca-tmpl - Privacy / File / Domain Modeling 결정
source_type: project
status: verified
confidence: high
tags: [ca-tmpl, privacy, gdpr, file-upload, ddd, actually-implemented, locally-verified, documented-only]
related_projects: [ca-tmpl]
last_reviewed: 2026-07-02
---
# ca-tmpl - Privacy / File / Domain Modeling 결정
> Layer: `wiki/projects/` — 내 프로젝트 사실. 일반 개념 / 공식 기준 / 트레이드오프는 [[wiki/concepts/privacy-file-domain-modeling]] 참조.
## 프로젝트 컨텍스트
**ca-tmpl skeleton** — 도메인 로직을 얹기 전 단계의 운영/보안/도메인 계약을 사전에 고정하기 위한 Spring Boot 기반 Clean Architecture 템플릿 프로젝트. 2026-07-02 기준 일부 privacy/domain guardrail은 코드화됐고, file handling/DSR/backup erasure는 여전히 계획 또는 문서 단계다.
본 문서는 Phase E Group G-J에서 결정된 3축 — **(1) Privacy / Retention**, **(2) File / Resource Handling**, **(3) Domain Modeling Guardrails** — 의 ca-tmpl 적용 결정사항을 정리한다.
핵심 결정값 요약:
- **Privacy**: 30/180/365일 3-tier log retention, HMAC-SHA-256 + 90일 salt rotation pseudonymization, DSR SLA 30일/14일(intake → execution), `is_sample` 컬럼 기반 sample 데이터 분리.
- **File**: app 10MB / global 12MB / gateway 20MB 3-layer size limit, content-type allowlist 6종(image/jpeg, image/png, image/gif, application/pdf, text/plain, application/zip 등), temp orphan 1h cleanup sweeper, ICAP antivirus gateway 기본값.
- **Domain Modeling**: VO private constructor + factory method, aggregate root mutator non-public(package-private/protected), domain layer logger ban(ArchUnit forbidden import), safe reason enum, invariant in constructor, Vernon Option A(ORM 외부 매핑) 채택.
## 실제 구현 내용 (`actually-implemented`)
- `adapter-identifier``HmacUserPrincipalPseudonymizer``app-bootstrap``PseudonymizationConfig`, `PrivacySettings`가 user principal pseudonymization 기반을 제공한다.
- `RequestLoggingFilter`가 raw principal이 아니라 pseudonymized principal을 MDC에 넣는 흐름을 갖는다.
- `docs/registries/env-keys.yaml`, `secrets-classification.yaml`, `mdc-keys.yaml`, `capabilities.yaml`, `error-codes.yaml`에 privacy/file/domain 관련 registry row가 존재한다.
- domain purity, logger ban, forbidden imports, aggregate boundary guard는 `CleanArchitectureTest` 계열과 domain/sample tests에서 일부 검증된다.
- file upload handler, DSR workflow, backup cryptographic erasure, ICAP integration은 아직 구현되지 않았다.
## 로컬/dev 검증 (`locally-verified`)
- `./gradlew check` 통과(2026-07-02, `BUILD SUCCESSFUL`, 114 tasks).
- `HmacUserPrincipalPseudonymizerTest`, `PseudonymizationConfigTest`, `PrivacySettingsTest`, `RequestLoggingFilterTest`가 pseudonymization/logging path를 검증한다.
- `CleanArchitectureTest`와 domain/sample tests가 domain forbidden import와 invariant 일부를 검증한다.
- file upload, DSR, backup erasure는 로컬 검증되지 않았다.
## 운영 검증 (`prod-verified`)
**없음.** ca-tmpl skeleton 자체가 운영 환경에 배포된 적 없음.
## 문서/계획만 존재 (`documented-only` / `planned`)
다음 항목은 구현된 privacy/domain guardrail과 아직 문서/계획으로 남은 file/DSR/backup 영역을 분리한다.
### Privacy (canonical §19)
- log retention 30/180/365일 3-tier 분류 (info / warn-business / audit-security)
- HMAC-SHA-256 + 90일 salt rotation pseudonymization (PII column 대상)
- DSR SLA: intake → identity verification → execution 30일, internal execution 14일
- `is_sample` boolean column으로 sample / production 데이터 분리, retention job exemption
- backup retention: cryptographic erase 방식 채택 의도(per-principal envelope key는 **미결정**, 후속 보강 후보)
- per-principal envelope key 패턴 선택 (2026-05-22) — **needs-confirmation**, Phase C2 결정 보류. (a) per-principal CMK / (b) per-principal DEK + master CMK envelope / (c) tenant-level CMK 3종 후보. 근거: [[raw/official-docs/gdpr-cryptographic-erasure-envelope-key-pattern]]. HMAC + 90d salt rotation은 forward security만 제공하므로 backup의 Art.17 단건 erasure에는 별도 envelope key 구조가 필요함.
### File (canonical §29 H, branch-notes)
- size limit 3-layer: Spring multipart 10MB (app envelope error) / reverse proxy 12MB / gateway WAF 20MB raw 413
- content-type allowlist 6종 + endpoint별 재검증
- temp file > 1h not closed → orphan 판정, sweeper가 삭제 (tus resumable session과 별도 threshold 필요성은 문서화만)
- ICAP gateway antivirus(ClamAV 등) 기본값, in-app daemon 채택 X
- direct S3 presigned URL은 **검토만 완료**, 채택 미정
### Domain Modeling (canonical §29 I-J, branch-notes)
- Value Object: private constructor + static factory method, invariant in constructor 강제
- Aggregate root: mutator를 public 금지(package-private/protected만 허용)
- Domain layer logger ban: `org.slf4j.Logger`, `java.util.logging.*`, HTTP type, `@Entity`, `@Service` 등 forbidden import — ArchUnit 테스트로 강제할 계획
- safe reason enum (도메인 거부 사유 noun 형태 enum)
- Vernon Option A(ORM 매핑을 domain 외부 mapper/persistence layer에서 수행) 채택, Option B(JPA direct annotation in domain) 거절
- CQRS / event sourcing **미채택**, "domain event = transport-free fact" 정의만 차용
### 검토했으나 채택하지 않은 대안 (concept 참조)
3 sub-topic 각각 5종 이상의 대안을 검토 — 자세한 trade-off는 [[wiki/concepts/privacy-file-domain-modeling]] §"한계 / 주의점".
- Privacy: PII detection SaaS(AWS Macie / OneTrust / TrustArc) — vendor 종속으로 skeleton 기본값 부적절.
- File: in-app ClamAV daemon, direct S3 presigned URL only, tus resumable 표준 채택, magic-byte sniffing only — 각각 trade-off로 인해 채택 보류.
- Domain: Anemic model, Pure DDD aggregates(over-engineering), Event sourcing, JPA direct annotation(Option B), Functional domain modeling(Scala/F#) — 모두 검토 후 거절.
## 면접에서 말할 수 있는 범위
### 자신 있게 답할 수 있는 질문
- GDPR Art.17 backup erasure 처리 방식과 cryptographic erase의 의미
- HMAC + salt rotation의 의미와 anonymization이 아닌 이유 (brute-force 가능 input space에서 tokenization 우위)
- file size 3-layer(app / proxy / gateway)의 defense-in-depth 의미와 trade-off
- ICAP gateway의 한계 (HTTPS E2E TLS 환경에서 평문 검사 불가)
- VO private constructor + factory method 이유 (invariant 보장, 잘못된 인스턴스 생성 차단)
- ORM 외부 매핑(Vernon Option A) vs JPA direct annotation(Option B) trade-off
### 적당히 답할 수 있는 질문
- Vernon Option A vs B의 코드량 / 학습 비용 trade-off 비교
- NIST SP 800-88 cryptographic erase의 backup 적용 메커니즘 (per-principal envelope key 구조 필요성 정도까지)
- DSR 운영 패턴 일반론 (intake → verification → scope → execution → audit)
### 답하면 안 되는 질문 (모른다고 해야 함)
- "GDPR DSR 요청을 실제로 처리해본 경험이 있는가?" → **없음.** ca-tmpl은 skeleton 단계, 운영 데이터 없음.
- "ICAP scan을 운영 환경에서 운영해본 경험은?" → **없음.** 설계 / 문서 단계.
- "domain event sourcing을 도입한 경험은?" → **없음.** ca-tmpl은 event sourcing **미채택**, transport-free fact 정의만 차용.
- "per-principal envelope key를 적용한 경험은?" → **없음.** 후속 보강 후보로 문서화만 됨.
## 과장 금지 지점
외부 설명(면접 / 이력서 / README / 블로그)에서 사실보다 부풀려지기 쉬운 표현들.
- **"HMAC + salt rotation으로 anonymization을 적용했다"** → **부정확 (가장 흔한 과장)**. ENISA / IAPP 기준 명확히 **pseudonymization**이지 anonymization이 아니다. brute-force 가능 input(휴대폰 11자리 등)에서는 tokenization이 우위인 구간이 존재하며, 무엇보다 HMAC + salt rotation은 **forward security만** 제공한다 — rotation 이전에 기록된 backup 안의 hash는 그대로 잔존하므로 GDPR Art.17 backup erasure 수단으로 사용할 수 없다. backup 단건 erasure는 별도의 per-principal envelope key 구조(NIST SP 800-88 § 2.5 CE)가 필요하며 ca-tmpl은 **미결정** 상태이다.
- **"ICAP antivirus gateway로 모든 위협을 막는다"** → 부정확. HTTPS end-to-end TLS 환경에서 gateway가 payload를 평문으로 보지 못하는 한계가 있음. post-upload async scan 보완 필요.
- **"ca-tmpl은 pure DDD 기반이다"** → 부정확. Vernon Option A(ORM 외부 매핑)만 차용했으며, CQRS / event sourcing은 미채택. "transport-free domain event 정의만 차용"이 정확한 표현.
- **"per-principal envelope key 구조를 적용해 GDPR Art.17 backup erasure를 완전 처리한다"** → 부정확. 후속 보강 후보로 **미결정** 상태. 현재는 cryptographic erase 의도만 문서화됨.
- **"DSR SLA 30일은 GDPR 요구치다"** → 부정확. GDPR Art.12는 "원칙적으로 1개월(연장 시 +2개월)"이며 30/14일은 ca-tmpl 내부 운영 결정값.
- **"retention job / file upload handler / DSR workflow를 구현했다"** → 거짓. pseudonymization/logging guard와 domain guardrail 일부는 구현됐지만, 이 운영 기능들은 문서/계획 단계다.
## 관련 개념
- [[wiki/concepts/privacy-file-domain-modeling]]
## Sources
- [[raw/project-notes/ca-skeleton-operational-contract]] §19 Domain Application Readiness Contract, §29 G-J 외부 근거 / 대안 조사
- [[raw/branch-notes/feature-data-retention-privacy-contract]] — log retention by profile, HMAC pseudonymization, DSR SLA, backup retention 결정
- [[raw/branch-notes/feature-file-resource-handling-contract]] — upload size 3-layer, content-type allowlist, temp file cleanup, antivirus position 결정
- [[raw/branch-notes/feature-domain-modeling-guardrails]] — VO private constructor, aggregate mutator non-public, domain forbidden import 결정
## Cluster / 묶음
<!-- GENERATED: derived-blogs:start -->
- [[wiki/blog/ca-tmpl-privacy-file-domain-modeling-2026-07-02]]
<!-- GENERATED: derived-blogs:end -->