[스파르타코딩클럽] 웹개발 종합반 - 4주 차
Flask 시작하기 - 본격 API 만들기
클라이언트가 요청할 때 "방식"이 존재한다.
HTTP라는 통신 규약을 따른다.
클라이언트는 요청할 때 HTTP request method(요청 메소드)를 통해서 어떤 요청 종류인지 응답하는 서버 쪽에 정보를 알려주는 것이다.
여러 방식이 존재하지만, 가장 많이 쓰이는 GET, POST 방식에 대해 다루어보겠다.
GET 요청
통상적으로 데이터 조회(Read)를 요청할 때 사용한다.
ex) 영화 목록 조회
→ 데이터 전달 : URL 뒤에 물음표를 붙여 key=value로 전달
POST 요청
통상적으로 데이터 생성(Create), 변경(Update), 삭제(Delete) 요청할 때 사용한다.
ex) 회원가입, 회원탈퇴, 비밀번호 수정
→ 데이터 전달 : 바로 보이지 않는 HTML
[화성땅 공동구매] - 프로젝트
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
mars 폴더 구조
- - -
prac
- venv
- app.py (서버)
- templates
- index.html (클라이언트 파일)
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
Flask는 만들 프로젝트의 폴더 구조가 정해져 있다.
-폴더 안에 app.py 파일을 생성한다.
-폴더 안에 templates 폴더를 생성한다.
-templates 폴더 안에 index.html 파일을 생성한다.
templates 폴더는 반드시 고정해야 한다.
app.py는 변경해도 좋지만, 라이브러리 이름과 같은 것을 이름으로 사용하면 안된다.
index.html도 변경해도 좋지만, 첫 페이지는 일반적으로 index.html을 사용한다.
필요한 패키지는 3개 : flask, pymongo, dnspython
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 창을 띄워둔다.
[화성땅 공동구매] - POST연습
API 만들고 사용하기 - 이름, 주소, 평수 저장하기(Create → POST)
데이터 생성은 POST 방식을 사용한다.
- 요청 정보 : URL= /mars, 요청 방식 = POST
- 클라이언트(fetch) → 서버(flask) : name, address, size
- 서버(flask) → 클라이언트(fetch) : 메시지를 보냄 (주문 완료!)
클라이언트와 서버 연결을 확인한다.
@app.route("/mars", methods=["POST"])
def mars_post():
sample_receive = request.form['sample_give']
print(sample_receive)
return jsonify({'msg': 'POST 연결 완료!'})
서버 코드
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>
클라이언트 코드
from pymongo import MongoClient
client = MongoClient('내 URL')
db = client.dbsparta
위 코드를 입력해서 DB에 연결해 준다.
@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': '주문 완료!'})
'name', 'address', 'size'의 정보를 받아 DB에 저장한다.
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();
});
}
서버에서 받을 'name', 'address', 'size'의 정보를 formData에 데이터를 넣고, 보내준다.

