웹 개발/웹개발종합

웹 개발 기초 7 - 배포하기

개발자명백 2023. 3. 7. 17:26

0. 프로젝트 세팅

Filezilla 다운로드(https://filezilla-project.org/download.php, 차단시 -> https://filezilla-project.org/download.php?type=client&show_all=1)

가비아 접속하기(https://www.gabia.com)

패키지 : flask, pymongo, dnspython

 

1. 요구명세

요청 : URL = /bucket, 요청방식 = POST
버킷리스트 기록 시 업데이트를 할 수 있도록 번호를 추가한다.

index.html : 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">

    <link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
          integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" rel="stylesheet">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script crossorigin="anonymous"
            integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
            src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"></script>

    <link href="https://fonts.googleapis.com/css2?family=Gowun+Dodum&display=swap" rel="stylesheet">

    <title>인생 버킷리스트</title>

    <style>
        * {
            font-family: 'Gowun Dodum', sans-serif;
        }

        .mypic {
            width: 100%;
            height: 200px;

            background-image: linear-gradient(0deg, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('https://images.unsplash.com/photo-1601024445121-e5b82f020549?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1189&q=80');
            background-position: center;
            background-size: cover;

            color: white;

            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
        }

        .mypic > h1 {
            font-size: 30px;
        }

        .mybox {
            width: 95%;
            max-width: 700px;
            padding: 20px;
            box-shadow: 0px 0px 10px 0px lightblue;
            margin: 20px auto;
        }

        .mybucket {
            display: flex;
            flex-direction: row;
            align-items: center;
            justify-content: space-between;
        }

        .mybucket > input {
            width: 70%;
        }

        .mybox > li {
            display: flex;
            flex-direction: row;
            align-items: center;
            justify-content: center;

            margin-bottom: 10px;
            min-height: 48px;
        }

        .mybox > li > h2 {
            max-width: 75%;
            font-size: 20px;
            font-weight: 500;
            margin-right: auto;
            margin-bottom: 0px;
        }

        .mybox > li > h2.done {
            text-decoration: line-through
        }
    </style>
    <script>
        $(document).ready(function () {
            show_bucket();
        });

        function show_bucket() {
            $.ajax({
                type: "GET",
                url: "/bucket",
                data: {},
                success: function (response) {
                    let bucket_list = response['buckets'];
                    let html = $('#bucket-list');
                    html.empty();

                    for (let i = 0; i < bucket_list.length; i++) {
                        let number = bucket_list[i]['num'];
                        let bucket = bucket_list[i]['bucket'];
                        let status = bucket_list[i]['done'];
                        let temp_html = '';
                        if (status == 0) {
                            temp_html = `<li>
                                                <h2>✅ ${bucket}</h2>
                                                <button class="btn btn-outline-primary" onclick="done_bucket(${number})" type="button">완료!</button>
                                            </li>`
                        } else {
                            temp_html = `<li>
                                                <h2 class="done">${bucket}</h2>
                                         </li>`
                        }
                        html.append(temp_html);
                    }
                }
            });
        }

        function save_bucket() {
            let bucket = $('#bucket').val();

            $.ajax({
                type: "POST",
                url: "/bucket",
                data: {bucket_give: bucket},
                success: function (response) {
                    alert(response["msg"]);
                    window.location.reload();
                }
            });
        }

        function done_bucket(num) {
            $.ajax({
                type: "POST",
                url: "/bucket/done",
                data: {num_give: num},
                success: function (response) {
                    alert(response["msg"]);
                    window.location.reload();
                }
            });
        }
    </script>
</head>
<body>
    <div class="mypic">
        <h1>나의 버킷리스트</h1>
    </div>
    <div class="mybox">
        <div class="mybucket">
            <input class="form-control" id="bucket" placeholder="이루고 싶은 것을 입력하세요" type="text">
            <button class="btn btn-outline-primary" onclick="save_bucket()" type="button">기록하기</button>
        </div>
    </div>
    <div class="mybox" id="bucket-list">
        <li>
            <h2>✅ 호주에서 스카이다이빙 하기</h2>
            <button class="btn btn-outline-primary" onclick="done_bucket(5)" type="button">완료!</button>
        </li>
        <li>
            <h2 class="done">✅ 호주에서 스카이다이빙 하기</h2>
        </li>
        <li>
            <h2>✅ 호주에서 스카이다이빙 하기</h2>
            <button class="btn btn-outline-primary" type="button">완료!</button>
        </li>
    </div>
</body>
</html>

app.py : 

from flask import Flask, render_template, request, jsonify
from pymongo import MongoClient
client = MongoClient('mongodb+srv://<<username>>:<<password>>@cluster0.klkai.mongodb.net/?retryWrites=true&w=majority')
db = client.dbsparta

app = Flask(__name__)

@app.route('/')
def home():
    return render_template('index.html')

@app.route("/bucket", methods=["POST"])
def bucket_post():
    bucket_receive = request.form['bucket_give']

    bucket_list = list(db.bucket.find({}, {'_id': False}))

    cnt = len(bucket_list) + 1

    doc = {
        'num':cnt,
        'bucket':bucket_receive,
        'done':0
    }
    db.bucket.insert_one(doc)

    return jsonify({'msg': '등록완료'})

@app.route("/bucket/done", methods=["POST"])
def bucket_done():
    num_receive = request.form['num_give']
    db.bucket.update_one({'num': int(num_receive)}, {'$set': {'done': 1}})
    return jsonify({'msg': '버켓 하나 클리어!'})

@app.route("/bucket", methods=["GET"])
def bucket_get():
    bucket_list = list(db.bucket.find({}, {'_id': False}))
    return jsonify({'buckets': bucket_list})

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

 

 

2. 배포하기 - EC2

AWS EC2란? : 클라우드 서버 컴퓨터 (https://ap-northeast-2.console.aws.amazon.com/ec2/v2/home?region=ap-northeast-2)

구매 방법 : Ubuntu 18.04 혹은 20.04를 선택 후 t2.micro(1년 무료 서비스)선택, key 생성(RSA, .pem)

접속하기 : (git bash) ssh -i </대충경로/mykey.pem> ubuntu@<생성된 인스턴스의 IPv4주소>

mkdir <dirname>
ls
cd <dirname>
cd ..

EC2 셋팅 : 

# python3 -> python
sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 10

# pip3 -> pip
sudo apt-get update
sudo apt-get install -y python3-pip
sudo update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1

# port forwarding
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 5000

FileZilla 연결하기 :

사이트관리자 -> 새 사이트 -> 이름 설정
프로토콜 : SFTP 
호스트 : AWS EC2 인스턴스의 IPv4 주소
포트 : 22
로그온유형 : 키파일
사용자 : ubuntu
키파일 : 파일 이름 우측 PEM 파일 설정 후 키파일 클릭

파이썬 파일을 이동시킨 후 python <파일명>.py 입력

 

3. 어플리케이션 업로드 하기

- app.py, templates, static을 FileZilla를 가지고 드래그 ->  EC2 서버에 업로드

- AWS EC2 인스턴스의 포트 개방 : 보안 -> 보안그룹 클릭 -> 인바운드 규칙 편집 -> (사용자지정 TCP 포트범위 5000, AnywhereIpv4 규칙)과 (사용자지정 TCP 포트범위 80, AnywhereIpv4 )추가 후 규칙 저장

* HTTP 요청은 기본이 80포트이다. 하지만 :80을 적지 않아도 자동으로 연결이 된다. 포트 번호를 입력하지 않아도 자동으로 접속할 수 있도록 되기위해 80포트로 오는 요청을 5000포트로 전달할 수 있다. 이를 포트포워딩(port forwarding)라고 한다. 리눅스는 기본으로 포트 포워딩을 제공하고 있어 외부 요청을 80포트로 받으면 우리 서버에 5000포트로 자동으로 전달할 수 있다. 이는 위에 port forwarding에서 실질적으로 작성되어 있다. 

pip install flask
pip install pymongo
pip install dnspython
python app.py

 

4. nohup 설정하기

Git bash를 종료할 경우 SSH 접속이 끊어집니다. 이 경우 프로세스는 종료되어 서버가 동작하지 않습니다. 원격 접속을 끊더라도 서버에서 계속 동작할 수 있도록 nohub을 설정해 줄 수 있습니다. 

nohup python app.py &

 

5. 서버와 nohub 종료하기

nohup이 설정되어 있지 않은 경우 Ctrl + C를 통해 종료할 수 있습니다. 

nohup가 설정되어 있는 경우 ps -ef | grep 'python app.py' | awk '{print $2}' | xargs kill

 

6. 정리

# python3 -> python
sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 10

# pip3 -> pip
sudo apt-get update
sudo apt-get install -y python3-pip
sudo update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1

# port forwarding
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 5000

# install package
pip install flask
pip install pymongo
pip install dnspython

# 실행하기
python app.py
nohup python app.py

# 강제종료
Ctrl + C
ps -ef | grep 'python app.py' | awk '{print $2}' | xargs kill

 

7. 도메인 연결하기

가비아는 네임서버를 운영하는 기업입니다. 숫자문자열을 IP주소와 매칭하는 서비스를 제공합니다. 

가비아 접속하기 (https://dns.gabia.com/) -> 도메인 연결, 설정 -> 호스트 = @, 값 = EC2인스턴스IPv4주소로 저장

 

8. og 태그 설정하기

<meta property="og:title" content="내 사이트의 제목" />
<meta property="og:description" content="보고 있는 페이지의 내용 요약" />
<meta property="og:image" content="이미지URL" />