Flask 문의 내역 이메일 발송 시스템 구현 with 파머

2025. 4. 23. 16:46·기술블로그

📌 개념 정리

문의 내역 이메일 발송 시스템은 사용자가 웹 폼을 통해 문의한 내용을 이메일로 보내는 시스템입니다. 이 시스템은 사용자가 웹 폼에 입력한 정보를 서버에서 처리하고, 이메일 서버를 통해 관리자가 이를 받을 수 있도록 구현됩니다. Flask-Mail 라이브러리를 사용하여 이메일을 쉽게 보내며, Flask에서 제공하는 웹 폼을 통해 사용자로부터 정보를 입력받습니다.

이 시스템은 간단한 이메일 보내기 기능뿐만 아니라, 사용자 입력값의 유효성 검증도 포함하고 있어 실제 서비스에서도 유용하게 활용될 수 있습니다.

🚦 동작 원리 및 구조

  1. 웹 폼에서 사용자 입력: 사용자는 /contact 경로에서 이름, 이메일 주소, 문의 내용을 입력합니다.
  2. 유효성 검증: 사용자가 입력한 값에 대해 유효성 검사를 진행합니다. 예를 들어, 이메일 형식이 유효한지, 모든 필드가 채워졌는지 확인합니다.
  3. 이메일 전송: 사용자의 입력값이 유효하면, Flask-Mail을 이용해 이메일을 발송합니다. 이때, contact_mail.html과 contact_mail.txt 템플릿을 사용하여 이메일 내용을 구성합니다.
  4. 성공적인 처리: 이메일 발송이 완료되면, 사용자는 완료 페이지로 리디렉션되어 "메일 전송 완료" 메시지를 확인합니다.

💻 코드 예시 및 흐름 분석

1. app.py

Flask 애플리케이션과 이메일 전송 기능을 설정합니다.

from flask import Flask, render_template, url_for, redirect, request, flash
from email_validator import validate_email, EmailNotValidError
import logging
import os
from flask_mail import Mail, Message

app = Flask(__name__)
app.config["SECRET_KEY"] = "secret"
app.logger.setLevel(logging.DEBUG)
app.logger.debug("debug")

# 이메일 서버 설정
app.config["MAIL_SERVER"] = os.environ.get("MAIL_SERVER")
app.config["MAIL_PORT"] = os.environ.get("MAIL_PORT")
app.config["MAIL_USE_TLS"] = os.environ.get("MAIL_USE_TLS")
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)

@app.route("/")
def index():
    return "메일 보내기 미니 프로젝트"

@app.route("/contact")
def contact():
    return render_template("contact.html")

@app.route("/contact/complete", methods=["GET", "POST"])
def contact_complete():
    if request.method == "POST":
        name = request.form["name"]
        email = request.form["email"]
        message = request.form["message"]
        
        # 유효성 검증
        valid = True
        if not name:
            flash("이름을 입력해줘.")
            valid = False
        if not email:
            flash("이메일을 입력해줘.")
            valid = False
            
        try:
            validate_email(email)
        except EmailNotValidError:
            flash("이메일의 유효성을 확인해줘.")
            valid = False
            
        if not message:
            flash("문의 내용을 입력해줘.")
            valid = False
            
        if not valid:
            return redirect(url_for("contact"))
        
        flash("문의해주셔서 감사합니다")
        
        # 이메일 전송
        send_email(email, "문의 내용", "contact_mail", user_name=name, description=message)
        return redirect(url_for("contact_complete"))
    return render_template("contact_complete.html")

# 이메일 전송 함수
def send_email(to, subject, template, **kwargs):
    msg = Message(subject, recipients=[to])
    msg.body = render_template(template + ".txt", **kwargs)
    msg.html = render_template(template + ".html", **kwargs)
    mail.charset = 'utf-8'
    mail.send(msg)

if __name__ == '__main__':
    app.run(debug=True)

2. contact.html

사용자로부터 이름, 이메일, 메시지를 입력받는 폼입니다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>메일 문의 폼</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
  </head>
  <body>
    <div class="contact-form-container">
      <form action="{{ url_for('contact_complete') }}" method="POST" novalidate="novalidate">
        <h2>문의하기</h2>
        {% with messages = get_flashed_messages() %}
          {% if messages %}
            <ul>
              {% for message in messages %}
                <li class="flash">{{ message }}</li>
              {% endfor %}
            </ul>
          {% endif %}
        {% endwith %}
        <label for="name">이름</label>
        <input type="text" id="name" name="name" placeholder="이름을 입력하세요">

        <label for="email">메일 주소</label>
        <input type="email" id="email" name="email" placeholder="이메일을 입력하세요">

        <label for="message">문의 내용</label>
        <textarea id="message" name="message" rows="5" placeholder="문의 내용을 입력하세요"></textarea>

        <button type="submit">메일 보내기</button>
      </form>
    </div>
  </body>
</html>

3. .env 파일구성

FLASK_APP=app.py
FLASK_ENV=development

