Post

Memo App CRUD

✅ POST

☑️ FE: JS:

📌 function handleSubmit

when clicked, call function handleSubmit
submit automatically reloads page, so prevent reload default

1
2
3
4
function handleSubmit(event) {
  //submit reloads page automatically, so prevent reload default
  event.preventDefault();
}

get input of the client

client input.value has to be let, as it will be changed

1
2
const clientInput = document.querySelector(".memo_input");
let clientInputValue = clientInput.value;

get input value, then send it to createMemo function

and clear the input tag

1
2
createMemo(clientInputValue);
clientInputValue = "";

📌 function createMemo

POST method

1
2
3
4
body: JSON.stringify({
            id: new Date().getTime(),
            content: value;
        }),

값을 ``로 감싸줘야 한다.
그 이유는 통신을 할 때는 문자열로만 할 수 있기 떄문이다.

CODE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async function createMemo(value) {
  //post on server
  const res = await fetch("/memos", {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      id: new Date().getTime(),
      content: value
    })
  });
  const jsonRes = await res.json();
}

☑️ BE: PYTHON

class 미리 설정해 두어야 한다.

아까 JS에서도 이렇게 정의해주기로 했음.

1
2
3
class Memo(BaseModel):
    id: str
    content: str

array를 하나 만들어 안에 memo넣을 것임

1
2
3
4
5
6
memos= []

@app.post("/memos")
def create_memo(memo):
    memos.append(memo)
    return "memo created successfully"

📌 def create_memo

@app.post(“/memos”)

memos라는 경로로 받을 건데, memo로 받을 거야.

CODE

1
2
3
4
5
6
7
8
9
10
class Memo(BaseModel):
    id: str
    content: str

memos= []

@app.post("/memos")
def create_memo(memo:Memo):
    memos.append(memo)
    return "memo created successfully"

✅ GET

☑️ FE: JS:

📌 function readMemo

디폴트로 가는 건 GET요청 이 함수는 언제 실행되어야 할까? (1) 페이지가 처음에 로딩되었을 떄 (2) 새로운 메모를 추가했을 때 createMemo

CODE

1
2
3
4
async function readMemo() {
  const res = await fetch("/memos");
  const jsonRes = await res.json();
}

☑️ BE: PYTHON

CODE

1
2
3
@app.get("/memos")
def read_memo():
    return memos

☑️ FE: JS:

forEach

BE에서 memos를 array로 내려주면 FE createMemo 함수에서는 그것을 받아 배열 안에서 빙글빙글 돌면서 하나하나 받아 화면에 보여줘야 한다. 그러기 위해 displayMemo 함수에 배열에서 받은 것을 전달함. 이 코드는 createMemo 함수안에서 일어나야 함을 주의!

1
2
3
4
5
6
async function readMemo() {
  const res = await fetch("/memos");
  const jsonRes = await res.json();

  jsonRes.forEach(displayMemo);
}

📌 function displayMemo

먼저 보여줄 li, ul을 만들어야 함

read한 memo를 화면에 보여주기
먼저 보여줄 li, ul을 만들어야 함.

1
2
3
const ul = document.querySelector(".memo_ul");
const li = document.createElement("li");
ul.appendChild(li);

그리고 li의 innerText를 memo.content로 정의

1
li.innerText = `id:${memo.id} content:${memo.content}`;

CODE

이 때 memo를 parameter로 받아와야 함 주의!

1
2
3
4
5
6
7
function displayMemo(memo) {
  const ul = document.querySelector(".memo_ul");
  const li = document.createElement("li");
  ul.appendChild(li);

  li.innerText = `id:${memo.id} content:${memo.content}`;
}

그런데 여기까지 하면 새로 메모를 추가할 때마다 새롭게 메모를 전체 다 불러오는 문제 발생

스크린샷 2023-11-22 오후 10 50 42

이를 해결하기 위해 readMemo에 read할 때마다 ul지우도록 명령.

1
2
3
4
5
6
7
async function readMemo() {
  const res = await fetch("/memos");
  const jsonRes = await res.json();
  const ul = document.querySelector(".memo_ul");
  ul.innerHTML = "";
  jsonRes.forEach(displayMemo);
}

✅ PUT(update)

☑️ FE: JS

클릭되면 수정하는 버튼, displayMemo함수 안에 만들 것

1
2
3
4
5
const updateBtn = document.createElement("button");
updateBtn.innerText = "update";
li.appendChild(updateBtn);

updateBtn.addEventListener("click", updateMemo);

어떤 메모의 updateBtn이 클릭되었는지 알아야되니까 memo의 id를 받아와야 한다.

이를 위해서 displayMemo함수안에 updateBtn의 dataset의 id를 memo.id로 설정

1
updateBtn.dataset.id = memo.id;

📌 function updateMemo

어떤 memo의 updateBtn이 눌렸는지 알아야 할 것 아니야

updateMemo 함수에서 memo.id받기 eventListener은 항상 event값을 return하니까 이를 받아와 memo id 받아오기 ` const id= event.target.dataset.id;`

수정할 값 입력받기

💡 prompt const updateContent= prompt("How would you like to change your memo?")

수정할 값 받아오기 => request body => PUT

수정 후에는 서버에서 다시 한 번 수정된 array받아와야 하니까 readMemo 호출

