웹개발 종합반_4주차
[ 4주차를 하기 앞서 ]
컴퓨터가 한대이기 때문에 같은 컴퓨터에 서버도 만들고 요청도 함
즉 클라이언트 = 서버가 되는 것. 이것을 로컬 개발환경이라고 함
하지만 DB는 mongoDB Atlas라는 클라우드 서비스를 이용하고 있기 때문에 총 그림을 이렇게 됨
[ Flask 시작하기 ]
어느정도 만들어져 있는 꾸러미들을 프레임워크라고 부름(라이브러리하고 결이 같다라고 생각해도 무방)
서버라는 큰 프로젝트를 만들기 위한 큰 라이브러리를 프레임 워크라고 부른다고 생각하면 됨
프레임워크는 라이브러리와 마찬가지로 만들어 둔 사람이 하라는 대로 차근차근해야 서버를 만들어갈 수 있음
현재 여기서 사용하는 프레임워크는 Flask라는 프레임 워크
Flask는 만들 프로젝트의 폴더구조가 정해져 있어 규칙을 꼭 지켜주어야 함
- 폴더 안에 app.py 파일을 생성
- 폴더 안에 templates 폴더를 생성
- templates 폴더 안에 index.html 파일을 생성
몇가지 규칙에 대해 자세히 살펴보면
- templates 폴더는 반드시 고정
- app.py는 변경해도 좋지만, 라이브러리 이름과 같은 것을 이름으로 사용하면 안됨
- index.html은 변경해도 좋지만, 첫페이지는 일반적으로 index.html을 사용함
▶ 서버 만들기
터미널에
python -m venv venv
입력하여 설치
가상환경 venv로 원하는 라이브러리만 설치해서 관리할 것임
인터프리터를 venv로 변경후 터미널을 다시 열어
pip install flask
입력하여 설치
Flask 프레임워크는 서버를 구동시켜주는 편한 코드 모음
웹서버를 구동하는데 필요한 복잡한 코드들을 쉽게 가져다 쓸 수 있음
Flask 시작코드
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return 'This is Home!'
if __name__ == '__main__':
app.run('0.0.0.0',port=5000,debug=True)
파이썬을 실행시켜서
가 나오면 잘 실행되는 것
웹페이지에 http://localhost:5000/으로 들어가면 return문인 This is Home이 뜰것임
서버를 종료하려면 터미널 창을 클릭하고 ctrl + c를 누르면 됨
인터넷 망에 오픈한것은 아니고 나한테만 오픈해놓고 자신만 들어가서 확인하고 있는 것임
URL 나눠보기
@app.route('/') 부분을 수정해서 URL을 나눌 수 있음
url 별로 함수명이 같거나 route('/')내의 주소가 같으면 안됨
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return 'This is Home!'
@app.route('/mypage')
def mypage():
return 'This is My Page!'
if __name__ == '__main__':
app.run('0.0.0.0',port=5000,debug=True)
▶ HTML 파일주기
Flask 서버를 만들 때 항상 프로젝트 폴더 안에 templates 폴더와 app.py 파일을 만들고 시작
templates 폴더는 HTML 파일을 담아두고, 불러오는 역할을 함
HTML파일 불러오기
- templates폴더를 생성하고 안에 Html파일을 만듬
- flask 내장함수 render_template를 이용하여 Html파일을 불러옴
from flask import Flask, render_template
app = Flask(__name__)
## URL 별로 함수명이 같거나,
## route('/') 등의 주소가 같으면 안됩니다.
@app.route('/')
def home():
return 'This is Home!'
@app.route('/mypage')
def mypage():
return render_template('index.html')
if __name__ == '__main__':
app.run('0.0.0.0', port=5000, debug=True)
▶ 본격 API 만들기
GET, POST 요청 타입 리마인드
클라이언트가 요청 할 때에도, "방식"이 존재함
클라이언트는 요청할 때 HTTP request method(요청 메소드)를 통해
어떤 요청 종류인지 응답하는 서버 쪽에 정보를 알려줌
- GET 요청: 통상적으로 데이터 조회(Read)를 요청할 때 사용
- 데이터 전달 : URL 뒤에 물음표를 붙여 key=value로 전달, ex) 영화 목록 조회
- POST 요청: 통상적으로 데이터 생성(Create), 변경(Update), 삭제(Delete) 요청 할 때 사용
- 데이터 전달 : 바로 보이지 않는 HTML, ex) 회원가입, 회원탈퇴, 비밀번호 수정
GET, POST 요청에서 클라이언트의 데이터를 받는 방법
예시로 요청을 어떻게 보내고 받는지 확인할 수 있는데 API코드는 .py에 Fetch는 .html파일에
html파일에는 jquery를 임포트해야함
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
GET 요청 API코드
@app.route('/test', methods=['GET'])
/testf라고 하는 창구로 GET요청이 들어올때
def test_get():
title_receive = request.args.get('title_give')
# title_given라는 데이터가 있으면 title_receive라는 변수에 넣음
print(title_receive)
return jsonify({'result':'success', 'msg': '이 요청은 GET!'})
GET 요청 확인 Fetch 코드
fetch("/test")
.then((res) => res.json())
.then((data) => {
console.log(data);
});
POST 요청 API코드
@app.route('/test', methods=['POST'])
def test_post():
title_receive = request.form['title_give']
print(title_receive)
return jsonify({'result':'success', 'msg': '이 요청은 POST!'})
POST 요청 확인 Fetch코드
let formData = new FormData();
// FormData라는 것을 만들어서 데이터를 실어서 보냄
formData.append("title_give", "블랙팬서");
fetch("/test", { method: "POST", body: formData })
.then((res) => res.json())
.then((data) => {
console.log(data);
});
[ 화성땅 공동구매 프로젝트 ]
▶ flask 폴더 구조 만들기
mars라는 폴더 안에 templates와 venv라는 폴더와 app.py파일이 있고
templates안에 index.html파일이 있음
venv는 폴더를 따로 만드는 것이 아니라 터미널에
python(맥 python3) -m venv venv를 입력하고 인터프리터를 venv로 설정해
가상환경을 생성하면 됨
▶ 패키지 설치
이번 프로젝트에 필요한 패키지 3가지: flask, pymongo, dnspython
터미널을 열어(venv가 활성화되어있는지 꼭 확인해야함)
pip install flask pymongo dnspython
여러개를 설치할 때는 띄어쓰기로 구분함
원하는 라이브러리를 확인해보려면 터미널에 pip freeze를 입력하고 엔터
▶ 뼈대 준비하기
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)
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>
mongoDB Atlas 창 띄어두기
MongoDB Cloud
MongoDB Cloud is a unified data platform for modern applications and includes a global cloud database, search, data lake, mobile, and application services.
www.mongodb.com
로그인한 뒤에 DB - Cluster0 - Collection 완료
▶ POST연습(주문 저장하기)
1. 데이터 명세
- 요청 정보 : URL= /mars, 요청 방식 = POST
- 클라(fetch) → 서버(flask) : name, address, size
- 서버(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>
3. 서버부터 만들기
데이터베이스에 연결. app.py 상단에 놓으면 됨
from pymongo import MongoClient
client = MongoClient('내 URL')
db = client.dbsparta
name, address, size 정보를 받아서, 저장
@app.route("/mars", methods=["POST"])
def mars_post():
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)
return jsonify({'msg': '주문 완료!'})
4. 클라이언트 만들기
name, address, size 정보를 formData에 데이터를 넣고, 보내줌
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()
});
}
5. 완성 확인하기
데이터를 입력하고 DB에 잘 들어갔는지 확인
▶ GET 연습(주문 보여주기)
1. 데이터 명세
- 요청 정보 : URL= /mars, 요청 방식 = GET
- 클라(fetch) → 서버(flask) : 없음
- 서버(flask) → 클라(fetch) : 전체 주문을 보내주기
2. 클라이언트와 서버 연결 확인하기
서버코드 - app.py
@app.route("/mars", methods=["GET"])
def mars_get():
return jsonify({'msg': 'GET 연결 완료!'})
클라이언트 코드 - index.html
$(document).ready(function() {
show_order()
})
function show_order() {
fetch('/mars').then((res) => res.json()).then((data) => {
console.log(data);
alert(data["msg"]);
});
}
3. 서버부터 만들기
받을 것 없이 result에 주문정보를 담아서 내려주기만 하면 됨
@app.route("/mars", methods=["GET"])
def mars_get():
mars_data = list(db.mars.find({},{'_id':False}))
return jsonify({'result':mars_data})
4. 클라이언트 만들기
주문정보는 리스트 형식이니 forEach문으로 반복하면서 데이터를 뽑아냄
뽑아낸 데이터는 temp_html에 담아 넣을 자리를 찾아 제이쿼리로 append
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. 완성 확인하기
동작테스트: 화면을 새로고침 했을 때, DB에 저장된 리뷰가 화면에 올바르게 나타나는지 확인
▶ 전체 완성 코드
app.py
from flask import Flask, render_template, request, jsonify
from pymongo import MongoClient
app = Flask(__name__)
client = MongoClient('내 mongoDB URL')
db = client.dbsparta
@app.route('/')
def home():
return render_template('index.html')
@app.route("/mars", methods=["POST"])
def mars_post():
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)
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) => {
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)
})
})
}
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>