본문 바로가기
MS 클라우드

MS클라우드 애저(Azure) 가상머신(VM) 배포 방법(feat:flask)

by 미토리야 2022. 5. 12.

안녕하세요, 내일배움캠프 클라우드 실시간 강의 자료입니다.

 

목차

1. MS 클라우드 CLI 연결

2. 웹서버와 DB설치

3. FTP접속방법

4. 배포하기(예제파일 소스)

 

MS클라우드 애저(Azure) 가상머신(VM:Virtual Machine)에서 배포 방법입니다.

가상머신 설치가 안되어 있다면 아래 링크에서 설치하시면 됩니다.

링크 : MS클라우드 애저(Azure) 가상머신(VM) 설치 방법(Github 가입 for DevOps)

이번 시간에는 flask 웹서버를 설치해서 배포하는 방법을 알아보겠습니다.

VM 설치할 때 생성한 인증서를 사용합니다.

 

1. MS 클라우드 CLI 연결

 

먼저 가상머신에서 접속정보를 확인합니다.

MS 클라우드 서비스에 접속합니다.https://portal.azure.com/

 

Microsoft Azure

 

portal.azure.com

접속한 후에 Github으로 로그인합니다(일반 로그인도 가능).

가상머신을 클릭합니다.

 

 

등록한 가상머신이 보입니다. 이름을 클릭합니다.

설정->연결을 클릭하면 터미널 SSH 접속 실행 정보를

확인할 수 있습니다(아이디, 인증서 파일, 접속IP).

파이참(PyCharm) 터미널에 접속합니다.

명령줄라인에 입력합니다.

ssh -i {인증서파일경로} {아이디}@{VM IP주소}

저같은 경우, 아래와같이 접속했습니다.

접속에 성공하면 아래와 같이 뜹니다. 공포의 명령줄라인~! 이젠 두렵지 않죠?

2. 웹서버와 DB설치

 

정상적으로 접속에 성공했다면,

가상머신에 웹서버 flask를 설치합니다.

먼저 pip을 설치합니다.

* pip : 파이썬 패키지 소프트웨어를 설치하거나 관리하는

패키지 관리 라이브러리입니다.

sudo apt install python3-pip

웹서버 사용을 위해 다음 명령어로 flask를 설치합니다.

 

pip install flask

DB사용을 위해 다음 명령어로 pymongo를 설치합니다.

pip install pymongo

 

로컬에서 설치한 flask와 pymongo가

이제는 MS클라우드 VM에 설치가 되었습니다.

이제 로컬에서 처럼 브라우저 URL에 VM의 IP를 입력하시면 됩니다.

하지만, 먼저 배포를 해야 실행이 되겠죠?

정상적으로 웹서버와 DB가 설치되었다면, 

다음 순서로 넘어갑니다.

 

여기서 잠깐, 클라우드 서비스를 비교해볼까요?

우리는 지금 MS클라우드에서 배포를 준비하고 있습니다.

위와같이 서로 같은 역할을 하는 서비스가 존재합니다.

여기서는 위의 서비스를 사용하지않고, 가상머신을 만들어서 배포하는 방법을 배우겠습니다.

 

3. FTP접속방법

 

웹서버 셋팅도 끝나고, 이제 웹서버에 파일을 올려서 배포를 해봐야 겠죠?

파일 ftp접속을 위해 파일질라 프로그램을 실행합니다.

 

웹서버에 배포하기 위해 ftp 접속 설정을 합니다.

<FTP 연결 방법>
① 보안SFTP로 설정합니다.
② MS클라우드 가상머신(VM) IP주소를 설정합니다.
③ 보안 22번 포트번호를 설정합니다.
④ 키파일 접속방식으로 변경합니다.
⑤ MS클라우드 가상머신(VM) 접속 아이디를 입력합니다. 
⑥ MS클라우드 가상머신(VM) 생성시에
    만들었던 키파일(abc.pem) 경로를 입력합니다.
⑦ 입력이 끝났으면, 연결을 클릭합니다.

 

'연결' 버튼을 누른 후, 아래와 같이 접속이 되면 정상적으로 연결된 것입니다.

FTP 접속이 되었나요?

웹서버와 DB가 설치되었으므로, 이제 프로그램 파일을 올리시면 됩니다.

 

 

