본문 바로가기

IT

Flask - 애플리케이션(메모앱) 만들어보기 #7

안녕하세요 ^^

저번시간 까지 [Flask - 애플리케이션(메모앱) 만들어보기 #6]  메모앱을 작성하면서 프론트앤드 기반으로 개선을 했었는데요, 오늘은 좀 더 사용자 편의성 개선 부분에서 소스를 수정하며 보려합니다.

 

1. import 구문 수정

먼저 app.py 파일에서 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

 

 

2. 로그인 기능 개선

예전 소스(app.py)에서 사용자가 로그인을 하고 성공하고 나면 홈페이지로 리다이렉트하고, 실패하게 되면 에러 메시지를 팝업으로 표시하는 기능을 구현해 봅니다.

# 로그인 및 로그아웃 처리를 위한 라우트 추가
@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': 'Logged in successfully'}), 200
        return abort(401, description="Invalid credentials")
    return redirect(url_for('home'))

 

 

3. 회원가입 기능 개선

회원가입 성공 시 로그인 페이지로 리다이렉트 하고, 실패 시 에러 메시지를 팝업으로 표시하는 기능을 구현해봅니다.

# 회원가입 기능 추가
@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': 'Account Created successfully'}), 201
    return redirect(url_for('home'))  # 비정상 요청일 경우 리다이렉트

 

 

4. 프런트엔드 페이지 수정하기

 

로그인 및 회원가입 성공 또는 실패 시 적절한 메시지를 팝업으로 표시하고, 성공 시에는 적절한 페이지로 리다이렉트 합니다. 이를 위해 index.html 페이지도 아래와 같이 수정합니다.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>온라인 메모 앱 v1.0</title>
    <style>
        body { font-family: Arial, Helvetica, 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>
    <h2>나의 메모 앱에 오신것을 환영해요</h2>
    <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>

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

 

 

5. 로그아웃 기능 개선

사용자가 로그아웃할 때 기본 페이지로 리다이렉트 하는 기능을 구현합니다. 이를 위해 app.py 파일의 다음 라우트를 수정해봅시다.

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('home'))

 

 

6. 테스트

 

지금까지 수정한 것으로 테스트 진행합니다.

 

먼저 http://127.0.0.1:5000/ 접속 후 회원가입합니다. 회원 가입에 성공 시 메인 페이지로 자동으로 이동합니다.

 

회원가입에 성공하면 아래와 같은 팝업 창이 뜨게 됩니다.

 

연달아 로그인 페이지로 리다이렉션 됩니다.

 

로그인이 정상적으로 처리된다면 여전히 팝업 창으로 알려주게 구현되었습니다.

 

 

그리고 바로 메모를 입력하도록 페이지 구현이 되었습니다.

 

 

메모 입력이 아주 잘되고 있습니다. 로그아웃도 원활히 잘 됨을 알 수 있네요

 

오늘은 여기까지 입니다. 그럼 다음에 또 만나요