# Custom Field JSONB 및 인덱스 정책 ## 현재 구조 - Tenant custom schema는 `tenants.config.userSchema` JSONB에 저장한다. - Tenant custom value는 backend DB의 `users.metadata` JSONB에 저장한다. - `isLoginId=true`인 Tenant field 값은 로그인 식별자 처리를 위해 `user_login_ids`에도 동기화한다. - Ory Kratos traits에는 인증/식별에 필요한 최소 값만 동기화하는 방향으로 정리한다. - RP custom value는 backend DB의 `rp_user_metadata.metadata` JSONB에 별도 저장한다. ## Tenant Custom Field Tenant schema field는 다음 속성을 기준으로 한다. ```json { "key": "employeeNo", "label": "사번", "type": "text", "required": false, "indexed": true, "isLoginId": true, "adminOnly": false, "validation": "^[A-Z0-9]+$" } ``` - `indexed=true`는 검색/필터 최적화 대상이라는 의미다. - `isLoginId=true`이면 backend와 adminfront 모두 `indexed=true`를 강제한다. - `isLoginId=true`는 값 필수를 의미하지 않는다. 값 필수 여부는 `required=true`로 별도 제어한다. - `isLoginId=true`인 field는 `type=text`만 허용한다. - JSONB 통합 정책에서는 `varchar` 크기 지정 의미를 두지 않는다. ## RP Custom Field RP custom schema는 client metadata의 `customUserSchema`에 저장한다. ```json { "customUserSchema": [ { "key": "approvalLevel", "label": "승인 등급", "type": "text", "required": false, "indexed": true, "claimEnabled": true } ] } ``` RP custom value는 `rp_user_metadata` 테이블에 저장한다. ```text client_id text user_id uuid metadata jsonb created_at timestamptz updated_at timestamptz primary key (client_id, user_id) foreign key (user_id) references users(id) ``` Backend API 초안: ```text GET /api/v1/dev/clients/:id/users/:userId/metadata PUT /api/v1/dev/clients/:id/users/:userId/metadata ``` PUT payload: ```json { "metadata": { "approvalLevel": "A", "preferences": { "theme": "dark" } } } ``` ## 검색 및 인덱스 - `indexed=true` field만 검색 UI/API 후보로 노출한다. - 기본 검색은 exact match, exists, JSON containment 중심으로 제한한다. - RP custom field의 LIKE/fuzzy 검색은 기본 제공하지 않는다. - GIN 인덱스는 backend index manager가 별도 상태로 관리하는 방향을 원칙으로 한다. - API 요청 처리 중 `CREATE INDEX`를 동기 실행하지 않는다. ## Claim Projection JWT 또는 userinfo 응답에서는 custom field를 top-level에 풀지 않는다. Tenant/RP 단위로 묶어서 전달한다. ```json { "tenant_profiles": [ { "tenant_id": "tenant-uuid", "tenant_slug": "hanmac-family", "fields": { "employeeNo": "E1001" } } ], "rp_profiles": [ { "client_id": "sample-rp", "fields": { "approvalLevel": "A" } } ] } ``` - `claimEnabled=true` field만 RP claim 후보로 포함한다. - 긴 JSON 값은 기본적으로 token claim보다 userinfo/profile API 응답에 싣는 방향을 우선한다.