4. 배포하기(예제파일 소스)

 

예제파일 소스입니다.

예제파일은 3가지로 구성되어 있습니다.

 

app.py - 웹서버 실행

index.html - 프론트페이지

login.html - 프론트페이지

 

app.py

from pymongo import MongoClient
import jwt
import datetime
import hashlib
from flask import Flask, render_template, jsonify, request, redirect, url_for
from werkzeug.utils import secure_filename
from datetime import datetime, timedelta


app = Flask(__name__)
app.config["TEMPLATES_AUTO_RELOAD"] = True
app.config['UPLOAD_FOLDER'] = "./static/profile_pics"

SECRET_KEY = 'SPARTA'

client = MongoClient('18.188.71.218', 27017, username="sparta", password="qwer1234")
db = client.dbsparta_plus_week4


@app.route('/')
def home():
    token_receive = request.cookies.get('mytoken')
    try:
        payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
        user_info = db.users.find_one({"username": payload["id"]})
        return render_template('index.html', user_info=user_info)
    except jwt.ExpiredSignatureError:
        return redirect(url_for("login", msg="로그인 시간이 만료되었습니다."))
    except jwt.exceptions.DecodeError:
        return redirect(url_for("login", msg="로그인 정보가 존재하지 않습니다."))


@app.route('/login')
def login():
    msg = request.args.get("msg")
    return render_template('login.html', msg=msg)


@app.route('/user/<username>')
def user(username):
    # 각 사용자의 프로필과 글을 모아볼 수 있는 공간
    token_receive = request.cookies.get('mytoken')
    try:
        payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
        status = (username == payload["id"])  # 내 프로필이면 True, 다른 사람 프로필 페이지면 False

        user_info = db.users.find_one({"username": username}, {"_id": False})
        return render_template('user.html', user_info=user_info, status=status)
    except (jwt.ExpiredSignatureError, jwt.exceptions.DecodeError):
        return redirect(url_for("home"))


@app.route('/sign_in', methods=['POST'])
def sign_in():
    # 로그인
    username_receive = request.form['username_give']
    password_receive = request.form['password_give']

    pw_hash = hashlib.sha256(password_receive.encode('utf-8')).hexdigest()
    result = db.users.find_one({'username': username_receive, 'password': pw_hash})

    if result is not None:
        payload = {
         'id': username_receive,
         'exp': datetime.utcnow() + timedelta(seconds=60 * 60 * 24)  # 로그인 24시간 유지
        }
        token = jwt.encode(payload, SECRET_KEY, algorithm='HS256').decode('utf-8')

        return jsonify({'result': 'success', 'token': token})
    # 찾지 못하면
    else:
        return jsonify({'result': 'fail', 'msg': '아이디/비밀번호가 일치하지 않습니다.'})


@app.route('/sign_up/save', methods=['POST'])
def sign_up():
    username_receive = request.form['username_give']
    password_receive = request.form['password_give']
    password_hash = hashlib.sha256(password_receive.encode('utf-8')).hexdigest()
    doc = {
        "username": username_receive,
        "password": password_hash,
        "profile_name": username_receive,
        "profile_pic": "",
        "profile_pic_real": "profile_pics/profile_placeholder.png",
        "profile_info": ""
    }
    db.users.insert_one(doc)
    return jsonify({'result': 'success'})


@app.route('/sign_up/check_dup', methods=['POST'])
def check_dup():
    username_receive = request.form['username_give']
    exists = bool(db.users.find_one({"username": username_receive}))
    # print(value_receive, type_receive, exists)
    return jsonify({'result': 'success', 'exists': exists})


@app.route('/update_profile', methods=['POST'])
def save_img():
    token_receive = request.cookies.get('mytoken')
    try:
        payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
        username = payload["id"]
        name_receive = request.form["name_give"]
        about_receive = request.form["about_give"]
        new_doc = {
            "profile_name": name_receive,
            "profile_info": about_receive
        }
        if 'file_give' in request.files:
            file = request.files["file_give"]
            filename = secure_filename(file.filename)
            extension = filename.split(".")[-1]
            file_path = f"profile_pics/{username}.{extension}"
            file.save("./static/"+file_path)
            new_doc["profile_pic"] = filename
            new_doc["profile_pic_real"] = file_path
        db.users.update_one({'username': payload['id']}, {'$set':new_doc})
        return jsonify({"result": "success", 'msg': '프로필을 업데이트했습니다.'})
    except (jwt.ExpiredSignatureError, jwt.exceptions.DecodeError):
        return redirect(url_for("home"))


