Rails를 이용한 아이폰, 안드로이드 통합 푸시 서버(third party) 만들기

 

들어가기에 앞서

안녕하세요 @daramm.g 입니다. 이번 포스팅은 Ruby on Rails를 이용해 아이폰과 안드로이드를 통합 관리하는 푸시 서버를 만드는 과정에 대한 것입니다. 

‘푸시서버? 그게 뭐지? 그.. 메세지 보내기 위해 필요한.. 음, 뭐 그런거?’ 정도만 알고 있으셔도 이 글을 읽는데는 큰 무리가 없으실 거에요.

왜냐면.. 저도 아무것도 몰랐거든요. ㅎㅎ

내용에 들어가기에 앞서 구현에 많은 도움을 주신 태연님과 현학님, 그리고 재현님께 깊은 감사를 드립니다.

자 그럼 푸시서버가 무엇인지, 왜 필요한지에 대하여 알아보도록 하겠습니다.

 

푸시서버의 개념과 구현하려는 푸시서버의 요구사항

제가 구현하려고 하는 push server는 애플에서 제공하는 apn(ios), 구글에서 제공하는 c2dm(android) 서버에 푸시(메세지) 요청을 보내는 third-party 서버입니다.

apn에 대하여 살짝 부연설명을 덧붙이자면 apn은 애플에서 제공하는 푸시 서비스로 apn에 푸시 요청을 보내면 apn이 각 디바이스(아이폰, 아이패드등)에 푸시 알림을 보내는 형태입니다.

c2dm은 구글에서 제공하는 푸시 서비스로 기능은 대부분 apn과 같습니다.(다만 c2dm은 android 2.2 버전, 즉 프로요 이상의 디바이스에만 푸시 알림을 보낼 수 있고 apn과 조금씩 다르지만 글을 쓰면서 언급하도록 하겠습니다)

 

(그림으로 보면 위와 같습니다)

그럼 제가 만들 푸시서버의 요구사항을 알아보도록 하겠습니다.

1. 푸시는 웹페이지에서(urban airship과 유사한) 전송할수 있고 api 요청으로 전송되어야 한다.

2. 서비스별 os_type 별 통합 및 별도 관리 전송이 가능해야 된다.

3. 앱에 로그인한 user 기반으로 device가 관리되어야 된다.(‘a’라는 user는 아이폰도,겔럭시s를 동시에 가질수도 있다.)

4. 전체 device(로그인하지 않은 device 포함)에 보낼 수 있는 기능과 특정 user에게 혹은 user list를 받아 푸시를 보내는 것이 가능해야 된다.

그리고 이것을 테스트하기 위해 필요한 사항에 대해서 알아보도록 하겠습니다.

1. device를 등록할 수 있고 push 전송 받을 ios device 2개, android device 2개.

2. push를 테스트할 push기능이 가능한 test 앱 2개(ios,android 각각)

3. ios push test 앱을 만들고 나서 앱 인증서. android의 경우 해당 앱을 마켓에 등록한 개발자 id, 비밀번호, 앱 이름

※ 푸시가 잘 오는지만 확인하기 위해서는 앱과 device가 하나씩만 있어도 됩니다.

 

Gem을 이용한 푸시 테스트

Rails 개발 환경에 대하여 언급을 하자면 linux(ubuntu 10.04)에 ruby 1.92(p180), rails3를 사용하였음을 알려드립니다.

제가 푸시서버를 만들면서 제일 먼저 찾아본 것이 gem 입니다. 마침 apn_on_rails 과 c2dm_on_rails이란 이름으로 gem이 있었으나 통합하여 쓰려면 일단 많은 부분을 고쳐서 써야되고 c2dm 의 경우 rails3에서 돌아가게 하려면 많은 부분을 고쳐야 됩니다. 2011/07/20일 즈음에는 rails3로 개발된 c2dm_on_rails가 없었으니 날짜가 많이 지났다면 다시 찾아보시는 것도 좋습니다 ^^

제가 사용한 gem 의 링크를 아래 적어 놓겠습니다.

apn_on_rails : https://github.com/natescherer/apn_on_rails   (readme 에 써있는데로 따라하면 잘 됩니다)

c2dm_on_rails : https://github.com/mmassaki/c2dm_on_rails (rails2버전으로 제작된 것으로 보이며 여러 수작업을 해줘야 돌아갑니다)

c2dm_on_rails gem을 rails3에서 사용할 수 있게 고치는 과정은 아래의 링크를 클릭하시면 확인할 수 있습니다.

c2dm_on_rails gem을 rails3에서 동작하게 변환하는 작업

