tags: [workspace, team, project, database, slack, auth, gateway, rag, migration, plans]

# workspace-team-project 계층 재정의 전환계획

상위 원칙:
- [0_VALUE Writing Principles](../../../../0_VALUE/02_Governance/writing-principles.md)
- [Workspace-Team-Project 도메인 모델](../../book/300_architecture/390_workspace_team_project_%EB%8F%84%EB%A9%94%EC%9D%B8_%EB%AA%A8%EB%8D%B8.md)
- [database/tables.md](../../book/300_architecture/database/tables.md)

관련 문서:
- [workspace-team-project 계층 재정의 아이디어](../ideas/260316_workspace_team_project_%EA%B3%84%EC%B8%B5_%EC%9E%AC%EC%A0%95%EC%9D%98_%EC%95%84%EC%9D%B4%EB%94%94%EC%96%B4.md)
- [workspace-team-project 계층 재정의 영향범위 리서치](../research/260316_workspace_team_project_%EA%B3%84%EC%B8%B5_%EC%9E%AC%EC%A0%95%EC%9D%98_%EC%98%81%ED%96%A5%EB%B2%94%EC%9C%84_%EB%A6%AC%EC%84%9C%EC%B9%98.md)
- [통합 인증 시스템 아키텍처](../../book/300_architecture/380_authentication_system.md)
- [테이블 관계도](../../book/300_architecture/database/relationships.md)

## 목적
- `team`이 상위 조직과 실행 조직 의미를 동시에 갖는 현재 구조를 종료합니다.
- 운영 중인 `auth-server`, `robeing-gateway`, `rb8001`, `skill-rag-file`, `skill-slack`, `admin-dashboard`를 한 번에 깨지 않도록 병행 지원 순서로 전환합니다.
- 최종적으로 `workspace > team`, `team owns project`를 운영 DB와 코드 SSOT에 맞춰 고정합니다.

## 범위
- 포함:
  - 새 조직 모델 정의와 목표 ERD 고정
  - 운영 DB의 병행 지원용 컬럼/테이블 추가
  - Slack/Auth/RAG 경로의 단계별 전환
  - Company-X와 Robeing의 초기 매핑 규칙 확정
  - 검증 체크리스트와 롤백 기준 고정
- 제외:
  - 모든 고객사의 조직 구조 일괄 정리
  - 실제 개별 프로젝트 팀 구조의 최종 업무 운영 정책 확정
  - 오래된 문서 전체 아카이브 정리

## 목표 모델

### 1. 도메인 해석
- `workspace`: Company-X, Goosefarm 같은 상위 조직 단위
- `team`: workspace 소속 실행 조직
- `project`: team이 담당하는 대상/업무 단위

### 2. 목표 관계
- `workspace has many teams`
- `team belongs to one workspace`
- `team owns many projects`
- `slack_workspace belongs to one workspace`
- `slack_channel`은 `workspace` 소속을 기본으로 하고, 필요 시 `project`를 선택적으로 참조

### 3. 초기 운영 매핑
- `Company-X`는 `workspace`로 승격
- 현재 `Company-X Team`은 임시로 `Company-X 실행팀` 성격의 `team`으로 재정의
- `xvaluelab`은 `project`로 등록
- `Robeing`도 같은 기준으로 `workspace`와 하위 `team`을 분리할 수 있게 설계

## 목표 ERD 초안

### 1. 신규/재정의 엔터티
- `workspace`
  - `id`
  - `name`
  - `slug`
  - `is_active`
  - `created_at`
  - `updated_at`
- `team`
  - `id`
  - `workspace_id`
  - `name`
  - `slug`
  - `role_type`
  - `is_active`
  - `created_at`
  - `updated_at`
- `project`
  - `id`
  - `team_id`
  - `name`
  - `slug`
  - `status`
  - `created_at`
  - `updated_at`

### 2. 기존 테이블의 목표 참조
- `user.team_id`는 유지하되 전환 완료 전까지 `team`만 참조
- `workspace_member`는 `workspace_id`를 추가해 이름과 구조를 일치시킴
- `slack_workspace.team_id`는 최종적으로 `workspace_id`로 전환
- `slack_channel`은 `workspace_id`와 `project_id`를 병행 지원
- `slack_canvas`는 기존 `slack_channel_id`를 유지
- `team_document`, `startup_valuation`은 우선 기존 `team_id` 유지 후 필요 시 후속 분리
- `coldmail_list_config`는 `slack_workspace_id` 유지

## 전환 원칙

