* 하나의 컴퓨터 안에서 서버도 만들고 접속도 해보는 것 = 로컬 개발환경
서버 테스트 하자고 컴퓨터를 한 대 더 살 수는 없으니까..
=> 앞으로의 프로젝트들 저장할 폴더도 만들어 둠
☞ bucket : 버킷리스트 (5주차)
☞ homework : 팬명록 (4주차 숙제)
☞ mars : 화성땅공동구매 (4주차)
☞ movie : 스파르타피디아 (4주차)
☞ prac : flask 연습 (4주차)
1. Flask 시작하기 - 서버 만들기, HTML 파일 주기, API 만들기
우선, prac 폴더에 새 프로젝트 만들고 (venv는 전에도 말했지만 안 보이는 것처럼 생각하기! 건들지 X) flask 패키지 설치
* flask 패키지란?
서버를 구동시켜주는 편한 코드 모음.
개발자들이 서버를 직접 다 만드는 게 아니라 이러한 프레임워크를 가져다가 만드는 것!
요리를 쉽게 할 수 있도록 해주는 3분 요리/소스 세트 같은 존재라고 생각하면 됨
(1-1) 서버 만들기
prac 폴더 안에 app.py라는 이름으로 python 파일 만들고 (통상적으로 flask 서버를 돌리는 파일은 app.py라고 이름 짓는다고 한다) 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)
크롬 주소창에 localhost:5000 입력하면 로컬 개발 환경에서 서버가 만들어진 것을 볼 수 있다.
@app.route('/mypage')
def mypage():
return 'This is Mypage!!!'
위의 코드에서 이렇게 살짝만 바꾸면 localhost:5000/mypage 주소로 또다른 서버도 만들 수 있다.
(1-2) HTML 파일 주기
templates 폴더에 index.html 파일을 만들어서 필요한 뼈대들을 작성해준다.
작성한 html 파일을 서버에서 읽어와서 보여주려면 flask 시작 코드 작성했던 것에서 아래처럼 바꿔줘야 한다.
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def home():
return render_template('index.html')
* 차이점 알고 가기!! 둘 다 페이지를 여는 거긴 하지만,
- localhost:5000 해서 연 페이지는 서버에서 작동하는 페이지
- index.html에서 크롬으로 연결해서 여는 것은 그냥 내 컴퓨터에 있는 파일을 연 것
(1-3) API 만들기
💡간단 복습💡 - 클라이언트의 요청 방식 2가지 (즉, 프론트엔드(html 쪽)와 백엔드(flask 서버 쪽)가 정보를 주고받는 방식)
1. GET : 조회(Read) / url 뒤에 물음표 붙여 key = value로 전달
2. POST : 생성(Create), 변경(Update), 삭제(Delete) / 바로 보이지 않는 HTML body에 key : value 형태로 전달
ajax로 요청하기 위해 html 파일에 jQuery를 임포트 해준다.
그러고 나서 버튼을 누르면 ajax가 호출될 수 있도록 해준다.
우선은 GET 방식부터 해보겠다.
index.html 파일
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--ajax 요청하기 위해 jQuery 임포트 해오기-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script> <!--ajax 쓰는 곳-->
function hey() {
// GET 방식
$.ajax({
type: "GET",
url: "/test?title_give=봄날은간다", // '/test' 라는 창구에서 'title_give'라는 이름으로 '봄날은 간다'라는 데이터를 가지고 간다!
data: {},
success: function (response) {
console.log(response)
}
})
}
</script>
</head>
<body>
<h1>나의 첫 웹페이지!</h1>
<button onclick="hey()">버튼을 만들자</button>
</body>
</html>
이렇게 했으면 '/test'라는 창구를 만들어줘야 하는데 이것은 app.py 파일에서 해준다.
app.py 파일
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)
@app.route('/')
def home():
return render_template('index.html')
# GET 창구
@app.route('/test', methods=['GET']) # '/test'라는 창구에서 받고 있음
def test_get():
title_receive = request.args.get('title_give') # 'title_give'라는 이름으로 어떤 데이터를 받아옴.
print(title_receive)
return jsonify({'result':'success', 'msg': '이 요청은 GET!'})
if __name__ == '__main__':
app.run('0.0.0.0',port=5000,debug=True)
이 때, import 항목에 request, jsonify 추가해 줘야 한다.
이렇게 index.html 파일과 app.py 파일을 구성해주게 되면 /test 창구에서, GET 방식으로, title_give라는 이름으로 '봄날은 간다'라는 데이터를 전달할 수 있게 되는 것이다.
결과 창은 다음과 같다. '버튼을 만들자' 버튼을 클릭하면 print 해준 '봄날은 간다'는 파이참의 결과창에 뜨고, 콘솔창에는 msg와 result 값이 뜬다.
다음으로 POST 방식도 해보자.
index.html 파일
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--ajax 요청하기 위해 jQuery 임포트 해오기-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script> <!--ajax 쓰는 곳-->
function hey() {
// POST 방식
$.ajax({
type: "POST",
url: "/test",
data: {title_give: '봄날은간다'},
success: function (response) {
console.log(response)
}
})
}
</script>
</head>
<body>
<h1>나의 첫 웹페이지!</h1>
<button onclick="hey()">버튼을 만들자</button>
</body>
</html>
GET 방식과는 데이터 전달 방식이 다른 것을 알 수 있다.
GET 방식은 url의 ? 뒤쪽에 key = value (e.g. title_give=봄날은 간다) 형태로 있었지만
POST 방식은 url에 있지 않으며, key : value (e.g. title_give : '봄날은 간다') 형태로 데이터를 전달하고 있다.
POST 방식도 마찬가지로 /test 라는 이름의 창구를 만들어 줘야 하기 때문에 app.py 파일로 이동해 보겠다.
app.py 파일
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)
@app.route('/')
def home():
return render_template('index.html')
# GET 창구
@app.route('/test', methods=['GET']) # '/test'라는 창구에서 받고 있음
def test_get():
title_receive = request.args.get('title_give') # 'title_give'라는 이름으로 어떤 데이터를 받아옴.
print(title_receive)
return jsonify({'result':'success', 'msg': '이 요청은 GET!'})
# POST 창구
@app.route('/test', methods=['POST'])
def test_post():
title_receive = request.form['title_give'] # 'title_give'라는 이름으로 어떤 데이터를 받아옴.
print(title_receive)
return jsonify({'result':'success', 'msg': '요청을 잘 받았어요'})
if __name__ == '__main__':
app.run('0.0.0.0',port=5000,debug=True)
똑같이 /test 라는 이름의 창구가 2개 있지만 GET 방식인지 POST 방식인지에 따라 창구가 달라진다.
즉, 정리해보면
버튼을 누리면 hey() 함수가 호출되면서 /test 창구에 POST 방식으로 ajax를 요청한다. (index.html)
그러면 title_give에 있는 '봄날은 간다' 라는 데이터를 title_receive 변수에 저장해서 print하고 (app.py)
'result' : 'success', 'msg' : '요청을 잘 받았어요' 라는 결과를 돌려준다. (app.py)
success 했기 때문에 response에서 이 값을 받아서 콘솔 창에 띄우게 된다. (index.html)
기본적인 이론은 이렇다. 수업을 듣고 다시 정리해 보면서 조금 더 이해가 되긴 했지만 아직 어렵긴 하다.
앞으로 4개의 프로젝트들을 하면서 서버를 만들고 html을 연결하고 api로 데이터를 주고받는 과정들에 대해 반복 연습을 해 보겠다!
2. 화성땅 공동구매
앞에서부터 계속 했던 기본 내용들이긴 하지만, 프로젝트 생성하고 기본 세팅 하는 것부터 차근차근 정리해보자.
✅ 프로젝트 생성
프로젝트 생성할 때 확인 할 것 두가지!!
1) 원하는 폴더 선택 후 폴더명 뒤에 venv 잘 들어갔는지 확인
2) Python38로 잘 들어가 있는지 확인
✅ 기본 폴더구조 세팅
- static 폴더 - 이미지, css 파일
- templates 폴더 - html 파일
- app.py 파일
✅ 패키지 설치 (필요한 것만 추가 설치)
- flask : 서버 만들거니까!
- pymongo : 데이터베이스 접근할거니까!
- dnspython : ? (어쨌든 flask와 DB 연결에 필요)
(2-1) 뼈대 준비하기
화성땅 공동구매 - 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 web_mars_post():
sample_receive = request.form['sample_give']
print(sample_receive)
return jsonify({'msg': 'POST 연결 완료!'})
@app.route("/mars", methods=["GET"])
def web_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() {
$.ajax({
type: 'GET',
url: '/mars',
data: {},
success: function (response) {
alert(response['msg'])
}
});
}
function save_order() {
$.ajax({
type: 'POST',
url: '/mars',
data: { sample_give:'데이터전송' },
success: function (response) {
alert(response['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>
<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>
강의의 코드스니펫으로 주어진 기본 뼈대들이다. 이것들만 제대로 복붙해서 넣고 실행하면 아래 화면이 나온다. 기본 뼈대는 갖추어 졌으니 이제 본격 코딩 들어가보자!
(2-2) POST 연습 (주문 저장하기)
주문하기 버튼을 누르면 이름, 주소, 평수에 해당하는 정보들이 서버에 저장되어야 한다.
좀 더 자세하게 흐름을 보자면 아래와 같다.
1. 요청 정보 : URL= /mars, 요청 방식 = POST
2. 클라(ajax) → 서버(flask) : name, address, size
3. 서버(flask) → 클라(ajax) : 메시지를 보냄 (주문 완료!)
[STEP1] 클라이언트와 서버 연결 확인하기
화성땅 공동구매 - index.html 中
function save_order() {
$.ajax({
type: 'POST',
url: '/mars',
data: { sample_give:'데이터전송' },
success: function (response) {
alert(response['msg'])
}
});
}
화성땅 공동구매 - app.py 中
@app.route("/mars", methods=["POST"])
def web_mars_post():
sample_receive = request.form['sample_give']
print(sample_receive)
return jsonify({'msg': 'POST 연결 완료!'})
준비해 놓은 뼈대 코드에서 다음의 두 파트를 보면서 연결이 잘 되어있는지 확인해 보자.
(index.html) 버튼을 누르면 save_order() 함수가 호출되면서 /mars 창구에 POST 방식으로 ajax를 요청한다.
(app.py) 그러면 sample_give에 있는 '데이터전송' 이라는 데이터를 sample_receive 변수에 저장해서 print하고
(app.py) 'msg' : 'POST 연결 완료!' 라는 결과를 돌려준다.
(index.html) success 했기 때문에 response에서 이 값을 받아서 msg에 해당하는 'POST 연결 완료!'를 alert로 띄우게 된다.
그 결과 alert 창도 잘 뜨고 '데이터전송'도 잘 print 되고 있는 것을 확인할 수 있다. 클라이언트와 서버가 잘 연결되어 있는 것이다.
지금은 다 만들어져 있는 코드스니펫을 복붙해 왔기 때문에 당연히 잘 작동하겠지만 뼈대부터 내가 만들게 되면 이 연결도 유의해서 확인해야 할 것이다.
[STEP2] 서버부터 만들기
서버는 name, address, size를 받아서 저장하면 된다.
저장을 위해서는 DB와의 연결이 필요하기 때문에 제일 위에 필요한 임포트 부분들을 기존의 daprac 파일에서 복사해와서 붙여줬다.
그리고 POST 방식으로 받아 온 데이터들을 DB에 저장하기 위해 아래와 같이 코드 수정을 했다.
화성땅 공동구매 - app.py 中
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)
from pymongo import MongoClient
import certifi # 사용하고 있는 인터넷 환경에 따라 보안 관련 추가 설정이 필요한 경우가 있음
ca = certifi.where();
client = MongoClient('mongodb+srv://test:sparta@cluster0.oi21xcu.mongodb.net/?retryWrites=true&w=majority', tlsCAFile=ca)
db = client.dbsparta
.
.
.
@app.route("/mars", methods=["POST"])
def web_mars_post():
name_receive = request.form['name_give']
address_receive = request.form['address_give']
size_receive = request.form['size_give']
# DB에 데이터 저장하기
doc = {
'name' : name_receive,
'address' : address_receive,
'size' : size_receive
}
db.nars.insert_one(doc)
return jsonify({'msg': '주문 완료!'})
[STEP3] 클라이언트 만들기
이제 프론트엔드 부분도 수정해 주어야 한다.
버튼을 누르면 save_order가 실행되고,
사용자에게서 입력 받은 데이터를 id를 활용하여 각각의 변수에 저장하고,
그 데이터를 백엔드로 넘겨 주기 위해 OOO_give 라는 형태의 미리 약속된 key 값에 실어서 보낸다.
그러면 STEP2에서 만든 서버에서 그것을 받아서 DB에 저장하고 '주문완료!'라는 메세지를 다시 보내준다.
그러면 다시 프론트엔드에서 그것을 alert 창으로 띄우고 새로고침(reload)도 해주는 것이다.
밑줄 친 내용들을 반영하여 완성한 코드는 아래와 같다.
화성땅 공동구매 - index.html 中
function save_order() {
let name = $('#name').val()
let address = $('#address').val()
let size = $('#size').val()
$.ajax({
type: 'POST',
url: '/mars',
data: { name_give : name, address_give : address, size_give : size },
success: function (response) {
alert(response['msg'])
window.location.reload() // 다 되고 나면 새로고침 해주기
}
});
}
[STEP4] 완성 확인하기
왼쪽 사진과 같이 입력하고 '주문하기' 버튼을 누르면 오른쪽처럼 DB에 해당 데이터가 잘 저장된 것을 확인할 수 있다.
(2-3) GET 연습 (주문 보여주기)
GET 방식은 조회(read)할 때 사용된다고 했었다. GET 방식으로 연습할 내용의 구체적 흐름은 다음과 같다.
1. 요청 정보 : URL = /mars, 요청 방식 = GET
2. 클라(ajax) → 서버(flask) : (없음)
3. 서버(flask) → 클라(ajax) : 전체 주문을 보내주기
[STEP1] 클라이언트와 서버 연결 확인하기
화성땅 공동구매 - index.html 中
$(document).ready(function () {
show_order();
});
function show_order() {
$.ajax({
type: 'GET',
url: '/mars',
data: {},
success: function (response) {
alert(response['msg'])
}
});
}
화성땅 공동구매 - app.py 中
@app.route("/mars", methods=["GET"])
def web_mars_get():
return jsonify({'msg': 'GET 연결 완료!'})
POST와 마찬가지로 뼈대 코드의 두 부분을 보면서 연결이 잘 되어 있는지 확인해보자.
(index.html) 로딩이 되면 show_order() 함수가 호출되면서 /mars 창구에 GET 방식으로 ajax를 요청한다.
(app.py) 'msg' : 'GET 연결 완료!' 라는 결과를 돌려준다.
(index.html) success 했기 때문에 response에서 이 값을 받아서 msg에 해당하는 'GET 연결 완료!'를 alert로 띄우게 된다.
당연히 잘 작동하는 것을 확인할 수 있다.
[STEP2] 서버부터 만들기
DB에서 데이터들, 즉 모든 주문 내역들을 가져와야 한다.
기존의 daprac 파일에서
# 여러개 찾기 - 예시 ( _id 값은 제외하고 출력)
all_users = list(db.users.find({},{'_id':False}))
이렇게 메모해뒀던 부분을 찾아서 복붙해오고 일부만 지금의 프로젝트에 맞게 수정해 준다.
(※ 앞에서 DB에 저장할 때 '화성' 프로젝트라 컬렉션 이름을 mars로 했어야 했는데 nars라고 오타가 났었다. 여기는 다시 mars로 수정해서 하는 중!)
화성땅 공동구매 - app.py 中
@app.route("/mars", methods=["GET"])
def web_mars_get():
order_list = list(db.mars.find({}, {'_id': False}))
return jsonify({'orders': order_list})
[STEP3] 클라이언트 만들기
서버에서 orders라는 키 값으로 넘겨준 데이터, 주문 내역들을 rows에 우선 저장하고 name, address, size로 나누어서 각각의 변수에 저장한다.
받아온 주문 내역들을 보여주려면 밑에 붙여 넣어야 하므로 temp_html을 사용해 줄 때다.
붙여 넣어야 할 곳에 "order-box"라고 id를 부여한 후에 원하는 형식으로 붙여 넣어준다.
화성땅 공동구매 - index.html 中
$(document).ready(function () {
show_order();
});
function show_order() {
$.ajax({
type: 'GET',
url: '/mars',
data: {},
success: function (response) {
let rows = response['orders']
for (let i=0 ; i<rows.length ; i++){
let name = rows[i]['name']
let address = rows[i]['address']
let size = rows[i]['size']
let temp_html = `<tr>
<td>${name}</td>
<td>${address}</td>
<td>${size}</td>
</tr>`
$('#order-box').append(temp_html)
}
}
});
}
.
.
.
<tbody id = "order-box">
</tbody>
[STEP4] 완성 확인하기
새로고침하면 그동안의 주문 내역들이 밑에 잘 뜬다!
3. 스파르타피디아
2주차까지 열심히 만들어 뒀던 스파르타피디아가 있다. 그동안 만들었던 스파르타피디아 뼈대에 이제 드디어 직접 기록하고 또 보여주는 기능을 구현해 볼 것이다.
POST(기록하기) ☞ url, 별점, 코멘트를 입력 받아서 기록하기. (이 때 사용자가 입력하지 않은 영화의 이미지, 제목, 디스크립션도 함께 가져와서 기록해야 함. 즉, url을 이용해서 크롤링 해줘야 함)
GET(보여주기) ☞ 기록한 영화들 카드로 만들어서 밑에 붙여주기
우선 프로젝트 생성과 세팅, 패키지 설치 등을 앞에서 정리한 것처럼 실행해준다.
(★ 크롤링을 위한 패키지 requests, bs4도 설치해줘야 함!)
(2-1) 조각 기능 구현하기
API에서 수행해야하는 작업 중 익숙하지 않은 것들, 프로젝트에 필요한 기술들은 따로 python 파일을 만들어 실행해보고, 잘 되면 코드를 붙여넣는 방식으로 하는 게 편하다. 이런 것을 조각 기능이라고 하는데 이 프로젝트에서는 우선 크롤링 기능을 먼저 구현해 보고자 한다.
💡 meta 태그란?
메타 태그는, <head></head> 부분에 들어가는, 눈으로 보이는 것(body) 외에 사이트의 속성을 설명해주는 태그들입니다.
(e.g. 구글 검색 시 표시 될 설명문, 사이트 제목, 카톡 공유 시 표시 될 이미지 등)
...라고 한다. 카톡으로 url 보낼 때 오른쪽처럼 사진이 뜨는 것도 meta 태그를 심어놨기 때문!
메타 태그를 이용하여 크롤링을 해보자.
meta_prac.py
import requests
from bs4 import BeautifulSoup
url = 'https://movie.naver.com/movie/bi/mi/basic.naver?code=191597'
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')
title = soup.select_one('meta[property="og:title"]')['content']
image = soup.select_one('meta[property="og:image"]')['content']
desc = soup.select_one('meta[property="og:description"]')['content']
print(title, image, desc)
soup까지는 '크롤링 기본 코드' 코드스니펫에 있던 코드다.
https://movie.naver.com/movie/bi/mi/basic.naver?code=191597 url을 들어가보면 네이버 영화의 '보스 베이비 2' 페이지이다.
해당 페이지의 html을 살펴보면 왼쪽 사진처럼 <head></head> 영역에 meta 태그들이 달려 있는 것을 볼 수 있다.
이 중 내가 원하는 정보인 title, image, description에 대한 정보만 가져다 쓰면 된다.
print를 통해 원하는 정보들을 잘 가져왔음, 즉 크롤링이 잘 되었음을 확인했기 때문에 이제 이 프로젝트에서는 크롤링이 필요할 때 meta_prac에 있는 코드를 가져다 쓰면 된다!!
(2-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("/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)
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() {
$.ajax({
type: 'GET',
url: '/movie',
data: {},
success: function (response) {
alert(response['msg'])
}
})
}
function posting() {
$.ajax({
type: 'POST',
url: '/movie',
data: {sample_give: '데이터전송'},
success: function (response) {
alert(response['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="input-group mb-3">
<label class="input-group-text" for="inputGroupSelect01">별점</label>
<select class="form-select" id="star">
<option selected>-- 선택하기 --</option>
<option value="1">⭐</option>
<option value="2">⭐⭐</option>
<option value="3">⭐⭐⭐</option>
<option value="4">⭐⭐⭐⭐</option>
<option value="5">⭐⭐⭐⭐⭐</option>
</select>
</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>
(2-2) POST 연습 (포스팅하기)
1. 요청 정보 : URL= /movie, 요청 방식 = POST
2. 클라(ajax) → 서버(flask) : url, star, comment + image, title, description도 크롤링 해서 같이 저장!!
3. 서버(flask) → 클라(ajax) : 메시지를 보냄 (포스팅 완료!)
프론트엔드 쪽에서 입력받은 데이터 받아오고, url 이용해서 크롤링도 하고, DB에 저장한 후 완료 메세지 다시 프론트엔드 쪽으로 보내기. 앞에서 한 것들의 반복이다.
app.py 中
@app.route("/movie", methods=["POST"])
def movie_post():
url_receive = request.form['url_give']
star_receive = request.form['star_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')
title = soup.select_one('meta[property="og:title"]')['content']
image = soup.select_one('meta[property="og:image"]')['content']
desc = soup.select_one('meta[property="og:description"]')['content']
doc = {
'title': title,
'image': image,
'desc' : desc,
'star' : star_receive,
'comment' : comment_receive
}
db.movies.insert_one(doc)
return jsonify({'msg':'포스팅 완료!'})
여기도 반복! '기록하기' 버튼을 누르면 posting 함수가 소환되고 ajax를 호출하게 된다. 필요한 데이터를 서버 쪽으로 넘겨줄 수 있게 코드를 수정해 주면 된다.
index.html 中
function posting() {
let url = $('#url').val()
let star = $('#star').val()
let comment = $('#comment').val()
$.ajax({
type: 'POST',
url: '/movie',
data: {url_give: url, star_give : star, comment_give : comment},
success: function (response) {
alert(response['msg'])
window.location.reload()
}
});
}
실행해 보면 왼쪽과 같이 입력한 데이터들이 오른쪽 DB에 잘 저장되는 것을 확인할 수 있다.
(2-2) GET 연습 (보여주기)
1. 요청 정보 : URL = /movie, 요청 방식 = GET
2. 클라(ajax) → 서버(flask) : (없음)
3. 서버(flask) → 클라(ajax) : 전체 영화를 보내주기
서버 먼저~
app.py 中
@app.route("/movie", methods=["GET"])
def movie_get():
movie_list = list(db.movies.find({}, {'_id': False}))
return jsonify({'movies':movie_list})
이어서 클라이언트도 만들어 준다.
index.html 中
function listing() {
$.ajax({
type: 'GET',
url: '/movie',
data: {},
success: function (response) {
let rows = response['movies']
for(let i=0 ; i<rows.length ; i++){
let comment = rows[i]['comment']
let title = rows[i]['title']
let desc = rows[i]['desc']
let image = rows[i]['image']
let star = rows[i]['star']
let star_image = '⭐'.repeat(star)
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>${star_image}</p>
<p class="mycomment">${comment}</p>
</div>
</div>
</div>`
$('#cards-box').append(temp_html)
}
}
})
}
결과! 내가 기록한 내용들이 카드로 만들어져서 잘 들어가는 것을 확인할 수 있다.
4. 숙제 - 팬명록
닉네임과 응원댓글 받아서 저장하기 (POST) & 로딩 완료되면 댓글들 자동으로 붙여주기 (GET)
2가지 기능을 구현해 볼 것이다.
(4-1) 닉네임과 응원댓글 받아서 저장하기 (POST)
[STEP1] 연결 확인하기
강의에서 준 뼈대 코드로 시작했으니 당연히 연결 잘 됨. 생략..
[STEP2] 서버부터 만들기
app.py 中
@app.route("/homework", methods=["POST"])
def homework_post():
name_receive = request.form['name_give']
comment_receive = request.form['comment_give']
doc = {
'name' : name_receive,
'comment' : comment_receive
}
db.fans.insert_one(doc)
return jsonify({'msg':'포스팅 완료!'})
[STEP3] 클라이언트 만들기
index.html 中
function save_comment(){
let name = $('#name').val()
let comment = $('#comment').val()
$.ajax({
type: 'POST',
url: '/homework',
data: {name_give : name, comment_give : comment},
success: function (response) {
alert(response['msg'])
window.location.reload()
}
})
}
[STEP4] 완성 확인하기
fans 컬렉션 만들어서 닉네임, 코멘트 잘 저장된 것까지 확인!
(4-1) 로딩 완료되면 댓글들 자동으로 붙여주기 (GET)
[STEP1] 연결 확인하기
생략..
[STEP2] 서버부터 만들기
app.py 中
@app.route("/homework", methods=["GET"])
def homework_get():
comment_list = list(db.fans.find({},{'_id':False}))
return jsonify({'comments':comment_list})
[STEP3] 클라이언트 만들기
index.html 中
function show_comment(){
$.ajax({
type: "GET",
url: "/homework",
data: {},
success: function (response) {
let rows = response['comments']
for (let i=0 ; i<rows.length ; i++){
let name = rows[i]['name']
let comment = rows[i]['comment']
let temp_html = `<div class="card">
<div class="card-body">
<blockquote class="blockquote mb-0">
<p>${comment}</p>
<footer class="blockquote-footer">${name}</footer>
</blockquote>
</div>
</div>
`
$('#comment-list').append(temp_html)
}
}
});
}
[STEP4] 완성 확인하기
+ 출석 도장 꾸욱~~!!!

KPT
* Keep : 계속 기억해야 할 것, 좋은 습관 등
1) 한 번에 모든 것을 알아야겠다 X, 반복하면서 자연스럽게 터득하겠다 O
2) 일단 저지르고, 왜 됐는지 나중에 파악하기
3) 줄맞춤 잘 해줬음 (★ctrl+alt+L)
4) 강의 들으면서 블로그 바로바로 정리하자. 미루지 않고 쓸 수 있고 내용 정리도 더 잘 되는 듯!
* Problem : 발생했던 문제, 오류, 실수 등
1) index_html 파일을 templates 폴더 안에 두지 않아서 처음에 서버를 만들었을 때 html 파일을 찾을 수 없다고 뜸
* Try : 문제를 해결하기 위해 노력했던 부분, 아이디어(이런 식으로 해보면 어떨까) 관해서 알아낸 것 등
1) 기본적인 폴더 구조와 각 파일이 위치해야 할 곳 잘 지키기!