[기술] 실시간 이벤트 스트리밍과 푸시 알림 구현

holyPigeon·2024년 2월 11일
0

기술

목록 보기
2/2
post-thumbnail

Intro

최근 원티드 프리온보딩 챌린지 사전미션을 진행하면서 인터파크와 같은 티켓 예매 시스템의 알림 서비스를 구현하게 되었다. 이전에 알림 서비스를 구현해본 적이 없어서 다소 쉽게 생각했었는데, 생각보다 고려해야할 것이 많다는 것을 알게되어 관련된 여러 기술에 대해 찾아보았다.

개요

알림 서비스란 무엇인가?

일단 관련 기술에 대해 알아보기 전에 알림 서비스가 무엇인지 정확하게 정의할 필요가 있다. 여기서 소개하는 알림 서비스란, 흔히들 알고있는 사용자가 구독한(관심 있는) 항목에 대해 알림을 보내주는 서비스이다. 인스타그램, 페이스북 등에서 좋아요를 받거나 메시지를 수신했을 때 날아오는 그 알림이라고 생각하면 된다.

문제점

언뜻 생각하면 그냥 이벤트가 발생할 때마다 사용자 알림 테이블에 알림 메시지를 하나씩 넣고 보여주면 될 것 같지만, 통상적인 서버-클라이언트 모델에서는 클라이언트의 요청 없이 서버가 응답을 할 수 없기 때문에 문제가 발생한다.

만약 알림이 도착했다 하더라도, 사용자 측에서 새로고침 등의 행위를 하지 않으면 서버는 알림을 보여줄 방법이 없는 것이다...!

모바일 환경에서는 새로고침 없이 알림을 받을 수 있는 것은 물론이고 앱을 실행하지 않은 백그라운드에서도 알림 수신이 가능했는데, 웹에서는 어떤 방법을 통해 기존 한계를 극복할 수 있는지 잘 생각이 나지 않아서 이에 대해 자세히 찾아보기로 했다.

실시간 이벤트 스트리밍 구현

설명에 사용된 이미지 및 개념들은 아래 링크들을 참조하였다.

어썸오님의 테코블 포스팅
아마란스님의 블로그 포스팅

폴링 (Polling)

먼저 폴링이라는 방법이 있다. 클라이언트가 일정 주기를 가지고 지속적으로 서버에 업데이트 사항이 있는지 물어보는 방법이다. 인스타그램으로 예를 들었을 때, 주기가 5초라고 치면 5초마다 “좋아요 있어?”, “댓글 있어?” 라고 물어보는 느낌이다.

하지만 이 방법에는 문제점이 있다.

바로 사용자가 유명 연예인일 경우, 실시간으로 수많은 DM을 받기 때문에 5초마다 정보를 갱신할 가치가 있지만, 방구석의 쓸쓸한 개발자일 경우, 5초에 한 번씩 정보 요청을 하는 게 무안할 정도로 연락올 일이 없기 때문에 서버 입장에서는 커다란 리소스 낭비가 발생한다는 점이다. 너 친구도 없으면서 왜 자꾸 DM 확인하냐

FAIL...

롱 폴링 (Long Polling)

두 번째로 롱 폴링이라는 방법이 있다. 롱 폴링의 경우 폴링과 비슷하지만, 계속해서 요청을 보내는 폴링과는 달리, 한 번 요청하고나서 서버의 응답이 올 때까지 대기하며 연결을 유지하는 방식이다.

계속해서 요청을 보내는 게 아닌, 한 번만 요청을 보내고 응답이 올 때까지 기다리는 방식이므로 일반 폴링에 비해서는 꽤 효율적인 방법이다. 하지만 결국에는 통신 주기와 사용자수가 증가할수록 서버 부담이 크게 증가하게 되기 때문에 한계점이 존재한다.

역시 FAIL...

웹 소켓 (Web Socket)

웹 소켓의 경우 최초의 HTTP 요청을 통해 접속한 이후, 연결을 유지하면서 양방향 통신을 진행하는 방식이다. 한 번 연결이 된 이후부터는 웹 소켓 포트에 접속해있는 모든 클라이언트들에게 이벤트 방식으로 응답할 수 있으므로, 매 응답/요청마다 커넥션을 생성할 수고가 사라진다.

다만, 게임이나 채팅처럼 양방향 통신이 활발한 서비스들과는 달리 알림은 대부분의 케이스에서 서버 → 클라이언트 방향으로만, 즉 단방향으로만 통신하기 때문에 리소스 낭비가 발생한다. 또한 웹 소켓은 HTTP와는 다른 별도의 프로토콜이므로, 사용에 필요한 부가적인 러닝커브가 존재한다.

FAIL...ㅠㅠ

SSE (Server-Sent-Event)

