3 분 소요

1. firebase 설정

클라이언트

프로젝트 생성이 완료되면 클라이언트 프로젝트에 넣어야 할 설정파일을 받아야 한다.
사진처럼 각 클라이언트에 맞는 플랫폼을 선택한다.
스크린샷 2022-09-20 오후 5 00 38

  • 프로젝트의 패키지 이름을 작성한다(패키지 이름은 build.gradle의 applicationId). 나머지는 선택적으로 작성하면 된다.
  • google.service.json 파일을 다운받고, 사진의 경로에 넣어준다.

서버

스프링부트 프로젝트에 넣어야 할 설정파일을 받아야 한다.
사진의 순서대로 선택 후 클라이언트에서의 방법과 마찬가지라 .json파일을 다운받으면 된다.
스크린샷 2022-09-16 오후 5 36 07

2. 클라이언트 (안드로이드)

라이브러리 추가

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으로 메세지를 보낼때 사용된다.

3. 서버 (스프링 부트)

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

설정파일

아까 위에서 Firebase 프로젝트를 생성하면서 서버에 넣을 설정파일을 다운받았다.
그 파일을 아래 사진처럼 경로에 넣어준다.
나는 이름을 사진처럼 수정하였다.
스크린샷 2022-09-20 오후 4 47 49

build.gradle

다음과 같이 Firebase sdk, okhttp 의존성을 추가한다.

dependencies {
  ...

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

구현

FecReqDto

@AllArgsConstructor
@Getter
@Setter
public class FcmReqDto {

    private String targetToken;
    private String title;
    private String body;
}

FcmMessage

메세지를 보내기 위해서 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;
    }
}

FirebaseCloudMessageService

이제 실제 메세지 요청을 수행하는 서비스를 작성한다.
총 3개의 메서드로 구성되어있다.

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

API_URL은 메세지 전송을 위해 요청하는 주소이다.
https://fcm.googleapis.com/v1/projects/{프로젝트 ID}/messages:send
여기서 프로젝트ID는 다음 사진을 보면 알 수 있다.
스크린샷 2022-09-20 오후 4 57 53

@Component
@RequiredArgsConstructor
public class FirebaseCloudMessageService {

    @Value("${fcm.api-url}")
    private final String API_URL;
    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();
    }

    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();
    }
}

getAccessToken() 메소드

Firebase로 부터, AccessToken을 가져온 뒤, 그것을 Header에 포함하여 Push 알림을 요청할 것이다.
이를 위해 생성하는 메소드가 getAccessToken()이다.

GoogleCredentials는 GoogleApi를 사용하기 위해서 oauth2를 이용해 인증한 대상을 나타내는 객체이다.
GoogleCredentials의 static 메소드인 fromStream()에 앞서 다운받은 firebase-service-key.json의 inputStream을 넣어주면 인스턴스를 얻을 수 있다.

이때, 인증하는 서버에서 필요로 하는 권한을 지정해주어야 한다.
이는 다시 반환 된 GoogleCredentials 인스턴스의 createScoped를 통해 설정 가능하다.

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

이제 모든 설정을 통해 얻어낸 GoogleCredentials의 객체를 통해서, FCM을 이용할 수 있는 권한이 부여된 Oauth2의 AccessToken을 받을 차례이다.
먼저 refreshIfExpired() 메소드를 호출하여 accessToken을 생성한 뒤,
다시 GoogleCredentials 객체의 getAccessToken() 메소드를 이용해 토큰을 받아온다.
그리고 getTokenValue()를 이용해 토큰 값을 최종적으로 얻어온다.

이렇게 얻어온 AccessToken은,
아래에서 RestAPI를 이용해 FCM에 Push 요청을 보낼 때, Header에 설정하여 인증을 위해 사용할 것이다.

sendMessageTo() 메소드

이 메소드는 매개변수로 전달받은 targetToken에 해당하는 device로 FCM 푸시 알림을 전송 요청한다.
targetToken의 경우 FCM을 이용해 front를 구현할 때 얻어낼 수 있다.

  • Request Body 추가
    - OkHttp3를 이용해 Http Post Request를 생성한다.
    - RequestBody에는 우선, 우리가 앞서 생성한 FcmMessage를 문자열로 변경한 데이터를 넣어주면 되는데,
    이 메시지를 생성하는 makeMessage() 메소드는 아래에서 살펴보자

  • header 추가
    - header에 AccessToken을 추가한다.
    - Authorization을 키로 하는 헤더에, Bearer <AccessToken> 형태로 추가해주면 된다.

makeMessage() 메소드

FcmMessage를 만들고, 이를 ObjectMapper를 이용해 String으로 변환하여 반환한다.

Controller

메세지 발송 방식은 엄청 많지만,
이번에는 바로 확인이 가능하도록 controller로 요청을 받으면 메세지 요청을 수행하는 컨트롤러 메서드를 작성해보려 한다.
간단하게 이전에 생성한 서비스를 주입받아 호출하면 끝이다.
추가로 FcmReqDto는 간단하게 생성하여 사용하였다.

@RequiredArgsConstructor
@RestController
@RequestMapping
    ("/admin")
@Slf4j
public class AdminController {

    private final FirebaseCloudMessageService firebaseCloudMessageService;

    ...

    @ApiOperation(value = "장애인 인증 요청 수락", notes = "장애인 인증 요청 수락")
    @PostMapping("/disabled/approval/{id}")
    public Response grantDisabledReq(@PathVariable("id") long disabledMemberInfoId,
        @RequestBody FcmReqDto fcmReqDto) throws IOException {

        ...

        // 푸시 알림 전송
        firebaseCloudMessageService.sendMessageTo(
            fcmReqDto.getTargetToken(),
            fcmReqDto.getTitle(),
            fcmReqDto.getBody()
        );

        return new Response("OK", "장애인 인증 요청 수락에 성공했습니다");
    }
}

Ref.



💛 개인 공부 기록용 블로그입니다. 👻

맨 위로 이동하기

태그:

카테고리:

업데이트: