Flask - 화성땅 공동구매

장현웅·2023년 8월 15일
0

EXAMPLE) Project 1. 화성땅 공동구매

1. Flask로 만들 프로젝트 폴더 구조 만들기

1) 프로젝트 폴더(MARS) 생성
2) 프로젝트 폴더 안에 app.py 파일 생성
3) 가상환경(venv) 만들고 활성화하기
4) Mars 폴더 안에 templates 폴더 생성
5) templates 폴더 안에 index.html 파일 생성

mars 폴더 구조
- - -
mars 
|— venv
|— app.py (서버)
|— templates
         |— index.html (클라이언트 파일)

6) 패키지 설치 (flask, pymongo, dnspython)
pip install flask pymongo dnspython (여러 개를 설치할 때는 띄어쓰기로 구분)

2. 프로젝트 준비 - app.py 준비

from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

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

@app.route("/mars", methods=["POST"])
def mars_post():
    sample_receive = request.form['sample_give']
    print(sample_receive)
    return jsonify({'msg':'POST 연결 완료!'})

@app.route("/mars", methods=["GET"])
def mars_get():
    return jsonify({'msg':'GET 연결 완료!'})

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

3. 프로젝트 준비 - index.html 준비하기

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

    <link href="https://fonts.googleapis.com/css2?family=Gowun+Batang:wght@400;700&display=swap" rel="stylesheet" />

    <title>선착순 공동구매</title>

    <style>
        * {
            font-family: "Gowun Batang", serif;
            color: white;
        }

        body {
            background-image: linear-gradient(0deg,
                    rgba(0, 0, 0, 0.5),
                    rgba(0, 0, 0, 0.5)),
                url("https://cdn.aitimes.com/news/photo/202010/132592_129694_3139.jpg");
            background-position: center;
            background-size: cover;
        }

        h1 {
            font-weight: bold;
        }

        .order {
            width: 500px;
            margin: 60px auto 0px auto;
            padding-bottom: 60px;
        }

        .mybtn {
            width: 100%;
        }

        .order>table {
            margin: 40px 0;
            font-size: 18px;
        }

        option {
            color: black;
        }
    </style>
    <script>
        $(document).ready(function () {
            show_order();
        });
        function show_order() {
            fetch('/mars').then((res) => res.json()).then((data) => {
                console.log(data)
                alert(data['msg'])
            })
        }
        function save_order() {
            let formData = new FormData();
            formData.append("sample_give", "샘플데이터");

            fetch('/mars', { method: "POST", body: formData }).then((res) => res.json()).then((data) => {
                console.log(data);
                alert(data["msg"]);
            });
        }
    </script>
</head>

<body>
    <div class="mask"></div>
    <div class="order">
        <h1>화성에 땅 사놓기!</h1>
        <h3>가격: 평 당 500원</h3>
        <p>
            화성에 땅을 사둘 수 있다고?<br />
            앞으로 백년 간 오지 않을 기회. 화성에서 즐기는 노후!
        </p>
        <div class="order-info">
            <div class="input-group mb-3">
                <span class="input-group-text">이름</span>
                <input id="name" type="text" class="form-control" />
            </div>
            <div class="input-group mb-3">
                <span class="input-group-text">주소</span>
                <input id="address" type="text" class="form-control" />
            </div>
            <div class="input-group mb-3">
                <label class="input-group-text" for="size">평수</label>
                <select class="form-select" id="size">
                    <option selected>-- 주문 평수 --</option>
                    <option value="10평">10평</option>
                    <option value="20평">20평</option>
                    <option value="30평">30평</option>
                    <option value="40평">40평</option>
                    <option value="50평">50평</option>
                </select>
            </div>
            <button onclick="save_order()" type="button" class="btn btn-warning mybtn">
                주문하기
            </button>
        </div>
        <table class="table">
            <thead>
                <tr>
                    <th scope="col">이름</th>
                    <th scope="col">주소</th>
                    <th scope="col">평수</th>
                </tr>
            </thead>
            <tbody id="order-box">
                <tr>
                    <td>홍길동</td>
                    <td>서울시 용산구</td>
                    <td>20평</td>
                </tr>
                <tr>
                    <td>임꺽정</td>
                    <td>부산시 동구</td>
                    <td>10평</td>
                </tr>
                <tr>
                    <td>세종대왕</td>
                    <td>세종시 대왕구</td>
                    <td>30평</td>
                </tr>
            </tbody>
        </table>
    </div>
</body>

</html>

4. 프로젝트 준비 - mongoDB Atlas 창 띄워두기