@app.route('/posting', methods=['POST'])
def posting():
    token_receive = request.cookies.get('mytoken')
    try:
        payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
        # 포스팅하기
        user_info = db.users.find_one({"username": payload["id"]})
        comment_receive = request.form["comment_give"]
        date_receive = request.form["date_give"]
        print(type(date_receive))
        doc = {
            "username": user_info["username"],
            "profile_name": user_info["profile_name"],
            "profile_pic_real": user_info["profile_pic_real"],
            "comment": comment_receive,
            "date": date_receive
        }
        db.posts.insert_one(doc)
        return jsonify({"result": "success", 'msg': '포스팅 성공'})
    except (jwt.ExpiredSignatureError, jwt.exceptions.DecodeError):
        return redirect(url_for("home"))


@app.route("/get_posts", methods=['GET'])
def get_posts():
    token_receive = request.cookies.get('mytoken')
    try:
        payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
        username_receive = request.args.get("username_give")
        if username_receive=="":
            posts = list(db.posts.find({}).sort("date", -1).limit(20))
        else:
            posts = list(db.posts.find({"username":username_receive}).sort("date", -1).limit(20))
        for post in posts:
            post["_id"] = str(post["_id"])
            post["count_heart"] = db.likes.count_documents({"post_id": post["_id"], "type": "heart"})
            post["heart_by_me"] = bool(db.likes.find_one({"post_id": post["_id"], "type": "heart", "username": my_username}))

        return jsonify({"result": "success", "msg": "포스팅을 가져왔습니다.", "posts": posts, "my_username":payload["id"]})
    except (jwt.ExpiredSignatureError, jwt.exceptions.DecodeError):
        return redirect(url_for("home"))


@app.route('/update_like', methods=['POST'])
def update_like():
    token_receive = request.cookies.get('mytoken')
    try:
        payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
        # 좋아요 수 변경
        user_info = db.users.find_one({"username": payload["id"]})
        post_id_receive = request.form["post_id_give"]
        type_receive = request.form["type_give"]
        action_receive = request.form["action_give"]
        doc = {
            "post_id": post_id_receive,
            "username": user_info["username"],
            "type": type_receive
        }
        if action_receive =="like":
            db.likes.insert_one(doc)
        else:
            db.likes.delete_one(doc)
        count = db.likes.count_documents({"post_id": post_id_receive, "type": type_receive})
        print(count)
        return jsonify({"result": "success", 'msg': 'updated', "count": count})
    except (jwt.ExpiredSignatureError, jwt.exceptions.DecodeError):
        return redirect(url_for("home"))


if __name__ == '__main__':
    app.run('0.0.0.0', port=5000, debug=True)

 

index.html

<!doctype html>
<html lang="en">
    <head>

        <!-- Webpage Title -->
        <title>Home | MS 클라우드 웹앱</title>
        <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
        <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
        <meta property="og:title" content="MS 클라우드 웹앱"/>
        <meta property="og:description" content="mini project for Web Plus"/>
        <meta property="og:image" content="{{ url_for('static', filename='ogimg.png') }}"/>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

        <!-- Bulma CSS -->
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
        <!-- Font Awesome CSS -->
        <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">

        <link rel="preconnect" href="https://fonts.gstatic.com">
        <link href="https://fonts.googleapis.com/css2?family=Gamja+Flower&family=Stylish&display=swap" rel="stylesheet">
        <link href="{{ url_for('static', filename='mystyle.css') }}" rel="stylesheet">

        <!-- JS -->
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script>
        <script src="{{ url_for('static', filename='myjs.js') }}"></script>


        <script>
            $(document).ready(function () {
                get_posts()
            })

        </script>

    </head>
    <body class="has-navbar-fixed-top">
        <nav class="navbar is-fixed-top is-white" role="navigation" aria-label="main navigation">
            <div class="navbar-brand">
                <a class="navbar-item" href="/">
                    <img src="{{ url_for('static', filename='logo.png') }}">
                    <strong class="is-sparta"
                            style="font-family: 'Stylish', sans-serif;font-size: larger;">MS 클라우드 웹앱</strong>
                </a>
            </div>
        </nav>
 <script>
