Skip to content

feat: 회원 탈퇴 기능 구현 - #175

Open
deli-minju wants to merge 2 commits into
developfrom
feat/#174-user-withdrawal
Open

feat: 회원 탈퇴 기능 구현#175
deli-minju wants to merge 2 commits into
developfrom
feat/#174-user-withdrawal

Conversation

@deli-minju

@deli-minju deli-minju commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

📌 작업 요약

  • 요약:
    • 현재 비밀번호 확인 기반 회원 탈퇴 API 구현
    • 계정 상태 변경 및 Refresh Token·푸시 토큰 무효화
    • 회원 탈퇴 통합 테스트 추가
  • 관련 이슈: closes [Feature] 회원 탈퇴 기능 구현 #174

🌿 브랜치 정보

  • Source: feat/#174-user-withdrawal
  • Target: develop

✅ 체크리스트

  • 브랜치 컨벤션 준수 (feat/refac/hotfix/chore/design/bugfix)
  • 커밋 컨벤션 준수 (feat/fix/refactor/docs/style/chore)
  • self-review 완료
  • 테스트 및 로컬 실행 확인 완료

🧪 테스트 결과

  • ./gradlew.bat --no-daemon spotlessCheck 통과
  • UserControllerIntegrationTest 전체 통과
  • ./gradlew.bat --no-daemon test 전체 통과

Summary by CodeRabbit

  • 새 기능
    • 현재 비밀번호로 본인 회원 탈퇴를 요청할 수 있습니다.
    • 탈퇴 시 계정이 비활성화되고 등록된 푸시 알림 기기도 해제됩니다.
    • 기존 로그인 세션과 리프레시 토큰이 무효화됩니다.
    • 비밀번호가 올바르지 않거나 입력값이 유효하지 않으면 탈퇴가 거부됩니다.

@deli-minju deli-minju self-assigned this Jul 27, 2026
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@deli-minju, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 35 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 67b980bb-c3c2-425c-adc1-90db38ea58f5

📥 Commits

Reviewing files that changed from the base of the PR and between 9b3e93e and 0007666.

📒 Files selected for processing (3)
  • src/main/java/com/gachi/be/domain/user/api/controller/UserController.java
  • src/main/java/com/gachi/be/domain/user/service/UserProfileService.java
  • src/test/java/com/gachi/be/domain/user/api/controller/UserControllerIntegrationTest.java
📝 Walkthrough

Walkthrough

회원 탈퇴 요청 DTO와 DELETE /api/v1/users/me API가 추가되었습니다. 비밀번호 확인 후 사용자를 WITHDRAWN 상태로 변경하고, 리프레시 토큰과 푸시 디바이스 토큰을 무효화하며, 정상·실패 흐름을 통합 테스트합니다.

Changes

회원 탈퇴

Layer / File(s) Summary
탈퇴 요청 계약과 API
src/main/java/com/gachi/be/domain/user/dto/request/UserWithdrawalRequest.java, src/main/java/com/gachi/be/domain/user/api/controller/UserController.java, src/main/java/com/gachi/be/global/code/SuccessCode.java
currentPassword 검증 DTO와 /api/v1/users/me DELETE 엔드포인트, USER_WITHDRAWAL_SUCCESS 응답 코드가 추가되었습니다.
탈퇴 상태 및 토큰 처리
src/main/java/com/gachi/be/domain/user/entity/User.java, src/main/java/com/gachi/be/domain/user/service/UserProfileService.java
비밀번호 검증 후 사용자 상태와 deletedAt을 변경하고, 활성 리프레시 토큰과 푸시 디바이스 토큰을 무효화합니다.
탈퇴 통합 검증
src/test/java/com/gachi/be/domain/user/api/controller/UserControllerIntegrationTest.java
정상 탈퇴 시 상태·토큰 무효화와 기존 인증 차단을, 잘못된 비밀번호 시 오류와 사용자 상태 미변경을 검증합니다.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant UserController
  participant AuthenticatedUserResolver
  participant UserProfileService
  participant PushDeviceTokenRepository
  Client->>UserController: DELETE /api/v1/users/me + currentPassword
  UserController->>AuthenticatedUserResolver: resolveActiveUser(Authorization)
  AuthenticatedUserResolver-->>UserController: 활성 사용자
  UserController->>UserProfileService: withdraw(User, UserWithdrawalRequest)
  UserProfileService->>PushDeviceTokenRepository: 활성 푸시 토큰 조회 및 소프트 삭제
  UserProfileService-->>UserController: 탈퇴 완료
  UserController-->>Client: USER_WITHDRAWAL_SUCCESS
Loading

Suggested labels: feat

Suggested reviewers: hminkyung

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed 현재 비밀번호 검증, WITHDRAWN 전환, deletedAt 기록, 토큰 무효화, 접근 차단 테스트가 모두 반영되었습니다.
Out of Scope Changes check ✅ Passed 컨트롤러, DTO, 서비스, 엔티티, 성공 코드, 통합 테스트만 변경되어 목적과 무관한 추가 변경은 보이지 않습니다.
Title check ✅ Passed 제목이 회원 탈퇴 기능 구현이라는 주요 변경점을 간결하게 잘 요약합니다.
Description check ✅ Passed 요약, 관련 이슈, 브랜치 정보, 체크리스트, 테스트 결과가 템플릿에 맞게 대부분 충실히 작성되었습니다.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#174-user-withdrawal

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.

