# 이메일 코드 인증 가이드
## 📧 개요
링크 클릭 방식 대신 **6자리 숫자 코드 입력 방식**의 이메일 인증을 사용합니다. 이는 스팸/피싱 의심을 줄이고 더 안전한 인증 경험을 제공합니다.
## ✨ 주요 기능
### 1. 회원가입 이메일 인증
- 회원가입 시 입력한 이메일로 6자리 인증 코드 발송
- 코드 입력 및 검증 후 회원가입 완료
- 코드 유효 시간: 5분
- 최대 5회 시도 가능
### 2. 비밀번호 재설정 이메일 인증
- 비밀번호 찾기 시 이메일로 6자리 인증 코드 발송
- 코드 검증 후 비밀번호 재설정 페이지로 이동
- 코드 유효 시간: 5분
- 재설정 페이지 접근 유효 시간: 10분
### 3. 코드 재전송
- 코드가 만료되거나 받지 못한 경우 재전송 가능
- 재전송은 첫 발송 후 30초 경과 시 가능
## 🎨 UI/UX 특징
### 1. 6자리 코드 입력
- 각 자리별 개별 입력 필드
- 자동으로 다음 칸으로 포커스 이동
- 붙여넣기 지원 (6자리 숫자 자동 분배)
- 백스페이스로 이전 칸으로 이동
### 2. 실시간 타이머
- 남은 시간을 시각적으로 표시 (분:초)
- 시간 만료 시 경고 메시지
- 재전송 버튼 활성화 타이밍 표시
### 3. 에러 처리
- 잘못된 코드 입력 시 shake 애니메이션
- 남은 시도 횟수 표시
- 명확한 에러 메시지
## 🏗️ 아키텍처
### Frontend Component: `EmailCodeVerification.vue`
#### Props
```typescript
interface Props {
email: string // 인증 대상 이메일
title?: string // 제목 (기본값: '이메일 인증')
expirySeconds?: number // 만료 시간 (기본값: 300초 = 5분)
}
```
#### Events
```typescript
// 코드 검증 요청
emit('verify', code: string)
// 코드 재전송 요청
emit('resend')
```
#### 사용 예시
```vue
```
### Backend API
#### 1. 코드 발송
```
POST /api/email/send-code
Content-Type: application/json
{
"email": "user@humetro.busan.kr",
"type": "signup" // 또는 "password_reset"
}
```
**Response:**
```json
{
"success": true,
"message": "인증 코드를 이메일로 전송했습니다.",
"debug_code": "123456" // 개발 환경에서만
}
```
#### 2. 코드 검증
```
POST /api/email/verify-code
Content-Type: application/json
{
"email": "user@humetro.busan.kr",
"code": "123456",
"type": "signup" // 또는 "password_reset"
}
```
**Response (성공):**
```json
{
"success": true,
"message": "인증이 완료되었습니다."
}
```
**Response (실패):**
```json
{
"success": false,
"error": "인증 코드가 올바르지 않습니다. (남은 시도: 3회)"
}
```
## 🔐 보안 기능
### 1. 코드 생성
- 6자리 무작위 숫자 생성 (`random.choices(string.digits, k=6)`)
- 각 이메일/타입별 고유 세션 키 사용
- 코드는 세션에 암호화되어 저장
### 2. 검증 제한
- **시도 횟수 제한**: 5회까지만 시도 가능
- **시간 제한**: 5분 후 자동 만료
- **일회성**: 한 번 검증되면 코드 즉시 삭제
### 3. 도메인 제한
- `@humetro.busan.kr` 도메인만 허용
- 백엔드에서 이메일 도메인 검증
### 4. 세션 관리
```python
# 코드 저장 (5분간 유효)
session[f"email_code_{email}_{type}"] = {
"code": "123456",
"expiry": timestamp + 300,
"attempts": 0
}
# 검증 완료 표시 (10분간 유효)
session[f"email_verified_{email}_{type}"] = {
"verified_at": timestamp
}
```
## 📱 사용 흐름
### 회원가입 흐름
```mermaid
sequenceDiagram
participant U as User
participant F as Frontend
participant B as Backend
participant E as Email
U->>F: 회원가입 정보 입력
F->>B: POST /api/email/send-code
B->>B: 6자리 코드 생성
B->>E: 이메일 발송 (TODO)
B->>F: {success: true}
F->>U: 코드 입력 화면 표시
U->>F: 코드 입력
F->>B: POST /api/email/verify-code
B->>B: 코드 검증
B->>F: {success: true}
F->>B: POST /auth/signup
B->>F: 회원가입 완료
F->>U: 로그인 페이지로 이동
```
### 비밀번호 재설정 흐름
```mermaid
sequenceDiagram
participant U as User
participant F as Frontend
participant B as Backend
U->>F: 이메일 입력
F->>B: POST /api/email/send-code (type: password_reset)
B->>F: {success: true}
F->>U: 코드 입력 화면
U->>F: 코드 입력
F->>B: POST /api/email/verify-code
B->>B: 세션에 인증 완료 표시
B->>F: {success: true}
F->>F: /reset-password?email=...&verified=true
U->>F: 새 비밀번호 입력
F->>B: POST /api/auth/reset-password
B->>B: 세션에서 인증 확인
B->>B: 비밀번호 업데이트
B->>F: {success: true}
F->>U: 로그인 페이지로 이동
```
## 🎯 회원가입 통합
### SignupView.vue
```vue
```
## 🔄 비밀번호 재설정 통합
### ForgotPasswordView.vue → ResetPasswordView.vue
```vue
```
```vue
```
## 🔧 개발 환경 설정
### 1. 이메일 발송 (TODO)
현재는 콘솔에만 코드를 출력하고, 개발 환경에서는 API 응답에 코드를 포함시킵니다:
```python
# 개발 환경에서만
return {
"success": True,
"message": "인증 코드를 이메일로 전송했습니다.",
"debug_code": code if app.debug else None
}
```
프로덕션에서는 실제 이메일 발송 서비스 연동 필요:
- SMTP 서버
- SendGrid / AWS SES
- Supabase Email (Docker 환경에서는 제한적)
### 2. 콘솔에서 코드 확인
백엔드 콘솔:
```
=== 이메일 인증 코드 ===
To: user@humetro.busan.kr
Code: 123456
Type: signup
Expiry: 2025-10-14 15:30:00
=======================
```
프론트엔드 콘솔:
```
=== 개발 환경 인증 코드 ===
코드: 123456
=======================
```
## 📊 에러 처리
### Frontend 에러 메시지
| 상황 | 메시지 |
|------|--------|
| 잘못된 코드 | "인증 코드가 올바르지 않습니다. (남은 시도: N회)" |
| 시도 횟수 초과 | "인증 시도 횟수를 초과했습니다. 코드를 재전송해주세요." |
| 코드 만료 | "인증 코드가 만료되었습니다. 코드를 재전송해주세요." |
| 코드 미존재 | "인증 코드가 존재하지 않거나 만료되었습니다." |
### Backend 에러 응답
```python
# 시도 횟수 초과
if stored_data["attempts"] >= 5:
session.pop(session_key, None)
return {
"success": False,
"error": "인증 시도 횟수를 초과했습니다. 코드를 재전송해주세요."
}, 400
# 만료
if datetime.now().timestamp() > stored_data["expiry"]:
session.pop(session_key, None)
return {
"success": False,
"error": "인증 코드가 만료되었습니다. 코드를 재전송해주세요."
}, 400
# 불일치
if stored_data["code"] != code:
stored_data["attempts"] += 1
session[session_key] = stored_data
return {
"success": False,
"error": f"인증 코드가 올바르지 않습니다. (남은 시도: {5 - stored_data['attempts']}회)"
}, 400
```
## 🎨 UI 커스터마이징
### 다크모드 지원
```css
:root.dark .code-digit {
background: #1a202c;
border-color: #4a5568;
color: #f7fafc;
}
:root.dark .timer {
color: #a78bfa;
}
```
### 애니메이션
```css
/* Shake 애니메이션 (에러 시) */
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
.code-digit.error {
border-color: #dc2626;
animation: shake 0.3s;
}
```
## 📝 TODO (향후 개선사항)
### 1. 이메일 발송 구현
```python
# SMTP 또는 이메일 서비스 연동
import smtplib
from email.mime.text import MIMEText
def send_verification_email(email, code):
msg = MIMEText(f"인증 코드: {code}")
msg['Subject'] = '부산교통공사 1호선 - 이메일 인증'
msg['From'] = 'noreply@humetro.busan.kr'
msg['To'] = email
# SMTP 발송...
```
### 2. 이메일 템플릿
- HTML 이메일 템플릿 디자인
- 회사 로고 및 브랜딩 추가
- 다국어 지원
### 3. 보안 강화
- Rate limiting (IP별 요청 제한)
- Captcha 추가 (무차별 대입 공격 방지)
- 로그 기록 (audit_logs)
### 4. 사용자 경험 개선
- 이메일 자동 완성
- 코드 자동 감지 (SMS/Email OTP)
- 음성 안내 (접근성)
## 🔗 참고 자료
- [OWASP Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html)
- [Email OTP Best Practices](https://www.twilio.com/docs/verify/email)
- [Flask Session Management](https://flask.palletsprojects.com/en/2.3.x/quickstart/#sessions)