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

# 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 계층 재정의 전환계획](../plans/260316_workspace_team_project_%EA%B3%84%EC%B8%B5_%EC%9E%AC%EC%A0%95%EC%9D%98_%EC%A0%84%ED%99%98%EA%B3%84%ED%9A%8D.md)
- [통합 인증 시스템 아키텍처](../../book/300_architecture/380_authentication_system.md)
- [테이블 관계도](../../book/300_architecture/database/relationships.md)
- [Slack install workspace_id error](../troubleshooting/251015_slack_install_workspace_id_error.md)
- [PostgreSQL 테이블명 단수형 통일](../troubleshooting/250911_happybell80_PostgreSQL_%ED%85%8C%EC%9D%B4%EB%B8%94%EB%AA%85_%EB%8B%A8%EC%88%98%ED%98%95_%ED%86%B5%EC%9D%BC.md)
- [rb8001 skill-rag-file 연동 구조](../troubleshooting/250916_rb8001_skill-rag-file_%EC%97%B0%EB%8F%99_%EA%B5%AC%EC%A1%B0.md)

## 목적
- `workspace > team`, `team owns project` 목표 모델을 현재 운영 구조에 적용하려 할 때 어디에 영향이 가는지 좁힙니다.
- 계획 문서를 쓰기 전에 반드시 확인해야 할 코드/DB/문서 충돌 지점을 한 곳에 모읍니다.
- 변경 도중과 변경 직후에 발생할 회귀 가능성을 미리 드러냅니다.

## Facts

### 1. 현재 운영 DB는 `company + team` 구조이며 `workspace` 테이블은 없습니다.
- 2026-03-16 현재 `main_db` 실테이블 확인 결과 존재하는 조직 테이블은 `company`, `team`, `workspace_member`, `slack_workspace`입니다.
- 같은 시각 데이터 건수는 다음과 같습니다.

| 테이블 | 건수 |
|---|---:|
| `company` | 2 |
| `team` | 2 |
| `user` | 11 |
| `robeing` | 1 |
| `slack_workspace` | 2 |
| `workspace_member` | 9 |
| `team_document` | 145 |
| `startup_valuation` | 13 |
| `slack_channel` | 6 |
| `slack_canvas` | 2 |
| `coldmail_list_config` | 2 |

### 2. `Company-X`는 현재 `company`이면서 동시에 단일 `team`에 거의 모든 실무 데이터가 묶여 있습니다.
- `company.name='Company-X'` 아래에 `team.name='Company-X Team'` 한 건이 존재합니다.
- 그 `team.id`는 `79441171-3951-4870-beb8-916d07fe8be5`입니다.
- 같은 `team_id`에 현재 아래 데이터가 직접 연결돼 있습니다.
  - `user`: 8명
  - `slack_workspace`: 1건 (`slack_team_id=T09C98KB933`)
  - `team_document`: 129건
  - `startup_valuation`: 13건
  - `slack_channel`: 4건
- 같은 `slack_workspace.id=59f4aeda-8ad0-4ab7-a818-5ee8dd14f5d1` 아래에는 `coldmail_list_config` 1건도 연결돼 있습니다.
- 즉 현재 운영 현실에서 `Company-X Team`은 단순 팀이 아니라, 사실상 Company-X 전체 실행 컨텍스트처럼 쓰이고 있습니다.

### 3. `workspace_member`는 이름과 달리 현재 `workspace_id` FK가 없습니다.
- 2026-03-16 현재 `workspace_member` 컬럼은 `id`, `user_id`, `role`, `is_active`, `joined_at`, `updated_at`, `slack_user_id`만 있습니다.
- `workspace_id` 컬럼이 실제 DB에 없습니다.
- 그런데 여러 코드/문서가 `workspace_member.workspace_id` 존재를 전제로 작성돼 있거나, 반대로 `user.team_id`를 사실상 `workspace_id`처럼 대신 쓰고 있습니다.

