10 KiB
title, source_type, status, confidence, tags, related_projects, last_reviewed, canonical_sources, audience, target_publish, status_label
| title | source_type | status | confidence | tags | related_projects | last_reviewed | canonical_sources | audience | target_publish | status_label | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Privacy와 File Handling을 Domain Modeling과 함께 보기 | blog | verified | high |
|
|
2026-07-03 |
|
backend-engineer | ready |
Privacy와 File Handling을 Domain Modeling과 함께 보기
Parent / 부모 (필수)
- 핵심 canonical: wiki/projects/ca-tmpl/privacy-file-domain-modeling
- 관련 개념 문서: wiki/concepts/privacy-file-domain-modeling - privacy, file upload, DDD guardrail의 일반 배경. 현재 이 글의 구현 사실 근거는 verified project canonical에 둔다.
타깃 독자 / Target reader
- 독자 profile: skeleton에서 privacy, file upload, domain modeling guardrail을 함께 고민하는 백엔드 엔지니어.
- 이미 안다고 가정하는 것: PII, file upload, DDD aggregate.
- 처음 듣는다고 가정하는 것: privacy/file/domain을 각각 따로 구현하지 않고 “어떤 값이 어디에 남으면 안 되는가”라는 모델링 경계로 연결하는 관점.
도입 / Hook
Privacy는 보안팀 문서처럼 보이고, file upload는 adapter 이슈처럼 보이며, domain modeling은 DDD 문법처럼 보입니다. 그런데 실제 프로젝트에서는 세 가지가 자주 엮입니다. 사용자의 원본 식별자가 로그에 남으면 안 되고, upload payload는 app/proxy/gateway 어디에서 막을지 정해야 하며, domain model은 ORM이나 logger에 오염되지 않아야 합니다.
ca-tmpl은 이 세 축을 한 문서에 묶었지만, 구현 범위는 균등하지 않습니다. HMAC 기반 principal pseudonymization, MDC logging path, domain purity/aggregate/value object guardrail은 코드와 테스트가 있습니다. 반면 file upload handler, DSR workflow, backup cryptographic erasure, ICAP integration은 아직 문서/계획 범위입니다. 이 글은 이 차이를 숨기지 않고, privacy와 modeling을 함께 보는 이유를 정리합니다.
본문 outline / Body outline
- privacy/file/domain modeling을 같은 문서에서 보는 이유.
- 구현된 privacy foundation - principal pseudonymization과 MDC logging.
- 구현된 domain guardrail - pure domain, logger ban, value object, aggregate setter rule.
- file handling과 DSR은 아직 planned 범위다.
- compliance/운영 검증은 없다.
본문 / Body
Privacy의 첫 번째 실수는 “민감 정보를 나중에 마스킹하면 된다”고 생각하는 것입니다. 하지만 로그에 raw principal이 들어가면, 이후 retention이나 DSR을 논의하기 전에 이미 추적 가능한 식별자가 퍼져 있습니다. ca-tmpl은 request logging path에서 raw principal을 바로 MDC에 넣지 않고, UserPrincipalPseudonymizerPort를 거친 pseudonymized value만 user_principal key에 넣습니다.
구현체는 HmacUserPrincipalPseudonymizer입니다. 입력 principal을 HMAC-SHA-256으로 64자 lowercase hex token으로 바꿉니다. PseudonymizationConfig는 이 구현체를 UserPrincipalPseudonymizerPort bean으로 제공합니다. PrivacySettings는 ca-skeleton.privacy.* 설정에서 salt를 읽고, local/test에서 blank salt면 dev sentinel을 사용합니다. 이 흐름은 “raw subject를 로그에 그대로 쓰지 않는다”는 최소 foundation입니다.
다만 이것을 anonymization이라고 부르면 안 됩니다. HMAC token은 같은 salt에서 같은 입력을 안정적으로 같은 token으로 만들기 때문에, 추적 가능성을 줄이는 pseudonymization에 가깝습니다. input space가 작으면 brute-force 위험도 남습니다. 또한 backup 안에 이미 남은 값을 단건 삭제하는 GDPR Art.17 문제까지 해결하지 않습니다. project canonical도 per-principal envelope key 구조는 미결정이라고 명시합니다.
Domain modeling guardrail은 privacy와 다른 문제처럼 보이지만, 같은 방향을 봅니다. domain layer가 logger를 직접 잡으면 domain invariant violation이 곧바로 log payload가 될 수 있습니다. domain class가 JPA annotation이나 framework type에 묶이면 persistence detail이 domain boundary로 들어옵니다. ca-tmpl의 CleanArchitectureTest는 domain package가 Spring/JPA/Hibernate/Lombok/application/adapter/bootstrap에 의존하지 못하게 하고, domain logger dependency도 별도 rule로 금지합니다.
Value Object와 Aggregate Root rule도 있습니다. @ValueObject는 public no-arg constructor를 금지합니다. 값 객체가 빈 생성자로 만들어지고 나중에 setter로 채워지면 invariant를 우회할 수 있기 때문입니다. @AggregateRoot에는 public set* mutator를 금지합니다. aggregate state는 의도가 드러나는 method를 통해 바뀌어야 하고, 그 method가 invariant를 확인해야 합니다.
File handling 쪽은 아직 구현됐다고 말하면 안 됩니다. app 10MB, proxy 12MB, gateway 20MB 같은 size limit, content-type allowlist, temp orphan cleanup, ICAP antivirus gateway는 project canonical에 문서화되어 있지만 file upload handler나 scan integration으로 닫힌 상태가 아닙니다. DSR SLA, backup cryptographic erasure도 마찬가지입니다. 설계 방향은 있지만, 실제 요청 처리 workflow나 법무/compliance review가 있는 것은 아닙니다.
그래서 이 글의 결론은 조심스럽습니다. ca-tmpl은 privacy/file/domain 전체를 완성한 것이 아닙니다. 구현된 것은 pseudonymized principal logging foundation과 domain modeling guardrail 일부입니다. file upload, DSR, backup erasure는 후속 구현이 필요합니다. 하지만 세 축을 함께 보는 관점은 유효합니다. 어떤 값이 어디에 남는지, 어떤 계층이 어떤 타입을 알 수 있는지, 어떤 construction path가 invariant를 우회하는지 모두 결국 boundary 문제이기 때문입니다.
코드 예제 / Code samples (있다면)
// 출처: [[wiki/projects/ca-tmpl/privacy-file-domain-modeling]]
// 실제 파일: adapter-identifier/.../HmacUserPrincipalPseudonymizer.java, ca-tmpl @f6fbd4e196b4
public String pseudonymize(String rawPrincipal) {
if (rawPrincipal == null || rawPrincipal.isBlank()) {
return null;
}
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(key);
byte[] digest = mac.doFinal(rawPrincipal.getBytes(StandardCharsets.UTF_8));
return HexFormat.of().formatHex(digest);
}
// 출처: [[wiki/projects/ca-tmpl/privacy-file-domain-modeling]]
// 실제 파일: adapter-web/.../RequestLoggingFilter.java, ca-tmpl @f6fbd4e196b4
private void putUserPrincipalIfAvailable() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.getPrincipal() instanceof AuthenticatedPrincipal user) {
String pseudo = pseudonymizer.pseudonymize(user.idpUserId());
if (pseudo != null) {
MDC.put(MdcKeys.USER_PRINCIPAL, pseudo);
}
}
}
// 출처: [[wiki/projects/ca-tmpl/privacy-file-domain-modeling]]
// 실제 파일: app-bootstrap/.../CleanArchitectureTest.java, ca-tmpl @f6fbd4e196b4
static final ArchRule DOMAIN_IS_PURE =
noClasses()
.that()
.resideInAPackage("..domain..")
.should()
.dependOnClassesThat()
.resideInAnyPackage("org.springframework..", "jakarta.persistence..", "org.hibernate..");
// 출처: [[wiki/projects/ca-tmpl/privacy-file-domain-modeling]]
// 실제 파일: app-bootstrap/.../CleanArchitectureTest.java, ca-tmpl @f6fbd4e196b4
static final ArchRule AGGREGATE_ROOT_SETTERS_ARE_NOT_PUBLIC =
methods()
.that()
.haveNameMatching("set.*")
.and()
.areDeclaredInClassesThat()
.areAnnotatedWith(AggregateRoot.class)
.should()
.notBePublic();
Sources / 근거 (canonical 인용 필수, derived layer 의무)
- wiki/projects/ca-tmpl/privacy-file-domain-modeling - 이 글의 1차 canonical. pseudonymization/logging path, domain guardrail, file/DSR/backup planned 범위, 운영/법무 미검증 경계를 따른다.
- wiki/concepts/privacy-file-domain-modeling - 관련 개념 문서. GDPR, cryptographic erase, file upload, DDD guardrail의 일반 배경으로만 둔다.
사실 vs 의견 / Fact vs opinion 구분
- 사실: ca-tmpl에는
HmacUserPrincipalPseudonymizer,PseudonymizationConfig,PrivacySettings,RequestLoggingFilter의 pseudonymized principal logging path가 존재한다. 근거: wiki/projects/ca-tmpl/privacy-file-domain-modeling - 사실: domain purity, domain logger ban, value object no public no-arg constructor, aggregate setter visibility rule이
CleanArchitectureTest계열에 존재한다. 근거: wiki/projects/ca-tmpl/privacy-file-domain-modeling - 사실: file upload handler, DSR workflow, backup cryptographic erasure, ICAP integration은 구현/로컬 검증 범위가 아니다. 근거: wiki/projects/ca-tmpl/privacy-file-domain-modeling
- 의견: privacy와 domain modeling은 서로 다른 주제처럼 보여도 “값이 어디에 남고 어떤 경계가 우회되는가”라는 같은 질문으로 연결된다.
- 알지 못하는 것: 실제 GDPR DSR 처리, legal review, antivirus gateway 운영, backup erasure 검증.
답할 수 있는 범위 / Answer boundary
- 자신 있게 답할 수 있는 후속 질문:
- HMAC pseudonymization이 anonymization이 아닌 이유.
- raw principal을 MDC에 직접 쓰지 않는 이유.
- domain layer logger ban과 aggregate/value object guardrail이 어떤 우회를 막는가.
- 다음 글로 넘길 부분:
- file upload handler와 antivirus integration.
- DSR workflow와 backup cryptographic erasure.
- compliance review와 production privacy workflow.
게시 체크리스트 / Publish checklist
- 모든 사실 주장에 canonical 링크 있음
- 사실 vs 의견 분리 명시됨
- 금지 마케팅 표현 없음
- 코드 예제 출처 명시
- 타깃 독자 가정과 톤 일치
/lint통과- 게시 URL 기록 (게시 후):