# 외부 서비스 조직선택기 통합 가이드 외부 서비스에서 조직·사용자를 선택하는 두 가지 연동 방법입니다. 이 문서에서 RP(Relying Party)는 Baron과 연동하는 외부 서비스를 뜻합니다. 현재 저장소 구현을 기준으로 합니다. | 필요한 방식 | 데이터 조회 | 선택 UI | 읽을 절 | | --- | --- | --- | --- | | Integration API로 받은 데이터를 자체 화면에서 선택 | RP Backend가 Integration Credential로 조회 | 독립 SDK의 `renderOrgPicker()` | 0절 | | OrgFront 선택기 화면을 그대로 임베딩 | iframe 안의 OrgFront가 조회 | `/embed/picker`와 `postMessage` | 1~5절 | ## 0. Integration API 데이터 + Picker SDK 방식 이 방식은 **RP Backend가 조직 데이터를 조회하고, RP 브라우저는 선택 UI만 렌더링**합니다. OrgFront iframe이나 OrgFront 사용자 로그인 세션을 사용하지 않습니다. RP 자체 로그인과 권한 검사는 계속 필요합니다. ```text RP 브라우저 → RP Backend → GET /api/v1/integrations/org-context │ Integration Credential └─ 승인된 v2 응답 → buildOrgChartModel → renderOrgPicker ``` ### Credential과 조회 API 1. [Directory API 가이드](directory-api.md)의 절차로 AdminFront에서 Integration Credential을 발급합니다. OperationID는 `baron.org-context.read`, 범위는 필요한 Tenant subtree, 사용자 선택에 필요한 exposure는 `members`로 승인받습니다. 2. key ID와 secret을 **RP Backend에 보관**합니다. 브라우저 JavaScript, iframe URL, `VITE_*` 환경변수에 넣지 않습니다. 3. RP Backend가 사용자의 로그인과 요청한 조직에 대한 접근 권한을 확인한 뒤 아래 API를 호출합니다. `tenantId` 또는 `tenantSlug` 중 하나만 전달합니다. ```http GET /api/v1/integrations/org-context?tenantId=&exposure=members X-Baron-Key-ID: X-Baron-Key-Secret: Accept: application/json ``` 응답은 `schemaVersion: "baron.org-context.v2"`이며 `scope`, `tree`, `tenants`를 포함합니다. 원본 계약을 유지한 채 브라우저가 사용할 수 있는 응답으로 전달합니다. 이 예제처럼 사용자 이름을 표시하려면 `members`를 사용합니다. `structure`는 기본 exposure로 조직 구조만 조회하며 사용자 이름을 표시하는 다중선택 예제에는 적합하지 않습니다. 정확한 필드와 오류·cache 처리는 [Org Context API 가이드](org-context-api.md)를 따릅니다. Credential은 서비스에 부여된 범위입니다. RP Backend는 이 범위 전체를 모든 RP 사용자에게 공개하지 말고, 해당 사용자가 조회해도 되는 subtree인지 별도로 확인해야 합니다. `401`/`403`/조회 실패를 빈 조직도 응답으로 바꾸지 않습니다. SDK의 `createOrgContextClient({ baseUrl, credentials: { keyId, keySecret } })`와 `fetchOrgContext({ tenantId, exposure: "members" })`를 Backend 조회에 사용할 수도 있습니다. 이때도 secret을 사용하는 코드는 Backend에서만 실행합니다. ### SDK 파일 준비 SDK는 저장소에서 ES module과 UMD 파일로 빌드합니다. ```sh cd orgfront pnpm build:org-context-chart ``` `orgfront/dist/org-context-chart/`에 `boc-.js`, `boc-.min.js`, UMD 파일 등이 생성됩니다. 필요한 ES module 파일 한 개를 RP의 정적 파일로 배포합니다. 아래 예제는 그 파일을 `/vendor/baron-org-context-chart.js`라는 이름으로 배포했다고 가정합니다. 이 주소는 RP가 정하는 경로이며, Baron의 고정 CDN 주소나 npm 패키지명이 아닙니다. ### 이미 받은 데이터로 사용자 선택기만 렌더링 아래 `/api/org-picker-context`는 **RP가 구현하는 예시 endpoint**입니다. 앞의 인증·권한 검사를 수행한 뒤 Baron v2 응답을 반환하도록 구현합니다. 브라우저는 Integration Credential을 알 필요가 없습니다. ```html

```

```js
import {
  buildOrgChartModel,
  renderOrgPicker,
} from "/vendor/baron-org-context-chart.js";

// RP 사용자 세션으로 자신의 Backend를 호출합니다.
const response = await fetch("/api/org-picker-context", {
  credentials: "same-origin",
  headers: { Accept: "application/json" },
});
if (!response.ok) throw new Error(`조직 조회 실패: ${response.status}`);
const payload = await response.json();
if (payload.schemaVersion !== "baron.org-context.v2") {
  throw new Error("지원하지 않는 조직 데이터 버전입니다.");
}

// 이미 API 응답을 보유하고 있다면 위 fetch 없이 payload부터 사용합니다.
const model = buildOrgChartModel(payload);
const host = document.getElementById("api-org-picker");
const result = document.getElementById("api-org-picker-result");
let picker;
picker = renderOrgPicker(host, model, {
  variant: "orgfront",
  mode: "multiple",
  selectable: "user",
  includeDescendants: true,
  showDescendantToggle: true,
  onChange(selection) {
    result.textContent = `${selection.length}명 선택 중`;
  },
  onConfirm(selection) {
    const userIds = [...new Set(selection
      .filter(item => item.type === "user")
      .map(item => item.id))];
    // 이 UUID들을 RP Backend에 제출합니다. 저장 권한은 Backend가 재검사합니다.
    result.textContent = JSON.stringify(userIds, null, 2);
    picker.destroy();
  },
  onCancel() {
    result.textContent = "선택을 취소했습니다.";
    picker.destroy();
  },
});
// 부모 모달을 닫거나 컴포넌트를 해제할 때도 picker.destroy() 호출
```

예제는 ES module로 실행합니다. HTML에 직접 넣을 때는 JavaScript 코드를 `