사실 5분은 오버고..
암튼 엄청 빠르게 서버 푸시 시스템을 구축할 수 있다는 뜻입니다. 아래 링크에 들어가서 따라하시면 됩니다.


위 링크에서 필요한 내용이나, 추가할만한 내용을 뽑아봤습니다. 



nodejs 서버사이드 iOS 푸시

iOS용부터 설명하겠다. 링크 여기 강좌에 들어가면 정말 쉽게 푸시 구현하는 방법을 설명해 놓았다. 3분안에 푸시를 쏘아볼 수 있다! 내가 위 링크의 포스팅보다는 잘 쓸 자신이 없으므로 나는 배포하는 부분을 좀 이야기하겠다. 테스트만 해볼 분들은 링크만 참고하면 되지만 배포할때는 아래 코드로 수정해야한다.

1) 푸시 개발용, 푸시 배포용 
// Developer 
var options = { 
    gateway : "gateway.sandbox.push.apple.com", 
    cert: './keys2/cert_production.pem',
    key: './keys2/key_production.pem',
    production: false
};

// AppStore 배포, Adhoc 배포
var options = { 
    gateway : "gateway.push.apple.com",//"gateway.sandbox.push.apple.com", 
    cert: './keys2/cert_production.pem',
    key: './keys2/key_production.pem',
    production: true
};

바뀐 부분은 두 라인인데, gateway부분과 production부분이다. gateway 값을 바꿔주고 productiontrue로 수정해야한다. 이렇게하면 AppStore배포나 Adhoc배포에서 푸시가 날아간다. 그러니 저렇게 바꿔서 테스트를 해보고 싶으면 Adhoc으로 배포하여 푸시테스트를 해보면 된다. (필자는 Developer, Adhoc, AppStore Distribute 상황에서 모두 테스트해보았고 그 결과를 말하는 것이다.) 

2) 푸시 여러개 한번에 보내기
위에서 소개한 강좌에 들어가면 푸시를 한번에 하나밖에 보내지 못한다. 아래 소스는 푸시 날리는 부분에 푸시아이디값을 Array로 만들어 한꺼번에 여러개 날릴 수 있게 해준다. 

var myDeviceArray = [ ]
for (var i=0; i<results.length; i++) {
     var token = results[i]._id;//'앞에서 Xcode로 build 하면서 획득한 아이폰 디바이스 토큰을 입력한다.'
     var myDevice = new apn.Device(token);
     myDeviceArray.push(myDevice);
}
try {
     apnConnection.pushNotification(note, myDeviceArray);
} catch (e) {
     console.log("apn exception : " + e);
}


apnConnection.pushNotification(note, myDeviceArray);
이 부분이 푸시를 실제 날리는 부분이고, 애플에게 푸시를 쏘아달라고 요청하게된다.

여기서 원래 myDeviceArray가 아닌 myDevice라는 객체를 넣었었는데, 필자는 여기에 myDevice의 배열을 넣었다. 이렇게하면 한번에 20개, 30개씩 푸시를 날릴 수 있다. 




nodejs 서버사이드 android 푸시

안드로이드는 iOS보다 휠씬 간단하게 구현이 가능하다. 안드로이드는 인증서 이런게 없기때문에 그냥 코드 구현을 하고 키값만 넣어주면 된다. 
그런데 자료를 찾다보니 앞서 소개한 iOS푸시구현 블로그와 동일한 저자가 안드로이드푸시 저자가 동일하고, 정리가 정말 잘되있다.. 링크를 따라가면  구현할 수 있을 것이다. 

여기서는 링크에서 서버쪽 코드만 빼내서 설명하겠다.

우리가 사용할 nodejs모듈은 https://github.com/ToothlessGear/node-gcm이다. node파일이 위치한 디렉토리에 들어가서 npm install node-gcm 명령을 치면 된다.

상단에 node-gcm를 불러오고

var gcm = require('node-gcm');


// or with object values
var message = new gcm.Message({
     collapseKey: 'demo',
     delayWhileIdle: true,
     timeToLive: 3,
     data: {
          lecture_id:"notice",
          title:"제목입니다",
          desc: "설명입니다",
          param1: '첫번째파람',
          param2: '두번째파람'
     }
});

var server_access_key = '/*안드로이드 개발자가 넘겨준 서버키*/';
var sender = new gcm.Sender(server_access_key);
var registrationIds = [ ];     // 여기에 pushid 문자열을 넣는다.

registrationIds = ['/*안드로이드 단말기에서 나온 푸시 아이디*/'];

/*
for (var i=0; i<push_ids.length; i++) {
     registrationIds.push(push_ids[i]);
}
*/

// 푸시를 날린다!
sender.send(message, registrationIds, 4, function (err, result) {
     // 여기서 푸시 성공 및 실패한 결과를 준다. 재귀로 다시 푸시를 날려볼 수도 있다.
     console.log(result); 
});
이상 서버사이드 아이폰, 안드로이드 푸시 알림이였습니다. 


WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,
이번 포스팅에서는 iOS에서 Objective-C로 푸시를 구현하는 방법에 대해 설명하겠다. 그렇지만 단순히 푸시를 받는것이 다가 아닌 푸시를 받았을 때 다음 이벤트 처리도 앱에서 어떻게 처리할지에대한 고민도 함께할 것이다. 이 부분은 애플에서 제공하는 방법이 생각보다 복잡하게 되있는것 같으므로 포스팅에 남기기로 하였다.

먼저 푸시를 받는 방법에 대해 설명하..  검색해보니 바로 원하는 링크가 안나와서 푸시 받는법부터 설명해보겠다.



1. 푸시 아이디를 얻어내는법

푸시 아이디를 얻고자 하는 시점에서 다음을 호출하면 된다. 
// 현재 푸시가 On인지 Off인지 알아내는 함수
BOOL pushEnable = NO;
if ([[UIApplication sharedApplication] respondsToSelector:@selector(isRegisteredForRemoteNotifications)]) {
    pushEnable = [[UIApplication sharedApplication] isRegisteredForRemoteNotifications];
} else {
    UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
    pushEnable = types & UIRemoteNotificationTypeAlert;
}

// 푸시 아이디를 달라고 폰에다가 요청하는 함수
UIApplication *application = [UIApplication sharedApplication];
if ([application respondsToSelector:@selector(isRegisteredForRemoteNotifications)]) {
    NSLog(@"upper ios8");
    // iOS 8 Notifications
    [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
    [application registerForRemoteNotifications];
} else {
    NSLog(@"down ios8");
    // iOS < 8 Notifications
    [application registerForRemoteNotificationTypes:
     (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)];
}


// AppDelegate.m 파일에서 아래 함수를 추가한다. 아래 함수는 푸시 아이디를 받아내는 함수이고, 푸시아이디는 아래 함수를 통해서만 받을 수 있다.
// AppDelegate.m
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
     NSString* newToken = [[[NSString stringWithFormat:@"%@",deviceToken]
                           stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]] stringByReplacingOccurrencesOfString:@" " withString:@""];
     NSLog(@"DeviceToken : %@", newToken );
}


위 코드를 보면 이것저것 복잡해보인다. 여기서 중요한 라인만 설명을 하자면

UIApplication *application = [UIApplication sharedApplication];
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
[application registerForRemoteNotifications];
위 세 라인이다.
1. 앱 싱글톤 델리게이트를 받아와서 2. 푸시받을 타입을 정한다. 그리고 3. 실제로 푸시 아이디를 달라고 요청한다.
registerForRemoteNotifications가 호출되는 순간 appdelegate.m에 
첫째, 앱을 설치하고 처음으로 registerForRemoteNotifications를 호출하는 상황이면 사용자에게 푸시를 허용할지 말지에 대한 경고창이 뜬다. 여기서 허용을 하면 AppDelegate.m에 구현된application:didRegisterForRemoteNotificationsWithDeviceToken: 함수가 호출되면서 deviceToken파라미터에 푸시아이디가 담기게 된다. 반대로 사용자가 푸시 허용을 하지 않는다면 application:didRegisterForRemoteNotificationsWithDeviceToken: 함수는 호출되지 않는다. 그게 다다. 사용자가 푸시알림 허용 안했다고해서 뭐 어떻게 더 바로 할 수 있는게 없다. 푸시알림 무한 유도를 막기위함의 애플의 생각이 아닐까 싶은데, 이 부분때문에 고민을 좀 많이했다. 그래서 준비한게 BOOL pushEnable;이고 푸시가 현재 허용되있는지 아닌지 대강 판단해준다. 푸시가 꼭 필요한 어플일 경우는 pushEnable를 받아내서 확인할 수 있다. (하지만 이것도 사용자가 한번도 푸시 설정을 안했는지, 실제 푸시 거부를 한건지 알 방법은 없다. 단지 현재 푸시 설정 On/Off 여부만 알 수 있다.)
둘째, 앱을 설치하고 푸시 허용을 해놓은 상태이며(최초 registerForRemoteNotifications를 호출 해본 상태), registerForRemoteNotifications를 호출했다면 AppDelegate.m에 application:didRegisterForRemoteNotificationsWithDeviceToken:함수가 실행되면서 푸시아이디를 받아올 수 있다. 푸시 아이디를 서버에 등록할 때 사실상 푸시 아이디는 바뀔 수가 있다. 그렇기에 나같은 경우는 푸시아이디와 디바이스아이디를 함께 서버에 보내서 디바이스 아이디 기준으로 푸시 아이디를 저장한다. 아무튼 두번째의 경우는 무조건 푸시아이디를 받아올 수 있는 상황이다.

