Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
0209518
Feat: ν…Œλ„ŒνŠΈλ³„ μ„€μ • 상속 κΈ°λŠ₯ κ΅¬ν˜„
YoungseoChoi23 Oct 23, 2025
63b2e7d
CacheConfig의 cacheManager에 @Primary μ–΄λ…Έν…Œμ΄μ…˜ μΆ”κ°€
YoungseoChoi23 Oct 24, 2025
9c947fc
feat : tenantConfigInheritanceServiceTest 검증
YoungseoChoi23 Oct 24, 2025
d35fea2
merge with develop
YoungseoChoi23 Nov 9, 2025
e80f691
fix : ApplicationTest 파일 develop λΈŒλžœμΉ˜μ™€ λ™μΌν•˜κ²Œ μˆ˜μ •
YoungseoChoi23 Nov 9, 2025
c3aa02d
refactor: TenantConfig μ—”ν‹°ν‹° μ½”λ“œλ¦¬λ·° 및 λ¦¬νŒ©ν† λ§
YoungseoChoi23 Nov 9, 2025
77c080f
refactor: TenantTypeConfig μ—”ν‹°ν‹° μ½”λ“œ 리뷰 및 λ¦¬νŒ©ν† λ§
YoungseoChoi23 Nov 9, 2025
25e7b5c
refactor: TenantConfigInheritanceService μ½”λ“œ 리뷰 및 λ¦¬νŒ©ν† λ§
YoungseoChoi23 Nov 9, 2025
6be3784
refactor: TenantConfigService μ½”λ“œ 리뷰 및 λ¦¬νŒ©ν† λ§
YoungseoChoi23 Nov 9, 2025
b20bf41
refactor: TenantConfigCacheService μ½”λ“œ 리뷰 및 λ¦¬νŒ©ν† λ§
YoungseoChoi23 Nov 9, 2025
b135c0f
refactor: TenantConfigController μ½”λ“œ 리뷰 및 λ¦¬νŒ©ν† λ§
YoungseoChoi23 Nov 9, 2025
c9cda0b
refactor: 클래슀/Public λ©”μ„œλ“œ/Private λ©”μ„œλ“œ JavaDoc 보완, μ‚­μ œ 성곡 및 κ²½κ³  λ‘œκΉ… μΆ”κ°€
YoungseoChoi23 Nov 9, 2025
ae08fd0
refactor: TenantConfigRepository μΈν„°νŽ˜μ΄μŠ€, λ©”μ„œλ“œ JavaDoc 보완
YoungseoChoi23 Nov 9, 2025
3a3a651
refactor: TenantTypeConfigRepository μΈν„°νŽ˜μ΄μŠ€, λ©”μ„œλ“œ JavaDoc 보완
YoungseoChoi23 Nov 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 69 additions & 1 deletion docker/mysql/init/01-init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,72 @@ ON DUPLICATE KEY UPDATE
-- μ°Έκ³ : MySQL은 μ—­μˆœ μ •λ ¬ μ΅œμ ν™” μ‹œ DESC μΈλ±μŠ€κ°€ 도움될 수 있음
-- CREATE INDEX idx_audit_logs_rt_rid_et_ts_desc ON audit_logs(resource_type, resource_id, event_type, event_timestamp DESC);
-- λŒ€μ•ˆ(버전 ν˜Έν™˜):
-- CREATE INDEX idx_audit_logs_rt_rid_et_ts ON audit_logs(resource_type, resource_id, event_type, event_timestamp);
-- CREATE INDEX idx_audit_logs_rt_rid_et_ts ON audit_logs(resource_type, resource_id, event_type, event_timestamp);

