❌버킷리스트 취소 기능 추가하기
서버쪽부터 추가해보자! (app.js)
bucket_done과 반대로 작동한다고 생각하면 간단하다
- cancel 작업을 해줄 url을 추가한다 ( /bucket/cancel )
- 클라이언트 쪽의 num값을bucket_done 과 동일하게 받아온다
- db에 존재하는 num값을 가진 데이터의 done값을 0으로 바꿔주는 작업을 하면 된다.
# 완료
@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/cancel**", methods=["POST"])
def bucket_cancel():
num_receive = request.form['num_give']
db.bucket.update_one({'num': int(num_receive)}, {'$set': {**'done': 0**}})
return jsonify({'msg': '버킷 취소!'})
다음은 클라이언트 쪽을 추가해보자! (index.html)
done_bucket 함수가 num값을 서버로 보내는것과 같이 동일하게 작동한다.
앞서서 추가했던 cancel 작업을 해줄 서버의 url을 다르게 입력해주어야한다.
function done_bucket(num) {
$.ajax({
type: 'POST',
url: '/bucket/done',
data: { num_give: num },
success: function (response) {
alert(response['msg']);
window.location.reload();
},
});
}
function cancel_bucket(num) {
$.ajax({
type: 'POST',
url: '/bucket/cancel',
data: { num_give: num },
success: function (response) {
alert(response['msg']);
window.location.reload();
},
});
}
다음으로는 cancel_bucket을 이벤트 추가해주면 된다.
버킷리스트를 불러와서 보여주는 함수 show_bucket 으로 넘어가자
done상태가 1(true)인 부분을 수정해주면 된다
let temp_html = ``;
if (done) {
temp_html = `
<li>
<h2 class="done">✅ ${bucket}</h2>
<button
**onclick="cancel_bucket(${num})"**
type="button"
**class="btn btn-outline-danger"**
>
취소
</button>
</li>
`;
} else {
temp_html = `
<li>
<h2>✅ ${bucket}</h2>
<button
onclick="done_bucket(${num})"
type="button"
class="btn btn-outline-primary"
>
완료
</button>
</li>
`;
}
$('#bucket-list').append(temp_html);
앞에서 했던 것과 동일하게 완료버튼과 같은 구성으로 추가해주면 된다.
취소 버튼은 부트스트랩에서 빨간색 버튼으로 추가했다. (class = “btn btn-outline-danger”)
▶전체코드보기
<!-- 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+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 rows = response['buckets'];
for (let i = 0; i < rows.length; i++) {
const bucket = rows[i].bucket;
const num = rows[i].num;
const done = rows[i].done;
let temp_html = ``;
if (done) {
temp_html = `
<li>
<h2 class="done">✅ ${bucket}</h2>
<button
onclick="cancel_bucket(${num})"
type="button"
class="btn btn-outline-danger"
>
취소
</button>
</li>
`;
} else {
temp_html = `
<li>
<h2>✅ ${bucket}</h2>
<button
onclick="done_bucket(${num})"
type="button"
class="btn btn-outline-primary"
>
완료!
</button>
</li>
`;
}
$('#bucket-list').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();
},
});
}
function cancel_bucket(num) {
$.ajax({
type: 'POST',
url: '/bucket/cancel',
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
id="bucket"
class="form-control"
type="text"
placeholder="이루고 싶은 것을 입력하세요"
/>
<button
onclick="save_bucket()"
type="button"
class="btn btn-outline-primary"
>
기록하기
</button>
</div>
</div>
<div class="mybox" id="bucket-list"></div>
</body>
</html>
# app.js
from flask import Flask, render_template, request, jsonify
from pymongo import MongoClient
app = Flask(__name__)
client = MongoClient(
'mongodb+srv://test:sparta@cluster0.wgvgsfc.mongodb.net/?retryWrites=true&w=majority')
db = client.dbsparta
@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}))
count = len(bucket_list) + 1
doc = {
'num': count,
'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/cancel", methods=["POST"])
def bucket_cancel():
num_receive = request.form['num_give']
db.bucket.update_one({'num': int(num_receive)}, {'$set': {'done': 0}})
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)
🗑버킷리스트 삭제 기능 추가하기
🙋 생각해보기
버킷리스트 1, 2, 3이 있는데 2를 삭제하고 새로운 버킷리스트를 추가하면 num값은 각각 어떻게 될까?
⇒ 정답은 1, 3, 3이 된다.
이렇게 되면 num값이 동일한 값이 생기게 된다. num이 같아지면 동시에 상태가 변한다.
그렇기 때문에 num값으로 값을 식별하는 방법을 사용하면 안된다.
고유하게 식별가능한 값을 생성해 사용해도 되지만 mongodb의 _id값을 이용해보자
id값 받아오기
# app.js
def bucket_get():
bucket_list = list(db.bucket.find())
return jsonify({'buckets': bucket_list})
id값을 읽어오지 않았던 부분을 삭제해주고, 조건을 포함하지 않는 {} 도 삭제해준다.
db에 존재하는 모든 데이터를 읽어오기 때문에 id값도 포함되어 있는 것을 확인할 수 있다.
🚫 여기서 오류가 발생한다. 그 이유는 ObjectId() 데이터를 json으로 변환하려고 했기 때문이다. 이 문제를 해결하기 위해서 ObjectId()를 문자열로 변환하는 작업이 필요하다.
# app.js
@app.route("/bucket", methods=["GET"])
def bucket_get():
bucket_list = list(db.bucket.find())
# IdObject() to string
**for i in range(len(bucket_list)):
bucket_list[i]['_id'] = str(bucket_list[i]['_id'])**
return jsonify({'buckets': bucket_list})
_id 부분을 str() 함수를 사용해서 문자열로 변환한 뒤 jsonify() 로 집어넣어준다.
받아온 id값 클라이언트에 추가하기
이전에 버튼 onclick에 있는 함수에 인자를 직접 지정해주는 방식이었는데 처리 방식을 조금 더 바꿔서 버튼의 속성에 id 값을 추가하고 id값을 읽어오는 event를 처리하는 방식으로 처리했다.
// index.html
<button
**onclick="cancel_bucket(${num})"**
type="button"
class="btn btn-outline-danger"
>num을 id값으로 변경하는 경우에는 오류가 발생한다.
**+`''` 추가하면 해결됨**
버튼 속성에 id 값 추가하기
// done
<button **id=${id} onclick="done_bucket(event)"** type="button"></button>
// cancel
<button **id=${id} onclick="cancel_bucket(event)"** type="button"></button>
temp_html 을 상태에 따라 선언해주는 부분 (done = 1, done = 0) 각각 추가해주면 된다.
다음은 버튼 event를 처리해주러가보자.
버튼 event 처리하기
function done_bucket(event) {
const id = event.target.getAttribute('id');
$.ajax({
type: 'POST',
url: '/bucket/done',
**data: { id_give: id },**
success: function (response) {
alert(response['msg']);
window.location.reload();
},
});
}
function cancel_bucket(event) {
const id = event.target.getAttribute('id');
$.ajax({
type: 'POST',
url: '/bucket/cancel',
data: { id_give: id },
success: function (response) {
alert(response['msg']);
window.location.reload();
},
});
}
event.target으로 event가 발생하는 HTML 요소의 정보를 읽어올 수 있다.
우리는 id 속성의 값이 필요하기 때문에 getAttribute('id')
를 이용해주면 된다.
읽어온 id 값을 ajax의 data부분에 넣어서 보내면 된다.
function delete_bucket(event) {
const id = event.target.getAttribute('id');
$.ajax({
type: 'POST',
url: '/bucket/delete',
data: { id_give: id },
success: function (response) {
alert(response['msg']);
window.location.reload();
},
});
}
똑같이 삭제 처리를 해줄 함수도 만들어주면 된다.
+🔮UI 변경하기
삭제 버튼을 추가해야하기 때문에 체크박스를 완료, 취소 기능으로 대체하고
원래 완료, 취소 버튼이 있던 부분에는 삭제 버튼을 추가해주었다.
.mybox > div.form-check {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-bottom: 10px;
min-height: 48px;
}
.mybox > div.form-check > h3 {
max-width: 75%;
font-size: 20px;
font-weight: 500;
margin-left: 10px;
margin-right: auto;
margin-bottom: 0px;
}
.mybox > div.form-check > h3.done {
text-decoration: line-through;
}
let temp_html = ``;
if (done) {
temp_html = `
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
checked
id="${id}"
onclick="cancel_bucket(event)"
/>
<h3 class="done">${bucket}</h3>
<button id=${id} onclick="delete_bucket(event)" type="button" class="btn-close" aria-label="Close"></button>
</div>
`;
} else {
temp_html = `
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="${id}"
onclick="done_bucket(event)"
/>
<h3>${bucket}</h3>
<button id=${id} onclick="delete_bucket(event)" type="button" class="btn-close" aria-label="Close"></button>
</div>
`;
}
$('#bucket-list').append(temp_html);
부트스트랩 체크 박스 : https://getbootstrap.kr/docs/5.2/forms/checks-radios/#content
부트스트랩 닫기 버튼 : https://getbootstrap.kr/docs/5.2/components/close-button/#content
클라이언트에서 보낸 id 처리하기
🚫 클라이언트에서 문자열 형태로 받아온 id값을 db로 보내면 오류가 일어나지는 않지만 아무 값도 변경되지 않는다. ObjectId 를 문자열로 바꿔준 것처럼 다시 문자열을 ObjectId로 변환해주어야 한다.
ObjectId로 변경하기
from bson.objectid import ObjectId
변환을 도와줄 라이브러리를 추가해준다.
이제 받아온 id값을 ObjectId() 함수를 이용하여 변환해주면된다.
done, cancel, delete 세가지 모두 추가해준다.
@app.route("/bucket/done", methods=["POST"])
def bucket_done():
id_receive = request.form['id_give']
db.bucket.update_one({'_id': ObjectId(id_receive)}, { '$set': {'done': 1}})
return jsonify({'msg': '버킷 완료!'})
@app.route("/bucket/cancel", methods=["POST"])
def bucket_cancel():
id_receive = request.form['id_give']
db.bucket.update_one({'_id': ObjectId(id_receive)}, {'$set': {'done': 0}})
return jsonify({'msg': '버킷 취소!'})
@app.route("/bucket/delete", methods=["POST"])
def bucket_delete():
id_receive = request.form['id_give']
db.bucket.delete_one({'_id': ObjectId(id_receive)})
return jsonify({'msg': '버킷 삭제!'})
delete 작업은 delete_one()
으로 해주면 된다.
▶전체코드
# app.js
from flask import Flask, render_template, request, jsonify
from pymongo import MongoClient
from bson.objectid import ObjectId
app = Flask(__name__)
client = MongoClient(
'mongodb+srv://test:sparta@cluster0.wgvgsfc.mongodb.net/?retryWrites=true&w=majority')
db = client.dbsparta
@app.route('/')
def home():
return render_template('index.html')
@app.route("/bucket", methods=["POST"])
def bucket_post():
bucket_receive = request.form['bucket_give']
doc = {
'bucket': bucket_receive,
'done': 0,
}
db.bucket.insert_one(doc)
return jsonify({'msg': '등록 완료!'})
@app.route("/bucket/done", methods=["POST"])
def bucket_done():
id_receive = request.form['id_give']
db.bucket.update_one({'_id': ObjectId(id_receive)}, {'$set': {'done': 1}})
return jsonify({'msg': '버킷 완료!'})
@app.route("/bucket/cancel", methods=["POST"])
def bucket_cancel():
id_receive = request.form['id_give']
db.bucket.update_one({'_id': ObjectId(id_receive)}, {'$set': {'done': 0}})
return jsonify({'msg': '버킷 취소!'})
@app.route("/bucket/delete", methods=["POST"])
def bucket_delete():
id_receive = request.form['id_give']
db.bucket.delete_one({'_id': ObjectId(id_receive)})
return jsonify({'msg': '버킷 삭제!'})
@app.route("/bucket", methods=["GET"])
def bucket_get():
bucket_list = list(db.bucket.find())
# IdObject() to string
for i in range(len(bucket_list)):
bucket_list[i]['_id'] = str(bucket_list[i]['_id'])
return jsonify({'buckets': bucket_list})
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+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 > div.form-check {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-bottom: 10px;
min-height: 48px;
}
.mybox > div.form-check > h3 {
max-width: 75%;
font-size: 20px;
font-weight: 500;
margin-left: 10px;
margin-right: auto;
margin-bottom: 0px;
}
.mybox > div.form-check > h3.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 rows = response['buckets'];
for (let i = 0; i < rows.length; i++) {
const bucket = rows[i].bucket;
const id = rows[i]._id;
const done = rows[i].done;
let temp_html = ``;
if (done) {
temp_html = `
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
checked
id="${id}"
onclick="cancel_bucket(event)"
/>
<h3 class="done">${bucket}</h3>
<button id=${id} onclick="delete_bucket(event)" type="button" class="btn-close" aria-label="Close"></button>
</div>
`;
} else {
temp_html = `
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="${id}"
onclick="done_bucket(event)"
/>
<h3>${bucket}</h3>
<button id=${id} onclick="delete_bucket(event)" type="button" class="btn-close" aria-label="Close"></button>
</div>
`;
}
$('#bucket-list').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(event) {
const id = event.target.getAttribute('id');
$.ajax({
type: 'POST',
url: '/bucket/done',
data: { id_give: id },
success: function (response) {
alert(response['msg']);
window.location.reload();
},
});
}
function cancel_bucket(event) {
const id = event.target.getAttribute('id');
$.ajax({
type: 'POST',
url: '/bucket/cancel',
data: { id_give: id },
success: function (response) {
alert(response['msg']);
window.location.reload();
},
});
}
function delete_bucket(event) {
const id = event.target.getAttribute('id');
$.ajax({
type: 'POST',
url: '/bucket/delete',
data: { id_give: id },
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
id="bucket"
class="form-control"
type="text"
placeholder="이루고 싶은 것을 입력하세요"
/>
<button
onclick="save_bucket()"
type="button"
class="btn btn-outline-primary"
>
기록하기
</button>
</div>
</div>
<div class="mybox" id="bucket-list"></div>
</body>
</html>
'항해99' 카테고리의 다른 글
[항해99] 4주차 React 숙련 회고 (0) | 2022.12.11 |
---|---|
[항해99] 3주차 React 입문 회고 (2) | 2022.12.04 |
[항해99] 2주차 알고리즘 회고 (2) | 2022.11.27 |
[항해99] 1주차 미니프로젝트 회고 (5) | 2022.11.20 |
Javascript의 특성에 대해서 알아보자 (0) | 2022.11.18 |