본문 바로가기
개발/Spring

[SpringBoot] FCM을 통해 Push알림 보내보기

by solchan98 2022. 2. 10.

+ 진행 당시, 기능 구현에 집중하다보니 전체적인 설계나 코드가 클린하지 못할 수 있으니 이 부분은 리팩토링 하면서 적용하시면 좋을 것 같아요! :)  )

 

현재 참여하고 있는 IT연합 동아리 YAPP에서 진행중인 프로젝트에서 Push 알림을 사용하기로 하였다.
하지만 Push알림을 구현 해본적이 없기 때문에 이번에 시도를 해보았다.

간단하게 타이틀과 메세지만 보내보자!👏

FireBase 프로젝트

클라이언트와 서버의 작업을 시작하기 전, Firebase 작업먼저 준비하여야 한다.
프로젝트를 생성하고 클라이언트와 서버 각각의 설정파일을 준비해야 한다.

프로젝트 생성

다음의 주소로 들어가서 프로젝트를 생성한다.
Firebase프로젝트 만들기

프로젝트 생성은 매우 간단하여 이미지 자료는 첨부하지 않았다.
진짜 간단..

클라이언트-Firebase 설정

프로젝트 생성이 완료되면 클라이언트 프로젝트에 넣어야 할 설정파일을 받아야 한다.

  1. 사진처럼 각 클라이언트에 맞는 플랫폼을 선택한다.
  2. 프로젝트의 패키지 이름을 작성한다(패키지 이름은 build.gradle의 applicationId). 나머지는 선택적으로 작성하면 된다.
    나는 귀찮아서 패키지 이름만 넣었다! 이후 앱 등록 버튼을 클릭한다.
  3. google.service.json파일을 다운받고, 사진의 경로에 넣어준다.
    이후 계소옥 다음~ 끝.

서버-Firebase 설정

다음은 스프링부트 프로젝트에 넣어야 할 설정파일을 받아야 한다.
사진의 순서대로 선택 후 클라이언트에서의 방법과 마찬가지라 .json파일을 다운받으면 된다.

안드로이드 세팅

라이브러리 추가

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

app단

dependencies {

    ...
    implementation 'com.google.firebase:firebase-messaging:21.1.0'
}
apply plugin: 'com.google.gms.google-services'

Manifests

AndroidManifest.xml파일에 다음 구문을 추가한다.

// Internet permission 추가
<uses-permission android:name="android.permission.INTERNET"/>

// service추가 및 intent필터 추가
<application>
<service android:name=".MyFirebaseMessagingService">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT"/>
            </intent-filter>
</service>
...
</application>

MyFirebaseMessagingService

MainActivity는 특별히 작성할 코드는 없다.

// MyFirebaseMessagingService.class
public class MyFirebaseMessagingService extends FirebaseMessagingService {
    @Override
    public void onNewToken(String token){
        Log.d("FCM Log", "Refreshed token: "+token);
    }
}

이렇게 클라이언트(안드로이드) 설정은 끝난다.

여기까지 마무리된 후 어플을 실행하면, 디바이스 토큰이 Log에 남겨진다.
잘 보관하자. 이후 targetToken으로 메세지를 보낼때 사용된다.

스프링부트

이번에는 서버단 작업을 시작한다.
FCM 서비스를 구현하고, 이를 호출하는 컨트롤러 메서드를 작성하면 된다.

 

다음과 같이 의존성을 추가한다.

설정파일

아까 위에서 Firebase 프로젝트를 생성하면서 서버에 넣을 설정파일을 다운받았다.

그 파일을 아래 사진처럼 경로에 넣어준다.
나는 이름을 사진처럼 수정하였다.

FCM 서비스

이제 실제 메세지 요청을 수행하는 서비스를 작성한다.

총 3개의 메서드로 구성되어있다.

  1. 메세지 전송(sendMessageTo())
  2. 메세지 생성(makeMessage())
  3. 메세지 전송을 위한 접근 토큰 발급(getAccessToken())

API_URL은 메세지 전송을 위해 요청하는 주소이다.
"https://fcm.googleapis.com/v1/projects/{프로젝트 ID}/messages:send"
여기서 프로젝트ID는 다음 사진을 보면 알 수 있다.

