TOP
class="layout-aside-left paging-number">
본문 바로가기
[파이썬 Projects]/<파이썬 웹개발>

[파이썬] 플라스크(Flask) - 플라스크 프로젝트 - 메모앱(5)

by 기록자_Recordian 2024. 8. 14.
728x90
반응형
시작에 앞서
해당 내용은 <가장 빠른 풀스택을 위한 Flask & FastAPI>, Dave Lee 지음. BJ Public 출판.
내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.

이전 내용(메모앱 1단계 - 3단계)
 

[파이썬] 플라스크(Flask) - 플라스크 프로젝트 - 메모앱(1)

시작에 앞서해당 내용은 , Dave Lee 지음. BJ Public 출판.내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.메모앱 만들기 - 1단계: 애플리케이션 생성 플라스크

puppy-foot-it.tistory.com

이전 내용(메모앱 4단계)
 

[파이썬] 플라스크(Flask) - 플라스크 프로젝트 - 메모앱(2)

시작에 앞서해당 내용은 , Dave Lee 지음. BJ Public 출판.내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.이전 내용(메모앱 1단계 - 3단계) [파이썬] 플라스크(Fl

puppy-foot-it.tistory.com

이전 내용(메모앱 5단계)
 

[파이썬] 플라스크(Flask) - 플라스크 프로젝트 - 메모앱(3)

시작에 앞서해당 내용은 , Dave Lee 지음. BJ Public 출판.내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.이전 내용(메모앱 1단계 - 3단계) [파이썬] 플라스크(Fl

puppy-foot-it.tistory.com

이전 내용(메모앱 6단계)
 

[파이썬] 플라스크(Flask) - 플라스크 프로젝트 - 메모앱(4)

시작에 앞서해당 내용은 , Dave Lee 지음. BJ Public 출판.내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.이전 내용(메모앱 1단계 - 3단계) [파이썬] 플라스크(Fl

puppy-foot-it.tistory.com


7단계: 사용자 편의성 향상

 

사용자 경험 개선을 위해, redirect() 와 url_for() 함수를 활용, 사용자의 행동에 따라 적절한 페이지로 리다이렉트하는 기능 구현

 

◆ 두 함수를 사용하기 위한 import 구문 수정

from flask import Flask, render_template, request, jsonify, abort, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from werkzeug.security import generate_password_hash, check_password_hash

 

◆ 로그인 기능 개선

사용자가 로그인에 성공 ▶ 홈페이지로 리다이렉트

실패 ▶ 에러 메시지를 팝업으로 표시

 

★ 로그인 뷰 수정 

app.py 파일의 @app.route('/login', ~ 영역 수정

# 로그인 뷰 정의
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        user = User.query.filter_by(username=request.form['username']).first()
        if user and user.check_password(request.form['password']):
            login_user(user)
            return jsonify({'message': '로그인을 성공하였습니다. 메모 페이지로 이동합니다.'}), 200
        # 에러 메시지를 JSON 형태로 반환
        return jsonify({'error': '아이디가 없거나 패스워드가 다릅니다.'}), 401
    return render_template(url_for('home'))

◆ 회원가입 기능 개선

회원가입 성공 ▶ 로그인 페이지로 리다이렉트

실패 ▶ 에러 메시지를 팝업으로 표시

 

★ 회원가입 뷰 수정 

app.py 파일의 @app.route('/signup', ~ 영역 수정

# 회원 가입 뷰
@app.route('/signup', methods=['GET', 'POST'])
def signup():
    if request.method == 'POST':
        username = request.form['username']
        email = request.form['email']
        password = request.form['password']
        
        #회원가입 실패 시 에러 메시지를 JSON 형태로 반환(프런트엔드 페이지에서 해당 메시지를 기반으로 팝업을 띄움)
        existing_user = User.query.filter((User.username == username) | (User.email == email)).first()
        if existing_user:
            return jsonify({'error': '사용자 이름 또는 이메일이 이미 사용 중 입니다.'}), 400
        
        user = User(username=username, email=email)
        user.set_password(password)
        
        db.session.add(user)
        db.session.commit()
        
        return jsonify({'message': '회원가입이 성공하였습니다. 가입한 아이디와 패스워드로 로그인할 수 있습니다.'}), 201
    return redirect(url_for('home')) #비정상 요청의 경우 리다이렉트

◆ 프런트엔드 페이지 수정

로그인 및 회원가입 성공 또는 실패에 따른 적절한 메시지를 팝업으로 표시하고, 성공 시에는 적절한 페이지로 리다이렉트home.html 수정

<!DOCTYPE html><html lang="ko"><head>
    <meta charset="UTF-8">
    <title> 마이 메모 앱 홈페이지</title>
    <style>
        body { font-family: Arial, sans-serif; }
        .container {
            width: 300px;
            margin: auto;
            border: 1px solid #ddd;
            padding: 20px;
        }
        .form-group {
            margin-bottom: 10px;
        }
        .form-group label, .form-group input {
            display: block;
            width: 100%;
        }
        .form-group input {
            padding: 5px;
            margin-top: 5px;
        }
        .buttons {
            display: flex;
            justify-content: space-between;
            margin-top: 20px;
        }
    </style></head><body>
    <div class="container">
        <h1> My Memo 앱에 오신 것을 환영합니다!</h1>
        <p> 간단한 메모를 작성하고 관리할 수 있는 앱입니다.</p>

        <!-- 로그인 폼-->
        <form id="loginForm" action="/login" method="post">
            <div class="form-group">
                <label for="username">사용자 이름:</label>
                <input type="text" id="username" name="username" required>
            </div>
            <div class="form-group">
                <label for="password">비밀번호:</label>
                <input type="password" id="password" name="password" required>
            </div>
            <div class="buttons">
                <input type="submit" value="로그인">
                <button type="button" onclick="showSignupForm()">회원가입</button>
            </div>
        </form>

        <!-- 회원가입 폼 (초기에는 숨겨져 있음)-->
        <form id="signupForm" style="display:none;" action="/signup" method="post">
            <div class="form-group">
                <label for="signupUsername">사용자 이름:</label>
                <input type="text" id="signupUsername" name="username" required>
            </div>
            <div class="form-group">
                <label for="signupEmail">이메일:</label>
                <input type="email" id="signupEmail" name="email" required>
            </div>
            <div class="form-group">
                <label for="signupPassword">비밀번호:</label>
                <input type="password" id="signupPassword" name="password" required>
            </div>
            <div class="buttons">
                <input type="submit" value="회원가입">
            </div>
        </form>
    </div>

    <script>
        document.getElementById('loginForm').addEventListener('submit', function(e) {
            e.preventDefault();
            const formData = new FormData(this);
            fetch('/login', {
                method: 'POST',
                body: formData
            })
            .then(response => {
                if (!response.ok) {
                    return response.json().then(err => { throw new Error(err.error); });
                }
                return response.json();
            })
            .then(data => {
                alert(data.message);
                // 성공 시 페이지 리디렉션
                window.location.href = '/memos';
            })
            .catch(error => {
                alert('로그인 실패: ' + error.message);
            });
        });

        document.getElementById('signupForm').addEventListener('submit', function(e) {
            e.preventDefault();
            const formData = new FormData(this);
            fetch('/signup', {
                method: 'POST',
                body: formData
            })
            .then(response => {
                if (!response.ok) {
                    return response.json().then(err => { throw new Error(err.error); });
                }
                return response.json();
            })
            .then(data => {
                alert(data.message);
                // 회원가입 성공 시 로그인 페이지로 리디렉션
                window.location.href = '/';
            })
            .catch(error => {
                alert('회원가입 실패: ' + error.message);
            });
        });

        function showSignupForm() {
            // 로그인 폼 숨기기
            document.getElementById('loginForm').style.display = 'none';
            // 회원가입 폼 보이기
            document.getElementById('signupForm').style.display = 'block';
        }
    </script>
</body>
</html>

 

이제 실행하면

<회원가입> 버튼을 누르면


◆ 로그아웃 기능 개선

사용자가 로그아웃할 때 홈페이지로 리다이렉트

 

★ 로그아웃 뷰 수정 

app.py 파일의 @app.route('/logout', ~ 영역 수정

# 로그아웃 뷰 정의
@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('home')) #로그아웃 후 메인 페이지로 리다이렉트

◆ 테스트

먼저 vscode 의 app.py 파일의 터미널에서 'flask run' 입력 후

http://127.0.0.1:5000/ 접속

 

★ 회원가입

 

 

★ 로그인

 

★ 로그인 후 /memos

메모를 확인할 수 있는 페이지에서는 상단 좌측에 로그인한 아아디가 표시되며 아이디별 메모를 추가, 수정, 삭제할 수 있다.

(저장된 메모는 리스트 형태로 표시되며, 우측 상단의 로그아웃을 통해 로그아웃 가능)

★ 로그아웃

<로그아웃> 버튼을 누르면 메인 페이지로 이동한다. (메모 페이지는 로그인한 사용자만 접속할 수 있음을 확인할 수 있다)


다음 내용

 

[파이썬] 플라스크(Flask) - 플라스크 프로젝트 - 메모앱(6)

시작에 앞서해당 내용은 , Dave Lee 지음. BJ Public 출판.내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.이전 내용(메모앱 1단계 - 3단계) [파이썬] 플라스크(Fl

puppy-foot-it.tistory.com

 

728x90
반응형