Node.js + MongoDB 채팅기능 만들기2 소켓x[EventSource](SSE)
이전글
https://daehyuntsory.tistory.com/93
Node.js + MongoDB 채팅기능 만들기1 소켓x(SSE)
채팅기능 구현전 우선 댓글에 대하여 알아보자 우리가 지금 구현할려는 채팅기능은 댓글기능과 굉장히 유사하다 밑의 글에서 함수중간에 있는 미들웨어 loginCheck 함수 없으시면 지우시면 됩니
daehyuntsory.tistory.com
메세지 발행을 해봅시다.
메세지 발행은 그냥 게시물 발행이랑 똑같습니다.
근데 댓글개념이기 때문에 부모 게시물도 기록해주면 끝이라고 했습니다.
- /chat 페이지에서 원하는 채팅방을 하나 누르고
- 메세지입력하고 전송버튼을 누르면 ▲ 이런 게시물이 DB에 저장되면 됩니다.
(부모 게시물은 채팅방 게시물을 뜻합니다)
채팅 메세지 저장하기
그냥 누가 전송버튼을 누르면 서버로 메세지를 전달하고
그걸 DB에 저장해두면 끝입니다.
(chat.ejs)
(위에 있던 html)
<input class="form-control" id="chat-input">
<button class="btn btn-secondary" id="send">전송</button>
<script>
$('#send').click(function(){
var getcontent = $('#chat-input').val() //1
$('#chat-input').val('')
var sendData = { // 2
parent : selId,
content : getcontent
}
//3
$.post('message', sendData).then(()=>{
console.log('전송성공')
}
)})
//4
var selId;
$('.list-group-item').click((event)=>{
$(event.currentTarget).css('background-color', '#eee');
selId = $(event.currentTarget).attr('data-id');
// 속성중 data-id의 값을 가져와 저장 (선택된 채팅방의 아이디)
$('.chat-content').html('')
if(eventSource != undefined){
eventSource.close();
}
})
</script>
1. 위에 있던 전송버튼을 누르면 서버로 보낼 데이터를 만들어줍니다.
2. 아마 지금 어떤 채팅방에서 메세지 썼는지, 내용이 뭔지 이런 거가 담기면 되겠군요.
3. 만든 데이터를 post 요청해서 보낸다고 써놨습니다.
근데 지금 누른 채팅방의 _id 는 채팅방을 불러올때 가져온 데이터로 가져온다
4. 왼쪽에 채팅방 리스트 클릭하면 / 배경 회색으로 이쁘게 주고 / 채팅방id를 찾아서 변수에 저장해놓는다고 써놨습니다.
그럼 채팅방 리스트 누르면 그 채팅방의 id가 변수에 저장됩니다.
그럼 이제 자유롭게 쓸 수 있겠군요.
서버는 그럼 데이터를 받았으면 DB에 저장하면 됩니다.
(server.js)
app.post('/message',loginCheck,(req,res)=>{
var 저장할거 ={
parent : req.body.parent,
content: req.body.content,
// parent와 content는 post로 받은 데이터
userid:req.user._id,
// 세션에 저장된 user의 id를 가져옴
date : new Date(),
// 내장 기능인 Date를 사용하여 오늘날짜 가져옴
}
db.collection('message').insertOne(저장할거)
.then((result)=>{
res.send(result)
})
})
1. 누군가 /message로 post 요청을 하면
2. 유저가 보낸 데이터 등을 활용해서 DB에 저장하고싶은 데이터를 귀엽게 만듭니다.
3. 그리고 DB에 집어넣습니다
서버에서 채팅메세지 가져오기
왼쪽에 있는 채팅방을 누르면 거기 있는 채팅메세지를 가져와봅시다.
그럼 당연히 DB에서 "parent : 지금선택한 채팅방 게시물 _id" 이거인 게시물을 다 찾아주세요 라고 하면 됩니다.
근데 메세지 변경사항을 계속 실시간으로 업데이트해줘야합니다.
그건 여러가지 방법으로 해결할 수 있는데
- 1초마다 서버에게 메세지 달라고 요청을 또 할 수도 있고(사용자가 많거나 방이 많다면 너무 비효율적이다.)
- 서버랑 유저간 지속적인 소통채널을 열면 됩니다.
둘째가 서버에 부담이 덜하니 둘째로 합시다.
서버가 유저에게 실시간으로 정보를 보내고 싶으면
원래 get, post같은 http 요청은 1회 요청하면 1회 응답하고 끝입니다.
하지만 1회 응답이 아니라 지속적으로 서버에서 응답을 하고싶은 경우가 있습니다.
근데 응답.send() 이러면 1회 응답하고 끝이기 때문에
1. Header 라는 정보의 connection 항목을 keep-alive로 설정해주고
2. 응답.write('안녕하세요') 이렇게 보내면 계속 유저에게 지속적으로 응답이 가능합니다.
그럼 이제 실시간으로 유저에게 계속 정보전달이 가능합니다.
Header가 뭐냐면
서버와 유저가 get, post http 요청으로 정보를 주고 받을 때 부가정보도 몰래 전달됩니다.
유저의 경우 사용하는 브라우저, OS, 쓰는 언어, 보유한 쿠키 등
이런 것을 get요청시 서버로 몰래 전달합니다.
반대로 서버도 응답시 유저에게 몰래 서버정보를 전달합니다.
이 정보를 보관하는 곳은 Header 라고 부릅니다.
유저 -> 서버 이렇게 전달되는 Header는 Request Header,(요청)
서버 -> 유저 이렇게 전달되는 Header는 Response Header(응답)라고 합니다.
Header가 어떻게 생겼는지 보고싶으면 크롬 개발자도구 Network 탭가면 됩니다.
그래서 누가 /message/어쩌구 로 요청을 하면 실시간 소통 채널을 열어봅시다.
(server.js)
app.get('/message/:parentid', 로그인했니, function(요청, 응답){
응답.writeHead(200, {
"Connection": "keep-alive", // 지속적인 응답 설정
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
});
응답.write('event: test\n');
응답.write('data: 안녕하세요\n\n');
});
1. 서버는 응답.writeHead() 어쩌구 저렇게 쓰면 지속적 소통채널 개설 완료입니다.
2. 유저에게 계속 메세지를 보내고 싶을 때마다 응답.write() 하면 됩니다.
3. 근데 event: 이벤트명을 잘 작성하고 data: 전달할내용을 쓰면 됩니다.
그리고 한줄 끝나면 \n (엔터키)를 잘 넣으면 됩니다.
그러면 간단한 문자 등을 바로바로 유저에게 전송가능합니다.
확인하고 싶으시면 응답.write('data: 안녕하세요\n\n'); 이거 여러개 써보십시오.
응답 몇번이고 해줍니다.
(chat.ejs)
var 지금누른채팅방id;
var eventSource; //일단변수
$('.list-group-item').click(function(){
지금누른채팅방id = this.dataset.id;
//프론트엔드에서 실시간 소통채널 여는법
eventSource = new EventSource('/message/' + 지금누른채팅방id);
eventSource.addEventListener('test', function (e){
console.log(e.data);
});
});
1. 유저는 GET요청 이런게 아니라 new EventSource('/message/' + 지금누른채팅방id);
이런 코드를 실행하면 서버에서 만들어놓은 실시간 채널에 입장가능합니다.
2. eventSource.addEventListener('서버에서작명한이벤트명') 이런 코드를 쓰면 서버가 보낸 데이터를 수신할 수 있습니다.
그럼 서버가 응답.write() 할 때마다 내부 코드를 실행해줍니다.
3. e.data 안에는 서버가 보낸 data : 어쩌구가 들어있습니다.
그럼 일단 실시간 채널 개설시 메세지들 가져오기
채널 개설시 DB에서 메세지들을 가져와서 보여줍시다.
(server.js)
app.get('/message/:parentid', 로그인했니, function(요청, 응답){
응답.writeHead(200, {
"Connection": "keep-alive",
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
});
db.collection('message').find({ parent: 요청.params.parentid }).toArray()
.then((결과)=>{
console.log(결과);
응답.write('event: test\n');
응답.write(`data: ${JSON.stringify(결과)}\n\n`);
});
});
1. 서버는 누군가 /message/어쩌구로 실시간 채널에 접속하면
2. DB에서 { parent : 어쩌구 } 를 가진 게시물을 다 찾아서 보내줍니다.
근데 실시간 채널이니까 응답.write 이용해서 보내줍니다.
3. 근데 찾은 자료는 [{ }, { } ... ] 이렇게 생겼습니다. 그걸 보내고 싶으면 JSON.stringify() 안에 넣어서 보내면 됩니다.
왜냐면 실시간 채널 이용할 때 문자만 전송가능하기 때문입니다.
JSON.stringify란?
let obj = {name: "John", age: 30, city: "New York"};
let myJSON = JSON.stringify(obj);
console.log(myJSON);
// 출력: {"name":"John","age":30,"city":"New York"}
{ }, [ ] 자료 내부에 전부 따옴표를 붙이고 싶으면 JSON.stringify() 함수 안에 넣어주시면 됩니다.
반대로 { }, [ ] 여기에 따옴표 붙였던걸 떼고 싶으면 JSON.parse() 함수 안에 넣어주시면 됩니다.
따옴표 왜 붙이냐고요?
붙이면 JSON이라는 자료가 되는데 일종의 문자자료 취급을 받습니다.
서버와 실시간 소통할 때는 { } [ ] 이런건 주고받을 수 없습니다.
문자만 주고받을 수 있습니다.
그래서 저렇게 따옴표 쳐서 JSON으로 만들어서 전송하기도 합니다.
근데 문제는 지금 DB에 메세지 document가 하나 추가되어도 실시간으로 그걸 감지하는 기능이 없습니다.
MongoDB 실시간 변동사항을 바로바로 알고싶을 땐 changeStream 기능을 쓸 수 있습니다.
쓰는 법은 다음 글에서 알아보자
다음글
https://daehyuntsory.tistory.com/95
Node.js + MongoDB 채팅기능 만들기3 소켓x[change stream(MongoDB)](SSE)
채팅방 누르면 서버에서 데이터를 보내주고 있습니다. 그 데이터를 실제 채팅메세지처럼 html로 보여주라고 했습니다. 그럼 자바스크립트로 "데이터 수신시 html 저기다가 만들어주세요" 라고 코
daehyuntsory.tistory.com