528 lines
12 KiB
Markdown
528 lines
12 KiB
Markdown
# SMTP 이메일 발신 시스템 설정 가이드
|
|
|
|
이메일 인증 및 비밀번호 재설정을 위한 SMTP 서버 설정 방법입니다.
|
|
|
|
## 📋 목차
|
|
|
|
- [방법 1: Python Flask-Mail (권장)](#방법-1-python-flask-mail-권장)
|
|
- [방법 2: Docker MailHog (개발용)](#방법-2-docker-mailhog-개발용)
|
|
- [방법 3: Docker Postfix (운영용)](#방법-3-docker-postfix-운영용)
|
|
- [방법 4: 외부 SMTP 서비스](#방법-4-외부-smtp-서비스)
|
|
|
|
---
|
|
|
|
## 방법 1: Python Flask-Mail (권장) ⭐
|
|
|
|
Flask 앱에 직접 통합하는 가장 간단한 방법입니다.
|
|
|
|
### 1-1. 설치
|
|
|
|
```bash
|
|
cd /home/ckh08045/Tr_Code
|
|
source bin/activate
|
|
pip install Flask-Mail
|
|
pip freeze > requirements.txt
|
|
```
|
|
|
|
### 1-2. .env 설정
|
|
|
|
```bash
|
|
# SMTP 설정 추가
|
|
MAIL_SERVER=smtp.gmail.com
|
|
MAIL_PORT=587
|
|
MAIL_USE_TLS=True
|
|
MAIL_USE_SSL=False
|
|
MAIL_USERNAME=your-email@gmail.com
|
|
MAIL_PASSWORD=your-app-password
|
|
MAIL_DEFAULT_SENDER=your-email@gmail.com
|
|
```
|
|
|
|
**Gmail 앱 비밀번호 생성:**
|
|
1. Google 계정 → 보안
|
|
2. 2단계 인증 활성화
|
|
3. 앱 비밀번호 생성
|
|
4. `.env`에 입력
|
|
|
|
### 1-3. app.py에 추가
|
|
|
|
```python
|
|
from flask_mail import Mail, Message
|
|
|
|
# Flask-Mail 설정
|
|
app.config['MAIL_SERVER'] = os.environ.get('MAIL_SERVER', 'smtp.gmail.com')
|
|
app.config['MAIL_PORT'] = int(os.environ.get('MAIL_PORT', 587))
|
|
app.config['MAIL_USE_TLS'] = os.environ.get('MAIL_USE_TLS', 'True').lower() == 'true'
|
|
app.config['MAIL_USE_SSL'] = os.environ.get('MAIL_USE_SSL', 'False').lower() == 'true'
|
|
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME')
|
|
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD')
|
|
app.config['MAIL_DEFAULT_SENDER'] = os.environ.get('MAIL_DEFAULT_SENDER')
|
|
|
|
mail = Mail(app)
|
|
|
|
def send_reset_email(to_email, reset_token, employee_id):
|
|
"""비밀번호 재설정 이메일 전송"""
|
|
reset_url = url_for('reset_password', token=reset_token, _external=True)
|
|
|
|
msg = Message(
|
|
subject='[1호선 고장코드] 비밀번호 재설정',
|
|
recipients=[to_email]
|
|
)
|
|
|
|
msg.body = f"""
|
|
안녕하세요,
|
|
|
|
비밀번호 재설정을 요청하셨습니다.
|
|
|
|
사번: {employee_id}
|
|
|
|
아래 링크를 클릭하여 비밀번호를 재설정하세요:
|
|
{reset_url}
|
|
|
|
이 링크는 1시간 동안 유효합니다.
|
|
|
|
본인이 요청하지 않았다면 이 이메일을 무시하세요.
|
|
|
|
---
|
|
부산교통공사 1호선 차량 고장코드 시스템
|
|
"""
|
|
|
|
msg.html = f"""
|
|
<html>
|
|
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
|
|
<div style="max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #ddd; border-radius: 5px;">
|
|
<h2 style="color: #0066cc;">비밀번호 재설정</h2>
|
|
<p>안녕하세요,</p>
|
|
<p>비밀번호 재설정을 요청하셨습니다.</p>
|
|
<p><strong>사번:</strong> {employee_id}</p>
|
|
<p>아래 버튼을 클릭하여 비밀번호를 재설정하세요:</p>
|
|
<div style="text-align: center; margin: 30px 0;">
|
|
<a href="{reset_url}"
|
|
style="background-color: #0066cc; color: white; padding: 12px 30px;
|
|
text-decoration: none; border-radius: 5px; display: inline-block;">
|
|
비밀번호 재설정
|
|
</a>
|
|
</div>
|
|
<p style="color: #666; font-size: 0.9em;">
|
|
이 링크는 <strong>1시간</strong> 동안 유효합니다.
|
|
</p>
|
|
<p style="color: #666; font-size: 0.9em;">
|
|
본인이 요청하지 않았다면 이 이메일을 무시하세요.
|
|
</p>
|
|
<hr style="margin: 30px 0; border: none; border-top: 1px solid #eee;">
|
|
<p style="color: #999; font-size: 0.8em; text-align: center;">
|
|
부산교통공사 1호선 차량 고장코드 시스템
|
|
</p>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
try:
|
|
mail.send(msg)
|
|
return True, "이메일이 전송되었습니다."
|
|
except Exception as e:
|
|
print(f"이메일 전송 실패: {str(e)}")
|
|
return False, f"이메일 전송에 실패했습니다: {str(e)}"
|
|
```
|
|
|
|
### 1-4. forgot_password 라우트 수정
|
|
|
|
```python
|
|
# 재설정 토큰 생성 후
|
|
reset_token = secrets.token_urlsafe(32)
|
|
session[f"reset_token_{reset_token}"] = {
|
|
"employee_id": employee_id,
|
|
"email": user["email"],
|
|
"expires": (datetime.now() + timedelta(hours=1)).isoformat()
|
|
}
|
|
|
|
# 이메일 전송
|
|
success, message = send_reset_email(user["email"], reset_token, employee_id)
|
|
|
|
if success:
|
|
return render_template(
|
|
"forgot_password.html",
|
|
app_name=APP_NAME,
|
|
success="등록된 이메일로 비밀번호 재설정 링크를 전송했습니다."
|
|
)
|
|
else:
|
|
return render_template(
|
|
"forgot_password.html",
|
|
app_name=APP_NAME,
|
|
error=message
|
|
)
|
|
```
|
|
|
|
### 1-5. 장단점
|
|
|
|
**장점:**
|
|
- ✅ 설치/설정 간단
|
|
- ✅ Flask 앱과 완벽 통합
|
|
- ✅ 추가 서버 불필요
|
|
- ✅ Gmail, Naver 등 기존 계정 사용 가능
|
|
|
|
**단점:**
|
|
- ❌ 메일 서버 의존성
|
|
- ❌ 발신 제한 (Gmail: 하루 500통)
|
|
|
|
---
|
|
|
|
## 방법 2: Docker MailHog (개발용) 🧪
|
|
|
|
개발/테스트 환경에서 실제 이메일 전송 없이 테스트하는 방법입니다.
|
|
|
|
### 2-1. Docker Compose 설정
|
|
|
|
```yaml
|
|
# docker-compose.yml (기존 Supabase에 추가)
|
|
services:
|
|
mailhog:
|
|
image: mailhog/mailhog:latest
|
|
container_name: mailhog
|
|
ports:
|
|
- "1025:1025" # SMTP
|
|
- "8025:8025" # Web UI
|
|
networks:
|
|
- tr_code_network
|
|
restart: unless-stopped
|
|
|
|
networks:
|
|
tr_code_network:
|
|
external: true
|
|
```
|
|
|
|
### 2-2. 실행
|
|
|
|
```bash
|
|
docker-compose up -d mailhog
|
|
```
|
|
|
|
### 2-3. .env 설정
|
|
|
|
```bash
|
|
# 개발 환경
|
|
MAIL_SERVER=localhost
|
|
MAIL_PORT=1025
|
|
MAIL_USE_TLS=False
|
|
MAIL_USE_SSL=False
|
|
MAIL_USERNAME=
|
|
MAIL_PASSWORD=
|
|
MAIL_DEFAULT_SENDER=noreply@tr-code.local
|
|
```
|
|
|
|
### 2-4. 이메일 확인
|
|
|
|
브라우저에서 `http://192.168.0.180:8025` 접속
|
|
|
|
- 전송된 모든 이메일 확인 가능
|
|
- 실제 전송은 안 됨 (개발용)
|
|
|
|
### 2-5. 장단점
|
|
|
|
**장점:**
|
|
- ✅ 실제 이메일 전송 없이 테스트
|
|
- ✅ 웹 UI로 쉽게 확인
|
|
- ✅ 설정 간단
|
|
|
|
**단점:**
|
|
- ❌ 개발용으로만 사용 가능
|
|
- ❌ 실제 이메일 전송 안 됨
|
|
|
|
---
|
|
|
|
## 방법 3: Docker Postfix (운영용) 🚀
|
|
|
|
자체 SMTP 서버를 운영하는 방법입니다.
|
|
|
|
### 3-1. Docker Compose 설정
|
|
|
|
```yaml
|
|
services:
|
|
postfix:
|
|
image: boky/postfix:latest
|
|
container_name: postfix
|
|
environment:
|
|
- ALLOWED_SENDER_DOMAINS=humetro.busan.kr
|
|
- HOSTNAME=mail.tr-code.local
|
|
ports:
|
|
- "25:25" # SMTP
|
|
- "587:587" # SMTP with TLS
|
|
volumes:
|
|
- ./postfix-data:/var/spool/postfix
|
|
networks:
|
|
- tr_code_network
|
|
restart: unless-stopped
|
|
```
|
|
|
|
### 3-2. .env 설정
|
|
|
|
```bash
|
|
MAIL_SERVER=localhost
|
|
MAIL_PORT=587
|
|
MAIL_USE_TLS=True
|
|
MAIL_USE_SSL=False
|
|
MAIL_USERNAME=
|
|
MAIL_PASSWORD=
|
|
MAIL_DEFAULT_SENDER=noreply@humetro.busan.kr
|
|
```
|
|
|
|
### 3-3. DNS 설정 (필수)
|
|
|
|
도메인 DNS에 다음 레코드 추가:
|
|
|
|
```
|
|
MX @ 10 mail.yourdomain.com
|
|
A mail 192.168.0.180
|
|
TXT @ "v=spf1 ip4:192.168.0.180 ~all"
|
|
```
|
|
|
|
### 3-4. 장단점
|
|
|
|
**장점:**
|
|
- ✅ 완전한 제어
|
|
- ✅ 발신 제한 없음
|
|
- ✅ 커스터마이징 가능
|
|
|
|
**단점:**
|
|
- ❌ 설정 복잡
|
|
- ❌ 스팸 필터링 이슈
|
|
- ❌ DNS/IP 평판 관리 필요
|
|
|
|
---
|
|
|
|
## 방법 4: 외부 SMTP 서비스 📧
|
|
|
|
전문 이메일 서비스를 사용하는 방법입니다.
|
|
|
|
### 4-1. SendGrid (추천)
|
|
|
|
**가격:** 무료 티어 100통/일
|
|
|
|
```bash
|
|
# .env
|
|
MAIL_SERVER=smtp.sendgrid.net
|
|
MAIL_PORT=587
|
|
MAIL_USE_TLS=True
|
|
MAIL_USERNAME=apikey
|
|
MAIL_PASSWORD=your_sendgrid_api_key
|
|
MAIL_DEFAULT_SENDER=noreply@yourdomain.com
|
|
```
|
|
|
|
**장점:**
|
|
- ✅ 높은 전달률
|
|
- ✅ 통계/분석 제공
|
|
- ✅ 무료 티어 제공
|
|
|
|
### 4-2. Gmail
|
|
|
|
**가격:** 무료 (500통/일 제한)
|
|
|
|
```bash
|
|
# .env
|
|
MAIL_SERVER=smtp.gmail.com
|
|
MAIL_PORT=587
|
|
MAIL_USE_TLS=True
|
|
MAIL_USERNAME=your-email@gmail.com
|
|
MAIL_PASSWORD=your-app-password
|
|
```
|
|
|
|
### 4-3. Naver
|
|
|
|
**가격:** 무료 (제한 있음)
|
|
|
|
```bash
|
|
# .env
|
|
MAIL_SERVER=smtp.naver.com
|
|
MAIL_PORT=587
|
|
MAIL_USE_TLS=True
|
|
MAIL_USERNAME=your-id@naver.com
|
|
MAIL_PASSWORD=your-password
|
|
```
|
|
|
|
### 4-4. AWS SES
|
|
|
|
**가격:** 0.10USD / 1000통
|
|
|
|
```bash
|
|
# .env
|
|
MAIL_SERVER=email-smtp.ap-northeast-2.amazonaws.com
|
|
MAIL_PORT=587
|
|
MAIL_USE_TLS=True
|
|
MAIL_USERNAME=your_access_key
|
|
MAIL_PASSWORD=your_secret_key
|
|
```
|
|
|
|
---
|
|
|
|
## 💡 권장 사항
|
|
|
|
### 개발/테스트 환경
|
|
```
|
|
MailHog (Docker) → 가장 간단
|
|
```
|
|
|
|
### 운영 환경 (소규모)
|
|
```
|
|
Gmail + Flask-Mail → 빠른 구축
|
|
```
|
|
|
|
### 운영 환경 (대규모)
|
|
```
|
|
SendGrid 또는 AWS SES → 안정성
|
|
```
|
|
|
|
### 내부망 전용
|
|
```
|
|
Postfix (Docker) → 완전한 제어
|
|
```
|
|
|
|
---
|
|
|
|
## 🔧 설치 스크립트
|
|
|
|
### Flask-Mail 설치 및 설정
|
|
|
|
```bash
|
|
cd /home/ckh08045/Tr_Code
|
|
source bin/activate
|
|
pip install Flask-Mail
|
|
pip freeze > requirements.txt
|
|
|
|
# .env에 설정 추가
|
|
cat >> .env << 'EOF'
|
|
|
|
# SMTP 설정 (Gmail 예시)
|
|
MAIL_SERVER=smtp.gmail.com
|
|
MAIL_PORT=587
|
|
MAIL_USE_TLS=True
|
|
MAIL_USE_SSL=False
|
|
MAIL_USERNAME=your-email@gmail.com
|
|
MAIL_PASSWORD=your-app-password
|
|
MAIL_DEFAULT_SENDER=your-email@gmail.com
|
|
EOF
|
|
|
|
# 서비스 재시작
|
|
sudo systemctl restart Tr_Code
|
|
```
|
|
|
|
### MailHog (Docker) 설치
|
|
|
|
```bash
|
|
cd /home/ckh08045/Tr_Code
|
|
|
|
# docker-compose.yml 생성 또는 수정
|
|
docker-compose up -d mailhog
|
|
|
|
# 웹 UI 접속
|
|
echo "MailHog UI: http://192.168.0.180:8025"
|
|
```
|
|
|
|
---
|
|
|
|
## 📝 이메일 템플릿 예시
|
|
|
|
### 비밀번호 재설정
|
|
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<style>
|
|
body { font-family: Arial, sans-serif; }
|
|
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
.button {
|
|
background-color: #0066cc;
|
|
color: white;
|
|
padding: 12px 30px;
|
|
text-decoration: none;
|
|
border-radius: 5px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h2>비밀번호 재설정</h2>
|
|
<p>안녕하세요, {{ name }}님</p>
|
|
<p>비밀번호 재설정을 요청하셨습니다.</p>
|
|
<a href="{{ reset_url }}" class="button">비밀번호 재설정</a>
|
|
<p>이 링크는 1시간 동안 유효합니다.</p>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
### 회원가입 인증
|
|
|
|
```html
|
|
<div class="container">
|
|
<h2>회원가입 인증</h2>
|
|
<p>환영합니다, {{ name }}님!</p>
|
|
<p>아래 버튼을 클릭하여 이메일을 인증하세요:</p>
|
|
<a href="{{ verify_url }}" class="button">이메일 인증</a>
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## 🐛 문제 해결
|
|
|
|
### Gmail 전송 실패
|
|
|
|
**원인:** 앱 비밀번호 미설정
|
|
**해결:**
|
|
1. Google 계정 → 보안
|
|
2. 2단계 인증 활성화
|
|
3. 앱 비밀번호 생성
|
|
|
|
### SMTP 연결 실패
|
|
|
|
```bash
|
|
# 포트 확인
|
|
telnet smtp.gmail.com 587
|
|
|
|
# 방화벽 확인
|
|
sudo ufw allow 587/tcp
|
|
```
|
|
|
|
### MailHog 접속 안됨
|
|
|
|
```bash
|
|
# 컨테이너 상태 확인
|
|
docker ps | grep mailhog
|
|
|
|
# 로그 확인
|
|
docker logs mailhog
|
|
|
|
# 재시작
|
|
docker restart mailhog
|
|
```
|
|
|
|
---
|
|
|
|
## 🔒 보안 고려사항
|
|
|
|
1. **환경변수 사용**: 비밀번호를 코드에 하드코딩하지 마세요
|
|
2. **TLS 사용**: 암호화된 연결 사용
|
|
3. **Rate Limiting**: 과도한 이메일 전송 방지
|
|
4. **토큰 만료**: 재설정 링크에 시간 제한 설정
|
|
5. **.env 보호**: `.gitignore`에 추가
|
|
|
|
---
|
|
|
|
## 📊 비교표
|
|
|
|
| 방법 | 설정 난이도 | 비용 | 전달률 | 제어 | 추천 |
|
|
|------|-----------|------|--------|------|------|
|
|
| Flask-Mail + Gmail | ⭐ 쉬움 | 무료 | 높음 | 낮음 | 소규모 |
|
|
| MailHog | ⭐ 쉬움 | 무료 | N/A | 높음 | 개발 |
|
|
| Postfix | ⭐⭐⭐ 어려움 | 무료 | 낮음 | 높음 | 내부망 |
|
|
| SendGrid | ⭐⭐ 보통 | 무료/유료 | 높음 | 보통 | 운영 |
|
|
| AWS SES | ⭐⭐ 보통 | 유료 | 높음 | 보통 | 대규모 |
|
|
|
|
---
|
|
|
|
**작성일**: 2025-10-15
|
|
**권장 방법**: Flask-Mail + Gmail (개발), SendGrid (운영)
|
|
|