5. app.py 터미널에서 실행해보고 브라우저 localhost:5000 들어가보기

→ 이름, 주소, 평수의 데이터를 '주문하기'버튼을 눌렀을 때(URL = /mars, 요청 방식 = POST), index.html(클라이언트)에서 'Fetch'로 데이터를 가져온다.
→ 클라이언트(index.html)에서 서버로 가져온 데이터(name, adress, size)를 저장(flask)
→ 데이터가 잘 저장되었다면 '저장되었다'라는 데이터를 내려주고(서버(flask) → 클라(fetch) : 메시지를 보냄 (주문 완료!))
→ alert에서 띄워준다.
→ Refresh 해주면 끝

6. [화성땅 공동구매] - POST연습(주문 저장하기)

(데이터 저장하기와 불러오기를 연습한다면, 저장하기를 먼저 연습해라. 불러오기를 먼저 연습한다면 저장되어있는 데이터가 없는 경우 불러오기가 된건지 안된건지 햇갈린다.)

1) 데이터 명세

    1. 요청 정보 : URL= /mars, 요청 방식 = POST
    1. 클라(fetch) → 서버(flask) : name, address, size
    1. 서버(flask) → 클라(fetch) : 메시지를 보냄 (주문 완료!)

2) 클라이언트와 서버 연결 확인하기

[서버 코드 - app.py]

@app.route("/mars", methods=["POST"])
def mars_post():
    sample_receive = request.form['sample_give']
    print(sample_receive)
    return jsonify({'msg': 'POST 연결 완료!'})

[클라이언트 코드 - index.html]

function save_order() {
    let formData = new FormData();
		formData.append("sample_give", "샘플데이터");

    fetch('/mars', {method: "POST",body: formData,}).then((res) => res.json()).then((data) => {
            console.log(data);
            alert(data["msg"]);
          });
}

<button onclick="save_order()" type="button" class="btn btn-warning mybtn">주문하기</button>

→ 어느 버튼에 들어가있는지 확인

<button onclick="save_order()" type="button" class="btn btn-warning mybtn">주문하기</button>

→ "save_order()"함수 확인

function save_order() {
    let formData = new FormData();
		formData.append("sample_give", "샘플데이터");

    fetch('/mars', {method: "POST",body: formData,}).then((res) => res.json()).then((data) => {
            console.log(data);
            alert(data["msg"]);
          });
}

→ "샘플데이터"라는 데이터를 만들어서 formData라는 변수에 넣고 "sample_give"라는 이름으로 주고 있다(append).

let formData = new FormData();
	formData.append("sample_give", "샘플데이터");

→ 그 데이터를 실어서 '/mars'(URL)에 formData를 POST라는 방식으로(method: "POST") 요청함 = 서버(백엔드)로 가져옴(fetch).

fetch('/mars', {method: "POST",body: formData,}).then((res) => res.json()).then((data) =>

→ app.py(백엔드)에서 보면 "/mars"라는 이름으로 데이터(request.form['sample_give'])가 들어옴(flask).

@app.route("/mars", methods=["POST"])
def mars_post():
    sample_receive = request.form['sample_give']

→ 'sample_give'가 데이터를 찾음 (formData.append("sample_give", "샘플데이터"))

sample_receive = request.form['sample_give']
sample_receive = '샘플데이터'

→ '샘플데이터'가 터미널에 찍히고

print(sample_receive)

→ 메세지({'msg':'POST 연결 완료!'})를 내려줌.(서버(flask) → 클라(fetch))

return jsonify({'msg':'POST 연결 완료!'})

→ 내려준 메시지는 클라이언트(index.html)의 console.log(data)에 들어옴.

console.log(data);
console.log({'msg':'POST 연결 완료!'});

→ 이 데이터는 딕셔너리. 이 데이터 값의 'msg'만 가지고 alert를 띄워준다.

alert(data["msg"]);

3) 서버부터 만들기 (요청하기 전에 받을 곳 먼저 만들기)

서버가 할 일은 name, adress, size를 받아서 db에 넣어주는 것

→ 클라이언트와 서버 연결 확인할 때 'sample_give'로 받아왔던 것처럼 이번엔 'name_give', 'address_give', 'size_give'로 받아와서 'name_receive', 'address_receive', 'size_receive'의 형태로 각각의 변수에 넣어보자.

@app.route("/mars", methods=["POST"])
def mars_post():
    #sample_receive = request.form['sample_give']
    name_receive = request.form['name_give']
    address_receive = request.form['address_give']
    size_receive = request.form['size_give']
    #print(sample_receive)
    return jsonify({'msg': 'POST 연결 완료!'})