### 1. 레거시 `team_id` 경로를 바로 제거하지 않습니다.
- 새 축을 먼저 추가합니다.
- 읽기는 `신규 필드 우선 + 레거시 fallback`으로 갑니다.
- 쓰기는 이행 기간 동안 dual-write를 허용합니다.

### 2. Slack/Auth 경로를 최우선 보호합니다.
- Slack OIDC 로그인
- Slack passport install/callback/status/token
- gateway Slack token lookup / user uuid mapping
- rb8001 Slack event/file/message 처리
- skill-slack Lists API

### 3. Company-X RAG 경로를 두 번째 보호 축으로 둡니다.
- `team_document`
- `skill-rag-file` 업로드/검색
- `companyx_grounding_service`
- IR 채널/Canvas 처리

### 4. 문서 SSOT를 코드 전환과 같은 묶음으로 처리합니다.
- 구조를 바꾸고 문서를 나중에 맞추는 방식은 금지합니다.
- 최소 `tables.md`, `relationships.md`, `380_authentication_system.md`는 같은 단계에서 갱신합니다.

## 작업 단계

### Phase 0. 사전 고정
- `workspace`, `team`, `project` 정의를 본 문서 기준으로 동결
- Company-X, Robeing 초기 매핑을 표로 확정
- `workspace_member`를 계속 쓸지 대체할지 결정하지 말고, 우선 `workspace_id` 추가를 기준안으로 둠

### Phase 1. DB 병행 지원 준비
- `workspace` 테이블 신설
- `project` 테이블 신설
- `team.workspace_id` 추가
- `workspace_member.workspace_id` 추가
- `slack_workspace.workspace_id` 추가
- `slack_channel.workspace_id`, `slack_channel.project_id` 추가
- 필요 시 `project_name`은 유지하되 FK 전환 완료 후 보조 메타 필드로만 사용

### Phase 2. 기초 데이터 이관
- 기존 `company` 레코드를 기준으로 `workspace` 생성
- `team.company_id`를 해석해 각 `team.workspace_id` 채움
- Company-X용 `project(xvaluelab)` 생성
- `slack_workspace.workspace_id`를 채우고 기존 `team_id`는 유지
- `slack_channel.workspace_id`를 채우고, `xvaluelab` 채널에 `project_id` 연결
- `workspace_member.workspace_id`를 사용자 소속 기준으로 채움

### Phase 3. 읽기 경로 병행 지원
- `auth-server`
  - `Workspace = Team` alias 제거 준비
  - Slack OAuth/passport 경로를 `workspace_id` 중심으로 병행 조회
  - `workspace_member` 조회를 실제 `workspace_id` 구조로 정합화
- `robeing-gateway`
  - `get_user_workspace`, `assign_user_to_workspace`, `slack_user_to_uuid` 병행 지원
  - Slack `team_id(T...) -> slack_workspace -> workspace/team` 매핑 명확화
- `rb8001`
  - Slack repository에서 `slack_team_id -> workspace/team` 해석 병행 지원
  - Company-X 전용 상수는 `workspace`/`project` 기반 식별자로 치환 준비
- `skill-slack`
  - `X-Team-Id` 계약을 유지하되 내부에서 workspace-aware lookup 지원
- `admin-dashboard`
  - `workspace + team` 통계 병행 노출 준비

### Phase 4. 쓰기 경로 dual-write
- 새 사용자/조직 연결 시 `team_id`와 `workspace_id`를 함께 기록
- Slack 설치/재설치 시 `slack_workspace.workspace_id`와 레거시 `team_id`를 함께 채움
- 새 채널/Canvas/프로젝트 연결 시 `project_id` 우선 기록
- 운영 로그에 `workspace_id`, `team_id`, `project_id`를 함께 남겨 비교 가능하게 함

### Phase 5. 서비스별 전환
- `auth-server`에서 `workspace`를 실제 `workspace` 엔터티로 전환
- `robeing-gateway`에서 사용자 컨텍스트 기본 축을 `workspace + team`으로 변경
- `rb8001`에서 Company-X 전용 로직을 `workspace/project` 기반으로 재작성
- `skill-rag-file`는 현 단계에서는 `team_document.team_id`를 유지하되, API 계약과 내부 검색 필터에서 `workspace/project` 메타를 병행 지원
- `admin-dashboard`는 기존 팀 통계와 새 워크스페이스 통계를 함께 검증

### Phase 6. 정리 단계
- 더 이상 사용하지 않는 `Workspace = Team` alias 제거
- `workspace_member` 레거시 해석 제거
- `slack_workspace.team_id` 사용처 제거 또는 보조 참조로 축소
- `project_name` 문자열 의존 경로 제거
- 필요 시 `company`의 역할을 archive/legacy 참조로 축소

