Skip to content

fix: 로그인 사용자 동기화 캐시 미적용 문제 해결#805

Merged
huncozyboy merged 3 commits intodevelopfrom
fix/#804
Mar 9, 2026
Merged

fix: 로그인 사용자 동기화 캐시 미적용 문제 해결#805
huncozyboy merged 3 commits intodevelopfrom
fix/#804

Conversation

@huncozyboy
Copy link
Member

@huncozyboy huncozyboy commented Mar 8, 2026

👩‍💻 Contents

1. AuthV2ServiceImpl 내부 호출로 인해 적용되지 않던 캐시 AOP 경계를 외부 Bean으로 분리

  • AuthV2UserCacheCommandServiceAuthV2UserCacheInvalidationService로 분리하면서
    AuthV2ServiceImpl은 유저 동기화만 담당하도록 해주었습니다.

2. 사용자 정보 변경 시 leader, coLeader, orgId 캐시가 올바르게 무효화 + 갱신되는지 검증하는 테스트 코드 구현

  • 테스트 코드는 기존에 있던 형식을 참고해서 작성했습니다.

📝 Review Note

1. 테스트 코드 형식 관련
기존에 구현되어있던 테스트 코드중에서 AuthV2ServiceImpl 와 관련하여 updateOrgIds(), clearCacheForUser()를 직접 검증하던 테스트를 찾지 못하였습니다. 만약 직접 검증하던 테스트가 있었다면 노티해주시면 좋을거같습니다 !

+추가로 테스트 코드는 기존에 있던 작성 형식과 네이밍을 최대한 참고해서 맞춰보려고 했는데, 아직 컨벤션이 익숙하지 않아 변수명이나 테스트 방식에서 컨벤션과 어긋난 부분이 없는지 봐주시면 감사하겠습니다 🙇🏻‍♂️

2. 트랜잭션-캐시 정합성 관련
현재 구조에서는 한가지 트랜잭션 안에서 사용자 정보 변경과 캐시 무효화, 갱신이 함께 수행되는 것으로 이해했습니다.
기능적으로는 문제 없지만, 트랜잭션이 롤백되는 경우 DB와 캐시 간 정합성이 어긋날 수도 있지 않을까하는 걱정이 들기도 했습니다.
따라서 추후에는 @TransactionalEventListener(phase = AFTER_COMMIT) 기반으로 변경 이벤트를 발행하고, 커밋 이후에 캐시를 처리하는 방향을 고려해보면 좋을거같다는 개인적인 생각이 들었습니다.

📣 Related Issue

✅ 점검사항

  • docker-compose.yml 파일에 마이그레이션 한 API의 포워딩을 변경해줬나요?
  • Spring Secret 값을 수정하거나 추가했다면 Github Secret에서 수정을 해줬나요?
  • Nestjs Secret 값을 수정하거나 추가했다면 Docker-Compose.yml 파일 및 인스턴스 내부의 .env 파일을 수정했나요?

@coderabbitai
Copy link

coderabbitai bot commented Mar 8, 2026

Walkthrough

Spring AOP의 자기 자신 호출 문제를 해결하기 위해 캐시 무효화 및 갱신 로직을 별도 Bean으로 분리했습니다. AuthV2ServiceImpl에서 캐시 관련 메서드를 제거하고 외부 서비스로 위임하는 방식으로 리팩토링되었습니다.

Changes

