Push Notification 서버를 기본 REST API 형태로 구현하려고 했으나, 애플 공식 문서를 보는 순간, 아, 그냥 node js의 모듈을 이용해야 겠다는 생각이 들었습니다. iOS 서버를 따로 만들어 구동 방식을 이해하고, Fire base 에 통합할 예정이라, 별 고민 없이 node의 모듈을 이용하게 되었습니다.
node js의 apn 이라는 모듈을 이용할 예정이며, 선행작업으로는 프로비저닝 프로파일 및 푸쉬 인증서, 클라이언트 푸쉬구현이 되어 있어야 합니다.
위의 선행 작업 문서 중, [프로비저닝 프로파일 및 푸쉬 인증서 생성] 링크를 눌러 나타나는 문서에, 푸쉬 인증서 .pem 을 만드는 방법이 있습니다. 여기서 푸쉬 인증서 pem 파일과, 푸쉬 인증서의 key pem 파일(암호를 풀어 놓은 것) 을 만들어 이것을 복사해 둡니다.
저는 인증서 파일의 이름은 [yeslife_ap_cer.pem] 이며, 키 파일의 이름은 [yeslife_ap_key.unencrypted.pem] 입니다.
3. apn 프로바이더 만들기.
옵션을 만들어 프로바이더를 생성하는 구조이며, 옵션은 토큰을 넣는 방법, 인증서를 넣는 방법 2가지가 있습니다. 저는 인증서를 넣는 방법을 선택했습니다. 아래 2가지 방법 중 하나로 옵션을 만들면 되겠습니다.
1) 푸쉬 인증서로 보내기.
gateway는 개발단계에서는 gateway.sandbox.push.apple.com 이며, 실제 서비스 할 때는 gateway.push.apple.com 입니다. 해당 내용은 애플 개발자 공식문서 (링크) 에 나와 있습니다.
var option = {
gateway:"gateway.sandbox.push.apple.com",
cert:'yeslife_ap_cer.pem',
key:'yeslife_ap_key.unencrypted.pem'
};
let apn_provider = new apn.Provider( option );
2) 토큰 으로 보내기.
먼저, 애플 개발자 사이트의 Certificates, Identifiers & Profiles 부분페이지에서 토큰을 생성하고 다운로드 받도록 하겠습니다. Certificates, Identifiers & Profiles 페이지로 에서 Keys, All 을 선택하고, + 버튼을 눌러 주도록 합니다.
나온 페이지에서, Name 과 APNs 항목을 클릭하고, Continue 버튼을 눌러 주도록 합니다.
키를 만들기 전 입력사항이 맞는지 확인하는 페이지에서 Confirm을 눌러 생성하도록 합니다.
키가 만들어 졌습니다. Key ID는 중요하므로, 꼭 적어 두도록 합니다. 그리고 키는 한 번만 다운로드 가능합니다. 옵션은 아래와 같은 요령으로 만들어 주면되겠습니다. Production은 개발인지 실제 서비스하는 지 여부 입니다. (호출 주소가 틀림).
var option = {
token : {
key : '.p8 파일 경로',
keyId:'키를 만들 때 표시된 아이디',
teamId: "팀명"
},
production: false
};
4. 보낼 내용 및 장치 토큰 저장해 두기.
앱이 켜지면, APNs 에 앱이 등록되는 데, 성공하면, 토큰을 받아 옵니다. 이 값을 deviceToken 값에 저장해 줍니다.
보낼 내용은 apn.Notification 객체에 저장이 되는 데, 이 부분은 공식 문서의 payload 부분을 참고 하면 되겠습니다.
// 앱에서 APNs에 앱을 등록하고, 얻은 값.
let deviceToken = 'A89B3B5731AD3821D0AA316CFBC6E54E750C216ED6308112CE437BAC3730005F';
// 보낼 데이터를 만들어 줍니다.
var note = new apn.Notification();
// 보내기 실패할 경우 언제까지 재시돌 할 것인지 여부.
note.expiry = Math.floor( Date.now() / 1000 ) + 3600;
// 앱의 아이콘에 표시될 숫자. ( 우리가 흔히 몇 개의 메시지가 있다고 인식하는 )
note.badge = 3;
// 메시지가 도착했을 때 나는 소리.
note.sound = "ping.aiff";
// 메시지 내용.
note.alert = "왓쇼이 왓쇼이";
// 누가 보냈는지 여부.
note.payload = {"messageFrom" : "메시지"};
// ios app 번들 명.
note.topic = "com.tistory.yeslife.hello";
5. 메시지 보내기.
apn.provider의 send 메소드를 이용해 보내면 됩니다.
// 실제 메시지를 보내도록 합니다.
apn_provider.send(note, deviceToken).then( function( result ){
console.log("결과 : " + result);
console.log( result );
}).catch( function (err){
throw( err );
});
Push Notification (일명 : 푸쉬)는 서버에서 사용자에게 일방적으로 메시지를 보내는 기능 입니다. 주로, 운영자가 특정 소식을 전할 때 사용됩니다. 사인할 프로비저닝 파일이 필요하며, App id에는 Push를 사용할 것으로 설정이되어 있어야 합니다. (앱 아이디 수정도 가능합니다.)
Xcode 프로젝트를 생성합니다. 위의 [프로비저닝 프로파일 만들기], 를 참고해, 프로젝트를 듭니다. 저는 [com.tistory.yeslife.hello] 라는 번들 아이디로 프로젝트를 만들었습니다. 이 페이지에서 참고한 기준 문서는 유저 노티피케이션 입니다. 아래 Topics 부분을 보면, 순서로 나열된 것이 있습니다.
2. 프레임 워크 추가.
프로젝트 > Build Phases 탭을 차례로 선택하고, Link Binary With Libraries를 확장 시킵니다.
+ 버튼을 눌러, UserNotifications.framework 와 PushKit.framework를 추가해 줍니다.
3. Capabilities 탭을 선택해 푸쉬를 활성화 해 줍니다.
프로젝트 > Capabilities 탭을 차례로 선택해 줍니다. 그리고, 페이지를 스크롤해 Push Notifications를 활성화 해 줍니다. (Provisioning Profile이 서정되어 있지 않다면, 활성화 할 수 없습니다.).
4. 사용자에게 알림을 사용할 것인지 묻기.
UserNotifications 문서 토픽에 보면, 첫 스텝으로 Asking Permission to Use Notifications 문서 ( 링크 ) 가 있습니다. 그 문서를 참고해 사용할 것인지 묻는 코드를 작성하였습니다.
1) 앱 델리 게이트 부분에 사용자 물음 추가. (Application 메소드 속에 넣으면 좋습니다.).
// 유저 노티피 케이션 센터를 가지고 옵니다.
let center = UNUserNotificationCenter.current()
// 사용 여부를 묻습니다.
center.requestAuthorization(options: [.alert, .sound])
{ (granted, error) in
// 승인 받았는지 여부를 여기서 확인할 수 있습니다.
guard granted else {
print( "유저가 푸쉬 알림을 허락하지 않았습니다." )
return
}
print( "유저가 푸쉬 알림을 허락하였습니다." )
}
주의 할 점은, 응답이 왔을 때 메인 쓰레드 (UI)와 동기화 처리가 필요하므로, DispatchQueue.main.async 메소드 속에서 호출해야 합니다.
위에서 작성했던 코드가 아래와 같이 됩니다.
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.getNotificationSettings
{ (settings) in
// Do not schedule notifications if not authorized.
let center = UNUserNotificationCenter.current()
// Request permission to display alerts and play sounds.
center.requestAuthorization(options: [.alert, .sound])
{ (granted, error) in
// 유저가 푸쉬를 사용하지 않겠다고 한 경우, 여기서 처리합니다.
guard granted else {
print("유저가 푸쉬 알림을 승인하지 않았습니다.")
return
}
// 푸쉬를 처리한 경우, 여기서 처리 합니다.
print("유저가 푸쉬를 승인하였습니다.")
// 메인 쓰레드 UI와 Thread 동기화 처리 될 수 있도록, DispatchQueue.main.async 메소드로 감싸 줍니다.
DispatchQueue.main.async {
// APNs 에 스마트폰을 등록하는 메소드 입니다. (네트워크)
UIApplication.shared.registerForRemoteNotifications()
}
}
}
2) APNs로 부터 응답처리.
2개의 메소드 모두 AppDelegate 클래스에 추가하도록 합니다.
// APNs 서버에 등록한 경우 표시.
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken
deviceToken: Data) {
// 토큰 값을 가지고 옵니다.
let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
// 콘솔에 토큰 값을 표시해 줍니다.
print("APNs device token: \(deviceTokenString)")
}
// APNs 서버에 등록하지 못한 경우, 오류를 표시.
func application(_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError
error: Error) {
// Try again later.
}
6 클라이언트 검증해 보기.
일단 클라이언트 부분의 푸쉬 부분은 끝났지만, 이게 잘 동작하는 지 확인할 필요가 있습니다. 서버까지 구축 후 푸쉬를 보내어 보았는데, 안되면, 과연 서버 문제인지, 클라이언트 문제인지 파악하기 힘들 수 있습니다. 그래서 다른 사람들이 구축해 놓은 서버 프로그램으로 푸쉬가 잘되는지 확인할 수 있습니다. 우리는 http://apns-gcm.bryantan.info 라는 사이트를 이용할 것 입니다.
당연한 이야기 일 수 있습니다만, 앱이 종료된 상태여야 푸쉬가 옵니다. 앱이 켜진 상태에서는 푸쉬를 보내도 푸쉬가 오지 않습니다.!!
그리고, 이 것을 테스트 하기 위해서는 푸쉬 인증서가 필요하며, 위의 인증서 설명 링크를 통해 인증서를 만들 도록 하시기 바랍니다.
Device Token에는 기기를 APNs에 등록하고, 받아 왔던 토큰, Message는 보낼 메시지를 넣어주세요. Pem File은 인증서로 만들었던, 파일을 넣어주세요. Environment는 Sandbox를 선택해 줍니다. 그리고 Submit 버튼을 누르면, 메시지가 보내어 집니다.
원래 계획은 이곳에 서버 제작도 넣으려고 했으나, 따로 분리하는 것이 좋겠다 생각되어, 클라이언트 제작 내용만 남깁니다. 이 후 서버 제작 부분도 페이지를 만들도록 하겠습니다.
오랜만에 애플을 개발할 일이 있어, 애플 계정을 사용하게 되었는데 한글로 잘 번역이 되어 있어 매우 놀랐습니다. 뭐 사용법을 모두 아는 터라, 필요는 없었지만, 미처 알지 못한 부분에 대해서 확인을 할 수 있어 유익했습니다.
원래 이 글을 남기기전, 영어 문서로 되어 잇을 것 같아 디테일하게 글을 쓰려고 했지만, 문서가 한글로되어 있어서, 그냥 개요정도만을 표시할 것 같습니다.
맥의 설명서를 보려면, 애플 개발자 프로그램을 활성한 유료 개발자만 가능합니다.
1. Developer ID 인증서 생성하기.
앱을 개발할수 있는 개발 ID를 생성하는 과정이며, 자신의 맥에서 생성한, CSR 파일을 애플 개발자 사이트에 등록함으로써 생성합니다. 즉 등록한 기기에서만 앱을 개발할 수 있습니다.
1) 키체인 접근에서 CSR (Certificate Singing Rquest) 을 생성하기.
하단 독에서 라운처 패드를 열어 줍니다.
기타 아이콘들이 들어 있는 폴더를 선택해 줍니다.
그리고 호출 된 창에서, 케체인을 선택합니다.
키체인 창이 뜨면, 상단 드롭다운 메뉴에서, 키체인 접근 > 인증서 지원 > 인증 기관에서 인증서 요청을 선택합니다.
인증 지원 창이 뜨면, 먼저 디스크에 저장됨을 선택하고, 사용자 이메일 주소와 일반 이름을 입력하고 계속을 눌러 줍니다.
그러면, 파일 저장위치가 선택되고 CSR 파일이 만들어지게 됩니다.
2) CSR 업로드.
애플 개발자 사이트 ( Developer.apple.com)에서, Account로 들어와 왼쪽 메뉴에서 [Certificates, IDs & Profiles] 를 선택해 줍니다.
화면이 바뀌면, Development항목을 선택하고, 오른쪽 화면 상단의 + 버튼을 눌러 줍니다.
어떤 인증서 타입이 필요하냐고 묻는 창이 뜨면, iOS App Development를 선택하고 스크롤를 쭉 내려,
CSR 생성 화면 입니다. 우리는 이미 앞서 생성했으므로, 컨티뉴를 선택해 다음으로 넘어 갑니다.
다음 창이 뜨면, CSR 파일(앞서 생성했던)을 업로드하고, 다음 버튼이 활성화 되면, 눌러 다음으로 넘어 갑니다.
그러면 아래와 같이 개발 인증서가 완성됩니다. 언제든지 다운로드 받을 수 있습니다. 다운로드를 눌러 파일을 다운로드 하도록 합니다.
아래와 같이 인증서가 다운로드 되었을 것 입니다. (파일 이름은 적당히 바꿔도 됩니다.). 이 것을 더블 클릭해 키체인에 등록해야 합니다.
2. 앱 아이디 등록.
앱 아이디는 어떤 앱을 자신이 만들것인지 등록하는 과정입니다. 모든 앱은 기기에 테스트하거나, 애플스토어에 등록하거나 할 때, 이 아이디가 필요합니다. 앱 아이디는 2종류가 있습니다. 명시적인 앱아이디와 와일드 카드 앱 아이디가 있습니다. 와일드 카드는 테스트등 앱의 번들 아이디가 수시로 바뀌는 환경에서 좋습니다. 명시적인 앱 아이디는 앱을 배포할 때 사용하는 것이 좋습니다.
1) 앱아이디 항목 선택.
왼쪽 메뉴에서 App IDs를 선택하고, 오른쪽 상단의 + 버튼을 눌러 줍니다.
2) 앱 아이디 등록.
App ID Description 은 앱에 대한 설명인데, 우리가 인식하기 좋은 값들을 넣어 줍니다.
App ID Suffix 항목에서는 명시적 아이디( Explicit App ID )를 선택하고 번들 아이디를 넣어 줍니다. 우리가 앞으로 생성할 프로젝트의 번들 아이디와 일치해야 합니다.
App Service 항목은 이 앱이 사용할 서비스를 선택할 수 있습니다. 당연한 이야기지만, 여기서 선택하지 않은 서비스는 앱에서구현할 수 없습니다. Game Center와 In-App Purchase는 이미 자동으로 선택이되어 있습니다. 여기서 푸쉬 정도만 추가로 선택하고, Continue를 눌러 다음으로 넘어 갑니다.
3) 앱 아이디 확인 및 등록.
그럼 아래와 같이 입력 사항을 확인하는 창이 듭니다. 별 이상이 없다면, 아래 Register 버튼을 눌러 줍니다.
그럼 앱 아이디 등록이 완료 됩니다.
3. 장치 등록.
위에서 앱 등록하는 과정이 어떤 앱을 만들것인지 등록하는 과정이 였다면, 장치는 어떤 장치에서 사용할것인지를 등록하는 과정입니다. 개발 단계외 출시전 테스트를 위한 애드 훅을 배포할 때는 이 장치 등록이 필요합니다.
1) IPHONE UDID 확인하기.
일단 아이튠즈를 실행 합니다. (아이폰을 꽂으면 자동을 실행되었던 것 같은데, 일단 켜두는 것이 좋을 것 같습니다.)
아이폰을 맥에 연결합니다. 그러면, IPhone 정보 연결 허용을 묻는 창이 뜨는 데 계속을 눌러 줍니다.
그리고 아래 창이 뜨면, 아이폰을 확인(잠겨 있다면, 잠겨 있는 것을 풀어 줍니다.), 이 컴퓨터를 신뢰하시겠습니까? 라는 다이얼로그가 떠 있다면, 신뢰 버튼을 눌러 줍니다. (암호가 있다면, 암호도 눌러 주도록 합니다.).
그럼 아래 이미지와 같이 기기가 추가 되고, 기기를 선택해 줍니다.
그럼 창이 뜨고, 아래 부분을 확인할 수 있을 것 입니다. 일련 번호 부분을 몇 번 누르다 보면, UDID를 확인할 수 있습니다.
아래와 같이 UDID가 표시되면 마우스 오른쪽 버튼을 눌러 복사하기를 선택하기를 눌러 주면, UDID가 복사 상태가 됩니다. 메모장에 붙여 넣거나 해 둡니다.
2) 장치 추가
아래와 같이 Devices > All을 선택하고, 나타난 화면 오른쪽 상단의 +. 버튼을 눌러 줍니다.
장치 정보를 입력하는 창이 뜨면, 이름에 작당한 값을 넣고, UDID는 조금전 확인했던 값을 넣어 주도록 합니다. 그리고, 다음 버튼을 눌러 줍니다.
마지막으로 장치를 검토 및 등록 창이 나오면, 문제가 없다면, 등록 버튼을 눌러 등록을 완료해 주도록 합니다.
4. 개발용 프로비전닝 프로파일 만들기.
마지막으로 개발용 프로비저닝 프로파일을 만들어 줍니다. 이 파일을 이용해 앱을 사인해 실제 기기에서 앱을 실행해 볼수 있습니다. 개발용 프로비저닝 프로파일을 선택해 창이 뜨면, + 버튼을 눌러 생성 페이지를 불러 주도록 합니다.
여기서 우리는 iOS용을 만들것 이므로, iOS App Development를 선택해 주도록 합니다. 당연한 이야기지만, 배포할 때는 Distbution 섹션의 App Store 를 선택하면 됩니다.
앱 아이디를 선택하는 창이 뜨면, 앞서 만들었던 앱 아이디를 선택해 주면됩니다.
앞서 만들었던 개발 인증서를 선택해 줍니다. 여러개 선택해도 되지만 우리는 하나만 만들었으므로, 하나만 표시됩니다. (이 인증서가 등록된 기기에서만 이 프로비저닝 프로파일을 이용해 사인을 할 수 있습니다.).
기기 등록창이 나타나면, 앞서 등록했던 기기를 선택해 주면 됩니다. (여러개 선택해 줄 수 있습니다.).
마지막으로 프로 비저닝 파일의 이름을 입력해 주는 창입니다. 적당히 입력하고 컨티뉴를 선택해 줍니다.
그럼 프로비저닝 프로파일이 생성완료되고, 다운로드 와 설치를 하면됩니다.
5. Push Notification 인증서 만들기. (옵션).
1) 인증서 생성.
이 작업은 하지 않아도 앱을 생성하거나 하는데 문제는 없습니다. 하지만, Push Notification은 거의 모든 앱에 사용되는 기능이 므로, 꼭 만들어 두기 바랍니다.
인증서 관리 페이지에서, 좌측메뉴 개발자 인증서를 선택하고, + 버튼을 눌러, 인증서를 추가하도록 합니다. (여기서는 개발용으로 만들지만, 푸쉬는 Production으로 만들어도, 개발과 배포에서 모두 사용이 가능합니다.)
개발용 Push Notification 파일을 선택하고, Continue 버튼을 눌러 주도록 합니다.
앱 선택 페이지에서 적당한 앱을 선택하도록 합니다.
다음 CSR File 업로드 설명 페이지가 나오는 데, 앞서 개발 인증서를 만들 때 사용하였으므로, 그 파일을 이용할 것 이므로, 그냥 Continue를 눌러 다음으로 넘어 갑니다.
CSR File 에서 파일을 앞서 만들었던 CSR File을 선택하고 Continue 파일을 눌러 주도록 합니다.
다운로드 페이지가 나오면, 인증서를 다운로드하고, 다운로드된 파일을 적당한 이름으로 바꿔 줍니다.
다운로드된 인증서를 더블 클릭하면, 인증서가 등록되는 데 다음과 같은 창이 뜰 것 입니다. 키체인을 로그인 으로 두고 추가 버튼을 눌러 줍니다.
2) APNs 파일 생성
등록했던 인증서에서 인증서와 키의 파일을 빼내는 작업이 필요합니다.
먼저, APs 인증서를 내보내기 해 p12 파일을 만들어 주도록 합니다. 키 인증서를 열고 키체인은 로그인, 카테고리는 인증서를 각각 선택해 줍니다.
개발용 푸쉬 인증서를 마우스 오른쪽 버튼으로 선택해 나온 메뉴에서, 내보내기를 선택합니다.
저장 창이 뜨면 적당한 이름을 입력하고 저장을 눌러 주도록 합니다.
그리고 다음 암호 입력창이 뜨는데, 적당한 값으로 입력합니다. 확인을 누르면, 맥 암호를 물을 텐데 입력 하면, 인증서에 대한 p12 파일이 만들이 집니다.
이 번에는 인증서의 키를 내 보내 도록 하겠습니다. (키 입니다. 인증서를 한 번더 선택하지 않도록 합니다.). APs 인증서의 앞쪽의 삼각형 눌러 키가 나열 되도록 합니다.
키가 나열 되면, 앞서 했던 작업과 같이 키를 마우스 오른쪽 버튼으로 선택하고 내보내기를 선택 합니다.
입력 창이 뜨면 적당한 파일 이름을 선택하고 저장 버튼을 눌러 주도록 합니다.
패스워드를 입력하고, 확인 버튼을 눌러 줍니다. 그리고 나온 맥 암호 입력 창에 암호를 입력하면, 키에 대한 p12 파일이 만들어 집니다.
자 이렇게 2개의 인증서가 만들어 지면 성공 입니다.
터미널을 열고 인증서를 pem 파일로 만들어 줍니다. (패스워드를 입력하라고 하는데, 내보내기 할 때 입력했던 패스워드를 입력해 주면됩니다.), 틀려도 파일은 생성되었지만, 과연 사용할 수 있을 런지는 모르겠습니다.
안드로이드 결제를 하면, 이를 검증하고 DB에 반영할 서버가 필요합니다. 이 node.js로 구현해 볼 생각 입니다. Android 개발자 사이트 공식 문서에는 결제 관련 데이터 접근을 Google Play Developer API로 하도록 하고 있습니다. 이 기능은 앱의 새 버전을 업로드하고 출시 하는 기능과 인앱 상품의 구매 이력을 관리할 수 있는 기능을 구현할 수 있습니다. 저는 이 중 인앱 상품 관련 API 를 이용해 볼 생각 입니다.
구글 플레이 개발자 API의 안내속 시작 하기 문서(링크) 속에는 구글 플레이 개발자 API기능에 대한 설명이 들어 있습니다. 여러분이 보는 시기에 이 문서가 어떻게 표시되는지 모르겠지만, 제가 보고 있는 문서는 한글로 번역이 되어 있습니다. 이 문서를 읽어 보면, 개발 진행사항에서 API를 통해, 개발 버전을 관리할 수 있고, 상품에 달려 있는 리플들을 가지고와 관리할 수 있는 기능, 구매 관련 기능이 있음을 설명하고 있습니다. 저는 이 중 구매 관련, (지정한 상품이 유효한지 여부)를 확인하는 기능을 구현할 생각 입니다. 이 기능을 이용해 유저가 앱에서 실제 구매 했는지 검증을 할 것 입니다.
2. 새 API 프로젝트 생성.
구글 플레이 콘솔에서, 가장 아래 톱니바퀴 모양의 설정 버튼을 눌러 줍니다.
설정 페이지가 나오면, 개발자 계정 > API 액세스 를 눌러 해당 페이지를 호출 합니다. 페이지가 나오면, 새 프로젝트를 눌러 API 프로젝트를 만들어 API 생성 페이지를 만들어 줍니다.
그렇게 API 프로젝트를 만들고 나면, Google Play Android Develope, Game Service Publishing API 두개의 프로젝트가 설정되어 있습니다. 이 것은 Goolge Play Android Developer 만 연결된 상태로 되어 있는데 그대로 두면 됩니다.
2. Autho 클라이언트 만들기.
결제등의 데이터를 확인할 때, 구글 플레이 서버는 서버라 칭하고, 구글 플레이 서버에 데이터를 요청하는 쪽을 클라이언트라고 합니다. 우리는 node js를 통해, 구글 플레이 서버에 데이터를 요청하지만, 요청자 이므로, 클라이언트라 불립니다. node js를 통해 접근할 때 사용할 클라이언트 계정을 만드는 단계 입니다.
그럼 자동으로 OAuth 클라이언트가 생성됩니다. 아이디는 바로 표시되지만, 비밀번호는 표시되지 않고, 구글 APIs로 이동한 뒤 확인하도록 되어 있습니다. Google Play Console에서 보기 버튼을 눌러 사용자 인증 페이지 창을 부릅니다.
그냥 만들어진 사용자는 지워 버립니다. 이 계정은 구글에서 사용하는 클라이언트 라이브러리를 사용할 때, 사용하게 되는 계정입니다. 우리는, nodejs REST FULL API 바식으로 Autho 2.0 인증을 사용할 것 입니다.
그럼 구글 APIs의 사용자 인증 정보 페이지가 표시되고, 클라이언트 아이디 리스트가 나타납니다. 여기서 비밀 번호를 바로 확인할 수는 없고, 연필버튼을 눌러 수정 창으로 들어가서 확인이 가능합니다. 참고로 말씀 드리면, 우리가 사용할 Google Play Android Developer는 구글 APIs 사이트의 하나의 API 입니다. (즉 바로 오셔도 됩니다.).
자 새로운 페이지에서 사용자 인증 정보 만들기 버튼을 눌러 풀다운 메뉴가 표시되면, OAuth 클라이언트 ID 항목을 선택해 계정 생성 창을 띄워 줍니다.
그럼 OAuth 클라이언트 ID 만들기 창이 생성되고, [애플리케이션 유형]은 [웹 애플리케이션]으로 선택 [이름]은 적당히, 승인된 리디렉션 URI 는 http://localhost/androidpublisher/exchange_token로 입력하고 (엔터 꼭,), 입력된 것을 확인 후 생성 버튼을 누릅니다.
3. 서비스 계정 만들기.
Autho2 로그인의 가장 큰 특징은 서버에 유저 패스워드나 비밀번호를 두지 않는 다는 점 입니다. 바로 SNS 등의 계정을 통해 로그인을 할 수 있는 데, 구글은 구글 계정을 통해 로그인을 하도록 되어 있습니다. 이 로그인을 할 때 사용하는 서비스 계정을 만들어 보도록 하겠습니다.
서비스 계정 만들기를 눌러 호출된 팝업에서, Google API 콘솔을 눌러 줍니디다.
여기서, [서비스 계정 만들기]를 눌러, 서비스 계만들기 창을 호출하도록 합니다.
적당한 값을 입력하고, [만들기] 버튼을 눌러 주도록 합니다.
이후 역할을 지정하도록 되어 있는데, 별다른 역할 없이 계속 계속을 눌러주면 되겠습니다.
4. node js로 영수증 검증 페이지를 만들기 위한 사전 정보 조사.
1) 클라이언트 아이디, 시크릿 키, 리딕렉션 URI, 서비스 계정.
이 값들은 위에서, 모두 보셨거나 직접 입력한 값들 입니다. 하지만, 언제든 다시 확인할 필요성이 있으므로, 다시 확인하는 방법을 알려 드립니다. 먼저 플레이 콘솔로 접속 합니다. ( 링크 ). 그리고, 나온 페이지 좌측에 설정을 눌러 줍니다.
나온 페이지에서, [개발자 계정] > [API 액세스] 를 선택해 나온 페이지에서, 서비스 계정을 확인해 둡니다.
그리고, 구글 플레이 콘솔에서, 보기를 눌러, 사용자 인증 정보 창이 뜨면 클라이언트 아이디의 이름 부분을 클릭해 정보 페이지를 호출합니다.
클라이언트 ID, 클라이언트 보안 비밀, 승인된 리디렉션 URI 를 확인해 둡니다.
5. node js 사전 준비.
이 내용은 express framework를 이용해, router 다루는 방법을 알아야 합니다. nodejs로 웹서버 구축까지 싣기에는 양이 너무 많습니다.
express 서버를 포트 80번으로 설치해 localhost를 기본적으로 사용할 수 있도록 합니다.
1) request 모듈 설치.
$ npm i request
서버에서 다른 웹사이트의 내용을 읽어 올 때 사용됩니다.
2) querystring 모듈 설치.
$ npm i query
GET으로 리디렉션 할 때, URL 인코딩 (특수 문자들을 주로소 넣어도 문제가 없도록) 하도록 합니다.
3) androidpublisher.js 라우트 만들어 연결 시키기. 소스는 아래와 같습 니다. 일단 동작을 하는 모듈은 넣지 않고 가장 기본적인 상태로 두었습니다.
var express = require('express');
var router = express.Router();
// 필요한 모듈을 가지고 옵니다.
var request = require('request');
var qs = require('querystring');
router.get('/:name', function( req, res, next ){
// 파라메터 값을 가지고 옵니다.
let filename = req.params.name;
// 처리 방법이 저정되지 않으면 다음으로 넘겨 버립니다.
next();
});
module.exports = router;
아래와 같이 익스프레스 서버에 연결하도록 합니다.
var androidpublisher = require('./androidpublisher');
app.use('/androidpublisher', androidpublisher);
5. node js 코드
1) 이 항목을 작성하는데, 읽어야할 기본 문서들.
이 페이지는 제가 필요한 부분들만 다루고 있습니다. 빠진 정보가 있을 수 있으므로, 공식 문서를 확인하도록 합니다. android 서버 부분은 android 개발 부분 뿐 이니라, 구글 APIs 부분도 봐야 합니다.
위 문서들 중 별다은 언급이 없이 공식 문서를 참조 했다고 하면, 웹서버 응용프로그램 (OAuth 2.0 구현) 문서라 생각하시면되겠습니다.
2) 로그인 페이지 호출 하게 하기.
OAuth 2.0의 핵심은, API를 가진 서버에, 자원을 활용하기 위해 웹브라우저를 이용하고, 우리 서버는 이 계정의 정보를 처리 하지 않음으로써, 안정성을 높이고, 우리는 계정 관리의 부담을 더는 것입니다. 즉, 우리서버를 아무리 해킹해도, 계정 정보는 남아 있지 않습니다.
이러한 작업을 할 수 있는 페이지를 호출 하고, 입력했던 리디렉션 주소로 결과를 받아 이 것이 올바르게 로그인 되었는지 한 번더 확인을 위한 토큰 교환을 시도하고, 성공하면 받은 토큰으로 API를 가진 서버의 자원에 접근할 수 있게 됩니다.
일단 저는 이 작업을 할 수 있는 페이지를 호출 하기위해 get_code 라는 페이지를 통해 페이지를 호출 할 수 있도록 하였습니다.
위 라우터에 http://localhost/androidpublisher/get_code 라고 브라우저 주소창에 입력하면, 구글 로그인 페이지가 호출 될 수 있도록 하기위해 다음과 같이 코드를 변경합니다. 앞으로 사용할 get_code와 exchange_token 메소드를 분기를 통해 처리할 수 있도록 하였습니다. 중요한 점은 recently_token 변수 인데, 잊지 않고 선언해 주도록 합니다.
router.get('/:name', function( req, res, next ){
// 파라메터 값을 가지고 옵니다.
let filename = req.params.name;
if( filename == "get_code" )
{
get_code( req, res, next );
return; // 리턴 중요.
}
else if( filename == "exchange_token" )
{
exchange_token( req, res, next );
return; // 리턴 중요.
}
// 처리 방법이 저정되지 않으면 다음으로 넘겨 버립니다.
next();
});
// 처음 코드를 가지고 오는 메소드 입니다.
function get_code( req, res, next )
{
}
// 토큰을 받고, 저장한 값.
var recently_token = "";
// 코드를 가지고 오는 메소드 입니다.
function exchange_token( req, res, next )
{
}
이렇게 입력하고, 코드를 가지고 오는 부분에서 공식 문서에서 지정한 로그인 페지를 불러 올 수 있도록, 주소를 입력합니다. 공식문서의 HTTP/REST 부분을 봐야 합니다.
코드로 다음과 같이 하면 될 것 같습니다. 여기서 사용되는 클라이언트 아이디, 클라이언트 시크릿 키, 리디게션 url은 위에서 모두 확인했던 값입니다. socpe (권한 범위는), 권한 범위 문서 (위에 링크 있음)을 참고 하시면됩니다.
// 결제 관리를 위한 상수값들 입니다. (앞서 확인했던 값들, 적당한 곳에서 상수로 선언).
var inn_client_id = "769707945781-kqe7j3bk8j3kbq3drliqv5josu74ukgn.apps.googleusercontent.com";
var inn_client_secret = "BfVV8gCKVlvGR_7G7E3IlWtN";
var inn_redirect_url = "http://localhost/androidpublisher/exchange_token";
// 처음 코드를 가지고 오는 메소드 입니다.
function get_code( req, res, next )
{
// 코드를 요청하기 위한 주소를 만들어 줍니다.
const url = "https://accounts.google.com/o/oauth2/v2/auth";
const scope = "https://www.googleapis.com/auth/androidpublisher";
// 코드를 획득하기 위한 요청을 만들어 줍니다.
let code_url = url
+ `?scope=${ qs.escape(scope) }`
+ `&access_type=offline`
// + `&include_granted_scopes=true`
+ `&redirect_uri=${ qs.escape(inn_redirect_url) }`
+ `&response_type=code`
+ `&client_id=${inn_client_id}`
;
// 리디렉션 페이지를 호출 합니다.
res.redirect( code_url );
}
이렇게 입력하면, 계정으로 로그인 창이 뜰 것 입니다.
3) 토큰 교환.
그럼 토큰을 교환하는 페이지를 만들도록 합시다. 위 로그인을 시도하면, 성공시, 입력한 리디렉션 (http://localhost/androidpublisher/exchange_token) 페이지를 마지막으로 호출 하며, 코드를 get 타입으로 서버에 넘겨 줍니다. 받은 코드로 토큰 교환을 시도할 것이며, 보안을 위해, POST 메소드, 그리고, 서버 내에서 교환할 것 입니다. POST로 다른 서버의 REST API를 호출하기 위해서는 request 모듈이 필요합니다. (그냥 참고).
exchange_token 부분을 아래와 같이 코드를 작성하면, 토큰 교환이 완료됩니다.
// 토큰을 받고, 저장한 값.
var recently_token = "";
// 코드를 가지고 오는 메소드 입니다.
function exchange_token( req, res, next )
{
let postForm = {
grant_type: "authorization_code"
, code: req.query.code
, client_id: inn_client_id
, client_secret: inn_client_secret
, redirect_uri: inn_redirect_url
};
// form 데이터 형태로 토큰을 교환할 것 이므로, 폼데이터를 만들어 줍니다.
request.post(
"https://www.googleapis.com/oauth2/v4/token"
, {form: postForm}, function( error, response, body ){
// 오류가 난 경우 오류를 출력하도록 합니다.
if( error )
{
res.end('Error!!');
return;
}
let parsed_body = JSON.parse( body );
recently_token = parsed_body.access_token;
// 오류가 없을 경우 token을 그대로 받아 화면에 출력합니다.
res.end( body );
});
}
자 그럼, nodejs를 실행하고, 웹브라우저를 통해, http://localhost/androidpublisher/get_code 라고 입력합니다.
별문제가 없다면, 아래와 같은 창을 보게 될 것 입니다. 다른 계정 사용을 클릭 합니다.
그럼 로그인을 하라고 하는데, 여기서, 구글 플레이 개발자 계정으로 사용하는 계정(등록 비용을 지불하고 등록된) 이나, 서비스로 입력했던, 계정을 입력하면 됩니다. 여기서는 위에서 만들어 두었던 서비스 계정을 사용하도록 하겠습니다. 여기서는 편의를 위해 구글 개발자 계정(결제후 등록된) 계정으로 로그인하겠습니다. (이 부분은 그냥 넘어 가는 부분이므로, 화면 첨부를 화지 않겠습니다.).
성공했다면, 대게 이런 화면을 보게 될 것 입니다. access_token만 사용할 것이므로, 일단 이 것을 내부적으로, recently_token에 저장되도록 하였습니다.
구글 에서는 인앱 결제에 대해서, 3가지 종류를 지원 합니다. 첫 번째는 1회성 결제를 하는 상품 결제, 구독을 하는 결제, 그리고, 중간 광고등을 보고 그 에 대한 보상을 처리하는 보상형 결제 등이 있습니다. 이 문서는 틈틈히 수정되고 업데이트 될 것 입니다. 첫 번째로, 1회성 결제를 설명할 것이고, 그 외에 것들은 차차 업데이트를 하게 될 것 같습니다.
2. 인앱 결제 권한 추가 및 결제 라이브러리 추가.
먼저 위 선행작업을 완료하였거나, 그에 준하는 경험을 가지고 있다고, 보고 설명을 시작합니다.
먼저, 구글 플레이 콘솔에가 APK 파일을 업로드 하고, 알파, 베타, 프로덕션 어느 버전이든, 출시를 시작합니다. 앱을 선택하고 왼쪽 메뉴 (출시 하기전 왼쪽 메뉴와 모습이 틀립니다.) 중 앱정보 > 인앱 상품 항목을 선택하면, 다음과 같은 문구를 볼 수 있습니다. 이미 APK 파일을 업로드 했지만, 인앱 결제 권한이 없어 항목을 사용할 수 없습니다.
그런 이유로, 인앱 결제 권한을 가진 APK 파일을 업로드 한 뒤에야 (알파나 베타 출시까지 할 필요는 없고, apk 파일만 업로드 하면됨) 비로서, 인앱 상품을 등록할 수 있습니다.
먼저, 앱레벨 build.gradle 파일을 열어 dependecy 에 독립 모듈이 추가될 수 있도록, 아래와 같은 구문을 추가합니다.
관리되는 제품 > 관리되는 제품 만들기를 차례로 눌러, 아래와 같이 1회성 상품을 등록할 수 있는 페이지를 호출 합니다. 호출된 페이지에서, 적당한 값을 넣어, 상품을 만들어 줍니다. 주의 할 점은 상태 값인데, 활성 APK 상태로 두어야 상품이 구매 가능한 상태가 됩니다. 모든 값을 입력했다면, 저장 버튼을 눌러 줍니다.
결제 관련 모듈을 클래스를 따로 생성해서 호출 하는 식으로 사용해야 합니다. 꼭 예제를 보면서 만드세요. 예제속 Shared-module > BillingManager.java 파일속에 해당 내용이 들어 있습니다.
PurchasesUpdatedListener 를 상속한 결제 메니저, BillingManager 클래스를 생성합니다. (implements 로 다중 상속을 합니다. - 그렇게 안하면 오류남. ) 그리고, 빌링 클라이언트 객체를 생성하면, onBillingSetupFinished 메소드가 호출 됩니다. 문서에는 이 부분이 완료되면, 이 후 결제 작업을 준비 한다고 되어 있습니다.
해당 클래스에 일단 onPurchasesUpdated 메소드를 재정의 해 줍니다. (필수 재정의 메소드).
public class BillingManager implements PurchasesUpdatedListener {
@Override
public void onPurchasesUpdated(int responseCode, @Nullable List<Purchase> purchases) {
}
}
아래는 billingClient를 생성하고, 구글 플레이 스토어와 연결이 되었을 때, 처리하는 코드 구글 플레이 스토어와 상태가 해제 되었을 때 처리하는 코드 입니다. 상속한 onBillingServiceDisconnected 메소드는 구글 플레이 스토어와의 접속을 잃었을 때 호출된다고 합니다. 이 때는 startConnection() 메소드를 재 호출 해 줘야 한다고 합니다. 그래서 접속이 끊어 졌는 지 확인할 수 있는 변수, isConnected 가 필요합니다. 공식예제 에서는 결제 모듈을 쓰레드에서 Rennable에서 처리를 하고 있습니다.
일단 저는 메뉴얼 대로 메인 쓰레드에서 처리하도록 구현할 것 입니다. 그래서 아래와 같은 소스가 됩니다. 여기까지 소스를 입력하고, 한 번 실행해 보면, 오류 코드3을 만나고 접속에 실패할 수 있습니다. 이 이유는 실제 기기에서만, 결제 모듈이 동작 하기 때 문입니다. (이 것 때문에 1시간을 헤메임).
public class BillingManager implements PurchasesUpdatedListener {
// 필요 상수들.
final String TAG = "IN-APP-BILLING";
// 초기화 시 입력 받거나 생성되는 멤버 변수들.
private BillingClient mBillingClient;
private Activity mActivity;
// 현재 접속 상태를 나타 냅니다.
public enum connectStatusTypes { waiting, connected, fail, disconnected }
public connectStatusTypes connectStatus = connectStatusTypes.waiting;
// 생성자.
public BillingManager( Activity _activity )
{
// 초기화 시 입력 받은 값들을 넣어 줍니다.
mActivity = _activity;
Log.d(TAG, "구글 결제 매니저를 초기화 하고 있습니다.");
// 결제를 위한, 빌링 클라이언트를 생성합니다.
mBillingClient = BillingClient.newBuilder(mActivity).setListener(this).build();
// 구글 플레이와 연결을 시도합니다.
mBillingClient.startConnection(new BillingClientStateListener() {
// 결제 작업 완료 가능한 상태.
@Override
public void onBillingSetupFinished(int responseCode) {
// 접속이 성공한 경우, 처리.
if (responseCode == BillingClient.BillingResponse.OK) {
connectStatus = connectStatusTypes.connected;
Log.d(TAG, "구글 결제 서버에 접속을 성공하였습니다.");
}
// 접속이 실패한 경우, 처리.
else {
connectStatus = connectStatusTypes.fail;
Log.d(TAG, "구글 결제 서버 접속에 실패하였습니다.\n오류코드:" + responseCode);
}
}
// 결제 작업 중, 구글 서버와 연결이 끊어진 상태.
@Override
public void onBillingServiceDisconnected() {
connectStatus = connectStatusTypes.disconnected;
Log.d(TAG, "구글 결제 서버와 접속이 끊어졌습니다.");
}
});
}
@Override
public void onPurchasesUpdated(int responseCode, @Nullable List<Purchase> purchases) {
}
}
위 소스를 이용해 클래스를 만들 고 있을 액티비티에서 호출 하면됩니다.
구글 결제 시스템이 초기화 되면, 구글에 등록된 상품의 정보를 요청할 수 있습니다. 이를 통해 해당 아이디들이 유효한지 확인할 수 있습니다. String List를 이용해 Sku 배열을 만들고, 요청할 상품들의 아이디를 추가해 줍니다. 그리고, 해당 상품들의 자세한 정보를 받아 오면됩니다.
구글 플레이 콘솔로가 등록된 상품들의 정보를 확인합니다. (물론 자신이 등록했으므로, 알고 있을 경우, 굳이 확인하지 않아도 됩니다.) 구글 플레이 콘솔 접속, 왼쪽메뉴에서 앱 정보 > 인앱 상품 > 관리되는 제품을 차례로 선택하면 등록된 상품을 확인할 수 있습니다.
그리고, 지금까지 만들어 왔던 빌드 매니저에, 구입 가능한 상품의 정보를 받아 오는 코드를 추가 합니다.
// 구입 가능한 상품의 리스트를 받아 오는 메소드 입니다.
public void get_Sku_Detail_List()
{
// 구글의 상품 정보들의 ID를 만들어 줍니다.
List<String> Sku_ID_List = new ArrayList<>();
Sku_ID_List.add( "gold_100" );
// SkuDetailsParam 객체를 만들어 줍니다. (1회성 소모품에 대한)
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList( Sku_ID_List ).setType(BillingClient.SkuType.INAPP);
// 비동기 상태로 앱의, 정보를 가지고 옵니다.
mBillingClient.querySkuDetailsAsync(params.build() , new SkuDetailsResponseListener()
{
@Override
public void onSkuDetailsResponse(int responseCode, List<SkuDetails> skuDetailsList) {
// 상품 정보를 가지고 오지 못한 경우, 오류를 반환하고 종료합니다.
if( responseCode != BillingClient.BillingResponse.OK)
{
Log.d(TAG, "상품 정보를 가지고 오던 중 오류를 만났습니다. 오류코드 : " + responseCode);
return;
}
// 하나의 상품 정보도 가지고 오지 못했습니다.
if( skuDetailsList == null )
{
Log.d(TAG, "상품 정보가 존재하지 않습니다.");
return;
}
// 응답 받은 데이터들의 숫자를 출력 합니다.
Log.d(TAG, "응답 받은 데이터 숫자:" + skuDetailsList.size());
// 받아 온 상품 정보를 차례로 호출 합니다.
for( int _sku_index = 0;_sku_index < skuDetailsList.size(); _sku_index++)
{
// 해당 인덱스의 객체를 가지고 옵니다.
SkuDetails _skuDetail = skuDetailsList.get( _sku_index );
// 해당 인덱스의 상품 정보를 출력하도록 합니다.
Log.d( TAG, _skuDetail.getSku() + ": " + _skuDetail.getTitle() + ", " + _skuDetail.getPrice() + ", " + _skuDetail.getDescription() );
}
// 받은 값을 멤버 변수로 저장합니다.
mSkuDetailsList = skuDetailsList;
}
}
);
}
그리고, 실행해 보면, 지정한 상품의 정보를 받아 올 수 있습니다. 당연한 이야기 지만 구글 결제 시스템과 접속이 성공적으로 이뤄진 상태에서 실행해야 합니다.
드디어 결제를 요청하는 부분 입니다. 당연한 이야기지만, 우리는 콜백만 받으면, 되므로, 결제는 사실상 끝입니다. 앞서 받았던, List<SkuDetails> 에서 구매를 원하는 혹은 유저가 선택한, SkuDetails 을 선택해 결제 요청창을 띄워 주시면 됩니다. 공식 메뉴얼에는 launchBillingFlow 에 1개의 인수만 넣도록 되어 있지만, 실제로 해 보면, 첫 인수에 Activity를 요청하고 있습니다. 위 단계들을 모두 거친뒤 요청해야 합니다.
// 실제 구입 처리를 하는 메소드 입니다.
public void purchase( SkuDetails skuDetails )
{
BillingFlowParams flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails)
.build();
int responseCode = mBillingClient.launchBillingFlow(mActivity, flowParams);
}
이 코드를 실행해 보면 아래와 같은 결제 창을 보실 수 있을 것 입니다.
처음 결제 클래스를 만들 때, PurchasesUpdatedListener 클래스를 상속하기 위해, 기본적으로 정의 해 두었던, onPurchasesUpdated 메소드가 생각이 나시나요? 이 메소드는 결제 처리 결과를 받는 메소드이며, 이제 이 것을 정의 하므로써, 결제 부분은 코드가 끝이 납니다. 이 메소드가 호출되는 시점은, 유저가 결제를 하던, 결제 실패를 하던, 어떤 이유로 결제가 실패해 결제 창이 닫히는 경우 발생합니다. 코드는 간단하므로, 더 설명 드리지 않겠습니다.
// 결제 처리를 하는 메소드.
@Override
public void onPurchasesUpdated(int responseCode, @Nullable List<Purchase> purchases) {
// 결제에 성공한 경우.
if( responseCode == BillingClient.BillingResponse.OK && purchases != null )
{
Log.d( TAG, "결제에 성공했으며, 아래에 구매한 상품들이 나열될 것 입니다." );
for( Purchase _pur : purchases )
{
Log.d( TAG, "결제에 대해 응답 받은 데이터 :"+ _pur.getOriginalJson() );
}
}
// 사용자가 결제를 취소한 경우.
else if( responseCode == BillingClient.BillingResponse.USER_CANCELED )
{
Log.d( TAG, "사용자에 의해 결제가 취소 되었습니다." );
}
// 그 외에 다른 결제 실패 이유.
else
{
Log.d( TAG, "결제가 취소 되었습니다. 종료코드 : " + responseCode );
}
}
5. 소모품 소모.
한 번 구매후 효과가 영구히 지속되는 경우, 아이템을 한 번만 구매해, 이 과정이 필요 없습니다. 하지만 대부분의 경우, 아이템을 재 구매해야 하므로, 소모품을 소모 하지않으면 아이템의 재 구매가 불가능합니다. 그냥 아이템 구매후 소모품을 소모하도록 하면되며, 비동기 처리를 하는 메소드 이므로, 아주 편리 합니다.
1) 소모품 소모후 콜백 메소드.
콜백 메소드를 처리하는 ConsumeResponseListener 객체를 멤벼 번수로 선언하고, 이것을 생성자에서 정의해 주도록 합니다.
// 멤버 변수로 선언해 줍니다.
private ConsumeResponseListener mConsumResListnere;
// 생성자에 리스너를 정의해 변수에 저장합니다.
mConsumResListnere = new ConsumeResponseListener() {
@Override
public void onConsumeResponse(int responseCode, String purchaseToken) {
// 성공적으로 아이템을 소모한 경우.
if( responseCode == BillingClient.BillingResponse.OK )
{
Log.d( TAG, "상품을 성공적으로 소모하였습니다. 소모된 상품=>" + purchaseToken );
return;
}
// 성공적으로 아이템을 소모한 경우.
else
{
Log.d( TAG, "상품 소모에 실패하였습니다. 오류코드 ("+responseCode+"), 대상 상품 코드:" + purchaseToken );
return;
}
}
};
2) 결제 후 소모 메소드 호출 하기.
기존의 결제 리스너에, 성공 메시지가 오면, 소모품으로 아이템을 소모 하도록 호출합니다.
// 결제 처리를 하는 메소드.
@Override
public void onPurchasesUpdated(int responseCode, @Nullable List<Purchase> purchases) {
// 결제에 성공한 경우.
if( responseCode == BillingClient.BillingResponse.OK && purchases != null )
{
Log.d( TAG, "결제에 성공했으며, 아래에 구매한 상품들이 나열될 것 입니다." );
for( Purchase _pur : purchases )
{
Log.d( TAG, "결제에 대해 응답 받은 데이터 :"+ _pur.getOriginalJson() );
mBillingClient.consumeAsync( _pur.getPurchaseToken(), mConsumResListnere );
}
}
// 사용자가 결제를 취소한 경우.
else if( responseCode == BillingClient.BillingResponse.USER_CANCELED )
{
Log.d( TAG, "사용자에 의해 결제가 취소 되었습니다." );
}
// 그 외에 다른 결제 실패 이유.
else
{
Log.d( TAG, "결제가 취소 되었습니다. 종료코드 : " + responseCode );
}
}
실행을 해 보면, 매우 결제가 잘 됩니다. 이 후 이 결제가 유효한 결제인지 서버 혹은 이 앱내에서 하게 됩니다. 앱내에서 검증은 신뢰할 수 없고, 저는 필요치 않아 다음에 서버 검증에 대한 글을 쓰게 될 것 같습니다. 앱내 검증이 필요하신 분은 아래 문서의 후반부를 참고 하시기 바랍니다.
그럼 빌링매니저 풀 소스를 남겨 드립니다. 많은 분들에게 도움이 되었으면 좋겠습니다.
package com.tistory.nicgoon.android.inapptest.billing;
import android.app.Activity;
import android.support.annotation.Nullable;
import android.util.Log;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.ConsumeResponseListener;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.SkuDetails;
import com.android.billingclient.api.SkuDetailsParams;
import com.android.billingclient.api.SkuDetailsResponseListener;
import java.util.ArrayList;
import java.util.List;
public class BillingManager implements PurchasesUpdatedListener {
// 필요 상수들.
final String TAG = "IN-APP-BILLING";
// 초기화 시 입력 받거나 생성되는 멤버 변수들.
private BillingClient mBillingClient;
private Activity mActivity;
private ConsumeResponseListener mConsumResListnere;
// 현재 접속 상태를 나타 냅니다.
public enum connectStatusTypes { waiting, connected, fail, disconnected }
public connectStatusTypes connectStatus = connectStatusTypes.waiting;
// 결제를 위해 가지온 상품 정보를 저장한 변수 입니다.
public List<SkuDetails> mSkuDetailsList;
// 생성자.
public BillingManager( Activity _activity )
{
// 초기화 시 입력 받은 값들을 넣어 줍니다.
mActivity = _activity;
Log.d(TAG, "구글 결제 매니저를 초기화 하고 있습니다.");
// 결제를 위한, 빌링 클라이언트를 생성합니다.
mBillingClient = BillingClient.newBuilder(mActivity).setListener(this).build();
// 구글 플레이와 연결을 시도합니다.
mBillingClient.startConnection(new BillingClientStateListener() {
// 결제 작업 완료 가능한 상태.
@Override
public void onBillingSetupFinished(int responseCode) {
// 접속이 성공한 경우, 처리.
if (responseCode == BillingClient.BillingResponse.OK) {
connectStatus = connectStatusTypes.connected;
Log.d(TAG, "구글 결제 서버에 접속을 성공하였습니다.");
}
// 접속이 실패한 경우, 처리.
else {
connectStatus = connectStatusTypes.fail;
Log.d(TAG, "구글 결제 서버 접속에 실패하였습니다.\n오류코드:" + responseCode);
}
}
// 결제 작업 중, 구글 서버와 연결이 끊어진 상태.
@Override
public void onBillingServiceDisconnected() {
connectStatus = connectStatusTypes.disconnected;
Log.d(TAG, "구글 결제 서버와 접속이 끊어졌습니다.");
}
});
// 소모성 상품을 소모한 후, 응답 받는 메소드 입니다.
mConsumResListnere = new ConsumeResponseListener() {
@Override
public void onConsumeResponse(int responseCode, String purchaseToken) {
// 성공적으로 아이템을 소모한 경우.
if( responseCode == BillingClient.BillingResponse.OK )
{
Log.d( TAG, "상품을 성공적으로 소모하였습니다. 소모된 상품=>" + purchaseToken );
return;
}
// 성공적으로 아이템을 소모한 경우.
else
{
Log.d( TAG, "상품 소모에 실패하였습니다. 오류코드 ("+responseCode+"), 대상 상품 코드:" + purchaseToken );
return;
}
}
};
}
// 구입 가능한 상품의 리스트를 받아 오는 메소드 입니다.
public void get_Sku_Detail_List()
{
// 구글의 상품 정보들의 ID를 만들어 줍니다.
List<String> Sku_ID_List = new ArrayList<>();
Sku_ID_List.add( "gold_500" );
// SkuDetailsParam 객체를 만들어 줍니다. (1회성 소모품에 대한)
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList( Sku_ID_List ).setType(BillingClient.SkuType.INAPP);
// 비동기 상태로 앱의, 정보를 가지고 옵니다.
mBillingClient.querySkuDetailsAsync(params.build() , new SkuDetailsResponseListener()
{
@Override
public void onSkuDetailsResponse(int responseCode, List<SkuDetails> skuDetailsList) {
// 상품 정보를 가지고 오지 못한 경우, 오류를 반환하고 종료합니다.
if( responseCode != BillingClient.BillingResponse.OK)
{
Log.d(TAG, "상품 정보를 가지고 오던 중 오류를 만났습니다. 오류코드 : " + responseCode);
return;
}
// 하나의 상품 정보도 가지고 오지 못했습니다.
if( skuDetailsList == null )
{
Log.d(TAG, "상품 정보가 존재하지 않습니다.");
return;
}
// 응답 받은 데이터들의 숫자를 출력 합니다.
Log.d(TAG, "응답 받은 데이터 숫자:" + skuDetailsList.size());
// 받아 온 상품 정보를 차례로 호출 합니다.
for( int _sku_index = 0;_sku_index < skuDetailsList.size(); _sku_index++)
{
// 해당 인덱스의 객체를 가지고 옵니다.
SkuDetails _skuDetail = skuDetailsList.get( _sku_index );
// 해당 인덱스의 상품 정보를 출력하도록 합니다.
Log.d( TAG, _skuDetail.getSku() + ": " + _skuDetail.getTitle() + ", " + _skuDetail.getPrice() + ", " + _skuDetail.getDescription() );
Log.d( TAG, _skuDetail.getOriginalJson() );
}
// 받은 값을 멤버 변수로 저장합니다.
mSkuDetailsList = skuDetailsList;
}
}
);
}
// 실제 구입 처리를 하는 메소드 입니다.
public void purchase( SkuDetails skuDetails )
{
BillingFlowParams flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails)
.build();
int responseCode = mBillingClient.launchBillingFlow(mActivity, flowParams);
}
// 결제 처리를 하는 메소드.
@Override
public void onPurchasesUpdated(int responseCode, @Nullable List<Purchase> purchases) {
// 결제에 성공한 경우.
if( responseCode == BillingClient.BillingResponse.OK && purchases != null )
{
Log.d( TAG, "결제에 성공했으며, 아래에 구매한 상품들이 나열될 것 입니다." );
for( Purchase _pur : purchases )
{
Log.d( TAG, "결제에 대해 응답 받은 데이터 :"+ _pur.getOriginalJson() );
mBillingClient.consumeAsync( _pur.getPurchaseToken(), mConsumResListnere );
}
}
// 사용자가 결제를 취소한 경우.
else if( responseCode == BillingClient.BillingResponse.USER_CANCELED )
{
Log.d( TAG, "사용자에 의해 결제가 취소 되었습니다." );
}
// 그 외에 다른 결제 실패 이유.
else
{
Log.d( TAG, "결제가 취소 되었습니다. 종료코드 : " + responseCode );
}
}
}
android 앱을 만든 뒤 실제 상품으로 등록되는 Production 버전으로 앱을 등록하면, 우리가 앱을 테스트하는 동안 실제 다운로드등이 일어나는 문제가 발생합니다. 특히 in-app billing 상품을 구매할 때, 실제 구매가 일어나, 테스트틀 위해 인앱 상품을 구해매 플레이스토어에 수수료를 물거나, 환불해야 할 지도 모릅니다. 이런 문제를 해결하기 위해, 구글플레이 스토어에서는 Alpha, Beta 버전이후 실제 제품을 출시하는 Production 단계로 업그레이드 하는 시스템을 가지고 있습니다.
Alpha : 비 공개 테스트를 위한 시스템 Beta : 공개 테스트를 위한 시스템.
저는 구매 테스트를 하기 위해 베타 버전으로 앱을 등록할 예정이고, 이 과정을 블로그에 남겨 둡니다. 다른 분들도 이 글이 도움이 되었으면 합니다.
1. 등록을 위한 간단한 앱 만들기.
앱을 만드는 방법은 생략합니다 저는 간단히, 버튼을 누르면 안녕하세요. 라는 메시지가 표시되는 앱을 만들었습니다.
2. 빌드하기.
안드로이드 스튜디오 상단 메뉴의 빌드 항목을 이용해 빌드를 시도합니다. 처음 빌드를 하신다면, 아래 링크를 참고해 주세요.
3. 플레이 스토어에 애플리케이션 만들기.
이 앱을 플레이 스토어에 앱으로 등록하기 위해 플레이 스토어 내에도 애플리케이션을 만들어 주어야 합니다. 플레이스토어에 접속해, [애플리케이션 만들기] 버튼을 눌러 주도록 합니다. (저는 많은 앱들 중 리젝된 앱이 있어, 경고가 떠 있지만, 여러분은 그런 창이 아마도 없을 것 입니다.)
그리고, 호출되는 창에 값들을 적당히 입력하고, 만들기 버튼을 입력해 줍니다.
그리고 나면 아래와 같이 스토어 등록정보를 입력하는 창이 나타납니다. 꼼꼼히 확인해서, 입력해 줍니다. 모든 입력사항을 입력하고, 임시 저장을 눌러 다음 페이지로 넘어 갑니다. 단 *콘텐츠 등급은 작성하지 않고 넘어 갑니다.
4. APK 파일 업로드.
플레이 콘솔의 왼쪽 메뉴의 앱 버전 버튼을 눌러 줍니다.
이제 앱버전 페이지가 보이는데 우리는 공개 트랙(베타) 항목에 apk 파일을 업로드 할 것 입니다. 해당 항목의 관리 버튼을 눌러 주도록 합니다.
베타 창에서 새 버전 출시하기 버튼을 눌러 새 버전을 출시하도록 합니다.
구글에서 앱 서명 키 관리 미 보호 옵션이 뜨는 데, 그냥 계속을 눌러 줍니다.
그럼 라이센스 관련 창이 뜨는데 언제나 처럼 다음으로 넘어 가줍니다. 그럼 그 아래 apk를 등록할 수 있는 항목이 활성화 됩니다. 여기서 중요한 것은 apk 파일을 업로드하는 것 입니다. 붉은 색으로 표시한 파일 찾아보기 버튼을 눌러 APK 파일을 추가한 뒤 그 아래 항목은 적당한 값들로 채워 넣습니다. 그 후 저장 버튼을 작성한 문서가 일단 저장됩니다. 그리고 검토 버튼이 활성화 되면, 검토 버튼을 눌러 다음 단계로 진행을 해 주도록 합니다.
저는 다음 화면으로 왔을 때 경고가 떴습니다. 일단 어떤 경고인지 확인해 보겠습니다. 경고 메시지 보기 버튼을 눌러 줍니다.
테스터가 한 명도 등록되지 않아 발생한 오류 입니다.
5. 테스터 설정하기.
구글 플레이 콘솔에서, 앱버전을 눌러 나온 창에서, 공개 트랙의 관리 버튼을 눌러 주도록 합니다.
그러면, 테스트 참여 대상 관리 항목이 나오는데, 테스트 방법 선택에 공개 베타 테스트를 선택하고, 최대 테스터 수를 1000명으로 합니다. 그리고 저장 버튼을 눌러 줍니다.
저장이 완료 되면, 그 아래 버전 수정 버튼을 눌러 다음 페이지로 이동합니다.
앱 버전 창이 나오면 검토 버튼을 눌러, 다음으로 이동하도록 합니다.
다음 창에서 확인해 보면, 앱 버전을 출시 준비가 되었지만, 아직 베타 출시 시작 버튼이 활성화 되지 않았습니다.
6. 콘텐츠 등급
왼쪽 메뉴의 콘텐츠 등급 버튼을 눌러 나온 페이지에 계속 버튼을 눌러 콘텐츠 등급을 작성합니다.
아래와 같은 창이 뜨면, 이메일 주소를 입력하고, 앱 카테고리 선택 항목에서, 참고자료, 뉴스 또는 교육을 선택합니다. (물론 여러분이 필요에 의해 작성할 때는 상황에 맞는 카테고리를 선택하면 되겠습니다.)
설문지 창이 나오면, 적절한 항목을 선택한 뒤, 설문지 저장 버튼을 눌러 줍니다.
설문지 저장 버튼을 누르고, 저장이 완료되면, 등급계산 버튼이 활성화 되는 데, 이 버튼을 눌러 콘텐츠 등급을 산정하도록 합니다.
그러면, 주요 국가의 등급 레벨이 나오는데, 특별한 사항이 없다면, 등급 적용 버튼을 눌러, 등급을 적용합니다.
7. 가격 및 배포.
구글 플레이 콘솔 왼쪽 메뉴에 가격 및 배포 메뉴를 선택하고, 뜬 창에서, 적절한 항목을 선택하고, 임시 저장 버튼을 누릅니다. 추천 항목은 배포 방법 선택 무료, 국가는 모두 사용 가능, 주로 어린이를 대상으로 앱 항목은 아니오, 광고 포함 유무는 아니오, 콘텐츠 가이드 라인 체크, 미국 수출 법규 체크 입니다.
8. 베타 테스트 출시하기.
위의 사항들을 잘 마무리 한다면, 필수 항목에 다음과 같이 모두 녹색으로 체크 될 것 입니다.
그럼 왼쪽 메뉴의 앱 버전 메뉴를 선택하고, 공개 트랙의 관리 버튼을 눌러 줍니다.
다음 창의 출시되지 않은 버전이 있습니다. 항목에서 버전수정 버튼을 눌러 주도록합니다.
앱 버전 창이 뜨면 검토 버튼을 눌러 줍니다.
그럼 언제나 비활성화 상태에 있던 베타 출시 시작 버튼이 활성화 되어 있습니다. 이 버튼을 눌러 베타 출시를 시작하도록 합니다.
자 그럼 아래와 같이 베타 버전이 출시가 완료 되었습니다. 일단 상태는 게시 보류중 상태 입니다. 이 것은, 구글 플레이 측에서, 이 앱이 게시가능한 앱인지 검토를 하고 있는 상태 입니다. 몇 분 혹은 몇 일간 이 상태가 계속 될 수 있습니다.
8. 베타 테스트 참여 주소 확인하기.
베타 버전 이라도 출시되면, 왼쪽 메뉴가 기존과 다르게 변경이 됩니다. 이 중 출시 관리 > 앱 버전 메뉴 항목을 선택해 줍니다.
iOS에 비하면 안드로이드는 출시 과정이 매우 간단한 편 입니다. 안드로이드 스튜디오 기준으로 설명을 하도록 하겠습니다.
1. 키스토어 생성
키스토어는 일종의 도장같은 파일 입니다. 이 것을 통해, 앱의 코드를 봉인 한다고 생각하시면 편하실 것 같습니다.
우선 메뉴를 통해, 코드에 사인하는 메뉴를 선택합니다.
이 후 나오는 창에서는 APK 파일을 선택하도록 합니다. 그리고, next를 선택합니다.
그 다음 키스토어를 선택하는 창이 뜨는 데 우리는 키스토어가 없으므로, 키스토어를 생성하도록 합니다.
그러면 키스토어를 생성하는 창이 호출 됩니다.
키스토어는 APK를 사인할 수 있는 키가 잔뜩 들어 있는 파일 입니다. 앱은 처음에 출시할 때, 사인된 키로만 싸인해 올려야 하는데, 이 키가 여러개 들어 있어 키스토어 입니다. 주의 할 점은 구조상, 키스토어에 여러개의 키를 넣을 수 있지만, 키스토어에 1개의 키만 넣는 것이 좋습니다. 키스토어 하나가 망가지면, 모든 키들을 복구 할 방법이 없을 테니까요. 키스토어에 키스토어 하나가 좋습니다.
아래와 같이 키스토어 생성창이 떴다면, 키스토어 패스워드와 그 패스워드 확인을 입려가혹, 그아래 키 별칭(Alias)에 이키의 용도를 알기쉽게 적어줍니다. 그리고, 이키의 패스워드 와 그 패스워드 확인값을 잘 입력해 줍니다. Validdity는 이 키의 유효한 햇(year)수 입니다. 이 기간이 지나면 사용불가할 것 같은데, 직접 확인해 보지는 못했습니다. 그 외 아래 항목에 적당한 값일 입력하고, OK 버튼을 눌러줍니다.
* 패스워드를 입력할 때, 절대로 한글 상태로 놓고 입력하지 마세요. 한글로 패스워드가 들어 갑니다. (맥에서, 윈도우 에서도 일지 모름).
그럼 키스토어가 생성되고, 자동으로 생성했던 키스토어에서 필요한 정보들이 입력됩니다. 만약 기존의 키스토어를 사용하려면, Choose existing 버튼을 눌러 기존의 키 스토어를 선택하면됩니다. 자동으로 선택되어 있으므로, 바로 next를 눌러 다음 단계로 넘어 갑니다.
결과물 출력 폴더와 빌드를 릴리즈, 디버그 방식 중 선택하는 옵션이 있습니다. debug모드는 해당 파일을 디버깅해 볼 수 있는 파일입니다. 우리는 출시예정이므로, release를 선택해야 합니다. 가장 아래 Signature Versions 라는 선택항목인데, v1을 선택해 줍니다. (v1 빼고 업로드 하면, no jar .... 이라는 오류가 뜹니다.)
그럼 빌드가 시작되고, 완료되면, 메시지 창이 떠 줍니다. 이 창에서 locate 를 선택하면, 빌드된 결과물이 있는 폴더로 이동합니다. (우 하단에 해당 다이얼로그가 표시됩니다.)
이 앱을 스마트폰으로 이동해 실행하면, 앱을 설치할 수 있습니다. (에뮬레이터는 드래그 & 드롭으로 복사 및 설치 가능)
FCM에는 모바일에 필요한 다양한 서비스를 제공하고 있다. 그 중 가장 많이 사용하는 서비스 중 하나인 Cloud Messaging 서비스의 서버를 node.js를 통해, 제작해 보겠습니다. 이 문서는 비공개 키를 통해 SDK를 초기화해 send 메소드를 통해 보내기를 합니다. 만약 그렇지 않은 경우, 토큰을 받거나, 유지하는 기능까지 구현해야 할 수도 있습니다. 다른 방법으로 하려면 참고해 주시기 바랍니다.
관련 문서인 서버 Firebase Admin SDK 추가 문서를 읽어 보면 다양한 경우에 대한 SDK 서버 설치 관련 옵션이 존재 합니다. 우리는 node.js 로 설치 할 것이 므로, node.js 관련 부분만 보면 되겠습니다.
1) firebase 관리자 설치.
$ npm install firebase-admin --save
2) sdk 초기화를 위한 비 공개 키 다운로드.
먼저, 프로젝트 설정으로 이동합니다.
서비스 계정 -> 새 비공개 키 생성을 차례로 선택해 줍니다.
새 비공개 키 생성 창이 뜨면 키 생성 버튼을 눌러 키를 생성합니다.
그럼 키가 다운로드 되는 데 이것을 적당한 위치에 저장합니다.
3) 소스 추가. 위에서 확인한 내용을 토대로, 아래 소스에 값을 쉽게 넣을 수 있으리라 생각됩니다.
var admin = require('firebase-admin');
var serviceAccount = require('path/to/serviceAccountKey.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
위 코드를 보면, Admin SDK 초기화 부분에, databaseURL: 'https://<DATABASE_NAME>.firebaseio.com' 부분이 있는데 위 코드에는 왜 빠져 있지 라고 생각하시는 분들이 있을 수 있습니다. 보내기 요청 승인 이라는 문서가 있는 데 이 문서는 지금처럼 자동으로 보내지 않는 경우 토큰으로 처리해야함을 설명하고 있는 문서 이며, 지금과 같이 Admin SDK를 사용하는 경우, Admin ADK를 초기화 하는 설명이 나와 있습니다. 이 부분에, 데이터 베이스 부분을 넣지 않고 설정하고 있습니다. 확인해 본 결과, 메시지를 보낼 때는 이 부분이 필요 없습니다.
2. 보내기
메시지 보내는 것은 보낼 메시지를 만드는 부분과 이를 보내는 부분을 구현하면됩니다. 보내는 방법은 개별기기에, 주제별, 조건별로 보낼 수 있습니다. 각각의 방법은 firebase 공식문서를 참고해 주시기 바랍니다. 여기서는 한 기기에 보내기만 합니다. 소스는 아래와 같으며, 설명은 불필요 할 것으로 보입니다.
// 보낼 메시지를 작성하는 부분 입니다.
var fcm_target_token = "cdtmyCPou2I:APA91bHYP7ltSi6Q_nd8lGVJnwrhYcHcQJIRFaseE494PYe1mQKBe1QfhW-8i_gEpJZqSM1XcWZrqkXEVxWt9BTGpQctJ_RVy9V3OZU-ltcITNpANsB0HsCa9gvRpNM3iOGKoDjOGeqa";
var fcm_message = {
notification: {
title: '시범 데이터 발송',
body: '클라우드 메시지 전송이 잘 되는지 확인하기 위한, 메시지 입니다.'
},
data:{
fileno:'44',
style:'good 입니다요~'
},
token:fcm_target_token
};
// 메시지를 보내는 부분 입니다.
fcm_admin.messaging().send(fcm_message)
.then(function( response ){
console.log('보내기 성공 메시지:' + response);
})
.catch(function( error ){
console.log( '보내기 실패 메시지:' + error );
});
서버에서 안드로이드로 메시지를 보내는 방법을 포스팅합니다. 파이어 베이스에서는 쉽다고 홍보하고 있지만, 프로그래머 대부분은 여기서 머리를 싸매게 됩니다. 일단 파이어 베이스 문서(공식)가 별로 자세하지 않고, 설명 띄엄 띄엄하고, 솔루션도 제공하지 않습니다. 따라서 가장 최신판 책을 한 권 구매하시기 바라며, 이 내용은 그저 트러블 해결 용도로 이용하시기 바랍니다.
참고 사항.
참고 사항 1. 메시지를 보낼 때, 앱이 켜져 있으면, 내부 앱에 메시지가 뿌려지고, 앱이 꺼져 있으면, 상단 알림으로, 메시지가 보내저 소리등이 나게 됩니다. (기본적으로.).
참고 사항 2. 설정에서 알림 받지 않기를 해도 앱이 켜져 있을 때는 알림을 받습니다. 꺼져 있을 때만, 받지 않습니다. 그러므로, 이 것을 이용해 챗팅을 구현해도 상관이 없습니다.
참고 사항 3. 알림을 받기로 허용하면, 앱을 한 번 실행한 뒤 부터 알림이 도착하기 시작합니다. (참고해 주세요).
1. FCM 에서 프로젝트 만들기.
1) 계정 만들기. (생략).
아마 별로 어려울 것이 없을 것이라 생각이 됩니다.
2) 프로젝트 생성.
이름을 잘 정해서 하나 만들어 주시기 바랍니다.
2. Android 앱 추가.
1) Android 앱 등록.
패키지 명을 잘 등록해 줍니다.
2) 구성파일 다운로드.
설명에도 나와 있지만, 프로젝트 보기로 전환하고, (중요), 다운로드 파일을 google-services.json 파일을 App 폴더 하위에 추가하도록 합니다.
3) Firebase SDK 추가.
이 부분은 설명은 참 쉽게 되어 있는 데 (추가하는 것 자체는 쉽습니다.). 입력값 그대로 넣으면 빨간 줄이 쭉쭉 그여질 수 있습니다. 구글 SDK가 업데이트 되면, Firebase SDK 업데이트도 다시 되어야 하는데, 파이어 베이스는 이런일에 둔감합니다. 그래서, 버전이 잘 안맞습니다. 그래서 트러블이 생길 수 있는 데, 이것들은 검색을 통해 확인해야 합니다.
실제 메시지를 받는 서비스의 클래스 입니다. 위 이름이 예제 파일과 같은 이름인데, 바꾸지 않는 것이 좋습니다. 파이어 베이스를 몇 번해 보았지만, 바꾸지 않는 것을 권해 드립니다. 서비스를 만들 때, FirebaseMessagingService클래스를 상속해서 만들도록 합니다.
생성자 외 메소드는 모두 지웁니다. 그리고, Ctrl+o를 눌러, onNewToken, onMessageReceived 가상 메소드들을 재정의 하도록 합니다. (선택하면 자동완성됨).
onNewToken은 메시지에 사용될 토큰을 새로 발급받았을 때, 호출되는 메소드로 이 토큰은 각각의 기기를 식별해 사용하는 메소드 입니다. onMessageReceived 는 메시지를 받았을 때 호출되는 메소드 입니다. 아래는 해당 서비스의 소스 코드 입니다.
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "FCM";
public MyFirebaseMessagingService() {
}
// 새로운 토큰을 확인했을 때 호출되는 메소드.
@Override
public void onNewToken(String token) {
super.onNewToken(token);
// 토큰 정보를 출력합니다.
Log.e(TAG, "onNewToken 호출됨: " + token);
}
// 새로운 메시지를 받았을 때 호출되는 메소드.
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
// 받은 메시지를 출력합니다.
Log.e( TAG, "onMessageReceived 호출됨" + remoteMessage );
}
}
3) 서비스 xml등록.
우리가 위에서 서비스로 앱을 등록해 주었기에, 서비스를 xml에 등록할 필요가 없을 것 같지만, 그렇게 해두면 푸쉬 메시지를 받지 못합니다. 아래 코드를 확인해 푸쉬 메시지를 등록할 수 있게 해 줍니다.