Node.js + MongoDB AJAX로 데이터 삭제하기(Jquery)[fadeOut( )]
전체코드
// js파일
const express = require('express');
const app = express();
app.use(express.urlencoded({extended: true}))
// post요청을 처리하고싶다면 bodyparser라이브러리가 필수이다.
const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb+srv://admin:qwer123@cluster0.4tfxe6p.mongodb.net/?retryWrites=true&w=majority'
app.set('view engine','ejs');
var db;
MongoClient.connect(url,{useUnifiedTopology:true} ,function(err, client){
if (err) return console.log(err);
db = client.db('todoapp')
app.listen('3000', function(){
console.log('listening on 3000')
});
})
// 리스트 가져오는 코드
app.get('/list',(req,res)=>{
db.collection('post').find().toArray((err,result)=>{
console.log(res);
res.render('list.ejs',{posts:result})
});
})
// 삭제 코드
app.delete('/delete',(req,res)=>{
req.body._id = parseInt(req.body._id)
db.collection('post').deleteOne(req.body,(err,result)=>{
console.log('삭제완료');
res.status(200).send({message : '성공했습니다'})
})
})
// html
<body>
<ul class="list-group">
<% for (var i =0; i< posts.length ; i++) {%>
<li class="list-group-item">
<p>글번호 : <%=posts[i]._id%></p>
<h3>할일 제목 : <%=posts[i].제목%></h3>
<h3>할일 제목 : <%=posts[i].날짜%></h3>
<button class="delete" data-id="<%=posts[i]._id%>" >삭제</button>
</li>
<% } %>
</ul>
</body>
// html > script
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script>
$('.delete').click((e)=>{
console.log($(this))
$.ajax({
method : 'DELETE',
url: '/delete',
data:{_id :e.target.dataset.id}
}).done(()=>{
console.log('성공했어요');
$(e.target).parent('li').fadeOut()
}).fail(()=>{
})
})
</script>
AJAX를 써서 삭제요청을 해보도록 합시다.
요청은 4개 종류가 있다고 했습니다. GET POST PUT DELETE
하지만 HTML 폼에서 일반적으로 PUT DELETE 요청을 할 수는 없습니다.
<form method="DELETE">
<button>버튼</button>
</form>
이런거 안됩니다. 일반적인 HTML 폼 안에선 GET, POST 요청밖에 못날립니다.
(HTML이라는 언어를 만들 때 잘못 만든 겁니다)
그래서 삭제요청을 할 때 쓸 수 있는 3가지 방법이 있습니다.
1. method-override 라이브러리의 도움을 받는다
2. AJAX로 DELETE 요청을 날린다
3. 그냥 POST요청을 날려서 DELETE 작업을 수행한다
우린 2번 방법으로 해보도록 합시다.
실은 3번이 가장 편합니다. POST로 삭제하라고 요청해도 전혀 상관 없습니다.
근데 REST한 API를 만들기 위해 1번과 2번을 쓰는 것일 뿐입니다.
AJAX 는 무엇인가
실은 별거 아닌데 프론트엔드에서 JavaScript를 이용해 서버에 여러가지 요청을 할 수 있는 문법 같은 것입니다.
장점은 새로고침 없이도 서버에 몰래몰래 요청을 할 수 있습니다.
그래서 새로고침이 없는 스무스한 사이트를 만들고 싶으면 많은 요청을 AJAX 문법을 이용해 처리하게 됩니다.
그래서 이걸 써서 개발하면 삭제 버튼을 누르는 순간 새로고침 없이도 글 삭제가 가능합니다.
AJAX를 쓰기 위한 jQuery 설치
쌩 자바스크립트로도 AJAX가 가능하지만 코드가 길어서 정신건강을 위해 jQuery를 설치합니다.
실은 근데 이미 설치가 되어있습니다.
여러분 list.ejs 파일에 Bootstrap이 정상적으로 설치가 잘 되었다면
이런 코드를 발견하실 수 있습니다. (위전체코드 에있는 호스팅코드를 가져가셔도 됩니다.)
(list.ejs 하단)
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="어쩌구" crossorigin="어쩌구" ></script>
이 코드가 바로 jQuery를 CDN 방식으로 설치하는 부분이라고 보시면 되겠습니다.
(CDN은 다른 사이트에서 호스팅해주는 jquery 파일을 내 HTML에 적용해주세요~ 라는 뜻입니다.)
하지만 그냥 쓸 순 없습니다. jQuery Slim 버전인데 이걸가지고 AJAX요청이 불가능합니다.
빨리 이 부분을 이렇게 수정합니다.
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
1. slim이라는 글자를 지우고 2. 뒤에 있는 이상한 긴 글자들을 제거합니다.
이렇게 써주셔야 정상적으로 AJAX 기능이 들어있는 jQuery 라이브러리를 설치할 수 있습니다.
이제 이거 하단에 코드 이렇게 짤겁니다.
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script>
서버에 AJAX DELETE요청 날려주세요~
</script>
알겠쥬?
(주의) jQuery 설치 파일보다 하단에 script 태그를 열어서 작성하셔야 잘 작동됩니다.
이거 안지키고 에러난다고 그러면 답안해줌
AJAX 기본 문법
이렇게 치십시오.
(list.ejs 하단)
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script>
$.ajax({
method : 'POST',
url : '/add',
data : '결혼하기'
})
</script>
지금 뭘 쓴거냐면.. /add 경로로 POST 요청을 하는데 요청과 함께 '결혼하기' 라는 데이터를 보내주세요~ 라는 것입니다.
그럼 서버는 요청을 처리해줍니다. 이게 AJAX 기본 문법 끝입니다.
이 코드는 언제 실행되냐면.. script 태그 안에 그대로 넣으시면 list.ejs 페이지를 방문할 때 마다 바로 실행됩니다.
근데 새로고침 현상이 없으니 요청이 성공한지 실패한지 전혀 모르죠?
그래서 완성형 문법을 공개합니다. 이렇게 치십시오.
(list.ejs 하단)
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script>
$.ajax({
method : 'DELETE',
url : '/delete',
data : '1번게시물'
}).done(function(결과){
AJAX 성공시 실행할 코드는 여기
}).fail(function(에러){
실패시 실행할 코드는 여기
});
</script>
/add 경로로 POST 요청을 하는데 요청과 함께 '1번게시물' 이라는 데이터를 보내주세요~ 라고 썼습니다.
그리고 뒤에 .done .fail 이런 함수들을 붙여서 쓰실 수 있는데
각각 설명은 위에 한글로 잘 나와있군요.
AJAX가 필요할 때마다 이거 그대로 복붙해서 쓰시면 됩니다.
그럼 서버는 이런 코드를 작성하면 삭제기능이 완성되지 않을까여
(server.js)
app.delete('/delete', function(요청, 응답){
DB에서 글 삭제해주쇼
응답.send('삭제완료')
});
어떤 사람이 /delete경로로 DELETE 요청을 하면 ~~해주세요 라고 코드를 짰습니다.
안에는 어떤 내용이 들어가야할까요?
아마 "DB에서 글삭제해주세요~"가 아닐까여?
게시물 번호를 보내기
AJAX로 DELETE 요청시 가장 중요한 내용이 있습니다.
여러분이 원하는 게시물을 삭제하고 싶으면 AJAX요청을 할 때
삭제 원하는 게시물 번호를 보내십시오.
(게시물 번호는 우리가 예전에 _id : 1 뭐 이런식으로 DB에 저장해놨응게 그걸 보내십쇼)
(list.ejs)
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script>
$.ajax({
method : 'DELETE',
url : '/delete',
data : { _id : 1 }
}).done(function(결과){
AJAX 성공시 실행할 코드는 여기
})
</script>
data : {} 부분에 삭제를 원하는 게시물 아이디를 적어 보내면 좋을 것 같습니다.
지금은 테스트해야하니 대충 _id : 1이라고 적어봅시다.
그럼 서버는 어떤 코드를 짜야 게시물이 삭제될까
이런 코드가 아닐까요. 빨리칩시다.
(server.js)
app.delete('/delete', function(요청, 응답){
db.collection('post').deleteOne(요청.body, function(에러, 결과){
console.log('삭제완료')
})
응답.send('삭제완료')
});
deleteOne 함수를 쓰면 원하는 데이터를 삭제 가능합니다.
deleteOne(삭제원하는 데이터이름, function(){} ) 이렇게 쓰시면 됩니다.
그리고 AJAX요청시 data : { _id : 1 } 이라고 적은 정보는 요청.body라는 곳에 담겨옵니다.
그래서 그 정보를 그대로 deleteOne에 집어넣으면 { _id : 1 }이라는 게시물을 삭제할 수 있겠죠?
근데 삭제가 안됩니다요
(원래 잘 안될 때 deleteOne 콜백함수 내 에러나 결과라는 파라미터를 출력해보시면 됩니다.)
삭제가 안되는 이유는 당연히 deleteOne 내에 잘못된 데이터를 집어넣어서가 아닐까요?
대충 출력해보니 DB에 저장된 { _id : 1 } 이라는 아이디는 숫자고
AJAX로 보낸 데이터를 (요청.body) 출력해보면 { _id : '1' } 이렇게 문자입니다.
Q. 전 분명 AJAX코드에서 { _id : 1 }를 보냈는데 왜 바뀌어있죠?
AJAX요청 등으로 데이터를 서버에 전송할 때 숫자 자료들이 가끔 문자화 되는 경우가 있습니다.
그래서 사기행위에 당황하지 마시고 이걸 다시 숫자로 바꿔주셔야합니다.
(server.js)
app.delete('/delete', function(요청, 응답){
요청.body._id = parseInt(요청.body._id)
db.collection('post').deleteOne(요청.body, function(에러, 결과){
console.log('삭제완료')
})
응답.send('삭제완료')
});
parseInt라는 함수는 '1' 이런걸 정수 1로 바꿔주는 고마운 함수입니다.
그리고 실행해보시면 (/list 페이지를 새로고침해서 AJAX 요청해보시면)
{_id : 1} 인 데이터가 삭제가 되어있음을 확인할 수 있습니다.
버튼을 눌렀을 때만 AJAX 요청을 해보도록 합시다.
지금은 /list 페이지를 로드할 때마다 AJAX 요청이 실행됩니다.
<script>태그 안에 담은 코드들은 페이지 로드할 때마다 실행되니깐요.
코드를 이렇게 고치면..
(list.ejs)
<ul class="list-group">
<% for (var i = 0; i < posts.length; i++){ %>
<li class="list-group-item">
<h4> 할일 제목 : <%= posts[i].제목 %> </h4>
<p> 할일 마감날짜 : <%= posts[i].날짜 %> </p>
<button class="delete">삭제</button>
</li>
<% } %>
</ul>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script>
$('.delete').click(function(){
$.ajax({
method : 'DELETE',
url : '/delete',
data : { _id : 1 }
}).done(function(결과){
//AJAX 성공시 실행할 코드는 여기
})
});
</script>
1. <button>태그에 class를 추가했고
2. $.ajax 코드를 이상한 코드로 감쌌습니다.
이상한 코드의 뜻은.. delete라는 클래스명을 가진 요소를 클릭하면 내부 $.ajax(~) 코드를 실행해주세요~ 입니다. (jQuery 문법입니다.)
그래서 이제 delete라는 클래스명을 가진 버튼을 클릭할 때만 ajax를 실행시켜주네요!
근데 왜 항상 1번글만 삭제해주죠?
우리가 AJAX 요청할 때마다 1번만 삭제하기 코드를 짰으니까 그렇지 않을까요. (제탓은 아님)
그럼 코드를 수정해봅시다.
(list.ejs)
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script>
$('.delete').click(function(){
$.ajax({
method : 'DELETE',
url : '/delete',
data : { _id : 지금 클릭한 삭제버튼의 글번호 }
}).done(function(결과){
//AJAX 성공시 실행할 코드는 여기
})
});
</script>
{_id : 1} 이라고 하드코딩하는게 아니라 지금 누른 삭제버튼의 번호?를 집어넣으면 되겠군요.
그럼 버튼마다 미리 번호를 달아줘야하지 않을까요? 버튼에 자연적으로 번호가 생기진 않잖아요.
코드를 또 수정해봅시다.
<ul class="list-group">
<% for (var i = 0; i < posts.length; i++){ %>
<li class="list-group-item">
<h4> 할일 제목 : <%= posts[i].제목 %> </h4>
<p> 할일 마감날짜 : <%= posts[i].날짜 %> </p>
<button class="delete" data-id="<%= posts[i]._id %>">삭제</button>
</li>
<% } %>
</ul>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script>
$('.delete').click(function(){
$.ajax({
method : 'DELETE',
url : '/delete',
data : { _id : e.target.dataset.id }
}).done(function(결과){
//AJAX 성공시 실행할 코드는 여기
})
});
</script>
1. <button>태그에 data-id라는 속성을 추가했으며
2. data : { } 부분을 변경했습니다.
빠른 해설 들어갑니다.
1. HTML요소에는 data-id="1" 이런 식으로 몰래 정보를 슬쩍넣을 수 있습니다. 몰랐죠?
여기에 posts[i]._id 라는 게시글의 번호를 EJS문법을 이용해 집어넣었을 뿐입니다.
2. 그리고 자바스크립트로 버튼에 숨겨져있는 data-id="1" 이라는 정보를 가져올 수 있습니다.
버튼요소.dataset.id 이런식입니다.
3. e.target 이라는 코드는 현재 지금 클릭한 요소를 뜻합니다.
그래서 e.target.dataset.id는 무슨 뜻일까여?
지금 클릭한 요소의 data-id 속성값을 가져오세요~ 라고 쓴 것입니다.
2번글 버튼을 누르면 data-id에 2가 박혀있으니까
e.target.dataset.id는 샤라락 2로 변하겠군요.
그래서
2번글 버튼을 누르면 data : { _id : 2 } 라고 채워지게되는 것입니다.
3번글 버튼을 누르면 data : { _id : 3 } 이라고 채워지게되는 것입니다.
이제 아무글 삭제버튼을 누르면 거기에맞는 번호의 게시물을 삭제해줍니다.
끝입니다.
고객 요청에 응답하는 방법
app.get('/어쩌구', function(요청, 응답){
응답.send('<p>some html</p>')
응답.status(404).send('Sorry, we cannot find that!')
응답.sendFile('/uploads/logo.png')
응답.render('list.ejs', { ejs에 보낼 데이터 })
응답.json(제이슨데이터)
});
send는 간단한 문자나 HTML을 보낼 수 있습니다.
status는 응답코드를 보낼 수 있습니다.
sendFile은 static 파일들을 보낼 수 있습니다.
render는 ejs등의 템플릿이 적용된 페이지들을 렌더링해줄 수 있습니다.
json은 제이슨 데이터를 담아보낼 수 있습니다.
응답코드를 이용해 요청이 성공했는지, 실패했는지 판정해줄 수 있습니다
대충 서버에러일 경우엔 500, 이상한 요청일 경우 400 이렇게 대충 적어보내셔도 무방하지만
정확히 적으시면 테스트할 때 어떤 문제인지 약간 더 쉽게 파악가능합니다.
아무튼 그래서 우리도 server.js에서 DELETE요청시 성공/실패 코드를 보내도록 합시다.
AJAX 요청 성공 또는 실패시 특정 코드 실행하기
당연히 데이터가 오거나 200 코드가 오면 성공이고, 데이터가 안오거나 400, 500 코드가 오면 실패라고 봅니다.
성공이나 실패시 각각 기능을 실행하려면 done 혹은 fail 함수를 이용하시면 되겠습니다.
(list.ejs)
<script>
$('.delete').click(function(){
$.ajax({
method : 'DELETE',
url : '/delete',
data : { _id : e.target.dataset.id }
}).done((결과)=>{
//AJAX 성공시 실행할 코드적기
}).fail((xhr,code,err)=>{
//AJAX 실패시 실행할 코드적기
});
});
</script>
AJAX 성공시 done 함수 내부의 코드를 실행하고
실패시 fail 함수 내부의 코드를 실행할 것입니다.
(function 대신 => 이거 써도 똑같은 함수입니다)
참고로 fail 함수 내부의 3개의 파라미터는 차례로 jQuery의 XMLHttpRequest(몰라도됨), 에러코드, 에러메세지입니다.
done 함수의 1개 파라미터는 요청시 받아온 결과가 담겨있습니다. (파일, 데이터 등)
그럼 성공이나 실패 각각 경우에 따라서 안내문을 띄워준다든지 그런 UI개발을 할 수 있겠군요?
삭제요청 성공시 해당 게시물 <li>를 지워보자
삭제버튼을 누르면 HTML화면은 변동이 없고 DB데이터만 지우고 있습니다.
(AJAX는 새로고침 없이 몰래 요청을 처리해주니깐요)
그럼 유저들이 삭제 된지 안된건지 모르겠죠?
그러니 삭제 AJAX 성공시 해당 게시물의 <li>태그를 지워보도록 합시다.
Q. 왜 <li> 태그를 지우나고요?
(list.ejs)
<ul class="list-group">
<% for (var i = 0; i < posts.length; i++){ %>
<li class="list-group-item">
<h4> 할일 제목 : <%= posts[i].제목 %> </h4>
<p> 할일 마감날짜 : <%= posts[i].날짜 %> </p>
<button class="delete">삭제</button>
</li>
<% } %>
</ul>
이 부분이 여러분 작성했던 게시물 리스트 HTML 입니다.
간단히 해석하자면 여러분 게시물은
<ul>
<li> 제목, 날짜, 삭제버튼 </li>
<li> 제목, 날짜, 삭제버튼 </li>
<li> 제목, 날짜, 삭제버튼 </li>
</ul>
이렇게 HTML로 이루어져있으니까요.
그래서 삭제버튼을 감싸고 있는 li 태그를 지우면 해당 게시물행이 하나가 깔끔하게 삭제될 것 같습니다.
그럼 코드를 수정해봅시다.
(list.ejs)
$('.delete').click((e)=>{
console.log($(this))
$.ajax({
method : 'DELETE',
url: '/delete',
data:{_id :e.target.dataset.id}
}).done(()=>{
console.log('성공했어요');
$(e.target).parent('li').fadeOut()
}).fail(()=>{
})
})
</script>
기존에 있던 done 함수 안에 한줄을 추가했습니다.
1. $(this) 라는 단어는 현재 클릭이벤트가 동작하는 곳입니다.(하지만 작자는 this가 안먹혀 e.target로 수정하였다)
하지만 e.target이나 this 같은 키워드 뒤에 jQuery 함수를 붙이고 싶다면 $() 안에 싸매셔야합니다.
2. parent('li')는 부모 HTML 중 li태그를 찾아주세요~
3. fadeOut()은 사라지게 해주세요~
4. 근데 done() 안에 function(){} 말고 ()=>{} 이런 함수로 바꿔봤습니다. 그래야 this값의 변형없이 잘됩니다.
입니다.
이제 버튼을 눌러 삭제하면 페이지새로고침 없이 데이터가 서서히 사라지는 효과가 보일것이다.