--- title: branch / feature-data-retention-privacy-contract source_type: branch-note status: raw branch: feature-data-retention-privacy-contract related_projects: [ca-skeleton] tags: [branch, ca-skeleton, data-retention, privacy, logging] created: 2026-05-22 target_merge: status_label: in-progress id: BR-CA-SKELETON-OPERATIONAL-CONTRACT-032 kind: project-work-item project: ca-skeleton-operational-contract work_item: WI-CA-SKELETON-OPERATIONAL-CONTRACT-032 inherits: [DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1] refines: [] overrides: [] depends_on: [] contract_packet: 1 parent_branch: contract_packet_sha256: ff83e470a0a30de5fc6591d73f5d7f1ed8cd3501c171a4361e1d206642d9be66 --- # branch: feature-data-retention-privacy-contract > Layer: `raw/branch-notes/` — 로그, audit/security event, sample data, backup/restore의 보존과 개인정보 노출 기준을 정의합니다. ## 부모 (필수) - **Parent project (canonical SSOT)**: [[raw/project-notes/ca-skeleton-operational-contract]] > ca-skeleton 은 별도 root branch 없이 project-note 가 SSOT 역할. 본 feature branch 는 project-note 의 운영 계약 중 해당 영역 (§<관련 섹션>) 의 결정/근거/금지 사항을 정제한다. ## 브랜치 계약 패킷 - **생성 시 프로젝트 개정**: `1` - **패킷 스키마**: `contract_packet: 1` - **완료 조건**: retention·deletion·masking contract test가 통과한다 ### 상속한 프로젝트 결정 | Decision Ref | Project Summary | Branch Application | Source | |---|---|---|---| | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1` | Gradle multi-module에서 domain-core·application-core·adapter-*·shared-contract·app-bootstrap·sample-portfolio 책임을 분리한다 | Work Item 완료 조건에 적용 | [[raw/project-notes/ca-skeleton-operational-contract]] | ### 브랜치 지역 결정 > 기존 branch-local 결정은 아래 `## Decision Evidence Map / 결정-근거 매핑`의 D-row가 소유하며 이 packet에서 복제하지 않는다. | Decision ID | Decision | Relation | Supporting Claims | Status | |---|---|---|---|---| ### 선언한 예외 | Override ID | Overrides | Reason | Approval | Status | |---|---|---|---|---| ## 목표 도메인이 없어도 skeleton은 개인정보와 운영 로그를 다룹니다. PII, token, request body, audit/security event 보존 기준이 없으면 운영 로그 자체가 리스크가 됩니다. - 이슈: - PR: ## 범위 ### 포함 범위 - log category별 retention 기준. - PII/secrets/token redaction 기준. - pseudonymization 기준. - audit/security event 보존 기준. - sample data와 real data 구분 기준. - backup/restore 책임 경계. ### 제외 범위 - 특정 법률 준수 문서. - 실제 DLP product 연동. - business data retention policy. ## TODO > TODO drained 2026-05-22 — 결정은 아래 "결정 사항" / "Retention by Profile" / "DSR Contract" / "Retention Defaults" 참조. application·security·audit retention / PII·token redaction / pseudonymization / sample-vs-real / backup·restore boundary / privacy contract test 기준 모두 결정 라인 또는 표로 반영됨. 잔존 TODO 없음. ## Work Item Contract 각 TODO는 아래 판정 단위로 재작성되어야 canonical 승급 가능합니다. TODO가 단순히 `기준 작성`으로 남아 있으면 이 branch는 완료로 보지 않습니다. | field | required | rule | | --- | --- | --- | | Decision | yes | 구현자가 선택해야 하는 기본값 | | Allowed | yes | 허용되는 예외와 조건 | | Forbidden | yes | 절대 금지되는 구현/문서 상태 | | Required registry update | conditional | error/env/header/log/metric/capability 변경 시 필수 | | Required contract test | yes | 계약 위반 시 실패해야 하는 테스트 | | Failure condition | yes | review/build에서 실패로 판정할 상태 | | Canonical extraction target | yes | `wiki/projects` 승급 위치 | ## 결정 사항 - 2026-05-22: skeleton 기본 로그에는 PII, token, raw body를 남기지 않음. - 2026-05-22: security event log의 principal은 최소 식별 또는 pseudonymized identifier 기준으로 둠. - 2026-05-22: DSR(delete/export) 절차는 이 branch가 owner. skeleton core는 business data 삭제를 구현하지 않지만 intake, identity verification, scope classification, audit evidence contract는 제공. - 2026-05-22: retention 기본값은 application log 30일, security event 180일, audit log 1년. 조직/법률 요구가 있으면 override 가능. - 2026-05-22: backup/restore는 persistence branch와 연결하되 privacy 관점의 retention/erasure evidence를 이 branch가 소유. - 2026-05-22: redaction layer SSOT는 log-management branch의 Logback masking converter. 본 branch는 PII field allowlist 표만 owns. - 2026-05-22: pseudonymization key = HMAC-SHA-256 with rotating salt. salt rotation interval = 90일. rotation 시 old salt 90일 retain (lookup 가능). collision rate < 1e-9 가정. - 2026-05-22: sample data 표시 메커니즘 = (1) entity flag column `is_sample BOOLEAN DEFAULT false` + (2) Spring profile `sample` 활성 시만 seed. prod profile에서 is_sample=true row 발견 시 fail (cleanup migration 의무). - 2026-05-22: DSR delete request 처리 SLA = 30일, export 14일. principal 식별은 pseudonymized id ↔ original id 변환 표(privacy branch가 owns). - 2026-05-22: backup encryption-at-rest 의무. backup retention default = 30일 daily + 6개월 monthly. restore drill 분기 1회 의무. - 2026-05-22: 본 branch가 모든 log type(application/security/audit)의 **retention SSOT**. log-management-contract는 형식만 owns. retention 수치는 본 branch의 Retention by Profile 표가 단일 source. - 2026-05-22: backup에 PII 포함 시 per-principal envelope key (또는 tenant-level CMK) 구조 적용. HMAC + salt rotation 90d는 "forward security only" 명시. 구체 패턴(per-principal vs tenant-level vs hybrid) 선택은 Phase C2 보류. (status: needs-confirmation) ## Retention by Profile | log type | dev | staging | prod | |----------|-----|---------|------| | application | 7일 | 14일 | 30일 | | security | 30일 | 90일 | 180일 | | audit | 90일 | 365일 | 365일 (또는 도메인별 override) | ## DSR Contract | step | default | | --- | --- | | intake | authenticated request or verified support workflow | | identity verification | principal proof before export/delete | | export | machine-readable JSON/CSV package with audit event | | delete | domain owner policy, tombstone/pseudonymization allowed | | evidence | audit event without raw PII payload | ## Retention Defaults | data | default retention | | --- | --- | | application log | 30 days | | security event log | 180 days | | audit log | 1 year | | sample data | never seeded in prod | | backup | project-specific, restore evidence required | ## 테스트 계약 - token/password/authorization header가 log capture에 남으면 실패. - raw request/response body logging이 prod profile에서 가능하면 실패. - sample data가 production profile에서 seed되면 실패. - DSR delete/export 절차 owner와 audit evidence가 없으면 실패. - retention 일수가 `0` 또는 미정이면 실패. ## 근거 (필수, 최소 1개+) > 본 branch의 결정 근거. 상세 비교는 §외부 근거 / 대안 조사 (있다면) 참조. | Source | 정당화하는 결정 | |---|---| | [[raw/official-docs/privacy-gdpr-article-25-design]] | GDPR Privacy by design/default (retention "as short as possible" + pseudonymization 권장; ca-tmpl 채택 legal basis | | [[raw/company-tech-blogs/privacy-pseudonymization-hmac-vs-tokenization-iapp]] | HMAC-SHA-256 + 90d salt rotation 선택 근거 (ENISA/IAPP 인정 | | [[raw/official-docs/privacy-cryptographic-erasure-nist-sp800-88]] | NIST 정식 인정; backup의 GDPR Art | | [[raw/official-docs/gdpr-cryptographic-erasure-envelope-key-pattern]] | 참조 | ## 외부 근거 / 대안 조사 (2026-05-22 — Group G-J: Data Retention / Privacy) 본 branch의 30/180/365d retention by profile + HMAC-SHA-256 salt rotation 90d + DSR SLA 30d delete / 14d export + is_sample column 결정에 대한 외부 source. - **채택 결정 (legal basis: GDPR Art.25 + HMAC pseudonymization + retention by category)**: - [[raw/official-docs/privacy-gdpr-article-25-design]] — GDPR Privacy by design/default (retention "as short as possible" + pseudonymization 권장; ca-tmpl 채택 legal basis) - [[raw/company-tech-blogs/privacy-pseudonymization-hmac-vs-tokenization-iapp]] — HMAC-SHA-256 + 90d salt rotation 선택 근거 (ENISA/IAPP 인정) - **검토한 대안**: - **대안 1: Tokenization service** — `privacy-pseudonymization-hmac-vs-tokenization-iapp` 동일 source 안에서 비교 (brute-force 가능 input space에서 HMAC보다 우위) - **대안 2: Cryptographic erasure (delete encryption key vs delete data)** — [[raw/official-docs/privacy-cryptographic-erasure-nist-sp800-88]] (NIST 정식 인정; backup의 GDPR Art.17 erasure 정합; per-principal envelope key 구조 필요 — ca-tmpl 미결정 보강 후보) - **대안 3: PII detection SaaS (AWS Macie / OneTrust / TrustArc)** — vendor 종속, ca-tmpl scope 외 - **비교 핵심**: ca-tmpl HMAC-SHA-256 + 90d salt rotation은 ENISA 인정 패턴이나 brute-force 가능 input space(예: 한국 휴대폰 11자리)에서 tokenization 우위. Cryptographic erase는 backup PII delete의 NIST 정식 방법 — per-principal envelope key 구조 도입 검토 필요(ca-tmpl 미결정). GDPR Art.25가 ca-tmpl retention/pseudonymization 결정의 legal basis. **후속 보강 (2026-05-22)**: GDPR Art.17 backup erasure 정합을 위한 per-principal envelope key 패턴 필요. HMAC + salt만으로는 forward security만 제공. [[raw/official-docs/gdpr-cryptographic-erasure-envelope-key-pattern]] 참조. ## 결정-근거 매핑 > 본 branch 의 각 결정이 어떤 raw source claim 으로 뒷받침되는지 명시한다. Decision ID 는 안정적으로 유지한다. company-tech-blog 출처는 `company-case-study` 로 표기하며 공식 best practice 로 일반화하지 않는다. | Decision ID | Decision | Supporting Claims | Evidence Strength | Open Risk | |---|---|---|---|---| | D1 | skeleton 기본 로그에 PII, token, raw body 미기록 | `raw/official-docs/privacy-gdpr-article-25-design.md#GDPR-A25-C1` (pseudonymisation as appropriate technical measure), `raw/official-docs/privacy-gdpr-article-25-design.md#GDPR-A25-C2` (default: only necessary data processed) | `official-standard` (GDPR Art.25) | Art.25 는 "necessary for purpose" 의 정량 기준을 지정하지 않음 — 도메인별 justification 필요 | | D2 | security event log principal = 최소 식별 또는 pseudonymized identifier | `raw/official-docs/privacy-gdpr-article-25-design.md#GDPR-A25-C1` (pseudonymisation 예시), `raw/company-tech-blogs/privacy-pseudonymization-hmac-vs-tokenization-iapp.md#ENISA-PSE-C1` ~ `C4` | `official-standard` (Art.25) + `company-case-study` (IAPP/ENISA mapping — 일반화 금지) | ENISA 가이드는 EU agency document 이나 본 raw 는 IAPP company-case-study 로 분류됨. Art.25 자체는 알고리즘 강도를 지정하지 않음 | | D3 | DSR (delete/export) 절차 owner = 본 branch | `raw/official-docs/privacy-gdpr-article-25-design.md#GDPR-A25-C3` (저장 기간 + 접근성이 default 의무 4축에 포함) | `official-standard` | Art.25 는 DSR SLA 수치 미지정 — Art.12(3) "without undue delay and in any event within one month" 와 결합 해석 필요 (별도 raw 미확보) | | D4 | retention 기본값 = application 30d / security 180d / audit 1y | `raw/official-docs/privacy-gdpr-article-25-design.md#GDPR-A25-C2`, `raw/official-docs/privacy-gdpr-article-25-design.md#GDPR-A25-C3` (저장 기간 default 의무) | `official-standard` (수치 자체는 official 가 아니라 운영 default) | Art.25 는 정확한 수치 미지정. 30/180/365d 는 ca-tmpl 의 운영적 기본값일 뿐 법적 강제값 아님 | | D5 | backup/restore = persistence branch 연결, retention/erasure evidence 는 본 branch 소유 | `raw/official-docs/gdpr-cryptographic-erasure-envelope-key-pattern.md#GDPR-CE-ENV-C1` ~ `C4` (CE + Art.17 의 통합) | `official-standard` (NIST SP 800-88 + GDPR Art.17) | per-principal envelope key 패턴이 EU regulator (DPA) 가 명시 수용한 권장 방식이라는 보장은 없음 | | D6 | redaction layer SSOT = log-management branch Logback masking converter; 본 branch 는 PII field allowlist 표만 소유 | UNSUPPORTED_DECISION — 책임 경계 분리는 branch 자체 정합성 규칙 | none | Logback masking converter 자체의 공식 spec raw 미확보 | | D7 | pseudonymization key = HMAC-SHA-256 + rotating salt 90d, collision rate < 1e-9 가정 | `raw/company-tech-blogs/privacy-pseudonymization-hmac-vs-tokenization-iapp.md#ENISA-PSE-C1` ~ `C4`, `raw/official-docs/privacy-gdpr-article-25-design.md#GDPR-A25-C1` | `company-case-study` (IAPP/ENISA mapping) + `official-standard` (Art.25 pseudonymisation principle) | brute-force 가능한 input space (예: 휴대폰 11자리) 에서 tokenization 우위. 90d rotation cadence 의 EDPB 권장값은 별도 미검증 | | D8 | sample data 표시 = `is_sample BOOLEAN` column + Spring profile `sample` 활성 시만 seed (prod 발견 시 fail) | `raw/official-docs/privacy-gdpr-article-25-design.md#GDPR-A25-C2` (data minimization default) | `official-standard` | Art.25 는 `is_sample` column 메커니즘을 명시하지 않음 — ca-tmpl 운영 구현 선택 | | D9 | DSR delete SLA = 30d, export = 14d | UNSUPPORTED_DECISION — Art.12(3) "within one month" raw 미확보. 30d 는 ca-tmpl 운영 default | none | Art.12(3) raw 등록 시 보강 가능 | | D10 | backup encryption-at-rest 의무 + retention 30d daily + 6m monthly + restore drill 분기 1회 | UNSUPPORTED_DECISION — backup retention 수치는 ca-tmpl 운영 default. NIST SP 800-88 은 sanitization 만 정의, retention 수치 미지정 | none | 운영 default 합리성은 별도 | | D11 | 모든 log type retention SSOT = 본 branch (log-management 는 형식만 소유) | UNSUPPORTED_DECISION — 책임 경계 분리는 branch 자체 정합성 규칙 | none | branch 간 책임 경계의 외부 official 근거 없음 | | D12 | backup PII = per-principal envelope key (or tenant-level CMK) — 구체 패턴 (a/b/c) Phase C2 보류 | `raw/official-docs/gdpr-cryptographic-erasure-envelope-key-pattern.md#GDPR-CE-ENV-C1` ~ `C5`, `raw/official-docs/privacy-cryptographic-erasure-nist-sp800-88.md#NIST-CE-C1` ~ `C4` | `official-standard` (NIST SP 800-88) + `official-vendor-doc` (AWS KMS envelope structure) | (a)/(b)/(c) 중 채택안 미결정. Per-principal CMK 비용 폭증 risk, DEK store 메타-erasure 책임 등 결정 미확정 — status `needs-confirmation` 유지 | ## 검증해야 할 주장 | Claim | Why uncertain | How to verify | Status | |---|---|---|---| | 30/180/365d retention 이 "necessary for each specific purpose" justification 을 만족 | Art.25 는 정량 기준 미지정 | 도메인별 (application / security / audit) justification 문서화 + DPIA 형식 작성 | `needs-confirmation` | | HMAC-SHA-256 + 90d salt rotation 이 EDPB 권장 cadence 와 일치 | EDPB Guidelines 4/2019 "periodic re-pseudonymisation" 의 정확한 cadence 미확인 | EDPB Guidelines 4/2019 또는 ENISA 가이드 raw 추가 + 90d cadence 의 권장 범위 확인 | `needs-confirmation` | | brute-force 가능 input space (예: 한국 휴대폰 11자리) 에서 HMAC + salt 의 re-identification risk 가 허용 수준 | input space 특성에 따라 HMAC 우위가 깨질 가능성 | 도메인 별 input space 크기 측정 + tokenization 도입 trigger 결정 | `needs-confirmation` | | backup PII 의 GDPR Art.17 단건 erasure 가 per-principal envelope key 패턴으로 충족 | EU regulator 의 명시 수용 의견서 미확인 | DPA 가이드 또는 case law raw 추가 + 패턴 채택 후 통합 테스트 | `needs-confirmation` | | `is_sample BOOLEAN` column 메커니즘이 prod 누출 차단에 충분 | prod profile + is_sample=true row 발견 시 fail 의 구현 미확인 | startup migration 또는 contract test 구현 + prod profile + is_sample=true seed 시 fail verify | `planned` | | sensitive log redaction (token / password / authorization header) 가 모든 log capture 경로에서 동작 | Logback masking converter (log-management branch SSOT) 구현 미완 | `LogMaskingContractTest` 구현 + token/password/auth header injection 시 redaction verify | `planned` | | Art.12(3) "within one month" 와 ca-tmpl DSR SLA 30d / 14d 가 정합 | Art.12(3) raw 미확보 | Art.12 raw 추가 + SLA 비교 | `needs-confirmation` | | backup restore drill 분기 1회 가 GDPR 요건 충족 | 외부 official 근거 없음 (ca-tmpl 운영 default) | 분기별 restore drill 실행 evidence (audit log) 보존 + 외부 audit 시 제출 | `planned` | | per-principal CMK 의 KMS API cost 가 ca-tmpl 규모에서 운영 가능 | AWS KMS pricing 시점/region 별 변동 + cost 정량 미측정 | Phase C2 에서 (a)/(b)/(c) 중 채택안 + 1 년 운영 비용 시뮬레이션 | `needs-confirmation` | | DEK store (DynamoDB / Postgres) 자체의 erasure 책임 경계 | wrapped DEK record 의 backup 정책 미정 | (b) per-principal DEK + master CMK 채택 시 DEK store backup 정책 + replication 정책 추가 결정 | `needs-confirmation` | ## 완료 후 wiki 추출 대상 - `wiki/projects/ca-skeleton-operational-contract.md`의 data retention/privacy canonical section. ## 마주친 문제 - 아직 없음(문서 단계). ## 묶음 - [[raw/company-tech-blogs/privacy-pseudonymization-hmac-vs-tokenization-iapp]] - [[raw/official-docs/gdpr-cryptographic-erasure-envelope-key-pattern]] - [[raw/official-docs/privacy-cryptographic-erasure-nist-sp800-88]] - [[raw/official-docs/privacy-gdpr-article-25-design]] > 본 feature branch 는 leaf — 자식 자료 없음. Phase C2 실 코드 작성 단계에서 errors / interview prep / lectures 가 누적되면 본 섹션에서 그룹화. ### 오류 기록 (본 feature 작업 중 발생) - (없음 — 현재 documented-only 단계) ### 면접 준비 (이 작업에서 나올 수 있는 면접 질문) - (없음 — Phase C2 실 구현 단계에 누적) ## 진행 중 메모 - retention profile·DSR·기본값의 결정 상태는 위 표와 TODO에서 추적한다. ## 구현 가이드 - 데이터 분류별 retention 기간과 삭제 주체를 registry로 관리하고 job은 registry를 소비한다. - DSR 삭제·익명화·legal hold를 서로 다른 상태 전이로 처리하며 감사 로그에는 원문 PII를 남기지 않는다. - dry-run과 실제 삭제를 분리하고 fixture clock으로 경계 시각을 검증한다. ## 엣지·실패·의존 - 부분 삭제·재시도 중복·legal hold 무시는 복구가 어려운 데이터 손실 또는 규제 위험으로 이어진다. - persistence auditing·scheduler lock·tenant context 계약과 함께 검증해야 한다. ## 관련 일일 노트 - 별도 일일 노트 없음. ## 완료 후 정리 > 머지/종료 시점에 채움. - PR 링크: - 리뷰 메모: - 머지 결과 / 배포 환경: - **wiki 추출 대상** (verified만, `wiki/projects/`로만 추출): - `actually-implemented` 항목: - `locally-verified` 항목: - `prod-verified` 항목: - **추출하지 않을 항목** (planned / documented-only / abandoned):