Skip to content

Conversation

@yerinchun
Copy link
Contributor

@yerinchun yerinchun commented Dec 31, 2025

#️⃣ 연관된 이슈

관련된 이슈 번호를 적어주세요.
Close #130

✨ 작업 내용 (Summary)

이번 PR에서 작업한 내용을 간략히 설명해주세요. (이미지 첨부 가능)

팔로우/언팔로우 API 호출 시 User 엔티티의 followerCountfollowingCount가 업데이트되지 않는 버그가 있었습니다.

  1. User 엔티티에 카운트 증감 메서드 추가
  2. FollowService에 카운트 업데이트 로직 추가

✅ 변경 사항 체크리스트

다음 항목들을 확인하고 체크해주세요.

  • 코드에 영향이 있는 모든 부분에 대한 테스트를 작성하고 실행했나요?
  • 문서를 작성하거나 수정했나요? (필요한 경우)
  • 중요한 변경 사항이 팀에 공유되었나요?

🧪 테스트 결과

코드 변경에 대해 테스트를 수행한 결과를 요약해주세요.

  • 테스트 환경: 로컬
  • 테스트 방법: 스웨거
  • 결과 요약: 두 개의 계정으로 테스트

📸 스크린샷

관련된 스크린샷 또는 GIF가 있다면 여기에 첨부해주세요.

image

followerCount, followingCount 증가

image

followerCount 감소

💬 리뷰 요구사항

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요.


📎 참고 자료

관련 문서, 레퍼런스 링크 등이 있다면 여기에 첨부해주세요.

Summary by CodeRabbit

  • New Features

    • 동시성 상황에서 팔로우/언팔로우/승인 작업의 자동 재시도로 신뢰성 향상 (경합 시 동작 안정화)
    • 팔로우 상태에 따라 팔로워·팔로잉 수가 정확히 증감되도록 실시간 반영
  • Bug Fixes

    • 언팔·승인 흐름에서 발생하던 카운트 불일치 및 음수 카운트 문제 해결
  • Chores

    • 사용자 데이터 버전 관리 도입 및 기존 데이터 초기화 마이그레이션 적용

✏️ Tip: You can customize this high-level summary in your review settings.

@yerinchun yerinchun self-assigned this Dec 31, 2025
@yerinchun yerinchun added 🐞 bug 버그 이슈 예린 labels Dec 31, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 31, 2025

📝 Walkthrough

Walkthrough

팔로우 생성·취소·승인 흐름에 사용자 팔로워·팔로잉 카운트를 동기화하는 로직과 낙관적 락 기반 retry 지원이 추가되었고, User 엔티티에 @Version 필드와 카운트 증감용 공개 메서드 4개가 추가되었습니다. DB 마이그레이션으로 기존 user 테이블에 version 컬럼을 idempotently 추가합니다.

Changes

Cohort / File(s) 변경 요약 (주의 지점)
팔로우 서비스 로직
src/main/java/com/hrr/backend/domain/follow/service/FollowService.java
@Retryable(OptimisticLockingFailureException) 적용 및 사용자 조회를 선행하여 followUser/unfollowUser/approveFollowRequest/approve 흐름에서 상태에 따라 followerCount/followingCount를 증감하도록 변경(증감 후 저장). 미사용 import 제거. 동시성 재시도 동작 검증 필요.
User 엔티티: 버전 및 카운트 조작자 추가
src/main/java/com/hrr/backend/domain/user/entity/User.java
@Version private Long version 추가(낙관적 락용), incrementFollowerCount(), decrementFollowerCount(), incrementFollowingCount(), decrementFollowingCount() 공개 메서드 추가(감소 시 0 미만 방지). 엔티티 상태 변이 메서드 API 변경 없음(새 메서드 추가).
DB 마이그레이션
src/main/resources/db/migration/V2.20__add_version_to_user
version BIGINT NOT NULL DEFAULT 0 컬럼을 idempotently 추가하고 NULL 초기화하는 스크립트(저장 프로시저 생성·실행·삭제). 운영 DB 마이그레이션 계획(백업/배포 순서) 검증 필요.
빌드 / 앱 설정
build.gradle, src/main/java/com/hrr/backend/HrrBackendApiApplication.java
spring-retryspring-aspects 의존성 추가, @EnableRetry 애플리케이션에 적용. AOP 설정 또는 프록시 관련 부작용 검토 필요(프록시 대상 빈, final 클래스 등).

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant FollowService
    participant UserRepo as UserRepository
    participant FollowRepo as FollowRepository
    participant DB

    Note over Client,FollowService: 팔로우 요청 (followUser)
    Client->>FollowService: POST /follow/{targetId}
    FollowService->>UserRepo: find currentUser, find targetUser
    FollowService->>FollowRepo: save Follow(status: PENDING or APPROVED)
    alt status == APPROVED
        FollowService->>UserRepo: currentUser.incrementFollowingCount()
        FollowService->>UserRepo: targetUser.incrementFollowerCount()
        UserRepo->>DB: save currentUser, targetUser (optimistic version update)
    end
    FollowRepo->>DB: persist Follow
    FollowService-->>Client: 응답 (메시지: 요청/완료)
