# tdc114plus API 계약 초안 작성일: 2026-07-02 상태: v0.1 초안 목적: `tdc114plus` Flutter 앱이 Baron SSO backend 및 orgFront 데이터와 연동하기 위해 필요한 API 계약을 정의한다. 본 문서는 구현 전 계약 기준이며, 실제 Baron SSO backend 구현은 `/home/ubuntu/workspace/baron-sso-tdc114plus-api`의 `feature/tdc114plus-api` 브랜치에서 진행한다. ## 1. 설계 기준 확인한 기준 문서: - `docs/tdc114plus-development-decision-brief-2026-07-01.md` - `docs/tdc114plus-development-policy-2026-07-02.md` - `docs/baron-sso-reference-source-policy-2026-07-02.md` - `docs/references/baron-safe-policies/api-contract.md` - Baron SSO `docs/API_DESIGN_POLICY.md` - Baron SSO `docs/identity-redis-mirror-policy-2026-06-09.md` - Baron SSO `docs/references/baron-safe-policies/nonstandard-phone-only-login-technical-review-2026-06-23.md` 확인한 기존 Baron SSO 코드: - `backend/cmd/server/main.go` - `backend/internal/handler/auth_handler.go` - `backend/internal/handler/tenant_handler.go` - `backend/internal/handler/user_handler.go` - `backend/internal/domain/user.go` - `backend/internal/domain/tenant.go` - `orgfront/src/lib/adminApi.ts` ## 2. 핵심 원칙 - 기존 Baron SSO API 응답을 `tdc114plus` 요구사항에 맞게 직접 변경하지 않는다. - `tdc114plus` 전용 endpoint와 response DTO를 둔다. - JSON field는 camelCase를 사용한다. - 목록 응답은 `items`, `limit`, `offset`, `total`, `nextCursor` 형식을 따른다. - 직원/조직 데이터는 Baron SSO `orgFront` 및 backend read model을 기준으로 제공한다. - 앱 자체 회원가입은 제공하지 않는다. - 미등록 사용자는 앱 사용을 허용하지 않는다. - 공지사항, 전자결재, 수신전화식별, 수신팝업은 1차 API 범위에서 제외한다. ## 3. API namespace 권장 namespace: ```text /api/v1/tdc114plus ``` 이유: - 기존 `/api/v1/admin/users`, `/api/v1/admin/orgchart/snapshot`은 관리자/orgFront 성격이 강하다. - 앱은 일반 등록 사용자용 직원검색/조직도 기능이므로 별도 namespace가 필요하다. - 향후 감사 로그, 마스킹, 앱별 권한 정책을 독립적으로 적용하기 쉽다. ## 4. 인증 API ### 4.1 전화번호 로그인 ```http POST /api/v1/tdc114plus/auth/phone-login ``` 설명: - 사용자가 앱 로그인창에 전화번호를 입력하면 Baron SSO 등록 사용자 여부를 확인한다. - 등록 사용자이면 앱 사용에 필요한 session token 또는 app access token을 반환한다. - 기존 Baron SSO의 `/api/v1/auth/phone-login` 흐름을 참고하되, `tdc114plus` 전용 DTO와 오류 정책을 둔다. 요청: ```json { "phoneNumber": "01012345678", "device": { "platform": "android", "appVersion": "0.1.0", "deviceName": "Pixel 8" } } ``` 응답: ```json { "status": "ok", "token": "session-or-app-token", "expiresAt": "2026-07-02T12:00:00Z", "user": { "id": "user-uuid", "name": "홍길동", "phoneNumber": "+821012345678", "tenantId": "tenant-uuid", "tenantName": "한맥", "tenantSlug": "hanmac", "department": "기술연구소", "grade": "책임", "position": "팀장", "jobTitle": "개발" } } ``` 오류: | HTTP | code | 설명 | | --- | --- | --- | | 400 | `invalid_phone_number` | 전화번호 형식 오류 | | 401 | `login_failed` | 등록 사용자 확인 실패. 사용자 존재 여부를 과도하게 드러내지 않는다. | | 429 | `rate_limited` | 반복 시도 제한 | | 503 | `identity_provider_unavailable` | Kratos/SSO 조회 실패 | 보안 메모: - 전화번호 단독 로그인은 표준 인증으로 보기 어렵다. - 1차 정책상 Baron SSO 등록 인원 확인용으로 사용하되, rate limit, 감사 로그, generic error message를 적용한다. - 운영 전에는 SMS OTP, 기기 등록, 내부망 제한, 추가 인증 중 하나 이상을 후속 검토한다. ### 4.2 내 프로필 ```http GET /api/v1/tdc114plus/me Authorization: Bearer {token} ``` 응답: ```json { "id": "user-uuid", "name": "홍길동", "phoneNumber": "+821012345678", "email": "user@example.com", "tenantId": "tenant-uuid", "tenantName": "한맥", "tenantSlug": "hanmac", "department": "기술연구소", "grade": "책임", "position": "팀장", "jobTitle": "개발", "permissions": { "directory": true, "organization": true, "favoritesSync": false } } ``` ## 5. 직원검색/전화번호검색 API ### 5.1 직원 목록 및 검색 ```http GET /api/v1/tdc114plus/directory/employees Authorization: Bearer {token} ``` Query: | 이름 | 필수 | 설명 | | --- | --- | --- | | `q` | N | 이름, 전화번호, 부서, 직위, 직무 검색어 | | `tenantId` | N | 가족사/회사/조직 필터 | | `tenantSlug` | N | 가족사 slug 필터 | | `department` | N | 부서명 필터 | | `limit` | N | 기본 50 | | `offset` | N | 기본 0 | | `cursor` | N | cursor pagination 사용 시 | 응답: ```json { "items": [ { "id": "user-uuid", "name": "홍길동", "phoneNumber": "+821012345678", "phoneDisplay": "010-1234-5678", "email": "user@example.com", "tenantId": "tenant-uuid", "tenantName": "한맥", "tenantSlug": "hanmac", "department": "기술연구소", "grade": "책임", "position": "팀장", "jobTitle": "개발", "status": "active", "profileImageUrl": null, "sortOrder": 100 } ], "limit": 50, "offset": 0, "total": 1, "nextCursor": "" } ``` 검색 규칙: - `q`는 이름, 전화번호, 부서, 직위, 직책, 직무, 이메일 일부를 대상으로 한다. - 전화번호 검색은 숫자만 입력해도 매칭되도록 서버에서 정규화한다. - 기본 노출 대상은 조직도 표시 가능한 사용자 상태로 제한한다. - Baron SSO 기준 `active`, `temporary_leave`, `suspended`는 조직도 노출 후보로 볼 수 있다. - `baron_guest`, `extended_leave`, `archived`는 기본 제외한다. ### 5.2 직원 상세 ```http GET /api/v1/tdc114plus/directory/employees/{employeeId} Authorization: Bearer {token} ``` 응답: ```json { "id": "user-uuid", "name": "홍길동", "phoneNumber": "+821012345678", "phoneDisplay": "010-1234-5678", "email": "user@example.com", "tenantId": "tenant-uuid", "tenantName": "한맥", "tenantSlug": "hanmac", "joinedTenants": [ { "id": "tenant-uuid", "name": "한맥", "slug": "hanmac", "type": "COMPANY", "parentId": "parent-tenant-uuid" } ], "department": "기술연구소", "grade": "책임", "position": "팀장", "jobTitle": "개발", "status": "active", "profileImageUrl": null, "actions": { "call": true, "sms": true, "email": true } } ``` ## 6. 가족사 필터/조직도 API ### 6.1 가족사/조직 필터 목록 ```http GET /api/v1/tdc114plus/organization/tenants Authorization: Bearer {token} ``` 응답: ```json { "items": [ { "id": "tenant-uuid", "name": "한맥", "slug": "hanmac", "type": "COMPANY", "parentId": "hanmac-family-root", "memberCount": 120, "totalMemberCount": 350 } ], "generatedAt": "2026-07-02T12:00:00Z" } ``` ### 6.2 조직도 snapshot ```http GET /api/v1/tdc114plus/organization/orgchart Authorization: Bearer {token} ``` Query: | 이름 | 필수 | 설명 | | --- | --- | --- | | `tenantId` | N | 특정 가족사/조직 하위만 조회 | | `refresh` | N | 서버 캐시 refresh 요청. 기본 `false` | 응답: ```json { "tenants": [ { "id": "tenant-uuid", "name": "기술연구소", "slug": "rnd", "type": "ORGANIZATION", "parentId": "company-tenant-uuid", "memberCount": 12, "totalMemberCount": 38 } ], "employees": [ { "id": "user-uuid", "name": "홍길동", "phoneNumber": "+821012345678", "phoneDisplay": "010-1234-5678", "tenantId": "tenant-uuid", "tenantName": "기술연구소", "tenantSlug": "rnd", "department": "기술연구소", "grade": "책임", "position": "팀장", "jobTitle": "개발", "status": "active" } ], "generatedAt": "2026-07-02T12:00:00Z", "cache": { "source": "redis", "hit": true, "ttlSeconds": 300 } } ``` 구현 참고: - 기존 `GET /api/v1/admin/orgchart/snapshot`은 `tenants`, `users`, `generatedAt`, `cache` 구조를 가진다. - `tdc114plus` 응답은 앱 의미에 맞춰 `users` 대신 `employees`를 사용한다. - 기존 admin/orgFront API를 직접 변경하지 않고 별도 DTO에서 변환한다. ## 7. 즐겨찾기 API 1차 구현은 로컬 저장을 기본으로 한다. 서버 동기화는 후속 단계에서 검토한다. 후속 후보: ```http GET /api/v1/tdc114plus/favorites PUT /api/v1/tdc114plus/favorites ``` ## 8. 오류 응답 공통 형식 Baron SSO API 설계 정책에 맞춰 신규 API는 `code`를 기본 포함한다. ```json { "error": "사람이 읽을 수 있는 메시지", "code": "machine_readable_code", "details": {} } ``` 공통 오류: | HTTP | code | 설명 | | --- | --- | --- | | 400 | `invalid_request` | 요청 형식 오류 | | 401 | `unauthorized` | 토큰 없음/만료 | | 403 | `forbidden` | tdc114plus 사용 권한 없음 | | 404 | `not_found` | 직원/조직 없음 | | 429 | `rate_limited` | 과도한 요청 | | 503 | `dependency_unavailable` | Kratos, Redis, DB 등 의존성 장애 | ## 9. 개인정보/보안 정책 - 전화번호 원문은 로그에 남기지 않고 마스킹 또는 정규화 값 일부만 기록한다. - 직원 검색/상세 조회는 감사 로그 대상으로 둔다. - 대량 조회, 짧은 시간 내 반복 조회는 이상 조회 탐지 후보로 기록한다. - 권한 없는 사용자의 민감정보 마스킹은 후속 정책에서 확정한다. - 1차 앱에서는 `call`, `sms` 액션을 제공하되, 앱 내부에서 수신전화식별/수신팝업 기능은 구현하지 않는다. ## 10. Baron SSO 구현 후보 신규 패키지/파일 후보: ```text backend/internal/domain/tdc114plus_models.go backend/internal/handler/tdc114plus_handler.go backend/internal/service/tdc114plus_service.go backend/internal/service/tdc114plus_service_test.go backend/internal/handler/tdc114plus_handler_test.go ``` 라우트 후보: ```go tdc114plus := api.Group("/tdc114plus") tdc114plus.Post("/auth/phone-login", tdc114plusHandler.PhoneLogin) tdc114plus.Get("/me", requireAnyUser, tdc114plusHandler.GetMe) tdc114plus.Get("/directory/employees", requireAnyUser, tdc114plusHandler.ListEmployees) tdc114plus.Get("/directory/employees/:id", requireAnyUser, tdc114plusHandler.GetEmployee) tdc114plus.Get("/organization/tenants", requireAnyUser, tdc114plusHandler.ListTenants) tdc114plus.Get("/organization/orgchart", requireAnyUser, tdc114plusHandler.GetOrgChart) ``` ## 11. 구현 전 확인사항 문서 기준으로 우선 진행 가능한 항목: - 전용 namespace `/api/v1/tdc114plus` - 전용 DTO - 기존 admin/user/orgchart API는 변경하지 않음 - 직원/조직 데이터는 기존 orgchart snapshot 및 identity mirror/read model 기반으로 변환 추가 확인이 필요한 항목: | 항목 | 확인 필요 이유 | 기본 권장안 | | --- | --- | --- | | 전화번호 로그인 보안 수준 | 전화번호 단독 로그인은 표준 인증이 아님 | 1차는 등록자 확인 + rate limit + audit, 후속 기기 등록/OTP 검토 | | token 종류 | 기존 session JWT를 그대로 앱에 줄지, app token을 별도로 둘지 결정 필요 | 초기에는 기존 session token 재사용, 후속 app token 검토 | | 개인정보 마스킹 범위 | 직급/전화번호/이메일 노출 정책 필요 | 1차는 등록 사용자 전체 공개, 후속 권한별 마스킹 | | 서버 즐겨찾기 동기화 | 1차 요구사항은 즐겨찾기이나 서버 동기화 여부 미정 | 1차 로컬 저장 |