@deli-minju deli-minju changed the title feat: 회원 탈퇴 기능 구현 (#174) feat: 회원 탈퇴 기능 구현 Jul 27, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/com/gachi/be/domain/user/entity/User.java`:
- Around line 126-132: User 엔티티의 withdraw, changeLanguage,
changeNotificationPreference 동시 변경 경로에 일관된 동시성 제어를 적용하세요. `@Version` 기반 낙관적 잠금을
User에 추가하거나 모든 변경 메서드가 동일한 비관적 잠금으로 최신 엔티티를 읽고 저장하도록 통일해, 탈퇴 커밋 이후 오래된 엔티티가
status와 deletedAt을 덮어쓰지 않게 하세요.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 18fc053c-f359-47dc-a0cd-61f2ff3fdf06

📥 Commits

Reviewing files that changed from the base of the PR and between a746957 and 9b3e93e.

📒 Files selected for processing (6)
  • src/main/java/com/gachi/be/domain/user/api/controller/UserController.java
  • src/main/java/com/gachi/be/domain/user/dto/request/UserWithdrawalRequest.java
  • src/main/java/com/gachi/be/domain/user/entity/User.java
  • src/main/java/com/gachi/be/domain/user/service/UserProfileService.java
  • src/main/java/com/gachi/be/global/code/SuccessCode.java
  • src/test/java/com/gachi/be/domain/user/api/controller/UserControllerIntegrationTest.java

Comment on lines +126 to +132
public void withdraw(OffsetDateTime withdrawnAt) {
if (withdrawnAt == null) {
throw new IllegalArgumentException("withdrawnAt은 비어 있을 수 없습니다.");
}
this.status = UserStatus.WITHDRAWN;
this.deletedAt = withdrawnAt;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# User 엔티티의 버전/동적 업데이트 설정과 사용자 변경 경로의 잠금 사용 여부를 확인합니다.
ast-grep outline src/main/java/com/gachi/be/domain/user/entity/User.java --items all
rg -n -C3 '`@Version`|`@DynamicUpdate`|findByIdWithLock|changeLanguage|changeNotificationPreference' \
  src/main/java/com/gachi/be/domain/user

Repository: GACHI-Project/GACHI-BE

Length of output: 4609


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== User.java relevant section =="
sed -n '1,220p' src/main/java/com/gachi/be/domain/user/entity/User.java

echo "== UserProfileService relevant sections =="
sed -n '1,260p' src/main/java/com/gachi/be/domain/user/service/UserProfileService.java

echo "== UserRepository =="
sed -n '1,90p' src/main/java/com/gachi/be/domain/user/repository/UserRepository.java

echo "== Controller relevant sections =="
sed -n '70,150p' src/main/java/com/gachi/be/domain/user/api/controller/UserController.java

echo "== Search all user state/status persistence paths =="
rg -n -C2 'setLanguage|setNotification|Status|deletedAt|withdraw|updateProfile|changeEmail|updateLanguage|updateNotification|resolvedActiveUser|findAllById|save\(|getOne|getReference' \
  src/main/java/com/gachi/be/domain/user || true

echo "== Behavioral probe for SQL UPDATE semantics in code if JPA annotations exist =="
python3 - <<'PY'
from pathlib import Path
p = Path('src/main/java/com/gachi/be/domain/user/service/UserProfileService.java')
text = p.read_text()
for name in ['changeLanguage', 'changeNotificationPreference', 'findActiveUserWithLock']:
    idx=text.find(name)
    print(f'--- {name} @ {idx} ---')
    print(text[idx:idx+1200])
PY

Repository: GACHI-Project/GACHI-BE

Length of output: 43986


탈퇴 상태가 동시 프로필 변경으로 되돌아갈 수 있습니다.

withdraw는 비관적 잠금으로 다시 읽지만, changeLanguage·changeNotificationPreference는 잠금/버전 검사 없이 먼저 읽은 User를 저장합니다. @Version도 없어 탈퇴 커밋 뒤 오래된 엔티티의 flush가 status=ACTIVE, deletedAt=null을 덮어써 계정을 재활성화될 수 있습니다. @Version 기반 낙관적 잠금을 추가하거나 모든 사용자 변경 경로를 동일한 잠금 전략으로 통일하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/gachi/be/domain/user/entity/User.java` around lines 126 -
132, User 엔티티의 withdraw, changeLanguage, changeNotificationPreference 동시 변경 경로에
일관된 동시성 제어를 적용하세요. `@Version` 기반 낙관적 잠금을 User에 추가하거나 모든 변경 메서드가 동일한 비관적 잠금으로 최신
엔티티를 읽고 저장하도록 통일해, 탈퇴 커밋 이후 오래된 엔티티가 status와 deletedAt을 덮어쓰지 않게 하세요.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 회원 탈퇴 기능 구현

1 participant