Loading
sequenceDiagram
    participant Client
    participant FollowService
    participant FollowRepo as FollowRepository
    participant UserRepo as UserRepository
    participant DB

    Note over Client,FollowService: 언팔로우 요청 (unfollowUser)
    Client->>FollowService: DELETE /follow/{followId}
    FollowService->>FollowRepo: find follow by id
    FollowService->>UserRepo: find currentUser, find unfollowedUser
    alt follow.status == APPROVED
        FollowService->>UserRepo: currentUser.decrementFollowingCount()
        FollowService->>UserRepo: unfollowedUser.decrementFollowerCount()
        UserRepo->>DB: save currentUser, unfollowedUser (optimistic version update)
    end
    FollowRepo->>DB: delete Follow
    FollowService-->>Client: 응답 (성공)
Loading
sequenceDiagram
    participant Client
    participant FollowService
    participant FollowRepo as FollowRepository
    participant UserRepo as UserRepository
    participant DB

    Note over Client,FollowService: 팔로우 요청 승인 (approveFollowRequest)
    Client->>FollowService: POST /follow/{followId}/approve
    FollowService->>FollowRepo: find follow
    FollowService->>UserRepo: find requester, find currentUser
    FollowService->>FollowRepo: update follow.status = APPROVED
    FollowService->>UserRepo: requester.incrementFollowingCount()
    FollowService->>UserRepo: currentUser.incrementFollowerCount()
    UserRepo->>DB: save requester, currentUser (optimistic version update)
    FollowRepo->>DB: save follow
    FollowService-->>Client: 응답 (승인)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 분

Possibly related PRs

🔧 카운트가 춤추듯 맞물려
팔로우, 승인, 언팔로우 — 숫자 하나하나 챙겨요
버전은 지키미, 메서드는 손길로
재시도는 조용한 수호자, 충돌은 줄어들고
데이터는 더 단단히 연결되었네 ✨

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly and concisely summarizes the main change: updating user follower/following counts during follow/unfollow operations.
Linked Issues check ✅ Passed Code changes fully address issue #130 requirements: User entity now includes count increment/decrement methods, and FollowService implements count updates during follow, unfollow, and approval operations.
Out of Scope Changes check ✅ Passed All changes are scoped to issue #130: User entity modifications, FollowService logic, database migration for versioning, and Spring Retry integration for optimistic locking—all necessary for implementing count updates.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings

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: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d0c96c8 and f9d2cfe.

📒 Files selected for processing (2)
  • src/main/java/com/hrr/backend/domain/follow/service/FollowService.java
  • src/main/java/com/hrr/backend/domain/user/entity/User.java
🔇 Additional comments (3)
src/main/java/com/hrr/backend/domain/follow/service/FollowService.java (3)

77-81: LGTM! 공개 계정 팔로우 시 카운트 업데이트가 정확합니다 ✨

APPROVED 상태일 때만 카운트를 증가시키는 조건 분기가 올바르게 구현되었습니다. 공개 계정과 비공개 계정을 구분하여 처리하는 로직이 명확합니다.


104-125: 언팔로우 로직이 정확하게 구현되었습니다 👍

APPROVED 상태였던 팔로우 관계만 카운트를 감소시키는 조건이 올바릅니다. PENDING 상태의 요청 취소 시에는 카운트가 변경되지 않아야 하므로, 이 구현이 정확합니다.


145-168: 팔로우 요청 승인 시 카운트 업데이트가 올바르게 처리되었습니다 🎯

PENDING 상태에서 APPROVED로 전환될 때 카운트를 증가시키는 타이밍이 정확합니다. 전체 팔로우 플로우에서 카운트 관리가 일관되게 구현되었습니다.

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: 3

🧹 Nitpick comments (1)
src/main/resources/db/migration/V2.20__add_version_to_user (1)

17-18: 불필요한 UPDATE 문을 제거하는 것을 권장합니다.

Line 14에서 version 컬럼을 NOT NULL DEFAULT 0로 정의했기 때문에, MySQL은 컬럼 추가 시 기존 모든 행에 자동으로 0을 설정합니다. Line 18의 UPDATE user SET version = 0 WHERE version IS NULL은 실행되어도 매칭되는 행이 없습니다 (NOT NULL 제약 조건 때문).

이 UPDATE 문은 동작에 영향을 주지 않지만, 불필요한 코드이므로 제거하면 마이그레이션 스크립트가 더 명확해집니다.

🔎 제안하는 수정
    END IF;

-    -- 기존 데이터에 version 초기값 설정 (혹시 NULL인 경우 대비)
-    UPDATE user SET version = 0 WHERE version IS NULL;
-
 END $$

참고: NOT NULL DEFAULT 제약 조건에 대한 MySQL 동작은 공식 문서를 참조하세요.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f9d2cfe and e660154.

📒 Files selected for processing (2)
  • src/main/java/com/hrr/backend/domain/user/entity/User.java
  • src/main/resources/db/migration/V2.20__add_version_to_user
🔇 Additional comments (3)
src/main/resources/db/migration/V2.20__add_version_to_user (1)

1-22: 마이그레이션 구조가 안전합니다! 👍

프로시저를 사용한 멱등성(idempotent) 패턴이 잘 구현되어 있습니다. information_schema를 확인하여 중복 실행 시에도 안전하게 처리되도록 설계되었습니다.

src/main/java/com/hrr/backend/domain/user/entity/User.java (2)

172-175: 팔로워 카운트 증가 로직이 정확합니다.

간단하고 명확한 구현입니다. @Version 필드가 동시성 문제를 처리하므로 추가적인 동기화는 필요하지 않습니다.


184-187: 팔로잉 카운트 증가 로직이 정확합니다.

incrementFollowerCount와 동일한 패턴으로 일관성 있게 구현되었습니다.