$(document).ready(function(){
  var fileTarget = $('.filebox .upload-hidden');

    fileTarget.on('change', function(){
        if(window.FileReader){
            var filename = $(this)[0].files[0].name;
        } else {
            var filename = $(this).val().split('/').pop().split('\\').pop();
        }

        $(this).siblings('.upload-name').val(filename);
    });
});

 </script>

        <section class="section">
            <article class="media">
                <figure class="media-left" style="align-self: center">
              <a class="image is-32x32" href="/user/{{ user_info.username }}">
                  <img class="is-rounded" src="{{ url_for('static', filename=user_info.profile_pic_real) }}">
              </a>
                </figure>
                <div class="media-content">
                    <div class="field">
                        <p class="control">
                            <input id="input-post" class="input is-rounded" placeholder="무슨 생각을 하고 계신가요?"
                                   onclick='$("#modal-post").addClass("is-active")'>
                        </p>
                    </div>
                </div>
            </article>
            <div class="modal" id="modal-post">
                <div class="modal-background" onclick='$("#modal-post").removeClass("is-active")'></div>
                <div class="modal-content">
                    <div class="box">
                        <article class="media">
                            <div class="media-content">
                                <div class="field">
                                    <p class="control">
                                        <textarea id="textarea-post" class="textarea"
                                                  placeholder="무슨 생각을 하고 계신가요?"></textarea>
                                    </p>
                                </div>
                                <nav class="level is-mobile">
                                    <div class="level-left">

                                    </div>
                                    <div class="level-right">
                                        <div class="level-item">
                                            <a class="button is-sparta" onclick="post()">포스팅하기</a>
                                        </div>
                                        <div class="level-item">
                                            <a class="button is-sparta is-outlined"
                                               onclick='$("#modal-post").removeClass("is-active")'>취소</a>
                                        </div>
                                    </div>
                                </nav>
                            </div>
                        </article>
                    </div>
                </div>
                <button class="modal-close is-large" aria-label="close"
                        onclick='$("#modal-post").removeClass("is-active")'></button>
            </div>

        </section>
        <section class="section">
            <div id="post-box" class="container">
                <div class="box">
                    <article class="media">
                        <div class="media-left">
                            <a class="image is-64x64" href="#">
                                <img class="is-rounded"
                                     src={{ url_for("static", filename="profile_pics/profile_placeholder.png") }} alt="Image">
                            </a>
                        </div>
                        <div class="media-content">
                            <div class="content">
                                <p>
                                    <strong>홍길동</strong> <small>@username</small> <small>10분 전</small>
                                    <br>
                                    글을 적는 칸
                                </p>
                            </div>
                            <nav class="level is-mobile">
                                <div class="level-left">
                                    <a class="level-item is-sparta" aria-label="heart"
                                       onclick="toggle_like('', 'heart')">
                                        <span class="icon is-small"><i class="fa fa-heart"
                                                                       aria-hidden="true"></i></span>&nbsp;<span
                                            class="like-num">2.7k</span>
                                    </a>
                                </div>

                            </nav>
                        </div>
                    </article>
                </div>

            </div>
        </section>
    </body>
</html>

login.html