이렇게 apn_on_rails 와 c2dm_on_rails gem을 설치 후 해당 gem의 git 페이지 readme를 잘 보면서 따라하고 각 device에 푸시 테스트를 완료하였다면 일단 기본적인 기능은 완료된겁니다.^^(전 여기서 쬐금 감동 ㅠㅠ)

※ 모든 가입자에게 푸시를 보낸다는 요구사항만 있으면 여기까지만 테스트 후에 device 등록 및 푸시 전송하는 api만 구현하면 됩니다.

 

User 기반 통합 푸시 서버 구현

위의 테스트를 하였다면 이제 대충 어떤식으로 푸시가 디바이스에 전송되는지 아실거라고 생각됩니다.

간략하게 설명을 하자면 device 모델에는 각 device의 정보가 들어갑니다. 그리고 그 device모델을 참조하는 notification 모델에는 해당 device_id와 alert(보낼 메세지), properties(속성), sound 등등이 들어갑니다. 요 notification모델에서 apn이나 c2dm 서버로 푸시 요청을 하는 것이지요. 

따로따로 메세지를 보내는 것은 가능하지만 User기반으로 ios와 android 통합관리를 위해서는 아직 많이  부족합니다.  User란 login한 user명을 나타냅니다. 카카오톡으로 생각하면 핸드폰 번호가 user가 됩니다.
여튼 저는 통합관리를 위해 apn_on_rails와 c2dm_on_rails의 모델을 싹 다 갈아 엎었습니다.(갈아 엎으면서 코드 변경은 불가피 합니다 ㅠㅠㅠ)

먼저 User라는 모델을 새로 만들었고 device라고 각각 되어있는 모델도 android_user와 ios_user로 명칭을 바꾸어 다시 만들었습니다. 또 ios_device,android_device모델은 User 모델을 참조하여 통합관리가 가능하도록 하였습니다. 예를들어 ‘a’라는 User는 안드로이드든 아이폰이든 상관없이 가질 수 있게 되는거죠. 또 ‘a’한테 푸시를 보낸다고 하면 ‘a’  user와 연결되어 있는 ‘a’  user를 참조하고 있는 android device와 ios device에게 푸시를 보내게 되는것이구요.

notification을 만들어서 apn 및 c2dm에 보내는 과정은 apn_on_rails 와 c2dm_on_rails gem에 있는 함수를 그대로 이용합니다.(apn_on_rails 와 c2dm_on_rails gem에서 connection.rb 파일에 각 서버에 접속 및 전송 하는 부분 구현되어 있습니다 ^^)

 

웹 Interface를 통한 전송

 

rails의 scaffold로 모델및 컨트롤러를 만들면 기본적으로 create라던가 new 라던가 delete라던가 하는 부분들이 restful하게 자동 생성됩니다. 아래 보시는 화면도 결국 new액션으로 새로운 push 요청을 만드는 화면(form)입니다.

서비스를 선택할 수 있고 os_type도 정할 수 있으며 user list도 적을 수 있죠. 이렇게 되면 기존의 요구사항을 충족하게 되죠.(사실 요구사항에 대한 이해도 잘 못했고 부분적으로 바뀐부분이 많아 부시고 만들고의 과정을 많이 거친 인터페이스입니다 ^^) 예쁘다거나 ux친화적이지는 않지만 기본적인 기능은 모두 제공을 하고 있습니다.

 

(Web interface 를 통한 push 전송)

꼼꼼히 정하고 나서 마지막 push 보내기를 누르게 되면 push가 apn과 c2dm 서버로 전송되게 됩니다.

이 과정을 좀더 깊게 살펴 보면 push 보내기 버튼을 누르면 create 액션이 작동하게 되고 form에서의 정보를 바탕으로 service, os type 그리고 user list의 분기를 거쳐 각각 위에서 만들었던 전송 함수를 실행하게 됩니다.

 

기타 기능 및 주의해야 될 점

아래 화면은 푸시를 보내고 나서 보냈던 푸시 리스트를 확인하는 화면입니다. 또 기타 기능으로 실수로 보낸 푸시 멈추기, 실패한 푸시 다시 보내기 등의 기능이 있고, 전체 푸시 count 수와 success count, 실패한 fail count를 표시해 주고 있습니다.

(보낸 Push 리스트를 보여주는 화면)

푸시 멈추기의 경우 푸시를 전송하는 과정이 비동기로 처리가 되어 있어야 가능한데요.(보내기 버튼을 누르고 바로 전송되어 버리면 멈추기가 의미가 없겠죠? 또 천개 이상의 메세지를 보낸다고 했을때 다 보낼때까지 해당 웹 어플리케이션 전체가 상당시간 멈춰있겠죠? 그러면  안되기 때문에 비동기로 처리해 주는 것은 필수 입니다)

 

