Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .DS_Store
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ public class FCM_Token extends BaseEntity {
@Column(name = "model", nullable = false, length = 100)
private String model;

public void update(String sFcmToken) {
this.fcmToken = sFcmToken;
public void updateToken(String fcmToken) {
this.fcmToken = fcmToken;
}

@Builder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@
import com.todaysound.todaysound_server.domain.user.entity.User;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface FCMRepository extends JpaRepository<FCM_Token, Long> {
List<FCM_Token> findByUser(User user);

FCM_Token findByUserId(Long userId);

void deleteAllByFcmTokenIn(List<String> fcmTokens);
@Modifying
@Query("DELETE FROM FCM_Token ft WHERE ft.fcmToken IN :fcmTokens")
void deleteAllByFcmTokenIn(@Param("fcmTokens") List<String> fcmTokens);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.google.firebase.messaging.ApnsConfig;
import com.google.firebase.messaging.Aps;
import com.google.firebase.messaging.BatchResponse;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.FirebaseMessagingException;
import com.google.firebase.messaging.MessagingErrorCode;
import com.google.firebase.messaging.MulticastMessage;
Expand Down Expand Up @@ -31,6 +30,7 @@ public class FCMService {

private final FCMRepository fcmRepository;
private final HeaderAuthValidator headerAuthValidator;
private final FirebaseMessagingClient firebaseMessagingClient;

/**
* (핵심 메소드) 특정 User에게 알림을 발송합니다.
Expand Down Expand Up @@ -65,7 +65,7 @@ public void sendNotificationToUser(User user, String title, String body) {
// FCM에 일괄 발송 요청
BatchResponse response;
try {
response = FirebaseMessaging.getInstance().sendEachForMulticast(message);
response = firebaseMessagingClient.sendEachForMulticast(message);

log.info(EXTERNAL_API, "FCM 알림 발송 완료 {} {} {}",
kv("total", response.getSuccessCount() + response.getFailureCount()),
Expand Down Expand Up @@ -135,13 +135,40 @@ private static String maskToken(String token) {
}

@Transactional
public void updateFcmToken(String userUuid, String deviceSecret, String SFcmToken) {
public void updateFcmToken(String userUuid, String deviceSecret, String requestToken) {

User user = headerAuthValidator.validateAndGetUser(userUuid, deviceSecret);

FCM_Token fcmToken = fcmRepository.findByUserId(user.getId());
List<FCM_Token> FCM_Tokens = fcmRepository.findByUser(user);

fcmToken.update(SFcmToken);
if(FCM_Tokens.isEmpty()){
FCM_Token newToken = FCM_Token.create(user, requestToken, "unknown");
fcmRepository.save(newToken);
} else {
FCM_Token existingToken = FCM_Tokens.get(0);
if (!existingToken.getFcmToken().equals(requestToken)) {
existingToken.updateToken(requestToken);
}
}
Comment on lines 142 to 152

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

updateFcmToken 메소드에 몇 가지 개선점이 있습니다.

  1. [Critical] 149행의 !existingToken.equals(requestToken) 비교 로직에 버그가 있습니다. existingTokenFCM_Token 객체이고 requestTokenString이므로, Object.equals()는 항상 false를 반환합니다. 따라서 토큰이 갱신되어야 할 때 갱신되지 않습니다. existingToken.getFcmToken().equals(requestToken)으로 토큰 문자열 값을 직접 비교해야 합니다.

  2. [Medium] 142행의 FCM_Tokens 변수명은 Java 네이밍 컨벤션(camelCase)에 따라 fcmTokens로 수정하는 것이 좋습니다.

Suggested change
List<FCM_Token> FCM_Tokens = fcmRepository.findByUser(user);
fcmToken.update(SFcmToken);
if(FCM_Tokens.isEmpty()){
FCM_Token newToken = FCM_Token.create(user, requestToken, "unknown");
fcmRepository.save(newToken);
} else {
FCM_Token existingToken = FCM_Tokens.get(0);
if (!existingToken.equals(requestToken)) {
existingToken.updateToken(requestToken);
}
}
List<FCM_Token> fcmTokens = fcmRepository.findByUser(user);
if(fcmTokens.isEmpty()){
FCM_Token newToken = FCM_Token.create(user, requestToken, "unknown");
fcmRepository.save(newToken);
} else {
FCM_Token existingToken = fcmTokens.get(0);
if (!existingToken.getFcmToken().equals(requestToken)) {
existingToken.updateToken(requestToken);
}
}


}

@Transactional
public void updateFcmTokenV2(String userUuid, String deviceSecret, String requestToken, String model) {

User user = headerAuthValidator.validateAndGetUser(userUuid, deviceSecret);

List<FCM_Token> fcmTokens = fcmRepository.findByUser(user);

if (fcmTokens.isEmpty()) {
FCM_Token newToken = FCM_Token.create(user, requestToken, model);
fcmRepository.save(newToken);
} else {
FCM_Token existingToken = fcmTokens.get(0);
if (!existingToken.getFcmToken().equals(requestToken)) {
existingToken.updateToken(requestToken);
}
}
Comment on lines +161 to +171

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

현재 updateFcmTokenV2 메소드는 사용자가 여러 기기를 사용하는 경우를 올바르게 처리하지 못할 수 있습니다.
fcmRepository.findByUser(user)로 모든 토큰을 가져온 후, fcmTokens.get(0)으로 리스트의 첫 번째 토큰만 가져와서 업데이트하고 있습니다. 만약 사용자가 여러 기기(예: 아이폰, 아이패드)를 사용한다면, 이 로직은 임의의 기기 토큰을 갱신하게 되어 다른 기기의 푸시 알림이 끊길 수 있습니다.

또한, 기존 토큰이 존재할 경우 토큰 값만 업데이트하고 model 정보는 업데이트하지 않습니다. 사용자가 기기를 변경했을 때 model 정보가 갱신되지 않는 문제가 있습니다.

이 로직은 사용자가 단 하나의 기기만 사용한다고 가정하는 것으로 보입니다. 여러 기기 지원이 필요하다면 아래와 같은 방식으로 재설계하는 것을 고려해 보세요.

  • (user, model)을 기반으로 토큰을 찾아 업데이트하거나,
  • (user, fcmToken)으로 기존 토큰이 있는지 확인하여 없다면 새로 추가하는 로직

}
Comment on lines 137 to 172

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

updateFcmToken 메소드와 updateFcmTokenV2 메소드의 로직이 거의 동일하여 코드 중복이 발생하고 있습니다. model 파라미터를 받는 부분을 제외하면 로직이 같으므로, 하나의 private 메소드로 추출하여 중복을 제거하고 코드 유지보수성을 높이는 것을 권장합니다.

예시:

@Transactional
public void updateFcmToken(String userUuid, String deviceSecret, String requestToken) {
    User user = headerAuthValidator.validateAndGetUser(userUuid, deviceSecret);
    upsertFcmToken(user, requestToken, "unknown");
}

@Transactional
public void updateFcmTokenV2(String userUuid, String deviceSecret, String requestToken, String model) {
    User user = headerAuthValidator.validateAndGetUser(userUuid, deviceSecret);
    upsertFcmToken(user, requestToken, model);
}

private void upsertFcmToken(User user, String requestToken, String model) {
    // 현재 updateFcmTokenV2의 로직을 여기에 구현
    List<FCM_Token> fcmTokens = fcmRepository.findByUser(user);

    if (fcmTokens.isEmpty()) {
        FCM_Token newToken = FCM_Token.create(user, requestToken, model);
        fcmRepository.save(newToken);
    } else {
        FCM_Token existingToken = fcmTokens.get(0);
        if (!existingToken.getFcmToken().equals(requestToken)) {
            existingToken.updateToken(requestToken);
        }
    }
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.todaysound.todaysound_server.global.application;

import com.google.firebase.messaging.BatchResponse;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.FirebaseMessagingException;
import com.google.firebase.messaging.MulticastMessage;
import org.springframework.stereotype.Component;

@Component
public class FirebaseMessagingClient {

public BatchResponse sendEachForMulticast(MulticastMessage message) throws FirebaseMessagingException {
return FirebaseMessaging.getInstance().sendEachForMulticast(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.todaysound.todaysound_server.global.dto;

import jakarta.validation.constraints.NotBlank;

public record FCMUpdateRequestV2(
@NotBlank String fcmToken,
@NotBlank String model
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.todaysound.todaysound_server.domain.user.service.UserQueryService;
import com.todaysound.todaysound_server.global.application.FCMService;
import com.todaysound.todaysound_server.global.dto.FCMUpdateRequest;
import com.todaysound.todaysound_server.global.dto.FCMUpdateRequestV2;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
Expand Down Expand Up @@ -41,6 +42,12 @@ public void updateFcmToken(@RequestHeader("X-User-ID") String userId,
fcmService.updateFcmToken(userId, deviceSecret, request.fcmToken());
}

@PutMapping("/v2")
public void updateFcmTokenV2(@RequestHeader("X-User-ID") String userId,
@RequestHeader("X-Device-Secret") String deviceSecret,
@Valid @RequestBody FCMUpdateRequestV2 request) {
fcmService.updateFcmTokenV2(userId, deviceSecret, request.fcmToken(), request.model());
}

}

Loading