-- ν…Œλ„ŒνŠΈ μ„€μ • κ΄€λ ¨ ν…Œμ΄λΈ” 생성
CREATE TABLE IF NOT EXISTS tenant_configs (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
tenant_id BIGINT NOT NULL,
config_key VARCHAR(255) NOT NULL,
config_value TEXT,
config_type ENUM('STRING', 'NUMBER', 'BOOLEAN', 'JSON', 'ENCRYPTED') NOT NULL,
description TEXT,
is_encrypted BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
created_by VARCHAR(100),
updated_by VARCHAR(100),
is_deleted BOOLEAN DEFAULT FALSE,
UNIQUE KEY uk_tenant_config (tenant_id, config_key),
INDEX idx_tenant_config_tenant_id (tenant_id),
INDEX idx_tenant_config_key (config_key),
INDEX idx_tenant_config_deleted (is_deleted)
);

CREATE TABLE IF NOT EXISTS tenant_type_configs (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
tenant_type ENUM('INDIVIDUAL', 'SMALL_BUSINESS', 'ENTERPRISE', 'GOVERNMENT') NOT NULL,
config_key VARCHAR(255) NOT NULL,
config_value TEXT,
config_type ENUM('STRING', 'NUMBER', 'BOOLEAN', 'JSON', 'ENCRYPTED') NOT NULL,
description TEXT,
is_encrypted BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
created_by VARCHAR(100),
updated_by VARCHAR(100),
is_deleted BOOLEAN DEFAULT FALSE,
UNIQUE KEY uk_tenant_type_config (tenant_type, config_key),
INDEX idx_tenant_type_config_type (tenant_type),
INDEX idx_tenant_type_config_key (config_key),
INDEX idx_tenant_type_config_deleted (is_deleted)
);

-- ν…Œλ„ŒνŠΈ νƒ€μž…λ³„ κΈ°λ³Έ μ„€μ • 데이터 μ‚½μž…
INSERT INTO tenant_type_configs (tenant_type, config_key, config_value, config_type, description, is_encrypted) VALUES
-- ENTERPRISE κΈ°λ³Έ μ„€μ •
('ENTERPRISE', 'max_users', '1000', 'NUMBER', 'Maximum number of users for Enterprise tenants', FALSE),
('ENTERPRISE', 'max_storage_gb', '10000', 'NUMBER', 'Maximum storage quota in GB for Enterprise tenants', FALSE),
('ENTERPRISE', 'support_level', 'premium', 'STRING', 'Support level for Enterprise tenants', FALSE),
('ENTERPRISE', 'max_file_size', '1024', 'NUMBER', 'Maximum file size in MB for Enterprise tenants', FALSE),

-- SMALL_BUSINESS κΈ°λ³Έ μ„€μ •
('SMALL_BUSINESS', 'max_users', '100', 'NUMBER', 'Maximum number of users for Small Business tenants', FALSE),
('SMALL_BUSINESS', 'max_storage_gb', '1000', 'NUMBER', 'Maximum storage quota in GB for Small Business tenants', FALSE),
('SMALL_BUSINESS', 'support_level', 'standard', 'STRING', 'Support level for Small Business tenants', FALSE),
('SMALL_BUSINESS', 'max_file_size', '100', 'NUMBER', 'Maximum file size in MB for Small Business tenants', FALSE),

-- INDIVIDUAL κΈ°λ³Έ μ„€μ •
('INDIVIDUAL', 'max_users', '10', 'NUMBER', 'Maximum number of users for Individual tenants', FALSE),
('INDIVIDUAL', 'max_storage_gb', '100', 'NUMBER', 'Maximum storage quota in GB for Individual tenants', FALSE),
('INDIVIDUAL', 'support_level', 'basic', 'STRING', 'Support level for Individual tenants', FALSE),
('INDIVIDUAL', 'max_file_size', '50', 'NUMBER', 'Maximum file size in MB for Individual tenants', FALSE),

