Skip to content

[FEAT] 팔로우 취소 API 개발#85

Merged
Be-HinD merged 2 commits intomainfrom
feat/unfollow
Dec 11, 2025
Merged

[FEAT] 팔로우 취소 API 개발#85
Be-HinD merged 2 commits intomainfrom
feat/unfollow

Conversation

@Be-HinD
Copy link
Member

@Be-HinD Be-HinD commented Dec 11, 2025

📝 Pull Request

📌 PR 종류

해당하는 항목에 체크해주세요.

  • 기능 추가 (Feature)
  • 버그 수정 (Fix)
  • 문서 수정 (Docs)
  • 코드 리팩터링 (Refactor)
  • 테스트 추가 (Test)
  • 기타 변경 (Chore)

✨ 변경 내용

  • 팔로우 취소 API 개발

  • 기존 팔로우 등록 API 엔티티 save 로직 추가 (이전 PR에서 누락)


🔍 관련 이슈

해당 PR이 해결하는 이슈가 있다면 연결해주세요.
Closes #61


🧪 테스트

변경된 기능에 대한 테스트 범위 또는 테스트 결과를 작성해주세요.

  • 유닛 테스트 추가 / 수정
  • 통합 테스트 검증
  • 수동 테스트 완료

Success

image

Fail

image image

🚨 확인해야 할 사항 (Checklist)

PR을 제출하기 전에 아래 항목들을 확인해주세요.

  • 코드 포매팅 완료
  • 불필요한 파일/코드 제거
  • 로직 검증 완료
  • 프로젝트 빌드 성공
  • 린트/정적 분석 통과 (해당 시)

🙋 기타 참고 사항

리뷰어가 참고하면 좋을 만한 추가 설명이 있다면 적어주세요.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added unfollow functionality to cancel following relationships via API
  • Bug Fixes

    • Optimized profile update to skip unnecessary checks when nickname remains unchanged
  • Documentation

    • Clarified follow/unfollow API documentation to explicitly indicate nickname-based requests

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

@Be-HinD Be-HinD self-assigned this Dec 11, 2025
@Be-HinD Be-HinD added the ✨enhancement New feature or request label Dec 11, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 11, 2025

워크스루

팔로우된 사용자를 닉네임 기반으로 언팔로우할 수 있는 새로운 기능을 추가합니다. 이를 위해 새로운 에러 코드, 예외 클래스, 서비스 메서드, 저장소 쿼리, API 엔드포인트가 구현되었으며, 사용자 프로필 업데이트 로직도 개선되었습니다.

변경사항

항목 / 파일(들) 변경 내용
에러 처리 및 예외
src/main/java/team/wego/wegobackend/common/exception/AppErrorCode.java, src/main/java/team/wego/wegobackend/user/exception/NotFoundFollowException.java, src/main/java/team/wego/wegobackend/user/exception/SameUnFollowException.java
NOT_SAME_UNFOLLOW, NOT_FOUND_FOLLOW 에러 코드 추가 및 이에 대응하는 예외 클래스 두 개 신규 추가
서비스 레이어
src/main/java/team/wego/wegobackend/user/application/FollowService.java, src/main/java/team/wego/wegobackend/user/application/UserService.java
unFollow() 메서드 구현 및 유효성 검사 로직 추가; 프로필 업데이트 시 닉네임 변경 시에만 중복 확인 수행
컨트롤러 및 API 문서
src/main/java/team/wego/wegobackend/user/presentation/UserController.java, src/main/java/team/wego/wegobackend/user/presentation/UserControllerDocs.java
DELETE /api/v1/users/unfollow 엔드포인트 신규 추가 및 Swagger 문서화; follow() 메서드 설명 업데이트
저장소
src/main/java/team/wego/wegobackend/user/repository/FollowRepository.java
findByFollowerIdAndFolloweeId() 쿼리 메서드 신규 추가
API 테스트
src/test/http/user/user-api.http
언팔로우 API 테스트 요청 추가

시퀀스 다이어그램

