Skip to content

Commit 176b973

Browse files
authored
Merge pull request #77 from billilge/feat/#76-fcm-push-setup
[Feat/#76] FCM 푸시 발송 초기 세팅
2 parents 5c0288c + d1bc365 commit 176b973

16 files changed

Lines changed: 341 additions & 1 deletion

File tree

‎.dockerignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ local-storage/
1919
.env
2020
.env.*
2121
!.env.example
22+
*firebase-adminsdk*.json
2223

2324
# 문서·기타
2425
docs/

‎.env.example‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,11 @@ KCONNECT_USER_INFO_STUDENT_ID_FIELD=replace-with-field-name
4343
KCONNECT_USER_INFO_NAME_FIELD=replace-with-field-name
4444
KCONNECT_USER_INFO_MAJOR_FIELD=replace-with-field-name
4545
KCONNECT_USER_INFO_ACADEMIC_STATUS_FIELD=replace-with-field-name
46+
47+
### infrastructure:client — 푸시 알림 (log | fcm) ###
48+
# log는 실제 발송 없이 로그만 남긴다 (로컬 기본값)
49+
PUSH_TYPE=log
50+
51+
# type=fcm일 때. Firebase 콘솔 > 프로젝트 설정 > 서비스 계정에서 받은 비공개 키 JSON을 base64로 인코딩한 값
52+
# 예: base64 < firebase-adminsdk.json | tr -d '\n'
53+
FIREBASE_CREDENTIALS_BASE64=replace-with-base64-encoded-service-account-json

‎.gitignore‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,7 @@ out/
3838

3939
.DS_Store
4040

41-
docs/plans
41+
docs/plans
42+
43+
# Firebase 서비스 계정 키 (env로 주입하고 파일은 커밋하지 않는다)
44+
*firebase-adminsdk*.json
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package kr.ac.kookmin.stream.member.domain.notification.client;
2+
3+
import java.util.List;
4+
import kr.ac.kookmin.stream.member.domain.notification.domain.PushMessage;
5+
import kr.ac.kookmin.stream.member.domain.notification.domain.PushSendResult;
6+
7+
public interface PushNotificationClient {
8+
PushSendResult send(List<String> tokens, PushMessage message);
9+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package kr.ac.kookmin.stream.member.domain.notification.domain;
2+
3+
import java.util.Map;
4+
5+
public record PushMessage(String title, String body, Map<String, String> data) {}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package kr.ac.kookmin.stream.member.domain.notification.domain;
2+
3+
public record PushSendOutcome(String token, PushSendStatus status) {}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package kr.ac.kookmin.stream.member.domain.notification.domain;
2+
3+
import java.util.List;
4+
5+
/**
6+
* 토큰별 발송 결과. 멤버와의 매핑은 토큰을 넘긴 호출 측이 갖는다.
7+
* INVALID_TOKEN 정리는 memberId가 아니라 토큰 값으로 해야 발송 이후 새로 등록된 토큰을 지우지 않는다.
8+
*/
9+
public record PushSendResult(List<PushSendOutcome> outcomes) {
10+
11+
public static PushSendResult of(List<String> tokens, PushSendStatus status) {
12+
return new PushSendResult(tokens.stream()
13+
.map(token -> new PushSendOutcome(token, status))
14+
.toList());
15+
}
16+
17+
public List<String> tokensWith(PushSendStatus status) {
18+
return outcomes.stream()
19+
.filter(outcome -> outcome.status() == status)
20+
.map(PushSendOutcome::token)
21+
.toList();
22+
}
23+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package kr.ac.kookmin.stream.member.domain.notification.domain;
2+
3+
public enum PushSendStatus {
4+
SUCCESS,
5+
INVALID_TOKEN, // 만료·삭제되어 다시 보내도 성공할 수 없는 토큰. fcm_token 정리 대상
6+
RETRYABLE, // 일시적 장애로 실패. 해당 토큰만 재시도 대상
7+
FAILED // 설정·요청 문제(자격증명, APNs 키, 다른 Firebase 프로젝트, 잘못된 페이로드 등). 원인을 고치기 전에는 재시도해도 실패
8+
}

‎gradle/libs.versions.toml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ junitBomVersion = "6.0.0"
77
lombokVersion = "1.18.36"
88
springdocVersion = "3.1.1"
99
awsSdkVersion = "2.54.15"
10+
firebaseAdminVersion = "9.11.0"
1011

1112
[libraries]
1213
# BOMs — import as `platform(...)` in modules that don't apply the Boot plugin directly
@@ -47,6 +48,8 @@ mysqlConnectorJ = { module = "com.mysql:mysql-connector-j" }
4748

4849
awsS3 = { module = "software.amazon.awssdk:s3" }
4950

51+
firebaseAdmin = { module = "com.google.firebase:firebase-admin", version.ref = "firebaseAdminVersion" }
52+
5053
jjwtApi = { module = "io.jsonwebtoken:jjwt-api", version.ref = "jjwtVersion" }
5154
jjwtImpl = { module = "io.jsonwebtoken:jjwt-impl", version.ref = "jjwtVersion" }
5255
jjwtJackson = { module = "io.jsonwebtoken:jjwt-jackson", version.ref = "jjwtVersion" }

‎infrastructure/client/build.gradle.kts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ dependencies {
88
implementation(project(":core:common"))
99
implementation(project(":core:domain:auth"))
1010
implementation(project(":core:domain:internal"))
11+
implementation(project(":core:domain:member"))
1112

1213
implementation(platform(libs.springBootDependenciesBom))
1314
implementation(libs.springBootStarter)
@@ -17,6 +18,8 @@ dependencies {
1718
implementation(platform(libs.awsSdkBom))
1819
implementation(libs.awsS3)
1920

21+
implementation(libs.firebaseAdmin)
22+
2023
testImplementation(platform(libs.springBootDependenciesBom))
2124
testImplementation(libs.springBootStarterTest)
2225
testRuntimeOnly(libs.junitPlatformLauncher)

0 commit comments

Comments
 (0)