-- GOVERNMENT κΈ°λ³Έ μ„€μ •
('GOVERNMENT', 'max_users', '5000', 'NUMBER', 'Maximum number of users for Government tenants', FALSE),
('GOVERNMENT', 'max_storage_gb', '50000', 'NUMBER', 'Maximum storage quota in GB for Government tenants', FALSE),
('GOVERNMENT', 'support_level', 'government', 'STRING', 'Support level for Government tenants', FALSE),
('GOVERNMENT', 'max_file_size', '2048', 'NUMBER', 'Maximum file size in MB for Government tenants', FALSE)
ON DUPLICATE KEY UPDATE
config_value = VALUES(config_value),
description = VALUES(description);
194 changes: 194 additions & 0 deletions docs/TENANT_CONFIG_INHERITANCE_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# ν…Œλ„ŒνŠΈλ³„ μ„€μ • 상속 μ‹œμŠ€ν…œ κ°€μ΄λ“œ

## κ°œμš”

ν…Œλ„ŒνŠΈλ³„ μ„€μ • 상속 μ‹œμŠ€ν…œμ€ ν”Œλž«νΌ μ „μ—­ μ„€μ •, ν…Œλ„ŒνŠΈ νƒ€μž…λ³„ κΈ°λ³Έ μ„€μ •, κ°œλ³„ ν…Œλ„ŒνŠΈ μ„€μ • κ°„μ˜ 상속 ꡬ쑰λ₯Ό μ§€μ›ν•˜μ—¬ λͺ¨λ“  ν…Œλ„ŒνŠΈμ˜ 유효 섀정을 일관성 있게 κ³„μ‚°ν•˜κ³  μΊμ‹œν•˜λŠ” μ‹œμŠ€ν…œμž…λ‹ˆλ‹€.

## 상속 μˆœμ„œ

섀정값은 λ‹€μŒ μˆœμ„œλ‘œ μƒμ†λ©λ‹ˆλ‹€:

1. **ν”Œλž«νΌ μ „μ—­ μ„€μ •** (μ΅œν•˜μœ„ μš°μ„ μˆœμœ„)
2. **ν…Œλ„ŒνŠΈ νƒ€μž…λ³„ κΈ°λ³Έ μ„€μ •** (쀑간 μš°μ„ μˆœμœ„)
3. **κ°œλ³„ ν…Œλ„ŒνŠΈ μ„€μ •** (μ΅œμƒμœ„ μš°μ„ μˆœμœ„)

## μ£Όμš” μ»΄ν¬λ„ŒνŠΈ

### 1. μ—”ν‹°ν‹°

- `TenantConfig`: κ°œλ³„ ν…Œλ„ŒνŠΈμ˜ μ„€μ •
- `TenantTypeConfig`: ν…Œλ„ŒνŠΈ νƒ€μž…λ³„ κΈ°λ³Έ μ„€μ •
- `PlatformConfig`: ν”Œλž«νΌ μ „μ—­ μ„€μ • (κΈ°μ‘΄)

### 2. μ„œλΉ„μŠ€

- `TenantConfigInheritanceService`: μ„€μ • 상속 둜직 처리
- `TenantConfigService`: κ°œλ³„ ν…Œλ„ŒνŠΈ μ„€μ • 관리
- `TenantConfigCacheService`: μΊμ‹œ 관리

### 3. 컨트둀러

- `TenantConfigController`: ν…Œλ„ŒνŠΈ μ„€μ • API
- `TenantTypeConfigController`: ν…Œλ„ŒνŠΈ νƒ€μž…λ³„ μ„€μ • API

## API μ‚¬μš© μ˜ˆμ‹œ

### 1. ν…Œλ„ŒνŠΈμ˜ 유효 μ„€μ • 쑰회

```http
GET /api/v1/tenants/{tenantKey}/configs/effective
```

**응닡 μ˜ˆμ‹œ:**