앞선 문제들을 총체적으로 해결한 것이 SSE라는 기술인데, 최초의 HTTP 요청을 통한 연결 이후, 오직 서버 측에서만 클라이언트로 데이터를 전송하며, 이로 인해 앞에서 언급된 문제점을 모두 해결할 수 있다.

따로 커넥션을 계속 생성하지 않더라도 한 번의 연결을 통해 서버로부터 지속적으로 데이터를 수신할 수 있으며, 쓸데없이 클라이언트 → 서버 방향 통신에 리소스를 낭비하지도 않는다.

오 드디어...?

그래서 그냥 SSE 쓰면 되는 거지?

이렇게 자료 조사를 하고보니 결국 웹 환경에서 알림 서비스를 구현하려면 SSE를 쓰면 되겠다는 생각이 들었다. 그런데 문득, 그래서 이 SSE를 사용하면 모바일에서의 푸시 알림과 같은 기능을 구현할 수 있는 건가? 라는 의문이 들었다.

그래서 유튜브에 SSE를 사용한 웹사이트 예제를 찾아보았는데, 자료를 찾으면 찾을수록 내가 생각하던 푸시 알림의 느낌이 아니었다. SSE에 사용된 Event-Streaming 이라는 개념은, 예시를 들자면 인스타그램 알림과 같은 푸시 알림보다는 실시간으로 작업현황에 따라 화면을 바꿔주는 Google Docs 같은 느낌이 강했다.

알고보니 SSE는 실시간 이벤트 스트리밍이라는 방식으로, 내가 생각했던 푸시 알림과는 목적과 효율 면에서 다소 거리가 있는 기술이었다.

처음에는 잘 몰라서 실시간 알림 서비스 라는 키워드로 조사를 진행했는데, 막상 내용을 까보니 결이 좀 다르다는 것을 알게되었고, 이후 푸시 알림 이라는 키워드에 초점을 두고 다시 구글링을 진행하면서 비로소 내가 원하던 정보를 얻을 수 있었다.

푸시 알림 구현

APNS와 FCM

APNS는 애플의 Apple Push Notification Service, FCM은 구글의 Firebase Cloud Messaging이며 각각 애플과 구글 진영의 푸시 알림 서비스이다. SSE와의 차이점이 있다면 이 둘은 지속적인 연결을 유지하지 않아도 되며, 백그라운드에서 동작한다는 점이다. 내가 구현하려 했던 알림에 딱 적합했다.

두 서비스 모두 앱 및 웹에서의 푸시 알림 구현이 가능했지만, 구글링해본 결과 웹에서의 푸시 구현은 FCM의 레퍼런스가 APNS에 비해 눈에 띄게 많았다. 따라서 FCM을 쓰는 것이 맞다고 생각했다.

사실 그냥 FCM을 쓰면 편한 게, 이미 FCM은 기존에 사파리를 제외한 모든 브라우저를 지원했으며 사파리 역시 IOS 16.4 이상의 버전부터는 FCM의 푸시를 허용하도록 바뀌었으므로 문제될 것이 없었다.

이메일

어플리케이션 내에서 알림을 전달하는 방법은 아니지만, 이메일을 이용해 정보를 전달하는 방법도 있다.

스프링 내에서는 "spring-boot-starter-mail" 라는 dependency를 제공하고 있으며, 일반적으로 이메일 전송 시에는 전송을 위한 제 3의 서버가 필요한데, 이 서버로는 주로 구글 SMTP를 많이 사용한다.

알림 중에는 단순 채팅이나 게시글 관련 알림이 아닌 계정 정보, 서비스 약관과 같은 중요한 알림들도 있을텐데, 이런 경우에는 인앱 알림과 이메일을 같이 사용하면 좋겠다는 생각이 들었다.

결론

알림 서비스 구현을 위해 위와 같이 많은 방법들을 살펴보았는데, 현 시점 가장 간편하면서 강력한 기능을 제공하는 것은 FCM인 것으로 결론지었다.

SSE는 모바일에서 사용하는데 상당한 제약이 많았고, APNS는 범용성에 문제가 있었는데, FCM같은 경우에는 플랫폼에 크게 구애받지 않는다는 점이 굉장히 매력적이었고, 항시 연결을 유지하지 않아도 백그라운드에서 기능을 수행할 수 있어 서버 부하를 크게 줄일 수 있다는 것이 강력한 장점이었다.

구글이라는 플랫폼에 알림 기능 자체를 완전히 의존한다는 것이 기분이 좋지는 않지만, 대부분의 상황에서는 이런 취약점을 앞선 장점들이 덮을 수 있다고 생각하기 때문에, 향후 있을 프로젝트에서는 FCM을 통해 푸시 알림 서비스를 구현해보려고 한다. 끗...!

profile
언젠가 전설이 될 남자... 피존입니다.

0개의 댓글