이전 내용
[파이썬] FastAPI - 메모 앱 프로젝트 12: 파일 분할하기
이전 내용 [파이썬] FastAPI - 메모 앱 프로젝트 11: 회원 탈퇴이전 내용 [파이썬] FastAPI - 메모 앱 프로젝트 10: 환영 이메일 발송이전 내용 [파이썬] FastAPI - 메모 앱 프로젝트 9: 소셜 로그인 추가(네
puppy-foot-it.tistory.com
아이디, 비밀번호 찾기 구현하기
웹 페이지 테스트를 위해 계정을 생성하는데, 비밀번호에 정규표현식을 걸어놔서 간단한 비밀번호 생성이 안되다보니 내가 만들어놓고도 금세 까먹는 상황이 자주 생겼다. 그리고 실제로도 오랜만에 사이트에 접속하거나, 자주 사용하지 않는 사이트에 접속 시 계정 정보를 잊는 경우가 있어 이에 대한 기능을 구현해 보는 것도 좋은 학습 기회가 될 거 같아 이를 구현해 보려 한다.
- 아이디: 가입한 이메일을 입력하면 이메일에 맞는 사용자명을 조회하고, 이를 등록한 이메일로 발송해 주는 기능을 구현
- 비밀번호: 기존 비밀번호가 아닌 새로운 비밀번호를 입력하도록 하는 기능 구현
- 최종적으로 home.html에 '아이디/비밀번호 찾기' 를 구현하여 연결되도록 설정
아이디 찾기 구현하기
◆ email_class.py
먼저 email_class.py 파일에 새로운 클래스 'EmailServiceFindId' 와 'EmailRequest' 를 추가해 준다.
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from dotenv import load_dotenv
import os
load_dotenv()
class EmailServiceWelcome:
def send_email(
self,
receiver_email: str,
):
sender_email = os.getenv('EMAIL_ADDRESS')
password = os.getenv('EMAIL_PASSWORD')
message = MIMEMultipart()
message["From"] = sender_email
message["To"] = receiver_email
message["Subject"] = "회원 가입을 환영합니다."
body = "메모 앱 서비스를 이용해 주셔서 감사합니다."
message.attach(MIMEText(body, "plain"))
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
server.login(sender_email, password)
server.send_message(message)
class EmailServiceBye:
def send_email(
self,
receiver_email: str,
):
sender_email = os.getenv('EMAIL_ADDRESS')
password = os.getenv('EMAIL_PASSWORD')
message = MIMEMultipart()
message["From"] = sender_email
message["To"] = receiver_email
message["Subject"] = "탈퇴 완료 이메일."
body = "그동안 메모 앱 서비스를 이용해 주셔서 감사합니다."
message.attach(MIMEText(body, "plain"))
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
server.login(sender_email, password)
server.send_message(message)
class EmailServiceFindId:
def send_email(
self,
receiver_email: str,
username: str,
):
sender_email = os.getenv('EMAIL_ADDRESS')
password = os.getenv('EMAIL_PASSWORD')
message = MIMEMultipart()
message["From"] = sender_email
message["To"] = receiver_email
message["Subject"] = "아이디 찾기 이메일."
body = f"사용자님의 아이디는 {username} 입니다."
message.attach(MIMEText(body, "plain"))
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
server.login(sender_email, password)
server.send_message(message)
class EmailRequest(BaseModel):
email: str
◆ email_service.py
그 다음에 email_service.py 에 사용자 이름을 이메일로 전송하는 로직을 추가해 준다.
from service.email_class import EmailServiceWelcome, EmailServiceBye, EmailServiceFindId # 추가
# 사용자 이름 이메일 전송 (사용자 찾기)
def send_username_email(email: str, username: str):
email_service = EmailServiceFindId() # EmailServiceFindId 클래스 인스턴스 생성
try:
email_service.send_email(receiver_email=email, username=username)
logger.info(f"사용자 이름 정보를 {email}로 전송했습니다.")
except Exception as e:
logger.error(f"이메일 전송 실패: {e}") # 에러 로깅
◆ users_controller.py
users_controller.py 파일에 아이디를 검색하면 이메일로 발송해 주는 라우터를 생성하여 로직을 구현한다.
from service.email_service import send_bye_email, send_welcome_email, send_username_email # 추가
from service.email_class import EmailRequest # 추가
# 아이디 찾기 (이메일로 발송)
@router.post("/send-username")
async def find_id(req: EmailRequest, db:Session=Depends(get_db)):
email = req.email
logger.info(f"사용자 이름 요청: 이메일 {email}")
user = db.query(User).filter(User.email == email).first()
if user is None:
logger.warning(f"사용자 이름 요청 실패: 해당 이메일로 등록된 사용자가 없습니다. 이메일: {email}")
raise HTTPException(status_code=404, detail="해당 이메일로 등록된 사용자가 없습니다.")
# 사용자 이름 이메일 발송
try:
send_username_email(email=email, username=user.username)
logger.info(f"사용자 이름 {user.username}을 {email}로 성공적으로 전송했습니다.")
except Exception as e:
logger.error(f"이메일 전송 실패: {e}")
raise HTTPException(status_code=500, detail="이메일 전송에 실패했습니다. 다시 시도해 주세요")
return {"success": True, "message": "사용자 이름이 이메일로 발송되었습니다."}
◆ home.html
home.html에 '아이디/비밀번호 찾기' 버튼을 추가한다. (회원 가입 밑)
<p>
<a href="/find_id" style="text-decoration: none; color:blue;">
아이디/비밀번호 찾기 </a>
</p>
◆ find_id.html
그리고 이메일을 입력하여 이메일 발송 버튼을 누를 페이지인 find_id.html 페이지를 생성하는데, 기존의 home.html의 스타일과 비슷하도록 만든다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>아이디/비밀번호 찾기</title>
<style>
body {
font-family: 'Noto Sans KR', sans-serif;
background-color: #f8f9fa;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
text-align: center;
}
.container {
max-width: 400px;
padding: 2rem;
background-color: #fff;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
margin: 1rem;
width: 100%;
}
h1 {
font-size: 1.5rem;
color: #007bff;
margin-bottom: 2rem;
}
.form-group {
margin-bottom: 1rem;
width: 100%;
}
.form-group label {
margin-bottom: .5rem;
color: #888;
text-align: left;
display: block;
}
.form-group input {
padding: 0.75rem;
border: 1px solid #ced4da;
border-radius: 5px;
width: 100%;
box-sizing: border-box;
}
.form-group input:focus {
border-color: #80bdff;
box-shadow: 0 0 0 2px rgba(0,123,255,.25);
}
.buttons button {
width: 100%;
padding: 0.75rem;
border: none;
border-radius: 5px;
background-color: #007bff;
color: white;
margin-top: 0.5rem;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
box-sizing: border-box;
}
.buttons button:hover {
background-color: #0056b3;
}
#message {
margin-top: 1rem;
color: #28a745;
font-weight: 500;
}
@media (max-width: 768px) {
.container {
width: 90%;
padding: 1.5rem;
}
h1 {
font-size: 1.25rem;
}
}
</style>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;700&display=swap" rel="stylesheet">
</head>
<body>
<div class="container">
<h1>아이디/비밀번호 찾기</h1>
<form id="find-id-form" onsubmit="return submitForm(event)">
<div class="form-group">
<label for="email">이메일 주소</label>
<input type="email" id="email" name="email" required>
</div>
<div class="buttons">
<button type="submit">아이디 전송</button>
</div>
</form>
<div id="message"></div>
</div>
<script>
function submitForm(event) {
event.preventDefault(); // 기본 제출 이벤트 방지
const email = document.getElementById('email').value;
const messageDiv = document.getElementById('message');
fetch('/send-username', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ email: email })
})
.then(response => {
if (!response.ok) {
return response.json().then(err => {
throw new Error(err.detail || '이메일 전송에 실패했습니다.');
});
}
return response.json();
})
.then(data => {
messageDiv.innerText = data.message;
messageDiv.style.color = '#28a745'; // 성공 메시지 색상
})
.catch(error => {
messageDiv.innerText = error.message;
messageDiv.style.color = '#dc3545'; // 에러 메시지 색상
});
}
</script>
</body>
</html>
◆ main.py
main.py 파일에도 아이디/비밀번호 찾기 페이지 엔드포인트를 추가해 준다.
import logging
from fastapi.responses import HTMLResponse
# 아이디 비밀번호 찾기 페이지 엔드포인트 추가
@app.get("/find_id", response_class=HTMLResponse)
async def read_find_id(request: Request):
logger.info("아이디/비밀번호 찾기 페이지 요청 수신")
return templates.TemplateResponse('find_id.html', {"request": request})
아이디 찾기 테스트
main.py를 실행하여 서버를 실행한 뒤, 메일로 아이디가 잘 오는지 확인해 보기 위해 이메일 주소가 유효한 것으로 새로운 계정을 생성한다.
그리고 하단에 뜨는 아이디/비밀번호 찾기 링크를 클릭하면 아래와 같이 /find_id 로 잘 넘어간다.
앞서 생성한 계정의 이메일 주소를 입력한 후, [아이디 전송] 버튼을 누르면 사용자 이름이 이메일로 발송되었다는 메시지가 뜨고,
이메일이 실제로 잘 발송된 것을 확인할 수 있다.
로그를 확인해 보니, 에러없이 잘 작동한다.
다음 내용
[파이썬] FastAPI - 메모 앱 프로젝트 14: 비밀번호 찾기(수정)
이전 내용 [파이썬] FastAPI - 메모 앱 프로젝트 13: 아이디 찾기이전 내용 [파이썬] FastAPI - 메모 앱 프로젝트 12: 파일 분할하기이전 내용 [파이썬] FastAPI - 메모 앱 프로젝트 11: 회원 탈퇴이전 내용 [
puppy-foot-it.tistory.com
'[파이썬 Projects] > <파이썬 웹개발>' 카테고리의 다른 글
[파이썬] FastAPI - 메모 앱 프로젝트 15: 이메일 기능 보완 (0) | 2025.05.14 |
---|---|
[파이썬] FastAPI - 메모 앱 프로젝트 14: 비밀번호 찾기(수정) (0) | 2025.05.13 |
[파이썬] FastAPI - 메모 앱 프로젝트 12: 파일 분할하기 (0) | 2025.05.13 |
[파이썬]FastAPI - 다른 컴퓨터에서 프로젝트(VSCODE, 깃허브) (0) | 2025.05.12 |
[파이썬] FastAPI - 메모 앱 프로젝트 11: 회원 탈퇴 (0) | 2025.05.12 |