### 4. 인증 계층은 `Workspace`라는 이름을 쓰지만 실제 구현은 `Team` alias입니다.
- `auth-server/app/models/workspace.py`는 `Company`, `Team`, `SlackWorkspace` 모델을 정의하고 마지막에 `Workspace = Team` alias를 둡니다.
- 같은 파일에서 `SlackWorkspace.team_id -> team.id` FK를 유지합니다.
- `auth-server/app/models/user.py`의 `User.team_id`도 여전히 `team.id` FK입니다.
- 즉 API/함수 이름은 workspace지만 실제 PK/FK는 team을 가리키는 구간이 많습니다.

### 5. Slack OAuth와 Slack passport 경로는 `workspace`라는 이름으로 `user.team_id`를 사용합니다.
- `auth-server/app/providers/slack.py`에서 Slack OIDC 로그인 시 Slack `team_id(T...)`를 `slack_workspace.slack_team_id`로 찾고, 그 결과 `slack_workspace.team_id`를 내부 사용자의 `team_id`에 넣습니다.
- 같은 파일의 `/passport/install` 경로는 주석상 `workspace_id 조회`라고 적지만 실제 구현은 `workspace_id = user.team_id`입니다.
- `/passport/callback`, `/passport/status/{workspace_id}`, `/passport/token/{workspace_id}`도 실제로는 `SlackWorkspace.team_id == workspace_id`를 사용합니다.

### 6. `auth-server`에는 실제 운영 DB와 다른 방향의 미완료 마이그레이션 흔적이 남아 있습니다.
- `auth-server/migrations/add_user_workspace_tables.sql`은 `companies -> workspaces`, `workspace_members`(복수형), `slack_workspaces.workspace_id` 같은 구조를 전제합니다.
- 하지만 현재 `main_db`에는 `company`, `team`, `workspace_member`, `slack_workspace`가 존재하고 `workspaces`, `workspace_members`, `slack_workspaces`는 없습니다.
- 즉 auth 문서/마이그레이션 설계와 실제 운영 DB가 이미 어긋나 있습니다.

### 7. `auth-server` Slack 라우터도 `team`과 `workspace`를 혼용한 상태입니다.
- `auth-server/app/api/slack_router.py`는 Slack 이벤트 payload의 `team_id(T...)`를 읽고도 `SlackWorkspace.team_id == team_id`로 조회하는 경로를 포함합니다.
- `auth-server/app/providers/slack.py`에는 `SlackWorkspace.workspace_id`를 참조하는 제거 경로가 남아 있지만 실제 모델 컬럼은 `team_id`입니다.
- `auth-server/app/models/user.py`의 `WorkspaceMember.__repr__`도 실제로 존재하지 않는 `workspace_id`를 문자열에 포함합니다.
- 즉 인증 서버 안에서도 `workspace`라는 이름과 실제 컬럼 구조가 서로 맞지 않는 코드가 이미 공존합니다.

### 8. `robeing-gateway`는 사용자 라우팅, Slack 토큰 조회, Slack UUID 변환을 모두 `team` 전제로 수행합니다.
- `robeing-gateway/app/database.py`의 `get_user_workspace()`는 `user -> team -> company` 조인으로 사용자 소속을 조회합니다.
- 같은 파일의 `slack_user_to_uuid()`는 `user.team_id -> team.id -> slack_workspace.team_id` 조인으로 Slack 사용자 UUID를 변환합니다.
- `get_slack_bot_token()`은 Slack `team_id(T...)`를 받아 `slack_workspace.slack_team_id`로 토큰을 찾습니다.
- `assign_user_to_workspace()`는 함수 이름과 달리 현재 `workspace_member`에 `user_id`만 기록하며 `workspace_id`를 저장하지 않습니다.

### 9. `rb8001`은 Slack 팀을 내부 `team_id`로 바꾸는 경로와 Company-X 전용 로직 모두를 `team` 중심으로 가정합니다.
- `rb8001/app/state/slack_repository.py`는 `slack_workspace.slack_team_id -> slack_workspace.team_id` 조회로 `team UUID`를 반환합니다.
- `rb8001/app/router/slack_handler.py`, `app/services/slack/message_service.py`, `app/services/slack/file_service.py`는 Slack 요청 처리에서 이 변환을 그대로 사용합니다.
- `rb8001/app/services/companyx_grounding_service.py`는 `COMPANYX_TEAM_ID = 79441171-...` 상수를 기준으로 Company-X 사용자 여부를 판정합니다.
- `app/services/naverworks_file_processor.py`, `app/services/ir_deck_analyzer.py`, `app/services/startup_valuation.py`도 문서/분석/RAG 흐름에서 `team_id`를 핵심 인자로 사용합니다.

