Skip to content

Commit 630e061

Browse files
SOIVclaude
andcommitted
docs(arch): 계정 관리 정책 결정 추가 (#6)
비밀번호 정책, Lazy 이메일 검증, 임시 비번 온보딩 플로우 확정 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent a176d38 commit 630e061

1 file changed

Lines changed: 91 additions & 1 deletion

File tree

docs/v2_FINANCIAL-LEDGER/architecture/01-decisions.md

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
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

Comments
 (0)