Comment on lines +177 to +182
//팔로워 카운트 감소
public void decrementFollowerCount() {
if (this.followerCount > 0) {
this.followerCount--;
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

카운트 감소 시 음수 방지 로직을 개선할 수 있습니다.

현재 구현은 followerCount가 0일 때 감소 시도를 조용히 무시합니다. 이는 음수를 방지하지만, 애플리케이션 상태 불일치 버그를 숨길 수 있습니다.

시나리오 예시:

  • 팔로워가 0명인 사용자에게 언팔로우 처리 시도
  • 현재: 아무 일도 일어나지 않음 (silent failure)
  • 문제: 팔로우/언팔로우 로직의 버그가 감춰짐

개선 제안:

public void decrementFollowerCount() {
    if (this.followerCount <= 0) {
        throw new IllegalStateException(
            "팔로워 카운트가 이미 0입니다. 현재 값: " + this.followerCount
        );
    }
    this.followerCount--;
}

또는 최소한 경고 로그를 남기는 것을 권장합니다.

참고: 방어적 프로그래밍의 fail-fast 원칙에 대해서는 Effective Java, Item 76을 참조하세요.

🤖 Prompt for AI Agents
In src/main/java/com/hrr/backend/domain/user/entity/User.java around lines 177
to 182, the current decrementFollowerCount silently ignores attempts to
decrement when followerCount is 0 which can hide bugs; change the method to
fail-fast by throwing an IllegalStateException when followerCount is <= 0 (or,
if throwing is unacceptable for your flows, at minimum emit a warning log) and
otherwise decrement the counter—ensure the exception/log message includes the
current followerCount for easier debugging.

Comment on lines +189 to +194
// 팔로잉 카운트 감소
public void decrementFollowingCount() {
if (this.followingCount > 0) {
this.followingCount--;
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

카운트 감소 시 음수 방지 로직을 개선할 수 있습니다.

decrementFollowerCount (lines 177-182)와 동일한 문제가 있습니다. 카운트가 0일 때 감소 시도를 조용히 무시하여 버그를 숨길 수 있습니다.

동일하게 예외를 던지거나 최소한 경고 로그를 남기는 것을 권장합니다.

개선 제안:

public void decrementFollowingCount() {
    if (this.followingCount <= 0) {
        throw new IllegalStateException(
            "팔로잉 카운트가 이미 0입니다. 현재 값: " + this.followingCount
        );
    }
    this.followingCount--;
}
🤖 Prompt for AI Agents
In src/main/java/com/hrr/backend/domain/user/entity/User.java around lines 189
to 194, the decrementFollowingCount method silently ignores attempts to
decrement when followingCount is 0 which can hide bugs; change it to mirror
decrementFollowerCount by validating followingCount and throwing an
IllegalStateException (or at minimum logging a warning) when followingCount <=
0, otherwise decrement the field—use a clear error message like "팔로잉 카운트가 이미
0입니다. 현재 값: " + this.followingCount to aid debugging.

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: 0

🧹 Nitpick comments (1)
build.gradle (1)

84-87: Spring Retry 의존성 추가 확인 완료! 🎯

OptimisticLockException 재시도 처리를 위한 의존성이 올바르게 추가되었습니다.

참고 사항:

  • spring-retry: 재시도 로직 제공 (@retryable, RetryTemplate)
  • spring-aspects: AspectJ 기반 AOP 지원으로 @retryable 어노테이션 활성화

개선 제안:

  1. 주석 위치를 다른 섹션과 일관되게 맞춰주세요 (85번 줄의 주석을 84번 줄로 이동)
  2. 재시도 설정(최대 시도 횟수, backoff 정책 등)을 명시적으로 구성하는 것을 고려해보세요. 현재는 @retryable의 기본값(maxAttempts=3, backoff=1초)을 사용하게 됩니다.
📚 재시도 설정 예시

@Configuration 클래스에서 재시도 정책을 명시적으로 정의할 수 있습니다:

@Configuration
@EnableRetry
public class RetryConfig {
    
    @Bean
    public RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();
        
        // 재시도 정책: 최대 3회 시도
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(3);
        
        // Backoff 정책: 100ms부터 시작해서 2배씩 증가
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setInitialInterval(100);
        backOffPolicy.setMultiplier(2.0);
        backOffPolicy.setMaxInterval(1000);
        
        retryTemplate.setRetryPolicy(retryPolicy);
        retryTemplate.setBackOffPolicy(backOffPolicy);
        
        return retryTemplate;
    }
}

Spring Retry 공식 문서: https://docs.spring.io/spring-retry/docs/api/current/

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e660154 and e6d2111.

📒 Files selected for processing (3)
  • build.gradle
  • src/main/java/com/hrr/backend/HrrBackendApiApplication.java
  • src/main/java/com/hrr/backend/domain/follow/service/FollowService.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/hrr/backend/domain/follow/service/FollowService.java
🔇 Additional comments (1)
src/main/java/com/hrr/backend/HrrBackendApiApplication.java (1)

9-9: Spring Retry 설정이 깔끔하게 완성되었습니다! ✨

애플리케이션 레벨에서 @EnableRetry를 활성화하여 전역 재시도 기능이 올바르게 구현되었습니다.

동작 원리:

  • FollowService의 followUser(), unfollowUser(), approveFollowRequest() 메서드에서 OptimisticLockingFailureException 발생 시 최대 3회 재시도 (100ms 간격)
  • Spring AOP 프록시를 통해 작동하므로 같은 클래스 내부 호출(self-invocation)에서는 적용되지 않음
  • build.gradle에 spring-retry와 spring-aspects 의존성이 정상 구성됨

개선 제안:

  1. 재시도 로직 문서화: 왜 낙관적 잠금 예외에 대한 재시도가 필요한지 클래스 또는 메서드 수준의 JavaDoc에 추가하면 좋습니다. 이후 유지보수자들이 의도를 더 잘 이해할 수 있습니다.

  2. @recover 메서드 검토: 현재 3회 재시도 후에도 실패하면 예외가 그대로 전파됩니다. 필요하다면 @Recover 메서드를 추가하여 재시도 실패 시 폴백 로직(예: 로깅, 알림)을 구현할 수 있습니다.

Copy link
Contributor

@yc3697 yc3697 left a comment

Choose a reason for hiding this comment

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

확인했습니다 수고하셨어요~

@yerinchun yerinchun merged commit abb2097 into develop Jan 1, 2026
2 checks passed
@yerinchun yerinchun deleted the bug/130-follow-count-update branch January 1, 2026 17:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

예린 🐞 bug 버그 이슈

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] 팔로우/팔로우 취소 시 사용자 카운트 업데이트

3 participants