# 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"""
비밀번호 재설정
안녕하세요,
비밀번호 재설정을 요청하셨습니다.
사번: {employee_id}
아래 버튼을 클릭하여 비밀번호를 재설정하세요:
이 링크는 1시간 동안 유효합니다.
본인이 요청하지 않았다면 이 이메일을 무시하세요.
부산교통공사 1호선 차량 고장코드 시스템
"""
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
비밀번호 재설정
안녕하세요, {{ name }}님
비밀번호 재설정을 요청하셨습니다.
비밀번호 재설정
이 링크는 1시간 동안 유효합니다.
```
### 회원가입 인증
```html
회원가입 인증
환영합니다, {{ name }}님!
아래 버튼을 클릭하여 이메일을 인증하세요:
이메일 인증
```
---
## 🐛 문제 해결
### 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 (운영)