😂 거북이반 인스타 클론코딩
😭 게시글 수정 / 삭제
- 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)
}
}