19 KiB
title, source_type, status, branch, related_projects, tags, created, target_merge, status_label, id, kind, project, work_item, inherits, refines, overrides, depends_on, contract_packet, parent_branch, contract_packet_sha256
| title | source_type | status | branch | related_projects | tags | created | target_merge | status_label | id | kind | project | work_item | inherits | refines | overrides | depends_on | contract_packet | parent_branch | contract_packet_sha256 | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| branch / feature-tenant-context-policy | branch-note | raw | feature-tenant-context-policy |
|
|
2026-05-22 | in-progress | BR-CA-SKELETON-OPERATIONAL-CONTRACT-022 | project-work-item | ca-skeleton-operational-contract | WI-CA-SKELETON-OPERATIONAL-CONTRACT-022 |
|
1 | a951c6ee8f27ba664f1919eab3d9750a969b5d76a0c0bd1a4ccc0d383c1ea699 |
branch: feature-tenant-context-policy
Layer:
raw/branch-notes/— tenant context 지원/비지원 정책을 정의합니다.
부모 (필수)
- Parent project (canonical SSOT): raw/project-notes/ca-skeleton-operational-contract
ca-skeleton 은 별도 root branch 없이 project-note 가 SSOT 역할. 본 feature branch 는 project-note 의 운영 계약 중 해당 영역 (§<관련 섹션>) 의 결정/근거/금지 사항을 정제한다.
묶음
- raw/company-tech-blogs/multitenancy-atlassian-tenant-context
- raw/company-tech-blogs/multitenancy-auth0-tenant-resolution
- raw/company-tech-blogs/multitenancy-hybrid-pooled-siloed-mix
- raw/company-tech-blogs/multitenancy-stripe-citus-schema-per-tenant
- raw/company-tech-blogs/multitenancy-subdomain-resolution-patterns
- raw/official-docs/multitenancy-aws-saas-tenant-isolation-whitepaper
- raw/official-docs/multitenancy-azure-architecture-patterns
- raw/official-docs/multitenancy-hibernate-user-guide
- raw/official-docs/multitenancy-microservices-io-pattern
- raw/official-docs/security-opa-policy-engine-official
본 feature branch 는 leaf — 자식 자료 없음. Phase C2 실 코드 작성 단계에서 errors / interview prep / lectures 가 누적되면 본 섹션에서 그룹화.
오류 기록 (본 feature 작업 중 발생)
- (없음 — 현재 documented-only 단계)
면접 준비 (이 작업에서 나올 수 있는 면접 질문)
- (없음 — Phase C2 실 구현 단계에 누적)
브랜치 계약 패킷
- 생성 시 프로젝트 개정:
1 - 패킷 스키마:
contract_packet: 1 - 완료 조건: tenant propagation·clear negative fixture가 통과한다
상속한 프로젝트 결정
| Decision Ref | Project Summary | Branch Application | Source |
|---|---|---|---|
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-FRAMEWORK-001@1 |
framework는 Spring Boot 3.5.14다 | 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 |
|---|
목표
멀티테넌트를 기본 지원하지 않더라도, 지원하지 않는다는 기준과 tenant header 처리 정책은 필요합니다. tenant context가 암묵적으로 섞이면 repository, log, security, cache key에서 누출 위험이 생깁니다.
- 이슈:
- PR:
범위
포함 범위
- multi-tenancy 지원 여부 명시.
- tenant header 허용/금지 기준.
- tenant context propagation 기준.
- tenant scoped repository는 tenant branch 활성화 시에만 허용.
- tenant leakage 테스트 기준.
- log/cache key tenant field 기준.
제외 범위
- 실제 SaaS tenant model 구현.
- tenant billing/plan policy.
- cross-tenant admin feature.
TODO
TODO drained 2026-05-22 — 결정은 아래 "결정 사항" / "Decisionized Work Items" 참조. multi-tenancy 지원 여부 / tenant header 허용/금지 / propagation / tenant scoped repository / leakage test / log·cache key 기준 모두 결정 라인 또는 matrix row로 반영됨. 잔존 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 승급 위치 |
진행 중 메모
- 지원하지 않는 기능도 out-of-scope로 명시해야 운영 ambiguity가 줄어듭니다.
결정 사항 (decisions)
- 2026-05-22: tenant context는 명시 정책이 필요.
- 2026-05-22: skeleton core는 multi-tenancy 미지원이 기본이며 tenant header는 기본 거부.
- 2026-05-22: tenant 활성화 시 idempotency/rate-limit/cache/log/repository key의 첫 scope는 tenant.
- 2026-05-22: tenant identifier는 raw PII가 아니어야 하며 log에는 opaque/pseudonymized id만 허용.
- 2026-05-22: tenant resolution 우선순위 = (1) JWT claim
tenant_id(2) 명시적 X-Tenant-Id 헤더 (admin/internal API only) (3) subdomain. 충돌 시 (1) > (2) > (3). - 2026-05-22: tenant ID format = opaque ULID (26 chars Crockford base32). UUID/numeric 금지. PII 아닌 opaque token.
- 2026-05-22: tenant 미지원 모드에서 X-Tenant-Id 헤더 수신 시 400 TENANT_NOT_SUPPORTED (filter 단계). gateway/interceptor가 아닌 Spring Security filter.
- 2026-05-22: async/event publish 경로 tenant propagation = TaskDecorator + message header
tenant_id. consumer-side는 message에서 tenant 복원 후 SecurityContext에 inject. tenant_id는 background-job-async-contract의 TaskDecorator(SSOT)를 통해 async/event boundary에서 전파. 본 branch는 TaskDecorator의 tenant_id field 의무화만 명시. 별도 decorator chain 작성 금지. - 2026-05-22: tenant_id ULID 원본은 metric tag에 직접 사용 금지. metric label 표현은 metrics-alerting-contract SSOT (bounded mapping id 또는 cohort bucket). 본 branch는 log/cache/repository scope에서만 ULID 원본 사용.
- 2026-05-22: repository-access-permission cross-cut = tenant 활성 시 모든
@UseCaseRepositoryAccess호출은 tenant_id를 query에 자동 필터링. cross-tenant admin은CROSS_TENANT_ADMINcapability 명시 선언 필요.
근거 (필수, 최소 1개+)
본 branch의 결정 근거. 상세 비교는 §외부 근거 / 대안 조사 (있다면) 참조.
| Source | 정당화하는 결정 |
|---|---|
| raw/official-docs/multitenancy-aws-saas-tenant-isolation-whitepaper | AWS의 Pool model (shared schema with row-level filter |
| raw/official-docs/multitenancy-hibernate-user-guide | Hibernate DISCRIMINATOR strategy (ca-tmpl 채택 |
| raw/company-tech-blogs/multitenancy-atlassian-tenant-context | 대규모 shared schema + tenant context 운영 사례 |
| raw/company-tech-blogs/multitenancy-auth0-tenant-resolution | JWT claim 우선 + subdomain/header 보조 (ca-tmpl과 정합 |
| raw/company-tech-blogs/multitenancy-subdomain-resolution-patterns | — |
| raw/company-tech-blogs/multitenancy-stripe-citus-schema-per-tenant | raw/official-docs/multitenancy-hibernate-user-guide (SCHEMA strategy |
| raw/official-docs/multitenancy-microservices-io-pattern | Silo model |
| raw/official-docs/multitenancy-azure-architecture-patterns | raw/company-tech-blogs/multitenancy-hybrid-pooled-siloed-mix |
| raw/company-tech-blogs/multitenancy-hybrid-pooled-siloed-mix | — |
외부 근거 / 대안 조사 (2026-05-22 — Topic 6)
본 branch의 multi-tenancy 결정 (opt-in APP_TENANT_ENABLED + shared DB + tenant_id column + ULID + JWT claim 우선 + X-Tenant-Id header admin only)에 대한 외부 source 조사. 비교 분석은 (예정) wiki/concepts/multi-tenancy-isolation-patterns.md 참조.
- 채택 결정 (opt-in shared DB + tenant_id column):
- raw/official-docs/multitenancy-aws-saas-tenant-isolation-whitepaper — AWS의 Pool model (shared schema with row-level filter)
- raw/official-docs/multitenancy-hibernate-user-guide — Hibernate DISCRIMINATOR strategy (ca-tmpl 채택)
- raw/company-tech-blogs/multitenancy-atlassian-tenant-context — 대규모 shared schema + tenant context 운영 사례
- tenant resolution 비교: raw/company-tech-blogs/multitenancy-auth0-tenant-resolution — JWT claim 우선 + subdomain/header 보조 (ca-tmpl과 정합)
- 검토한 대안:
- 대안 1: Subdomain-based resolution — raw/company-tech-blogs/multitenancy-subdomain-resolution-patterns
- 대안 2: JWT claim only (header 차단) —
multitenancy-auth0-tenant-resolution의 variation - 대안 3: Schema-per-tenant — raw/company-tech-blogs/multitenancy-stripe-citus-schema-per-tenant, raw/official-docs/multitenancy-hibernate-user-guide (SCHEMA strategy)
- 대안 4: Database-per-tenant (Silo) — raw/official-docs/multitenancy-microservices-io-pattern (Silo model)
- 대안 5: Hybrid (tier-based / Deployment Stamps) — raw/official-docs/multitenancy-azure-architecture-patterns, raw/company-tech-blogs/multitenancy-hybrid-pooled-siloed-mix
- 비교 핵심: ca-tmpl의 opt-in + shared DB + tenant_id는 B2B 초기 단계 적합 (tenant 수 수십
수백). migration trigger: (a) 규제(금융/의료)로 isolation 강제 → schema-per-tenant, (b) tenant 수 수백수천 + 단일 row 수 수억 → schema-per-tenant 또는 hybrid, (c) enterprise tier 등장 시 isolation 가격화 → db-per-tenant.
Decisionized Work Items
| item | Decision | Allowed | Forbidden | Required test |
|---|---|---|---|---|
| support mode | disabled by default | explicit tenant branch activation | silent tenant header acceptance | unsupported header test |
| propagation | request context -> application -> repository/cache/log | async propagation with context wrapper | thread-local leak | propagation test |
| repository | tenant-scoped query required when enabled | cross-tenant admin with explicit capability | missing tenant predicate | leakage test |
| key prefix | tenant first | no tenant for disabled mode | tenant in some keys only | key consistency test |
테스트 계약
- tenant 미지원 모드에서 tenant header가 조용히 수용되면 실패.
- tenant 지원 모드에서 repository query에 tenant scope가 빠지면 실패.
- tenant id가 PII/secret처럼 과도하게 노출되면 실패.
- cache key에 tenant scope 기준이 없으면 실패.
- tenant 활성화 시 idempotency/rate-limit/cache/log principal scope가 서로 다르면 실패.
결정-근거 매핑
본 branch 의 각 결정이 어떤 raw source claim 으로 뒷받침되는지 명시한다. Decision ID 는 안정적으로 유지한다. company-tech-blog 출처는
company-case-study로 표기하며 공식 best practice 로 일반화하지 않는다.
| Decision ID | Decision | Supporting Claims | Evidence Strength | Open Risk |
|---|---|---|---|---|
| D1 | tenant context 는 명시 정책 필요 | raw/official-docs/multitenancy-aws-saas-tenant-isolation-whitepaper.md#AWS-TENANT-C1 (tenant isolation 은 SaaS 의 fundamental), raw/official-docs/multitenancy-aws-saas-tenant-isolation-whitepaper.md#AWS-TENANT-C2 (boundary breach 는 un-recoverable) |
official-vendor-doc (AWS whitepaper, 2026-05-27 main page verbatim 재확인) |
AWS whitepaper Silo/Pool/Bridge sub-page 의 verbatim 정의는 needs-confirmation (2026-05-27 sub-page WebFetch truncated) |
| D2 | skeleton core = multi-tenancy 미지원 기본, tenant header 기본 거부 | raw/official-docs/multitenancy-aws-saas-tenant-isolation-whitepaper.md#AWS-TENANT-C1, raw/official-docs/multitenancy-aws-saas-tenant-isolation-whitepaper.md#AWS-TENANT-C2 |
official-vendor-doc |
AWS whitepaper 는 "opt-in 기본 거부" 권장을 명시하지 않음 — ca-tmpl 운영 안전 default |
| D3 | tenant 활성화 시 idempotency/rate-limit/cache/log/repository key 의 첫 scope = tenant | raw/official-docs/multitenancy-aws-saas-tenant-isolation-whitepaper.md#AWS-TENANT-C6 (Authentication is not isolation; resource layer enforcement — needs-confirmation), raw/official-docs/multitenancy-hibernate-user-guide.md#HBN-MT-C2 (DISCRIMINATOR strategy 공식 지원) |
needs-confirmation (AWS sub-page) + needs-confirmation (Hibernate body truncated, 구조는 official-vendor-doc 수준 확인) |
AWS-TENANT-C6 의 verbatim 재확인 실패. Hibernate body verbatim 도 truncated — strategy 존재만 확인 |
| D4 | tenant identifier = raw PII 아님, log 에는 opaque/pseudonymized id 만 허용 | raw/official-docs/privacy-gdpr-article-25-design.md#GDPR-A25-C1 (pseudonymisation as appropriate measure) — cross-link |
official-standard |
Art.25 는 tenant id 의 PII 여부를 명시하지 않음 — ca-tmpl 운영 안전 default |
| D5 | tenant resolution 우선순위 = (1) JWT claim tenant_id (2) X-Tenant-Id header (admin/internal API only) (3) subdomain |
raw/company-tech-blogs/multitenancy-auth0-tenant-resolution.md#AUTH0-TR-C1 ~ C4 (JWT claim 우선 + subdomain/header 보조), raw/company-tech-blogs/multitenancy-subdomain-resolution-patterns.md#MT-SUBDOM-C1 ~ C7 |
company-case-study (Auth0 + subdomain 패턴 — 공식 best practice 로 일반화 금지) |
JWT claim 우선의 official standard 근거 없음. OIDC/JWT spec 의 multi-tenancy 관행 raw 미확보 |
| D6 | tenant ID format = opaque ULID (26 chars Crockford base32). UUID/numeric 금지 | UNSUPPORTED_DECISION — ULID 표준 spec raw 미확보 (Alizain Feerasta ULID spec 등) | none | ULID spec raw 등록 시 보강 가능 |
| D7 | tenant 미지원 모드에서 X-Tenant-Id 헤더 수신 시 400 TENANT_NOT_SUPPORTED (Spring Security filter) |
raw/official-docs/multitenancy-aws-saas-tenant-isolation-whitepaper.md#AWS-TENANT-C2 (boundary breach un-recoverable — fail-fast 정당화) |
official-vendor-doc (간접 근거) |
AWS whitepaper 는 specific HTTP status 또는 filter layer 를 명시하지 않음 — ca-tmpl 구현 선택 |
| D8 | async/event tenant propagation = TaskDecorator + message header tenant_id |
UNSUPPORTED_DECISION — Spring TaskDecorator reference 또는 OpenTelemetry baggage 표준 raw 미확보 | none | Spring TaskDecorator / OpenTelemetry baggage spec raw 등록 시 보강 가능 |
| D9 | tenant_id ULID 원본은 metric tag 직접 사용 금지 (metrics-alerting-contract SSOT 가 bounded mapping 결정) | UNSUPPORTED_DECISION — high-cardinality label 회피 운영 결정. Prometheus 공식 doc raw 미확보 | none | Prometheus best practices raw 등록 시 보강 가능 |
| D10 | repository-access-permission cross-cut = tenant 활성 시 모든 @UseCaseRepositoryAccess 호출에 tenant_id 자동 필터링; cross-tenant admin = CROSS_TENANT_ADMIN capability 필수 |
raw/official-docs/multitenancy-aws-saas-tenant-isolation-whitepaper.md#AWS-TENANT-C6 (resource layer enforcement — needs-confirmation), raw/official-docs/multitenancy-hibernate-user-guide.md#HBN-MT-C2 (DISCRIMINATOR strategy) |
needs-confirmation + needs-confirmation |
AWS sub-page 와 Hibernate body verbatim 모두 재확인 실패. capability 강제 enforcement 자체는 ca-tmpl 고유 |
검증해야 할 주장
| Claim | Why uncertain | How to verify | Status |
|---|---|---|---|
| AWS Silo/Pool/Bridge 정의의 verbatim 정확성 | 2026-05-27 sub-page WebFetch 가 페이지 title 만 반환 — body truncated | archive.org snapshot 으로 sub-page 본문 verbatim 재확인 또는 manual browser 검증 | needs-confirmation |
| AWS "Authentication is not isolation" 의 verbatim 정확성 | 2026-05-27 sub-page WebFetch 가 body truncated | archive.org snapshot 으로 sub-page 본문 verbatim 재확인 또는 manual browser 검증 | needs-confirmation |
| Hibernate 3 strategy (DATABASE/SCHEMA/DISCRIMINATOR) 정의의 verbatim 정확성 | WebFetch 가 sub-section 구조만 확인, body truncated | archive.org snapshot 으로 Hibernate User Guide chapter 24 본문 verbatim 재확인 또는 manual browser 검증 | needs-confirmation |
@TenantId annotation 적용 시 entity 별 강제 여부 (opt-in 모델과 호환) |
Hibernate 6 native 지원이라는 본문 verbatim 미확인 | Hibernate 6 reference doc + 실제 entity 에 적용 후 자동 필터 동작 contract test | needs-confirmation |
CurrentTenantIdentifierResolver 의 ThreadLocal vs SecurityContextHolder 선택 |
Spring Security 와의 통합 검증 미완 | Spring Security SecurityContextHolder 와 Hibernate resolver 통합 + thread-local leak 테스트 |
planned |
JWT claim tenant_id 우선이 OIDC/JWT 표준 multi-tenancy 관행 |
OIDC/JWT multi-tenancy spec raw 미확보 | RFC 7519 (JWT) + RFC 7517 (JWK) + OIDC multi-tenancy 가이드 raw 등록 | needs-confirmation |
| ULID format opaqueness 가 PII 분류 회피 보장 | ULID spec 의 timestamp 추출 가능성 (앞 48-bit) | ULID spec raw 등록 + timestamp embed 의 PII risk 평가 | needs-confirmation |
TaskDecorator + message header tenant_id propagation 의 thread-local leak 차단 |
비동기 경로 leak 테스트 미완 | TenantPropagationContractTest 구현 + @Async / Kafka publish 시 tenant_id leak 안 함 verify |
planned |
cache key tenant_id 우선 prefix 가 모든 cache 접근 경로에서 동작 |
cache-consistency-contract 연동 미검증 | CacheKeyTenantScopeTest 구현 + Redisson / Caffeine 접근 시 tenant prefix 강제 verify |
planned |
| migration trigger (tenant 수 수백~수천 + row 수억 → schema-per-tenant) 의 정량 기준 | 본 raw 의 비교 핵심은 일반 가이드. 실제 정량 trigger 미정 | tenant 증가 추이 + Citus / schema-per-tenant migration runbook 작성 | needs-confirmation |
마주친 문제
- 아직 없음.
구현 가이드
- ingress에서 검증한 tenant ID를 immutable context로 캡처하고 use case·outbound call에 명시적으로 전달한다.
- thread reuse·async handoff 전후에는 capture/restore/clear를 짝지어 이전 요청의 context가 남지 않게 한다.
- repository query와 cache key에는 같은 tenant scope를 적용하고 누락 시 fail-closed한다.
엣지·실패·의존
- context clear 누락은 cross-tenant data leak로 이어질 수 있으며 background job에는 요청 context가 없다는 별도 경계가 필요하다.
- authentication·runtime context propagation·persistence auditing 계약과 함께 검증한다.
관련 일일 노트
- 별도 일일 노트 없음.
완료 후 정리
- PR 링크:
- 리뷰 메모:
- 머지 결과 / 배포 환경:
- wiki 추출 대상 (verified만,
wiki/projects/로만 추출):actually-implemented항목:locally-verified항목:prod-verified항목:
- 추출하지 않을 항목 (planned / documented-only / abandoned):