<!doctype html>
<html lang="en">
    <head>

        <!-- Webpage Title -->
        <title>Log In | MS 클라우드웹앱</title>
        <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
        <link rel="icon" href="{{ url_for('static', filename='favicon_new.ico') }}" type="image/x-icon">
        <meta property="og:title" content="MS클라우드 - 웹앱"/>
        <meta property="og:description" content="mini project for Web Plus"/>
        <meta property="og:image" content="{{ url_for('static', filename='ogimg.png') }}"/>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

        <!-- Bulma CSS -->
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
        <!-- Font Awesome CSS -->
        <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">

        <link rel="preconnect" href="https://fonts.gstatic.com">
        <link href="https://fonts.googleapis.com/css2?family=Gamja+Flower&family=Stylish&display=swap" rel="stylesheet">
        <link href="{{ url_for('static', filename='mystyle.css') }}" rel="stylesheet">

        <!-- JS -->
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script>


        <style>


            .title {
                font-weight: 800;
                font-size: 5rem;
                font-family: 'Stylish', sans-serif;
            }

            .subtitle {
                font-family: 'Gamja Flower', cursive;
                font-size: 2rem;

            }

            .help {
                color: gray;
            }
        </style>
        <script>
            // {% if msg %}
                //    alert("{{ msg }}")
                // {% endif %}

            function toggle_sign_up() {
                $("#sign-up-box").toggleClass("is-hidden")
                $("#div-sign-in-or-up").toggleClass("is-hidden")
                $("#btn-check-dup").toggleClass("is-hidden")
                $("#help-id").toggleClass("is-hidden")
                $("#help-password").toggleClass("is-hidden")
                $("#help-password2").toggleClass("is-hidden")
            }

            function is_nickname(asValue) {
                var regExp = /^(?=.*[a-zA-Z])[-a-zA-Z0-9_.]{2,10}$/;
                return regExp.test(asValue);
            }

            function is_password(asValue) {
                var regExp = /^(?=.*\d)(?=.*[a-zA-Z])[0-9a-zA-Z!@#$%^&*]{8,20}$/;
                return regExp.test(asValue);
            }

            function check_dup() {
                let username = $("#input-username").val()
                console.log(username)
                if (username == "") {
                    $("#help-id").text("아이디를 입력해주세요.").removeClass("is-safe").addClass("is-danger")
                    $("#input-username").focus()
                    return;
                }
                if (!is_nickname(username)) {
                    $("#help-id").text("아이디의 형식을 확인해주세요. 영문과 숫자, 일부 특수문자(._-) 사용 가능. 2-10자 길이").removeClass("is-safe").addClass("is-danger")
                    $("#input-username").focus()
                    return;
                }
                $("#help-id").addClass("is-loading")
                $.ajax({
                    type: "POST",
                    url: "/sign_up/check_dup",
                    data: {
                        username_give: username
                    },
                    success: function (response) {

                        if (response["exists"]) {
                            $("#help-id").text("이미 존재하는 아이디입니다.").removeClass("is-safe").addClass("is-danger")
                            $("#input-username").focus()
                        } else {
                            $("#help-id").text("사용할 수 있는 아이디입니다.").removeClass("is-danger").addClass("is-success")
                        }
                        $("#help-id").removeClass("is-loading")

                    }
                });
            }

            function sign_up() {
                let username = $("#input-username").val()
                let password = $("#input-password").val()
                let password2 = $("#input-password2").val()
                console.log(username, password, password2)


                if ($("#help-id").hasClass("is-danger")) {
                    alert("아이디를 다시 확인해주세요.")
                    return;
                } else if (!$("#help-id").hasClass("is-success")) {
                    alert("아이디 중복확인을 해주세요.")
                    return;
                }

                if (password == "") {
                    $("#help-password").text("비밀번호를 입력해주세요.").removeClass("is-safe").addClass("is-danger")
                    $("#input-password").focus()
                    return;
                } else if (!is_password(password)) {
                    $("#help-password").text("비밀번호의 형식을 확인해주세요. 영문과 숫자 필수 포함, 특수문자(!@#$%^&*) 사용가능 8-20자").removeClass("is-safe").addClass("is-danger")
                    $("#input-password").focus()
                    return
                } else {
                    $("#help-password").text("사용할 수 있는 비밀번호입니다.").removeClass("is-danger").addClass("is-success")
                }
                if (password2 == "") {
                    $("#help-password2").text("비밀번호를 입력해주세요.").removeClass("is-safe").addClass("is-danger")
                    $("#input-password2").focus()
                    return;
                } else if (password2 != password) {
                    $("#help-password2").text("비밀번호가 일치하지 않습니다.").removeClass("is-safe").addClass("is-danger")
                    $("#input-password2").focus()
                    return;
                } else {
                    $("#help-password2").text("비밀번호가 일치합니다.").removeClass("is-danger").addClass("is-success")
                }
                $.ajax({
                    type: "POST",
                    url: "/sign_up/save",
                    data: {
                        username_give: username,
                        password_give: password
                    },
                    success: function (response) {
                        alert("회원가입을 축하드립니다!")
                        window.location.replace("/login")
                    }
                });

            }

            function sign_in() {
                let username = $("#input-username").val()
                let password = $("#input-password").val()

                if (username == "") {
                    $("#help-id-login").text("아이디를 입력해주세요.")
                    $("#input-username").focus()
                    return;
                } else {
                    $("#help-id-login").text("")
                }

                if (password == "") {
                    $("#help-password-login").text("비밀번호를 입력해주세요.")
                    $("#input-password").focus()
                    return;
                } else {
                    $("#help-password-login").text("")
                }
                $.ajax({
                    type: "POST",
                    url: "/sign_in",
                    data: {
                        username_give: username,
                        password_give: password
                    },
                    success: function (response) {
                        if (response['result'] == 'success') {
                            $.cookie('mytoken', response['token'], {path: '/'});
                            window.location.replace("/")
                        } else {
                            alert(response['msg'])
                        }
                    }
                });
            }


        </script>

    </head>
    <body>
        <section class="hero is-white">
            <div class="hero-body has-text-centered" style="padding-bottom:1rem;margin:auto;">
                <h1 class="title is-sparta">MS 클라우드</h1>
                <h3 class="subtitle is-sparta">웹앱</h3>
            </div>
        </section>
        <section class="section">
            <div class="container">
                <div class="box" style="max-width: 480px;margin:auto">
                    <article class="media">
                        <div class="media-content">
                            <div class="content">
                                <div class="field has-addons">
                                    <div class="control has-icons-left" style="width:100%">
                                        <input id="input-username" class="input" type="text" placeholder="아이디">
                                        <span class="icon is-small is-left"><i class="fa fa-user"></i></span>
                                    </div>
                                    <div id="btn-check-dup" class="control is-hidden">
                                        <button class="button is-sparta" onclick="check_dup()">중복확인</button>
                                    </div>

                                </div>
                                <p id="help-id" class="help is-hidden">아이디는 2-10자의 영문과 숫자와 일부 특수문자(._-)만 입력 가능합니다.</p>
                                <p id="help-id-login" class="help is-danger"></p>

                                <div class="field">
                                    <div class="control has-icons-left">
                                        <input id="input-password" class="input" type="password" placeholder="비밀번호">
                                        <span class="icon is-small is-left"><i class="fa fa-lock"></i></span>
                                    </div>
                                    <p id="help-password" class="help is-hidden">영문과 숫자 조합의 8-20자의 비밀번호를 설정해주세요.
                                        특수문자(!@#$%^&*)도 사용 가능합니다.</p>
                                    <p id="help-password-login" class="help is-danger"></p>

                                </div>


                            </div>
                            <div id="div-sign-in-or-up" class="has-text-centered">
                                <nav class="level is-mobile">
                                    <button class="level-item button is-sparta" onclick="sign_in()">
                                        로그인
                                    </button>

                                </nav>
                                <hr>
                                <h4 class="mb-3">아직 회원이 아니라면</h4>
                                <nav class="level is-mobile">

                                    <button class="level-item button is-sparta is-outlined"
                                            onclick="toggle_sign_up()">
                                        회원가입하기
                                    </button>
                                </nav>
                            </div>

                            <div id="sign-up-box" class="is-hidden">
                                <div class="mb-5">
                                    <div class="field">
                                        <div class="control has-icons-left" style="width:100%">
                                            <input id="input-password2" class="input" type="password"
                                                   placeholder="비밀번호 재입력">
                                            <span class="icon is-small is-left"><i class="fa fa-lock"></i></span>
                                        </div>
                                        <p id="help-password2" class="help is-hidden">비밀번호를 다시 한 번 입력해주세요.</p>

                                    </div>
                                </div>
                                <nav class="level is-mobile">
                                    <button class="level-item button is-sparta" onclick="sign_up()">
                                        회원가입
                                    </button>
                                    <button class="level-item button is-sparta is-outlined" onclick="toggle_sign_up()">
                                        취소
                                    </button>
                                </nav>
                            </div>


                        </div>
                    </article>
                </div>

            </div>
        </section>

    </body>
</html>

 

아래와 같이 화면이 나오면 접속에 성공한 것입니다.

데이터베이스 연결과 로그인이 되는지 확인해봅시다.