주의할 사항

전송과정의 background 처리

저의 경우 rake task를 통해 전송 과정을 백그라운드로 처리하였습니다. 관련해서 참고하실만한 rails cast를 링크로 걸어드릴게요 ^^
rails task 백그라운드 처리 http://railscasts.com/episodes/127-rake-in-background  (이거 찾고 엄청난 감동이.. 결국 & 하나 차이였다는 ㅠ)

Message size validate

apn에서 받을 수 있는 total message size는 256byte입니다. 그런데 device token 등을 빼고나면 231byte 정도만 message로 사용할 수 있게 되죠. 그런데 json으로 날라가기때문에 key 값은 또 허무하게 날라간다고 보시면 실제로 push 메세지로 보낼 수 있는 양은 상당히 줄어들게 됩니다. (왜 아이폰에서 몇십줄씩 메세지로 안오는지 아시겠죠? ㅎㅎ) 그래서 전송전에 미리 bytesize validate를 해주시는게 좋습니다.(gem에서 255byte이상일 경우 전송하지 않게 되어있습니다).

그에 비해 c2dm의 경우 1024byte의 제한이 있는데 1024byte라 함은 푸시전송으로 쓰기에는 충분히 남는 양이기 때문에 그다지 신경 안써주셔도 됩니다.

Device에서 앱이 삭제되어서 푸시가 공중으로 떠버리는 경우

만약 제 스마트폰에서 카카오톡을 지웠다고 하면 카카오톡은 저에게 계속 메세지를 보내게 될까요?..그게 나뿐만 아니고 수많은 사람의 푸시가 계속 다시 보내지게 된다면?.. 에 대한 문제입니다.

다행히 apn과 c2dm 모두 그에 대한 해결책을 주고 있는데요. apn의 경우 feedback service를 통해 지워진 device를 체크합니다. 이게 뭐냐면 앱이 해당 디바이스에서 지워지고 앱관리자가 push를 보냈다면 apn 서버가 이것을 기억하고 있다가 해당 앱으로 feedback 요청(apn_on_rails에서 rake task로 지원하고 있습니다)을 하면 앱에서 지워진 token(각 device마다 있는 고유의 키)을 보내주는 서비스입니다.

근데 정말 황당한 것은 그 device에 푸시가 가능한 다른 앱이 하나라도 있어야 된다는 것입니다. 즉 제 아이폰에 ‘푸시 가능한 앱이 하나있었는데 그것을 지웠다’ 라고 하면 아무리 feedback 요청을 해도 해당 token은 지워진 리스트는 넘어오지 않게 되죠. 사실 푸시 가능한 앱이 하나밖에 없다는게 거의 말이 안되지만 test앱의 경우 보통은 다  apn 실서버가 아닌 apn sandbox 서버로 푸시를 테스트하게 될텐데 이럴 경우 거의 무조건 이런일이 발생합니다.(저도 엄청 고생했었습니다).당황하지 말고 푸시가능한 다른 테스트 앱을 깔아서 테스트 해보시기 바랍니다.

c2dm의 경우 요청을 보내면 바로 응답을 주게 되어서 지워졌을경우 지워졌다고 알려줍니다. c2dm_on_rails gem에서 이런경우 해당 device를 지우도록 코딩되어 있습니다.

 

글을 마치면서..

사실 이 포스팅에는 제가 구현한 부분중에 몇가지 부분이 빠져있습니다.(api 요청을 통해 푸시 전송, device 등록과 user와의 관계라던가, logout했을 시에 처리 등등) 하지만 위의 내용을 꼼꼼히 보시면서 꾸준히 하시다 보면 해당 기능들은 충분히 혼자서 하실 수 있을겁니다.

rails를 통해 해본 첫 프로젝트이고, 코딩은 거의 혼자 해서 여러모로 막히는 부분이 많았지만 그때마다 큰 도움들을 주신 태연님, 재현님께 감사를 드리며 푸시 test 앱 및 api 요청관련해서 많이 알려주신 태연님, 현학님, 무익님께도 깊은 감사를 드립니다.


출처 - http://thinkreals.com/678








'Android > GCM' 카테고리의 다른 글

android - gcm-server.jar maven repository and dependency  (0) 2013.06.08
GCM 특징 및 설정  (0) 2013.01.18
android - C2DM 이용하기  (0) 2012.09.11
Posted by linuxism
,