### 10. RAG/문서 계층은 `team_document`와 컬렉션명 모두 `team_id`에 고정돼 있습니다.
- `skill-rag-file/app/api/upload.py`는 업로드 시 `X-Team-Id` 헤더를 필수로 받으며, 먼저 `SELECT 1 FROM team WHERE id = :team_id`로 팀 존재를 검증합니다.
- 같은 서비스는 DB 레코드를 `team_document.team_id`로 저장하고, Chroma 컬렉션 이름도 `skill_rag_file_{team_id}_documents`를 사용합니다.
- `team_document` 145건 중 `Company-X Team` 소속이 129건으로 가장 큽니다.
- 따라서 `Company-X Team`의 의미를 바꾸면 저장 경로, 컬렉션명, 업로드 검증, 재인덱싱 스크립트가 모두 영향권입니다.

### 11. `skill-slack`도 내부 식별자를 `team_id`로 받아 Slack workspace로 역변환합니다.
- `skill-slack/app/state/slack_workspace_repository.py`는 `get_slack_team_id_by_team_id(team_id)`로 내부 팀 UUID를 Slack `slack_team_id`로 바꿉니다.
- `skill-slack/app/api/endpoints/lists.py`를 포함한 Slack Lists 경로는 `X-Team-Id`를 받아 이 변환 결과로 Slack API를 호출합니다.
- 따라서 조직 축을 재정의할 때 `rb8001`뿐 아니라 `skill-slack`도 함께 병행 지원 대상에 넣어야 합니다.

### 12. 운영 DB의 FK는 이미 `company -> team -> slack_workspace -> channel/canvas`와 `team -> document/valuation` 축으로 닫혀 있습니다.
- `team.company_id -> company.id`
- `user.team_id`, `robeing.team_id`, `slack_workspace.team_id`, `team_document.team_id`, `startup_valuation.team_id`가 모두 `team.id`를 참조합니다.
- `slack_channel.slack_workspace_id -> slack_workspace.id`, `slack_canvas.slack_channel_id -> slack_channel.id`, `coldmail_list_config.slack_workspace_id -> slack_workspace.id`도 운영 중입니다.
- 즉 이번 문제는 일부 화면만 바꾸는 수준이 아니라 실제 FK 그래프 자체를 재배치하는 작업입니다.

### 13. 관리자 대시보드도 사용자/로빙 통계를 `team_id` 기준으로 집계합니다.
- `admin-dashboard/backend/state/user_repository.py`는 사용자 응답 모델에 `team_id`를 노출하고 `COUNT(DISTINCT team_id)`를 통계로 사용합니다.
- `admin-dashboard/backend/state/robeing_repository.py`도 `team_id`를 그대로 응답하고 `teams_with_robeings`를 집계합니다.
- 대응 Pydantic 스키마 역시 `team_id: UUID`를 필수 필드로 고정합니다.

### 14. Slack 채널/Canvas 계층은 이미 `project_name`을 임시 보강했지만 FK는 여전히 `team` 계층 위에 걸려 있습니다.
- `slack_channel`은 `slack_workspace_id -> slack_workspace.id` FK를 가집니다.
- `slack_canvas`는 `slack_channel_id -> slack_channel.id` FK를 가집니다.
- 현재 `C0ALCV5S0FL` 채널에는 `project_name='xvaluelab'`이 들어가 있고, Canvas 2건도 같은 채널에 연결돼 있습니다.
- 하지만 이 `project_name`은 FK가 아니라 보강 필드라서 현재 조직 구조를 교정하지는 못합니다.