DB에 잘 들어간 것을 확인할 수 있다.
[화성땅 공동구매] - GET 연습(주문 보여주기)
API 만들고 사용하기 - 저장된 주문을 화면에 보여주기(Read → GET)
요청 정보 : URL= /mars, 요청 방식 = GET
클라이언트(fetch) → 서버(flask) : 없음
서버(flask) → 클라이언트(fetch) : 전체 주문을 보내주기
서버와 클라이언트의 연결을 확인한다.
@app.route("/mars", methods=["GET"])
def mars_get():
return jsonify({'msg': 'GET 연결 완료!'})
서버 코드
$(document).ready(function() {
show_order()
})
function show_order() {
fetch('/mars').then((res) => res.json()).then((data) => {
console.log(data);
alert(data["msg"]);
});
}
클라이언트 코드
그리고,
@app.route("/mars", methods=["GET"])
def mars_get():
mars_data = list(db.mars.find({},{'_id':False}))
return jsonify({'result':mars_data})
서버를 만든다.
받을 것 없이 result에 주문정보를 담아서 내려주기만 하면 된다.
다음으로,
function show_order() {
$("#order-box").empty();
fetch('/mars').then((res) => res.json()).then((data) => {
let rows = data['result']
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);
});
});
}
클라이언트를 만든다.
forEach문으로 반복하면서 데이터를 뽑아서 temp_html에 담은 후, 담아준 temp_html을 넣을 자리를 찾아 제이쿼리로 append를 사용한다.
그리고, 화면을 새로고침 했을 때, DB에 저장된 리뷰가 화면에 올바르게 나타나는지 확인한다.
완성 코드
app.py(서버)
from flask import Flask, render_template, request, jsonify
from pymongo import MongoClient
app = Flask(__name__)
client = MongoClient('내 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() {
$("#order-box").empty();
fetch('/mars').then((res) => res.json()).then((data) => {
let rows = data['result']
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) => {
console.log(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>
[스파르타피디아] - 프로젝트
조각 기능 구현해 보기
프로젝트 내부에서 꼭 만들 필요는 없다.
flask 라이브러리를 사용하지 않는 기능이 있다면 app.py 밖에서 조각으로 구현해 보자.
일일이 테스트를 flask에서 브라우저를 열고 진행하지 않아서 더 빠르다.
원래 있던 서버 파일이 아니라 다른 파일을 만들어서 그곳에 코드를 작업해서 성공하면 가져오는 것이다.
웹스크래핑을 사용해 URL에서 페이지 정보 가져오기
스파르타피디아에서는 영화 url을 사용했다.
meta 태그를 활용
- meta태그는 눈에 보이는 것 이외의 웹의 속성을 설명해 주는 태그이다.

예시로 위에 사진에서 보이는 세 가지 정보를 들 수 있다.
차례대로
썸네일 사진 - og:image
썸네일 제목 - og:title
썸네일 설명 - og:description
이 meta 태그를 활용해서 크롤링을 해보았다.
조각 기능을 구현해서 말이다.
app.py가 아닌 새로운 파일을 만들고,
import requests
from bs4 import BeautifulSoup
url = '영화 url
headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get(url,headers=headers)
soup = BeautifulSoup(data.text, 'html.parser')
크롤링 기본 코드와
og_image = soup.select_one('meta[property="og:image"]')['content']
og_title = soup.select_one('meta[property="og:title"]')['content']
og_description = soup.select_one('meta[property="og:description"]')['content']
select_one을 이용해 meta 태그를 가져온다,
이제 본격적인 스파르타피디아를 만든다.
서버 기본코드와
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)
@app.route('/')
def home():
return render_template('index.html')
@app.route("/movie", methods=["POST"])
def movie_post():
sample_receive = request.form['sample_give']
print(sample_receive)
return jsonify({'msg':'POST 연결 완료!'})
@app.route("/movie", methods=["GET"])
def movie_get():
return jsonify({'msg':'GET 연결 완료!'})
if __name__ == '__main__':
app.run('0.0.0.0', port=5000, debug=True)
클라이언트 기본 코드를 입력해 준다.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<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=Gowun+Dodum&display=swap" rel="stylesheet">
<style>
* {
font-family: 'Gowun Dodum', sans-serif;
}
.mytitle {
width: 100%;
height: 250px;
background-image: linear-gradient(0deg, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('https://movie-phinf.pstatic.net/20210715_95/1626338192428gTnJl_JPEG/movie_image.jpg');
background-position: center;
background-size: cover;
color: white;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.mytitle > button {
width: 200px;
height: 50px;
background-color: transparent;
color: white;
border-radius: 50px;
border: 1px solid white;
margin-top: 10px;
}
.mytitle > button:hover {
border: 2px solid white;
}
.mycomment {
color: gray;
}
.mycards {
margin: 20px auto 0px auto;
width: 95%;
max-width: 1200px;
}
.mypost {
width: 95%;
max-width: 500px;
margin: 20px auto 0px auto;
padding: 20px;
box-shadow: 0px 0px 3px 0px gray;
display: none;
}
.mybtns {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-top: 20px;
}
.mybtns > button {
margin-right: 10px;
}
</style>
<script>
$(document).ready(function(){
listing();
});
function listing() {
fetch('/movie').then((res) => res.json()).then((data) => {
console.log(data)
alert(data['msg'])
})
}
function posting() {
let formData = new FormData();
formData.append("sample_give", "샘플데이터");
fetch('/movie', {method : "POST",body : formData}).then((res) => res.json()).then((data) => {
console.log(data)
alert(data['msg'])
})
}
function open_box(){
$('#post-box').show()
}
function close_box(){
$('#post-box').hide()
}
</script>
</head>
<body>
<div class="mytitle">
<h1>내 생애 최고의 영화들</h1>
<button onclick="open_box()">영화 기록하기</button>
</div>
<div class="mypost" id="post-box">
<div class="form-floating mb-3">
<input id="url" type="email" class="form-control" placeholder="name@example.com">
<label>영화URL</label>
</div>
<div class="form-floating">
<textarea id="comment" class="form-control" placeholder="Leave a comment here"></textarea>
<label for="floatingTextarea2">코멘트</label>
</div>
<div class="mybtns">
<button onclick="posting()" type="button" class="btn btn-dark">기록하기</button>
<button onclick="close_box()" type="button" class="btn btn-outline-dark">닫기</button>
</div>
</div>
<div class="mycards">
<div class="row row-cols-1 row-cols-md-4 g-4" id="cards-box">
<div class="col">
<div class="card h-100">
<img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
class="card-img-top">
<div class="card-body">
<h5 class="card-title">영화 제목이 들어갑니다</h5>
<p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
<p>⭐⭐⭐</p>
<p class="mycomment">나의 한줄 평을 씁니다</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
class="card-img-top">
<div class="card-body">
<h5 class="card-title">영화 제목이 들어갑니다</h5>
<p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
<p>⭐⭐⭐</p>
<p class="mycomment">나의 한줄 평을 씁니다</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
class="card-img-top">
<div class="card-body">
<h5 class="card-title">영화 제목이 들어갑니다</h5>
<p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
<p>⭐⭐⭐</p>
<p class="mycomment">나의 한줄 평을 씁니다</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
class="card-img-top">
<div class="card-body">
<h5 class="card-title">영화 제목이 들어갑니다</h5>
<p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
<p>⭐⭐⭐</p>
<p class="mycomment">나의 한줄 평을 씁니다</p>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
그리고 mongoDB 창을 띄워둔다.
[스파르타피디아] - POST 연습(포스팅하기)
API 만들고 사용하기 - 포스팅 API(Create→POST)
요청 정보 : URL= /movie, 요청 방식 = POST
클라이언트(fetch) → 서버(flask) : url, comment
서버(flask) → 클라이언트(fetch) : 메시지를 보냄 (포스팅 완료!)
클라이언트와 서버의 연결을 확인해 준다.
@app.route("/movie", methods=["POST"])
def movie_post():
sample_receive = request.form['sample_give']
print(sample_receive)
return jsonify({'msg':'POST 연결 완료!'})
서버
function posting() {
fetch('/movie', {method: "POST",body: formData,}).then((res) => res.json()).then((data) => {
console.log(data);
alert(data["msg"]);
});
}
<button onclick="posting()" type="button" class="btn btn-dark">기록하기</button>
클라이언트
alert로 'POST연결 완료!'가 뜨면 된다.
이제 서버를 만든다.
@app.route("/movie", methods=["POST"])
def movie_post():
url_receive = request.form['url_give']
comment_receive = request.form['comment_give']
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get(url_receive, headers=headers)
soup = BeautifulSoup(data.text, 'html.parser')
ogimage = soup.select_one('meta[property="og:image"]')['content']
ogtitle = soup.select_one('meta[property="og:title"]')['content']
ogdesc = soup.select_one('meta[property="og:description"]')['content']
doc = {
'image':ogimage,
'title':ogtitle,
'desc':ogdesc,
'comment':comment_receive
}
db.movies.insert_one(doc)
return jsonify({'msg':'포스팅 완료!'})
만들어둔 조각 기능을 구현했던 파일을 불러오고,
url, comment정보를 받아서, 저장한다.
클라이언트
function posting() {
let url = $('#url').val()
let comment = $('#comment').val()
let formData = new FormData()
formData.append('url_give', url)
formData.append('comment_give', comment)
fetch('/movie', {method: "POST",body: formData}).then(res => res.json()).then(data => {
alert(data['msg'])
window.location.reload()
})
}
url, comment정보를 formData에 데이터를 넣고 보내주고,
요청을 보내고 포스팅 완료!라고 받은 메시지를 alert()로 띄워준다.
그다음은 자연스럽게 보이기 위해 window.location.reload() 새로고침을 해준다.
[스파르타피디아] - GET 연습(보여주기)
API 만들고 사용하기 - 포스트 보여주기 API(Read→GET)
요청 정보 : URL= /movie, 요청 방식 = GET
클라이언트(fetch) → 서버(flask) : 없음
서버(flask) → 클라이언트(fetch) : 전체 주문을 보내주기
@app.route("/movie", methods=["GET"])
def movie_get():
return jsonify({'msg':'GET 연결 완료!'})
$(document).ready(function(){
listing();
});
function listing() {
fetch('/movie').then((res) => res.json()).then((data) => {
console.log(data)
alert(data['msg'])
})
}
서버와 클라이언트 연결을 확인해 주고,
@app.route("/movie", methods=["GET"])
def movie_get():
all_movies = list(db.movies.find({},{'_id':False}))
return jsonify({'result':all_movies})
서버를 만든다.
function listing() {
$('#cards-box').empty()
fetch('/movie').then(res => res.json()).then(data => {
let rows = data['result']
rows.forEach((a) => {
let title = a['title']
let image = a['image']
let desc = a['desc']
let comment = a['comment']
let temp_html = `<div class="col">
<div class="card h-100">
<img src="${image}"
class="card-img-top">
<div class="card-body">
<h5 class="card-title">${title}</h5>
<p class="card-text">${desc}</p>
<p class="mycomment">${comment}</p>
</div>
</div>
</div>`
$('#cards-box').append(temp_html)
})
})
}
그리고 클라이언트도 만든다.
forEach문으로 반복하면서 데이터를 뽑아낸다.
뽑아낸 데이터는 temp_html에 담아주고,
담아준 temp_html을 넣을 자리를 찾아 제이쿼리로 append를 쓴다.
완성코드
app.py(서버)
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)
from pymongo import MongoClient
client = MongoClient('내 mongoDB URL')
db = client.dbsparta
import requests
from bs4 import BeautifulSoup
@app.route('/')
def home():
return render_template('index.html')
@app.route("/movie", methods=["POST"])
def movie_post():
url_receive = request.form['url_give']
comment_receive = request.form['comment_give']
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get(url_receive, headers=headers)
soup = BeautifulSoup(data.text, 'html.parser')
ogimage = soup.select_one('meta[property="og:image"]')['content']
ogtitle = soup.select_one('meta[property="og:title"]')['content']
ogdesc = soup.select_one('meta[property="og:description"]')['content']
doc = {
'image':ogimage,
'title':ogtitle,
'desc':ogdesc,
'comment':comment_receive
}
db.movies.insert_one(doc)
return jsonify({'msg':'저장완료!'})
@app.route("/movie", methods=["GET"])
def movie_get():
all_movies = list(db.movies.find({},{'_id':False}))
return jsonify({'result':all_movies})
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 name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<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=Gowun+Dodum&display=swap" rel="stylesheet">
<style>
* {
font-family: 'Gowun Dodum', sans-serif;
}
.mytitle {
width: 100%;
height: 250px;
background-image: linear-gradient(0deg, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('https://movie-phinf.pstatic.net/20210715_95/1626338192428gTnJl_JPEG/movie_image.jpg');
background-position: center;
background-size: cover;
color: white;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.mytitle>button {
width: 200px;
height: 50px;
background-color: transparent;
color: white;
border-radius: 50px;
border: 1px solid white;
margin-top: 10px;
}
.mytitle>button:hover {
border: 2px solid white;
}
.mycomment {
color: gray;
}
.mycards {
margin: 20px auto 0px auto;
width: 95%;
max-width: 1200px;
}
.mypost {
width: 95%;
max-width: 500px;
margin: 20px auto 0px auto;
padding: 20px;
box-shadow: 0px 0px 3px 0px gray;
display: none;
}
.mybtns {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-top: 20px;
}
.mybtns>button {
margin-right: 10px;
}
</style>
<script>
$(document).ready(function () {
listing();
});
function listing() {
fetch('/movie').then((res) => res.json()).then((data) => {
let rows = data['result']
$('#cards-box').empty()
rows.forEach((a)=>{
let comment = a['comment']
let title = a['title']
let desc = a['desc']
let image = a['image']
let temp_html = `<div class="col">
<div class="card h-100">
<img src="${image}"
class="card-img-top">
<div class="card-body">
<h5 class="card-title">${title}</h5>
<p class="card-text">${desc}</p>
<p>⭐⭐⭐</p>
<p class="mycomment">${comment}</p>
</div>
</div>
</div>`
$('#cards-box').append(temp_html)
})
})
}
function posting() {
let url = $('#url').val()
let comment = $('#comment').val()
let formData = new FormData();
formData.append("url_give", url);
formData.append("comment_give", comment);
fetch('/movie', { method: "POST", body: formData }).then((res) => res.json()).then((data) => {
alert(data['msg'])
window.location.reload()
})
}
function open_box() {
$('#post-box').show()
}
function close_box() {
$('#post-box').hide()
}
</script>
</head>
<body>
<div class="mytitle">
<h1>내 생애 최고의 영화들</h1>
<button onclick="open_box()">영화 기록하기</button>
</div>
<div class="mypost" id="post-box">
<div class="form-floating mb-3">
<input id="url" type="email" class="form-control" placeholder="name@example.com">
<label>영화URL</label>
</div>
<div class="form-floating">
<textarea id="comment" class="form-control" placeholder="Leave a comment here"></textarea>
<label for="floatingTextarea2">코멘트</label>
</div>
<div class="mybtns">
<button onclick="posting()" type="button" class="btn btn-dark">기록하기</button>
<button onclick="close_box()" type="button" class="btn btn-outline-dark">닫기</button>
</div>
</div>
<div class="mycards">
<div class="row row-cols-1 row-cols-md-4 g-4" id="cards-box">
<div class="col">
<div class="card h-100">
<img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
class="card-img-top">
<div class="card-body">
<h5 class="card-title">영화 제목이 들어갑니다</h5>
<p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
<p>⭐⭐⭐</p>
<p class="mycomment">나의 한줄 평을 씁니다</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
class="card-img-top">
<div class="card-body">
<h5 class="card-title">영화 제목이 들어갑니다</h5>
<p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
<p>⭐⭐⭐</p>
<p class="mycomment">나의 한줄 평을 씁니다</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
class="card-img-top">
<div class="card-body">
<h5 class="card-title">영화 제목이 들어갑니다</h5>
<p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
<p>⭐⭐⭐</p>
<p class="mycomment">나의 한줄 평을 씁니다</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
class="card-img-top">
<div class="card-body">
<h5 class="card-title">영화 제목이 들어갑니다</h5>
<p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
<p>⭐⭐⭐</p>
<p class="mycomment">나의 한줄 평을 씁니다</p>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
드디어 감기가 거의 괜찮아졌다.
주말에 감기 때문에너무 아파서 강의 듣는 것도 밀렸다.
원래 주말에 4, 5주 차 끝내려 했는데, 못 끝내서 월요일에 4주 차, 화요일에 5주 차를 들으려 했다.
근데, 어제 노트북 뭐 살지 고르다가 시간을 다 보내버려서 못 들었다..ㅋㅋㅋ (아직 못 정했어요ㅜ 누가 좀 추천해 주세요/)
오늘 4주 차 마저 들으면서 멘탈이 반쯤 갈렸다.
그래도 여러 번 되감으면서 봐서 어느 정도 이해하긴 했다. 하지만 덕분에 새벽시간까지 못 자고 있다.
이따가 일 다녀와서 5주 차 빨리 들어야겠다.