diff --git a/src/main/java/com/eatsfine/eatsfine/domain/user/converter/UserConverter.java b/src/main/java/com/eatsfine/eatsfine/domain/user/converter/UserConverter.java index d99117ff..0f1d6ebe 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/user/converter/UserConverter.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/user/converter/UserConverter.java @@ -30,10 +30,10 @@ public static UserResponseDto.LoginResponseDto toLoginResponse(User user, String // 유저 정보 조회 응답 변환 - public static UserResponseDto.UserInfoDto toUserInfo(User user) { + public static UserResponseDto.UserInfoDto toUserInfo(User user, String profileImageUrl) { return UserResponseDto.UserInfoDto.builder() .id(user.getId()) - .profileImage(user.getProfileImage()) + .profileImage(profileImageUrl) .email(user.getEmail()) .name(user.getName()) .phoneNumber(user.getPhoneNumber()) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/user/entity/User.java b/src/main/java/com/eatsfine/eatsfine/domain/user/entity/User.java index bd2fc6e3..e232c147 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/user/entity/User.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/user/entity/User.java @@ -6,6 +6,9 @@ import com.eatsfine.eatsfine.global.common.BaseEntity; import jakarta.persistence.*; import lombok.*; + +import java.time.LocalDateTime; + @Entity @Getter // 수정한 부분: access 레벨을 PROTECTED로 설정하여 Hibernate가 접근할 수 있게 합니다. @@ -46,6 +49,12 @@ public class User extends BaseEntity { @Column(length = 500) private String refreshToken; + @Column(name = "deleted_at") + private LocalDateTime deletedAt; + + @Column(name = "is_deleted") + private Boolean isDeleted = false; + public void updateName(String name) { this.name = name; } @@ -87,6 +96,17 @@ public void linkSocial (SocialType socialType, String socialId){ this.socialId = socialId; } + // 회원 탈퇴 메서드 추가 + public void withdraw() { + this.isDeleted = true; + this.deletedAt = LocalDateTime.now(); + this.refreshToken = null; // refresh token도 null 처리 + } + + public boolean isDeleted() { + return this.isDeleted != null && this.isDeleted; + } + @OneToOne(mappedBy = "user", cascade = CascadeType.REMOVE, orphanRemoval = true, fetch = FetchType.LAZY) private Term term; } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/user/service/userService/UserServiceImpl.java b/src/main/java/com/eatsfine/eatsfine/domain/user/service/userService/UserServiceImpl.java index f773a80f..fdd8dc38 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/user/service/userService/UserServiceImpl.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/user/service/userService/UserServiceImpl.java @@ -67,6 +67,10 @@ public UserResponseDto.LoginResponseDto login(UserRequestDto.LoginDto loginDto) User user = userRepository.findByEmail(loginDto.getEmail()) .orElseThrow(() -> new UserException(UserErrorStatus.MEMBER_NOT_FOUND)); + if (user.isDeleted()) { + throw new UserException(UserErrorStatus.WITHDRAWN_USER); + } + // 2) 비밀번호 검증 if (!passwordEncoder.matches(loginDto.getPassword(), user.getPassword())) { throw new UserException(UserErrorStatus.INVALID_PASSWORD); @@ -85,12 +89,12 @@ public UserResponseDto.LoginResponseDto login(UserRequestDto.LoginDto loginDto) .refreshToken(refreshToken) .build(); } - @Override @Transactional public UserResponseDto.UserInfoDto getMemberInfo(HttpServletRequest request) { User user = getCurrentUser(request); - return UserConverter.toUserInfo(user); + String profileUrl = s3Service.toUrl(user.getProfileImage()); + return UserConverter.toUserInfo(user, profileUrl); } @Override @@ -191,6 +195,7 @@ private void validateProfileImage(MultipartFile file) { } + @Override @Transactional public void withdraw(HttpServletRequest request) { @@ -205,8 +210,8 @@ public void withdraw(HttpServletRequest request) { } } - user.updateRefreshToken(null); - userRepository.delete(user); + user.withdraw(); + userRepository.save(user); } @Override @@ -225,8 +230,13 @@ private User getCurrentUser(HttpServletRequest request) { String email = jwtTokenProvider.getEmailFromToken(token); - return userRepository.findByEmail(email) + User user = userRepository.findByEmail(email) .orElseThrow(() -> new UserException(UserErrorStatus.MEMBER_NOT_FOUND)); + + if (user.isDeleted()) { + throw new UserException(UserErrorStatus.WITHDRAWN_USER); + } + return user; } @Override @@ -283,4 +293,4 @@ public UserResponseDto.UpdatePasswordDto changePassword( return UserConverter.toUpdatePasswordResponse(true, LocalDateTime.now(), "비밀번호가 성공적으로 변경되었습니다."); } -} \ No newline at end of file +} diff --git a/src/main/java/com/eatsfine/eatsfine/domain/user/status/UserErrorStatus.java b/src/main/java/com/eatsfine/eatsfine/domain/user/status/UserErrorStatus.java index 5041af6e..a4c8b147 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/user/status/UserErrorStatus.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/user/status/UserErrorStatus.java @@ -16,8 +16,8 @@ public enum UserErrorStatus implements BaseErrorCode { EMAIL_ALREADY_EXISTS(HttpStatus.CONFLICT, "MEMBER4003", "이미 존재하는 이메일입니다."), INVALID_PASSWORD(HttpStatus.BAD_REQUEST, "MEMBER4004", "비밀번호가 올바르지 않습니다."), PASSWORD_NOT_MATCH(HttpStatus.BAD_REQUEST, "MEMBER4005", "현재 비밀번호가 일치하지 않습니다."), - SAME_PASSWORD(HttpStatus.BAD_REQUEST, "MEMBER4006", "새 비밀번호가 현재 비밀번호와 동일합니다.") - + SAME_PASSWORD(HttpStatus.BAD_REQUEST, "MEMBER4006", "새 비밀번호가 현재 비밀번호와 동일합니다."), + WITHDRAWN_USER(HttpStatus.FORBIDDEN, "MEMBER4007", "탈퇴한 회원입니다.") ; private final HttpStatus httpStatus; diff --git a/src/main/java/com/eatsfine/eatsfine/global/config/SecurityConfig.java b/src/main/java/com/eatsfine/eatsfine/global/config/SecurityConfig.java index c4691884..f3d189ac 100644 --- a/src/main/java/com/eatsfine/eatsfine/global/config/SecurityConfig.java +++ b/src/main/java/com/eatsfine/eatsfine/global/config/SecurityConfig.java @@ -95,12 +95,12 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http.build(); } - @Bean public HttpCookieOAuth2AuthorizationRequestRepository cookieAuthorizationRequestRepository() { return new HttpCookieOAuth2AuthorizationRequestRepository(); } + @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); @@ -122,6 +122,7 @@ public CorsConfigurationSource corsConfigurationSource() { return source; } + @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder();