# 설계서: SQL trace의 set_vpd_context 사용자 컨텍스트 표시 (#571)

> **상태**: Approved
> **작성**: [AI] Architect · **최종수정**: 2026-06-30
> **추적성** — Redmine: #571 · 선행 기능: #567, #570
> · 구현 파일: `probe-result.html`, `TokenContextView`, `ProbeController`
> · 테스트: `GuidedFlowTemplateTest`, `mvn test`

## 1. 목적

권한 적용 SQL trace에서 최종 predicate만 보여 사용자와 predicate의 연결이 끊겨 보이는 문제를 해결한다. `set_vpd_context`가 설정한 `CB_AGENT_CTX.USER_ID`와 백오피스가 해석한 사용자·역할·그룹을 predicate 앞에 표시해 다음 흐름을 한 화면에서 증명한다.

```text
Bearer Token
  → set_vpd_context
  → CB_AGENT_CTX.USER_ID = 사용자 ID
  → 사용자/역할/그룹 권한 규칙 조회
  → VPD predicate
  → effective SQL
```

## 2. 설계 원칙

- SQL에 `AND USER_ID = ...` 같은 가짜 조건을 추가하지 않는다. 벡터 검색 대상에는 `USER_ID` 컬럼이 없고, 실제 VPD 함수는 사용자 context로 권한 규칙을 계산한 뒤 대상 데이터의 `TECH_TAG` predicate를 반환한다.
- context는 실제 토큰에서 해석된 `TokenContextView`를 사용한다. Bearer 원문은 표시하지 않는다.
- `USER_ID`, 사용자명, 직접 역할, 그룹, 상속 역할은 trace 영역의 “set_vpd_context 사용자 컨텍스트” 카드에 표시한다.
- 아래 VPD predicate는 바로 위 context에서 계산된 결과라는 설명을 명시한다.

## 3. 인수조건

- [ ] `토큰 적용 후 SQL`에 `CB_AGENT_CTX.USER_ID`와 사용자명이 표시된다.
- [ ] 직접 역할·그룹·상속 역할이 context 카드에서 확인된다.
- [ ] context → `VPD가 추가한 WHERE 조건` → `권한 적용 SQL` 순서가 화면에 보인다.
- [ ] SQL 본문에 실제 존재하지 않는 `USER_ID` 조건을 삽입하지 않는다.
- [ ] invalid/inactive token처럼 context가 없을 때는 기존 “토큰에서 사용자를 찾지 못함” 안내를 유지한다.
- [ ] Bearer 원문과 임베딩 숫자는 context 카드에 표시하지 않는다.
- [ ] 전체 Maven 테스트와 실제 vector probe smoke가 통과한다.

## 4. 화면 계약

`TokenContextView.userId()`를 `CB_AGENT_CTX.USER_ID` 값으로 표시하고, `username`, `directRoles`, `groups`, `inheritedRoles`를 사람이 읽을 수 있는 label로 표시한다. predicate와 effective SQL은 기존 `ProbeResult` 필드를 그대로 사용한다.

## 5. 검증

- 템플릿 테스트에서 `set_vpd_context 사용자 컨텍스트`, `CB_AGENT_CTX.USER_ID`, `VPD가 추가한 WHERE 조건`, `권한 적용 SQL`을 확인한다.
- `mvn test`.
- vector `/probe`에서 `agent_all` 또는 선택 사용자의 ID/역할과 TAG predicate가 같은 결과 카드에 표시되는지 확인한다.