sequenceDiagram
    actor User as 사용자
    participant Controller as UserController
    participant Service as FollowService
    participant Repo as FollowRepository
    participant DB as Database
    
    User->>Controller: DELETE /api/v1/users/unfollow<br/>(unFollowNickname)
    
    Controller->>Service: unFollow(unFollowNickname,<br/>followerId)
    
    Service->>Repo: 팔로워 조회
    Repo->>DB: SELECT by followerId
    DB-->>Repo: follower
    Repo-->>Service: follower
    
    Service->>Repo: 언팔로우 대상 조회
    Repo->>DB: SELECT by nickname
    DB-->>Repo: followee
    Repo-->>Service: followee
    
    alt 자신을 언팔로우 시도
        Service-->>Controller: SameUnFollowException
        Controller-->>User: 400 Bad Request
    else
        Service->>Repo: findByFollowerIdAndFolloweeId()
        Repo->>DB: SELECT Follow 관계
        DB-->>Repo: Follow entity
        Repo-->>Service: Optional<Follow>
        
        alt Follow 관계 없음
            Service-->>Controller: NotFoundFollowException
            Controller-->>User: 400 Bad Request
        else Follow 관계 존재
            Service->>Repo: delete(Follow)
            Repo->>DB: DELETE Follow
            DB-->>Repo: 삭제 완료
            Repo-->>Service: 완료
            Service-->>Controller: void
            Controller-->>User: 201 Created<br/>"팔로우 취소 성공"
        end
    end
Loading

예상 코드 리뷰 시간

🎯 3 (보통) | ⏱️ ~20분

  • 주의 깊게 검토할 영역:
    • FollowService.unFollow() 메서드의 유효성 검사 로직 및 예외 처리 흐름 (자신 언팔로우 방지, Follow 관계 존재 여부 확인)
    • FollowRepository.findByFollowerIdAndFolloweeId() 쿼리의 정확성
    • UserService의 프로필 업데이트 로직 변경이 의도한 대로 작동하는지 확인
    • API 엔드포인트의 응답 코드(201) 및 메시지 적절성

관련된 가능성 있는 PR

  • PR #26: AppErrorCode 확장 및 AppException 기반 도메인 예외 처리 인프라를 도입하였으며, 본 PR의 NotFoundFollowException과 SameUnFollowException이 이에 직접 의존합니다.
  • PR #83: 동일한 팔로우 도메인(AppErrorCode, FollowService, FollowRepository, 예외 클래스)을 수정하여 상호 보완적인 팔로우/언팔로우 기능을 구현합니다.
  • PR #84: 팔로우/언팔로우 흐름을 닉네임 기반으로 변경하고 관련 에러 코드 및 예외를 추가하는 점에서 공통됩니다.

🐰 팔로우에 이어 언팔로우가 나타났네,
ID 대신 닉네임으로 깔끔하게 처리하고,
자기 자신은 언팔로우 못 하도록 방어하며,
관계 없는 팔로우는 찾을 수 없다고 알려주지!
케어한 기능 하나 더 완성되었어! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning UserService.java의 닉네임 검증 로직 개선는 본 PR의 주요 목표와 무관한 범위 외 변경으로 보입니다. UserService.java의 닉네임 존재 여부 검증 로직 개선은 별도 PR로 분리하거나, 이 변경이 필요한 이유를 PR 설명에 명시하세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목 '[FEAT] 팔로우 취소 API 개발'은 변경 사항의 주요 내용인 언팔로우 API 개발을 명확하게 요약하고 있습니다.
Linked Issues check ✅ Passed PR의 모든 코드 변경 사항이 #61 이슈에서 요구한 언팔로우 API 개발 목표를 충족합니다.
✨ 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/unfollow

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

🧹 Nitpick comments (8)
src/main/java/team/wego/wegobackend/user/application/UserService.java (1)

50-57: 닉네임 변경 조건 분기 개선 👍

기존 닉네임과 다를 때만 중복 체크/업데이트하도록 바꾼 점 좋아 보입니다. 불필요한 DB exists 조회가 줄어들어 효율적입니다. 추가로, 닉네임에 공백 문자열 허용 여부를 DTO 레벨(@notblank 등)에서 명확히 해두면 더 안전할 것 같습니다.

src/test/http/user/user-api.http (1)

69-72: 언팔로우 성공/실패 케이스를 명확히 구분하는 것이 좋겠습니다

현재 흐름상 accessToken 은 닉네임을 예은으로 바꾼 첫 번째 사용자이고, 팔로우 대상은 "팔로우 대상"이라서, 아래 언팔로우 요청은 “자기 자신 언팔로우” 예외(SameUnFollowException)만 검증하게 됩니다.

실제 언팔로우 성공 케이스도 확인하려면 예를 들어 다음과 같이 나누는 걸 고려해볼 수 있습니다.

### 팔로우 취소 (성공 케이스)
DELETE http://localhost:8080/api/v1/users/unfollow?unFollowNickname=팔로우 대상
Authorization: Bearer {{accessToken}}