Cohort / File(s) Summary
캐시 무효화 및 갱신 서비스 - 인터페이스
AuthV2UserCacheInvalidationService.java, AuthV2UserCacheCommandService.java
사용자 업데이트/생성 후 캐시 갱신과 특정 캐시 제거 작업을 위한 계약을 정의하는 두 개의 새로운 인터페이스 추가
캐시 무효화 및 갱신 서비스 - 구현
AuthV2UserCacheInvalidationServiceImpl.java, AuthV2UserCacheCommandServiceImpl.java
인터페이스 구현체 추가. 전자는 사용자 업데이트/생성 시 캐시 갱신 로직을 조율하며, 후자는 실제 캐시 제거 및 갱신을 수행
AuthV2ServiceImpl 리팩토링
AuthV2ServiceImpl.java
CoLeaderRepository 제거, 내부 캐시 메서드(clearCacheForUser 등) 삭제, 외부 AuthV2UserCacheInvalidationService 호출로 위임 (+3/-44)
캐시 무효화 서비스 테스트
AuthV2UserCacheInvalidationServiceTest.java
사용자 업데이트 및 생성 시나리오에서 캐시 갱신 위임을 검증하는 단위 테스트
캐시 명령 서비스 통합 테스트
AuthV2UserCacheCommandServiceIntegrationTest.java
Caffeine 캐시 기반 통합 테스트로 실제 캐시 제거 및 갱신 동작 검증
AuthV2Service 단위 테스트
AuthV2ServiceTest.java
로그인 시 캐시 갱신 위임 여부를 검증하는 테스트 시나리오 추가

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client
    participant AuthV2ServiceImpl
    participant AuthV2UserCacheInvalidationService
    participant AuthV2UserCacheCommandService
    participant Cache as Spring Cache<br/>(AOP)
    participant DB as UserRepository

    rect rgba(200, 150, 100, 0.5)
        Note over AuthV2ServiceImpl: 이전 문제: 자기 자신 호출
        Client->>AuthV2ServiceImpl: loginUser(userId)
        AuthV2ServiceImpl->>AuthV2ServiceImpl: clearCacheForUser()<br/>(AOP 미적용)
        Note over Cache: ❌ 캐시 제거 안 됨
    end

    rect rgba(100, 200, 100, 0.5)
        Note over AuthV2ServiceImpl: 개선된 방식: 외부 Bean 호출
        Client->>AuthV2ServiceImpl: loginUser(userId)
        AuthV2ServiceImpl->>AuthV2UserCacheInvalidationService: refreshCachesAfterUserUpdate()
        AuthV2UserCacheInvalidationService->>AuthV2UserCacheCommandService: evictMeetingLeaderCache(userId)
        AuthV2UserCacheCommandService->>Cache: `@CacheEvict` 실행<br/>(AOP 적용 ✓)
        AuthV2UserCacheCommandService->>DB: 필요 시 데이터 조회
        AuthV2UserCacheCommandService->>Cache: `@CachePut` 실행<br/>(AOP 적용 ✓)
        Note over Cache: ✅ 캐시 정상 갱신
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Bean을 분리하니 AOP가 살아나고,
자기 호출의 악몽은 사라졌네!
캐시는 춤을 추고, 데이터는 신선하고,
아키텍처는 한층 더 우아해졌다네. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed PR 변경사항이 #804의 모든 주요 요구사항을 충족합니다. 캐시 무효화·갱신 책임을 별도 Bean(AuthV2UserCacheCommandService, AuthV2UserCacheInvalidationService)으로 분리하여 Spring Cache AOP가 정상 동작하도록 수정했으며, 캐시 동작을 검증하는 테스트도 추가했습니다.
Out of Scope Changes check ✅ Passed 모든 코드 변경이 #804의 범위 내입니다. 캐시 AOP 문제 해결을 위한 서비스 분리, 인터페이스 추가, 테스트 구현이 주요 변경이며, 예제 코드나 불필요한 수정은 없습니다.
Description check ✅ Passed PR 설명은 변경사항과 관련이 있으며, 캐시 AOP 경계 분리와 테스트 코드 추가에 대해 명확하게 설명하고 있습니다.
Title check ✅ Passed PR 제목이 주요 변경사항을 명확하게 요약합니다. Spring AOP 캐시 미적용 문제를 해결하기 위해 캐시 처리 책임을 외부 서비스로 분리한 것이 핵심 변경사항이며, 제목이 이를 정확히 반영합니다.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/#804

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.

@huncozyboy huncozyboy requested a review from khyojun March 8, 2026 14:47
@huncozyboy huncozyboy added the 🐞 fix 기능 버그 label Mar 8, 2026
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.

🧹 Nitpick comments (3)
main/src/test/java/org/sopt/makers/crew/main/auth/v2/service/AuthV2UserCacheInvalidationServiceTest.java (1)

48-52: 중복된 검증 제거 고려

Line 49의 verify(authV2UserCacheCommandService).evictCoLeadersCache(100)와 Line 51의 verify(..., times(1)).evictCoLeadersCache(100)는 동일한 검증입니다. 기본 verify()는 이미 정확히 1회 호출을 검증합니다.

♻️ 제안된 수정
 		verify(authV2UserCacheCommandService).evictMeetingLeaderCache(1);
 		verify(authV2UserCacheCommandService).evictCoLeadersCache(100);
 		verify(authV2UserCacheCommandService).evictCoLeadersCache(200);
-		verify(authV2UserCacheCommandService, times(1)).evictCoLeadersCache(100);
 		verify(authV2UserCacheCommandService).refreshOrgIdCache();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@main/src/test/java/org/sopt/makers/crew/main/auth/v2/service/AuthV2UserCacheInvalidationServiceTest.java`
around lines 48 - 52, The test contains duplicate verifications for
authV2UserCacheCommandService.evictCoLeadersCache(100): both
verify(authV2UserCacheCommandService).evictCoLeadersCache(100) and
verify(authV2UserCacheCommandService, times(1)).evictCoLeadersCache(100) assert
the same single invocation; remove one of them (keep either the default
verify(...) or the explicit times(1)) to avoid redundant assertions in
AuthV2UserCacheInvalidationServiceTest.
main/src/main/java/org/sopt/makers/crew/main/auth/v2/service/AuthV2UserCacheInvalidationServiceImpl.java (1)

23-26: 스트림 체이닝 들여쓰기 일관성

.distinct()의 들여쓰기가 다른 메서드 체이닝과 일관되지 않습니다.

♻️ 제안된 수정
 		coLeaderRepository.findAllByUserIdWithMeeting(userId).stream()
 			.map(coLeader -> coLeader.getMeeting().getId())
-		.distinct()
+			.distinct()
 			.forEach(authV2UserCacheCommandService::evictCoLeadersCache);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@main/src/main/java/org/sopt/makers/crew/main/auth/v2/service/AuthV2UserCacheInvalidationServiceImpl.java`
around lines 23 - 26, The method chain starting at
coLeaderRepository.findAllByUserIdWithMeeting(userId).stream() has inconsistent
indentation for .distinct(); align the `.distinct()` call with the other chained
methods (same indentation level as `.map(...)` and `.forEach(...)`) so the chain
is consistently formatted—locate the stream chain in
AuthV2UserCacheInvalidationServiceImpl (the calls:
coLeaderRepository.findAllByUserIdWithMeeting, .stream(), .map(coLeader ->
coLeader.getMeeting().getId()), .distinct(),
.forEach(authV2UserCacheCommandService::evictCoLeadersCache)) and adjust
whitespace/indentation accordingly.
main/src/main/java/org/sopt/makers/crew/main/auth/v2/service/AuthV2ServiceImpl.java (1)

34-36: 트랜잭션-캐시 정합성에 대한 향후 개선 고려

PR 설명에서 언급하신대로, 현재 캐시 갱신이 같은 트랜잭션 내에서 수행됩니다. 트랜잭션 롤백 시 DB는 원복되지만 캐시는 이미 갱신되어 정합성 문제가 발생할 수 있습니다.

향후 @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) 기반으로 커밋 이후 캐시 처리로 변경하면 이 문제를 해결할 수 있습니다.

Also applies to: 51-59

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@main/src/main/java/org/sopt/makers/crew/main/auth/v2/service/AuthV2ServiceImpl.java`
around lines 34 - 36, Replace the in-transaction direct cache refresh call to
avoid rollback inconsistencies: in AuthV2ServiceImpl stop calling
authV2UserCacheInvalidationService.refreshCachesAfterUserUpdate(...) inside the
transactional flow (where updateUserIfChanged(...) is used) and instead publish
a UserUpdatedEvent (or similar) with the user's id when updateUserIfChanged
returns true; implement a separate listener component that listens for that
event with `@TransactionalEventListener`(phase = TransactionPhase.AFTER_COMMIT)
and invokes
authV2UserCacheInvalidationService.refreshCachesAfterUserUpdate(userId) from the
listener so cache invalidation only runs after the DB transaction commits.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@main/src/main/java/org/sopt/makers/crew/main/auth/v2/service/AuthV2ServiceImpl.java`:
- Around line 34-36: Replace the in-transaction direct cache refresh call to
avoid rollback inconsistencies: in AuthV2ServiceImpl stop calling
authV2UserCacheInvalidationService.refreshCachesAfterUserUpdate(...) inside the
transactional flow (where updateUserIfChanged(...) is used) and instead publish
a UserUpdatedEvent (or similar) with the user's id when updateUserIfChanged
returns true; implement a separate listener component that listens for that
event with `@TransactionalEventListener`(phase = TransactionPhase.AFTER_COMMIT)
and invokes
authV2UserCacheInvalidationService.refreshCachesAfterUserUpdate(userId) from the
listener so cache invalidation only runs after the DB transaction commits.

