Skip to content

[FEAT] FCM 로직 수정 및 트라이브 챗 무음 토글 API 추가 #132

Merged
Chhun-Lee merged 3 commits intodevfrom
feat/#75_OAuth2_login
Feb 11, 2026
Merged

[FEAT] FCM 로직 수정 및 트라이브 챗 무음 토글 API 추가 #132
Chhun-Lee merged 3 commits intodevfrom
feat/#75_OAuth2_login

Conversation

@Chhun-Lee
Copy link
Collaborator

@Chhun-Lee Chhun-Lee commented Feb 10, 2026

🎋 이슈 및 작업중인 브랜치

🔑 주요 내용

  • FCM 토큰 중복 문제 로직 수정
  • 트라이브 챗 무음 토글 API 추가

Check List

  • Reviewers 등록을 하였나요?
  • Assignees 등록을 하였나요?
  • 라벨(Label) 등록을 하였나요?
  • PR 머지하기 전 반드시 CI가 정상적으로 작동하는지 확인해주세요!

Summary by CodeRabbit

변경 사항

  • 새로운 기능

    • 트라이브 채팅 무음 설정 기능 추가: 개별 트라이브의 알림을 음소거할 수 있습니다.
  • 개선 사항

    • FCM 토큰 보안 강화: 다중 계정 악용을 방지하도록 개선되었습니다.
    • 무음 설정된 사용자를 제외한 알림 전송으로 사용자 경험 최적화
    • 로그아웃 시 모든 FCM 토큰 자동 비활성화

@Chhun-Lee Chhun-Lee requested a review from jaemin0413 February 10, 2026 21:49
@Chhun-Lee Chhun-Lee self-assigned this Feb 10, 2026
@Chhun-Lee Chhun-Lee added the ✨ FEAT 기능 구현 관련 라벨 label Feb 10, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 10, 2026

📝 Walkthrough

Walkthrough

FCM 토큰 관리를 개선하여 동일 토큰의 다른 사용자 활성 토큰을 비활성화하도록 변경하고, 사용자 트라이브 음소거 기능을 추가하며, 채팅 및 이모지 알림에서 음소거된 사용자를 필터링합니다.

Changes

Cohort / File(s) Summary
FCM 토큰 관리 개선
src/main/java/com/umc/nuvibe/domain/notification/repository/FcmRepository.java, src/main/java/com/umc/nuvibe/domain/notification/service/FcmService.java, src/main/java/com/umc/nuvibe/domain/notification/service/FcmTokenService.java, src/main/java/com/umc/nuvibe/domain/user/service/AuthServiceImpl.java
FcmRepository의 findByToken 반환 타입을 Optional에서 List로 변경하고, 새로운 쿼리 메서드 추가. FcmService에서 저장 시 동일 토큰을 가진 다른 사용자의 활성 FCM 토큰을 비활성화. FcmTokenService의 deactivateToken을 배치 처리로 변경하고 deactivateAllTokens 메서드 추가. AuthServiceImpl의 로그아웃 플로우에 모든 FCM 토큰 비활성화 통합.
사용자 트라이브 음소거 기능
src/main/java/com/umc/nuvibe/domain/tribe/controller/UserTribeController.java, src/main/java/com/umc/nuvibe/domain/tribe/dto/response/userTribe/UserTribeMuteRes.java, src/main/java/com/umc/nuvibe/domain/tribe/entity/UserTribe.java, src/main/java/com/umc/nuvibe/domain/tribe/repository/UserTribeRepository.java, src/main/java/com/umc/nuvibe/domain/tribe/service/userTribe/UserTribeService.java, src/main/java/com/umc/nuvibe/domain/tribe/service/userTribe/UserTribeServiceImpl.java
UserTribe 엔티티에 isMuted 필드와 toggleMute() 메서드 추가. UserTribeRepository에 음소거 상태 확인 및 활성 음소거되지 않은 사용자 조회 메서드 추가. UserTribeController에 PATCH /api/userTribe/{userTribeId}/mute 엔드포인트 및 UserTribeMuteRes DTO 신규 추가. UserTribeService 인터페이스와 구현체에 toggleMute 메서드 추가.
채팅 알림 음소거 통합
src/main/java/com/umc/nuvibe/domain/tribe/service/chat/ChatServiceImpl.java, src/main/java/com/umc/nuvibe/domain/tribe/service/emoji/EmojiServiceImpl.java
ChatServiceImpl의 chatSend에서 참여자 조회 시 findActiveUnmutedUsersByTribeIdExcept 사용. EmojiServiceImpl에서 NOTI_03 알림 전송 전 음소거 상태 확인하여 음소거된 사용자에게는 알림 차단.
결과 코드 추가
src/main/java/com/umc/nuvibe/global/apiPayLoad/result/UserTribeResultCode.java
USERTRIBE_MUTE_SUCCESS 열거형 상수(HttpStatus.OK, "USERTRIBE006") 추가.

Possibly related PRs

Suggested reviewers

  • jaemin0413
  • Neo1228
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 변경 사항의 주요 내용을 명확하게 반영합니다. FCM 로직 수정과 트라이브 챗 무음 토글 API 추가라는 두 가지 주요 변경사항을 정확하게 설명합니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#75_OAuth2_login

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/main/java/com/umc/nuvibe/domain/notification/service/FcmService.java (1)

26-42: ⚠️ Potential issue | 🔴 Critical

재로그인 시 비활성화된 토큰이 재활성화되지 않는 버그가 있습니다.

existsByUserAndToken (line 36)은 isActive 상태를 구분하지 않으므로, 로그아웃 후 동일 토큰으로 재로그인하면:

  1. 기존 레코드가 존재하므로 (비활성 상태) 새 레코드 저장이 스킵됨
  2. 기존 비활성 레코드를 재활성화하는 로직이 없음
  3. 결과적으로 해당 사용자에게 FCM 알림이 전송되지 않음
🐛 기존 토큰 재활성화 로직 추가 제안
     `@Transactional`
     public void registerToken(Long userId, String token) {
         User user = userRepository.findById(userId)
                 .orElseThrow(() -> new BusinessException(UserErrorCode.USER_NOT_FOUND));

         // 1. 같은 토큰을 가진 다른 유저의 행 비활성화 (다계정 문제 방지)
         List<Fcm> otherUserTokens = fcmRepository.findByTokenAndIsActiveTrueAndUserNot(token, user);
         otherUserTokens.forEach(Fcm::deactivate);

-        // 2. 같은 유저 + 같은 토큰이 없을 때만 저장
-        if (!fcmRepository.existsByUserAndToken(user, token)) {
-            fcmRepository.save(Fcm.builder()
-                    .user(user)
-                    .token(token)
-                    .build());
-        }
+        // 2. 같은 유저 + 같은 토큰이 이미 존재하면 재활성화, 없으면 새로 저장
+        Optional<Fcm> existing = fcmRepository.findByUserAndToken(user, token);
+        if (existing.isPresent()) {
+            existing.get().activate();
+        } else {
+            fcmRepository.save(Fcm.builder()
+                    .user(user)
+                    .token(token)
+                    .build());
+        }
     }

이를 위해 FcmRepositoryfindByUserAndToken 메서드와 Fcm 엔티티에 activate() 메서드가 필요합니다.

src/main/java/com/umc/nuvibe/domain/user/service/AuthServiceImpl.java (1)

103-108: ⚠️ Potential issue | 🟠 Major

withdraw 메서드에서 FCM 토큰 비활성화 처리가 누락되었습니다.

logout 메서드에서는 deactivateAllTokens를 호출하여 사용자의 모든 FCM 토큰을 비활성화하지만, withdraw 메서드에서는 사용자 삭제 전에 이 처리를 하지 않습니다. Fcm 엔티티의 외래 키가 ON DELETE SET NULL로 설정되어 있기 때문에 제약 조건 위반은 발생하지 않지만, user_id가 NULL로 설정된 채로 남아있는 고아 레코드가 생깁니다. 이러한 토큰들은 여전히 활성 상태일 수 있어 데이터 무결성 문제가 될 수 있습니다.

logout과 withdraw의 동작을 일관되게 하기 위해, withdraw 메서드에서도 사용자를 삭제하기 전에 deactivateAllTokens를 호출하여 모든 FCM 토큰을 비활성화하는 것을 권장합니다.

🤖 Fix all issues with AI agents
In
`@src/main/java/com/umc/nuvibe/domain/notification/repository/FcmRepository.java`:
- Line 14: The existsByUserAndToken query in FcmRepository currently ignores
token active state causing re-login with same token to be treated as existing;
change the repository method signature from existsByUserAndToken to
existsByUserAndTokenAndIsActiveTrue so only active tokens are considered, or
alternatively in the service layer (where existsByUserAndToken is used together
with updateToken and deactivateAllTokens, e.g., in FcmTokenService or
AuthServiceImpl) detect a matching inactive token and call updateToken to
reactivate it; update usages of the repository method accordingly to ensure
re-login creates or reactivates an active FCM record.

In
`@src/main/java/com/umc/nuvibe/domain/notification/service/FcmTokenService.java`:
- Around line 19-22: deactivateToken currently calls fcmRepository.findByToken
which returns all records and then calls Fcm::deactivate on every result,
causing unnecessary dirty checks and UPDATEs for already inactive tokens; change
the repository call to use a new finder that only returns active tokens (e.g.,
add and use findByTokenAndIsActiveTrue in the repository) and then iterate over
that result in deactivateToken to call Fcm::deactivate only for active entries.

In
`@src/main/java/com/umc/nuvibe/domain/tribe/controller/UserTribeController.java`:
- Around line 91-99: The controller method muteUserTribe currently returns a
success response using Response.of(...); change it to use Response.ok(...)
instead to follow project convention: locate the method muteUserTribe in
UserTribeController and replace the
Response.of(UserTribeResultCode.USERTRIBE_MUTE_SUCCESS, res) return with
Response.ok(UserTribeResultCode.USERTRIBE_MUTE_SUCCESS, res) while keeping the
call to userTribeService.toggleMute(userId, userTribeId) and the
UserTribeMuteRes payload unchanged.

In
`@src/main/java/com/umc/nuvibe/domain/tribe/service/emoji/EmojiServiceImpl.java`:
- Around line 86-101: Extract the duplicated mute-check and notification send
logic into a private helper (e.g., sendEmojiNotificationIfNotMuted) and replace
the two identical blocks in the CREATED and UPDATED branches with a call to that
helper; the helper should accept (User imageOwner, Long reactorUserId, Long
tribeId, Chat chat, Long chatId), return early if imageOwner is null or
imageOwner.getId().equals(reactorUserId), check mute via
userTribeRepository.existsByUser_IdAndTribe_IdAndIsMutedTrue(imageOwner.getId(),
tribeId), fetch reactor nickname via
userRepository.findById(reactorUserId).orElse(null), and call
fcmService.sendNotification(imageOwner, NotificationType.NOTI_03,
chat.getTribe().getImageTag().name(), nickname, chatId, tribeId).

In `@src/main/java/com/umc/nuvibe/domain/user/service/AuthServiceImpl.java`:
- Around line 34-44: Constructor parameter indentation in AuthServiceImpl is
inconsistent: align the indentation of verificationService and fcmTokenService
to match the other parameters in the AuthServiceImpl(...) declaration. Edit the
AuthServiceImpl constructor signature so all parameters (UserRepository,
PasswordEncoder, JwtTokenProvider, EmailVerificationService verificationService,
FcmTokenService fcmTokenService) use the same column alignment/indent level to
maintain consistency.

@@ -13,5 +13,7 @@ public interface FcmRepository extends JpaRepository<Fcm, Long> {

boolean existsByUserAndToken(User user, String token);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how existsByUserAndToken is used
echo "=== Checking existsByUserAndToken usage ==="
rg -n 'existsByUserAndToken' --type=java

echo ""
echo "=== Checking Fcm entity for activate/deactivate methods ==="
fd -t f -name 'Fcm.java' --exec rg -n 'activate|deactivate|isActive' {} +

Repository: Team-Nuvibe/BE

Length of output: 691


🏁 Script executed:

#!/bin/bash
# Find FcmService and examine registerToken method
echo "=== Locating FcmService ==="
fd -t f -name 'FcmService.java'

echo ""
echo "=== Finding all Fcm-related files ==="
fd -t f -name '*Fcm*' -type f

Repository: Team-Nuvibe/BE

Length of output: 603


🏁 Script executed:

#!/bin/bash
# Find Fcm entity and FcmService files
echo "=== Finding Fcm-related files ==="
find . -type f -name "*Fcm*.java"

echo ""
echo "=== Find Service files ==="
find . -type f -name "*FcmService*"

Repository: Team-Nuvibe/BE

Length of output: 790


🏁 Script executed:

#!/bin/bash
# Read FcmService to see registerToken method
echo "=== Examining FcmService.registerToken ==="
cat src/main/java/com/umc/nuvibe/domain/notification/service/FcmService.java

Repository: Team-Nuvibe/BE

Length of output: 2227


🏁 Script executed:

#!/bin/bash
# Check Fcm entity structure
echo "=== Examining Fcm entity ==="
find . -type f -name "Fcm.java" -exec cat {} \;

Repository: Team-Nuvibe/BE

Length of output: 1528


🏁 Script executed:

#!/bin/bash
# Check if there are other places where Fcm tokens are deactivated
echo "=== Searching for deactivate() calls ==="
rg -n 'deactivate\(' --type=java

echo ""
echo "=== Searching for updateToken() calls ==="
rg -n 'updateToken\(' --type=java

echo ""
echo "=== Check if there's any logic for token expiration or cleanup ==="
rg -n 'isActive\s*=\s*false' --type=java

Repository: Team-Nuvibe/BE

Length of output: 519


🏁 Script executed:

#!/bin/bash
# Look for any scheduled tasks or cleanup logic
echo "=== Checking for scheduled tasks or batch operations ==="
rg -n 'Scheduled|@Async|BatchService' --type=java -g '**/*.java' | head -20

Repository: Team-Nuvibe/BE

Length of output: 2201


🏁 Script executed:

#!/bin/bash
# Check NotificationScheduler to see what it does
echo "=== Checking NotificationScheduler for token cleanup ==="
cat src/main/java/com/umc/nuvibe/domain/notification/scheduler/NotificationScheduler.java | head -100

echo ""
echo "=== Check if deactivate is called anywhere (with method reference) ==="
rg -n 'forEach.*deactivate' --type=java

Repository: Team-Nuvibe/BE

Length of output: 4086


🏁 Script executed:

#!/bin/bash
# Check if there's a logout or token cleanup mechanism
echo "=== Searching for logout-related methods ==="
rg -n 'logout|signout|disconnect' --type=java -i | head -20

echo ""
echo "=== Look for any code that modifies isActive field ==="
rg -n 'isActive.*=' --type=java

Repository: Team-Nuvibe/BE

Length of output: 1502


🏁 Script executed:

#!/bin/bash
# Check FcmTokenService to see what deactivate is doing
echo "=== Examining FcmTokenService ==="
cat src/main/java/com/umc/nuvibe/domain/notification/service/FcmTokenService.java

echo ""
echo "=== Examining AuthService logout method ==="
rg -n -A 20 'public void logout' src/main/java/com/umc/nuvibe/domain/user/service/AuthServiceImpl.java

Repository: Team-Nuvibe/BE

Length of output: 1746


🏁 Script executed:

#!/bin/bash
# Check FcmRepository for all query methods
echo "=== Examining FcmRepository for all methods ==="
cat src/main/java/com/umc/nuvibe/domain/notification/repository/FcmRepository.java

Repository: Team-Nuvibe/BE

Length of output: 677


existsByUserAndToken 메서드가 isActive 상태를 검증하지 않아 로그아웃 후 재로그인 시 FCM 토큰이 재등록되지 않는 버그가 있습니다.

로그아웃 시 AuthServiceImpl.logout()에서 fcmTokenService.deactivateAllTokens(user)를 호출하여 사용자의 모든 활성 토큰을 비활성화합니다. 그 후 동일한 토큰으로 재로그인할 때, existsByUserAndToken(user, token)은 비활성화 상태를 무시하고 true를 반환하므로, 새로운 활성 레코드가 생성되지 않습니다. 결과적으로 사용자는 계속해서 비활성 토큰 상태가 유지되어 알림을 수신할 수 없게 됩니다.

해결 방안:

  1. existsByUserAndTokenAndIsActiveTrue로 변경하여 활성 토큰만 확인하거나
  2. 비활성 토큰이 있는 경우 updateToken() 메서드를 호출하여 재활성화하는 로직 추가
🤖 Prompt for AI Agents
In
`@src/main/java/com/umc/nuvibe/domain/notification/repository/FcmRepository.java`
at line 14, The existsByUserAndToken query in FcmRepository currently ignores
token active state causing re-login with same token to be treated as existing;
change the repository method signature from existsByUserAndToken to
existsByUserAndTokenAndIsActiveTrue so only active tokens are considered, or
alternatively in the service layer (where existsByUserAndToken is used together
with updateToken and deactivateAllTokens, e.g., in FcmTokenService or
AuthServiceImpl) detect a matching inactive token and call updateToken to
reactivate it; update usages of the repository method accordingly to ensure
re-login creates or reactivates an active FCM record.

Comment on lines 19 to +22
public void deactivateToken(String token) {
fcmRepository.findByToken(token).ifPresent(Fcm::deactivate);
List<Fcm> fcmList = fcmRepository.findByToken(token);
fcmList.forEach(Fcm::deactivate);
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

deactivateToken에서 이미 비활성화된 토큰도 불필요하게 처리됩니다.

findByTokenisActive 상태와 무관하게 모든 레코드를 반환합니다. 이미 비활성화된 레코드에 대해서도 deactivate()를 호출하게 되어 불필요한 dirty checking 및 UPDATE 쿼리가 발생할 수 있습니다.

♻️ 활성 토큰만 조회하도록 개선

Repository에 findByTokenAndIsActiveTrue 메서드를 추가하고 사용:

-        List<Fcm> fcmList = fcmRepository.findByToken(token);
+        List<Fcm> fcmList = fcmRepository.findByTokenAndIsActiveTrue(token);
         fcmList.forEach(Fcm::deactivate);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public void deactivateToken(String token) {
fcmRepository.findByToken(token).ifPresent(Fcm::deactivate);
List<Fcm> fcmList = fcmRepository.findByToken(token);
fcmList.forEach(Fcm::deactivate);
}
List<Fcm> findByTokenAndIsActiveTrue(String token);
Suggested change
public void deactivateToken(String token) {
fcmRepository.findByToken(token).ifPresent(Fcm::deactivate);
List<Fcm> fcmList = fcmRepository.findByToken(token);
fcmList.forEach(Fcm::deactivate);
}
public void deactivateToken(String token) {
List<Fcm> fcmList = fcmRepository.findByTokenAndIsActiveTrue(token);
fcmList.forEach(Fcm::deactivate);
}
🤖 Prompt for AI Agents
In
`@src/main/java/com/umc/nuvibe/domain/notification/service/FcmTokenService.java`
around lines 19 - 22, deactivateToken currently calls fcmRepository.findByToken
which returns all records and then calls Fcm::deactivate on every result,
causing unnecessary dirty checks and UPDATEs for already inactive tokens; change
the repository call to use a new finder that only returns active tokens (e.g.,
add and use findByTokenAndIsActiveTrue in the repository) and then iterate over
that result in deactivateToken to call Fcm::deactivate only for active entries.

Comment on lines 91 to 99
@PatchMapping("/{userTribeId}/mute")
@Operation(summary = "트라이브 챗 무음 설정", description = "특정 트라이브 챗의 푸시 알림 무음 토글")
public Response<UserTribeMuteRes> muteUserTribe(
@AuthUser Long userId,
@PathVariable Long userTribeId
){
UserTribeMuteRes res = userTribeService.toggleMute(userId, userTribeId);
return Response.of(UserTribeResultCode.USERTRIBE_MUTE_SUCCESS, res);
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🏁 Script executed:

fd -t f -e java Response.java

Repository: Team-Nuvibe/BE

Length of output: 1284


🏁 Script executed:

cat -n src/main/java/com/umc/nuvibe/global/apiPayLoad/response/Response.java

Repository: Team-Nuvibe/BE

Length of output: 2690


성공 응답에는 Response.ok()를 사용하세요.

프로젝트 컨벤션에 따르면, 성공 응답은 Response.of() 대신 Response.ok()를 사용하는 것이 권장됩니다. 두 메서드 모두 동일한 매개변수를 지원하며, ok()를 사용하면 의도가 더 명확합니다.

♻️ 제안된 수정
-        return Response.of(UserTribeResultCode.USERTRIBE_MUTE_SUCCESS, res);
+        return Response.ok(UserTribeResultCode.USERTRIBE_MUTE_SUCCESS, res);
🤖 Prompt for AI Agents
In
`@src/main/java/com/umc/nuvibe/domain/tribe/controller/UserTribeController.java`
around lines 91 - 99, The controller method muteUserTribe currently returns a
success response using Response.of(...); change it to use Response.ok(...)
instead to follow project convention: locate the method muteUserTribe in
UserTribeController and replace the
Response.of(UserTribeResultCode.USERTRIBE_MUTE_SUCCESS, res) return with
Response.ok(UserTribeResultCode.USERTRIBE_MUTE_SUCCESS, res) while keeping the
call to userTribeService.toggleMute(userId, userTribeId) and the
UserTribeMuteRes payload unchanged.

Comment on lines 86 to 101
// NOTI-03: 이미지 작성자에게 리액션 알림 (본인이 본인 이미지에 반응한 경우 제외)
if (imageOwner != null && !imageOwner.getId().equals(userId)) {
User reactor = userRepository.findById(userId).orElse(null);
String nickname = reactor != null ? reactor.getNickname() : "";
fcmService.sendNotification(
imageOwner,
NotificationType.NOTI_03,
chat.getTribe().getImageTag().name(),
nickname,
chatId,
tribeId
);
boolean isMuted = userTribeRepository.existsByUser_IdAndTribe_IdAndIsMutedTrue(imageOwner.getId(), tribeId);
if (!isMuted) {
User reactor = userRepository.findById(userId).orElse(null);
String nickname = reactor != null ? reactor.getNickname() : "";
fcmService.sendNotification(
imageOwner,
NotificationType.NOTI_03,
chat.getTribe().getImageTag().name(),
nickname,
chatId,
tribeId
);
}
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

NOTI_03 알림 전송 로직이 두 곳에서 중복됩니다.

CREATED(Line 86-101)와 UPDATED(Line 121-136)의 무음 체크 및 알림 전송 로직이 거의 동일합니다. 별도 private 메서드로 추출하면 유지보수성이 향상됩니다.

♻️ 리팩토링 제안

클래스 하단에 헬퍼 메서드를 추가하고 두 곳에서 호출:

private void sendEmojiNotificationIfNotMuted(User imageOwner, Long reactorUserId, Long tribeId, Chat chat, Long chatId) {
    if (imageOwner == null || imageOwner.getId().equals(reactorUserId)) return;
    boolean isMuted = userTribeRepository.existsByUser_IdAndTribe_IdAndIsMutedTrue(imageOwner.getId(), tribeId);
    if (isMuted) return;
    User reactor = userRepository.findById(reactorUserId).orElse(null);
    String nickname = reactor != null ? reactor.getNickname() : "";
    fcmService.sendNotification(
            imageOwner,
            NotificationType.NOTI_03,
            chat.getTribe().getImageTag().name(),
            nickname,
            chatId,
            tribeId
    );
}

그리고 두 호출 지점을 다음과 같이 변경:

-            if (imageOwner != null && !imageOwner.getId().equals(userId)) {
-                boolean isMuted = userTribeRepository.existsByUser_IdAndTribe_IdAndIsMutedTrue(imageOwner.getId(), tribeId);
-                if (!isMuted) {
-                    User reactor = userRepository.findById(userId).orElse(null);
-                    String nickname = reactor != null ? reactor.getNickname() : "";
-                    fcmService.sendNotification(
-                            imageOwner,
-                            NotificationType.NOTI_03,
-                            chat.getTribe().getImageTag().name(),
-                            nickname,
-                            chatId,
-                            tribeId
-                    );
-                }
-            }
+            sendEmojiNotificationIfNotMuted(imageOwner, userId, tribeId, chat, chatId);

Also applies to: 121-136

🤖 Prompt for AI Agents
In
`@src/main/java/com/umc/nuvibe/domain/tribe/service/emoji/EmojiServiceImpl.java`
around lines 86 - 101, Extract the duplicated mute-check and notification send
logic into a private helper (e.g., sendEmojiNotificationIfNotMuted) and replace
the two identical blocks in the CREATED and UPDATED branches with a call to that
helper; the helper should accept (User imageOwner, Long reactorUserId, Long
tribeId, Chat chat, Long chatId), return early if imageOwner is null or
imageOwner.getId().equals(reactorUserId), check mute via
userTribeRepository.existsByUser_IdAndTribe_IdAndIsMutedTrue(imageOwner.getId(),
tribeId), fetch reactor nickname via
userRepository.findById(reactorUserId).orElse(null), and call
fcmService.sendNotification(imageOwner, NotificationType.NOTI_03,
chat.getTribe().getImageTag().name(), nickname, chatId, tribeId).

Comment on lines 34 to 44
public AuthServiceImpl(UserRepository userRepository,
PasswordEncoder passwordEncoder,
JwtTokenProvider jwtTokenProvider,
EmailVerificationService verificationService) {
EmailVerificationService verificationService,
FcmTokenService fcmTokenService) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.jwtTokenProvider = jwtTokenProvider;
this.verificationService = verificationService;
this.fcmTokenService = fcmTokenService;
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

생성자 파라미터 들여쓰기가 일관적이지 않습니다.

Line 37의 verificationService와 line 38의 fcmTokenService의 들여쓰기가 다릅니다.

🔧 들여쓰기 통일
     public AuthServiceImpl(UserRepository userRepository,
                           PasswordEncoder passwordEncoder,
                           JwtTokenProvider jwtTokenProvider,
-                          EmailVerificationService verificationService,
-                           FcmTokenService fcmTokenService) {
+                          EmailVerificationService verificationService,
+                          FcmTokenService fcmTokenService) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public AuthServiceImpl(UserRepository userRepository,
PasswordEncoder passwordEncoder,
JwtTokenProvider jwtTokenProvider,
EmailVerificationService verificationService) {
EmailVerificationService verificationService,
FcmTokenService fcmTokenService) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.jwtTokenProvider = jwtTokenProvider;
this.verificationService = verificationService;
this.fcmTokenService = fcmTokenService;
}
public AuthServiceImpl(UserRepository userRepository,
PasswordEncoder passwordEncoder,
JwtTokenProvider jwtTokenProvider,
EmailVerificationService verificationService,
FcmTokenService fcmTokenService) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.jwtTokenProvider = jwtTokenProvider;
this.verificationService = verificationService;
this.fcmTokenService = fcmTokenService;
}
🤖 Prompt for AI Agents
In `@src/main/java/com/umc/nuvibe/domain/user/service/AuthServiceImpl.java` around
lines 34 - 44, Constructor parameter indentation in AuthServiceImpl is
inconsistent: align the indentation of verificationService and fcmTokenService
to match the other parameters in the AuthServiceImpl(...) declaration. Edit the
AuthServiceImpl constructor signature so all parameters (UserRepository,
PasswordEncoder, JwtTokenProvider, EmailVerificationService verificationService,
FcmTokenService fcmTokenService) use the same column alignment/indent level to
maintain consistency.

@Chhun-Lee Chhun-Lee merged commit 7e8ff2f into dev Feb 11, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ FEAT 기능 구현 관련 라벨

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant