초보 개발자

[ Node.js, Vue.js, Socket.io ] 카톡같은 채팅 만들기 2 본문

[ Node.js Vue.js Socket.io ] 채팅

[ Node.js, Vue.js, Socket.io ] 카톡같은 채팅 만들기 2

taehyeki 2022. 5. 21. 21:27

소켓의 특성상 화면이 새로고침되거나 다른 페이지로 이동하면 socket연결이 끊긴다.

 

따라서 나는 효율적으로 만들기 위하여 채팅방에 입장하면 소켓을 만들어 주는 방식을 사용하려고 하였다.

하지만 방에 들어갈 때마다( 특정 url ) 소켓이 계속 생성이 되었고, 방에 나가거나 해도( 다른 url로 이동 ) 소켓이 사라지지 않는 현상이 발생하였다.

 

이로인해 한번 메시지를 보내면 여러개가 전송이 되는 현상이 발생하였다.

 

짐작하건데 vue의 SPA특성상 하나의 페이지 안에서 모든 것이 이루어지기 때문에 어디로 실제로 어디로 이동이 된다는 개념이아니고 하나의 페이지 안에서 다른 화면을 보여주는 것이기 때문에 소켓 연결이 끊어지지 않은 것 같다.

 

따라서 나는 특정 채팅방에 입장할 때 소켓을 생성하는 방법이 아니라,

App.vue가 생성이 될 때 소켓을 만들어 주는 방식을 사용하려고 한다.

vuex를 사용하여 생성된 소켓을 store에 저장하려고 했지만 순환오류??가 계속 떠서 vuex를 사용하지 못했다.

어떻게 할지 고민하던 중 plugin을 사용하여 전역적으로 사용해보려고 한다.

 

plugin을 만드는 방법은 아래에 설명해 두었다.

2022.05.18 - [vue.js/vue 잡기술] - vue 플러그인 생성

 

vue 플러그인 생성

플러그인은 여러 컴포넌트에서 사용되는(특히 전역) 기능을 만들 때 사용한다. Vue.use(Vuex); Vue.use(VueRouter); Vue.use(Vuex)를 호출하면, Vuex에 정의되어있는 install()이라는 메서드가 호출된다. 이 사실..

taehyeki.tistory.com

 

소켓 플러그인

 

소켓 생성

먼저 클라이언트 측 소켓을 생성해주었다. VUE_APP_CHAT_API라는 환경변수를 사용하여 채팅서버의 주소를 적어주었고, 몇가지 옵션을 주었다. 저기서 withCredentials옵션을 주지 않았을 때에는 오류가 발생하였다.

 

이제부터 소켓에 이벤트를 주는 플러그인 함수를 여러개 설명하려고 한다.

 

1. Vue.prototype.$socket 

 

위에서 만든 소켓을 전역적으로 사용할 수 있도록 설정을 해주었다.

이로써 vue파일에서 this.$socket을 사용하면 해당 소켓을 사용할 수 있게 되었다.

 

 

2. Vue.prototype.$socket.on('connect',cb)

소켓이 연결되었을 때 실행되는 콜백함수를 설정해주었다.

vuex의 chat모듈의 state중 sockets라는(default 0)값을 하나씩 증가시켜주었다.

 

이 이유는 나중에 서버측의 문제로 인해 ( 혹은 다른 이유 ), 클라이언트와 연결이 끊긴 경우에 클라이언트측 소켓은 계속 재연결하려고 할 것이다. 신기하게 알아서 재연결을 시켜준다. 하지만 재연결이 되도 재연결되었다고 감지를 못하기때문에 감지할 수 있도록 하나의 속성을 만들어 그 값이 변화하는 것을 감지하는 역할을 하도록 만든 것이다.

 

3.Vue.prototype.$enterRoom

 소켓으로 특정 방에 입장하는 역할의 함수이다. 서버측 소켓으로 joinRoom이벤트를 보낸다.

 

4. Vue.prototype.$chatSet

chat이라는 이벤트의 리스너를 만들어 주었다. chat이라는 이벤트가 발생하면, fn이라는 함수를 콜백한다.

 

5. Vue.prototype.$leaveRoom 

leaveRoom이라는 이벤트를 서버에 보내는 역할을 한다. 이로인해서 소켓이 해당방에서 나오게 된다.

 

 

6. Vue.prototype.$disconnectFromServer

클라이언트측 소켓이 연결이 끊어졌을 때 발생하는 함수이다.

 

7. Vue.prototype.$roload

상대방이 채팅방에 들어오거나 하였을 때 서버에서 클라이언트 소켓측으로 reload이벤트를 보내는데,

이를 감지하고 이에따른 콜백함수를 실행시킨다.

 

8. Vue.prototype.$reloadChatRooms

reload와 마찬가지이지만 이건 채팅방목록에 있을 때 상대방이 메시지를 보내면 실시간으로 메시지를 받아오는 역할을 한다.

 

9. Vue.prototype.$deleteChat

메시지를 삭제하였을 때 삭제되었다고 바뀌어야할 것이다. 그 역할을 담당해준다.

 

10. Vue.prototype.$deleteChatReloadRoomList

$deleteChat과 마찬가지이다. 이것 역시 상대방이 메시지를 지우면 발생하는데, 내가 채팅방 목록에 있다면 발생한다.

