-
Notifications
You must be signed in to change notification settings - Fork 0
[#27] FCM을 이용한 Push 알림 서버 구현 #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| plugins { | ||
| id "org.ec4j.editorconfig" version "0.0.3" | ||
| id "checkstyle" | ||
| } | ||
|
|
||
| java { | ||
| sourceCompatibility = JavaVersion.VERSION_11 | ||
| targetCompatibility = JavaVersion.VERSION_11 | ||
| } | ||
|
|
||
| compileJava.options.encoding = "UTF-8" | ||
| compileTestJava.options.encoding = "UTF-8" | ||
|
|
||
| idea { | ||
| module { | ||
| outputDir file("build/classes/main") | ||
| testOutputDir file("build/classes/test") | ||
| } | ||
| } | ||
|
|
||
| configurations { | ||
| compileOnly { | ||
| extendsFrom annotationProcessor | ||
| } | ||
| } | ||
|
|
||
| ext { | ||
| FIREBASE_ADMIN_VERSION = "6.8.1" | ||
| OKHTTP_VERSION = "4.2.2" | ||
| } | ||
|
|
||
| dependencies { | ||
| implementation("org.springframework.boot:spring-boot-starter-web") | ||
| implementation("com.google.firebase:firebase-admin:${FIREBASE_ADMIN_VERSION}") | ||
| implementation("com.squareup.okhttp3:okhttp:${OKHTTP_VERSION}") | ||
|
|
||
| compileOnly("org.projectlombok:lombok") | ||
| annotationProcessor("org.projectlombok:lombok") | ||
|
|
||
| testImplementation("org.springframework.boot:spring-boot-starter-test") | ||
|
|
||
| testCompileOnly("org.projectlombok:lombok") | ||
| testAnnotationProcessor("org.projectlombok:lombok") | ||
| } | ||
|
|
||
| test { | ||
| useJUnitPlatform() | ||
| } | ||
|
|
||
| editorconfig { | ||
| excludes = ["build"] | ||
| } | ||
|
|
||
| check.dependsOn editorconfigCheck | ||
|
|
||
| checkstyle { | ||
| configFile = file("${project.rootDir}/rule/naver-checkstyle-rules.xml") | ||
| configProperties = [config_loc: ".."] | ||
| toolVersion = "8.24" | ||
| ignoreFailures = false | ||
| maxErrors = 0 | ||
| maxWarnings = 0 | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package com.bluebird.pipit.fcm.controller; | ||
|
|
||
| import com.bluebird.pipit.fcm.dto.PushNotificationRequest; | ||
| import com.bluebird.pipit.fcm.service.PushNotificationService; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.web.bind.annotation.RequestBody; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| @RestController | ||
| @RequiredArgsConstructor | ||
| public class PushNotificationController { | ||
| private final PushNotificationService pushNotificationService; | ||
|
|
||
| public void sendMessage(@RequestBody PushNotificationRequest request) { | ||
| pushNotificationService.sendMessageTo(request.getTargetToken(), request.getTitle(), request.getBody()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| package com.bluebird.pipit.fcm.domain; | ||
|
|
||
| import lombok.AllArgsConstructor; | ||
| import lombok.Builder; | ||
| import lombok.Getter; | ||
|
|
||
| @Builder | ||
| @Getter | ||
| @AllArgsConstructor | ||
| public class PushNotificationMessage { | ||
| private boolean validate_only; | ||
| private Message message; | ||
|
|
||
| @Builder | ||
| @Getter | ||
| @AllArgsConstructor | ||
| public static class Message { | ||
| private Notification notification; | ||
| private String token; | ||
| } | ||
|
|
||
| @Builder | ||
| @Getter | ||
| @AllArgsConstructor | ||
| public static class Notification { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 Notification notification;아니라면 Notification 은 다른 클래스로 빼주는게 자연스러울것 같아요
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| private String title; | ||
| private String body; | ||
| private String image; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package com.bluebird.pipit.fcm.dto; | ||
|
|
||
| import lombok.Value; | ||
|
|
||
| @Value | ||
| public class PushNotificationRequest { | ||
| String targetToken; | ||
| String title; | ||
| String body; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| package com.bluebird.pipit.fcm.service; | ||
|
|
||
| import com.bluebird.pipit.fcm.domain.PushNotificationMessage; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import com.google.auth.oauth2.GoogleCredentials; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import okhttp3.*; | ||
| import com.fasterxml.jackson.core.JsonProcessingException; | ||
| import org.apache.http.HttpHeaders; | ||
| import org.springframework.core.io.ClassPathResource; | ||
| import org.springframework.stereotype.Service; | ||
|
|
||
| import java.io.IOException; | ||
| import java.util.List; | ||
|
|
||
| @Slf4j | ||
| @Service | ||
| @RequiredArgsConstructor | ||
| public class PushNotificationService { | ||
| private static final String FIREBASE_CONFIG_PATH = "firebase/firebase_service_key.json"; // TODO : add firebase project service key file | ||
| private static final String API_URL = "https://fcm.googleapis.com/v1/projects/{firebase-project-id}/messages:send"; | ||
| private static final String OAUTH_SCOPE = "https://www.googleapis.com/auth/cloud-platform"; | ||
|
Comment on lines
+21
to
+23
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 급한건 아니지만 이런것들은 yml 설정파일에 넣어주는 편이 좋을것 같습니다 |
||
|
|
||
| private final ObjectMapper objectMapper; | ||
|
|
||
|
|
||
| private String getAccessToken() { | ||
| GoogleCredentials googleCredentials; | ||
| try { | ||
| googleCredentials = GoogleCredentials | ||
| .fromStream(new ClassPathResource(FIREBASE_CONFIG_PATH).getInputStream()) | ||
| .createScoped(List.of(OAUTH_SCOPE)); | ||
| } catch (IOException e) { | ||
| throw new RuntimeException("Failed to open Firebase Configuration File.", e); | ||
| } | ||
|
|
||
| try { | ||
| googleCredentials.refreshIfExpired(); | ||
| } catch (IOException e) { | ||
| log.error("Failed to refresh Google Credentials.", e); | ||
| } | ||
|
|
||
| return googleCredentials.getAccessToken().getTokenValue(); | ||
| } | ||
|
|
||
| private String makeMessage(String targetToken, String title, String body) throws JsonProcessingException { | ||
| PushNotificationMessage message = PushNotificationMessage.builder() | ||
| .message( | ||
| PushNotificationMessage.Message.builder() | ||
| .token(targetToken) | ||
| .notification( | ||
| PushNotificationMessage.Notification.builder() | ||
| .title(title) | ||
| .body(body) | ||
| .image(null) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 null 은 임시방편인가요?? 아님 항상 null 인건가요?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 임시방편으로 null 값을 넣어놨습니다! |
||
| .build() | ||
| ).build()) | ||
| .validate_only(false) | ||
| .build(); | ||
|
|
||
| return objectMapper.writeValueAsString(message); | ||
|
|
||
| } | ||
|
|
||
| public void sendMessageTo(String targetToken, String title, String body) { | ||
| String message; | ||
| try { | ||
| message = makeMessage(targetToken, title, body); | ||
| } catch (JsonProcessingException e) { | ||
| throw new RuntimeException("Failed to make Message.", e); | ||
| } | ||
|
|
||
| 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()) | ||
| .build(); | ||
|
|
||
| try { | ||
| client.newCall(request).execute(); | ||
| } catch (IOException e) { | ||
| log.error("Failed to send request.", e); | ||
| } | ||
| } | ||
|
|
||
|
|
||
|
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 두개는
@Value하나로 관리할 수 있을것 같습니다There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그리고 Value 사용하게되면 필드에 명시한 private 도 생략가능할것 같습니다.
(default 로 private final 데이터가 유지되므로)