Node.js

node>삭제기능 만들기 (AJAX, query string,dataset, AJAX 추가 내용)

연습노트 2024. 7. 25. 10:48

글 삭제기능도 만들어봅시다.

기능이 어떻게 동작하는지 글로 정리부터 해보면

"글마다 있는 삭제버튼 누르면 DB에 있는 document 삭제하기"가 끝인데 

근데 유저가 직접 DB를 조작하면 위험하니까 중간에 서버를 거치도록 합시다. 

 

1. 글마다 삭제버튼이 있는데 누르면

2. 서버로 이 글 삭제해달라고 요청을 날리고 

3. 서버는 확인 후 DB에 있는 document 삭제 

이러면 될 것 같습니다. 

그리고 이런거 작성할 때 상세할 수록 더 좋은 코드 작성이 가능합니다. 

 

 

 

 

 

 

AJAX 

 

삭제기능은 똑같이 만들면 재미없으니까 좀 다르게 만들어보도록 합시다. 

여러분들 서버로 GET요청 POST요청 날리는 법 배웠죠? 

주소창에 URL 입력하거나 <form>을 전송하거나 하면 됩니다.

근데 그런걸로 요청을 날리면 항상 새로고침이 되는데

새로고침되는게 아니꼬우면 새로고침 없이도 서버에게 요청을 날리고 데이터를 주고받고 그럴 수 있습니다.

AJAX로 요청을 날리면 됩니다. 

 

 

 

 

 

1. 글마다 삭제버튼이 있는데 이거 누르면 

 

삭제버튼은 여기 list.ejs 글목록에다가 만들면 편할거같은데

 

 

(list.ejs)

<div class="white-bg">
  <% for (let i = 0; i < 글목록.length; i++){ %>
    <div class="list-box">
      <h4>
        <a href="/detail/<%= 글목록[i]._id %>">
          <%= 글목록[i].title %>
        </a>
        <a href="/edit/<%= 글목록[i]._id %>">✏️</a>
        <span class="delete">🗑️</span>
      </h4>
      <p>글내용임</p>
    </div>
  <% } %>
</div>

쓰레기통 누르면 삭제되도록 만들어봅시다. 

근데 이 span 태그를 클릭하면 삭제요청이 서버로 전송되어야하는데 

그러려면 <form>과 전송버튼이 필요하겠지만

<form> 전송시 새로고침 되는게 죽도록 싫으면 AJAX라는걸 씁시다. 

쓰려면 자바스크립트 코드를 좀 짜야합니다. 

 

 

 

 

<script>
  document.querySelectorAll('.delete')[0].addEventListener('click', function(){
    fetch()
  })
</script>

list.ejs 하단에 <body>태그 끝나기 전 쯤 이런 코드를 추가해봅시다. 

그냥 정말 기본적인 자바스크립트 문법이구요 

 

- <script> 태그안에 자바스크립트를 적을 수 있습니다. 그럼 페이지 로드시 자동 실행됩니다.

- querySelector로 원하는 html 요소를 찾을 수 있고 addEventListener 쓰면 그걸 클릭시 실행할 코드를 입력할 수 있습니다. 

- fetch() 는 AJAX 요청 날릴 때 쓰는 자바스크립트 기본 함수입니다. 

그래서 지금 뭘 짠거냐면 "delete라는 클래스명을 가진 여러 html 요소 중 0번째를 찾아서 클릭시 fetch() 실행해주쇼" 라고 코드짠 것임

 

 

 

 

 

 

fetch( ) 사용법 

 

fetch() 를 쓰면 서버로 GET, POST, PUT, DELETE 요청을 할 수 있는데

새로고침 없이 몰래 할 수 있습니다. 이걸 AJAX라고 부릅니다. 

새로고침 없이 서버랑 데이터를 주고받으면 더 이쁜고 부드러운 감성가득한 사이트가 되지 않을까요. 

 

 

 

fetch('/URL~~')

사용법 잠깐 알아보자면 fetch('/URL') 적으면 이 URL로 GET요청이 날라갑니다. 에구쉬워 

 

 

 

fetch('/URL~~', {
  method : 'POST',
  headers : { 'Content-Type' : 'application/json' },
  body : JSON.stringify({a : 1})
})

POST요청하고싶으면 뒤에다가 중괄호 열고 설정들을 집어넣을 수 있는데 

- method 에는 원하는 method 넣으면 되고 

- headers 는 부가정보 기입란인데 저렇게 작성해야 array, object 데이터를 서버로 잘 전송할 수 있습니다. 

