ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [실행 및 오류] flask, mongoDB 회원가입 페이지 만들기 (1)_아이디 중복체크, 회원가입 후 페이지 로딩 오류 발생
    프로젝트/토이 프로젝트 2023. 6. 6. 23:03
    728x90

     

     

     

    사용환경 : flask, mongoDB

    프로젝트 목표: 하루의 일과를 간단한 이모티콘과 키워드 클릭을 통해 보다 쉽고 빠른 일기를 작성하기
    구현담당 : 회원가입, 로그인, 일기작성시 등록된 키워드 불러오기

     

     

     

     

    <!-- 회원가입 페이지(join.html) -->
    
    {% extends 'layout.html' %}
    
    {% block content %}
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
            integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous" />
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
            integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
            crossorigin="anonymous"></script>
    
    
        <title>회원가입 페이지</title>
    
        <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+KR:wght@200;300;400;500;600;700;900&display=swap"
            rel="stylesheet" />
        <style>
            * {
                font-family: "Noto Serif KR", serif;
            }
    
            .mypost {
                width: 95%;
                max-width: 500px;
                margin: 20px auto 20px auto;
    
                box-shadow: 0px 0px 3px 0px black;
                padding: 20px;
            }
    
            .mypost>button {
                margin-top: 15px;
            }
    
            .mycards {
                width: 95%;
                max-width: 500px;
                margin: auto;
            }
    
            .mycards>.card {
                margin-top: 10px;
                margin-bottom: 10px;
            }
        </style>
    
        <script>
            function save_join() {
                let email = $('#email').val();          
                let name = $('#name').val();
                let userID = $('#id').val();
                let pwd = $('#pwd').val();
    
                let formData = new FormData();
                formData.append('email_give', email);
                formData.append('name_give', name);
                formData.append('id_give', userID);
                formData.append('pwd_give', pwd);
    
                // /join_done에서 받은 msg로 회원가입이 다시 필요한지, 가입이 완료인지 확인한다. 
                fetch('/join_done', { method: "POST", body: formData })
                    .then((res) => res.json())
                    .then((data) => {
                        if (data.msg === "회원 가입이 완료되었습니다.") {
                            alert(data.msg);
                            window.location.href = "/";
                        } else {
                            alert("아이디가 중복입니다.");
                            window.location.href = "/join";
                        }
                    });
            }
        </script>
    
    </head>
    
    <body>
        <h1>회원가입</h1>
        <form action="/join_done" method="post">
            <label for="email">이메일:</label>
            <input type="email" id="email" name="email" required><br>
            <label for="name">이름:</label>
            <input type="text" id="name" name="name" required><br>
            <label for="username">아이디:</label>
            <input type="text" id="id" name="username" required><br>
            <label for="password">비밀번호:</label>
            <input type="password" id="pwd" name="password" required><br>
            <input onclick="save_join()" type="submit" value="회원가입">
    
        </form>
    </body>
    
    </html>
    {% endblock %}

     

     

     

    from pymongo import MongoClient
    from flask import Flask, render_template, request, jsonify, session, redirect, url_for
    
    client = MongoClient('*URL')
    #
    
    db = client.dbsparta
    application = app = Flask(__name__)
    app.secret_key = 'any random string'
    
    @app.route('/join')
    def join():
        return render_template('join.html')
    
    
    @app.route('/join_done', methods=['POST'])               
    def join_done():
    
        email = request.form['email_give']      
        name = request.form['name_give']
        userID = request.form['id_give']
        pwd = request.form['pwd_give']
    
        # MongoDB에서 회원가입시 입력한 ID값을 조회하여 중복여부 판단하기
        user = db.users.find_one({'id': userID})
    
    	# 중복일 경우
        if user:        
            return jsonify({'msg': '회원가입 실패'})
    
    	# 중복이 아닐 경우 MongoDB에 데이터 저장한다
        else:   
            doc = {
                'email': email,
                'name': name,
                'id': userID,
                'pwd': pwd
            }
            db.users.insert_one(doc)
            return jsonify({'msg': '회원 가입이 완료되었습니다.'})

     

     


     

    1. 회원가입 기능 구현하기_오류발생

     

    의도한 코드
     - 회원가입 : 이메일, 이름, 아이디, 비밀번호를 입력 후 해당값을 데이터베이스에 저장한다.
        단, 아이디는 중복이 되면 안된다.
         ① 아이디가 중복일 경우 "아이디 중복불가. 다시 입력하세요."를 alert한 후 회원가입 페이지 재로딩
         ② 아이디가 중복이 아닐 경우 입력값을 데이터베이스에 저장 후
              "회원 가입이 완료되었습니다."를 alert한 후 홈페이지('/') 재로딩

      

     

     

    ⓛ 오류발생

     

    중복값을 입력했을 경우 의도한대로 진행되나, 

    정상값을 입력했을 경우 홈페이지가 아닌 "Bad Request"가 뜬다. (데이터는 MongoDB에 정상입력됨)

     

     


     

     

     

     

    도대체 뭐가 문제니...? 🤷

     

     

     

    한번에 성공할거란 생각은 없었지만 부분적으로 안되는 상황이라 너무나도 난감했다.

     

    원하던 기능이 구현되지 않고 데이터도 저장되지 않는다 -> 모든 코드가 다 문제다

    기능이 조건별로 오류와 정상작동을 반복하고 기능은 오류지만 데이터가 저장된다 -> 도대체 뭐가 문제니...? 🤷 

     

    마음을 진정시킨 후 하나씩 다시 살펴보았다.

     

     


     

    오류(1): 정상 회원가입일 경우 Bad Request가 뜨면서 화면이 로딩되지 않음

    * 데이터는 정상적으로 들어감

     

     

     

     

     

    데이터 정상수신

     

    회원가입 후 홈 화면이 로딩되지 않음

     

    에러메세지 127.0.0.1 - - [06/Jun/2023 22:00:38] "POST /join_done HTTP/1.1" 400 -

     

     

     

    "Bad Request"란?

    Bad Request : 클라이언트(브라우저)가 서버에 잘못된 요청을 보낸 경우 발생한다.

     클라이언트가 서버에 전달한 데이터의 형식이나 내용이 잘못되었을 경우가 많다.

    위의 오류는 여러가지 상황에서 발생할 수 있기 때문에 아래의 경우를 모두 살펴보자

     

     

     <"Bad Request" 오류가 발생하는 가능성 3가지>

    ★ 1.  클라이언트에서 POST 요청 시 전송하는 데이터의 이름(name) 속성과 서버에서 받는 데이터의 키(key) 값이 일치하지 않는 경우.
    예를 들어, 클라이언트에서 데이터를 "username"으로 전송했는데 서버에서는 "id_give"로 받으려고 한다면 오류가 발생할 수 있다.(내 코드가 실행되지 않은 이유이다)
    이 경우, 클라이언트와 서버 간의 데이터 키(key) 값이 일치하도록 변경해준다.

    2. 클라이언트에서 전송하는 데이터가 필수(required) 필드인데 비어있거나 올바르지 않은 형식일 경우.
    예를 들어, 이메일 필드가 비어있거나 올바른 이메일 형식이 아닌 경우 "Bad Request" 오류가 발생할 수 있다.
    이 경우, 클라이언트에서 전송하는 데이터의 유효성을 확인하고 필수 필드가 모두 채워져 있는지 확인해야 한다.

    3. 서버에서 데이터를 받는 방식이나 파싱 방법에 문제가 있는 경우.
    예를 들어, 서버에서 request.form을 통해 데이터를 받는데 클라이언트에서 FormData를 사용하여 데이터를 전송하지 않는 경우 오류가 발생할 수 있다.
    이 경우, 클라이언트와 서버 간의 데이터 전송 방식을 일치시켜야 한다.

     

     

     

     

     

     

     

     

    ★ 1.  클라이언트에서 POST 요청 시 전송하는 데이터의 이름(name) 속성과 서버에서 받는 데이터의 키(key) 값이 일치하지 않는 경우.

    <body>
        <h1>회원가입</h1>
        <form action="/join_done" method="post">
            <label for="email">이메일:</label>
            <input type="email" id="email" name="email" required><br>
            <label for="name">이름:</label>
            <input type="text" id="name" name="name" required><br>
            <label for="username">아이디:</label>
            <input type="text" id="id" name="username" required><br>
            <label for="password">비밀번호:</label>
            <input type="password" id="pwd" name="password" required><br>
            <input onclick="save_join()" type="submit" value="회원가입">
    
        </form>
    </body>

     

     

    @app.route('/join_done', methods=['POST'])
    def join_done():
        email = request.form['email_give']  # join.html에서 받아온 email값
        name = request.form['name_give']
        userID = request.form['id_give']
        pwd = request.form['pwd_give']
    
        # MongoDB에서 ID 값으로 조회
        user = db.users.find_one({'id': userID})
    
        if user:  # True인 경우
            return jsonify({'msg': '아이디 중복불가. 다시 입력하세요.'})
        else:
            # MongoDB에 데이터 저장
            doc = {
                'email': email,
                'name': name,
                'id': userID,
                'pwd': pwd
            }
            db.users.insert_one(doc)
            return jsonify({'msg': '회원 가입이 완료되었습니다.'})

     

     

    나의 계획은 join.html에서 내용을 입력하고 submit을 onclick 했을때 save_join()으로 입력된 정보를 서버에 전송한 뒤에 중복여부를 판단하는 것이었다.

     

     

    "Bad Request"의 첫번째 가능성에 주목해보았다.

    오류찾기 용으로 키값을 email과 id값 두개만 가지고 진행해보았다.

    그리고 우연의 일치로 오류가 무엇인지 발견할 수 있었다.

     

     

     

    HTML 템플릿과 Flask 서버의 코드에 일부에서 불일치 오류 발생!

    HTML에서 키 값의 name속성이 서버의 키값과 불일치하여 문제가 발생했다.
    		<label for="email">이메일:</label>
            <input type="email" id="email" name="email" required><br>
            
            <label for="username">아이디:</label>
            <input type="text" id="id" name="username" required><br>

     

    name 속성은 서버에서 폼 데이터를 받을 때 사용되는 키값으로, 서버 코드에서 해당 키값으로 데이터를 참조하는데

    HTML에서 name 속성이 name="username"로 되어있으나 서버에서는 name="id_give"로 받기때문에 오류가 발생한 것이다.

    회원가입 입력창에서 입력된 값은 save_join()에서 .val()로 들어오기 때문에 <form></form>에 작성되어 있던 name을 지웠더니 정상적으로 작동했다.

     

     

     

     

     

     

     

    정상코드

     

    <!-- join.html -->
    {% extends 'layout.html' %}
    {% block content %}
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
            integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous" />
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
            integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
            crossorigin="anonymous"></script>
    
        <title>회원가입 페이지</title>
    
        <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+KR:wght@200;300;400;500;600;700;900&display=swap"
            rel="stylesheet" />
        <style>
            * {
                font-family: "Noto Serif KR", serif;
            }
    
            .mypost {
                width: 95%;
                max-width: 500px;
                margin: 20px auto 20px auto;
    
                box-shadow: 0px 0px 3px 0px black;
                padding: 20px;
            }
    
            .mypost>button {
                margin-top: 15px;
            }
    
            .mycards {
                width: 95%;
                max-width: 500px;
                margin: auto;
            }
    
            .mycards>.card {
                margin-top: 10px;
                margin-bottom: 10px;
            }
        </style>
    
        <script>
            function save_join() {
                let email = $('#email').val();
                let name = $('#name').val();
                let userID = $('#id').val();
                let pwd = $('#pwd').val();
    
                let formData = new FormData();
                formData.append('email_give', email);
                formData.append('name_give', name);
                formData.append('id_give', userID);
                formData.append('pwd_give', pwd);
    
                fetch('/join_done', { method: "POST", body: formData })
                    .then((res) => res.json())
                    .then((data) => {
                        if (data.msg === "회원 가입이 완료되었습니다.") {
                            alert(data.msg);
                            window.location.href = "/";
                        } else {
                            alert(data.msg);
                            window.location.href = "/join";
                        }
                    });
            }
        </script>
    
    </head>
    
    <body>
        <h1>회원가입</h1>
        <form>
            <label for="email">이메일:</label>
            <input type="email" id="email" required><br>
            <label for="name">이름:</label>
            <input type="text" id="name" required><br>
            <label for="username">아이디:</label>
            <input type="text" id="id" required><br>
            <label for="password">비밀번호:</label>
            <input type="password" id="pwd" required><br>
            <input onclick="save_join()" type="button" value="회원가입">
        </form>
    </body>
    
    </html>
    {% endblock %}

     

     

     

    from pymongo import MongoClient
    from flask import Flask, render_template, request, jsonify, session, redirect, url_for
    
    client = MongoClient('URL')
    db = client.dbsparta
    application = app = Flask(__name__)
    app.secret_key = 'any random string'
    
    @app.route('/')
    def home():
        id = session.get('id', None)
        return render_template('index.html', id=id)
    
    
    @app.route('/join')
    def join():
        return render_template('join.html')
    
    
    @app.route('/join_done', methods=['POST'])
    def join_done():
        email = request.form['email_give']  # join.html에서 받아온 email값
        name = request.form['name_give']
        userID = request.form['id_give']
        pwd = request.form['pwd_give']
    
        # MongoDB에서 ID 값으로 조회
        user = db.user.find_one({'id': userID})
    
        if user:  # True인 경우
            return jsonify({'msg': '아이디가 중복입니다.'})
        else:
            # MongoDB에 데이터 저장
            doc = {
                'email': email,
                'name': name,
                'id': userID,
                'pwd': pwd
            }
            db.user.insert_one(doc)
            return jsonify({'msg': '회원 가입이 완료되었습니다.'})
    
    if __name__ == '__main__':
        app.run()

     

     

    하지만 문제를 또 발견했다.

    작성시

    <input type="password" id="pwd" required><br>
     

    을 설정했으나 NULL이어도 데이터는 저장이 된다.

    다음 시간에는 NULL값 미허용과 로그인 기능을 만들 예정이다.

    728x90
Designed by Tistory.