## 데이터 이관 규칙

### 1. Company-X
- `company.name='Company-X'` 기준 `workspace` 1건 생성
- 현재 `team.id=79441171-3951-4870-beb8-916d07fe8be5`는 같은 workspace 하위 team으로 유지
- `project.name='xvaluelab'` 생성
- `slack_workspace.slack_team_id='T09C98KB933'`는 새 Company-X workspace에 연결
- `slack_channel.channel_id='C0ALCV5S0FL'`는 Company-X workspace + `xvaluelab` project에 연결

### 2. Robeing
- `company.name='Robeing'` 기준 `workspace` 1건 생성
- 기존 `Robeing` team은 해당 workspace 하위 team으로 유지
- 하위 project는 후속 운영 구조 확정 후 추가

### 3. 레거시 보존
- 이행 기간 동안 기존 UUID는 가능한 한 재사용합니다.
- 신규 엔터티 추가 때문에 기존 `team.id`를 바꾸지 않습니다.
- old/new 매핑 테이블 또는 migration note를 남겨 추적성을 확보합니다.

## 서비스별 검증 체크리스트

### 1. auth-server
- Slack OIDC 로그인 성공
- Slack passport install/callback/status/token 성공
- Slack uninstall 경로에서 잘못된 `workspace_id` 참조 제거 확인
- `workspace_member` 조회가 실제 `workspace_id`를 사용해도 정상 동작

### 2. robeing-gateway
- Slack event 요청에서 bot token 조회 성공
- Slack user -> internal UUID 변환 성공
- 사용자 소속 조회에서 `workspace`, `team`이 모두 기대값으로 반환

### 3. rb8001
- Slack message/file/event 처리 성공
- Company-X grounding 성공
- IR 채널 `C0ALCV5S0FL` 읽기/전송 성공
- Canvas 조회 성공

### 4. skill-rag-file
- 업로드 API 성공
- 기존 Company-X 문서 검색 성공
- 재인덱싱 없이도 기존 컬렉션 fallback이 동작

### 5. skill-slack
- Lists 조회/동기화 성공
- 내부 팀 식별자와 workspace 매핑이 모두 로그에 남음

### 6. admin-dashboard
- `total_workspaces`, `total_teams` 통계 비교
- 사용자 상세 응답에 `workspace_id`, `team_id` 병행 노출 확인

## 롤백 기준
- Slack 로그인 또는 Slack 이벤트 처리 실패가 1건이라도 재현되면 즉시 롤백
- Company-X RAG 검색 0건 회귀가 발생하면 즉시 롤백
- Company-X IR 채널 읽기/전송 또는 Canvas 조회 실패 시 즉시 롤백
- dual-write 비교에서 `workspace_id`와 `team_id` 해석이 어긋나는 데이터가 발견되면 해당 단계는 닫지 않음

## 예상 리스크와 대응

### 1. `workspace_member` 정합화 실패
- 대응: Phase 1에서 실제 컬럼 추가 후 읽기 경로를 명시적으로 분리

### 2. Slack payload의 `team_id(T...)`와 내부 UUID 혼선 재발
- 대응: Slack 외부 식별자와 내부 식별자를 함수/컬럼명 단계에서 분리

### 3. Company-X 전용 상수로 인한 부분 회귀
- 대응: 상수 제거 전까지는 `workspace_id`, `team_id`, `project_id`를 모두 로그로 남김

### 4. 기존 RAG 컬렉션명과 새 메타 모델 충돌
- 대응: `team_document`와 컬렉션명은 후속 단계까지 유지하고, 이번 계획에서는 metadata bridge만 추가

## 완료 판정
- 계획 문서 단계의 완료는 구현 완료가 아닙니다.
- 아래 조건이 채워지면 이 계획 문서는 닫을 수 있습니다.
  - 목표 ERD 확정
  - 단계별 DB 변경 순서 확정
  - 서비스별 병행 지원 범위 확정
  - 검증 체크리스트와 롤백 기준 확정

## 다음 단계
- 이 문서를 기준으로 DB 마이그레이션 초안 문서를 별도로 작성
- `auth-server`, `robeing-gateway`, `rb8001`, `skill-rag-file`, `skill-slack`, `admin-dashboard`별 세부 구현 작업을 worklog로 분리
- 전환 시작 전 `tables.md`, `relationships.md`, `380_authentication_system.md` 갱신 순서를 먼저 잡음