- body 안에는 서버로 전송할 array, object 데이터를 집어넣으면 됩니다.

근데 array, object 를 그냥 넣으면 깨지기 때문에 JSON.stringify() 를 써서 문자형태로 (JSON형태로) 바꿔서 전송해야 잘 갑니다.

 

진짜 POST 요청이 잘 가는지 확인하고 싶으면 

fetch() 잘 작성해서 한 번 실행시켜보십시오. 

그리고 서버에서 요청.body 한 번 출력해보면 되겠습니다. 

참고로 요청.body가 잘 안나오면 서버파일 상단에 app.use(express.json()) 이런게 없어서 그럴 수도 있음 

 

 

Q. 전 자바스크립트 코드 실행 어떻게 하는지 몰라요 

- 방금 만든 addEventListener 안에 적으면 0번째 삭제버튼 누를 때 실행됩니다. 

 

 

 

 

 

 

 

서버로 데이터 전송하는 여러가지 방법

 

AJAX 배운 기념으로 서버로 데이터 전송하는 법 몇 개만 더 알아봅시다.

서버로 데이터 보내고 싶으면 폼태그 쓰거나

아니면 AJAX 쓰는 경우 body : 뒤에 데이터 적어두면 전송되는데

가끔 DELETE요청시 body : 를 넣으면 잘 안되는 경우도 있고

아니면 너무 짧고 하찮아서 굳이 body : 에 적기 귀찮은 경우도 있습니다.

 

그럴 때 서버로 데이터 전송할 수 있는 방법이 한 2개 정도 더 있는데 query string 이랑 URL parameter를 이용하는겁니다.

 

 

 

 

 

 

URL 파라미터로 서버에 데이터 전송하기

 

URL 파라미터는 써봤으니까 짧게 합시다. 

예를 들어 유저가 서버에게 내 이름을 전송하고 싶은 겁니다.

 

app.get('/abc/:name') 

그럴 때 서버는 이렇게 URL 파라미터를 이용해서 API를 만들어두면 

유저는 /abc/어쩌구 이런 URL로 GET요청 보낼 수 있겠죠?

근데 어쩌구 자리에 내 이름을 집어넣어서 /abc/홍길동 이렇게 요청보내는 겁니다.

그럼 서버는 요청.params 출력해보면 '홍길동'이 들어있기 때문에 이거 써도 유저가 서버로 데이터를 전송할 수 있는 겁니다.

 

 

 

 

 

 

query string으로 서버에 데이터 전송하기

 

아니면 서버에 URL 파라미터같은거 셋팅해놓기 귀찮은 경우 쿼리스트링이라는 것도 사용가능합니다. 

쿼리스트링은 서버에 아무 셋팅해놓을 필요 없고 

그냥 아무 API로 GET, POST, PUT, DELETE 요청할 때 

 

 

fetch('/abc?데이터이름=데이터값') 

?데이터이름=데이터값

이런 식으로 데이터를 적는겁니다. 

그럼 신기하게 서버로 갑니다. 

 

 

fetch('/abc?데이터이름=데이터값&데이터이름2=데이터값2') 

하나 더 전송하고 싶으면 & 기호로 연결하면 됩니다. 

서버에서 이걸 꺼내고 싶으면 요청.query 출력해보시면 아마 들어있습니다. 

필요할 때 쓰도록 합시다.

 

 

 

URL 파라미터 / query string 의 장점은

둘 다 아무 API로 GET, POST, PUT, DELETE 요청할 때 전부 쓸 수 있다는게 장점이고 

단점은 URL에 정보가 노출된다는 겁니다. 

그래서 짧고 안중요한 데이터 전송하는데 쓰는게 좋습니다. 

 

필요한건 다 알려드렸으니 삭제기능도 다음시간까지 최대한 직접 한번 만들어봅시다.

“삭제버튼누르고 새로고침했을 때 그 글이 안보이면” 성공입니다.

 

첫 글의 삭제버튼 기능부터 만들어보자

 

한 번에 모든 버튼에 기능개발하려는 사람들이 많은데

그렇게 코딩하면 안됩니다.

초보들은 한 버튼의 기능만 먼저 만들어보고 그걸 확장해나가는 식으로 코드짜는게 더 쉽습니다.

그래서 첫 글의 삭제기능부터 완성해봅시다. 

첫째 글의 삭제기능 어떻게 만들어요? 

 

- 첫째 삭제버튼 누르면 서버로 삭제요청날리고

- 서버는 확인후에 DB에서 글 삭제해주면 

끝 아닙니까 

 

 

 

 

