이번 포스팅에서는 FCM을 활용한 푸시 알림 서비스를 구현하는 과정에 대하여 설명한다.
CustomerPush
FCM 서버에 보낼 Push 메세지는 JSONObject 형식으로 구현해야 한다.
CustomerPush
클래스에서는 타겟 디바이스 토큰과 주문 상태를 받아 메세지를 생성하여 리턴한다.
public class CustomerPush {
public static String buildCustomerPushOnProgressUpdate(String targetToken, Progress progress) throws JSONException {
JSONObject data = buildData(progress);
JSONObject body = buildBody(targetToken, data);
return body.toString();
}
private static JSONObject buildData(Progress progress) {
JSONObject data = new JSONObject();
data.put("title", "주문 진행 알림");
data.put("message", setMessage(progress));
return data;
}
private static String setMessage(Progress progress) {
switch (progress) {
case MAKING:
return "음료 제조가 시작되었습니다. 매장에 방문하여 대기해주세요.";
case DECLINED:
return "주문이 매장 사정에 의해 거절되었습니다. 매장에 직접 문의해주세요.";
case READY:
return "주문하신 음료가 모두 준비되었습니다. 매장에서 결제 후 수령해주세요.";
case DONE:
return "주문이 정상적으로 거래완료되었습니다. 헤이동동을 이용해주셔서 감사합니다.";
case NOSHOW:
return "주문이 노쇼 처리되었습니다. 누적 횟수에 따라 헤이동동 서비스 이용이 중지될 수 있습니다.";
default:
return null;
}
}
private static JSONObject buildBody(String targetToken, JSONObject data) {
JSONObject body = new JSONObject();
body.put("to", targetToken);
body.put("priority", "high");
body.put("data", data);
return body;
}
}
푸시를 받을 고객 클라이언트는 targetToken
값으로 구분한다. 이 값은 고객이 특정 기기로 최초 로그인했을 때 DB에 저장되고 사용된다.
또한, 메세지를 생성할 때 유의할 점은 body에 ("priority", "high")
를 꼭 넣어 주어야 푸시가 제대로 뜬다는 것이다. 아마 안드로이드 자체 설정때문일 것 같은데, 처음에 저거 없이 보냈다가 푸시가 안 떴었는데 추가하니까 제대로 작동하였다.
HeaderRequestInterceptor
public class HeaderRequestInterceptor implements ClientHttpRequestInterceptor {
private final String headerName;
private final String headerValue;
public HeaderRequestInterceptor(String headerName, String headerValue) {
this.headerName = headerName;
this.headerValue = headerValue;
}
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
HttpRequest wrapper = new HttpRequestWrapper(request);
wrapper.getHeaders().set(headerName, headerValue);
return execution.execute(wrapper, body);
}
}
PushService
PushService
는 Firebase api 서버에 push request를 보낸다.
@Slf4j
@Service
public class PushService {
private static final String firebase_server_key = 서버키;
private static final String firebase_api_url = "https://fcm.googleapis.com/fcm/send";
@Autowired
private UserService userService;
public void sendCustomerPush(User user, Progress progress) throws JSONException, InterruptedException {
String notifications = CustomerPush.buildCustomerPushOnProgressUpdate(userService.getUserDeviceToken(user), progress);
HttpEntity<String> request = new HttpEntity<>(notifications);
CompletableFuture<String> pushNotification = sendPush(request);
CompletableFuture.allOf(pushNotification).join();
try {
String firebaseResponse = pushNotification.get();
} catch (InterruptedException e) {
log.error("InterruptedException on CustomerPush");
throw new InterruptedException();
} catch (ExecutionException e) {
log.error("ExecutionException on CustomerPush");
}
}
@Async
public CompletableFuture<String> sendPush(HttpEntity<String> entity) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
ArrayList<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(new HeaderRequestInterceptor("Authorization", "key=" + firebase_server_key));
interceptors.add(new HeaderRequestInterceptor("Content-Type", "application/json"));
restTemplate.setInterceptors(interceptors);
String firebaseResponse = restTemplate.postForObject(firebase_api_url, entity, String.class);
return CompletableFuture.completedFuture(firebaseResponse);
}
}
이 부분은 다른 분의 블로그를 참고하여 작성하였다. 하지만 아직 완벽하게 이해가 가지 않아서 좀 더 공부가 필요할 것 같다.
Firebase, Spring boot 에서 Android app으로 알림 push 하기