배경
프로젝트 진행 중에 스프링에서 프론트(앱)으로 알림을 보내야하는 경우가 생겼음
그래서
개념
구현 순서 요약
- Firebase 프로젝트 생성
- restTemplate 준비
- 프론트에 알림을 전송하는 Util 클래스 작성
- Firebase 의존성 추가
- resource 폴더 + .gitignore에 .json 파일 추가
- 설정파일(application.yml)에 프로젝트 id 추가
- config 패키지에 FirebaseConfig 클래스 작성
- common/fcm 패키지에 FCMessage DTO 작성
- common/fcm 패키지에 FCMUtil 클래스 작성
- 프론트에서 디바이스 토큰을 받아 DB에 저장하는 API 코드 작성
- domain 패키지에 FCMToken Entitiy 작성
- 디바이스 토큰 저장 API 작성 (repository + service + request DTO + controller)
- 알림 관련 API 코드 작성
- domain 패키지에 Notification Entitiy 작성
- 알림 리스트 조회 API 작성 (repository + service + response DTO + controller)
- 알림 삭제 API (repository + service + controller)
1. Firebase 프로젝트 생성
https://console.firebase.google.com/u/0/
로그인 - Google 계정
이메일 또는 휴대전화
accounts.google.com
프로젝트 생성 후, 설정 -> 서비스 계정 -> Admin SDK 구성 스니펫을 자바를 선택하여 다운로드
설정 -> 서비스 계정 | 자바 -> 새 비공개 키 생성 |
![]() |
![]() |
설정 -> 일반 -> 프로젝트 ID 기억
2. restTemplate 준비
config/RestTemplateConfig 클래스 작성
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
3. 프론트에 알림을 전송하는 Util 클래스 작성
Firebase 의존성 추가
dependencies {
....
implementation 'com.google.firebase:firebase-admin:9.2.0'
}
resource 폴더 + .gitignore에 .json 파일 추가
위에서 발급받은 .json 파일을 아래에 추가 ( 이때, 이름은 상관 없음 )
resource 폴더 추가 | .gitignore에 추가 |
![]() |
![]() |
설정파일(application.yml)에 프로젝트 id 추가
위에서 봤던 프로젝트 id를 저장
config 패키지에 FirebaseConfig 클래스 작성
위에서 저장한 .json 파일의 경로를 입력해야함
@Configuration
public class FirebaseConfig {
@PostConstruct
public void init(){
try{
FileInputStream serviceAccount =
new FileInputStream("src/main/resources/firebaseKey.json");
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.build();
FirebaseApp.initializeApp(options);
}catch (Exception e){
e.printStackTrace();
}
}
}
common/fcm 패키지에 FCMessage DTO 작성
알림 서버에 전송할 메세지(DTO) 내용을 작성해야함
메세지 형식
{
"validateOnly" : "false",
"message": {
"token": "target_device_token_here",
"notification": {
"title": "알림 제목",
"body": "알림 내용",
"img": "img url"
},
"data": {
"url": "알림을 눌렀을때 이동할 페이지 url"
}
}
}
코드
public record FCMessage(boolean validateOnly, Message message) {
public record Message(String token, Notification notification, Data data) {
}
public record Notification(String title, String body) {
}
public record Data(String url) {
}
}
common/fcm 패키지에 FCMUtil 클래스 작성
@Component
@RequiredArgsConstructor
@Slf4j
public class FCMUtil { ... }
FCM 메세지 생성 메서드 - makeMessage
아래 정보를 입력받아 FCMessage(DTO)를 생성한 후 json 형식으로 변경
- 디바이스 토큰(String), 알림 제목(String), 알림 내용(String), 알림 클릭시 이동 위치(String)
private final ObjectMapper objectMapper;
private String makeMessage(String targetToken, String title, String body, String targetUrl) {
try {
FCMessage fcMessage = new FCMessage(false, new FCMessage.Message(
targetToken,
new FCMessage.Notification(title, body),
new FCMessage.Data(targetUrl)
));
return objectMapper.writeValueAsString(fcMessage);
} catch (JsonProcessingException e) {
throw new RuntimeException("FCM 메세지 내용 생성 실패");
}
}
AccessToken 발급 메서드 - getAccessToken
FCM 알림 요청을 보낼 때, AccessToken을 발급받아 헤더에 추가해서 요청을 보내야 한다.
private static final String GOOGLE_CLOUD_PLATFORM_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
private final String firebaseConfigPath = "FCMAccountKey.json";
private String getAccessToken(){
try {
GoogleCredentials googleCredentials = GoogleCredentials
.fromStream(new ClassPathResource(firebaseConfigPath).getInputStream())
.createScoped(List.of(GOOGLE_CLOUD_PLATFORM_SCOPE));
googleCredentials.refreshIfExpired();
return googleCredentials.getAccessToken().getTokenValue();
} catch (IOException e) {
throw new RuntimeException("AccessToken 얻기 실패");
}
}
FCM 메시지 전송 메서드 - sendMessageTo
HTTP 요청 메시지 생성 후, restTemplate을 통해 Firebase 프로젝트에 요청 전송
- body: makeMessage()를 통해 생성
- header: getAccessToken()을 통해 생성
@Value("${fcm.project-name}")
private String projectName;
private static final String API_URL = "https://fcm.googleapis.com/v1/projects/%s/messages:send";
private final RestTemplate restTemplate;
public void sendMessageTo(String targetToken, String title, String body, String targetUrl) {
try {
String message = makeMessage(targetToken, title, body, targetUrl);
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(getAccessToken());
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(message, headers);
ResponseEntity<String> response = restTemplate.exchange(
String.format(API_URL, projectName),
HttpMethod.POST,
entity,
String.class
);
log.info("FCM Response: {}", response.getBody());
} catch (Exception e) {
throw new RuntimeException("FCM 전송 실패"); //custom exception 으로 변경 가능
}
}
4. 프론트에서 디바이스 토큰을 받아 DB에 저장하는 API 코드 작성
spring-data-jpa 의존성 추가해서 해야함
domain 패키지에 FCMToken Entitiy 작성
- 디바이스 토큰 저장 API 작성 (repository + service + request DTO + controller)
참고자료
https://velog.io/@dionisos198/스프링-푸시-알림-구현예제FCM-사용