😂 거북이반 인스타 클론코딩
😭 게시글 수정 / 삭제
- authorize 수정
- 이번 수업에서 다룬 내용들은 이전과 달리 user 이외의 인자들을 필요로 한다. 따라서 여러 값이 인자로 들어갈 수 있도록 조치해주어야 한다.
*args : list 형태인 argument는 아무거나 다 들어와도 된다는 뜻
**kwargs : a=b의 형태인 키워드들이 몇개가 들어와도 인식할 수 있다.
def authorize(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if not 'Authorization' in request.headers:
abort(401)
token = request.headers['Authorization']
try:
user = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
except:
abort(401)
return f(user, *args, **kwargs)
return decorated_function
- 게시글 수정
- 백앤드 부분
/article/<article_id>에서 볼 수 있듯이, article_id 값을 꼭 붙여 어떤 게시글을 수정하는지 기입해야 한다.
- 게시글을 수정하게 되면
제목과 내용을 수정하게 된다. 따라서 title과 content의 데이터를 프론트에서 받아 와야 한다.
- 여기서 중요한 점은
게시글을 작성한 사용자를 식별하는 것이며, 해당 사용자가 작성한 게시글이 어떤 것이냐이다. 따라서 게시글의 _id를 사용해야 한다.
- 게시글의 아이디는 json 형식으로 프론트에서 넘어오기 때문에
str 타입이고, 우리가 db에서 해당 아이디를 가지고 있는 게시글을 찾아내기 위해서는 ObjectId 값이 필요하다. 따라서 ObjectId(article_id)를 꼭 해주어야 한다.
- postman으로 게시글의 정보를 확인해 보면, 우리는 게시글을 작성한 사용자의 아이디를 str 타입으로 db에 저장하였기 때문에 user의 id는 그대로 사용해 주면 된다.(여기서 user란 authorize 함수에서 디코딩되어 나오는 내용이다.
id, email, exp가 들어있다.)
- 여태까지와 다르게 우리는 게시글을
수정하는 것이기 때문에 update_one을 사용하게 되었다. 하나의 게시글만을 수정하는 것이기 때문에 위의 함수를 사용하면 되며, .matched_count를 사용해서 수정 여부를 알 수 있다.
.matched_count = 1 --> 수정 완료
.matched_count = 0 --> 수정되지 않음
@app.route('/article/<article_id>', methods=["PATCH"])
@authorize
def patch_article_detail(user, article_id):
data = json.loads(request.data)
title = data.get("title")
content = data.get("content")
article = db.article.update_one(
{"_id": ObjectId(article_id), "user": user["id"]}, {"$set": {"title": title, "content": content}})
print(article.matched_count)
if article.matched_count:
return jsonify({'msg': 'success'})
else:
return jsonify({'msg': 'fail'}), 403
- 프론트앤드 부분
updateMode() 함수
- update를 진행하는
버튼에 onclick으로 적용되어 있는 함수이다.
수정하기 버튼을 클릭하게 되면 원래 값이 적혀있는 부분이 hidden으로 인해 가려지게 된다.
- 수정할 부분을 작성할
textarea가 생성되면서 title과 content를 각각 작성하면 된다.
- 현재 값이 가려져 있는 title과 content 변수에 방금 작성한 부분이
insertBefore 함수로 인해 들어가게 된다.
- 그 후, 내가 방금 누른
update_button의 onclick 함수가 updateArticle()로 변하게 되며 해당 함수가 실행된다.
function updateMode() {
const title = document.getElementById('title')
const content = document.getElementById('content')
title.style.visibility = "hidden";
content.style.visibility = "hidden";
const input_title = document.createElement("textarea")
input_title.setAttribute("id", "input_title")
input_title.innerText = title.innerHTML
const input_content = document.createElement("textarea")
input_content.setAttribute("id", "input_content")
input_content.innerText = content.innerHTML
input_content.rows = 10
const body = document.body
body.insertBefore(input_title, title)
body.insertBefore(input_content, content)
const update_button = document.getElementById("update_button")
update_button.setAttribute("onclick", "updateArticle()")
}
updateArticle() 함수
- 위에서 작성된
input_title과 input_content가 해당 함수로 들어와 patchArticle()의 인자로 들어가게 된다. 아래에도 설명되겠지만 해당 함수로 인해 수정된 내용들이 백앤드 부분으로 넘어갈 수 있게 된다.
- 위에서 작성된
body.insertBefore(input_title, title)에서 유추할 수 있듯이, 우리가 작성한 수정 부분은 이미 title과 content 변수에게 값이 넘어간 상태이다. 그러므로 단지 textarea로서 값만을 보여주던 input_title과 input_content를 화면에서 지워도 상관이 없다. 또한 우리가 수정된 값을 보여주려면 textarea를 없애주어야 되기도 하기 때문에 remove하도록 하겠다.
- 수정된 부분이 백앤드로 넘어가 db 상에서 수정이 다 되었기 때문에
title과 content 변수의 값을 다시 사용자에게 보여주어도 된다.
- 수정이 완료되기도 하였고, 우리가
수정하기 버튼을 누르게 되면서 해당 버튼의 함수를 updateArticle()로 변경했었기 때문에 사용자가 다시 또 수정을 할 수 있도록 원래의 함수인 updateMode()로 바꿔주었다.
- 그리고 마지막에
loadArticles(article_id)를 통해 title과 content를 불러왔다.
async function updateArticle() {
var input_title = document.getElementById('input_title')
var input_content = document.getElementById('input_content')
console.log(input_title.value, input_content.value)
const article = await patchArticle(article_id, input_title.value, input_content.value)
input_title.remove()
input_content.remove()
const title = document.getElementById('title')
const content = document.getElementById('content')
title.style.visibility = "visible";
content.style.visibility = "visible";
update_button.setAttribute("onclick", "updateMode()")
loadArticles(article_id)
}
patchArticle 함수
- 게시글에서 수정할 내용인 title과 content를 담은
articleData를 body에 담고, headers에 localstorage에 저장되어 있던 token 값과 함께 백앤드의 url을 가진 부분으로 fetch 해준다.
patchArticle 함수에 인자로 담긴 article_id와 회원가입이 완료되어 로그인하고 게시글을 작성한 user의 정보, 수정될 게시글의 내용이 백앤드로 전달되어 수정이 되고 db에 update되는 것이다.
async function patchArticle(article_id, title, content) {
const articleData = {
"title": title,
"content": content
}
const response = await fetch(`${backend_base_url}/article/${article_id}`, {
headers: {
'Authorization': localStorage.getItem('token')
},
method: 'PATCH',
body: JSON.stringify(articleData)
})
if (response.status == 200) {
response_json = await response.json()
return response_json
} else {
alert(response.status)
}
}
- 삭제하기
- 백앤드 부분
PATCH의 경우와 같이, /article/<article_id>를 작성해야 한다.
- 함수의 인자로
user와 article_id가 들어간다. 이를 통해 글을 작성한 사용자만이 본인의 글을 삭제할 수 있도록 조치해 주는 것이다.
- json 형식으로 넘어온 article_id를
ObjectId(article_id)로 변경해주어야 db에서 찾을 수 있으니 주의해야 한다.
- 항상 하다가 헷갈리면 db를 확인하거나, postman을 통해
어떤 값들이 오고 가는지 확인해 보는것이 중요하다!
@app.route('/article/<article_id>', methods=["DELETE"])
@authorize
def delete_article_detail(user, article_id):
article = db.article.delete_one(
{"_id": ObjectId(article_id), "user": user['id']})
if article.deleted_count:
return jsonify({'msg': 'success'})
else:
return jsonify({'msg': 'fail'}), 403
- 프론트앤드 부분
removeArticle() 함수
삭제하기 버튼을 누르게 되면 deleteArticle 함수가 실행되게 된다. 이때 인자로 article_id를 사용하게 된다.
async function removeArticle() {
await deleteArticle(article_id)
}
deleteArticle() 함수
- 위에서 받은 인자를 백앤드의 url에 포함시켜
DELETE 요청을 보내게 된다. 이때 token을 같이 보내 해당 글을 작성한 사용자가 맞는지에 대한 검사를 진행하게 된다.
- 게시글의 삭제가 완료되면 메인 페이지로 이동할 수 있도록 조치하였다.
async function deleteArticle() {
const response = await fetch(`${backend_base_url}/article/${article_id}`, {
headers: { 'Authorization': localStorage.getItem('token') },
method: 'DELETE'
})
if (response.status == 200) {
window.location.replace(`${frontend_base_url}/`)
} else {
alert(response.status)
}
}