```json
{
"success": true,
"data": {
"tenantKey": "enterprise-tenant-1",
"configurations": {
"max_users": {
"value": 1000,
"source": "TENANT_TYPE",
"description": "Maximum number of users for Enterprise tenants",
"configType": "NUMBER"
},
"max_file_size": {
"value": 2048,
"source": "TENANT",
"description": "Custom file size limit",
"configType": "NUMBER"
},
"support_level": {
"value": "premium",
"source": "TENANT_TYPE",
"description": "Support level for Enterprise tenants",
"configType": "STRING"
}
}
}
}
```

### 2. νŠΉμ • μ„€μ • 쑰회

```http
GET /api/v1/tenants/{tenantKey}/configs/effective/{configKey}
```

### 3. ν…Œλ„ŒνŠΈ κ°œλ³„ μ„€μ • 관리

```http
# μ„€μ • 생성/μˆ˜μ •
POST /api/v1/tenants/{tenantKey}/configs
Content-Type: application/json

{
"configKey": "custom_feature_enabled",
"configValue": "true",
"configType": "BOOLEAN",
"description": "Enable custom feature for this tenant"
}

# μ„€μ • μ‚­μ œ
DELETE /api/v1/tenants/{tenantKey}/configs/{configKey}
```

### 4. ν…Œλ„ŒνŠΈ νƒ€μž…λ³„ κΈ°λ³Έ μ„€μ • 관리

```http
# νƒ€μž…λ³„ μ„€μ • 생성/μˆ˜μ •
POST /api/v1/tenant-types/{tenantType}/configs
Content-Type: application/json

{
"configKey": "max_users",
"configValue": "1000",
"configType": "NUMBER",
"description": "Maximum number of users for Enterprise tenants"
}
```

## μ‹œλ‚˜λ¦¬μ˜€ μ˜ˆμ‹œ

### μ‹œλ‚˜λ¦¬μ˜€ 1: ν”Œλž«νΌ μ„€μ • 상속

**Given**: ν”Œλž«νΌ μ „μ—­ 섀정에 `max_file_size: 100MB`κ°€ 섀정됨
**When**: ν…Œλ„ŒνŠΈκ°€ νŠΉλ³„ν•œ 섀정을 ν•˜μ§€ μ•ŠμŒ
**Then**: ν…Œλ„ŒνŠΈλŠ” μžλ™μœΌλ‘œ 100MB μ œν•œμ„ μƒμ†λ°›μŒ

### μ‹œλ‚˜λ¦¬μ˜€ 2: ν…Œλ„ŒνŠΈλ³„ μ„€μ • μ˜€λ²„λΌμ΄λ“œ

**Given**: ν”Œλž«νΌ μ „μ—­ 섀정이 `max_file_size: 100MB`
**When**: νŠΉμ • ν…Œλ„ŒνŠΈκ°€ `max_file_size: 500MB`둜 μ„€μ •
**Then**: ν•΄λ‹Ή ν…Œλ„ŒνŠΈλŠ” 500MB μ œν•œμ„ μ‚¬μš©ν•˜κ³ , λ‹€λ₯Έ ν…Œλ„ŒνŠΈλŠ” 100MB μ œν•œ μœ μ§€

### μ‹œλ‚˜λ¦¬μ˜€ 3: νƒ€μž… κΈ°λ³Έκ°’ 적용

**Given**: 전역에 `support_level` μ—†μŒ
**And**: νƒ€μž…(ENTERPRISE) κΈ°λ³Έ `support_level=premium`
**When**: ν…Œλ„ŒνŠΈκ°€ λ―Έμ„€μ •
**Then**: `support_level=premium` 적용

### μ‹œλ‚˜λ¦¬μ˜€ 4: νƒ€μž… λ³€κ²½ 반영

**Given**: ν…Œλ„ŒνŠΈ νƒ€μž… INDIVIDUAL β†’ ENTERPRISE둜 λ³€κ²½
**When**: νƒ€μž… λ³€κ²½ 이벀트 λ°œμƒ
**Then**: μΊμ‹œ λ¬΄νš¨ν™” 및 유효 섀정에 ENTERPRISE κΈ°λ³Έκ°’ 반영

