From ca46f2f034bdfc9fc3932e1753da0947f2d32168 Mon Sep 17 00:00:00 2001 From: si-zero Date: Sun, 16 Aug 2026 19:41:38 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20Android=20FCM=20=ED=91=B8=EC=8B=9C?= =?UTF-8?q?=EC=97=90=20title/body=EA=B0=80=20=EC=8B=A4=EB=A6=AC=EC=A7=80?= =?UTF-8?q?=20=EC=95=8A=EC=95=84=20=EC=95=8C=EB=A6=BC=EC=9D=B4=20=EC=95=88?= =?UTF-8?q?=20=EB=9C=A8=EB=8D=98=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infra/fcm/service/FcmPushService.java | 7 ++- .../infra/fcm/service/FcmPushServiceTest.java | 59 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/swyp/picke/global/infra/fcm/service/FcmPushServiceTest.java diff --git a/src/main/java/com/swyp/picke/global/infra/fcm/service/FcmPushService.java b/src/main/java/com/swyp/picke/global/infra/fcm/service/FcmPushService.java index 1f836b9..34601a3 100644 --- a/src/main/java/com/swyp/picke/global/infra/fcm/service/FcmPushService.java +++ b/src/main/java/com/swyp/picke/global/infra/fcm/service/FcmPushService.java @@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.util.HashMap; import java.util.Map; @Slf4j @@ -23,9 +24,13 @@ public class FcmPushService { * iOS는 FCM을 거치지 않고 ApnsPushService가 APNs로 직접 발송한다. */ public void send(UserDevice device, String title, String body, Map data) { + Map payload = new HashMap<>(data); + payload.put("title", title); + payload.put("body", body); + Message message = Message.builder() .setToken(device.getFcmToken()) - .putAllData(data) + .putAllData(payload) .setAndroidConfig(AndroidConfig.builder() .setPriority(AndroidConfig.Priority.HIGH) .build()) diff --git a/src/test/java/com/swyp/picke/global/infra/fcm/service/FcmPushServiceTest.java b/src/test/java/com/swyp/picke/global/infra/fcm/service/FcmPushServiceTest.java new file mode 100644 index 0000000..3b0f818 --- /dev/null +++ b/src/test/java/com/swyp/picke/global/infra/fcm/service/FcmPushServiceTest.java @@ -0,0 +1,59 @@ +package com.swyp.picke.global.infra.fcm.service; + +import com.google.firebase.messaging.FirebaseMessaging; +import com.google.firebase.messaging.Message; +import com.swyp.picke.domain.notification.entity.UserDevice; +import com.swyp.picke.domain.notification.enums.DevicePlatform; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.lang.reflect.Field; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class FcmPushServiceTest { + + @Mock + private FirebaseMessaging firebaseMessaging; + + @Test + void send_includes_title_and_body_in_data_payload() throws Exception { + FcmPushService fcmPushService = new FcmPushService(firebaseMessaging); + UserDevice device = buildDevice(); + + fcmPushService.send(device, "제목", "본문", Map.of("type", "TEST")); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + verify(firebaseMessaging).send(messageCaptor.capture()); + + Map data = extractData(messageCaptor.getValue()); + assertThat(data).containsEntry("title", "제목"); + assertThat(data).containsEntry("body", "본문"); + assertThat(data).containsEntry("type", "TEST"); + } + + @SuppressWarnings("unchecked") + private Map extractData(Message message) throws Exception { + Field dataField = Message.class.getDeclaredField("data"); + dataField.setAccessible(true); + return (Map) dataField.get(message); + } + + private UserDevice buildDevice() throws Exception { + UserDevice device = UserDevice.builder() + .fcmToken("token") + .platform(DevicePlatform.ANDROID) + .build(); + Field idField = device.getClass().getSuperclass().getDeclaredField("id"); + idField.setAccessible(true); + idField.set(device, 1L); + return device; + } +}