# B2B Shop Hierarchy — Overview (v0)

## 왜 이 설계를 도입하는가

RoomfitV2는 현재 B2C 단일 유저 앱이다. 하지만 실제 배포 대상 중 PT 샵/피트니스 스튜디오가
다수이며, 트레이너가 회원을 대신해 운동 데이터를 수집하고 공유받는 구조가 필요하다.
본 설계는 기존 B2C 흐름을 깨지 않고 **샵 계층**을 얹는 최소 v0이다.

## 범위

### 포함 (IN)

- Owner가 일반 회원가입 후 "샵 생성" → `centers` row + `center_memberships(role=owner)` 생성
- Owner가 Trainer를 email로 초대 (shadow 상태 허용, trainer 본인이 email 로그인 시 실사용)
- Trainer/Owner가 Member를 email로 추가 → **shadow 계정**으로 auth.users에 존재하지만 로그인 불가
- Trainer가 샵 소속 member 리스트에서 자유롭게 선택 → 수업 세션 시작
- 수업 세션은 `session_type='supervised'`, `trainer_id` 기록, 데이터 소유자는 `user_id=member`
- Member가 직접 로그인 시 자율 세션 (`session_type='self'`, `trainer_id=NULL`) — 기존 B2C 경로 유지
- Owner: 샵 전체 member + trainer + 세션 열람
- Trainer: 샵 전체 member 리스트 + 자신이 진행한 + 샵 내 세션 열람
- Member: 본인 세션만 열람 (B2C와 동일)

### 제외 (OUT — v1 이후)

- Shadow member의 self-claim 플로우 (운영자 수동 처리)
- 초대 email 자동 발송 (Supabase template 미사용, v0은 shadow만)
- 프랜차이즈 / 멀티 지점 / 브랜드 본사
- 기기 귀속 모델 (shop 소유 vs 개인 소유)
- 결제 / 정산 / 매출
- 트레이너 처방 (다음 세션 프로그래밍)
- 샵별 매칭 정책 옵션 (항상 "세션별 자유 선택")
- 트레이너 전용 별도 앱 (동일 앱 내 role 분기)

## 용어

| 용어 | 정의 |
|---|---|
| **Shop / Center** | 물리적 PT 샵. 스키마 상 `centers` 테이블 row. |
| **Owner** | 샵 소유자. `center_memberships.role='owner'`. 샵당 1명. |
| **Trainer** | 수업을 진행하는 직원. `center_memberships.role='trainer'`. |
| **Member** | 고객/회원. `center_memberships.role='member'`. 데이터 소유자. |
| **Staff** | Owner 또는 Trainer. RLS 헬퍼에서 사용. |
| **Shadow 계정** | `auth.users`에 존재하지만 로그인 수단 없음 (비밀번호 미설정). Member 기록 생성을 위해 Admin API로 생성. |
| **Supervised 세션** | Trainer가 진행/기록한 세션. `session_type='supervised'`, `trainer_id NOT NULL`. |
| **Self 세션** | Member 본인이 진행한 세션. `session_type='self'`, `trainer_id NULL`. |
| **매칭** | Trainer-Member 쌍. v0에서 영구 매칭 없음. 세션마다 trainer가 member를 고른다. |

## 아키텍처 한눈에

```
┌─ auth.users ───────────────────────────────────────────────┐
│  (Supabase Auth — email + optional password)               │
└────────────────────────────────────────────────────────────┘
         │                      │                      │
         ▼                      ▼                      ▼
┌─ centers ─┐   ┌─ center_memberships ─┐   ┌─ workout_sessions ──┐
│ id        │   │ center_id, user_id   │   │ user_id (member)    │
│ name      │   │ role                 │   │ trainer_id (nullable)│
│ owner_id  │   │ (owner/trainer/      │   │ center_id (nullable) │
└───────────┘   │  member)             │   │ session_type         │
                │ UNIQUE per center    │   └──────────────────────┘
                └──────────────────────┘              │
                                                      ▼
                     ┌─ exercise_entries ─┬─ exercise_sets ─┬─ reps ─┐
                     │ (RLS via session)  │                 │        │
                     └────────────────────┴─────────────────┴────────┘
```

## 앱 구성

본 v0은 **두 개의 Flutter 앱**으로 분리된다.

- `apps/b2c/` — 회원(Member) 앱. 기존 RoomfitV2. self 세션 중심.
- `apps/b2b/` — 샵 운영자(Owner/Trainer) 앱. 샵 관리 + supervised 세션.

두 앱은 `packages/roomfit_*` (BLE/device/exercise/firmware/protocol/design_system/**shop**) 를 공유.
구조 상세는 [monorepo-structure.md](./monorepo-structure.md) 참조.

## 관련 문서

- [monorepo-structure.md](./monorepo-structure.md) — 모노레포 레이아웃 + 이행 단계
- [hierarchy.md](./hierarchy.md) — 계층/Role/온보딩 플로우
- [rls.md](./rls.md) — RLS 정책 전체 SQL + 테스트
- [shadow-accounts.md](./shadow-accounts.md) — Shadow 계정 + Edge function

## 설계 원칙

1. **데이터 소유자는 항상 member** — Owner/Trainer는 공유받을 뿐.
2. **기존 B2C 경로는 깨지 않는다** — Member 직접 로그인 시 모든 기존 테스트가 pass.
3. **세션별 매칭** — 영구 trainer-member 관계 테이블 없음. `workout_sessions.trainer_id`만 기록.
4. **RLS 1계층만** — staff 여부는 `is_shop_staff(user, center)` 헬퍼로, 다른 테이블은 session의 정책을 EXISTS로 상속.
5. **Supervised의 session.user_id는 즉시 member의 id** — shadow이든 실사용이든 동일 user_id. trainer는 오직 저자(author)로 `trainer_id`에 기록.
6. **shadow ↔ 실사용 전환 없음 (v0)** — 같은 email의 auth.user가 v0에서는 만들어지자마자 끝. 본인 로그인은 v1에서.
