ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [๊ตฌํ˜„] Spring์—์„œ Firebase ํ‘ธ์‹œ์•Œ๋ฆผ ๊ตฌํ˜„ํ•˜๊ธฐ
    SPRING/PROJECT 2023. 9. 26. 02:52
    ํ‘ธ์‹œ ์•Œ๋žŒ ๊น”๊น”

    Firebase ํ”„๋กœ์ ํŠธ ์„ธํŒ…ํ•˜๊ธฐ

    https://firebase.google.com/?hl=ko 

    Firebase | Googleโ€™s Mobile and Web App Development Platform

    Discover Firebase, Googleโ€™s mobile and web app development platform that helps developers build apps and games that users will love.

    firebase.google.com

    firebase ์›น์‚ฌ์ดํŠธ์— ๋“ค์–ด๊ฐ€์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ด์ค๋‹ˆ๋‹ค.

    ํ”„๋กœ์ ํŠธ ์ด๋ฆ„๊ณผ ํ•จ๊ป˜ ํ”Œ๋žซํผ์„ ์„ ํƒํ•˜๋ผ๊ณ  ๋‚˜์˜ต๋‹ˆ๋‹ค.
    ํ•„์š”ํ•œ client ํ”Œ๋žซํผ์„ ๋ชจ๋‘ ์ƒ์„ฑํ•ด์„œ ์‹œํ‚ค๋Š” ๋Œ€๋กœ ํ•ด์ค์‹œ๋‹ค. ์ด ๋•Œ client ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ ์ด๊ฑฐ ๋ณด๊ณ  ์ด๋Œ€๋กœ ํ•ด๋‹ฌ๋ผ๊ณ  ํ•ฉ์‹œ๋‹ค!

    iOS๋Š” ๋ฒˆ๋“คID๋ผ๋Š” ๊ฒƒ์„ ์ž…๋ ฅํ•ด์•ผ ํ•ด์š”
    https://firebase.google.com/docs/ios/setup?hl=ko 

    Apple ํ”„๋กœ์ ํŠธ์— Firebase ์ถ”๊ฐ€  |  Firebase for Apple platforms

    Google I/O 2023์—์„œ Firebase์˜ ์ฃผ์š” ์†Œ์‹์„ ํ™•์ธํ•˜์„ธ์š”. ์ž์„ธํžˆ ์•Œ์•„๋ณด๊ธฐ ์˜๊ฒฌ ๋ณด๋‚ด๊ธฐ Apple ํ”„๋กœ์ ํŠธ์— Firebase ์ถ”๊ฐ€ ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ์ •๋ฆฌํ•˜๊ธฐ ๋‚ด ํ™˜๊ฒฝ์„ค์ •์„ ๊ธฐ์ค€์œผ๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ถ„๋ฅ˜ํ•˜์„ธ์š”

    firebase.google.com

    ๊ทธ๋“ค๋„ firebase ์•Œ๋žŒ์„ ๋ฐ›์„ ๋ฐ‘์ž‘์—…์ด ํ•„์š”ํ•˜๋‹ˆ๊นŒ ์ด๊ฒƒ๋„ ๋งํ•ด์ฃผ๊ธฐ
    ์ €๋Š” ์ž˜ ์˜ค๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด Android Studio๋ฅผ ๊น”์•„์„œ ์ง์ ‘ ํ•ด๋ดค๋‹ต๋‹ˆ๋‹ค...ใ…Ž

     

    Android ์„ธํŒ…ํ•˜๊ธฐ

    ์ผ๋‹จ Firebase ์— ์•ฑ ์ถ”๊ฐ€ํ•  ๋•Œ google-services.json ํŒŒ์ผ ์ฃผ๋ฉด์„œ ์ง‘์–ด๋„ฃ์œผ๋ผ๊ณ  ํ•œ๋‹ค.
    ์—ฌ๊ธฐ๋‹ค ๋„ฃ์–ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

    ๋‹ค์Œ ์ฝ”๋“œ๋“ค์„ ์ถ”๊ฐ€ํ•ด์ค์‹œ๋‹ค
    build.gradle

    dependencies {
        classpath 'com.google.gms:google-services:4.3.5'
    }

    build.gradle(:app)

    dependencies {
    	...
        implementation 'com.google.firebase:firebase-messaging:21.1.0'
        implementation 'com.google.firebase:firebase-analytics-ktx:17.3.0'
    }
    apply plugin: 'com.google.gms.google-services'

    AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools">
    
        <uses-permission android:name="android.permission.INTERNET"/>
    
        <application
            ...>
            ...
            <service android:name=".MyFirebaseMessagingService"
                android:exported="true">
                <intent-filter>
                    <action android:name="com.google.firebase.MESSAGING_EVENT"/>
                </intent-filter>
            </service>
        </application>
    </manifest>
    //MyFirebaseMessagingService.kt
    class MyFirebaseMessagingService : FirebaseMessagingService() {
        override fun onNewToken(token: String) {
            Log.d("FCM Log", "Refreshed token: $token")
        }
    
        override fun onMessageReceived(remoteMessage: RemoteMessage) {
            // TODO(developer): Handle FCM messages here.
        }
    }

     

    Spring Boot ์„ธํŒ…

    build.gradle๋ถ€ํ„ฐ ์ˆ˜์ •ํ•ด์ค๋‹ˆ๋‹ค.

    implementation 'com.google.firebase:firebase-admin:6.8.1'
    implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.2.2'

    ๊ทธ๋ฆฌ๊ณ  Firebase ํ”„๋กœ์ ํŠธ์—์„œ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” jsonํŒŒ์ผ 

    ์•„๋ž˜ ๊ฒฝ๋กœ์— ๋„ฃ์–ด์ค๋‹ˆ๋‹ค.

     

    ๊ตฌํ˜„

    ์ผ๋‹จ ํšŒ์›๊ฐ€์ž…, ๋กœ๊ทธ์ธ ์‹œ DB์— device token์„ ์ €์žฅํ•˜๋Š” ์ž‘์—…์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
    Android ๋Š” fcm token์„ ์ €์žฅํ•˜๋ฉด ๋˜๊ตฌ์š”
    iOS๋Š” device token ์ €์žฅํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. 
    ๊ทธ๋ฆฌ๊ณ  ๋ฉ”์„ธ์ง€๋ฅผ ์ „์†กํ•  ๋•Œ, ์ „์†กํ•  ๊ธฐ๊ธฐ๋“ค์˜ ์ด token์„ List๋กœ ๋„˜๊ฒจ์ค˜์•ผ ํ•ด์š”
     
    FirebaseCloudMessageUtil๋ผ๋Š” ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ์š”

    • Firebase App์„ ์ดˆ๊ธฐํ™”ํ•˜๋Š” initialFirebaseApp
    • ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ด๋Š” sendMessage <- ์–˜๋ฅผ ์™ธ๋ถ€์—์„œ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ ํ‘ธ์‹œ์•Œ๋ฆผ์„ ๋ณด๋‚ผ๊ฒ๋‹ˆ๋‹ค

    ํฌ๊ฒŒ ๋‘ ๊ฐœ์˜ ๋ฉ”์„œ๋“œ๋กœ ์ด๋ฃจ์–ด์งˆ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.
    ์ฒ˜์Œ์—๋Š” sendMessage  ๋ฉ”์„œ๋“œ์—์„œ Android์™€ iOS๋ฅผ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ํ‘ธ์‹œ์•Œ๋ฆผ์„ ๋ณด๋‚ด๊ณ ์ž ํ–ˆ์Šต๋‹ˆ๋‹ค.

    FcmMessage fcmMessage = FcmMessage.builder()
            .message(FcmMessage.Message.builder()
                    .token(targetToken)
                    .data(
                            new LinkedHashMap<>() {{
                                put("type", String.valueOf(type));
                            }}
                    )
                    .notification(FcmMessage.Notification.builder()
                            .title(title)
                            .body(body)
                            .build()
                    ).build()).validateOnly(false).build();
    
    return objectMapper.writeValueAsString(fcmMessage);

    ์ด ๋ฐฉ์‹์˜ ๋ฌธ์ œ์ ์€ ์•ˆ๋“œ๋กœ์ด๋“œ๊ฐ€ ์•Œ๋žŒ์„ ์ˆ˜์‹ ํ–ˆ์„ ๋•Œ
    ํ™”๋ฉด์ด ์ผœ์ง€์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹คโ€ฆ!
    ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” notification์œผ๋กœ ๋ณด๋‚ด๋ฉด ์•ˆ๋์–ด์š”
    ๊ทธ๋ ‡๋‹ค๊ณ  iOS๋„ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋ณด๋‚ด๋ฉด ์ •์ƒ์  ์ˆ˜์‹ ์ด ๋˜์ง€ ์•Š๋”๋ผ๊ตฌ์š”

    ๊ทธ๋ž˜์„œ ์ฐพ์•„๋‚ธ ๋ฐฉ๋ฒ•
    setAndroidConfig, setApnsConfig์„ ์ด์šฉํ•˜์—ฌ ๊ฐ์ž์— ๋งž๊ฒŒ ๋ณด๋‚ด๊ธฐ

    @Component
    @RequiredArgsConstructor
    public class FirebaseCloudMessageUtil {
        static FirebaseApp firebaseApp;
        static final String firebaseAppName = "[์•ฑ์ด๋ฆ„]";
        static final String firebaseConfigPath = "firebase/firebase_service_key.json";
    	
        //์˜์กด์„ฑ ์ฃผ์ž… ์ฒ˜์Œ๋  ๋•Œ ํ•œ ๋ฒˆ ํ˜ธ์ถœ
        @PostConstruct
        private void initialFirebaseApp() throws IOException {
            FirebaseOptions options = FirebaseOptions.builder()
                    .setCredentials(GoogleCredentials.fromStream(new ClassPathResource(firebaseConfigPath).getInputStream()))
                    .build();
    
            firebaseApp = FirebaseApp.initializeApp(options, firebaseAppName);
        }
    
        public void sendMessage(List<String> deviceTokenToSendList, String title, String body, Map<String, String> data) {
    
            if (deviceTokenToSendList.size() > 0) {
                MulticastMessage message = MulticastMessage.builder()
                        .addAllTokens(deviceTokenToSendList)
                        .setAndroidConfig(AndroidConfig.builder()
                                .putAllData(creatDataForAOS(title, body, data))
                                .build())
                        .setApnsConfig(ApnsConfig.builder()
                                .putAllCustomData(createDataForiOS(data))
                                .setAps(Aps.builder()
                                        .setContentAvailable(true)
                                        .setAlert(ApsAlert.builder()
                                                .setTitle(title)
                                                .setBody(body)
                                                .build())
                                        .build())
                                .build())
                        .build();
    
                try {
                    FirebaseMessaging.getInstance(firebaseApp).sendMulticast(message);
                } catch (FirebaseMessagingException e) {
                    throw new CustomException(ResponseCode.SERVER_ERROR_FAILED_TO_SEND_FCM);
                }
            }
        }
    
        @NotNull
        private static LinkedHashMap<String, String> creatDataForAOS(String title, String body, Map<String, String> data) {
            LinkedHashMap<String, String> dataForAOS = new LinkedHashMap<>() {{
                put(FcmDataType.TITLE.getType(), title);
                put(FcmDataType.BODY.getType(), body);
            }};
            dataForAOS.putAll(data);
            return dataForAOS;
        }
    
        @NotNull
        private static LinkedHashMap<String, Object> createDataForiOS(Map<String, String> data) {
            LinkedHashMap<String, Object> dataForiOS = new LinkedHashMap<>();
            dataForiOS.putAll(data);
            return dataForiOS;
        }
    
    }

    deviceTokenToSendList๋Š” ์ „์†กํ•  ๋””๋ฐ”์ด์Šค ํ† ํฐ๋“ค์ด ๋“ค์–ด์žˆ๋Š” List!
    ๋‹จ์ฒด๋กœ ํ•œ ๋ฒˆ์— ๋ณด๋‚ด๊ธฐ ์œ„ํ•ด MulticastMessage๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
    https://firebase.google.com/docs/cloud-messaging/send-message?hl=ko 

    ์•ฑ ์„œ๋ฒ„ ์ „์†ก ์š”์ฒญ ์ž‘์„ฑ  |  Firebase ํด๋ผ์šฐ๋“œ ๋ฉ”์‹œ์ง•

    Google I/O 2023์—์„œ Firebase์˜ ์ฃผ์š” ์†Œ์‹์„ ํ™•์ธํ•˜์„ธ์š”. ์ž์„ธํžˆ ์•Œ์•„๋ณด๊ธฐ ์˜๊ฒฌ ๋ณด๋‚ด๊ธฐ ์•ฑ ์„œ๋ฒ„ ์ „์†ก ์š”์ฒญ ์ž‘์„ฑ ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ์ •๋ฆฌํ•˜๊ธฐ ๋‚ด ํ™˜๊ฒฝ์„ค์ •์„ ๊ธฐ์ค€์œผ๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ถ„๋ฅ˜ํ•˜์„ธ์š”. Fire

    firebase.google.com

     
    setAndroidConfig, setApnsConfig ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๋‹ฌ๋ผ์„œ ๊ฐ์ž์— ๋งž์ถฐ์ง„ Data๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ๋ฉ”์„œ๋“œ ๋‘ ๊ฐœ (creatDataForAOS, createDataForiOS)๋ฅผ ์ถ”๊ฐ€ํ•ด์คฌ์Šต๋‹ˆ๋‹ค.
    +) FcmDataType์€ ํƒ€์ž… ๋ช… enum ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค.
     
    +) ํ˜น์‹œ ์ž˜ ๊ฐ€๋Š”์ง€ ํ™•์ธ์ด ํ•„์š”ํ•˜๋‹ค๋ฉด message๋ฅผ ๋กœ๊ทธ ์ฐ์–ด ๋ณด์„ธ์—ฌ
     
    ์‚ฌ์šฉ๋ฒ•!

    firebaseCloudMessageUtil.sendMessage({๋ฉ”์„ธ์ง€ ๋ฐ›์„ User๋“ค device token list},
                        {ํ‘ธ์‹œ์•Œ๋ฆผ ์ œ๋ชฉ},
                        {ํ‘ธ์‹œ์•Œ๋ฆผ ๋‚ด์šฉ},
                        {๋ณด๋‚ผ ๋ฐ์ดํ„ฐ ํƒ€์ž…, ๋ฐ์ดํ„ฐ HashMap});

    ํ‘ธ์‹œ์•Œ๋ฆผ ์ œ๋ชฉ์€ ๋ง ๊ทธ๋Œ€๋กœ ์ œ๋ชฉ 
    ํ‘ธ์‹œ์•Œ๋ฆผ ๋‚ด์šฉ์€ ๋ง ๊ทธ๋Œ€๋กœ ๋‚ด์šฉ
    ๋ณด๋‚ผ ๋ฐ์ดํ„ฐ๋Š” payload, ํ‘ธ์‹œ ์•Œ๋ฆผ์— ๋ณด์ด์ง€๋Š” ์•Š์ง€๋งŒ Client ์ธก์—์„œ ํ‘ธ์‹œ ์•Œ๋ฆผ์„ ์ˆ˜์‹ ํ–ˆ์„ ์‹œ ๊บผ๋‚ด์˜ฌ ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ

    ์ œ๋ชฉ๊ณผ ๋‚ด์šฉ

    ์˜ˆ์‹œ
     

    LinkedHashMap<String, String> dataToSend = new LinkedHashMap<>() {{
        put(FcmDataType.TYPE.getType(), String.valueOf(FcmPushType.CPR_CALL.ordinal()));
        put(FcmDataType.CPR_CALL_ID.getType(), String.valueOf(cprCall.getId()));
    }};
    
    List<String> deviceTokenToSendPushList;
    do {
        pageable = PageRequest.of(offset, maxSize);
        deviceTokenToSendPushList = deviceTokenRepository.findAllDeviceTokenByUserAddressExceptCaller(cprCall.getAddress().getId(), userId, pageable);
        firebaseCloudMessageUtil.sendMessage(
                deviceTokenToSendPushList,
                FcmMessage.CPR_CALL_TITLE.getMessage(),
                cprCall.getFullAddress(),
                dataToSend
        );
        offset += maxSize;
    } while (deviceTokenToSendPushList.size() >= maxSize);

     

    ํ…Œ์ŠคํŠธ ํ•ด๋ณด๊ธฐ

    ํด๋ผ์ด์–ธํŠธ ๊ฐœ๋ฐœ์ž๋ฅผ ์ง์ ‘ ๋งŒ๋‚˜์ง€ ์•Š๊ณ ๋Š” ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ๊ฐ€ ํž˜๋“ญ๋‹ˆ๋‹ค
    ํ˜ผ์ž ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์•„๊นŒ ๋งŒ๋“  Android ํ”„๋กœ์ ํŠธ๋ฅผ ๋‹ค์‹œ ์ผญ์‹œ๋‹ค

    class MyFirebaseMessagingService : FirebaseMessagingService() {
        override fun onNewToken(token: String) {
            Log.d("FCM Log", "Refreshed token: $token")
        }
    
        override fun onMessageReceived(remoteMessage: RemoteMessage) {
            // TODO(developer): Handle FCM messages here.
            // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
            Log.d("FCM LOG", "From: ${remoteMessage.from}")
    
            // Check if message contains a data payload.
            if (remoteMessage.data.isNotEmpty()) {
                Log.d("FCM LOG", "Message data payload: ${remoteMessage.data["type"]}")
            }
    
            // Check if message contains a notification payload.
            remoteMessage.notification?.let {
                Log.d("FCM LOG", "Message Notification Body: ${it.body}")
            }
    
            // Also if you intend on generating your own notifications as a result of a received FCM
            // message, here is where that should be initiated. See sendNotification method below.
        }
    }

    ๊ฐœ์ธ ๊ณต๊ธฐ๊ณ„๋ฅผ ํ”„๋กœ์ ํŠธ์— ๋“ฑ๋กํ•˜๊ณ , ์ด ์•ฑ์„ ์„ค์น˜ํ•ด์„œ ์ด๋Ÿฐ ์‹์œผ๋กœ ๋กœ๊ทธ๋ฅผ ์ฐ์œผ๋ฉฐ ํ™•์ธํ•ด๋ด…์‹œ๋‹ค
     
    ๋!

Designed by Tistory.