[헤이동동 #05] FCM 푸시 알림

Jiwoo Kim·2020년 12월 2일
0
post-thumbnail

☕헤이동동 : 생협 음료 원격 주문 서비스

이번 포스팅에서는 FCM을 활용한 푸시 알림 서비스를 구현하는 과정에 대하여 설명한다.


개요

  • 클라이언트 간 주문 상태 동기화가 되면, 관리자 클라이언트는 서버에 주문 업데이트 HTTP request를 보낸다.
  • 서버는 해당 주문에 대한 푸시 알림을 해당 고객 클라이언트에게 보내도록 FCM 서버에 HTTP request를 보낸다.

구현

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 하기


전체 코드는 Github에서 확인하실 수 있습니다.

0개의 댓글