### 15. 관련 문서 계층도 이미 `workspace`와 `team`을 혼용하고 있습니다.
- [380_authentication_system.md](../../book/300_architecture/380_authentication_system.md)는 `company vs workspaces 테이블 공존`을 미해결로 적고 있습니다.
- [database/relationships.md](../../book/300_architecture/database/relationships.md)는 `workspaces`, `workspace_member.workspace_id` 같은 구조를 설명하지만, 현재 실DB와 다릅니다.
- [251015_slack_install_workspace_id_error.md](../troubleshooting/251015_slack_install_workspace_id_error.md)와 [251016_troubleshooting_summary.md](../troubleshooting/251016_troubleshooting_summary.md)는 실제 `workspace_member.workspace_id`가 없어서 `user.team_id`를 대신 사용했다고 기록합니다.
- 즉 이번 문제는 새로 생긴 문제가 아니라, 문서와 구현이 서로 다른 방향으로 누적된 상태입니다.

## Interpretation

### 1. 이번 문제는 단순 테이블 추가가 아니라 도메인 기준 복구 문제입니다.
- 현재 구조는 `company`, `team`, `workspace`, `slack_workspace`가 역할별로 분리된 것이 아니라, 이름과 실제 의미가 서로 다른 채 중첩돼 있습니다.
- 그래서 `workspace`를 새로 만든다고 끝나지 않고, 현재 `team`이 맡고 있는 상위 조직 의미를 어디까지 분리할지부터 고정해야 합니다.

### 2. 가장 먼저 깨질 축은 Slack 인증/Slack 라우팅입니다.
- Slack OIDC, Slack passport, gateway Slack proxy, rb8001 Slack event/file/message 처리 모두 `Slack team ID -> internal team UUID` 변환에 기대고 있습니다.
- 이 경로를 먼저 병행 지원 없이 바꾸면 로그인, 이벤트 수신, 사용자 UUID 변환, 봇 토큰 조회가 함께 깨질 가능성이 큽니다.

### 3. 두 번째로 큰 영향은 Company-X RAG와 문서 저장 축입니다.
- 현재 Company-X 내부문서, 채널, Canvas, grounding 로직이 모두 `Company-X Team UUID`를 사실상 고객/워크스페이스 식별자처럼 사용하고 있습니다.
- 따라서 `team_document`, Chroma 컬렉션명, `COMPANYX_TEAM_ID` 상수, `X-Team-Id` 헤더 규약을 병행 지원 없이 바꾸면 Company-X 질문 응답과 업로드 파이프가 곧바로 회귀할 가능성이 큽니다.

### 4. 부수 업무 테이블도 이미 `team` 또는 `slack_workspace`에 붙어 있어서 영향 범위가 예상보다 넓습니다.
- `startup_valuation`은 현재 전량이 `Company-X Team`에 연결돼 있습니다.
- `coldmail_list_config`는 `slack_workspace_id`에 묶여 있어 Slack workspace 재배치와 함께 검토해야 합니다.
- 따라서 이번 변경은 Slack 채널만이 아니라 valuation, 콜드메일 운영 설정, 대시보드 집계까지 함께 영향권입니다.

### 5. `workspace_member`는 구조상 가장 위험한 부채 지점입니다.
- 이름은 workspace membership이지만 실제 DB에는 `workspace_id`가 없습니다.
- 코드도 이를 일관되게 다루지 못하고 `workspace_member`를 사용자-로빙 매핑처럼 쓰는 구간과, `user.team_id`를 workspace처럼 대체하는 구간으로 나뉘어 있습니다.
- 따라서 계획 문서에서는 `workspace_member`를 유지/교체/재정의 중 무엇으로 갈지 먼저 고정해야 합니다.

### 6. 문서 SSOT도 함께 교정하지 않으면 재발 가능성이 큽니다.
- 현재 DOCS 안에서도 `company -> workspaces 통합 완료`처럼 남아 있는 문서와, 실제 DB 현실이 다릅니다.
- 이번 문제를 푸는 동안 코드를 고쳐도 상위 해석 문서와 하위 구조 문서가 계속 엇갈리면 같은 혼선이 다시 발생할 가능성이 높습니다.

## Unresolved

### 1. 새 `workspace` 테이블을 `company`의 이름 변경으로 갈지, 별도 신설로 갈지는 아직 미확정입니다.

### 2. 현재 `Company-X Team`을 분해할 실제 실행 팀 수와, `xvaluelab` 외 프로젝트 목록은 아직 미확정입니다.

