글 작성 타임어택: 10분
AWS SNS를 이용해 Push Notification(이하 '푸시 알림')구현을 시도했는데 생각보다 관리포인트도 많고 잘 납득되지 않는 부분도 있어 FCM을 직접 쓰는 것으로 진행했다. 무엇이 불편했는지 써본다.
SNS를 이용하려면 관련 Topic을 먼저 생성해야 한다. Topic으로 메시지가 들어오면 이메일이나 SMS, 기타 플랫폼 애플리케이션 엔드포인트로 트리거 할 수 있다. 나는 모바일 푸시 엔진으로 FCM을 선택했다. FCM에서 앱 생성 후 키를 받고 이를 SNS에 업로드하면 쉽게 연동된다.
문제는 SNS Topic을 각 모바일앱(클라이언트)가 구독하고 해지하는 과정이 너무 번거롭고 불편했다. SNS를 이용한 푸시 알림을 보내려면 각 모바일앱이 FCM으로부터 획득한 토큰을 이용해 엔드포인트를 생성해줘야 한다. 그러면 해당 토큰으로 엔드포인트가 생기고 ARN(Amazon Resource Name)이 부여된다. 이 엔드포인트를 SNS Topic에 구독을 시켜줘야 한다.
SNS 토픽에서 [Mobile] 메뉴 아래 [푸시 알림]을 눌러보면 나오는 플랫폼 엔드포인트 자체를 구독시킬 수 없기 때문에 그 아래 속한 (각 모바일의 토큰으로 생성한) 엔드포인트를 하나씩 구독해야 하는데, 만약에 엔드포인트가 유효하지 않아(앱을 삭제하거나 등등) 푸시가 실패했을 경우가 문제다.
별도의 실패 구독용 SNS Topic을 만들면 여기로 해당 엔드포인트 정보를 받을 수 있다. 여기까지는 좋다. 그러면 남은 작업은 기존에 구독중인 SNS토픽에서 해당 엔드포인트를 제거하고, 엔드포인트 자체도 삭제하면 된다. 이 작업은 앞서 생성한 실패용 SNS Topic을 Lambda에 트리거한 후 처리되도록 할 수 있다. 그런데 SNS Topic에서 해당 엔드포인트만 조회하는 방법이 없다. 해당 Topic의 구독을 전부 조회한다음에 하나씩 비교해서 제거해야 한다. 아무리 생각해도 다른 방법이 있을 것 같은데(이게 말이 되나..) 공식 문서도 그렇고 적합한 방법을 찾지 못했다. SNS Topic에 구독자를 모두 순회해야 한다니..
백엔드에서 관리하고 있는 토큰과 데이터 정합성도 문제가 있다. 모바일앱의 FCM 토큰도 서버에서 관리를 해야하는데 이 부분에 SNS의 엔드포인트 관리와 정확히 일치해야 오발송을 피할 수 있다. FCM 토큰과 SNS 엔드포인트는 1:1이지만 SNS Topic의 구독은 그렇지 않다. 하나의 엔드포인트는 여러 Topic을 구독할 수 있다. 백엔드에서 관리하는 FCM 토큰, SNS 엔드포인트, SNS Topic 구독관리.. 이게 한 트렌젝션이 아니라면.. 문제가 생길 여지가 너무나 많다.
FCM 토큰의 특성도 간과하면 안된다. FCM 토큰은 JWT와 같이 키 자체에 만료 시간을 가진 것도 아니고 FCM에서 해당 유효 토큰 목록을 제공하지도 않는다. 사용자가 앱을 삭제하거나 앱 데이터를 지우면 FCM 토큰이 무효화되는데 이건 응답 값으로 알 수 있을 뿐이다. 그렇다고 그냥 방치하면 안된다. FCM 공식 문서에는 2달을 권고하고 있는데, 모바일앱이 실행될 때마다 FCM 토큰의 유효성을 확인하는 것이다. 확인된 FCM 토큰은 서버에서 관리가 필요하다. 사용된 FCM 토큰은 최신 사용일자를 갱신해주고 오래된 토큰은 갱신되지 않는다. 이 데이터를 이용해 2달이 넘은 FCM 토큰을 클랜징 해야 한다.
자 이제 위 SNS의 엔드포인트와 SNS Topic 구독을 트랜젝션 없이 데이터 정합성을 유지해야 하는데 이게 세련되지 못하다고 느꼈다.
FCM도 Topic 기반 전송을 지원한다. 가급적 AWS SNS를 상단에 두고 인터페이스처럼 쓰면서 AWS의 다른 서비스 통합성도 높이려는 의도였는데 생각처럼 마음에 들지 않았다. FCM Topic은 IoT 토픽처럼 계층구조를 가지고 있지 않고 원하는 문자열을 상호 약속해서 쓰면 된다. 서버에서는 해당 Topic으로 1회 메시지를 보내면 FCM이 나머지 발송 부분을 책임져 준다. 모바일앱이 해당 푸시를 받는데 걸리는 시간은 개별 토큰을 이용한 발송보다 좀 더 걸린다고 느겼다. 개별 토큰 발송이 1~2초 정도라면 Topic기반 발송은 5~30초 정도의 지연 시간이 있는 것 같았다. 모바일앱도 FCM SDK에서 제공하는 Subscribe, Unsubscribe를 이용하면 손쉽게 컨트롤 할 수 있다. 한번 Subscribe한 것을 또 해도 FCM이 알아서 필터링 해준다. 그렇다고 항상 Subscribe를 호출하는 코드를 넣지는 말자.
애초에 이 검토를 한 이유가 개별 토큰을 이용해 Push Notification을 발송할 경우 루프문 실행도 실행이지만 FCM과 통신에 사용하는 IO Blocking으로 인한 지연을 피할 수 없기 때문이다. 코루틴이나 멀티쓰레드를 이용할수도 있지만 더 세련된 방법을 찾고 싶었다. SQS와 Lambda를 이용해 비동기 서버리스로 발송 엔진을 구현할 수도 있지만 동일 메시지, 다수 사용자에 대한 최적의 방법은 아니라고 생각했다. 이러한 유스케이스가 아니라 다품종 소량? 발송의 경우 SQS + Lambda도 괜찮고, DynamoDB Stream + Lambda도 선택해 볼 만 한 것 같다. 더 많은 메시지량과 흔적 추적을 위해서는 Kafka나 Kinesis같은 것을 써 보면 좋겠다.
댓글