메인 ReadMe.md를 참고 해주세요!
'짜여' 사이트가 궁금하시다면 아래 바로가기를 참고 해주세요!
이름 | 깃허브 주소 | 포지션 |
---|---|---|
이기곤 | https://github.com/LeeKiGont | 백엔드 |
서동현 | https://github.com/donghyeon23 | 백엔드 |
이재정 | https://github.com/jaejeonglee | 백엔드 |
이름 | 설명 |
---|---|
AWS EC2 | 서버 인스턴스 |
Node.js | JS 실행 환경 |
Express | nodeJS Framework |
mongoDB | 데이터 베이스 |
git | 버전 관리 |
nginx | Reverse Proxy |
name | Appliance | version |
---|---|---|
mongoose | 데이터베이스 ORM | 6.2.4 |
cors | cors 정책 설정 | 2.8.5 |
dotenv | 환경변수 설정 | 16.0.0 |
multer | 이미지 데이터 처리 | 1.4.4 |
multer-S3 | 사진 파일 업로드 | 2.10.0 |
aws-sdk | S3 접근 | 2.1085.0 |
socket.io | 웹소켓 라이브러리 | 4.4.1 |
jsonwebtoken | Json 포맷으로 사용자 속성을 저장 | 8.5.1 |
bcrypt | 비밀번호 해쉬화 | 5.0.1 |
passport | 소셜 로그인 구현 | 0.5.2 |
helmet | 서버 보안성 향상 | 5.0.2 |
uniqid | 고유값 생성 | 5.4.0 |
prettier | 코드 스타일 통일 | 2.5.1 |
채팅기능이 구현된 상태에서 유저가 웹에 들어와 있을 때 채팅방에 접속해있지 않아도 채팅이 왔는지 알 수 있어야 하고, 해당 유저의 게시글에 대한 타유저의 반응도 실시간으로 알려줄 수 있어야 한다고 생각.
-
새로운 알림 및 메세지를 확인 할수 있는 API를 만들어 주기적으로 요청에 대한 응답을 보내준다.
-
socket 이벤트가 발생할때마다 개별 유저에게 이벤트를 보내준다.
-
socket으로 이벤트 발생 시 알려준다.
새로운 알림 및 메세지를 확인 할수 있는 API를 만들어 주기적으로 요청에 대한 응답을 보내준다면 실시간 처럼 보일 수는 있겠지만, 이벤트가 발생하지 않은 상황에서 요청을 하게된다면 클라이언트와 서버에서는 불필요한 비용을 사용하게 될 것이다. 그래서 socket을 이용하여 실시간 알림을 선택하게 되었다.
// user A 와 B의 snsId 를 받아 고유 RoomName 생성시켜줌
const roomNameCreator = (snsIdA, snsIdB) => {
const roomName = [snsIdA, snsId];
arr.sort((a, b) => a - b);
let createdRoomName= roomName[0] + roomName[1];
return createdRoomName;
};
// user 가 채팅을 보냈을때 DB에 저장후 해당 Room에 다시 뿌려준다.
socket.on('room', async ({ fromSnsId, toSnsId, chatText, createdAt }) => {
...
const roomName = await roomNameCreator(fromSnsId, toSnsId)
io.to(roomName).emit('chat', chatMessage);
...
추가적으로, 채팅방 접속 시 RoomName 을 생성해서 접속하는데, 유저가 해당 Room에 접속해있지 않은 경우 메세지를 실시간으로 받을수 없었다.
// 로그인 시 해당 유저 snsId 로 된 Room으로 보내줌
socket.on('login', async ({ fromSnsId }) => {
socket.join(fromSnsId);
const checkNew = await NoticeService.checkNewNotice({ snsId: fromSnsId})
io.to(fromSnsId).emit('checkNewNotice', checkNew)
});
// user 가 채팅을 보냈을때 DB에 저장후 해당 Room과 다른 User Room 에도 뿌려준다.
socket.on('room', async ({ fromSnsId, toSnsId, chatText, createdAt }) => {
...
const roomName = await roomNameCreator(fromSnsId, toSnsId)
io.to(roomName).emit('chat', chatMessage);
io.to(toSnsId).emit('chat', chatMessage);
...
유저가 로그인 시 본인의 ID로 된 Room 에 접속하게 하게 한 후 채팅 시 Room과 해당 유저 Room 에 동시에 이벤트를 송신하여 채팅방 접속 여부에 상관없이 실시간으로 데이터를 받을수 있도록 했다.
채팅방에 입장할 땐 기존의 Room 연결을 끊고, 고유 roomName로 사용하여 새로운 연결을 해주고 채팅방에서 나올 때는 이 과정을 반대로 해줌. 이렇게 해서 유저는 채팅방 입장 시 같은 roomName를 통해 채팅이 가능하고 채팅방을 빠져나와서도 각자의 소켓주소를 통해 실시간 알림을 받을 수 있는 조건이 마련됨.
위에서 마련된 조건을 통해 채팅메세지뿐만 아니라클라이언트에서 새로운 이벤트가 발생 시 해당 유저의 소켓으로 emit을 하도록 하고, 서버에선 on으로 이벤트를 받아 해당 유저의 알림창으로 이벤트의 발생을 알려줄 수 있게됨.
메인페이지에서 DB에 있는 “좋아요”, “북마크” 처럼 분야별로 표현하는 것이 아닌, 가장 인기가 많은 게시물을 노출시키기 위해 모든 분야의 개수를 다 더해준 새로운 값(totalcount)이 필요했다.
기존에 좋아요 개수, 북마크 개수를 표현할 때는 해당 게시글이나 댓글의 _id가 포함된 도큐먼트를 전부 count해서 mongoose기능인 VirtualField를 생성 후 populate로 해당 VirtualField를 response 했으므로 같은 방식으로 접근하여 totalcount를 정렬하려 했는데 sort는 DB에 있는 field 기준으로 정렬은 가능하지만 virtual로 정의된 스키마는 mongoDB에서 제공하는 query를 사용할 수 없기 때문에 사용이 불가능했다.
-
각각의 컬렉션에 새로운 필드를 만들어주어 그 필드에 count된 값을 넣어줄 생각을 했다.
-
mongoDB에서 제공하는 aggregate라는 기능을 사용하기.
-
aggregate 사용하기.
lookup으로 DB에 있는 값을 가져와서 addFields(mongoDB 4.2버전부터 추가)를 통해 totalcount를 생성하고 sort해주었다. aggregate는 virtual처럼 mongoose에서 처리해주는 것이 아니고 mongoDB에서 처리하므로 sort가 가능했다.
totalcount는 해당 로직 내에서만 존재하기 때문에 DB에 insert,update하는 행위를 줄인다는 장점과 사용하지 않는 필드를 불러오면서 발생하는 비용을 없앨 수 있다는 장점을 얻을 수 있었다.