## μΊμ‹œ 관리

μ‹œμŠ€ν…œμ€ μžλ™μœΌλ‘œ μΊμ‹œλ₯Ό κ΄€λ¦¬ν•©λ‹ˆλ‹€:

- **μ„€μ • λ³€κ²½ μ‹œ**: ν•΄λ‹Ή ν…Œλ„ŒνŠΈμ˜ μΊμ‹œ μžλ™ λ¬΄νš¨ν™”
- **ν…Œλ„ŒνŠΈ νƒ€μž… λ³€κ²½ μ‹œ**: ν•΄λ‹Ή νƒ€μž…μ˜ λͺ¨λ“  ν…Œλ„ŒνŠΈ μΊμ‹œ λ¬΄νš¨ν™”
- **ν”Œλž«νΌ μ„€μ • λ³€κ²½ μ‹œ**: λͺ¨λ“  ν…Œλ„ŒνŠΈ μΊμ‹œ λ¬΄νš¨ν™”

μˆ˜λ™ μΊμ‹œ λ¬΄νš¨ν™”:

```http
POST /api/v1/tenants/{tenantKey}/configs/cache/evict
```

## μ„€μ • νƒ€μž…

μ§€μ›ν•˜λŠ” μ„€μ • νƒ€μž…:

- `STRING`: λ¬Έμžμ—΄
- `NUMBER`: 숫자 (μ •μˆ˜/μ‹€μˆ˜)
- `BOOLEAN`: λΆˆλ¦°κ°’
- `JSON`: JSON 객체
- `ENCRYPTED`: μ•”ν˜Έν™”λœ κ°’

## μ—λŸ¬ 처리

ν‘œμ€€ν™”λœ μ—λŸ¬ μ½”λ“œλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€:

- `TENANT_CONFIG_NOT_FOUND`: 섀정을 찾을 수 μ—†μŒ
- `INVALID_CONFIG_VALUE`: μœ νš¨ν•˜μ§€ μ•Šμ€ μ„€μ •κ°’
- `CONFIG_KEY_ALREADY_EXISTS`: μ€‘λ³΅λœ μ„€μ • ν‚€
- `CONFIG_INHERITANCE_FAILED`: 상속 처리 μ‹€νŒ¨

## μ„±λŠ₯ 고렀사항

- μ„€μ • μ‘°νšŒλŠ” μΊμ‹œλ₯Ό 톡해 μ΅œμ ν™”λ¨
- μ„€μ • λ³€κ²½ μ‹œμ—λ§Œ μΊμ‹œ λ¬΄νš¨ν™”
- λŒ€λŸ‰ μ„€μ • 쑰회 μ‹œ 배치 처리 ꢌμž₯
- μ„€μ • 상속 계산은 λΉ„λ™κΈ°λ‘œ 처리 κ°€λŠ₯

## λ³΄μ•ˆ 고렀사항

- λ―Όκ°ν•œ 섀정은 μ•”ν˜Έν™” μ €μž₯
- ν…Œλ„ŒνŠΈλ³„ μ„€μ • μ ‘κ·Ό κΆŒν•œ 검증
- 감사 λ‘œκΉ…μœΌλ‘œ μ„€μ • λ³€κ²½ 이λ ₯ 좔적
- μ„€μ •κ°’ μœ νš¨μ„± 검증

## λͺ¨λ‹ˆν„°λ§

- μ„€μ • 쑰회 μ„±λŠ₯ λ©”νŠΈλ¦­
- μΊμ‹œ 히트율 λͺ¨λ‹ˆν„°λ§
- μ„€μ • λ³€κ²½ λΉˆλ„ 좔적
- μ—λŸ¬μœ¨ 및 μ˜ˆμ™Έ 상황 λͺ¨λ‹ˆν„°λ§



Loading