document.querySelectorAll('.delete')[0].addEventListener('click', function(){
  0번째 .delete 버튼 누르면 실행할 코드~~
}) 

첫째 버튼 누르면 서버로 삭제요청 날리고 싶으면 이거처럼 이벤트리스너 작성하면 된다고 했습니다. 

 

 

fetch('/delete', {
  method : 'DELETE',
}) 

그리고  이벤트리스너 안에 이런 코드 써서 DELETE 요청을 날려봤습니다.

 

이제 서버에서 API 만들면 될 것 같은데 

근데 잘 생각해보면 이렇게만 요청하면 서버는 무슨 document를 삭제해야할지 알까요?

서버가 점쟁이도 아니고 아마 모를겁니다.

그래서 아마 삭제하고싶은 document의 _id같은것도 보내라고 하지 않을까요 

아마 그럴 것 같습니다. 

 

 

 

 

fetch('/delete?docid=첫째글_id', {
  method : 'DELETE',
}) 

그래서 삭제할 글의 _id도 함께 서버로 보내보도록 합시다. 

body 이용해도 되는데 참고로 delete 요청할 때 body 보내면 잘 안가는 경우도 있어서 

쿼리스트링으로 보내봤습니다. 

 

그래서 첫째글의 _id 를 쿼리스트링으로 보내면 될텐데 첫째 글의 _id가 어딨어요? 

서버에서 보낸 <%= 글목록 %>이라는 변수 출력해보시면 있지않습니까 

출력해보면 아마 첫째글, 그러니까 0번째 글의 _id도 그 변수안에 들어있을걸요.

그걸 쿼리스트링에 넣어서 전송해야겠습니다. 

 

 

 

 

document.querySelectorAll('.delete')[0].addEventListener('click', function(){
  fetch('/delete?docid=' + '<%= 글목록[0]._id %>', {
    method : 'DELETE',
  })
}) 

첫 버튼의 삭제요청기능 완성

서버에서 /delete로 DELETE요청 잘 오는지 테스트해봅시다.

 

 

 

 

 

 

 

요청들어오면 서버는 삭제

 

삭제하라고 요청이 들어오면 

서버는 확인 후에 삭제해주면 됩니다. 

 

(server.js)

app.delete('/delete', async (요청, 응답) => {
  let result = await db.collection('post').deleteOne( { _id : new ObjectId(요청.query.docid) } )
  응답.send('삭제완료')
})

삭제하고 싶으면 .deleteOne() 써주면 됩니다. 

소괄호 안에는 삭제하고 싶은 document의 정보를 기입하면 되는데 { _id : 유저가보낸_id } 를 기입하면 되겠군요. 

진짜 삭제 잘 되는지 테스트해봅시다.

 

참고로 AJAX로 서버에 요청하는 경우 

서버는 요청.redirect() 요청.render() 이런걸 사용하면 안됩니다.

이런건 다른 페이지로 이동하는거라 새로고침되지않습니까. 

 

 

 

 

 

나머지 버튼도 개발해봅시다 

 

지금 0번째 버튼에만 기능개발해놨죠?

지금 0번째 버튼 클릭하면 DELETE 요청해주세요 라고 코드를 짜놨을 뿐입니다. 

1번째 버튼 누르면 서버로 또 삭제요청해주고싶으면 어떻게해요?

 

 

 

document.querySelectorAll('.delete')[1].addEventListener('click', function(){
  fetch('/delete?docid=' + '<%= 글목록[1]._id %>', {
    method : 'DELETE',
  })
}) 

[0]으로 기재된걸 [1]로 바꿔서 똑같이 한번 더 작성해주면 되겠죠?  

1번 버튼 누르면 서버로 지금 글의 _id를 적어서 DELETE요청 잘 보내줄 것 같군요. 

근데 이런 비슷한 코드 반복되면 뭐 쓰라고 했습니까

반복문 쓸 수 있을 것 같은데 한 번 써보시면 되겠습니다.

반복문 못 쓰겠으면 그냥 손수 글 갯수만큼 복붙하면 됩니다. 

 

 

 

 

 

 

 

_id 가져오는 다른 방법 dataset

 

자바스크립트 잘하면 이런 방법 써도 _id를 가져올 수 있는데

html에다가 _id 를 미리 중요한 정보를 숨겨놓고

클릭할 때 마다 가져올 수도 있습니다.

 

 

<span data-어쩌구="저쩌구"></span>

아무 html에다가 data-어쩌구="저쩌구" 이렇게 기입할 수 있는데 

이게 뭐냐면 어쩌구라는 이름으로 저쩌구라는 자료를 저장해두라는 뜻입니다. 

 