→ pymongo를 활용해서 받은 데이터들을 db에 넣어야한다. pymongo import 코드를 넣어준다.

from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

from pymongo import MongoClient
client = MongoClient('mongodb+srv://sparta:test@cluster0.wop0dox.mongodb.net/?retryWrites=true&w=majority')
db = client.dbsparta

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

@app.route("/mars", methods=["POST"])
def mars_post():
    #sample_receive = request.form['sample_give']
    name_receive = request.form['name_give']
    address_receive = request.form['address_give']
    size_receive = request.form['size_give']

    #print(sample_receive)
    return jsonify({'msg': 'POST 연결 완료!'})
@app.route("/mars", methods=["GET"])
def mars_get():
    return jsonify({'msg':'GET 연결 완료!'})

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

→ 이제 document를 만들어서 insert_one(doc)해주면 된다. pymongo로 데이터 db에 저장하기 코드로 받은 데이터들을 넣어준 변수의 값들을 딕셔너리 형태로 넣어준다.

    name_receive = request.form['name_give']
    address_receive = request.form['address_give']
    size_receive = request.form['size_give']
    
	doc = {
    	'name':name_receive,
        'address':address_receive,
        'size':size_receive
    }

→ 이제 insert_one(doc)으로 db에 mars 프로젝트에 넣어준다.

db.mars.insert_one(doc)

→ db에 저장이 완료 되었다는 데이터의 'msg' 값을 '저장완료!'로 변경한다.

return jsonify({'msg': '저장완료'})

4) 클라이언트 만들기(서버가 클라이언트의 요청을 처리하기 위해 클라이언트로부터 넘겨 받을 데이터를 담아야한다.)

index.html(클라이언트) 안의 '이름'에 대한 input값, '주소'에 대한 input값, '평수'에 대한 select값을 'name_give', 'address_give', 'size_give'의 형태로 formData에 담아준다.

→ index.html에서 save_order()버튼 함수를 수정한다.

function save_order() {
            let formData = new FormData();
            formData.append("sample_give", "샘플데이터");

            fetch('/mars', { method: "POST", body: formData }).then((res) => res.json()).then((data) => {
                console.log(data);
                alert(data["msg"]);
            });
        }

→ 화성땅 공동구매 페이지에서 사용자가 입력한 '이름','주소','평수' 값을 "샘플데이터"의 형태로 담아주기 위해서 Jquery를 이용해서 각 태그에 지정되어있는 id값을 가리켜서 각각의 값들을 각각의 변수에 담아주고 formData에 append해준다.

<div class="order-info">
            <div class="input-group mb-3">
                <span class="input-group-text">이름</span>
                <input id="name" type="text" class="form-control" />
            </div>
            <div class="input-group mb-3">
                <span class="input-group-text">주소</span>
                <input id="address" type="text" class="form-control" />
            </div>
            <div class="input-group mb-3">
                <label class="input-group-text" for="size">평수</label>
                <select class="form-select" id="size">
                    <option selected>-- 주문 평수 --</option>
                    <option value="10평">10평</option>
                    <option value="20평">20평</option>
                    <option value="30평">30평</option>
                    <option value="40평">40평</option>
                    <option value="50평">50평</option>
                </select>
            </div>
function save_order() {
        let name = $("#name").val()
        let address = $("#address").val()
        let size = $("#size").val()

        let formData = new FormData()
        formData.append("name_give", name)
        formData.append("address_give", address)
        formData.append("size_give", size)

        fetch('/mars', {method: "POST",body: formData,}).then((res) => res.json()).then((data) => {
            alert(data["msg"])
            window.location.reload() 모든 처리 완료 후 페이지가 새로고침 된다.
          });
      }

→ 잘 되는지 브라우저에서 주문해보고 mongoDB에서 확인해보자.

7. [화성땅 공동구매] - GET 연습(주문 보여주기)

(POST요청으로 DB에 데이터가 저장되고 저장완료! alert가 뜬 후 Refresh가 되어 페이지가 로딩되면 자동으로 fetch로 데이터를 날려 GET요청을 해서 데이터를 받아와서 보여준다.)

1) 데이터 명세

    1. 요청 정보 : URL= /mars, 요청 방식 = GET
    1. 클라(fetch) → 서버(flask) : 없음
    1. 서버(flask) → 클라(fetch) : 전체 주문을 보내주기

2) 클라이언트와 서버 연결 확인하기

[서버 코드 - app.py]