세번째, 앱을 설치하고 푸시 허용을 해제해놓은 상태이며(마찬가지로 최초 registerForRemoteNotifications를 호출 해본 상태), registerForRemoteNotifications를 호출했다면 아무일도 일어나지 않는다. 그렇기때문에 이 경우는 미리 pushEnable를 받아내서 푸시가 가능한지 아닌지를 판단해놓을 필요가 있다. pushEnableNO이면 푸시 허용 Off인 상태인거다. 그러면 푸시 설정을 해라고 경고를 띄우면 된다. 


2. 서버에서 푸시를 쏘았을때 폰에서 받은 위 호출되는 함수
서버에서 푸시를 쏘자마자 알아내는 방법은 하나밖에 없다. -> 앱이 실행되는 중일때이다.
앱이 만약 꺼저있거나, 백그라운드에서 돌고있으면 푸시가 왔는지 안왔는지 앱에서는 모른다. 앱이 다시 포그라운드로 왔을때 푸시가 왔으면 ‘푸시받는함수'가 호출된다. 하지만 웃긴것이, 앱이 백그라운드에도 없고 메모리에 올라가있지 않을때 푸시가 왔을때, 바탕화면에서 푸시를 받아서 푸시를 눌러 앱에 들어가면 ‘푸시받는함수’가 호출되지 않는다. 이 예외적인 부분을 처리하는 방법에 대해 설명하겠다.
 
// AppDelegate.m
// 앱이 런칭되서 메모리에 올라갈때 실행되는 함수이다.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    UILocalNotification *notificationUserInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
    if (notificationUserInfo) {
//        NSLog(@"app recieved notification from remote%@",notification);
        NSMutableDictionary *mNotificationUserInfo = [NSMutableDictionary dictionaryWithDictionary:(id)notificationUserInfo];
        mNotificationUserInfo[@"appLaunch"] = @"yes";
        [self application:application didReceiveRemoteNotification:mNotificationUserInfo];
    }else{
//        NSLog(@"app did not recieve notification");
    }
    return YES;
}

여기서
// AppDelegate.m
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {

    [PushController shared].userInfo = userInfo;

    if (userInfo[@"appLaunch"]) {
        [PushController shared].waitingViewDidLoad = YES;
    } else {
        if (application.applicationState==UIApplicationStateActive) {
            [[PushController shared] pushInActiveStatus];
        } else {
            // if (application.applicationState==UIApplicationStateInactive) {
            [[PushController shared] presentPushPostTableViewController];
        }
    }
}
위의 PushController는 일단 신경쓰지 말고 푸시를 받았을 때 라이프 사이클만 신경쓰자. 
저기서 특이한 점은 
if (userInfo[@"appLaunch”])
를 이용하여 첫째 상황인지 먼저 구분하고
if (application.applicationState==UIApplicationStateActive) ;
를 이용하여 둘째상황과 셋째상황을 구분했다는 점이다.

첫째 상황에서는 아직 ViewController가 생성이 되기 전의 라이프사이클이다. 그렇기때문에 ViewControllerviewDidLoad를 호출하기 전까지 푸시정보를 잠시 가지고 있어야한다. 
둘째 상황에서는 application.applicationState==UIApplicationStateInactive에 해당하는 상태인데, 현재 앱이 백그라운드에서 포그라운드로 가고 있다는 뜻으로 사용자가 앱을 켜놓고 바탕화면에서 푸시를 눌렀을 때를 말한다. 이때는 바로 푸시 행위를 진행하면 된다.
셋째상황은 application.applicationState==UIApplicationStateActive의 상황으로써 앱이 이미 포그라운드에서 돌고 있었다는 뜻이고 이때는 자동으로 푸시 기능을 실행시켜버리는게 아니라 상단에 팝업창이 떠서 푸시가 온것처럼 만들어주면 사용자가 인지하기 쉬울 것이다. (안드로이드는 기본으로 제공되고, 아이폰은 카톡에서 볼 수 있는 기능이다) 상단에서 팝업이 잠시 내려왔을때 팝업을 누르면 푸시 기능을 수행하게 만들었는데, 이 기능은 PushController에 있다. PushController는 UI작업을 좀 했기 때문에 라인이 좀 되서 첨부해두겠다.

아무튼 위와같이 처리하면 푸시를 받는 일을 놓히지 않고 처리할 수 있다. 
 



WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,