이 짓거리의 장점은 이 html요소를 클릭하거나 그런 경우

거기 숨겨놨던 데이터를 되게 쉽게 가져올 수가 있는데 

이벤트리스너 안에서 e.target.dataset.데이터이름 사용하면 숨겨놨던 데이터가 이 자리에 나옵니다. 

 

그래서 현재 게시물 _id같은것도 이 삭제버튼에다가 숨겨놓으면 

이 삭제버튼을 클릭했을 때 되게 쉽게 가져와서 쓸 수 있지않을까요.

 

 

 

(list.ejs)

<span class="delete" data-id="<%= 글목록[i]._id %>">🗑️</span>

예를 들어서 삭제버튼에 id라는 이름으로 글의 _id를 숨겨놓으면 

그럼 이제 요걸 클릭했을 때 e.target.dataset.id라고 쓰면 글의 _id를 가져다쓸 수 있으니까

 

 

document.querySelectorAll('.delete')[0].addEventListener('click', function(e){
  fetch('/delete?docid=' + e.target.dataset.id, {
    method : 'DELETE',
  })
}) 

이렇게 구현하셔도 상관없습니다.

(addEventListener 콜백함수 안에 e 파라미터 추가해야 사용가능)

 

개인적으로 아까보다는 이 방법이 더 나은거같은데

왜냐면 나중에 글들을 정렬하고 필터하고 그러면 html이 순서가 바뀌고 아니면 일부가 없어지고 그럴 수도 있을텐데

이걸 쓰면 글목록이 섞였을 때도 정확히 삭제요청을 날릴 수 있을 수 있겠군요. 

 

 

 

오늘의 교훈은 코드짤 때 맨날 한 번에 모든걸 구현하려고 하는 분들이 있는데 

그런건 코딩 고수나 하는 것이고

여러분들은 그러면 안되고 한 번에 하나의 기능부터 차근차근 구현하시면 됩니다. 

 

 

AJAX 추가내용

 

AJAX 추가 문법들을 더 알아보고 지나갑시다. 

AJAX 성공시 서버가 보낸 데이터같은것도 출력해볼 수 있습니다.

지금 삭제요청 완료하면 서버에서 메세지같은거 유저에게 보내주고 있을텐데 그거 한번 출력해보도록 합시다.

 

 

fetch('/URL').then((r)=>r.json())
.then((result)=>{ console.log(result) }) //서버에서 array, object보내는 경우

fetch('/URL').then((r)=>r.text())
.then((result)=>{ console.log(result) }) //서버에서 문자보내는 경우

fetch 만든 사람이 then 2번 붙이라는군요.  

정확히말하면 자바스크립트에서 좀 늦게처리되는 함수들이 있는데

그런거 작업이 완료되었을 때 코드를 실행하고 싶으면 뒤에 .then 붙이거나 await 붙이거나 해야합니다. 

진짠지 출력해보면 되는데 html 파일에 적은 console.log는 크롬개발자도구에서 확인가능합니다. 

 

 

 

그래서 AJAX 잘 쓰면 새로고침 없이도 서버에서 새로운 데이터를 가져올 수 있다보니까 

AJAX 잘 쓰면 재밌는 기능을 많이 만들수 있습니다.

예를 들어서 지금 글제목 누르면 상세페이지 html을 받아오는게 아니라

1. 상세페이지에 필요한 글의 제목과 내용만 AJAX로 새로고침 없이 받아오고 

2. 그리고 그 받아온걸로 이쁘게 html을 만들어서 유저에게 보여주는겁니다. 

이러면 어떻게 되겠습니까. 

클릭하면 이제 새로고침없이도 상세내용을 보여줄 수 있으니까

좀 더 이쁘고 부드럽게 동작하는 사이트를 만들 수 있지 않을까요 ? 

 

 

 

렌더링 방법 

 

여기서 알수있는게

유저에게 html 페이지를 보여줄 수 있는 방법이 2개 있습니다. 

 

첫째는 우리가 지금까지 계속 했던것처럼 서버에서 html을 만들어서 보내주는 것이고

이걸 서버사이드렌더링이라고 합니다. 

둘째는 서버에서 데이터만 AJAX로 가져와서 프론트엔드에서 자바스크립트로 html을 막 생성해주는겁니다.

이걸 클라이언트사이드 렌더링이라고 합니다.

 

원래 근본은 서버사이드렌더링인데 단점이 새로고침이 된다는거라 

 새로고침없이 좀 부드럽게 새로운 html을 보여주고 싶으면 클라이언트사이드 렌더링을 선택하면 되는겁니다.

 

