-
Notifications
You must be signed in to change notification settings - Fork 0
[Debug] 회원탈퇴 외래키 제약으로 인한 오류 & 프로필 이미지 url 반환 코드 수정 #133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
442fb47
8220906
5d487f8
f31624d
af53b10
f84b769
37be66f
8307545
c89395e
e271230
16e29ee
dcc3412
107a30c
9943956
32482b8
8d19b22
c849010
4acbc09
af9b5a4
0239089
dfc0983
d3d6f19
ab72c3a
af75e6c
9578c2d
a0410ad
379112a
6e49016
e4604fa
09e7154
2b5ff86
4316265
f360a57
6d46ef5
5a4ff0c
cb5902f
dcbac96
ac07dbc
22a93d2
931b6ac
6f6b2a5
592b436
dc56c8c
65c36ad
8d48aa2
1a1fd2f
e39b7b5
4fa3dff
3937bd5
a86fc8e
22c2b74
8f15e04
7f636a2
089716b
8a20401
8dacd18
f5396fc
619fa60
82e8e73
418e7fe
c7cddec
aef94e9
30d23f3
c5de559
d92614b
f2f06c4
c4d04b6
a2c8b29
888168b
cc5e0c4
d0324c1
29b6b0f
eac38d2
c293cba
d001a3d
12ad54c
189c19b
80a4cb7
a0831d0
3f5f131
08949fe
d9fc2eb
9a87870
9e99154
95bab25
c12ac36
dc1d4ee
60ccbfa
1723fa2
567dd11
50417c0
32f6697
48ed4fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
| } | ||
|
Comment on lines
+99
to
+108
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Find User.java and related entity files
find . -type f -name "User.java" -o -name "*Term*.java" -o -name "*Entity.java" | head -20Repository: Eatsfine/BE Length of output: 337 🏁 Script executed: #!/bin/bash
# Check repository structure
ls -la src/main/java/com/eatsfine/eatsfine/domain/ 2>/dev/null || ls -la . | head -20Repository: Eatsfine/BE Length of output: 1033 🏁 Script executed: #!/bin/bash
# Find all entity files and examine User references
fd "\.java$" src/main/java --type f -x grep -l "@Entity\|@Table" {} \; | head -30Repository: Eatsfine/BE Length of output: 1225 🏁 Script executed: #!/bin/bash
# Read User.java to see the withdraw() and isDeleted() methods and context
cat -n src/main/java/com/eatsfine/eatsfine/domain/user/entity/User.java | head -120Repository: Eatsfine/BE Length of output: 3686 🏁 Script executed: #!/bin/bash
# Read Term.java to check User relationship and cascade config
cat -n src/main/java/com/eatsfine/eatsfine/domain/term/entity/Term.javaRepository: Eatsfine/BE Length of output: 1166 🏁 Script executed: #!/bin/bash
# Search for getCurrentUser implementation
rg -n "getCurrentUser" --type java -A 5Repository: Eatsfine/BE Length of output: 5362 🏁 Script executed: #!/bin/bash
# Search for token invalidation or blacklist mechanism
rg -n "refreshToken|accessToken|token.*invalidat|blacklist|logout" --type java -i | head -40Repository: Eatsfine/BE Length of output: 6367 🏁 Script executed: #!/bin/bash
# Search for where isDeleted() method is actually called
rg -n "\.isDeleted\(\)" --type java -B 3 -A 3Repository: Eatsfine/BE Length of output: 981 🏁 Script executed: #!/bin/bash
# Check security configuration and filter/interceptor for user validation
rg -n "class.*Filter|class.*Interceptor|class.*Advice|SecurityContextHolder" --type java | head -20Repository: Eatsfine/BE Length of output: 708 🏁 Script executed: #!/bin/bash
# Search for batch jobs, scheduled tasks, or cleanup logic
rg -n "@Scheduled|@Transactional.*void|batch|cleanup|purge|delete" --type java -i | grep -i "term\|orphan\|delete\|batch" | head -20Repository: Eatsfine/BE Length of output: 2488 🏁 Script executed: #!/bin/bash
# Check if isDeleted filter exists in repository queries
rg -n "isDeleted|deleted_at" --type java src/main/java/com/eatsfine/eatsfine/domain/user/ -B 2 -A 2Repository: Eatsfine/BE Length of output: 2606 🏁 Script executed: #!/bin/bash
# Check JwtAuthenticationFilter to see if isDeleted is checked there
cat -n src/main/java/com/eatsfine/eatsfine/global/config/jwt/JwtAuthenticationFilter.java | head -80Repository: Eatsfine/BE Length of output: 3602 🏁 Script executed: #!/bin/bash
# Search for all entities that reference User to understand cascade impact scope
rg -n "User.*user|@ManyToOne.*User|@OneToOne.*User" --type java src/main/java/com/eatsfine/eatsfine/domain/ -B 1 -A 1 | head -60Repository: Eatsfine/BE Length of output: 6335 🏁 Script executed: #!/bin/bash
# Check if there's a withdraw service method and how it's implemented
rg -n "withdraw" --type java -B 5 -A 10Repository: Eatsfine/BE Length of output: 9453 🏁 Script executed: #!/bin/bash
# Check if there are any batch jobs or scheduled deletion tasks
fd ".*Batch.*\.java|.*Scheduler.*\.java|.*Job.*\.java" --type f | xargs cat 2>/dev/null | head -50Repository: Eatsfine/BE Length of output: 1548 🏁 Script executed: #!/bin/bash
# Verify if there are any other User references or repositories that do isDeleted filtering
rg -n "findByEmail|findById" --type java src/main/java/com/eatsfine/eatsfine/domain/user/repository/ -A 3Repository: Eatsfine/BE Length of output: 559 🏁 Script executed: #!/bin/bash
# Check if there's any aspect/annotation that globally checks isDeleted
rg -n "@Aspect|@Around|isDeleted" --type java src/main/java/com/eatsfine/eatsfine/global/ -B 2 -A 5Repository: Eatsfine/BE Length of output: 37
소프트 딜리트로 FK 제약을 해결한 점은 좋으나, 다음 사항들을 개선해야 합니다:
🤖 Prompt for AI Agents |
||
|
|
||
| @OneToOne(mappedBy = "user", cascade = CascadeType.REMOVE, orphanRemoval = true, fetch = FetchType.LAZY) | ||
| private Term term; | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
Comment on lines
92
to
98
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial
이 메서드는 데이터를 조회만 하므로 ♻️ 수정 제안 `@Override`
- `@Transactional`
+ `@Transactional`(readOnly = true)
public UserResponseDto.UserInfoDto getMemberInfo(HttpServletRequest request) {As per coding guidelines, "읽기 전용 트랜잭션(readOnly = true)을 적절히 사용했는지." 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| @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; | ||||||||||||||||||||||||||||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||
|
|
@@ -283,4 +293,4 @@ public UserResponseDto.UpdatePasswordDto changePassword( | |||||||||||||||||||||||||||||||
| return UserConverter.toUpdatePasswordResponse(true, LocalDateTime.now(), "비밀번호가 성공적으로 변경되었습니다."); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isDeleted필드에@Builder.Default누락 — Builder로 생성 시null이 됩니다.Lombok
@Builder는 필드 초기화 값(= false)을 무시합니다.UserConverter.toUser()등 Builder로 User를 생성하면isDeleted가null로 설정되어 DB에도null이 저장됩니다.isDeleted()메서드에서 null-safe 처리를 하고 있어 런타임 오류는 없지만, DB 데이터 정합성을 위해@Builder.Default를 추가하거나columnDefinition으로 DB 기본값을 설정해야 합니다.🔧 수정 제안
`@Column`(name = "is_deleted") + `@Builder.Default` private Boolean isDeleted = false;📝 Committable suggestion
🤖 Prompt for AI Agents