이번에 우테코 프로젝트로 "원만한 친구 사이를 위한 약속 지킴이 서비스" 오디(Ody)를 안드로이드 애플리케이션으로 만들게 되면서
MVP 기능으로 약속에 늦지 않도록 하기 위한 출발 시간 푸시 알림 기능을 구현하게 되었다.
안드로이드 푸시 알림을 구현하는데에 여러 대안이 있었다.
- Amazon SNS
- Firebase Cloud Messaging(FCM)
오디 팀은 FCM을 선택하게 되었는데 이유를 간단하게 설명하자면 다음과 같다.
FCM 선정 이유
Amazon SNS는 푸시 알림 외에도 SMS, 이메일 등 다양한 메시징 옵션을 제공하지만
FCM에 비해 push 알림을 구현할 참고 자료가 상당히 부족했다.
무엇보다 유료이기도 했고, 안드로이드 측에서는 FCM을 학습해와서
익숙한 기술 + 무료 라는 장점으로 FCM을 선정하게 되었다.
FCM 이란?
Firebase Cloud Message의 약자인 교차 플랫폼 메시지 솔루션 플랫폼으로
플랫폼에 종속적이지 않게 PUSH 메세지를 전송할 수 있게 도와준다.
이러한 플랫폼을 사용하지 않을 경우 서버를 경유해서 실시간으로 푸쉬 메시지를 받기 위해 사용자는 항상 서버에 접속해있어야하는데
이는 사용자 기기의 배터리 및 네트워크 리소스를 크게 낭비한다.
클라우드 메시징 서비스인 FCM을 중간에 둠으로써
서버와 앱이 네트워크 연결을 지속적으로 유지하지 않고 사용자는 낮은 배터리 사용량으로 메세지를 실시간으로 송수신 처리를 할 수 있다.
FCM 작업 흐름
아무래도 안드로이드, 백엔드가 각각 어떠한 작업을 해야하는지를 알아야 구현하는데 어려움이 없을 것 같다.
각각의 분야별로 어떤 과정이 필요한지 그리고 FCM으로 안드로이드 기기에 푸쉬 알림이 가기까지 어떤 과정이 있는지 한번 알아보자.
안드로이드
- Firebase SDK를 설치하고 프로젝트를 연결
- Firebase 콘솔에서 앱을 등록하고 필요한 설정
- 앱 내에서 FCM 토큰을 가져오고, 이를 서버에 전송
서버 (백엔드)
- Firebase에 등록한 프로젝트의 Access Key 파일 추가 후 이 key로 Fcm 연결 설정 (한번)
- 프론트엔드에서 전송받은 FCM 토큰을 DB에 저장
- 푸시 알림을 보내고자 할 때, 저장된 토큰을 이용하여 푸시 알림의 제목, 내용, 클릭 동작 등을 설정해 FCM 서버에 푸시 알림 전송 요청
FCM
- 백엔드에서 전송받은 푸시 알림 요청을 처리해 안드로이드에 전송
FCM 연결 설정하기
참고로 이 글은 서버 측에서 작성하는 방법에 대해서만 설명한다.
1. Firebase Console에서 프로젝트 생성
https://console.firebase.google.com/?hl=ko
위 링크로 들어가 프로젝트를 생성한다.
2. 안드로이드 앱 설정 (안드로이드 쪽에서 설정 필요)
프로젝트가 다 만들어지면 아래 화면이 보일 것이다.
안드로이드 푸쉬 알림을 구현할 것이므로 안드로이드 앱 모양을 클릭하고 아래 정보들을 입력해준다.
3. 엑세스 키 생성
위 과정을 완료한 뒤 좌측 상단의 톱니모양 클릭 -> 프로젝트 설정 -> 서비스 계정 -> 자바 체크 -> 새 비공개 키 생성
그리고 발급된 AccessKey json 파일을 서버에서 resource 패키지 하위에 저장한다.
4. FCM 연결 설정 클래스 추가
@Slf4j
@Configuration
public class FcmConfig {
@PostConstruct
public void initialize() {
try {
FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(
GoogleCredentials.fromStream(new ClassPathResource("엑세스 키 파일 이름").getInputStream())
)
.build();
FirebaseApp.initializeApp(options);
log.info("Fcm 설정 성공");
} catch (IOException exception) {
log.error("Fcm 연결 오류 {}", exception.getMessage());
}
}
}
이후 Main에 있는 애플리케이션을 실행했을 때 "Fcm 설정 성공" 문구가 나오면 정상적으로 연결 된 것이다.
FCM으로 메시지 전송하기
연결을 마쳤으니 이제 FCM으로 안드로이드 측에서 수신할 메세지를 전송해보자.
firebase 라이브러리 의존성 주입
implementation 'com.google.firebase:firebase-admin:9.2.0'
위 라이브러리를 build.gradle에 추가하면 firebase에서 제공해주는 객체들을 다룰 수 있게 된다.
FCM 토큰으로 FCM에 메시지 전송
FCM으로 메세지를 전송하기 위해서는 우선 안드로이드 측에서 FCM 토큰을 받아야 한다.
그리고 이 FCM 토큰으로 FCM에 어떤 기기에 어떤 메세지를 보낼 것인지 알려주어야 한다.
오디 팀은 FCM으로 보내는 메시지가 출발 시간 알림인지, 준비 시간 알림인지를 type으로 구분해 보내주고
안드로이드 측에서 FCM에서 수신한 메시지를 가공하고 기기에 푸쉬 알림을 보내주기로 해
아래와 같이 FCM에 요청을 보낼 DTO와 FCM에 메세지를 전송하는 코드를 만들었다.
public record FcmSendRequest(String token, String notificationType) {
}
@Slf4j
@Component
public class FcmPushSender {
public String sendPushNotification(FcmSendRequest fcmSendRequest) {
Message message = Message.builder()
.setToken(fcmSendRequest.token())
.setNotification(Notification.builder()
.setTitle("알림 제목")
.setBody(fcmSendRequest.notificationType())
.build())
.setAndroidConfig(
AndroidConfig.builder()
.setNotification(
AndroidNotification.builder()
.setTitle("알림 제목")
.setBody(fcmSendRequest.notificationType())
.setClickAction("push_click")
.build()
).build()
).build();
try {
return FirebaseMessaging.getInstance().send(message);
} catch (FirebaseMessagingException exception) {
log.error("Fcm 메시지 전송 실패 : {}", exception.getMessage());
throw new RuntimeException(exception);
}
}
}
그리고 위 전송 로직은 컨트롤러에서 모임방에 친구가 참여할 때마다 아래 URL을 POST 요청 받아 FCM에 메시지를 전송하도록 하였다.
@RestController
@RequiredArgsConstructor
public class MateController {
...
@PostMapping("/mates")
public ResponseEntity<MeetingSaveResponse> save(
@RequestHeader(HttpHeaders.AUTHORIZATION) String fcmToken,
@RequestBody MateSaveRequest mateSaveRequest
) {
...
FcmSendRequest fcmSendRequest = new FcmSendRequest(fcmToken, "입장알림");
fcmpushSender.sendPushNotification(fcmSendRequest);
return ResponseEntity.status(HttpStatus.CREATED)
.body(MeetingSaveResponse.of(meeting, mates));
}
}
참고로 android 뿐 아니라 setApnsConfig()로 ios에도 보낼 메세지를 설정할 수 있다.
메시지 전송에 대한 부가 설정은 아래 Firebase 공식문서에서 잘 알려주고 있으니
추가적으로 필요하면 아래 문서를 확인하는 것을 추천한다.
'◼ JAVA > Spring' 카테고리의 다른 글
[Spring] 커스텀 Validation 어노테이션 만들기 (0) | 2024.08.15 |
---|---|
[Spring] 사용자별 출발 시간 알림 예약 전송 기능 구현 (37) | 2024.07.21 |
[Spring] 통합 테스트 vs 슬라이스 테스트 (+ 개인적인 생각) (5) | 2024.05.15 |
[Spring] 스프링 테스트 어노테이션 알아보기 (feat. 슬라이스 테스트) (47) | 2024.04.25 |
[Spring] Bean이 2개 이상일 때 특정 Bean 선택 방법 (다형성 의존성 주입) (1) | 2024.04.16 |