In
`@main/src/main/java/org/sopt/makers/crew/main/auth/v2/service/AuthV2UserCacheInvalidationServiceImpl.java`:
- Around line 23-26: The method chain starting at
coLeaderRepository.findAllByUserIdWithMeeting(userId).stream() has inconsistent
indentation for .distinct(); align the `.distinct()` call with the other chained
methods (same indentation level as `.map(...)` and `.forEach(...)`) so the chain
is consistently formatted—locate the stream chain in
AuthV2UserCacheInvalidationServiceImpl (the calls:
coLeaderRepository.findAllByUserIdWithMeeting, .stream(), .map(coLeader ->
coLeader.getMeeting().getId()), .distinct(),
.forEach(authV2UserCacheCommandService::evictCoLeadersCache)) and adjust
whitespace/indentation accordingly.

In
`@main/src/test/java/org/sopt/makers/crew/main/auth/v2/service/AuthV2UserCacheInvalidationServiceTest.java`:
- Around line 48-52: The test contains duplicate verifications for
authV2UserCacheCommandService.evictCoLeadersCache(100): both
verify(authV2UserCacheCommandService).evictCoLeadersCache(100) and
verify(authV2UserCacheCommandService, times(1)).evictCoLeadersCache(100) assert
the same single invocation; remove one of them (keep either the default
verify(...) or the explicit times(1)) to avoid redundant assertions in
AuthV2UserCacheInvalidationServiceTest.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2f8596d2-8729-4404-80ff-4969efd27110

📥 Commits

Reviewing files that changed from the base of the PR and between d860d81 and 3b2fe82.

📒 Files selected for processing (8)
  • main/src/main/java/org/sopt/makers/crew/main/auth/v2/service/AuthV2ServiceImpl.java
  • main/src/main/java/org/sopt/makers/crew/main/auth/v2/service/AuthV2UserCacheCommandService.java
  • main/src/main/java/org/sopt/makers/crew/main/auth/v2/service/AuthV2UserCacheCommandServiceImpl.java
  • main/src/main/java/org/sopt/makers/crew/main/auth/v2/service/AuthV2UserCacheInvalidationService.java
  • main/src/main/java/org/sopt/makers/crew/main/auth/v2/service/AuthV2UserCacheInvalidationServiceImpl.java
  • main/src/test/java/org/sopt/makers/crew/main/auth/v2/service/AuthV2ServiceTest.java
  • main/src/test/java/org/sopt/makers/crew/main/auth/v2/service/AuthV2UserCacheCommandServiceIntegrationTest.java
  • main/src/test/java/org/sopt/makers/crew/main/auth/v2/service/AuthV2UserCacheInvalidationServiceTest.java

@huncozyboy huncozyboy changed the title fix: AuthV2 사용자 동기화 캐시 미적용 문제 해결 fix: 로그인 사용자 동기화 캐시 미적용 문제 해결 Mar 8, 2026
@khyojun
Copy link
Member

khyojun commented Mar 9, 2026

너무 좋은데요! 바로 올리면 될 거 같아요!

@huncozyboy
Copy link
Member Author

크루 파이팅 🥐 🚀

@huncozyboy huncozyboy merged commit 9e58920 into develop Mar 9, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐞 fix 기능 버그 size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: 로그인 사용자 동기화시 캐시 무효화, 갱신이 수행되지 않는 문제

2 participants