260706_1_계정, 로그인, 보안 초안 작성

This commit is contained in:
2026-07-07 19:17:13 +09:00
parent 404941c47a
commit f8633bb1fe
4696 changed files with 6110 additions and 642171 deletions
@@ -0,0 +1,623 @@
# 로그인 & 보안 시스템 구현 계획서 (v1.0)
## 1. 프로젝트 개요
### 1.1 목표
- B2B SaaS 환경에서 **마스터-사용자** 기반 조직 관리
- **이메일 인증** 기반 강화된 보안
- **시스템관리자** 전체 감시/관리 권한
- 개인정보보호법 준수
### 1.2 적용 대상
- 30인 미만 소규모 기업 (공용 이메일 허용)
- 개인별 계정 운영 (마스터 승인제)
- 한국 개인정보보호법 준수 필수
---
## 2. 계정 유형 및 역할 (3가지)
### 2.1 마스터 (Master / Company Admin)
**정의**: 회사 최초 가입자 (회사당 1명 권장, 복수 가능)
**권한**:
- 회사 정보 입력/수정
- 초대코드 발급 및 관리
- 신규 팀원 가입 승인/거부
- 팀원 목록 조회
- 팀원 강제 제거 (내보내기)
- 회사 구독/결제 관리
- 회사 대시보드 (기본 통계)
**DB 구분**: `users.is_master = true`
---
### 2.2 팀원 (Member / Regular User)
**정의**: 마스터가 승인한 회사 소속 사용자
**권한**:
- 서비스 기능 일반 이용 (B04~B09 워크플로우)
- 개인 프로필 수정
- 프로젝트 생성/수정/조회
**DB 구분**: `users.is_master = false`
**상태 관리**:
- `PENDING` (승인 대기중)
- `ACTIVE` (승인됨, 이용 가능)
- `INACTIVE` (마스터가 내보냄, 접근 불가)
- `REJECTED` (마스터가 거부함)
---
### 2.3 시스템관리자 (System Administrator)
**정의**: 개발사 (Aislo) 측 운영 담당자 = **당신**
**권한**:
- 모든 회사 데이터 조회 (슈퍼유저)
- 회사/사용자 계정 생성/삭제/수정
- 회사별 구독 상태 관리
- 전체 시스템 Q&A/요청사항 처리 (회사 메일로 수신 → 검토/수정/삭제)
- 관리자 대시보드
- 활성 사용자 수
- 하드웨어 리소스 모니터링 (CPU, 메모리, 디스크)
- 소프트웨어 사용 현황 (업로드/다운로드/API 호출 통계)
- 에러/경고 로그 모니터링
- 회사별 파일 저장소 사용량
- 마스터 계정 대체 관리 (마스터 부재 시)
**DB 구분**: `users.role = 'SYSTEM_ADMIN'` (별도 테이블로 관리)
**접근**: 별도 관리 포털 (프론트엔드에서 숨김)
---
## 3. 회원가입 및 조직 연결 흐름
### 3.1 마스터 가입 흐름
```
1단계: 회원가입 시작
└─ 이메일 입력 (예: founder@gmail.com)
└─ 비밀번호 입력
2단계: 이메일 인증
└─ 시스템이 해당 이메일로 6자리 OTP 코드 발송
└─ 사용자가 OTP 입력하여 인증
3단계: 회사 정보 입력
└─ 회사명
└─ 사업자번호 (선택)
└─ 회사 전화번호
└─ 주소
└─ 대표자명
4단계: 가입 완료
└─ DB에 Company 레코드 생성
└─ 고유 초대코드 자동 생성 (예: ABC-789-XYZ)
└─ users.is_master = true로 설정
└─ 마스터 대시보드로 이동
```
---
### 3.2 팀원 가입 흐름 (요청 기반)
```
1단계: 팀원이 가입 신청
└─ 이메일 입력 (예: member@gmail.com)
└─ 비밀번호 입력
2단계: 이메일 인증
└─ OTP 입력하여 인증
3단계: 회사 정보 입력 (선택지)
├─ [새 회사 등록하기] → 마스터 역할 시작
└─ [기존 회사에 참여하기] → 4단계로 진행
4단계: 회사 선택/검색
└─ "우리 회사가 이미 가입되어 있나요?"
└─ 회사명으로 검색
└─ 검색 결과에서 해당 회사 선택
5단계: 승인 요청
└─ [가입 신청 보내기] 버튼 클릭
└─ 백엔드: JoinRequest 레코드 생성
└─ 백엔드: 마스터의 등록된 이메일로 승인 메일 발송
6단계: 마스터의 승인 메일 확인
└─ 마스터가 받은 승인 메일:
"홍길동(member@gmail.com)님이 우리 회사에 가입을 신청했습니다.
[승인] [거부] 버튼"
└─ 마스터가 [승인] 클릭
└─ 팀원 users.status = 'ACTIVE'로 변경
└─ 팀원에게 "승인되었습니다" 메일 발송
7단계: 팀원 로그인 가능
└─ 팀원이 이제 서비스에 로그인 가능
```
**💡 핵심 차이점**
- ❌ 초대코드 쪽지 방식 아님
- ✅ 사용자가 스스로 요청 → 마스터가 메일로 승인
---
## 4. 이메일 인증 (MFA) 정책
### 4.1 로그인 프로세스
```
1단계: 아이디(이메일)/비밀번호 입력
2단계: 백엔드 검증 (계정 존재, 비밀번호 일치, 상태 = ACTIVE)
3단계: OTP 발송 및 입력
└─ 등록된 이메일로 6자리 OTP 코드 발송
└─ 사용자 입력 (유효시간: 5분)
4단계: 로그인 완료
└─ JWT/세션 토큰 발급
```
### 4.2 이메일 인증 주기
| 상황 | 인증 여부 |
|------|---------|
| **최초 가입 시** | ✅ 필수 |
| **로그인할 때마다** | ❌ 아니오 (UX 개선) |
| **3개월 주기 재인증** | ✅ 필수 |
| **새로운 브라우저/기기** | ✅ 필수 |
| **비밀번호 변경 후** | ✅ 필수 |
| **관리자가 강제 로그아웃** | ✅ 필수 (재로그인 시) |
**구현 방식**:
- DB: `users.last_email_verified_at` 컬럼 추가
- 로그인 시점에 "마지막 인증이 3개월 이상 지났는가?" 체크
- 맞으면 이메일 인증 강제
---
## 5. 세션 관리 정책
### 5.1 세션 유지 시간
| 시나리오 | 유지 시간 | 이유 |
|---------|---------|------|
| **기본 세션 유지** | 12시간 | 대용량 파일 업로드 고려 (최대 20GB) |
| **활동 중인 경우** | 연장 | 파일 전송/처리 중일 때는 타임아웃 제외 (마지막 요청으로부터 4시간) |
| **로그인 유지 옵션** | 최대 30일 | (선택사항) 차후 구현 검토 |
### 5.2 활동 감지 로직
```
활동으로 간주하는 API 엔드포인트:
- 파일 업로드/다운로드
- 프로젝트 저장
- 데이터 처리 요청
- 대시보드/페이지 조회
세션 연장 방식:
- 각 API 호출 시 last_activity_at 컬럼 업데이트
- 만료 판정:
if (current_time - last_activity_at > 4시간)
→ 세션 자동 만료
else if (current_time - created_at > 12시간)
→ 세션 자동 만료
```
### 5.3 세션 만료 처리
- ✅ 프론트엔드: 만료 5분 전 알림 ("5분 후 로그아웃됩니다")
- ✅ 백엔드: 만료 후 모든 요청 거부 (401 Unauthorized)
- ✅ 재로그인 필요
---
## 6. 비밀번호 정책 (Google 기준 참고)
### 6.1 비밀번호 입력 요구사항
| 요구사항 | 기준 |
|---------|------|
| **최소 길이** | 8자 이상 |
| **대문자** | A-Z 최소 1개 (필수 아님, 권장) |
| **소문자** | a-z 최소 1개 (필수 아님, 권장) |
| **숫자** | 0-9 최소 1개 (필수 아님, 권장) |
| **특수문자** | !@#$%^&* 등 (필수 아님, 권장) |
**정책**: Google과 유사하게 "권장이지만 강제하지 않음"
- 이유: 강제 정책은 사용자 이탈 증가
### 6.2 비밀번호 저장 (DB)
- ❌ 평문 저장 절대 금지
- ✅ bcrypt 또는 PBKDF2 해시 저장
- ✅ Salt 포함 (bcrypt 내장)
- 해시 라운드: 12라운드 이상
### 6.3 비밀번호 변경
- 이전 비밀번호와 동일하면 거부
- 변경 시 "모든 기기 로그아웃" 옵션 제공 (선택사항)
- 마스터의 경우: 보안상 권장
---
## 7. 로그인 이력 및 감시 (Audit Log)
### 7.1 기록할 정보
**로그인 성공**:
- `user_id`
- `email`
- `login_timestamp`
- `user_agent` (브라우저명, OS 정보만 - 기기 ID 아님)
- `last_activity_at` (업데이트 시작 시점)
**로그인 실패**:
- `email` (계정 존재 여부 관계없이)
- `failed_timestamp`
- `failure_reason` (계정없음 / 비밀번호틀림 / 이메일인증실패 / 계정비활성 등)
- `user_agent`
**프로그램 사용 이력**:
- `user_id`
- `action_type` (파일업로드 / 프로젝트저장 / 데이터처리 등)
- `action_timestamp`
- `resource_id` (프로젝트ID, 파일ID 등)
- `status` (성공/실패)
### 7.2 IP 주소 수집 (법적 고려)
**❌ 수집하지 않음 (권장)**
- 이유: 국내 개인정보보호법상 개인정보이며, 별도 동의 + 암호화 필수
- 법적 리스크 회피
**만약 수집한다면**:
- ✅ 회원가입 약관에 명시 ("보안 목적으로 로그인 IP 기록")
- ✅ 개인정보 처리방침에 명시 (보관기간: 3개월)
- ✅ DB에 암호화하여 저장 (평문 금지)
- ✅ 마스터/시스템관리자만 조회 가능
**현재 권장**: IP 수집 제외, User-Agent(브라우저/OS)만 저장
### 7.3 데이터 보관 기간
| 로그 타입 | 보관 기간 | 자동 삭제 |
|----------|---------|---------|
| 로그인 성공 | 1년 | ✅ 자동 삭제 |
| 로그인 실패 | 6개월 | ✅ 자동 삭제 |
| 프로그램 사용 | 2년 | ✅ 자동 삭제 |
| 삭제된 사용자 로그 | 법적 보유 기간 후 삭제 | ✅ 자동 삭제 |
---
## 8. 시스템관리자 대시보드
### 8.1 기능
#### 📊 시스템 모니터링
- 활성 사용자 수 (실시간)
- 활성 회사 수
- 일일/주간/월간 활동 통계 그래프
#### 💾 리소스 모니터링
- **하드웨어**
- CPU 사용률 (%)
- 메모리 사용률 (%)
- 디스크 사용량 (GB)
- 활성 프로세스 수
- **소프트웨어**
- 일일 API 호출 수
- 업로드된 총 파일 크기 (GB)
- 다운로드 횟수
- 동시 접속자 수
#### 👥 회사 관리
- 회사 목록 조회 (가입일, 상태, 사용자 수)
- 회사 구독 상태 확인
- 마스터 정보 확인 및 변경
- 회사 삭제 (강제)
#### 👤 사용자 관리
- 전체 사용자 목록
- 사용자 상태 변경 (ACTIVE / INACTIVE / REJECTED)
- 비밀번호 초기화 (관리자용)
- 사용자 로그인 이력 조회
#### 📧 Q&A / 요청사항 관리
- 회사 메일로 수신된 Q&A/요청 목록
- 상태 관리 (NEW / REVIEWED / RESOLVED / DELETED)
- 요청 내용 검토 및 수정
- 요청 삭제 및 히스토리 관리
#### ⚠️ 에러/경고 로그
- 시스템 에러 로그 조회
- 비정상 로그인 시도 (같은 계정 5회 이상 실패)
- 대역폭 초과 경고
- 데이터베이스 에러
---
## 9. 보안 방어 로직
### 9.1 무차별 대입(Brute Force) 공격 방지
```
로그인 실패 5회 이상 → 계정 15분 잠금
- 로그인 실패 횟수 저장: login_failures (컬럼)
- 마지막 실패 시간: last_failed_at (컬럼)
- 15분 후 자동 해제
시스템관리자에게 알림 (캡파 or SMS)
- 5회 실패 시 시스템 관리자 메일 알림
- 10회 이상 실패 시 경고 레벨 상향
```
### 9.2 동시 로그인 처리 (개인별 계정 기준)
- ✅ 동시 로그인 허용
- 이유: 개인별 계정이므로 가정용 + 업무 기기 동시 사용 고려
- 다만, 의심 로그인 시 알림 (예: 다른 국가/지역에서 동시 접속)
### 9.3 세션 토큰 보안 (확정: Secure HttpOnly 세션 쿠키)
**선택**: Secure HttpOnly 세션 쿠키 (JWT 아님)
- **이유**: 모놀리식 구조 + 중소규모 + 보안 최우선 + 세션 추적 필요
**쿠키 설정**:
-**쿠키 이름**: `session_id`
-**유효 시간**: 12시간
-**Secure 플래그**: true (HTTPS만 전송)
-**HttpOnly 플래그**: true (JS 접근 불가, XSS 방지)
-**SameSite**: Lax (CSRF 방지)
**세션 저장소**: MariaDB `sessions` 테이블
```sql
CREATE TABLE sessions (
id VARCHAR(255) PRIMARY KEY,
user_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP NOT NULL,
last_activity_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
```
---
## 10. 이메일 발송 인프라
### 10.1 발송해야 할 메일 유형
| 메일 타입 | 수신자 | 발송 타이밍 | 중요도 |
|----------|--------|-----------|------|
| **가입 확인 메일** | 신규 가입자 | 가입 완료 직후 | 🔴 필수 |
| **OTP 인증 코드** | 로그인/재인증 자 | 로그인 시도 직후 | 🔴 필수 |
| **팀원 승인 요청** | 마스터 | 팀원이 가입 신청 시 | 🔴 필수 |
| **가입 승인/거부 결과** | 팀원 | 마스터 승인/거부 직후 | 🟠 중요 |
| **3개월 재인증 알림** | 모든 사용자 | 3개월마다 정기적 | 🟠 중요 |
| **세션 만료 알림** | 활동 중인 사용자 | 만료 5분 전 | 🟡 알림 |
| **비밀번호 변경 알림** | 해당 사용자 | 변경 직후 | 🟡 알림 |
| **Q&A 답변 회신** | 문의자 | 시스템 관리자 처리 후 | 🟡 알림 |
### 10.2 메일 발송 서비스
**권장 선택**: AWS SES, SendGrid, 또는 한국 국내 서비스 (Solapi, Stibee)
**이유**:
- 높은 배송 성공률 (99% 이상)
- 빠른 발송 속도 (즉시~1초)
- 스팸 필터 회피 (신뢰도 높음)
- API 기반 통합 용이
**구현 방식**:
- FastAPI 백엔드에서 비동기 작업 큐 (Celery + Redis)
- 실패한 메일은 재시도 로직 (최대 3회)
---
## 11. DB 스키마 (최소 필수 테이블)
### 11.1 companies 테이블
```sql
CREATE TABLE companies (
id INT AUTO_INCREMENT PRIMARY KEY,
company_name VARCHAR(255) NOT NULL,
company_code VARCHAR(20) UNIQUE NOT NULL, -- 초대코드 역할
business_number VARCHAR(20),
phone_number VARCHAR(20),
address VARCHAR(500),
representative_name VARCHAR(100),
master_user_id INT NOT NULL,
status ENUM('ACTIVE', 'INACTIVE', 'SUSPENDED') DEFAULT 'ACTIVE',
subscription_status ENUM('FREE', 'TRIAL', 'PAID', 'EXPIRED') DEFAULT 'FREE',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (master_user_id) REFERENCES users(id)
);
```
### 11.2 users 테이블
```sql
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
company_id INT,
is_master BOOLEAN DEFAULT FALSE,
status ENUM('PENDING', 'ACTIVE', 'INACTIVE', 'REJECTED') DEFAULT 'PENDING',
last_email_verified_at TIMESTAMP,
login_failures INT DEFAULT 0, -- 실패 횟수
last_failed_at TIMESTAMP, -- 마지막 실패 시간
account_locked_until TIMESTAMP, -- 계정 잠금 해제 시간
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (company_id) REFERENCES companies(id)
);
```
### 11.3 join_requests 테이블
```sql
CREATE TABLE join_requests (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
company_id INT NOT NULL,
status ENUM('PENDING', 'APPROVED', 'REJECTED') DEFAULT 'PENDING',
requested_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
reviewed_at TIMESTAMP,
reviewed_by INT, -- 마스터 user_id
review_comment TEXT,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (company_id) REFERENCES companies(id),
FOREIGN KEY (reviewed_by) REFERENCES users(id)
);
```
### 11.4 login_logs 테이블
```sql
CREATE TABLE login_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT,
email VARCHAR(255), -- 실패 시 user_id가 NULL일 수 있음
status ENUM('SUCCESS', 'FAILURE') NOT NULL,
failure_reason VARCHAR(100),
user_agent TEXT, -- 브라우저, OS 정보만
login_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
INDEX (user_id),
INDEX (login_at)
);
```
### 11.5 activity_logs 테이블
```sql
CREATE TABLE activity_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
action_type ENUM('FILE_UPLOAD', 'FILE_DOWNLOAD', 'PROJECT_SAVE', 'DATA_PROCESS', 'PAGE_VIEW'),
action_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
resource_id VARCHAR(255), -- 프로젝트/파일 ID
status ENUM('SUCCESS', 'FAILURE') DEFAULT 'SUCCESS',
details JSON, -- 추가 정보
FOREIGN KEY (user_id) REFERENCES users(id),
INDEX (user_id),
INDEX (action_at)
);
```
### 11.6 system_admin_logs 테이블
```sql
CREATE TABLE system_admin_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
admin_user_id INT NOT NULL,
action_type ENUM('USER_DELETE', 'COMPANY_DELETE', 'QA_MODIFY', 'QA_DELETE', 'RESET_PASSWORD'),
target_id INT, -- 대상 사용자/회사 ID
action_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
details JSON,
FOREIGN KEY (admin_user_id) REFERENCES users(id),
INDEX (admin_user_id),
INDEX (action_at)
);
```
### 11.7 support_requests 테이블 (Q&A)
```sql
CREATE TABLE support_requests (
id INT AUTO_INCREMENT PRIMARY KEY,
company_id INT,
user_email VARCHAR(255),
subject VARCHAR(255) NOT NULL,
message TEXT NOT NULL,
status ENUM('NEW', 'REVIEWED', 'RESOLVED', 'DELETED') DEFAULT 'NEW',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
reviewed_at TIMESTAMP,
reviewed_by INT, -- 시스템 관리자 user_id
review_notes TEXT,
FOREIGN KEY (company_id) REFERENCES companies(id),
FOREIGN KEY (reviewed_by) REFERENCES users(id),
INDEX (status),
INDEX (created_at)
);
```
---
## 12. 구현 우선순위 (Phase)
### Phase 1 (필수): 기본 인증 및 회원가입
- [ ] 회원가입 로직 (이메일 OTP 포함)
- [ ] 마스터 회사 정보 입력
- [ ] 로그인 로직 (비밀번호 + 이메일 OTP)
- [ ] 세션/토큰 관리
- [ ] 사용자 상태 관리 (ACTIVE/INACTIVE)
### Phase 2 (필수): 팀원 가입 승인제
- [ ] 팀원 가입 신청 기능
- [ ] 가입 요청 알림 메일 (마스터 수신)
- [ ] 마스터 승인/거부 대시보드
- [ ] 승인/거부 결과 메일 (팀원 수신)
### Phase 3 (중요): 보안 강화
- [ ] 3개월 주기 재인증 로직
- [ ] 로그인 이력 기록 (login_logs)
- [ ] 무차별 대입 공격 방지 (5회 실패 잠금)
- [ ] 활동 로그 기록 (activity_logs)
### Phase 4 (중요): 마스터 관리 기능
- [ ] 팀원 목록 조회
- [ ] 팀원 강제 내보내기 (status = INACTIVE)
- [ ] 마스터 대시보드 (회사 통계)
### Phase 5 (고급): 시스템관리자 포털
- [ ] 관리자 대시보드
- [ ] 회사/사용자 관리
- [ ] Q&A 관리
- [ ] 리소스 모니터링
- [ ] 에러 로그 모니터링
---
## 13. 주요 검토 사항
### 13.1 이메일 발송 인프라
- [ ] SMTP 서버 선정 (AWS SES / SendGrid / Solapi)
- [ ] 테스트: 가입 메일 발송 성공률 확인
- [ ] 스팸 필터 회피 전략 (SPF, DKIM, DMARC 설정)
### 13.2 비밀번호 해싱 라이브러리
- [ ] Python bcrypt 또는 argon2 설정
- [ ] 라운드 수 결정 (권장: 12 이상)
### 13.3 JWT vs 세션 쿠키
- [ ] 선택: Secure HttpOnly 쿠키 (권장, CSRF/XSS 방어)
- [ ] 토큰 만료 시간 설정
### 13.4 개인정보 처리방침 작성
- [ ] 로그인 이력 수집 목적 명시
- [ ] 보관 기간 명시
- [ ] 사용자 동의 형식 결정
---
## 14. 다음 단계
1.**이 계획서 검토** (당신의 피드백)
2. 📝 **세부 기술 사양 작성** (.agent/backend.md 업데이트)
3. 💻 **Phase 1 코드 구현 시작** (회원가입 + 로그인)
4. 🧪 **단위 테스트 작성**
5. 🎨 **프론트엔드 UI/UX 설계** (.agent/frontend.md)
6. 📊 **통합 테스트 및 검증**
---
**작성일**: 2026-07-07
**담당자**: 시스템 설계 팀
**상태**: 초안 (v1.0)