CODE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
async function updateMemo(event) {
  //get ID of memo to know which memo update btn was clicked
  //eventlistener은 항상 event를 return하니까.
  const id = event.target.dataset.id;
  //뭐라고 수정할지 값 입력받기
  const updateContent = prompt("How would you like to change your memo?");
  const res = await fetch(`./memos/${id}`, {
    method: "PUT",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      id: id,
      content: updateContent
    })
  });
  readMemo();
}

☑️ BE: PYTHON

📌 def update_memo

FE에서 put, request body로 보냈으니까 받아야 함.
@app.put("/memos/{memo_id}")
여기서 /memos/는 FE에서 설정해 준 경로이다. {memo_id}PATH해서 받아올 그 값! 을 의미

for문을 사용해
memos라는 array를 빙글빙글 돌면서 item인 memo를 하나씩 꺼내본다. for memo in memos:
if문을 사용해
memo.id(array에 있는 memo id가) FE에서 받아온 메모의 id req_memo.id와 같다면, memo.content(array에 있는 memo content)를 FE에서 받아온 수정 값 req_memo.content으로 바꾸세요.

1
2
3
4
5
for memo in memos:
        if memo.id == req_memo.id:
            memo.content= req_memo.content
            return "Update Succeeded"
    return "No such Memo"

내가 쉽게 설명해 본 코드

1
2
3
4
5
6
@app.put("/FE에서 설정한 경로memos/{memo_id}")
def update_memo(updateRequestedMemo:Memo):
    #memos라는 배열을  돌면서 for문
    for arrayItemMemo in memosArray:
        if arrayItemMemo.id ==updateRequestedMemo.id:
            arrayItemMemo.content= updateRequestedMemo .content

CODE

1
2
3
4
5
6
7
8
9
10
@app.put("/memos/{memo_id}")
def update_memo(req_memo:Memo):
    #memos라는 배열을  돌면서 for문
    for memo in memos:
        if memo.id ==req_memo.id:
            memo.content= req_memo.content
            return "Update Succeeded"
    return "No such Memo"

✅ DELETE

☑️ FE: JS:

클릭되면 삭제하는 버튼, displayMemo함수 안에 만들 것

1
2
3
4
5
6
7
8
//delete button
const deleteBtn = document.createElement("button");
deleteBtn.innerText = "delete";
li.appendChild(deleteBtn);

deleteBtn.addEventListener("click", deleteMemo);

deleteBtn.dataset.id = memo.id;

📌 function deleteMemo

삭제할 memo의 id 받아와서 DELETE

method: "DELETE"는 request body가 아니니까 headers, body모두 필요 없다!

CODE

1
2
3
4
5
6
7
async function deleteMemo(event) {
  const id = event.target.dataset.id;
  const res = await fetch(`/memos/{id}`, {
    method: "DELETE"
  });
  readMemo();
}

☑️ BE: PYTHON

📌 def delete_memo

array.pop

FE에서 보낸 memo_id를 받는다.
그 아이디 숫자와 일치하는 아이디를 가진 [메모 배열]의 메모를 찾아 배열에서 pop한다.

enumerate

for문에서 두 가지를 돌릴 떄는 enumerate함수를 사용한다.
delete_memo의 경우 메모의 index, memo모두 받기 떄문에 enumerate가 필요하다.

CODE

1
2
3
4
5
6
7
8
9
10
11
class Memo(BaseModel):
    id: str
    content: str

@app.delete("/memos/{memo_id}")
def delete_memo(memo_id):
    for index, memo in enumerate(memos):
        if memo.id == memo_id:
            memos.pop(index)
            return "memo successfully deleted"
    return "no such memo"

✅ Trouble Shooting

🔴 Trouble deleteBtn doenst work!!!

🟠 Mistakes I Made_헷갈리거나 실수한 점

백엔드에서 메모 아이디 int로 받으라고 했고, 프론트에서도 잘 보냈으며 create, read, update까지, 그리고 post, put, read메소드는 문제가 없었는데 delete에서 삭제 버튼을 눌러 메모가 지워지지 않는 문제가 생김.

🟡 What I tried_스스로 시도해 본 것들

  • memos.pop을 pop말고 clear, remove 써봤지만 안됨
  • memos.pop(index)에서 index말고 memo 등 다른 값 넣었지만 안 됨

🟢 What I learned_알게된 점

메모 아이디 값을 int가 아닌 str으로 바꿔주기,
BE에서도 아이디 값을 스트링으로 받아야 함.
이렇게 했더니 해결이 되었고, 이제는 삭제 버튼을 누르면 메모가 잘 삭제된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function createMemo(value) {
  //post on server
  const res = await fetch("/memos", {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      id: toString(new Date().getTime()),
      content: value
    })
  });

  readMemo();
}
1
2
3
class Memo(BaseModel):
    id: str
    content: str

✅ 쿼리를 사용해 정렬된 데이터를 서버에서 내려주기

정렬 기준은 가나다ABC순 또는 등록순 2가지 모두 구현 (ex) /memos?sorted=ASC memo에는 다양한 프로퍼티가 있음. (title, createAt 등등) 어떤 프로퍼티를 기준으로 정렬을 시킬건지도 쿼리에 함께 포함

등록일자순 내림차순으로 정렬:

1
GET /memos?sort_by=created&sort_order=desc

제목순 오름차순으로 정렬:

1
GET /memos?sort_by=title&sort_order=asc
This post is licensed under CC BY 4.0 by the author.