@Component
@RequiredArgsConstructor
public class FirebaseCloudMessageService {

    private final String API_URL = "https://fcm.googleapis.com/v1/projects/android-****/messages:send";
    private final ObjectMapper objectMapper;

    public void sendMessageTo(String targetToken, String title, String body) throws IOException {
        String message = makeMessage(targetToken, title, body);

        OkHttpClient client = new OkHttpClient();
        RequestBody requestBody = RequestBody.create(message,
                MediaType.get("application/json; charset=utf-8"));
        Request request = new Request.Builder()
                .url(API_URL)
                .post(requestBody)
                .addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + getAccessToken())
                .addHeader(HttpHeaders.CONTENT_TYPE, "application/json; UTF-8")
                .build();

        Response response = client.newCall(request).execute();

        System.out.println(response.body().string());
    }

    private String makeMessage(String targetToken, String title, String body) throws JsonParseException, JsonProcessingException {
        FcmMessage fcmMessage = FcmMessage.builder()
                .message(FcmMessage.Message.builder()
                    .token(targetToken)
                    .notification(FcmMessage.Notification.builder()
                            .title(title)
                            .body(body)
                            .image(null)
                            .build()
                    ).build()).validateOnly(false).build();

        return objectMapper.writeValueAsString(fcmMessage);
    }

    private String getAccessToken() throws IOException {
        String firebaseConfigPath = "firebase/firebase_service_key.json";

        GoogleCredentials googleCredentials = GoogleCredentials
                .fromStream(new ClassPathResource(firebaseConfigPath).getInputStream())
                .createScoped(List.of("https://www.googleapis.com/auth/cloud-platform"));

        googleCredentials.refreshIfExpired();
        return googleCredentials.getAccessToken().getTokenValue();
    }
}

FCM Message

메세지를 보내기 위해서 DTO와 같은 역할을 하는 녀석이 필요하다.

사실 Message와 Notification은 많은 속성을 가지고 있지만, 이번에는 간단하게 title, body 정도만 다룰 예정이기 때문에 간단하게 직접 생성하였다.

@Builder
@AllArgsConstructor
@Getter
public class FcmMessage {
    private boolean validateOnly;
    private Message message;

    @Builder
    @AllArgsConstructor
    @Getter
    public static class Message {
        private Notification notification;
        private String token;
    }

    @Builder
    @AllArgsConstructor
    @Getter
    public static class Notification {
        private String title;
        private String body;
        private String image;
    }
}

MainController

메세지 발송 방식은 엄청 많지만, 이번에는 바로 확인이 가능하도록 controller로 요청을 받으면 메세지 요청을 수행하는 컨트롤러 메서드를 작성해보려 한다.

간단하게 이전에 생성한 서비스를 주입받아 호출하면 끝이다.
추가로 RequestDTO는 간단하게 생성하여 사용하였다.
그리고 클라이언트(안드로이드)에서 어플이 실행중이면 알림은 오지않고 어플이 실행중이지 않을때 알림이 온다. (이것은 설정 가능하다고 한다. but 내가 신경쓸 부분은 아니기 때문에..)

@RestController
@RequiredArgsConstructor
public class MainController {

    private final FirebaseCloudMessageService firebaseCloudMessageService;

    @PostMapping("/api/fcm")
    public ResponseEntity pushMessage(@RequestBody RequestDTO requestDTO) throws IOException {
        System.out.println(requestDTO.getTargetToken() + " "
                +requestDTO.getTitle() + " " + requestDTO.getBody());

        firebaseCloudMessageService.sendMessageTo(
                requestDTO.getTargetToken(),
                requestDTO.getTitle(),
                requestDTO.getBody());
        return ResponseEntity.ok().build();
    }
}

메세지 보내기!

이제 준비는 끝났다.

Postman을 활용해서 테스트를 해본다.

  1. targetToken, title, body를 작성하여 서버에 보낸다.
  2. 서버는 firebase 서버에 메세지 푸시를 요청한다.
  3. 토큰이 확인되면 firebase서버는 클라이언트에게 메세지를 푸시한다.

이렇게 간단하게 push알림을 보내는 방법을 알아보고 진행해보았다.


FCM 안드로이드1
FCM 안드로이드2 / onNewToken만 보셈
FCM 스프링부트

깃허브 저장소