@app.route("/mars", methods=["GET"]) // 3. 클라이언트가 '/mars'로 fetch를 통해 데이터를 날림.
def mars_get():
    return jsonify({'msg': 'GET 연결 완료!'}) // 4. 데이터의 msg값을 내려줌.

[클라이언트 코드 - index.html]

$(document).ready(function() { // 1. 페이지가 로딩되면,
	show_order() // 2. show_order()가 불려옴.
})
function show_order() {
    fetch('/mars').then((res) => res.json()).then((data) => { // 3. '/mars'에 데이터를 날린다(fetch).
            console.log(data); 5. 내려준 데이터({'msg': 'GET 연결 완료!'})를 콘솔에 출력
            alert(data["msg"]); 6. 내려준 데이터의 msg값('GET 연결 완료!')을 알림으로 띄움.
          });
}

3) 서버부터 만들기

서버는 내가 가지고 있는 데이터를 다 내려준다.

→ 일단, DB에서 데이터를 서버로 다 가져옴.(pymongo 사용)

@app.route("/mars", methods=["GET"])
def mars_get():
    mars_data = list(db.mars.find({},{'_id':False})) # db의 mars에 있는 모든 데이터를 가져와서 mars_data라는 변수에 담는다.
    return jsonify({'result':mars_data}) # 클라이언트(index.html)에 데이터 값을 내려준다.

4) 클라이언트 만들기

→ 클라이언트로 데이터가 잘 내려갔는지 확인

        $(document).ready(function () {
            show_order();
        });
        function show_order() {
            fetch('/mars').then((res) => res.json()).then((data) => {
                console.log(data) // 콘솔에서만 확인해보자.
                //alert(data['msg']) 내려주는 데이터가 'msg'값이 아니기 때문에 없애줌.
            })
        }

리스트의 형태로 가져옴.

→ 리스트에 저장되는 데이터들이 많아졌을 경우를 생각해보면, 페이지에 '이름','주소','평수'를 각각 출력하기 위해서는, 서버에서 내려온 리스트 형태의 데이터를 forEach반복문을 통해 하나씩 뽑아내서 그 데이터의 '이름','주소','평수'의 값을 각각의 변수에 담고 붙여주고 싶은 html의 형태로 jQuery를 사용해서 변수 temp_html에 담아 append해주면 된다.

→ 서버에서 내려온 리스트 형태의 데이터를 forEach반복문을 통해 하나씩 뽑아서 콘솔에 찍어보자.

$(document).ready(function () {
            show_order();
        });
        function show_order() {
            fetch('/mars').then((res) => res.json()).then((data) => {
              let rows = data['result']
              rows.forEach((a) => {
                console.log(a)
              })
            })
        }

→ 붙여주고 싶은 html에는 데이터의 '이름','주소','평수' 각각 따로 붙여줘야 하니까 각각의 변수에 담고 temp_html에 원하는 html의 형태로 백틱으로 감싸서 담아서 id="order-box"에 붙여준다.

$(document).ready(function () {
  show_order();
});
function show_order() {
  fetch('/mars').then((res) => res.json()).then((data) => {
    let rows = data['result']
    rows.forEach((a) => {
      //console.log(a)
      let name = a["name"]
      let address = a["address"]
      let size = a["size"]

      let temp_html = `<tr>
                    <td>${name}</td>
                    <td>${address}</td>
                    <td>${size}</td>
                  </tr>`
      $("#order-box").append(temp_html)            
    })
    // console.log(data)
    //alert(data['msg'])
  })
}

→ 브라우저에서 서버에서 내려온 데이터가 출력되는지 보자.

→ 원래 id="order-box"에 있던 것들은 반복문으로 돌기 .empty()로 지워준다.

function show_order() {
        fetch('/mars').then((res) => res.json()).then((data) => {
            let rows = data['result']
						$("#order-box").empty()
	            rows.forEach((a) => {
              let name = a["name"]
              let address = a["address"]
              let size = a["size"]

              let temp_html = `<tr>
                        <td>${name}</td>
                        <td>${address}</td>
                        <td>${size}</td>
                      </tr>`
              $("#order-box").append(temp_html)
            })
          })
      }

5) 완성 확인하기

→ 새로운 주문으로 잘 작동하는지 확인해보자.


지금 이 사이트는 내 컴퓨터에서만 작동하지만 나중에는 인터넷상에 올려보겠다.

  • app.py - 화성땅 공동구매 서버
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

from pymongo import MongoClient
client = MongoClient('mongodb+srv://sparta:test@cluster0.wop0dox.mongodb.net/?retryWrites=true&w=majority')
db = client.dbsparta

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

