본문 바로가기

프로젝트/인스타그램 클론코딩

[인스타그램 클론코딩] 채팅기능 추가

expo에서 제공하는 Push Notification 기능과 Context를 사용하여 1:1 채팅 기능을 추가해 봤습니다.

firebase도 큰 차이는 없는 것 같습니다. 나중에 react-native로 작업할 때는 firebase를 사용해 봐야 겠습니다.

 

 

실시간 채팅 기능을 추가하면서 작성한 대략적인 설계도입니다.

데이터 흐름 UI 구조

데이터 흐름

  • 채팅 기능을 추가하기 위해서 데이터베이스에 Room, Message 테이블을 추가했습니다.
  • Room에는 participants(참가자), subscribers(구독자), messages 스키마가 정의되어 있는데요.
  • A가 B에게 처음 메세지를 전송하면 서버에서 Room을 생성하면서, participants와 subscribers 모두에 A와 B를 연결합니다.
  • Push Notification은 participants에 포함된 User 중에서 나를 제외한 사용자가 subscribers에도 포함되어 있을 경우에만 수행합니다.
  • 채팅창의 벨을 터치해서 알람을 끄거나 켤 수 있는데, 이때 Room의 subscribers에 자신의 id를 추가(연결)하거나 삭제(연결 해제)합니다.

UI 구조

  • PushNotificationContext 컴포넌트가 Expo 서버에서 Push Notification을 수신합니다.
  • PushNotificationContext 컴포넌트는 앱에서 거의 최상위 컴포넌트이면서 Context이기 때문에, 그 아래 있는 모든 컴포넌트에서 Context를 참조할 수 있습니다.
    참조: [인스타그램 클론코딩] 웹 로그인 구조
  • Context는 3개가 사용됐습니다. expoNotification은 가장 최근의 메세지 내용을 담고 있습니다. uncheckedRooms에는 메세지가 연결된 Room의 ID가 중복없이 포함된 배열입니다. newMessagesExist는 uncheckedRooms에 한 개 이상의 Room ID가 포함되어 있을 경우에 true가 됩니다.
  • newMessageExist를 사용해서 새로운 메세지가 전송되면, Home 화면의 비행기 좌측 상단에 빨간점을 표시할 수 있습니다.
  • uncheckedRooms를 사용해서 채팅 리스트에서 메세지가 수신된 채팅에만 좌측 상단에 빨간점을 표시할 수 있습니다.
  • expoNotification을 사용해서 채팅창에서 새로운 메세지를 추가할 수 있습니다. (실시간 채팅)

한계점

  • 실시간 채팅을 위해서 Push NotificationContext(expoNotification)를 사용했습니다. 그런데 채팅창 우측 상단에 있는 벨 아이콘을 사용해서 알람을 끄면, 서버에서 Push Notification을 요청하지 않기 때문에 메세지가 전송되지 않습니다.
  • 위의 동영상 1분 3초 정도에 알람이 꺼진 상태에서 채팅창의 내용이 업데이트되지 않았다가 채팅창을 나갔다가 들어오고 나서야 업데이트된 것을 확인할 수 있습니다.
  • 앱이 켜진 상태(foreground)에서도 알림창에 메세지가 나타납니다.

그렇다면 왜 이렇게 구성했나?

  • Expo Push Notification 시스템에서는 클라이언트측에서 Push Notification을 수신하거나 수신하지 않을 수만 있지 필터링할 수 있는 기능은 없습니다.
  • 그래서 굳이 Room에 participants 외에 subscribers를 추가하여 서버측에서 필터링을 할 수 있도록 했습니다.
  • Apollo(GraphQL) 서버는 기본적으로 구독(Subscription) 기능을 포함하고 있지만, 전 AWS 람다를 사용해서 서버리스 상태이기 때문에 구독 기능을 사용하려면 몇 가지 추가 설정이 필요한 것으로 알고 있습니다.
  • Expo 37 버전 기준으로 수신된 Push Notification을 어떻게 표시할 지 설정할 API가 없습니다. 

가능성

  • 불과 며칠 전에 Expo 38 버전이 추가되었는데요. 앱이 켜진 상태에서 Push Notification 표시 방법을 설정할 수 있게 되었습니다.
    React Native 환경에서는 Firebase를 사용하면 더 세부적인 조정을 할 수도 있습니다.
  • 실제 서비스에 실시간 채팅 기능을 추가할 때는 Context를 사용하지 않을 것입니다. 아마 MQTT를 사용할 것 같습니다.
  • MQTT를 사용하면 사용자가 알람을 껐을 때 실시간 채팅에서 메세지가 업데이트되지 않는 문제점을 해결할 수 있을 것 같습니다. 2년 전쯤에 AWS IoT 서비스를 이용해서 MQTT 통신을 해본 경험이 있는데요. 서버와 클라이언트를 모두 구현해 본 것은 아니기 때문에 아직 확신할 수는 없습니다.
  • 실제로 실시간 채팅 서비스를 구현한다면, 사용자가 채팅창에 들어갔을 때 MQTT 브로커(서버)에서 특정 토픽(예를들면 "송신자/수신자")을 구독하고, Push Notification과 Context는 백그라운드 상태와 Home 화면과 채팅 리스트에서 빨간점을 표시하기 위한 용도로만 사용할 것입니다. 하지만 MQTT를 추가하려면 새로운 서버를 만들어야 하기 때문에 시간적 여유가 되지 않아 이번엔 그냥 Context를 사용했습니다.
    참조: https://joondong.tistory.com/66
  • 참고로 NodeJS용 MQTT 서버인 Mosca라는 MQTT 라이브러리가 있습니다.
  • 그리고 Mosca를 사용하면 인증기능까지 추가할 수 있는데요. 인스타그램 클론 백앤드에서 사용자를 인증하는 방법과 동일한 방법으로 인증할 것입니다.
    참조:  [인스타그램 클론코딩] API 인증 구조

실제 구현 과정은 다음 포스트에서 설명하도록 하겠습니다.

 

github에 커밋하기 전에 포맷을 하게되어 소스코드가 날라갔습니다.

지금 진행중인 프로젝트에 추가할 계획이 있으니 기회가 되면 그때 자세히 설명하도록 하겠습니다. 

그땐 FCM과 카산드라 DB(메세지 데이터), MySQL(채팅방, 사용자 데이터)과 연계해서 제대로 만들어볼 생각입니다.