PostMan과 Google Developer를 이용하여 앱에 알림을 보냈었다. 이를 서비스에서 이용하기 위하여 서버도 수정하려 한다.
헷갈리는 점 정리: 채팅방 로직
abc가 있는 채팅 방에서 a가 채팅을 침 -> 채팅방에 접속해있던 b는 websocket으로 바로 메시지를 받음 -> 접속해 있지 않던 c는 service에서 raw data를 받음 -> raw data를 파싱하여 Room에 '읽지 않은 메시지'로 저장 후 push 알림을 보냄
-> {
1) 알림을 본 c는 알림을 클릭하여 해당 채팅방으로 바로 이동
2) 알림을 지우고 앱을 직접 실행 - Room에서 '읽지 않은 메세지' 조회 후 채팅방 리스트 위에 그 개수를 알려줌.
}
-> 채팅방에 입장한 c의 앱에서 서버에 최근 메시지 리스트를 요청 후 화면에 띄어줌.
헷갈리는 점 정리: 초대 로직
a가 bc와 함께 하는 채팅방을 만듦 -> User table에 저장되어 있는 device token 값으로 해당 기기들을 FirebaseTopic(roomId) 에 구독시킴 -> a가 새로 생성된 채팅방에서 메세지 입력 시 firebase를 통하여 문자를 보냄 -> bc의 앱에선 받은 메시지의 roomId를 확인 후 해당 채팅 방이 없으니 Room에 '채팅방'을 새로 생성 후 '읽지 않은 메시지'에 저장
우선 메시지를 정의해보자.
class MessageForm(
id: Long,
roomId: Long,
title: String,
body: String,
sender: String,
date: LocalDateTime,
var address: String
) {
var data = MessageData(
id, roomId, title, body, sender, date
)
fun toJson() = "{" +
"\"message\":{" +
"\"token\":\"$address\"," +
"\"data\":{" +
"\"id\":\"${data.id}\"," +
"\"roomId\":\"${data.roomId}\"," +
"\"title\":\"${data.title}\"," +
"\"body\":\"${data.body}\"," +
"\"sender\":\"${data.sender}\"," +
"\"date\":\"${data.date}\"" +
"}}}"
data class MessageData(
var id: Long,
var roomId: Long,
var title: String,
var body: String,
var sender: String,
var date: LocalDateTime
)
}
필요한 필드를 선언하고
Android에서 요구하는 양식에 맞춰 toJson() 함수를 정의하였다.
Stomp가 redirect한 ChatController에서 rawMessage를 파싱해 MessageForm으로 이용한다.
@Controller
class ChatController {
@Autowired
private lateinit var chatService: ChatService
@Autowired
private lateinit var simpleMessage: SimpMessageSendingOperations
private val fcmManager = FcmManager()
@MessageMapping("/chat") //여기로 전송되면 메서드 호출 -> WebSocketConfig prefixes 에서 적용한건 앞에 생략
fun chat(rawMessage: RawMessage): ChatResponseDto {
//채팅 저장
val dto = chatService.addChat(rawMessage)
val roomId = dto.roomId
//TODO: FireBase push notification.
simpleMessage.convertAndSend("/sub/${roomId}", Json.encodeToString(dto))
return dto
}
}
-> TODO를 작성해보자.
FireBase를 관리하는 FirebaseManager를 정의하자.
class FireBaseManager {
@Value("\${firebase_sdk_dir}")
private lateinit var firebaseSdkPath: String
private val fireBaseInstance: FirebaseMessaging by lazy {
val serviceAccount = FileInputStream(firebaseSdkPath)
val options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.build()
FirebaseApp.initializeApp(options)
FirebaseMessaging.getInstance()
}
fun sendToSingleDevice(targetToken: String, dto: ChatResponseDto) {
val message = Message.builder()
.putAllData(dto.toMap())
.setToken(targetToken)
.build()
try {
fireBaseInstance.send(message)
logger.info { "FcmManager:sendToSingleDevice) Successfully sent message" }
} catch (exception: FirebaseMessagingException) {
logger.error { "FcmManager:sendToSingleDevice: $exception" }
}
}
fun sendToTopic(topicUrl: String, dto: ChatResponseDto) {
val message = Message.builder()
.putAllData(dto.toMap())
.setTopic(topicUrl)
.build()
try {
fireBaseInstance.send(message)
logger.info { "FcmManager:sendToTopic) Successfully sent message" }
} catch (exception: FirebaseMessagingException) {
logger.error { "FcmManager:sendToTopic: $exception" }
}
}
}
모든 채팅방은 topic & subcribe로 관리할 예정이지만 직접 기기에 보내는 기능도 이용해보았다.
TODO 아래 작성.
firebaseManager.sendToTopic(
rawMessage.topicUrl, dto
)
해볼라 했는데 jwt를 헤더에 넣어야 해서 간단한 테스트가 어려울거 같다... 낼 앱을 수정해야겠다.