44> 모든 개발과 문서는 이 결정을 기반으로 진행됩니다.
55
66** 최종 업데이트:** 2026-04-12
7- ** 문서 버전:** 1.2 .0
7+ ** 문서 버전:** 1.3 .0
88
99---
1010
@@ -527,6 +527,94 @@ Hard-Core를 별도 계층으로 분리하지 않고 **Core 단일 프로세스
527527
528528---
529529
530+ ## 결정 #6 : 사용자 계정 관리 — 비밀번호 정책 · 이메일 검증 · 신규 유저 온보딩
531+
532+ ### ✅ 결정 사항
533+
534+ 1 . ** 비밀번호 정책** — 12~ 20자, 영어 대/소문자 + 숫자 + 특수문자 각 1자 이상 필수
535+ 2 . ** 이메일 검증 방식** — Lazy (비밀번호 복구 시점에만 검증)
536+ 3 . ** 신규 유저 온보딩** — 관리자가 임시 비밀번호를 생성하여 전달, 첫 로그인 시 비밀번호 변경 강제
537+
538+ ### 📖 배경
539+
540+ Fieldstack은 Whitelist 기반 접근 제어를 사용하는 셀프호스팅 앱이다. 외부 사용자가 자유롭게 가입할 수 없고, 관리자가 명시적으로 이메일을 추가해야 접근이 가능하다. 이 구조에서 이메일 인증과 계정 생성 방식을 어떻게 설계할지 결정이 필요했다.
541+
542+ ### 🔍 주요 결정 상세
543+
544+ #### 1. 비밀번호 정책
545+
546+ ```
547+ 길이: 12자 이상 ~ 20자 이하
548+ 필수 조건: 영어 대문자 1자 이상
549+ 영어 소문자 1자 이상
550+ 숫자 1자 이상
551+ 특수문자 1자 이상
552+ ```
553+
554+ 구현체: ` packages/core/src/utils/validation.ts ` 의 ` validatePassword() ` 함수.
555+ 계정 생성 및 비밀번호 변경 시 이 함수로 단일하게 검증한다.
556+
557+ #### 2. 이메일 검증 — Lazy 방식
558+
559+ ** 채택하지 않은 방식 (Eager):** 계정 생성 시 인증 코드 이메일 발송
560+ ```
561+ 단점:
562+ - 설치 시점에 SMTP 설정이 완료되어 있어야 함
563+ - SMTP 없으면 계정 생성 자체가 불가능
564+ - Whitelist 기반에서 실익이 없음 (이미 관리자가 신원 확인)
565+ ```
566+
567+ ** 채택한 방식 (Lazy):** 비밀번호 복구 시점에만 이메일 유효성 검증
568+ ```
569+ 장점:
570+ - SMTP 없이도 계정 생성 가능
571+ - Whitelist 추가 자체가 "관리자 신원 확인" 역할을 대신함
572+ - 설치 초기 마찰 최소화
573+ ```
574+
575+ #### 3. 신규 유저 온보딩 플로우
576+
577+ ```
578+ 1. 관리자 → Admin 콘솔에서 이메일을 Whitelist에 추가
579+ 2. 시스템 → 임시 비밀번호 자동 생성 (안전한 랜덤 문자열, 1회용)
580+ 3. 관리자 → 화면에 1회 표시된 임시 비밀번호를 유저에게 직접 전달
581+ (SMTP 설정 시 자동 발송으로 업그레이드 가능)
582+ 4. 유저 → 이메일 + 임시 비밀번호로 로그인
583+ 5. 시스템 → must_change_password 플래그 감지
584+ → 비밀번호 변경 화면으로 강제 이동 (앱 진입 불가)
585+ 6. 유저 → 새 비밀번호 설정 (validatePassword 조건 통과 필수)
586+ 7. 시스템 → 플래그 해제, 정상 사용 시작
587+ ```
588+
589+ 이 방식에서 관리자는 유저의 실제 비밀번호를 알 수 없다 (임시 비밀번호는 1회용).
590+
591+ ### 🎯 구현 방향
592+
593+ ** DB 스키마 (User 테이블 추가 필드):**
594+ ```
595+ must_change_password: boolean -- 첫 로그인 강제 변경 여부
596+ temp_password_hash: string | null -- 임시 비밀번호 해시 (변경 완료 시 null)
597+ ```
598+
599+ ** 임시 비밀번호 생성 정책:**
600+ - 길이: 16자 (영문 대소문자 + 숫자 조합)
601+ - 저장: Argon2id 해시 (일반 비밀번호와 동일)
602+ - 표시: 생성 시 관리자 화면에 1회만 노출, 이후 조회 불가
603+
604+ ** 관련 라우팅:**
605+ - 로그인 후 ` must_change_password === true ` 이면 ` /change-password ` 강제 리다이렉트
606+ - 다른 모든 경로 접근 차단 (비밀번호 변경 완료 전까지)
607+
608+ ### 📚 관련 문서
609+
610+ > 📖 ** 인증 설계:**
611+ > → ` technical/02-authentication.md `
612+
613+ > 📖 ** 검증 유틸:**
614+ > → ` packages/core/src/utils/validation.ts `
615+
616+ ---
617+
530618## 📝 요약표
531619
532620| 결정 | 선택 | 핵심 이유 | 상태 |
@@ -536,6 +624,7 @@ Hard-Core를 별도 계층으로 분리하지 않고 **Core 단일 프로세스
536624| #3 DB 추상화 | Query Builder | 적절한 레벨 | ✅ 확정 |
537625| #4 API 네임스페이스 | Internal/External 분리 | 보안 및 AI 연동 최적화 | ✅ 확정 |
538626| #5 Hard-Core 폐기 | Core 단일 통합 | Ring 3 분리 실익 없음, 외부 프로세스 매니저로 대체 | ✅ 확정 |
627+ | #6 계정 관리 | 정책 3종 | 비밀번호 정책 + Lazy 이메일 검증 + 임시 비번 온보딩 | ✅ 확정 |
539628
540629---
541630
@@ -547,6 +636,7 @@ Hard-Core를 별도 계층으로 분리하지 않고 **Core 단일 프로세스
547636| 2026-03-05 | 1.1.0 | API 네임스페이스 분리 결정 추가 (#4 ) |
548637| 2026-04-12 | 1.1.0 | 결정 번호 순서 정렬 (#5 → #4 ), 헤더 날짜/버전 동기화 |
549638| 2026-04-12 | 1.2.0 | Hard-Core 계층 폐기 및 Core 단일 통합 결정 추가 (#5 ) |
639+ | 2026-04-12 | 1.3.0 | 계정 관리 정책 추가 — 비밀번호 정책, Lazy 이메일 검증, 임시 비번 온보딩 (#6 ) |
550640
551641---
552642
0 commit comments