Skip to content

Commit 261334f

Browse files
authored
Merge pull request #78 from Hsu-Connect/feat/#67
[#67]Feat: 계정탈퇴 API 구현완료
2 parents 5ccb2fe + c622490 commit 261334f

File tree

6 files changed

+81
-0
lines changed

6 files changed

+81
-0
lines changed

src/main/java/hansung/hansung_connect/auth/controller/AuthController.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
package hansung.hansung_connect.auth.controller;
22

3+
import hansung.hansung_connect.auth.dto.AccountDeleteRequest;
4+
import hansung.hansung_connect.auth.dto.AccountDeleteResponse;
35
import hansung.hansung_connect.auth.dto.LoginRequest;
46
import hansung.hansung_connect.auth.dto.LoginResponse;
57
import hansung.hansung_connect.auth.dto.LogoutRequest;
68
import hansung.hansung_connect.auth.dto.OnboardingRequest;
79
import hansung.hansung_connect.auth.service.AuthService;
810
import hansung.hansung_connect.auth.token.JwtAuthFilter.SimpleUserPrincipal;
11+
import hansung.hansung_connect.common.response.ApiResponse;
912
import io.swagger.v3.oas.annotations.Operation;
1013
import io.swagger.v3.oas.annotations.Parameter;
1114
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
1215
import io.swagger.v3.oas.annotations.tags.Tag;
16+
import jakarta.validation.Valid;
1317
import lombok.RequiredArgsConstructor;
1418
import org.springframework.security.core.annotation.AuthenticationPrincipal;
19+
import org.springframework.web.bind.annotation.DeleteMapping;
1520
import org.springframework.web.bind.annotation.PostMapping;
1621
import org.springframework.web.bind.annotation.RequestBody;
1722
import org.springframework.web.bind.annotation.RequestMapping;
@@ -72,6 +77,23 @@ public void logout(
7277
@RequestBody LogoutRequest req) {
7378
authService.logout(me.id(), req.refreshToken());
7479
}
80+
81+
@Operation(
82+
summary = "계정 탈퇴",
83+
description = """
84+
현재 로그인한 사용자의 계정을 삭제합니다.
85+
- 전달받은 refreshToken의 소유자가 본인인지 확인 후 진행
86+
- 사용자 레코드 삭제 및 해당 사용자의 모든 RefreshToken 삭제
87+
""",
88+
security = @SecurityRequirement(name = "bearerAuth")
89+
)
90+
@DeleteMapping("/withdraw")
91+
public ApiResponse<AccountDeleteResponse> withdraw(
92+
@AuthenticationPrincipal SimpleUserPrincipal me,
93+
@Valid @RequestBody AccountDeleteRequest req
94+
) {
95+
return ApiResponse.onSuccess(authService.withdraw(me.id(), req));
96+
}
7597
}
7698

7799

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package hansung.hansung_connect.auth.converter;
2+
3+
import hansung.hansung_connect.auth.dto.AccountDeleteResponse;
4+
import java.time.Instant;
5+
6+
public class AuthConverter {
7+
8+
private AuthConverter() {
9+
}
10+
11+
public static AccountDeleteResponse toDeleteResponse(Long userId) {
12+
return new AccountDeleteResponse(userId, Instant.now());
13+
}
14+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package hansung.hansung_connect.auth.dto;
2+
3+
import jakarta.validation.constraints.NotBlank;
4+
5+
public record AccountDeleteRequest(
6+
@NotBlank String refreshToken // 본인 확인 및 탈취 방지 목적
7+
) {
8+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package hansung.hansung_connect.auth.dto;
2+
3+
import java.time.Instant;
4+
5+
public record AccountDeleteResponse(
6+
Long userId,
7+
Instant deletedAt
8+
) {
9+
}

src/main/java/hansung/hansung_connect/auth/service/AuthService.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package hansung.hansung_connect.auth.service;
22

3+
import hansung.hansung_connect.auth.converter.AuthConverter;
4+
import hansung.hansung_connect.auth.dto.AccountDeleteRequest;
5+
import hansung.hansung_connect.auth.dto.AccountDeleteResponse;
36
import hansung.hansung_connect.auth.dto.LoginResponse;
47
import hansung.hansung_connect.auth.dto.OnboardingRequest;
58
import hansung.hansung_connect.auth.kakao.KakaoClient;
@@ -69,4 +72,28 @@ public void onboarding(Long userId, OnboardingRequest req) {
6972
public void logout(Long userId, String refreshToken) {
7073
refreshTokenRepository.deleteByToken(refreshToken);
7174
}
75+
76+
@Transactional
77+
public AccountDeleteResponse withdraw(Long userId, AccountDeleteRequest req) {
78+
// 1) 전달된 refreshToken이 유효한지, 그리고 주인이 본인인지 검증
79+
var jwt = req.refreshToken();
80+
if (!tokenProvider.validate(jwt) || !tokenProvider.isRefreshToken(jwt)) {
81+
// 프로젝트의 전역 예외 체계에 맞게 치환 가능
82+
throw new IllegalArgumentException("유효하지 않은 리프레시 토큰입니다.");
83+
}
84+
Long ownerId = tokenProvider.getUserId(jwt);
85+
if (!ownerId.equals(userId)) {
86+
throw new IllegalArgumentException("요청자와 토큰 소유자가 일치하지 않습니다.");
87+
}
88+
89+
// 2) 리프레시 토큰 전부 삭제
90+
refreshTokenRepository.deleteAllByUser_Id(userId);
91+
92+
// 3) 사용자 삭제(하드 딜리트)
93+
User user = userRepository.findById(userId).orElseThrow();
94+
userRepository.delete(user);
95+
96+
// 4) 응답
97+
return AuthConverter.toDeleteResponse(userId);
98+
}
7299
}

src/main/java/hansung/hansung_connect/domain/user/controller/UserController.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public ResponseEntity<ApiResponse<UserResponseDTO.MyProfileResponse>> getMyProfi
7676
@Operation(
7777
summary = "내 프로필(기본 정보) 부분수정",
7878
description = """
79+
`🔒 JWT 인증(토큰) 필요` <br>
7980
학번, 이름, 전공, 멘토참여여부, 구직여부만 부분 수정합니다.
8081
<br><br>
8182
- PATCH 이므로 전달된 필드만 반영합니다(null은 무시)

0 commit comments

Comments
 (0)