MAIL_SERVER=smtp.gmail.com
#프로토콜 -> 통신 규악 -> 이메일을 보낼때 사용하는 규칙 = smpt / gmail.com의 서비스를 이용해서 메일을 보내겠다.

MAIL_PORT=587
MAIL_USE_TLS=True
#보안설정 활용
MAIL_USERNAME = gmail@gmail.com # 구글을 사용하기위한 이메일
MAIL_PASSWORD = mnasdfasdfasrg # 생성한 앱 비밀번호 공백없이 설정
MAIL_DEFAULT_SENDER = gmail@gmail.com # 보내는 사람의 이메일 EX)gmail@nate.com

 

4. contact_complete.html

사용자가 이메일을 성공적으로 보냈을 때 표시되는 완료 페이지입니다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>메일 전송 완료</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
  </head>
  <body>
    <div class="complete-container">
      <h2>메일 전송 완료</h2>
      {% with messages = get_flashed_messages() %}
        {% if messages %}
          <ul>
            {% for message in messages %}
              <li class="flash">{{ message }}</li>
            {% endfor %}
          </ul>
        {% endif %}
      {% endwith %}
    </div>
  </body>
</html>

5. contact_mail.html

이메일 내용의 HTML 형식입니다. 사용자가 보내는 문의 내용을 포함합니다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>문의 내용 확인</title>
    <style>
      body {
        font-family: Arial, sans-serif;
        line-height: 1.6;
        margin: 0;
        padding: 0;
        background-color: #f4f4f9;
      }
      .email-container {
        max-width: 600px;
        margin: 20px auto;
        background: #ffffff;
        border: 1px solid #ddd;
        border-radius: 8px;
        overflow: hidden;
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
      }
      .email-header {
        background: #0078d7;
        color: #ffffff;
        text-align: center;
        padding: 20px;
        font-size: 20px;
        font-weight: bold;
      }
      .email-body {
        padding: 20px;
        color: #333333;
      }
      .email-footer {
        background: #f4f4f9;
        text-align: center;
        padding: 10px;
        font-size: 12px;
        color: #888888;
      }
    </style>
  </head>
  <body>
    <div class="email-container">
      <div class="email-body">
        <p><strong>{{ user_name }}님</strong></p>
        <p>문의주셔서 감사합니다.</p>
        <p>보내주신 문의 내용은 다음과 같습니다.</p>
        <div style="background: #f9f9f9; border-left: 4px solid #0078d7; padding: 10px; margin: 15px 0;">
          <p><strong>문의 내용</strong></p>
          <p>{{ description }}</p>
        </div>
        <p>추가적인 문의 사항이 있으시면 언제든지 말씀해 주세요!</p>
      </div>
    </div>
  </body>
</html>

🧪 왜 contact_mail.txt 템플릿이 필요한가?

  • 호환성: 모든 이메일 클라이언트가 HTML을 지원하지 않기 때문에, 텍스트 형식의 이메일을 보내는 것이 중요합니다.
  • 단순한 텍스트 이메일: 텍스트 형식의 이메일은 디자인 요소 없이 내용을 간단하게 전달하는 데 유용합니다. 이는 일부 사용자가 텍스트 이메일을 선호하는 경우에 적합합니다.
  • 대체 형식 제공: Flask-Mail에서 이메일을 보낼 때, HTML 형식과 텍스트 형식 모두 제공하는 것이 일반적입니다. 이를 통해 사용자가 HTML을 지원하지 않는 환경에서도 제대로 내용을 확인할 수 있습니다.
{{ user_name }}님
문의해주셔서 감사합니다.

문의 주신 내용은 다음과 같습니다.

[문의내용]
{{ description }}

 

🧪 실전 사례

이 예제는 사용자가 contact.html 폼을 통해 문의한 내용을 이메일로 보내는 시스템입니다. 폼 제출 후, 서버에서 입력값의 유효성 검사를 수행하고, 검증된 데이터는 Flask-Mail을 통해 관리자의 이메일로 전송됩니다. 이메일은 HTML 및 텍스트 형식으로 발송되며, 사용자는 이메일을 통해 자신의 문의 내용을 확인할 수 있습니다.

 

 

🧠 고급 팁 or 자주 하는 실수

  • 보안 및 유효성 검사: 실제 환경에서는 이메일 유효성 검사를 강화하고, 서버 측에서 추가적인 보안을 구현해야 합니다. 예를 들어, 이메일 발송 시 서버에서 발송자의 이메일을 검증하는 절차를 추가할 수 있습니다.
  • 비밀번호 및 개인 정보 보호: 이메일 내용에 민감한 개인 정보가 포함될 수 있으므로, 이를 암호화하거나 보안 전송 채널(SSL)을 사용하는 것이 중요합니다.

✅ 마무리 요약 및 복습 포인트

  • Flask-Mail을 활용한 이메일 발송 시스템 구현 방법을 학습했습니다.
  • Flask 웹 애플리케이션에서 사용자의 문의 내용을 이메일로 전송하는 방법을 배웠습니다.
  • 이메일 발송 시 텍스트와 HTML 템플릿을 활용하여 포맷을 꾸미고 사용자에게 정보를 제공합니다.

이 시스템을 통해 간단한 이메일 보내기 기능을 구현하고, 실제 서비스에 맞게 확장할 수 있습니다

 

📌 구글 2단계 인증 관련 설명

구글 2단계 인증은 계정의 보안을 강화하는 방법 중 하나입니다. 이를 설정하면 비밀번호 외에도 추가적인 인증 절차를 거쳐 계정에 로그인할 수 있습니다. 이 과정에서 앱 비밀번호를 생성하여 외부 애플리케이션에서 구글 계정에 접근할 수 있도록 허용합니다.

🚦 구글 2단계 인증 활성화 방법

  1. 구글 계정 페이지로 이동:
    • 구글 계정 페이지에 접속하여, 2단계 인증을 설정할 수 있습니다.
  2. 앱 비밀번호 설정:
    • 앱 비밀번호 페이지로 이동하여 특정 앱에 대해 별도의 비밀번호를 생성합니다. 예를 들어, 이메일 클라이언트나 다른 앱에서 구글 계정에 로그인하려면 이 앱 비밀번호를 사용해야 합니다.
    • 앱 비밀번호 설정 페이지: 앱 비밀번호 페이지
  3. 앱 비밀번호 사용:
    • 생성된 앱 비밀번호는 구글 계정에 연결된 애플리케이션에서 사용됩니다. 이 비밀번호는 일반적으로 로그인 비밀번호와 다르며, 외부 애플리케이션에서 구글 계정에 접근할 때 사용됩니다.

💻 코드 분석: 구글 2단계 인증과 이메일 발송

구글 2단계 인증을 사용하는 경우, 외부 앱에서 이메일을 발송하려면 앱 비밀번호를 사용해야 합니다. 위에서 설명한 코드 예시에서 이메일을 보내는 기능은 Flask-Mail을 이용하여 처리되며, MAIL_USERNAME과 MAIL_PASSWORD는 구글 계정의 앱 비밀번호를 사용하여 설정해야 합니다.

  • MAIL_USERNAME: 구글 계정의 이메일 주소
  • MAIL_PASSWORD: 구글 계정에서 생성한 앱 비밀번호

이 앱 비밀번호를 사용하여 Flask-Mail에서 이메일을 안전하게 발송할 수 있습니다.

✅ 정리

  • contact_mail.txt: 텍스트 형식 이메일을 보내는 템플릿으로, 이메일 클라이언트와의 호환성을 높이고, HTML을 지원하지 않는 환경에서도 이메일 내용을 확인할 수 있게 합니다.
  • 구글 2단계 인증: 구글 계정의 보안을 강화하기 위한 추가 인증 절차입니다. 앱 비밀번호를 통해 외부 애플리케이션에서 구글 계정에 안전하게 접근할 수 있습니다.

https://security.google.com/settings/security/apppasswords

'기술블로그' 카테고리의 다른 글

🚀 Git Flow 브랜치 전략 초기 셋팅 가이드(1-1)  (0) 2025.04.24
Flask-Login 을 활용한 간단한 로그인 프로젝트  (0) 2025.04.23
📌 SMTP,프로토콜,통신 규약 이란?  (0) 2025.04.23
Flask : 서비스 기획부터 API 개발까지: 시스템 설계의 전체 흐름(with 프론트엔드 소통)  (0) 2025.04.23
Flask 템플릿 렌더링 with 파머(1/3)  (0) 2025.04.23
'기술블로그' 카테고리의 다른 글
  • 🚀 Git Flow 브랜치 전략 초기 셋팅 가이드(1-1)
  • Flask-Login 을 활용한 간단한 로그인 프로젝트
  • 📌 SMTP,프로토콜,통신 규약 이란?
  • Flask : 서비스 기획부터 API 개발까지: 시스템 설계의 전체 흐름(with 프론트엔드 소통)
Chansman
Chansman
안녕하세요! 코딩을 시작한 지 얼마 되지 않은 초보 개발자 찬스맨입니다. 이 블로그는 제 학습 기록을 남기고, 다양한 코딩 실습을 통해 성장하는 과정을 공유하려고 합니다. 초보자의 눈높이에 맞춘 실습과 팁, 그리고 개발하면서 겪은 어려움과 해결 과정을 솔직하게 풀어내려 합니다. 함께 성장하는 개발자 커뮤니티가 되기를 바랍니다.
  • Chansman
    찬스맨의 프로그래밍 스토리
    Chansman
  • 전체
    오늘
    어제
    • 분류 전체보기 (472)
      • Python (31)
      • 프로젝트 (43)
      • 과제 (21)
      • Database (40)
      • 멘토링 (7)
      • 특강 (18)
      • 기술블로그 (126)
      • AI 분석 (4)
      • HTML & CSS (31)
      • JavaScript (17)
      • AWS_Cloud (21)
      • 웹스크래핑과 데이터 수집 (14)
      • Flask (42)
      • Django (34)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Chansman
Flask 문의 내역 이메일 발송 시스템 구현 with 파머
상단으로

티스토리툴바