### 팔로우 취소 (본인 언팔로우 예외 케이스)
DELETE http://localhost:8080/api/v1/users/unfollow?unFollowNickname=예은
Authorization: Bearer {{accessToken}}

이렇게 하면 성공/실패 시나리오를 각각 손쉽게 재현할 수 있습니다.

src/main/java/team/wego/wegobackend/user/repository/FollowRepository.java (1)

3-10: 언팔로우용 조회 메서드 추가 적절합니다

Optional<Follow> findByFollowerIdAndFolloweeId 추가로 언팔로우 시 관계 존재 여부를 깔끔하게 처리할 수 있어서 좋습니다.

자잘한 부분이지만, 파라미터 이름 followingIdfolloweeId 정도로 맞춰두면 도메인 용어(팔로워/팔로이)와 더 잘 매칭될 것 같습니다(동작에는 영향 없습니다).

src/main/java/team/wego/wegobackend/user/presentation/UserController.java (1)

117-127: 언팔로우 엔드포인트 동작은 적절하지만 HTTP 상태 코드는 조정이 좋아 보입니다

  • 언팔로우 비즈니스 플로우를 FollowService.unFollow에 위임한 구조는 명확합니다.
  • 다만 DELETE /unfollow 에서 HttpStatus.CREATED(201)를 반환하는 것은 REST 관점에서 다소 어색합니다. 일반적으로 삭제 성공 시에는 200 OK(본문이 있을 때)나 204 No Content를 많이 사용합니다.

예를 들어 다음처럼 맞추는 것을 고려해볼 수 있습니다.

return ResponseEntity
    .status(HttpStatus.OK)
    .body(ApiResponse.success(200, "팔로우 취소 성공"));

또한 메서드/파라미터 이름을 unfollow, unfollowNickname 처럼 소문자 카멜케이스로 통일하면 기존 follow 메서드와도 스타일이 더 일관적일 것 같습니다.

src/main/java/team/wego/wegobackend/common/exception/AppErrorCode.java (1)

30-33: 팔로우/언팔로우 전용 에러 코드 추가 일관성 좋습니다

NOT_SAME_FOLLOW, ALREADY_EXIST_FOLLOW 와 동일한 패턴으로 NOT_SAME_UNFOLLOW, NOT_FOUND_FOLLOW 를 추가해서 도메인 에러가 잘 구분됩니다.

선택 사항으로, NOT_FOUND_FOLLOW 는 리소스 부재에 가까운 의미라 HttpStatus.NOT_FOUND(404)를 사용할지 한 번 더 팀 내에서 논의해 보면 좋겠습니다(현재 400도 큰 문제는 아닙니다만, 의미 상 404가 조금 더 직관적일 수 있습니다).

src/main/java/team/wego/wegobackend/user/application/FollowService.java (2)

27-46: 팔로우 저장 로직 추가로 비즈니스 요구사항이 완성되었습니다

followRepository.save(Follow.builder()...) 를 추가해 실제 팔로우 관계가 영속화되도록 한 점이 핵심 수정으로 보이고, 빌더에 follower, follow 를 명시해서 의도가 잘 드러납니다.

추가로, existsByFollowerIdAndFolloweeId 로 사전 체크 후 save 하는 패턴은 동시성 환경에서 레이스 컨디션이 발생할 수 있으니, DB 레벨에서 (follower_id, followee_id) 유니크 인덱스를 두고 예외를 잡아 처리하는 방식도 한 번 고려해 볼 만합니다.


48-64: 언팔로우 플로우가 follow와 대칭적으로 잘 설계되었습니다

  • follower 조회 → 자기 자신 언팔로우 방지(SameUnFollowException) → 대상 유저 조회 → 관계 조회 후 없으면 NotFoundFollowException, 있으면 delete 하는 흐름이 명확합니다.
  • 클래스 레벨 @Transactional 덕분에 조회·삭제가 하나의 트랜잭션으로 처리되는 점도 적절합니다.

선택적으로,

  • 현재는 컨트롤러에서만 호출되므로 괜찮지만, 다른 호출 경로가 생길 경우를 대비해 unFollowNickname 에 대한 null/blank 검증을 서비스 레이어에서도 한 번 더 해두면 방어적일 수 있습니다.
  • findBy...delete 대신 deleteByFollowerIdAndFolloweeId(...) 같이 한 번에 삭제하고 영향 행 개수로 존재 여부를 판단하는 리포지토리 메서드를 두면, DB 라운드트립을 줄이고 의도가 더 분명해집니다.