아니면 페이지 일부만 클라이언트사이드 렌더링해도 상관없고 

아니면 아예 처음부터 모든걸 클라이언트사이드 렌더링하고 싶으면 React같은 라이브러리 쓰면 편리할 수 있습니다. 

요즘 사람들은 React 안쓰면 개발 못하는 줄 알고 있는데

클라이언트사이드 렌더링하는 부드러운 사이트 만들고 싶을 때 쓰면 됩니다. 

 

 

 

 

 

AJAX 에러처리 

 

서버가 다운되었거나 인터넷이 끊겼거나 

서버가 이상한 에러코드를 보내거나 그런 경우 AJAX가 실패할 수 있는데 

그럴 때 코드를 실행하고 싶으면 

 

 

fetch('/URL')
.then((r)=>{
  if(r.status == 200) {
    return r.json()
  } else {
    //서버가 에러코드전송시 실행할코드
  }
})
.then((result)=>{ 
  //성공시 실행할코드
}).catch((error)=>{
  //인터넷문제 등으로 실패시 실행할코드
  console.log(error)
})

이렇게 길게 코드짜면 됩니다. 

진짜인지 확인하고 싶으면 서버에서 status code를 200말고 다른거 한번 보내봅시다. 

 

근데 fetch 기본 문법이 코드가 매우 긴 편이라 불편한데 

그래서 AJAX 많이 쓰면 외부 라이브러리 설치해서 쓰는 분들이 많습니다.

axios 같은거 설치하면 더 짧게 쓸 수 있는데 

 

 

 

 

 

 

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> 

axios 라이브러리 js 설치파일같은거 구글에서 찾아와서 html 파일에 붙여넣어두면

 

 

 

axios.get('/URL').then((r)=>{ console.log(r) })

이렇게 GET요청 날리고 결과 출력도 가능합니다. 

then 2번쓰고 지랄할 필요없음 

서버에서 에러메세지같은거 보내거나 실패하는 경우는 .catch(()=>{  })  붙여서 에러체크도 가능합니다. 

 

 

 

 

 

 

간단한 애니메이션

 

지금보면 삭제버튼 누르면 DB에서 삭제는 잘 됩니다.

Q. 근데 굳이 새로고침까지 해야 글이 사라지는데 왜 그렇죠? 

 

이런 현상을 이해하고 싶으면 '프로그래밍 대 원칙' 하나가 있습니다.

뭔가 의도대로 동작하지 않으면 그건 컴퓨터 문제가 아니라 95% 확률로 여러분들이 코드를 안짜거나 못짠겁니다.

 

지금도 잘 생각해보시면 버튼누르면 DB에 있던 document 삭제요청하는 코드만 작성해놨죠? 

그래서 삭제누른다고 html 부분이 자동으로 사라지고 그런 기능은 없습니다. 

html도 자동으로 사라져야한다는건 여러분의 희망사항일 뿐이고

컴퓨터는 여러분 명령대로만 움직이지 얘가 알아서 뭘 자동으로 해주고 그런거 없습니다. 

 

 

삭제버튼눌러서 삭제완료되면 

- 새로고침을 강제로 하거나 

- 현재 버튼이 속해있는 글의 html을 안보이게 처리하라고 코드를 짜면 될 것 같군요. 

새로고침은 싫으니까 저는 그냥 html만 안보이게 처리만 해보겠습니다. 

 

 

 

document.querySelectorAll('.delete')[0].addEventListener('click', function(e){
  fetch('/delete?docid=' + e.target.dataset.id, {
    method : 'DELETE',
  })
  .then((r)=>r.text())
  .then((r)=>{
    e.target.parentElement.parentElement.style.display = ‘none’
  })
}) 

삭제요청 성공시 

"지금 누른 버튼의 부모의 부모 html을 찾아서 display : none을 주세요"

라고 코드짜봤습니다. 

 

부모의 부모를 안보이게 해야되는걸 어떻게 알았냐고요? 

불확실하면 실험해보면 되는거죠 뭐 

삭제버튼 누르고 한 번 잘 없어지나 시도해봅시다.

 

 

 

삭제기능 만들면서 새로 배운거 정리해보자면 

1. 새로고침없이 서버로 요청날리고 싶으면 AJAX 사용 

2. AJAX로 데이터도 가져올 수 있다보니까 클라이언트사이드 렌더링이라는것도 가능하구요 

3. dataset 문법 이용하면 html에 몰래 데이터 숨겨놓을 수도 있습니다.