Tr_Code/SMTP_SETUP.md

12 KiB

SMTP 이메일 발신 시스템 설정 가이드

이메일 인증 및 비밀번호 재설정을 위한 SMTP 서버 설정 방법입니다.

📋 목차


방법 1: Python Flask-Mail (권장)

Flask 앱에 직접 통합하는 가장 간단한 방법입니다.

1-1. 설치

cd /home/ckh08045/Tr_Code
source bin/activate
pip install Flask-Mail
pip freeze > requirements.txt

1-2. .env 설정

# 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에 추가

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 라우트 수정

# 재설정 토큰 생성 후
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 설정

# 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. 실행

docker-compose up -d mailhog

2-3. .env 설정

# 개발 환경
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 설정

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 설정

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통/일

# .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통/일 제한)

# .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

가격: 무료 (제한 있음)

# .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통

# .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 설치 및 설정

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) 설치

cd /home/ckh08045/Tr_Code

# docker-compose.yml 생성 또는 수정
docker-compose up -d mailhog

# 웹 UI 접속
echo "MailHog UI: http://192.168.0.180:8025"

📝 이메일 템플릿 예시

비밀번호 재설정

<!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>

회원가입 인증

<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 연결 실패

# 포트 확인
telnet smtp.gmail.com 587

# 방화벽 확인
sudo ufw allow 587/tcp

MailHog 접속 안됨

# 컨테이너 상태 확인
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 (운영)