Files
Aislo/.agent/plan_register_split.md
T

95 lines
9.1 KiB
Markdown

# 회원가입 플로우 개편: 계정 생성과 회사 연결 분리
## Context
현재 `A07_Register` 회원가입 폼은 계정 생성과 동시에 회사 생성/연결(신규 회사명 또는 기존 company_id)을 강제로 요구한다. 사용자는 이 결합이 부적절하다고 판단했다 — 회사 관련 정보(사업자등록번호, 전화번호, 주소 등)는 계정을 만든 사람이 로그인한 이후에 "회사 생성 또는 검색·연결" 단계로 넘겨야 자연스럽다. 또한:
- 회원가입 폼 필드를 이름/직급/이메일/전화번호/비밀번호/비밀번호 확인으로 재정의 (department는 여기서 제외, 회사 연결 이후 설정)
- 약관/개인정보처리방침/마케팅 동의는 필수 항목이지만 실제 내용이 어디에도 없었다 — 체크박스 옆에 이름을 두고 펼치면 내용이 열리는 UI로 바꾸고, 실제 텍스트는 나중에 쉽게 고칠 수 있도록 리소스 텍스트 파일로 분리
- 이메일 인증은 회원가입 제출 즉시 폼 하단에 OTP 입력 칸이 나타나는 현재 방식을 유지
목표: 계정 생성(A07) → 이메일 인증 → 로그인 가능(company 없이) → 로그인 후 B01_AccountDetail에서 회사 생성/연결 → 연결 완료 후에만 B02~B11 워크플로우 접근 가능.
## 현재 구조 파악 결과
- `users` 테이블: `company_id` nullable, `role` ENUM('SYSTEM_ADMIN','MASTER','MEMBER') 기본 MEMBER, `is_master` BOOLEAN, `status` ENUM('PENDING_EMAIL','PENDING','ACTIVE','INACTIVE','REJECTED')
- `create_registration()` (`common_util/common_util_auth_repository.py:30`): 현재 회원가입 시 계정+회사(or join_request)+user_consents를 한 트랜잭션에서 같이 생성
- `complete_registration()` (`common_util/common_util_auth_repository.py:134`): 이메일 인증 완료 시 MASTER→ACTIVE, MEMBER→PENDING(마스터 승인 대기)로 status 변경
- `verify_session()` (`common_util/common_util_auth.py:74`): 세션 조회 시 `company_id`, `role`, `is_master`, `status`를 함께 반환 — 이미 dependency에서 활용 가능한 형태
- `main.py:236-240`: B03~B06 라우터에 `protected = [Depends(verify_session)]`만 걸려 있음 (company_id 미검사, B07~B11은 라우터 자체가 아직 없음)
- `A00_Common/router.ts`: `PROTECTED_ROUTES` 목록으로 인증 가드만 수행, company 연결 여부는 검사하지 않음
- `A09_Security_Terms.ts` + `A09_Security_UI_Page.ts`: 이미 "보안정책 안내 페이지" 용도로 존재하지만 세션/보안 정책 설명이지 회원가입 약관 동의가 아님 — 이 구조(약관 텍스트 분리 + 언어별 배열)를 참고해 새 리소스를 만든다
- `B01_AccountDetail`: 현재 `UI_Page.ts`, `UI_Style.css`만 있고 백엔드/API 없음 — 여기에 계정 상세 정보 표시 + 회사 연결 섹션을 새로 구현해야 함
- `resources/` 폴더(프로젝트 루트)는 로고/배너 등 에셋용으로 이미 정의돼 있음 (`structure.md:41`) — 약관 텍스트는 이 폴더 하위에 신설 서브폴더로 정리
## DB 변경
`db_management/003_register_split.sql` 신규 마이그레이션:
1. `users.status` ENUM에 `'NO_COMPANY'` 추가 (이메일 인증 완료, 회사 미연결 상태)
2. `users.company_id`, `users.role`, `users.is_master` 는 구조 그대로 유지하되 회원가입 시점에는 전부 NULL/기본값으로 시작
3. 기존 `join_requests` 테이블 그대로 재사용 (MEMBER가 기존 회사 연결 신청 시)
4. `department` 컬럼은 그대로 두되 회원가입 INSERT에서 제외 (NULL 허용이라 문제 없음)
## 백엔드 변경
### A07_Register (회원가입 축소)
- `A07_Register_Schema.py`: `RegisterRequest`에서 `account_type`, `company_id`, `company_name`, `business_number`, `phone_number`(→ 개인 전화번호로 의미 변경), `address`, `representative_name` 제거. 필드: `email`, `password`, `password_confirm`, `name`, `position`, `phone`, `terms_agreed`, `privacy_agreed`, `marketing_agreed`, `terms_version`. `password_confirm == password` 검증을 `model_validator`에 추가.
- `A07_Register_Router.py``request_registration()`: `create_registration()` 호출을 단순화된 데이터로 변경.
- `common_util_auth_repository.py``create_registration()`: 회사/join_request 생성 로직 제거. `users` INSERT만 수행 (`role` 기본값 MEMBER, `company_id` NULL, `is_master` FALSE). `user_consents` INSERT는 유지.
- `complete_registration()`: 이메일 인증 완료 시 무조건 `status = 'NO_COMPANY'`로 변경 (MASTER/MEMBER 분기 제거).
- `A06_Login_Router.py``request_login()`: `user["status"] != "ACTIVE"` 체크를 `status not in ("ACTIVE", "NO_COMPANY")`로 완화 — company 미연결 상태에서도 로그인은 허용.
### 회사 생성/연결 API (신규, B01_AccountDetail 백엔드)
`B01_AccountDetail/B01_AccountDetail_Router.py`, `_Schema.py`, `_Repository.py` 신규 생성 (기존 패턴 준수, `common_util_auth_repository.py``search_companies`/`_company_code` 재사용):
- `GET /api/account/me` — 현재 계정 상세 (session 기반)
- `PATCH /api/account/me` — 이름/직급/전화번호 수정
- `GET /api/account/companies?q=` — 기존 `search_companies()` 재사용
- `POST /api/account/company/create` — 신규 회사 생성 + 본인을 MASTER/is_master=TRUE로 연결, `role='MASTER'`, `status='ACTIVE'`로 전환. 회사 필드(사업자등록번호/주소/전화번호/대표자명)를 여기서 받음.
- `POST /api/account/company/join` — 기존 회사에 `join_requests` 생성 + `users.company_id` 세팅(단, status는 `PENDING`으로, 마스터 승인 전까지 유지)
### 접근 제어 (company 연결 강제)
- `common_util/common_util_auth.py``require_company()` dependency 추가: `session["company_id"] is None` 이면 403.
- `main.py`: B03~B06 라우터의 `protected` 리스트에 `Depends(require_company)` 추가.
## 프론트엔드 변경
### 리소스 텍스트 분리
`resources/legal/` 신규 폴더:
- `terms_of_service.txt` — 이용약관 (Aislo/임도설계 자동화 서비스에 맞는 내용 초안 작성)
- `privacy_policy.txt` — 개인정보처리방침
- `marketing_consent.txt` — 마케팅 정보 수신 동의
이 텍스트는 빌드 시 정적 자산으로 fetch하거나, 간단히 `A07_Register`에서 TS 상수 모듈로 다시 export하는 두 방식 중 — Vite가 `?raw` import를 지원하므로 `import termsText from "../../resources/legal/terms_of_service.txt?raw"` 형태로 로드 (외부 자산 폴더를 벗어나지 않고 사용자가 텍스트 파일만 고치면 되도록).
### A07_Register (폼 축소 + 약관 아코디언 + OTP 유지)
- `A07_Register_UI_Auth_Page.ts`: `accountType`, `company`, `companyId` 필드 및 관련 로직 제거. `position`(직급), `phone`(전화번호), `passwordConfirm` 필드 추가.
- 약관 체크박스 영역: 현재 `check()` 헬퍼(단순 체크박스+라벨)를 확장한 `checkWithAccordion()`로 교체 — 라벨 클릭 시 하단에 `resources/legal/*.txt` 내용이 펼쳐지는 `<details>/<summary>` 또는 토글 버튼 구조.
- OTP 입력 칸: 기존처럼 제출 후 폼 하단에 노출되는 현재 로직 그대로 유지 (`verifyStep` 플래그 방식 재사용).
- `A07_Register_Api_Fetch.ts``RegisterPayload`: 축소된 필드로 갱신, `searchCompanies` export는 B01로 이동(또는 공용 유지 — B01에서 재사용하도록 export 유지).
### B01_AccountDetail (계정 상세 + 회사 연결)
- `B01_AccountDetail_UI_Page.ts`: 계정 기본정보 표시/수정 폼 + "회사 생성" / "회사 검색·연결" 두 개의 섹션(회사 미연결 시에만 노출, `A07_Register_Api_Fetch.searchCompanies` 재사용 가능).
- `B01_AccountDetail_Api_Fetch.ts` 신규: 위 백엔드 API 클라이언트.
### 라우팅 가드 (company 없으면 B02+ 차단)
- `A00_Common/router.ts``renderCurrentRoute()`: `PROTECTED_ROUTES` 검사 이후, B02~B11 라우트에 한해 세션의 `company_id` 확인 로직 추가 (session 조회 API에 `company_id` 이미 포함돼 있음 — `A06_Login_Router.py``/session` 응답에 `company_id` 필드만 추가하면 됨). 없으면 `ROUTES.B01_ACCOUNT`로 리다이렉트.
- `A06_Login_Router.py``current_session()`: 응답에 `company_id`, `status` 추가.
## 검증 방법
1. `python main.py` 실행 후 브라우저에서 `/#/a07-register` 접속 → 축소된 필드로 가입, 약관 아코디언 펼쳐서 텍스트 노출 확인, 제출 후 OTP 칸 노출 확인
2. OTP 인증 완료 → DB에서 `users.status = 'NO_COMPANY'`, `company_id IS NULL` 확인
3. 해당 계정으로 로그인 성공 확인 (기존엔 `status != ACTIVE`면 403이었음)
4. 로그인 상태에서 `/#/b02-proj-register` 접속 시도 → `/#/b01-account`로 리다이렉트되는지 확인
5. B01에서 "회사 생성" 실행 → `role='MASTER'`, `status='ACTIVE'`, `company_id` 세팅 확인 후 B02 접근 가능한지 재확인
6. 두 번째 테스트 계정으로 "회사 검색·연결"(join) 플로우 → `join_requests` 생성 확인, 기존 마스터 승인 API(`A09_Security_Router.py``/master/join-requests`)로 승인 후 status ACTIVE 전환 확인