src/main/java/team/wego/wegobackend/user/presentation/UserControllerDocs.java (1)

52-56: 언팔로우 메서드/파라미터 네이밍 일관성 개선 제안

  • 메서드명 unFollow, 파라미터/쿼리 파라미터 "unFollowNickname"은 카멜 케이스가 다소 어색하고, 기존 follow/followNickname과도 규칙이 달라 보입니다.
  • unfollow / unfollowNickname처럼 모두 소문자 카멜 케이스로 통일하거나, 두 API 모두 동일하게 "targetNickname" 등으로 맞추는 것을 고려하면 가독성과 유지보수성이 좋아질 것 같습니다(클라이언트와의 API 계약 변경 범위는 한 번 더 검토 필요).

예시:

-    @Operation(summary = "팔로우 취소 API", description = "요청 닉네임에 해당하는 사용자를 팔로우 취소합니다.")
-    ResponseEntity<ApiResponse<String>> unFollow(
+    @Operation(summary = "팔로우 취소 API", description = "요청 닉네임에 해당하는 사용자를 팔로우 취소합니다.")
+    ResponseEntity<ApiResponse<String>> unfollow(
         @AuthenticationPrincipal CustomUserDetails userDetails,
-        @Valid @RequestParam("unFollowNickname") String unFollowNickname
+        @Valid @RequestParam("unfollowNickname") String unfollowNickname
     );
📜 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 f8da740 and 1ea5d3d.

📒 Files selected for processing (9)
  • src/main/java/team/wego/wegobackend/common/exception/AppErrorCode.java (1 hunks)
  • src/main/java/team/wego/wegobackend/user/application/FollowService.java (2 hunks)
  • src/main/java/team/wego/wegobackend/user/application/UserService.java (1 hunks)
  • src/main/java/team/wego/wegobackend/user/exception/NotFoundFollowException.java (1 hunks)
  • src/main/java/team/wego/wegobackend/user/exception/SameUnFollowException.java (1 hunks)
  • src/main/java/team/wego/wegobackend/user/presentation/UserController.java (1 hunks)
  • src/main/java/team/wego/wegobackend/user/presentation/UserControllerDocs.java (1 hunks)
  • src/main/java/team/wego/wegobackend/user/repository/FollowRepository.java (1 hunks)
  • src/test/http/user/user-api.http (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/team/wego/wegobackend/user/application/FollowService.java (4)
src/main/java/team/wego/wegobackend/user/exception/ExistFollowException.java (1)
  • ExistFollowException (6-11)
src/main/java/team/wego/wegobackend/user/exception/NotFoundFollowException.java (1)
  • NotFoundFollowException (6-11)
src/main/java/team/wego/wegobackend/user/exception/SameFollowException.java (1)
  • SameFollowException (7-12)
src/main/java/team/wego/wegobackend/user/exception/SameUnFollowException.java (1)
  • SameUnFollowException (6-11)
🔇 Additional comments (3)
src/main/java/team/wego/wegobackend/user/exception/SameUnFollowException.java (1)

1-11: 언팔로우 자기 자신 방지 예외 정의 좋습니다

SameFollowException과 대칭되는 전용 예외를 분리해 둔 점, AppErrorCode.NOT_SAME_UNFOLLOW와도 일관성이 있어 유지보수 측면에서 깔끔합니다.

src/main/java/team/wego/wegobackend/user/exception/NotFoundFollowException.java (1)

1-11: 팔로우 관계 없음 전용 예외 정의 적절합니다

팔로우 존재 여부 실패를 NotFoundFollowException 으로 명확히 분리해 둔 덕분에 서비스 레이어에서 의도를 드러내기 좋고, AppErrorCode.NOT_FOUND_FOLLOW와도 잘 매핑되어 있습니다.

src/main/java/team/wego/wegobackend/user/presentation/UserControllerDocs.java (1)

46-50: 팔로우 API 닉네임 설명 업데이트 적절합니다

followNickname 쿼리 파라미터와 설명이 잘 맞춰져 있고, 기존 ID 기반 설명을 닉네임 기준으로 정리한 방향 좋습니다. 추가적인 변경 필요 없어 보입니다.

@Be-HinD Be-HinD merged commit 91a2a98 into main Dec 11, 2025
1 check passed
@Be-HinD Be-HinD deleted the feat/unfollow branch December 11, 2025 16:17
@github-project-automation github-project-automation bot moved this from Backlog to Done in WeGo-Together Backend Dec 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨enhancement New feature or request

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant