feat(user): 사용자 자기정보 편집과 관리자 재설정 플로우를 연동
- lib/widgets/app_shell.dart에서 내 정보 다이얼로그를 추가하고 UserRepository.updateMe·비밀번호 변경 로직을 연결 - lib/features/masters/user/* 모듈에 phone·forcePasswordChange·passwordUpdatedAt 필드를 반영하고 reset-password/update-me API를 사용 - lib/core/validation/password_rules.dart을 신설해 비밀번호 정책 검증을 공통화하고 신규 위젯·테스트에서 재사용 - doc/stock_approval_system_api_v4.md 등 문서를 users 스펙 개편 내용으로 갱신하고 user_management_plan.md를 추가 - test/widgets/app_shell_test.dart 등에서 자기정보 수정·비밀번호 재설정 시나리오를 검증하고 기존 테스트를 보강
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
입고 등록/사용자 관리 기능에서 작성자(로그인 사용자) 정보를 정확히 추적하고, 관리자가 신규 사용자를 생성·관리할 수 있도록 백엔드/프런트엔드 동시 진행 항목을 정리했습니다. 기존 인증/세션 흐름과 충돌할 수 있으므로, 아래 항목을 참고해 단계별 검증을 병행하세요.
|
||||
|
||||
## 요구사항 요약
|
||||
- 작성자는 현재 로그인한 사용자 계정을 사용한다. (기존 더미 계정 `terabits` 제거 예정)
|
||||
- 작성자는 현재 로그인한 사용자 계정을 사용한다. (기존 더미 계정 `terabits`는 최고 관리자 계정으로 유지하며 삭제하지 않는다)
|
||||
- 신규 사용자는 관리자만 등록할 수 있으며, 필수 입력 필드는 `employee_id`, `name`, `phone`, `email`, `password`.
|
||||
- `employee_id`는 영문/숫자만 허용한다. (정규식 예: `^[A-Za-z0-9]{4,32}$`)
|
||||
- 사용자는 등록 후 `phone`, `email`, `password`만 스스로 수정할 수 있다.
|
||||
@@ -14,12 +14,22 @@
|
||||
- 로그인 후 우상단 사용자 메뉴에서 이메일/비밀번호/연락처 변경 가능하며 `저장` 버튼으로 확정한다.
|
||||
- 비밀번호 변경 성공 시 즉시 로그아웃 팝업을 띄우고 확인과 동시에 세션을 만료한다. 취소는 허용하지 않는다.
|
||||
|
||||
## 영향 범위 및 문서 동기화 체크리스트
|
||||
- 사용자 인증, 알림, 입고 등록 플로우 모두에 영향을 준다. 배포 전 후속 회귀 테스트를 위해 `doc/IMPLEMENTATION_TASKS.md`, `doc/frontend_api_alignment_plan.md`, `doc/frontend_backend_alignment_report.md`를 업데이트하고 담당자 서명을 남긴다.
|
||||
- API/DTO 변경은 `doc/stock_approval_system_api_v4.md`, `doc/stock_approval_system_spec_v4.md`와 중복 정의가 없는지 교차 검토한다. 충돌 시 해당 문서를 동시에 수정한다.
|
||||
- UI 사양 변경은 `doc/input_widget_guide.md`, `doc/frontend_auto_numbering_update.md`에서 안내하는 컴포넌트 규칙과 일치해야 한다. 불일치 발견 시 문서와 코드 모두 조정한다.
|
||||
- 새로운 정책 문구는 QA/기획 승인 후 공유하고, 수정 이력은 `doc/DTO_TASKS.md` 또는 관련 변경 로그에 기록한다.
|
||||
- 사이드 이펙트 방지를 위해 세션 만료, 알림(Notify), 이메일 발송 시스템에 대한 변경 감지 테스트를 작성하고, 관련 서비스 운영자에게 사전 알린다.
|
||||
- 작업자는 위 문서들을 포함해 최신 요구사항 문서를 모두 검토한 뒤 개발을 시작하며, 수정한 문서 목록을 PR 설명에 명시한다.
|
||||
|
||||
## 백엔드 작업 항목
|
||||
|
||||
### 1. 도메인 및 저장소 구조 정리
|
||||
- `users` 테이블/컬렉션에 `employee_id`, `phone`, `password_hash`, `password_updated_at`, `force_password_change` 필드를 추가한다.
|
||||
- `employee_id` 컬럼은 유니크 인덱스를 생성하고 대소문자 구분 여부를 명확히 한다.
|
||||
- 기존 `terabits` 계정은 마이그레이션 스크립트에서 관리자 권한 유지 여부만 검토하고, 실사용자 생성 이후 제거 플랜을 마련한다.
|
||||
- 기존 `terabits` 계정은 최상위 관리자(Super Admin)로 고정 유지하며, 마이그레이션 스크립트에서 권한 승계가 깨지지 않는지 검증한다. 삭제 작업은 서비스 전환 이후 운영 측에서 수동 처리하도록 문서화한다.
|
||||
- 마이그레이션 파일은 `backend/migrations/2024xxxx_add_user_fields.sql` 형태로 작성하고, 롤백 스크립트에 `DROP COLUMN` 대신 값 초기화를 고려한다.
|
||||
- 도메인 레이어(`lib/domain/auth/entities/user.dart`)와 데이터 레이어 DTO가 새 필드를 반영하도록 Freezed 모델을 갱신한다.
|
||||
|
||||
### 2. 사용자 생성 API (`POST /users`)
|
||||
- 관리자 권한 체크 로직을 선행한다.
|
||||
@@ -27,6 +37,8 @@
|
||||
- `password`는 Bcrypt/Scrypt 등 기존 정책에 맞춰 해시 저장한다.
|
||||
- 생성 직후 `force_password_change = true`로 설정하고 응답에 포함한다.
|
||||
- 사용자 생성 완료 시 비밀번호 초기값을 메일 발송 큐에 전달한다.
|
||||
- OpenAPI/Swagger 문서(`backend/openapi/user.yaml`)를 업데이트하고, 프런트엔드와 공유할 샘플 응답을 `doc/frontend_api_alignment_plan.md`에 추가한다.
|
||||
- 새 API 연동을 위해 `lib/injection_container.dart`에서 `UserRepository` 구현에 `createUser` 메서드를 공개한다.
|
||||
|
||||
### 3. 사용자 정보 수정 API
|
||||
- **자기 정보 수정 (`PATCH /users/me`)**
|
||||
@@ -35,6 +47,8 @@
|
||||
- **관리자용 수정 (`PATCH /users/{id}`)**
|
||||
- 허용 필드: `phone`, `email`, 권한 변경 등 필요한 항목만 열어준다.
|
||||
- 비밀번호 재설정은 별도 엔드포인트로 분리한다.
|
||||
- 비밀번호 변경 시 감사 로그(`audit_logs` 테이블)와 보안 이벤트 버스에 `password_change` 이벤트를 발행해 SIEM과 연계한다.
|
||||
- `PATCH /users/me` 응답에 `forcePasswordChange` 상태를 포함해 프런트 동기화를 용이하게 한다.
|
||||
|
||||
### 4. 비밀번호 재설정 API (`POST /users/{id}/reset-password`)
|
||||
- 관리자 권한 필터 적용.
|
||||
@@ -42,23 +56,31 @@
|
||||
- 새 비밀번호 해시 저장 후 `force_password_change = true` 설정.
|
||||
- 이메일 발송: 템플릿에 임시 비밀번호 및 최초 로그인 안내 포함.
|
||||
- 감사 로깅: 누가 언제 어떤 사용자 비밀번호를 초기화했는지 저장.
|
||||
- 임시 비밀번호는 10분 동안만 유효하도록 선택적 만료 토큰을 발급하고, 사용자는 첫 로그인 시 즉시 변경해야 한다는 메시지를 포함한다.
|
||||
- API 오류 응답 코드(권한 없음, 사용자 없음, 메일 전송 실패)를 정의하고 `doc/error_message_guide.md`와 일치시킨다.
|
||||
|
||||
### 5. 최초 로그인 강제 변경 로직
|
||||
- 로그인 성공 시 `force_password_change`가 `true`면 액세스 토큰 발급 대신 “비밀번호 재설정 필요” 상태 코드/에러 코드를 반환한다.
|
||||
- 프런트엔드는 이 코드를 받아 전용 비밀번호 변경 화면으로 이동한다.
|
||||
- 기존 Remember-me/세션 로직과의 호환성을 검증한다.
|
||||
- 모바일/웹 동시 로그인 시 한쪽에서 비밀번호 변경하면 다른 기기 세션도 강제 만료되도록 Redis 세션 캐시를 무효화한다.
|
||||
- 인증 로그(`auth_events`)에 `force_password_change_triggered` 이벤트를 남겨 추적 가능하도록 한다.
|
||||
|
||||
### 6. 이메일 발송 및 템플릿
|
||||
- 신규 계정 생성/비밀번호 재설정 공통 템플릿을 작성하고 변수: `name`, `employee_id`, `temp_password`, `reset_url`.
|
||||
- SMTP/메일 서비스 환경 변수 재점검(`MAIL_FROM`, `RESET_URL_BASE` 등).
|
||||
- QA 환경에서는 메일 전송을 sandbox로 대체하고 로그로 임시 비밀번호를 출력한다.
|
||||
- 템플릿 초안은 `doc/qa/email_templates/user_password_reset.md`에 저장하고, PR 시점에 QA 승인 코멘트를 첨부한다.
|
||||
- 메일 발송 실패 시 재시도 큐를 3회까지 두고, 실패 알림을 Slack/Notify로 전송한다.
|
||||
|
||||
### 7. 테스트 & 배포 체크리스트
|
||||
- [ ] 사용자 생성, 자기 정보 수정, 관리자 초기화 API 통합 테스트 작성.
|
||||
- [ ] 비밀번호 정책 유효성 테스트 (허용/거부 케이스) 구현.
|
||||
- [x] 사용자 생성, 자기 정보 수정, 관리자 초기화 API 통합 테스트 작성. (테스트: `test/features/masters/user/data/user_repository_remote_test.dart`)
|
||||
- [x] 비밀번호 정책 유효성 테스트 (허용/거부 케이스) 구현. (테스트: `test/core/validation/password_rules_test.dart`)
|
||||
- [ ] 마이그레이션 스크립트와 롤백 스크립트 준비.
|
||||
- [ ] 배포 전 staging에서 실제 메일 발송 여부 검증.
|
||||
- [ ] 기존 로그인 세션/토큰 구조와 충돌 여부 점검.
|
||||
- [ ] Notify 파이프라인(`notify.py`)을 통해 릴리스 직후 운영자에게 변경 내역을 알린다.
|
||||
- [ ] 장애 발생 시 롤백 전략(임시 비밀번호 재발급 차단, 구 계정 복구)을 문서화한다.
|
||||
|
||||
## 프런트엔드 작업 항목
|
||||
|
||||
@@ -67,32 +89,46 @@
|
||||
- 신규 등록 모달/페이지에 입력 필드를 추가하고 프런트 검증(정규식, 필수 여부)을 구현한다.
|
||||
- 제출 시 비밀번호 정책 위반 시 프론트에서 선제적으로 에러 메시지 표시(한글 메시지, 정책 안내 문구 포함).
|
||||
- 생성 성공 후 토스트 및 리스트 리프레시, 임시 비밀번호가 이메일로 발송됨을 명시한다.
|
||||
- UI 구현 시 `lib/features/user_management/presentation/widgets/shad_user_table.dart`와 `ShadTable` 컴포넌트를 기반으로 열 구성을 추가한다.
|
||||
- 상태 관리는 `lib/features/user_management/presentation/controllers/user_controller.dart`에 `createUser` 액션을 확장하고, 에러 핸들링을 중앙화한다.
|
||||
- (2025-10-24) 신규 등록 모달에 임시 비밀번호/이메일/연락처 필수 검증을 적용하고 목록 액션에 비밀번호 재설정 버튼과 확인 다이얼로그를 추가했다. (`lib/features/masters/user/presentation/pages/user_page.dart`, 테스트: `test/features/masters/user/presentation/pages/user_page_test.dart`)
|
||||
|
||||
### 2. 관리자 > 사용자 상세 보기
|
||||
- 비밀번호 재설정 버튼 추가: 클릭 시 확인 다이얼로그 → API 호출 → 성공 토스트.
|
||||
- 다이얼로그 문구에 “임시 비밀번호가 이메일로 발송된다”는 안내 포함.
|
||||
- 감사 로그 필요 시 별도 이벤트 추적(`analytics`/`Sentry breadcrumb`)을 연동한다.
|
||||
- 다이얼로그는 `SuperportShadDialog`를 사용하고, 컴포넌트는 `lib/widgets/dialogs/` 밑에 재사용 가능하게 분리한다.
|
||||
- API 응답(메시지/실패 코드)은 `lib/core/network/api_error_mapper.dart`에서 한글 메시지로 변환한다.
|
||||
|
||||
### 3. 우상단 사용자 메뉴 개선
|
||||
- `내 정보` 패널에서 이메일/연락처 수정 필드를 제공하고, 변경 시 Dirty 상태 감지 → `저장` 버튼 활성화.
|
||||
- 저장 성공 후 사용자 상태 스토어/Provider를 갱신하여 헤더/다른 화면과 동기화.
|
||||
- 비밀번호 변경 진입 버튼을 분리하고, 모달 또는 전용 페이지에서 3개 입력 필드를 제공한다.
|
||||
- `lib/features/profile/presentation/pages/profile_page.dart`에서 폼 상태와 검증 로직을 `lib/features/profile/presentation/controllers/profile_controller.dart`로 분리한다.
|
||||
- `lib/core/validation/password_rules.dart`에 비밀번호 정책 검증 유틸을 추가하고, 모든 비밀번호 입력 필드에서 재사용한다.
|
||||
- (2025-10-24) 상단 내 정보 모달에 이메일/연락처 저장과 비밀번호 변경(강제 로그아웃) 흐름을 구현하고 테스트를 추가했다. (`lib/widgets/app_shell.dart`, 테스트: `test/widgets/app_shell_test.dart`)
|
||||
|
||||
### 4. 비밀번호 변경 플로우
|
||||
- `현재 비밀번호`, `새 비밀번호`, `새 비밀번호 확인` 필드와 실시간 정책 검증(대/소문자, 숫자, 특수문자) UI를 구현한다.
|
||||
- 저장 시 API 호출 → 성공하면 즉시 비밀번호 변경 완료 다이얼로그/스낵바 → **강제 로그아웃 팝업** 표출.
|
||||
- 팝업은 확인 버튼만 제공하며, 클릭 시 `authController.signOut()` 호출 후 로그인 페이지로 리다이렉트.
|
||||
- 실패 케이스 처리: 기존 비밀번호 불일치, 정책 위반, 서버 에러 각각에 맞는 오류 메시지.
|
||||
- 팝업 UI는 `lib/features/auth/presentation/widgets/logout_alert.dart`로 분리하여 테스트 가능하도록 한다.
|
||||
- 로그아웃 후 캐시된 사용자 정보는 `UserSessionStore.clear()`를 호출해 잔여 데이터를 모두 제거한다.
|
||||
|
||||
### 5. 최초 로그인 경로 처리
|
||||
- 로그인 성공 응답이 “비밀번호 변경 필요” 상태이면 인증 토큰을 저장하지 않고, 전용 비밀번호 변경 화면으로 라우팅.
|
||||
- 비밀번호 변경 완료 후에는 정상 로그인 플로우 재시도(저장된 자격 증명 재사용 금지).
|
||||
- 라우터(`lib/core/router/app_router.dart`)에 `forcePasswordChange` 상태를 처리하는 보호 라우트를 추가한다.
|
||||
- 상태 관리(`AuthNotifier`)는 비밀번호 변경 필요 상태를 저장하고, 다른 화면 접근 시 가드를 적용한다.
|
||||
|
||||
### 6. 공통 사항
|
||||
- `employee_id`는 읽기 전용 표시로 유지하며, 자기 정보 수정 화면에서는 비활성화한다.
|
||||
- 폼 검증 메시지는 `lib/core/validation` 또는 기존 유틸 모듈에 추가하고 재사용한다.
|
||||
- API 타입 정의(DTO/모델) 업데이트: `forcePasswordChange` 플래그, `phone`/`email` 수정 필드 등 반영.
|
||||
- 테스트: 관리자 화면 위젯 테스트, 비밀번호 변경 위젯 테스트, 상태 관리 유닛 테스트 작성.
|
||||
- `dart run build_runner build --delete-conflicting-outputs` 명령으로 DTO 업데이트 후 생성물을 재생성한다.
|
||||
- Storybook/샘플 화면이 있다면 사용자 메뉴/다이얼로그 스토리를 갱신한다.
|
||||
|
||||
### 7. QA 체크리스트
|
||||
- [ ] 신규 사용자 생성 시 임시 비밀번호 안내 모달과 이메일 발송 메세지가 노출된다.
|
||||
@@ -100,10 +136,16 @@
|
||||
- [ ] 비밀번호 변경 후 로그아웃 팝업이 표시되고, 확인 시 실제 로그아웃 된다.
|
||||
- [ ] 이메일/연락처 저장 시 즉시 프로필 정보가 갱신된다.
|
||||
- [ ] 관리자 비밀번호 재설정 후 사용자가 로그인 시 새 임시 비밀번호가 요구된다.
|
||||
- [ ] API 실패(메일·세션·권한 오류) 시 사용자에게 명확한 한글 메시지가 노출된다.
|
||||
- [ ] 프로필 데이터 캐시가 오래된 경우 새 정보로 갱신되는지 확인한다.
|
||||
- [ ] 접근 권한이 없는 사용자가 관리자 화면에 진입하면 즉시 차단된다.
|
||||
|
||||
## 동시 작업 및 커뮤니케이션 가이드
|
||||
- 백엔드는 임시로 `force_password_change` 상태를 반환하는 Mock 응답을 제공하고, 프런트엔드는 이를 기준으로 화면을 선행 구현한다.
|
||||
- 개발 순서는 **백엔드 구현과 검증을 선행**하고, API 스펙이 확정·배포된 이후 프런트엔드 개발을 진행한다. 프런트에서 임의 Mock을 사용하지 말고, 백엔드가 전달한 실제 응답 계약을 기반으로 연동한다.
|
||||
- 백엔드 구현 완료 시점에 API 계약서와 샘플 응답을 공유하고, 프런트엔드는 이를 수신한 뒤 화면/로직을 개발한다.
|
||||
- API 스펙 변경 사항은 `doc/frontend_api_alignment_plan.md`에 연동 기록을 추가하고, DTO 변경은 `dart run build_runner` 실행 시점 합의 후 진행한다.
|
||||
- 이메일 템플릿, 비밀번호 정책 문구 등 사용자 노출 텍스트는 `product/QA` 승인 후 배포한다.
|
||||
- 배포 순서: 백엔드 마이그레이션 → 신규 API 배포 → 프런트 배포 → 더미 계정 정리.
|
||||
- 사이드 이펙트 대비: 로그인 세션 만료, 캐시된 사용자 정보, 기존 Admin UI 권한 체크 로직 변경 시 전체 기능 회귀 테스트를 수행한다.
|
||||
- 변경 사항은 `notify.py` 워크플로로 전달하고, 영향 범위(입고 등록, 승인 플로우, 보고서 출력)를 명시한다.
|
||||
- 프런트·백엔드 책임자는 매일 스탠드업에서 진행 현황과 문서 업데이트 여부를 공유한다.
|
||||
|
||||
Reference in New Issue
Block a user