@app.route("/mars", methods=["POST"])
def mars_post():
    #sample_receive = request.form['sample_give']
    name_receive = request.form['name_give']
    address_receive = request.form['address_give']
    size_receive = request.form['size_give']
    
    doc = {
    	  'name':name_receive,
        'address':address_receive,
        'size':size_receive
    }
    db.mars.insert_one(doc)

    #print(sample_receive)
    return jsonify({'msg': '저장완료'})

@app.route("/mars", methods=["GET"])
def mars_get():
    mars_data = list(db.mars.find({},{'_id':False}))
    return jsonify({'result':mars_data})

if __name__ == '__main__':
    app.run('0.0.0.0', port=5000, debug=True)
  • index.html - 화성땅 공동구매 클라이언트
<!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>

  <link href="https://fonts.googleapis.com/css2?family=Gowun+Batang:wght@400;700&display=swap" rel="stylesheet" />

  <title>선착순 공동구매</title>

  <style>
    * {
      font-family: "Gowun Batang", serif;
      color: white;
    }

    body {
      background-image: linear-gradient(0deg,
          rgba(0, 0, 0, 0.5),
          rgba(0, 0, 0, 0.5)),
        url("https://cdn.aitimes.com/news/photo/202010/132592_129694_3139.jpg");
      background-position: center;
      background-size: cover;
    }

    h1 {
      font-weight: bold;
    }

    .order {
      width: 500px;
      margin: 60px auto 0px auto;
      padding-bottom: 60px;
    }

    .mybtn {
      width: 100%;
    }

    .order>table {
      margin: 40px 0;
      font-size: 18px;
    }

    option {
      color: black;
    }
  </style>
  <script>
    $(document).ready(function () {
      show_order();
    });
    function show_order() {
      fetch('/mars').then((res) => res.json()).then((data) => {
        let rows = data['result']
        $("#order-box").empty()
        rows.forEach((a) => {
          //console.log(a)
          let name = a["name"]
          let address = a["address"]
          let size = a["size"]

          let temp_html = `<tr>
                        <td>${name}</td>
                        <td>${address}</td>
                        <td>${size}</td>
                      </tr>`
          $("#order-box").append(temp_html)            
        })
        // console.log(data)
        //alert(data['msg'])
      })
    }
    function save_order() {
      let name = $("#name").val()
      let address = $("#address").val()
      let size = $("#size").val()

      let formData = new FormData()
      formData.append("name_give", name)
      formData.append("address_give", address)
      formData.append("size_give", size)

      fetch('/mars', { method: "POST", body: formData, }).then((res) => res.json()).then((data) => {
        alert(data["msg"])
        window.location.reload()
      });
    }
  </script>
</head>

<body>
  <div class="mask"></div>
  <div class="order">
    <h1>화성에 땅 사놓기!</h1>
    <h3>가격: 평 당 500원</h3>
    <p>
      화성에 땅을 사둘 수 있다고?<br />
      앞으로 백년 간 오지 않을 기회. 화성에서 즐기는 노후!
    </p>
    <div class="order-info">
      <div class="input-group mb-3">
        <span class="input-group-text">이름</span>
        <input id="name" type="text" class="form-control" />
      </div>
      <div class="input-group mb-3">
        <span class="input-group-text">주소</span>
        <input id="address" type="text" class="form-control" />
      </div>
      <div class="input-group mb-3">
        <label class="input-group-text" for="size">평수</label>
        <select class="form-select" id="size">
          <option selected>-- 주문 평수 --</option>
          <option value="10평">10평</option>
          <option value="20평">20평</option>
          <option value="30평">30평</option>
          <option value="40평">40평</option>
          <option value="50평">50평</option>
        </select>
      </div>
      <button onclick="save_order()" type="button" class="btn btn-warning mybtn">
        주문하기
      </button>
    </div>
    <table class="table">
      <thead>
        <tr>
          <th scope="col">이름</th>
          <th scope="col">주소</th>
          <th scope="col">평수</th>
        </tr>
      </thead>
      <tbody id="order-box">
        <tr>
          <td>홍길동</td>
          <td>서울시 용산구</td>
          <td>20평</td>
        </tr>
        <tr>
          <td>임꺽정</td>
          <td>부산시 동구</td>
          <td>10평</td>
        </tr>
        <tr>
          <td>세종대왕</td>
          <td>세종시 대왕구</td>
          <td>30평</td>
        </tr>
      </tbody>
    </table>
  </div>
</body>

</html>

0개의 댓글