### 3. `workspace_member`를 `workspace_id` 포함 구조로 정상화할지, 별도 membership 테이블로 대체할지는 아직 미확정입니다.

### 4. `slack_workspace.team_id`를 `workspace_id`로 바꿀지, `workspace_id + owner_team_id` 이중 참조로 갈지는 아직 미확정입니다.

### 5. `team_document.team_id`를 `project_id` 또는 `workspace_id`로 옮길 때 컬렉션명과 저장 경로를 어떻게 병행 운영할지는 아직 미확정입니다.

### 6. 관리자 화면에서 보여줄 기본 조직 축을 `team` 중심으로 둘지 `workspace + team` 2계층으로 바꿀지는 아직 미확정입니다.

## 계획 수립 시 반드시 참고할 내용

### 1. 병행 지원 순서가 필요합니다.
- 우선 `workspace`, `team`, `project` 새 해석을 추가합니다.
- 그 다음 기존 `team_id` 경로를 유지한 채 새 FK/새 조회를 병행 지원해야 합니다.
- 마지막에 데이터 이관과 레거시 제거를 진행해야 합니다.

### 2. Slack 경로를 1순위 검증 대상으로 잡아야 합니다.
- 검증 대상:
  - Slack OIDC 로그인
  - Slack passport install/callback/status/token
  - gateway Slack event/interative proxy
  - rb8001 Slack event/file/message 처리

### 3. Company-X RAG 경로를 2순위 검증 대상으로 잡아야 합니다.
- 검증 대상:
  - `team_document` 업로드
  - `skill-rag-file` 검색
  - Company-X grounding
  - IR 채널/Canvas 조회와 전송

### 4. 문서 SSOT 갱신을 계획에 포함해야 합니다.
- 갱신 대상:
  - `database/tables.md`
  - `database/relationships.md`
  - `380_authentication_system.md`
  - 관련 troubleshooting 중 현실과 충돌하는 문서

## 예상 회귀 지점

### 변경 도중 발생 가능
- Slack 로그인은 되지만 사용자 `team_id`/`workspace_id` 매핑이 어긋나 `team_not_found` 또는 403이 발생할 수 있습니다.
- gateway가 Slack `team_id(T...)`는 받지만 내부 UUID 변환을 못 해서 사용자 미등록 오류가 날 수 있습니다.
- `auth-server`의 Slack 이벤트/설치 해제 경로처럼 이미 혼용된 코드가 먼저 터져 특정 API만 500이 날 수 있습니다.
- `skill-rag-file`가 새 조직 ID를 인식하지 못해 업로드 404 또는 검색 0건이 될 수 있습니다.
- `skill-slack` Lists 경로가 새 식별자를 Slack `slack_team_id`로 바꾸지 못해 리스트 조회/동기화가 실패할 수 있습니다.
- Company-X 전용 상수가 남아 있으면 일부 경로만 옛 `team_id`를 계속 사용해 이중 상태가 생길 수 있습니다.

### 변경 후 발생 가능
- 관리자 대시보드의 `total_teams`, `teams_with_robeings` 같은 통계가 새 해석과 맞지 않아 숫자가 왜곡될 수 있습니다.
- 기존 Chroma 컬렉션명과 스토리지 경로가 `team_id` 기반이라 과거 문서 검색이 누락될 수 있습니다.
- `workspace_member` 정리가 불완전하면 사용자-로빙 배정과 권한 판정이 일부 사용자에서만 깨질 수 있습니다.
- `startup_valuation`, `coldmail_list_config`를 이관 목록에서 빼먹으면 Company-X 운영 기능 일부만 조용히 실패하는 비대칭 회귀가 생길 수 있습니다.
- 문서 SSOT를 함께 갱신하지 않으면 이후 운영자가 `workspace`, `team`, `project`를 다시 혼용할 수 있습니다.

## 이번 리서치의 닫힘 조건
- 이 문서는 문제 해결 문서가 아닙니다.
- 다음 단계에서 닫아야 할 것은 `plans` 문서이며, 그 계획은 최소한 아래를 포함해야 합니다.
  - 목표 ERD
  - 병행 지원 순서
  - 데이터 이관 규칙
  - 서비스별 검증 체크리스트
  - 롤백 기준