상대방이 보낸 마지막 메시지를 지우면 나에게도 마지막 메시지가 삭제된 메시지입니다. 라고 보여져야 할 것이기 때문이다.

 

11. Vue.prototype.$imOnApp

이건 상대방이 현재 접속해있는지를 확인하는 역할이다. 이에 따라 접속해있다면 채팅방목록에 초록불이 들어오도록 하였다.

 

플러그인 함수를 보면  함수를 만들어서 on , emit을 실행시키고 있다. 

소켓에서 함수를 만들어 params를 받아와서 그 받아온 값을 on, emit에 넣어주는 방식으로 만든 것이다.

 

라우터 구조

라우터 구조는 위와 같다. chat은 단순히 하위 라우터들을 보여주기 위한 깡통 같은 역할이다. chat/rooms로 들어가면 채팅 목록이 나오고, chat/123 으로 들어가면 123번방의 채팅을 볼 수 있도록 할 것이다.

 

채팅방 목록

 

채팅방 목록에서는 아래의 내용이 확인 가능하다.

  • 프로필사진
  • 상대방 닉네임
  • 마지막 메시지
  • 안 읽은 메시지 개수
  • 마지막 메시지의 시간
  • 상대방의 접속 유무

상대방이 채팅을 쳤을 때 실시간으로 채팅방 목록에서 내용을 확인할 수 있다.

 

먼저 created가 되면 아까 만들어 둔 여러 플러그인 함수를 사용한다.

chatRooms의 역할은 채팅방 목록을 불러오는 역할을 한다.

먼저 axios를 사용하여서 해당 user_id ( index )를 보내어 해당 유저가 접속해 있는 채팅방의 정보들을 받아온다. 그 받아온 정보는 chatRooms안에 들어있다. 그리고 여기서 

user id를 담아서 보낸다. 백에서는 받은 id를 가지고 db에 요청을 보내 프로시져를 실행시켜 해당 채팅 목록을 받아온다.

 

반환된 값을 가지고 파싱하여 chatRooms안에 넣고 mutaion을 통해 vuex에 저장해두었다.

이렇게 저장한 값을 getter로 가져와서 html에 뿌려주는 것이다.

처음에 getters는 아무값이 없어 아무 것도 보여주지 못할 것이다. 하지만 setChatRooms를 통하여  chatRooms을 설정해주면 getChatRooms가 바뀌어 화면이 재 렌더링이 될 것이다. 사실 이 부분 또한 마음에 들지 않는다. 왜냐하면

2번 렌더링이 되어버리기 때문이다. 이 걸 해결할 수 있는 방법을 찾아야 할 것 같다. ( getters로 html을 보여줄 때의 문제점 )

 

위에서 시간은 특별히 ChatRoomsTime이라는 함수를 사용해주었는데 이는 아래와 같다.

같은 날이라면 시간을 보여주고(오후 1:13분), 같은 날이 아니라면 날짜를 보여준다(6월 3일). 

 

채팅방 목록 실시간 업데이트

카톡처럼 채팅방 목록에 있을 때 상대방이 채팅을 보내면 실시간으로 바뀌어야 한다.

소켓에서는 같은 방에 있어야 이벤트를 주고 받을 수 있는 걸로 알고있다. 같은 방이 아니면 전체 밖에 안되지 않나 싶다. ( 확실하지 않음 )  따라서 특정 방에 특정 이벤트를 보내주어야 할 것 같았다.

 

 과연 이 두 유저의 접점을 어떻게 연결할까?

 

나는 채팅방 목록화면(왼쪽 화면)에 들어가면 1-1 처럼 -1(다시 1)을 붙여서 입장시키도록 하였다. 그리고 채팅 화면(오른쪽 화면)에서는 메시지를 보낼 때 아래와 같이 세팅을 하였다. 플래그를 설정하고 방에 1명이라면 ( 내가 만든 채팅은 1:1 전용이라 이렇게 했음 ) 플래그를 1로 설정하고 아래에서 플래그가 1일 경우에 reloadChatRooms 이벤트를 전송하였다.

뒤에서 한번 더 나옴

채팅방 목록화면이 생성될 때 created() 아래와 같이 설정해주었다

 

그 이후에 map으로 하나씩 돌리면서 방-1 에 입장시킨 뒤에 reloadChatRooms를 실행시켜주고, 그 안에 보낼 params는 다시 chatRooms를 불러오는 함수를 실행시키었다. ( chatRooms를 실행시키면 다시 채팅방 목록을 불러 올 것임 )

 

또 destroyed될 때도 비슷한 작업을 해주어야 한다. 이번엔 나가는 작업이다.

만약 서버와 연결이 끊기면 소켓에 설정해둔 이벤트 리스너들이 일부(어떤건 그대로 있고, 어떤건 없어짐.. 이유는 모름) 사라진다. 

 

따라서 다시 소켓과 연결이 되면 아래와 같이 한번 더 created에서 했던 작업을 해준다.

getSocket은 vuex에 있는 getters로 소켓이 변경될 때 마다 숫자를 하나씩 올린다.

이 sockets를 가져오는 getters이다. 이 걸로 인해서 소켓이 변경된 것을 캐치할 수 있도록 하였다.