Files
Q-A_test/docs/architecture_secretary_sso_components_v2.md
T
SDI 12e4f17b62
CI / typecheck (push) Successful in 1m8s
CI / format (push) Failing after 1m6s
CI / lint (push) Failing after 49s
CI / test (push) Failing after 1m8s
first commit
2026-07-15 18:05:12 +09:00

699 lines
31 KiB
Markdown

# 사내 지원 플랫폼 통합 아키텍처 설계서
## 1. 문서 목적
본 문서는 BARON-SSO 기반 공용 플랫폼으로 다음 두 서비스를 하나의 구조로 통합하는 방안을 설명함.
- 각 S/W 프로그램에서 진입하는 S/W별 Q&A 플랫폼
- 회사 인트라넷에서 진입하는 사내 지원 플랫폼
이번 설계의 핵심은 기존 ABC User Feedback 솔루션을 그대로 폐기하지 않고, 원본 게시글 저장소와 채널 관리 도구로 활용하면서 우리 시스템이 권한 제어, 승인 워크플로우, 동적 폼, 운영 대시보드를 담당하는 구조로 고도화하는 데 있음.
## 2. 한눈에 보는 통합 방향
### 2.1 통합 대상 서비스
| 구분 | 설명 |
| --- | --- |
| S/W Q&A | 각 S/W 프로그램에서 로그인 후 해당 앱 전용 Q&A 채널로 연결되는 지원 서비스 |
| 사내 지원 | 인트라넷에서 진입하여 물품신청, 도서 신청, 출장 차량 신청, 비품 대여, 사내 Q&A를 처리하는 지원 서비스 |
### 2.2 핵심 설계 판단
- 사용자 인증과 `tenant_id` 식별의 원본은 BARON-SSO가 담당함.
- ABC User Feedback는 게시글 원본 저장소이자 관리자 기반 채널/필드 관리 도구로 사용함.
- 우리 시스템은 FastAPI와 자체 DB를 통해 권한 분기, 상태 제어, 승인 이력, 운영 화면을 담당함.
- 외부 진입 경로의 `app_id`, `service_type_id`는 내부 `workspace`로 매핑함.
- 일반 사용자는 Next.js 동적 폼과 조회 화면을 사용하고, 담당자는 운영 페이지에서 승인/반려/후속 조치를 처리함.
- Q&A와 신청은 공통 티켓 모델로 다루되, 실제 원본 본문은 ABC에 저장하고 우리 DB에는 제어용 메타데이터와 매핑 정보를 저장함.
### 2.3 역할 분리 요약
| 구성요소 | 주 역할 | 저장 데이터 |
| --- | --- | --- |
| BARON-SSO | 사용자 인증, `user_id`, `tenant_id` 발급 | 사용자/조직 원본 정보 |
| ABC User Feedback | 게시글 저장, 댓글/첨부 관리, 채널/필드 구성, 기본 목록/상세 UI | 피드백 원본 데이터, 채널 설정, 필드 값 |
| 우리 시스템 | 권한 필터링, 승인 워크플로우, 상태 제어, 운영 페이지, 동적 폼, 외부 연동 오케스트레이션 | 권한, 워크스페이스, 매핑, 승인, 자산, 차량, 알림, 운영 이력 |
## 3. High-Level Architecture
### 3.1 아키텍처 관점
| 관점 | 설명 |
| --- | --- |
| 사용자 접점 | 사용자가 어디서 진입하는지 |
| 인증 계층 | BARON-SSO가 어디에서 인증을 담당하는지 |
| 데이터 저장 계층 | ABC와 자체 DB가 어떤 데이터를 나눠 저장하는지 |
| 제어 계층 | 권한, 승인, 운영 로직이 어디에서 처리되는지 |
| 외부 연동 | 알림, 자산, 조직도, 기타 사내 API가 어디에서 연결되는지 |
### 3.2 High-Level Architecture 다이어그램
아래 다이어그램은 처리 행위를 화살표 라벨로 표시하고, 결과가 쌓이거나 보여지는 지점은 사각형 박스로 구분한 구조임.
```mermaid
flowchart LR
classDef result fill:#eef6ff,stroke:#1d4ed8,stroke-width:1.5px,color:#0f172a;
classDef control fill:#f7f7f7,stroke:#334155,stroke-width:1.2px,color:#111827;
classDef external fill:#fff7ed,stroke:#c2410c,stroke-width:1.2px,color:#111827;
subgraph U[사용자 영역]
U1[인트라넷 사용자]
U2[S/W 사용자]
U3[운영 담당자]
end
subgraph E[진입 채널]
E1[인트라넷 포털]
E2[각 S/W 프로그램]
E3[운영 메뉴]
end
SSO[BARON-SSO\nOAuth 2.0 / OIDC]:::external
subgraph UI[화면 계층]
N1[Next.js 사용자 화면\n동적 폼 / 조회 / 상세]:::result
N2[Next.js 운영 페이지\n승인 / 반려 / 후속 조치]:::result
end
subgraph CTL[우리 시스템\nFastAPI + 자체 DB]
F1[권한 분기 엔진\nuser_id + tenant_id + workspace]:::control
F2[업무 프로세스 엔진\n상태 제어 / 승인 워크플로우 / 후속 조치]:::control
F3[브릿지 API\nABC API 연동 / 매핑 기록]:::control
D1[자체 제어 DB\n권한 / 매핑 / 승인 / 자산 / 차량 / 알림]:::result
end
subgraph ABC[ABC User Feedback]
A1[채널 / 필드 관리자 UI]:::result
A2[Feedback API\nPOST /feedbacks 등]:::control
A3[원본 데이터 저장소\n제목 / 본문 / 댓글 / 첨부 / 필드값]:::result
A4[기본 목록 / 상세 UI]:::result
end
subgraph X[외부 연동]
X1[Naver Works API]:::external
X2[SMS Gateway]:::external
X3[자산 / 조직도 / 기타 사내 API]:::external
end
U1 -->|인트라넷 서비스 진입| E1
U2 -->|앱 내부 지원 메뉴 진입| E2
U3 -->|운영 메뉴 진입| E3
E1 -->|사용자 화면 호출| N1
E2 -->|사용자 화면 호출| N1
E3 -->|운영 페이지 호출| N2
N1 -->|SSO 로그인 요청| SSO
N2 -->|운영자 인증 요청| SSO
SSO -->|user_id, tenant_id, 토큰 반환| N1
SSO -->|운영 권한 토큰 반환| N2
N1 -->|폼/목록 조회 요청| F1
N1 -->|신청/문의 등록 요청| F3
N2 -->|승인 대기/처리 요청| F2
F1 -->|workspace 매핑 및 권한 판정| D1
F2 -->|승인 이력 / 상태 저장| D1
F3 -->|매핑 정보 조회 및 기록| D1
F3 -->|채널/필드 조회| A1
F3 -->|원본 게시글 생성/조회| A2
A2 -->|피드백 원본 저장| A3
A2 -->|기본 목록/상세 제공| A4
F2 -->|알림 발송 요청| X1
F2 -->|실패 시 대체 발송| X2
F2 -->|자산/차량 후속 처리| X3
```
### 3.3 전체 처리 흐름 요약
1. 사용자는 인트라넷 포털 또는 S/W 프로그램에서 Next.js 사용자 화면으로 진입함.
2. 사용자 화면은 BARON-SSO를 통해 인증하고 `user_id`, `tenant_id`를 확보함.
3. 우리 시스템의 권한 분기 엔진은 진입 경로를 `workspace`로 매핑하고 조회/등록 가능 범위를 판단함.
4. 사용자가 문의 또는 신청서를 등록하면 브릿지 API가 ABC Feedback API로 원본 데이터를 저장함.
5. 동시에 우리 DB에는 `workspace`, 상태, 요청 유형, ABC 피드백 ID, 기존 시스템 ID, 승인 필요 여부 등 제어 정보를 저장함.
6. 담당자는 운영 페이지에서 승인 대기 건을 조회하고 `request_approvals` 기반으로 승인/반려/후속 조치를 처리함.
7. 승인 결과와 상태 변경은 우리 DB에 기록되고, 필요 시 Naver Works 또는 SMS, 자산/차량 API 연동이 수행됨.
## 4. 서비스 책임 분리
### 4.1 ABC User Feedback의 역할
ABC User Feedback는 기능 저장소이자 데이터 저장소로 사용함. 즉, 사용자에게 보이는 게시글 원본과 채널 구조는 ABC가 책임지고, 우리 시스템은 이를 직접 대체하지 않음.
ABC가 담당하는 범위는 다음과 같음.
- 게시글 원본 데이터 저장
- 제목, 본문, 댓글, 첨부파일 저장
- 채널 생성 및 채널별 필드 구성
- 신청서 항목을 표현하는 커스텀 필드 저장
- 시스템 API를 통한 피드백 생성 및 조회
- 기본 목록/상세 UI 제공
즉, ABC는 "무엇이 기록되었는가"를 보존하는 원본 시스템임.
### 4.2 우리 시스템의 역할
우리 시스템은 FastAPI, Next.js, 자체 DB를 사용하여 ABC 위에 제어 계층을 추가함.
우리 시스템이 담당하는 범위는 다음과 같음.
- `tenant_id``workspace` 기준 권한 분기
- 사용자별 조회 가능 데이터 필터링
- 신청서 상태 제어: 대기, 승인, 반려, 처리 완료
- `request_approvals` 기반 승인 워크플로우 처리
- 자산 배정, 차량 배차, 원격지원 등 확장 프로세스 처리
- 운영 대시보드 제공
- Next.js 동적 폼 렌더링 및 유효성 검사
- 기존 데이터 마이그레이션 시 ABC ID와 기존 글 ID 매핑 관리
즉, 우리 시스템은 "누가 무엇을 볼 수 있고, 어떤 절차로 처리되는가"를 책임지는 지능형 제어 엔진임.
### 4.3 화면 구성 원칙
| 화면 | 주 시스템 | 설명 |
| --- | --- | --- |
| 일반 사용자 입력 화면 | 우리 시스템 | 진입 경로별 동적 폼, 유효성 검사, 등록 프로세스 제어 |
| 일반 사용자 목록/상세 | 혼합 | 우리 시스템의 권한 필터링 결과와 ABC 원본 데이터를 조합하여 노출 |
| 운영 페이지 | 우리 시스템 | 승인/반려/후속 조치 전용 대시보드 |
| 채널/필드 관리자 화면 | ABC User Feedback | 채널 생성, 필드 구성, 스키마 관리 |
## 5. DB 설계 방향
### 5.1 설계 원칙
- 사용자 마스터와 조직 정보는 BARON-SSO에서 관리함.
- 게시글 원본, 댓글, 첨부, 채널 필드 값은 ABC에 저장함.
- 우리 DB는 제어용 메타데이터와 운영용 확장 데이터만 저장함.
- `support_tickets`는 내부 티켓 식별자이자 ABC 피드백과 연결되는 제어 엔트리 역할을 수행함.
- 마이그레이션과 운영 연계에 필요한 식별자 매핑은 별도 매핑 테이블로 관리함.
- 승인, 자산, 차량, 원격지원은 ABC 원본과 느슨하게 연결된 확장 테이블로 관리함.
### 5.2 데이터 저장 책임 분리
| 데이터 범주 | 저장 위치 | 설명 |
| --- | --- | --- |
| 사용자 인증 정보 | BARON-SSO | `user_id`, `tenant_id`, 토큰, 조직 기준 원본 |
| 게시글 원본 | ABC User Feedback | 제목, 본문, 댓글, 첨부, 사용자 입력 필드 값 |
| 권한/워크스페이스 | 우리 DB | `workspace`, 역할, 읽기/쓰기/승인/관리 권한 |
| 제어용 티켓 메타데이터 | 우리 DB | 내부 티켓 ID, 상태, ABC 피드백 ID, 승인 필요 여부 |
| 업무 확장 데이터 | 우리 DB | 승인, 자산, 차량, 원격지원, 알림 로그 |
| 마이그레이션 매핑 | 우리 DB | 기존 시스템 ID와 ABC 피드백 ID, 내부 티켓 ID 연결 |
### 5.3 핵심 관계 구조
```mermaid
flowchart TD
classDef result fill:#eef6ff,stroke:#1d4ed8,stroke-width:1.2px,color:#0f172a;
classDef control fill:#f7f7f7,stroke:#334155,stroke-width:1.2px,color:#111827;
W[workspaces]:::result -->|1:N 서비스 범위 정의| UWA[user_workspace_access]:::result
W -->|1:N 내부 티켓 소속| ST[support_tickets]:::result
ST -->|1:1 또는 1:N ABC 원본 연결| AFM[abc_feedback_mappings]:::result
AFM -->|ABC feedback_id 참조 기록| ABCREF[ABC feedback]:::control
ST -->|1:N 승인 이력| RA[request_approvals]:::result
ST -->|1:N 자산 배정| AA[asset_allocations]:::result
ST -->|1:N 차량 일정| VS[vehicle_schedules]:::result
ST -->|1:N 원격지원 이력| RS[remote_support]:::result
ST -->|1:N 알림 이력| NL[notification_logs]:::result
ST -->|1:N 마이그레이션 추적| MM[migration_mappings]:::result
```
## 6. DB 스키마 상세화
### 6.1 자체 DB 데이터 영역
| 데이터 영역 | 주요 테이블 | 설명 |
| --- | --- | --- |
| 서비스 마스터 | `software_apps`, `service_types`, `workspaces` | 진입 채널을 내부 `workspace`로 매핑 |
| 권한 관리 | `user_workspace_access` | 읽기, 쓰기, 운영, 승인 권한 제어 |
| 폼/채널 연동 | `workspace_channel_mappings`, `workspace_field_mappings` | `workspace`와 ABC 채널/필드 연결 |
| 공통 티켓 메타데이터 | `support_tickets` | 내부 상태, 요청자, 티켓 유형, 우선순위 관리 |
| ABC 연동 매핑 | `abc_feedback_mappings` | 내부 티켓과 ABC `feedback_id` 연결 |
| 마이그레이션 추적 | `migration_mappings`, `migration_batches` | 기존 글 ID, 기존 첨부 ID, ABC ID 적재 이력 관리 |
| 승인/운영 | `request_approvals`, `notification_logs` | 승인 및 알림 처리 이력 |
| 업무 확장 | `assets`, `asset_allocations`, `vehicle_schedules`, `remote_support` | 자산, 차량, 원격지원 업무 관리 |
### 6.2 핵심 상세 테이블 제안
아래 스키마는 ABC와 우리 DB의 역할 분리를 반영한 초안임.
```sql
CREATE TABLE software_apps (
id BIGINT NOT NULL AUTO_INCREMENT,
app_code VARCHAR(50) NOT NULL,
app_name VARCHAR(100) NOT NULL,
description TEXT,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uq_software_apps_app_code (app_code),
UNIQUE KEY uq_software_apps_app_name (app_name)
);
CREATE TABLE service_types (
id BIGINT NOT NULL AUTO_INCREMENT,
service_code VARCHAR(50) NOT NULL,
service_name VARCHAR(100) NOT NULL,
description TEXT,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uq_service_types_service_code (service_code),
UNIQUE KEY uq_service_types_service_name (service_name)
);
CREATE TABLE workspaces (
id BIGINT NOT NULL AUTO_INCREMENT,
workspace_type VARCHAR(20) NOT NULL,
software_app_id BIGINT NULL,
service_type_id BIGINT NULL,
workspace_code VARCHAR(50) NOT NULL,
workspace_name VARCHAR(100) NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uq_workspaces_workspace_code (workspace_code),
CONSTRAINT fk_workspaces_software_app
FOREIGN KEY (software_app_id) REFERENCES software_apps(id),
CONSTRAINT fk_workspaces_service_type
FOREIGN KEY (service_type_id) REFERENCES service_types(id),
CONSTRAINT chk_workspaces_scope
CHECK (
(workspace_type = 'SOFTWARE_APP' AND software_app_id IS NOT NULL AND service_type_id IS NULL)
OR
(workspace_type = 'INTRANET_SERVICE' AND service_type_id IS NOT NULL AND software_app_id IS NULL)
)
);
CREATE TABLE user_workspace_access (
id BIGINT NOT NULL AUTO_INCREMENT,
user_id VARCHAR(100) NOT NULL,
tenant_id VARCHAR(100) NOT NULL,
workspace_id BIGINT NOT NULL,
workspace_role VARCHAR(30) NOT NULL DEFAULT 'USER',
can_read BOOLEAN NOT NULL DEFAULT TRUE,
can_write BOOLEAN NOT NULL DEFAULT FALSE,
can_manage BOOLEAN NOT NULL DEFAULT FALSE,
can_approve BOOLEAN NOT NULL DEFAULT FALSE,
page_scope VARCHAR(50),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uq_user_workspace_access_user_tenant_workspace (user_id, tenant_id, workspace_id),
CONSTRAINT fk_user_workspace_access_workspace
FOREIGN KEY (workspace_id) REFERENCES workspaces(id)
);
CREATE TABLE workspace_channel_mappings (
id BIGINT NOT NULL AUTO_INCREMENT,
workspace_id BIGINT NOT NULL,
abc_channel_id VARCHAR(100) NOT NULL,
abc_channel_key VARCHAR(100),
form_template_version VARCHAR(30),
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uq_workspace_channel_mappings_workspace_channel (workspace_id, abc_channel_id),
CONSTRAINT fk_workspace_channel_mappings_workspace
FOREIGN KEY (workspace_id) REFERENCES workspaces(id)
);
CREATE TABLE workspace_field_mappings (
id BIGINT NOT NULL AUTO_INCREMENT,
workspace_id BIGINT NOT NULL,
abc_channel_id VARCHAR(100) NOT NULL,
abc_field_key VARCHAR(100) NOT NULL,
local_field_code VARCHAR(100) NOT NULL,
field_label VARCHAR(100) NOT NULL,
field_type VARCHAR(30) NOT NULL,
is_required BOOLEAN NOT NULL DEFAULT FALSE,
sort_order INT NOT NULL DEFAULT 0,
validation_rule JSON,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uq_workspace_field_mappings_workspace_channel_field (workspace_id, abc_channel_id, abc_field_key),
CONSTRAINT fk_workspace_field_mappings_workspace
FOREIGN KEY (workspace_id) REFERENCES workspaces(id)
);
CREATE TABLE support_status_codes (
code VARCHAR(20) NOT NULL,
name VARCHAR(50) NOT NULL,
sort_order INT NOT NULL,
PRIMARY KEY (code)
);
CREATE TABLE support_category_codes (
code VARCHAR(20) NOT NULL,
name VARCHAR(50) NOT NULL,
sort_order INT NOT NULL,
PRIMARY KEY (code)
);
CREATE TABLE support_tickets (
id BIGINT NOT NULL AUTO_INCREMENT,
workspace_id BIGINT NOT NULL,
requester_id VARCHAR(100) NOT NULL,
requester_tenant_id VARCHAR(100) NOT NULL,
ticket_type VARCHAR(30) NOT NULL,
source_system VARCHAR(30) NOT NULL DEFAULT 'ABC',
title VARCHAR(255) NOT NULL,
category_code VARCHAR(20),
status_code VARCHAR(20) NOT NULL,
is_secret BOOLEAN NOT NULL DEFAULT FALSE,
requires_approval BOOLEAN NOT NULL DEFAULT FALSE,
current_assignee_id VARCHAR(100),
current_assignee_tenant_id VARCHAR(100),
requested_start_at TIMESTAMP NULL,
requested_end_at TIMESTAMP NULL,
priority VARCHAR(20) NOT NULL DEFAULT 'NORMAL',
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
CONSTRAINT fk_support_tickets_workspace
FOREIGN KEY (workspace_id) REFERENCES workspaces(id),
CONSTRAINT fk_support_tickets_category_code
FOREIGN KEY (category_code) REFERENCES support_category_codes(code),
CONSTRAINT fk_support_tickets_status_code
FOREIGN KEY (status_code) REFERENCES support_status_codes(code)
);
CREATE TABLE abc_feedback_mappings (
id BIGINT NOT NULL AUTO_INCREMENT,
ticket_id BIGINT NOT NULL,
workspace_id BIGINT NOT NULL,
abc_channel_id VARCHAR(100) NOT NULL,
abc_feedback_id VARCHAR(100) NOT NULL,
abc_feedback_url TEXT,
sync_status VARCHAR(20) NOT NULL DEFAULT 'SYNCED',
last_synced_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uq_abc_feedback_mappings_ticket_id (ticket_id),
UNIQUE KEY uq_abc_feedback_mappings_feedback_id (abc_feedback_id),
CONSTRAINT fk_abc_feedback_mappings_ticket
FOREIGN KEY (ticket_id) REFERENCES support_tickets(id),
CONSTRAINT fk_abc_feedback_mappings_workspace
FOREIGN KEY (workspace_id) REFERENCES workspaces(id)
);
CREATE TABLE migration_batches (
id BIGINT NOT NULL AUTO_INCREMENT,
batch_name VARCHAR(100) NOT NULL,
source_system VARCHAR(50) NOT NULL,
started_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
completed_at TIMESTAMP NULL,
status VARCHAR(20) NOT NULL DEFAULT 'RUNNING',
executed_by VARCHAR(100),
notes TEXT,
PRIMARY KEY (id)
);
CREATE TABLE migration_mappings (
id BIGINT NOT NULL AUTO_INCREMENT,
batch_id BIGINT NOT NULL,
source_system VARCHAR(50) NOT NULL,
source_entity_type VARCHAR(30) NOT NULL,
source_entity_id VARCHAR(100) NOT NULL,
source_parent_id VARCHAR(100),
workspace_id BIGINT NOT NULL,
ticket_id BIGINT NULL,
abc_feedback_id VARCHAR(100),
migration_status VARCHAR(20) NOT NULL,
error_message TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uq_migration_mappings_source_entity (source_system, source_entity_type, source_entity_id),
CONSTRAINT fk_migration_mappings_batch
FOREIGN KEY (batch_id) REFERENCES migration_batches(id),
CONSTRAINT fk_migration_mappings_workspace
FOREIGN KEY (workspace_id) REFERENCES workspaces(id),
CONSTRAINT fk_migration_mappings_ticket
FOREIGN KEY (ticket_id) REFERENCES support_tickets(id)
);
CREATE TABLE request_approvals (
id BIGINT NOT NULL AUTO_INCREMENT,
ticket_id BIGINT NOT NULL,
approver_id VARCHAR(100) NOT NULL,
approver_tenant_id VARCHAR(100) NOT NULL,
approval_status VARCHAR(20) NOT NULL,
comment TEXT,
approved_at TIMESTAMP NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
CONSTRAINT fk_request_approvals_ticket
FOREIGN KEY (ticket_id) REFERENCES support_tickets(id)
);
CREATE TABLE assets (
id BIGINT NOT NULL AUTO_INCREMENT,
asset_code VARCHAR(50) NOT NULL,
asset_name VARCHAR(100) NOT NULL,
asset_type VARCHAR(30) NOT NULL,
quantity INT NOT NULL DEFAULT 1,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
location VARCHAR(100),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uq_assets_asset_code (asset_code)
);
CREATE TABLE asset_allocations (
id BIGINT NOT NULL AUTO_INCREMENT,
ticket_id BIGINT NOT NULL,
asset_id BIGINT NOT NULL,
assignee_id VARCHAR(100),
assignee_tenant_id VARCHAR(100),
allocation_status VARCHAR(20) NOT NULL,
loaned_at TIMESTAMP NULL,
due_at TIMESTAMP NULL,
returned_at TIMESTAMP NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
CONSTRAINT fk_asset_allocations_ticket
FOREIGN KEY (ticket_id) REFERENCES support_tickets(id),
CONSTRAINT fk_asset_allocations_asset
FOREIGN KEY (asset_id) REFERENCES assets(id)
);
CREATE TABLE vehicle_schedules (
id BIGINT NOT NULL AUTO_INCREMENT,
ticket_id BIGINT NOT NULL,
asset_id BIGINT NOT NULL,
departure_at TIMESTAMP NOT NULL,
arrival_at TIMESTAMP NULL,
destination VARCHAR(255),
driver_name VARCHAR(100),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
CONSTRAINT fk_vehicle_schedules_ticket
FOREIGN KEY (ticket_id) REFERENCES support_tickets(id),
CONSTRAINT fk_vehicle_schedules_asset
FOREIGN KEY (asset_id) REFERENCES assets(id)
);
CREATE TABLE remote_support (
id BIGINT NOT NULL AUTO_INCREMENT,
ticket_id BIGINT NOT NULL,
status_code VARCHAR(20) NOT NULL,
support_engineer_id VARCHAR(100),
support_engineer_tenant_id VARCHAR(100),
scheduled_time TIMESTAMP NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
CONSTRAINT fk_remote_support_ticket
FOREIGN KEY (ticket_id) REFERENCES support_tickets(id)
);
CREATE TABLE notification_logs (
id BIGINT NOT NULL AUTO_INCREMENT,
ticket_id BIGINT NULL,
recipient_id VARCHAR(100) NOT NULL,
recipient_tenant_id VARCHAR(100) NOT NULL,
channel VARCHAR(20) NOT NULL,
target_address VARCHAR(100),
delivery_status VARCHAR(20) NOT NULL,
fallback_channel VARCHAR(20),
error_message TEXT,
sent_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
CONSTRAINT fk_notification_logs_ticket
FOREIGN KEY (ticket_id) REFERENCES support_tickets(id)
);
```
### 6.3 테이블 간 연동 해석
- `workspaces`는 외부 진입 채널과 내부 처리 단위를 연결하는 기준 테이블임.
- `workspace_channel_mappings`는 하나의 `workspace`가 어떤 ABC 채널을 사용하는지 정의함.
- `workspace_field_mappings`는 Next.js 동적 폼과 ABC 커스텀 필드를 동일한 규칙으로 묶어줌.
- `support_tickets`는 우리 시스템이 상태와 권한을 제어하기 위한 내부 티켓 헤더임.
- `abc_feedback_mappings`는 내부 티켓과 ABC 원본 피드백을 1:1로 연결하는 핵심 브릿지 테이블임.
- `migration_mappings`는 기존 시스템 글, 댓글, 첨부가 어느 내부 티켓과 ABC 피드백으로 적재되었는지 추적함.
## 7. API 엔드포인트 설계
### 7.1 API 설계 원칙
- 외부 클라이언트는 직접 ABC API를 호출하지 않고 우리 FastAPI를 경유함.
- FastAPI는 SSO 토큰을 검증하고 `tenant_id`, `workspace`, 권한 범위를 해석함.
- 등록 시에는 ABC API와 자체 DB 쓰기가 하나의 업무 트랜잭션처럼 동작해야 함.
- 조회 시에는 우리 DB에서 권한 필터링을 먼저 수행하고, 필요 시 ABC 원본 데이터를 조합하여 응답함.
### 7.2 브릿지 API 목록
| 메서드 | 경로 | 목적 |
| --- | --- | --- |
| `POST` | `/api/workspaces/{workspaceCode}/tickets` | 사용자 문의/신청 등록, ABC 피드백 생성, 내부 티켓 및 매핑 저장 |
| `GET` | `/api/workspaces/{workspaceCode}/tickets` | 권한 필터링된 목록 조회 |
| `GET` | `/api/workspaces/{workspaceCode}/tickets/{ticketId}` | 내부 상태 + ABC 원본 상세 조합 조회 |
| `POST` | `/api/workspaces/{workspaceCode}/tickets/{ticketId}/comments` | 댓글 또는 처리 메모 등록 |
| `POST` | `/api/workspaces/{workspaceCode}/tickets/{ticketId}/attachments` | 첨부 업로드 브릿지 처리 |
| `GET` | `/api/workspaces/{workspaceCode}/form-template` | 동적 폼 렌더링용 필드 구성 조회 |
| `GET` | `/api/admin/workspaces/{workspaceCode}/pending-approvals` | 운영 페이지 승인 대기 목록 조회 |
| `POST` | `/api/admin/tickets/{ticketId}/approve` | 승인 처리 및 `request_approvals` 기록 |
| `POST` | `/api/admin/tickets/{ticketId}/reject` | 반려 처리 및 반려 사유 기록 |
| `POST` | `/api/admin/tickets/{ticketId}/follow-up/assets` | 자산 배정 후속 처리 |
| `POST` | `/api/admin/tickets/{ticketId}/follow-up/vehicle` | 차량 배차 후속 처리 |
### 7.3 등록 브릿지 API 처리 순서
```mermaid
flowchart LR
classDef result fill:#eef6ff,stroke:#1d4ed8,stroke-width:1.2px,color:#0f172a;
classDef control fill:#f7f7f7,stroke:#334155,stroke-width:1.2px,color:#111827;
C1[클라이언트 요청]:::result -->|SSO 토큰 포함| C2[FastAPI 브릿지]:::control
C2 -->|workspace 및 권한 확인| C3[user_workspace_access 조회]:::result
C2 -->|필드 매핑 로드| C4[workspace_field_mappings 조회]:::result
C2 -->|ABC payload 변환| C5[ABC POST /feedbacks 호출]:::control
C5 -->|feedback_id 반환| C6[ABC 원본 저장 완료]:::result
C2 -->|내부 티켓 생성| C7[support_tickets 저장]:::result
C2 -->|ABC 연결 기록| C8[abc_feedback_mappings 저장]:::result
C2 -->|승인 필요 여부 판정| C9[초기 상태 결정]:::result
```
### 7.4 조회 API 권한 필터링 규칙
| 시나리오 | 필터 기준 |
| --- | --- |
| 일반 사용자 내 글 조회 | `requester_id = current_user_id` and `requester_tenant_id = current_tenant_id` |
| 워크스페이스 담당자 조회 | `user_workspace_access.can_manage = true` |
| 승인 담당자 조회 | `user_workspace_access.can_approve = true` and `page_scope` 일치 |
| 외부 고객 조회 | 본인 글만 조회, 비밀글은 작성자/담당자만 허용 |
### 7.5 예시 응답 모델
```json
{
"ticketId": 1024,
"workspaceCode": "INTRA_BOOK_REQUEST",
"statusCode": "PENDING_APPROVAL",
"approvalRequired": true,
"abc": {
"channelId": "book-request-channel",
"feedbackId": "fb_938421",
"detailUrl": "https://abc.example.com/feedbacks/fb_938421"
},
"requester": {
"userId": "u1001",
"tenantId": "baron"
}
}
```
## 8. 마이그레이션 매핑 로직 설계
### 8.1 마이그레이션 목표
기존 시스템의 게시글, 신청서, 댓글, 첨부를 ABC 원본 구조로 적재하면서, 동시에 우리 시스템의 내부 티켓과 매핑 정보를 남겨 이후 조회, 권한 제어, 운영 처리에 문제없이 연결되도록 해야 함.
### 8.2 배치 처리 단계
1. 기존 시스템에서 대상 데이터와 첨부 메타데이터를 추출함.
2. 기존 글 유형을 `workspace``ticket_type`으로 변환함.
3. 필드 매핑 규칙에 따라 ABC 커스텀 필드 payload를 생성함.
4. ABC API로 게시글 원본을 생성하고 `abc_feedback_id`를 수신함.
5. 우리 DB의 `support_tickets`에 내부 제어용 티켓을 생성함.
6. `abc_feedback_mappings``migration_mappings`에 연결 정보를 저장함.
7. 댓글과 첨부는 원본 글 적재 완료 후 후속 단계로 연결함.
8. 실패 건은 `migration_status = 'FAILED'``error_message`로 남기고 재처리 가능하게 함.
### 8.3 마이그레이션 흐름 다이어그램
```mermaid
flowchart TD
classDef result fill:#eef6ff,stroke:#1d4ed8,stroke-width:1.2px,color:#0f172a;
classDef control fill:#f7f7f7,stroke:#334155,stroke-width:1.2px,color:#111827;
M1[기존 시스템 데이터]:::result -->|배치가 원본 추출| M2[Migration Script]:::control
M2 -->|workspace 규칙 적용| M3[workspace 결정 결과]:::result
M2 -->|ABC payload 생성| M4[ABC POST /feedbacks]:::control
M4 -->|abc_feedback_id 수신| M5[ABC 원본 생성 결과]:::result
M2 -->|내부 티켓 생성| M6[support_tickets]:::result
M2 -->|브릿지 연결 저장| M7[abc_feedback_mappings]:::result
M2 -->|이관 추적 저장| M8[migration_mappings]:::result
M2 -->|실패 사유 기록| M9[재처리 대상 목록]:::result
```
### 8.4 마이그레이션 스크립트 의사코드
```python
def migrate_record(source_record):
workspace = resolve_workspace(source_record.app_id, source_record.service_type)
template = load_workspace_template(workspace.id)
abc_payload = build_abc_payload(source_record, template)
abc_feedback = abc_client.create_feedback(
channel_id=template.abc_channel_id,
payload=abc_payload,
)
ticket = create_support_ticket(
workspace_id=workspace.id,
requester_id=source_record.user_id,
requester_tenant_id=source_record.tenant_id,
ticket_type=source_record.ticket_type,
title=source_record.title,
status_code=initial_status(source_record),
)
create_abc_feedback_mapping(
ticket_id=ticket.id,
workspace_id=workspace.id,
abc_channel_id=template.abc_channel_id,
abc_feedback_id=abc_feedback.id,
)
create_migration_mapping(
source_system="legacy_support",
source_entity_type="POST",
source_entity_id=source_record.legacy_id,
workspace_id=workspace.id,
ticket_id=ticket.id,
abc_feedback_id=abc_feedback.id,
migration_status="SYNCED",
)
```
### 8.5 운영상 주의점
- 댓글과 첨부는 원본 글 생성 성공 이후 단계적으로 적재해야 함.
- 기존 시스템 ID 중복을 막기 위해 `migration_mappings`에 유니크 제약이 필요함.
- 재실행 가능성을 고려해 배치는 멱등적으로 설계해야 함.
- ABC 적재 성공 후 내부 DB 저장 실패가 발생하면 보상 처리 또는 재동기화 큐가 필요함.
## 9. 정리
이 설계의 핵심은 S/W별 Q&A와 인트라넷 사내 지원을 하나의 `workspace` 기반 플랫폼으로 통합하되, 시스템 역할을 명확히 나누는 데 있음.
- ABC User Feedback는 게시글 원본 저장소이자 채널/필드 관리 도구임.
- 우리 시스템은 FastAPI와 자체 DB를 통해 권한 분기, 승인 워크플로우, 운영 페이지, 동적 폼, 외부 연동을 담당함.
- BARON-SSO는 사용자와 `tenant_id`의 원본 인증 계층으로 유지됨.
- `abc_feedback_mappings`, `workspace_channel_mappings`, `migration_mappings`가 두 시스템을 연결하는 핵심 브릿지 역할을 수행함.
다음 단계에서는 상태 코드 표준값, `page_scope` 체계, 실제 ABC API 스펙, 운영 페이지 화면 와이어프레임을 추가하면 구현 준비 수준의 설계 문서로 확장 가능함.