9.1 KiB
회원가입 플로우 개편: 계정 생성과 회사 연결 분리
Context
현재 A07_Register 회원가입 폼은 계정 생성과 동시에 회사 생성/연결(신규 회사명 또는 기존 company_id)을 강제로 요구한다. 사용자는 이 결합이 부적절하다고 판단했다 — 회사 관련 정보(사업자등록번호, 전화번호, 주소 등)는 계정을 만든 사람이 로그인한 이후에 "회사 생성 또는 검색·연결" 단계로 넘겨야 자연스럽다. 또한:
- 회원가입 폼 필드를 이름/직급/이메일/전화번호/비밀번호/비밀번호 확인으로 재정의 (department는 여기서 제외, 회사 연결 이후 설정)
- 약관/개인정보처리방침/마케팅 동의는 필수 항목이지만 실제 내용이 어디에도 없었다 — 체크박스 옆에 이름을 두고 펼치면 내용이 열리는 UI로 바꾸고, 실제 텍스트는 나중에 쉽게 고칠 수 있도록 리소스 텍스트 파일로 분리
- 이메일 인증은 회원가입 제출 즉시 폼 하단에 OTP 입력 칸이 나타나는 현재 방식을 유지
목표: 계정 생성(A07) → 이메일 인증 → 로그인 가능(company 없이) → 로그인 후 B01_AccountDetail에서 회사 생성/연결 → 연결 완료 후에만 B02~B11 워크플로우 접근 가능.
현재 구조 파악 결과
users테이블:company_idnullable,roleENUM('SYSTEM_ADMIN','MASTER','MEMBER') 기본 MEMBER,is_masterBOOLEAN,statusENUM('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: B03B06 라우터에B11은 라우터 자체가 아직 없음)protected = [Depends(verify_session)]만 걸려 있음 (company_id 미검사, B07A00_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 신규 마이그레이션:
users.statusENUM에'NO_COMPANY'추가 (이메일 인증 완료, 회사 미연결 상태)users.company_id,users.role,users.is_master는 구조 그대로 유지하되 회원가입 시점에는 전부 NULL/기본값으로 시작- 기존
join_requests테이블 그대로 재사용 (MEMBER가 기존 회사 연결 신청 시) 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 생성 로직 제거.usersINSERT만 수행 (role기본값 MEMBER,company_idNULL,is_masterFALSE).user_consentsINSERT는 유지.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: 축소된 필드로 갱신,searchCompaniesexport는 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추가.
검증 방법
python main.py실행 후 브라우저에서/#/a07-register접속 → 축소된 필드로 가입, 약관 아코디언 펼쳐서 텍스트 노출 확인, 제출 후 OTP 칸 노출 확인- OTP 인증 완료 → DB에서
users.status = 'NO_COMPANY',company_id IS NULL확인 - 해당 계정으로 로그인 성공 확인 (기존엔
status != ACTIVE면 403이었음) - 로그인 상태에서
/#/b02-proj-register접속 시도 →/#/b01-account로 리다이렉트되는지 확인 - B01에서 "회사 생성" 실행 →
role='MASTER',status='ACTIVE',company_id세팅 확인 후 B02 접근 가능한지 재확인 - 두 번째 테스트 계정으로 "회사 검색·연결"(join) 플로우 →
join_requests생성 확인, 기존 마스터 승인 API(A09_Security_Router.py의/master/join-requests)로 승인 후 status ACTIVE 전환 확인