-
Notifications
You must be signed in to change notification settings - Fork 0
[Feat] 회원 탈퇴 기능 #17
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
[Feat] 회원 탈퇴 기능 #17
Changes from all commits
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 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -45,6 +45,8 @@ public void logout(HttpServletRequest request, RefreshTokenRequest tokenRequest) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String accessToken = jwtUtil.extractAccessToken(request) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .orElseThrow(() -> new CustomAuthenticationException(ErrorCode.SECURITY_INVALID_ACCESS_TOKEN)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.info("LogOut Access Token: {}", accessToken); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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. 프로덕션 환경에서 토큰 로깅 주의 필요 액세스 토큰 전체를 로그에 남기는 것은 보안상 위험할 수 있습니다. 프로덕션 환경에서는 토큰의 일부만 마스킹하여 로깅하는 것을 권장합니다. 다음과 같이 마스킹 처리를 고려해보세요: -log.info("LogOut Access Token: {}", accessToken);
+log.info("LogOut Access Token: {}****", accessToken.substring(0, Math.min(10, accessToken.length())));또는 프로덕션 프로파일에서는 DEBUG 레벨로 변경: -log.info("LogOut Access Token: {}", accessToken);
+log.debug("LogOut Access Token: {}", accessToken);🤖 Prompt for AI Agents보안 문제: 로그에 액세스 토큰 노출. 액세스 토큰을 로그에 기록하면 로그 파일을 통해 토큰이 유출될 수 있습니다. 프로덕션 환경에서 이는 심각한 보안 위험입니다. 다음 중 하나를 선택하여 적용하세요: 옵션 1 (권장): 토큰 마스킹 -log.info("LogOut Access Token: {}", accessToken);
+log.info("LogOut Access Token: {}...", accessToken.substring(0, Math.min(10, accessToken.length())));옵션 2: 로그 레벨을 DEBUG로 낮추고 프로덕션에서는 비활성화 -log.info("LogOut Access Token: {}", accessToken);
+log.debug("LogOut Access Token: {}", accessToken);옵션 3: 완전히 제거 -log.info("LogOut Access Token: {}", accessToken);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String refreshToken = tokenRequest.getRefreshToken(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jwtUtil.validateToken(refreshToken); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!"refresh".equals(jwtUtil.getTokenType(refreshToken))) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -56,13 +58,13 @@ public void logout(HttpServletRequest request, RefreshTokenRequest tokenRequest) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| invalidAccessToken(accessToken); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public ReissueResponse reissueTokens(RefreshTokenRequest tokenRequest) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public ReissueResponse reissueTokens(RefreshTokenRequest tokenRequest, Long userId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String refreshToken = tokenRequest.getRefreshToken(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jwtUtil.validateToken(refreshToken); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!"refresh".equals(jwtUtil.getTokenType(refreshToken))) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new CustomJwtException(ErrorCode.INVALID_REFRESH_TYPE); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return reissueAndSendTokens(refreshToken); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return reissueAndSendTokens(refreshToken, userId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+61
to
+67
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. 토큰 재발급 시 사용자 ID 검증 추가 권장 현재 다음과 같이 검증 로직을 추가하는 것을 권장합니다: public ReissueResponse reissueTokens(RefreshTokenRequest tokenRequest, Long userId) {
String refreshToken = tokenRequest.getRefreshToken();
jwtUtil.validateToken(refreshToken);
if (!"refresh".equals(jwtUtil.getTokenType(refreshToken))) {
throw new CustomJwtException(ErrorCode.INVALID_REFRESH_TYPE);
}
+
+ // 리프레시 토큰의 userId와 인증된 userId 일치 검증
+ Long tokenUserId = jwtUtil.getUserId(refreshToken);
+ if (!tokenUserId.equals(userId)) {
+ throw new CustomAuthenticationException(ErrorCode.SECURITY_UNAUTHORIZED);
+ }
+
return reissueAndSendTokens(refreshToken, userId);
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI AgentsuserId 검증 누락.
다음 diff를 적용하여 userId 검증을 추가하세요: public ReissueResponse reissueTokens(RefreshTokenRequest tokenRequest, Long userId) {
String refreshToken = tokenRequest.getRefreshToken();
jwtUtil.validateToken(refreshToken);
if (!"refresh".equals(jwtUtil.getTokenType(refreshToken))) {
throw new CustomJwtException(ErrorCode.INVALID_REFRESH_TYPE);
}
+
+ // userId 검증: refresh token의 userId와 파라미터로 받은 userId가 일치하는지 확인
+ Long tokenUserId = jwtUtil.getUserId(refreshToken);
+ if (!tokenUserId.equals(userId)) {
+ throw new CustomAuthenticationException(ErrorCode.SECURITY_UNAUTHORIZED);
+ }
+
return reissueAndSendTokens(refreshToken, userId);
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void checkLogout(String accessToken) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -72,30 +74,30 @@ public void checkLogout(String accessToken) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void storeRefreshToken(String refreshToken) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| redisService.setValues(REFRESH_TOKEN_KEY_PREFIX, refreshToken, Duration.ofMillis(REFRESH_TOKEN_EXPIRED_IN)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void storeRefreshToken(String refreshToken, Long userId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| redisService.setValues(REFRESH_TOKEN_KEY_PREFIX+refreshToken, String.valueOf(userId), Duration.ofMillis(REFRESH_TOKEN_EXPIRED_IN)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void deleteRefreshToken(String refreshToken){ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if(refreshToken == null){ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new CustomJwtException(ErrorCode.INVALID_REFRESH_TYPE); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| redisService.delete(refreshToken); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| redisService.delete(REFRESH_TOKEN_KEY_PREFIX+refreshToken); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void invalidAccessToken(String accessToken) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void invalidAccessToken(String accessToken) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| redisService.setValues(accessToken, LOGOUT_VALUE, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Duration.ofMillis(ACCESS_TOKEN_EXPIRED_IN)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private ReissueResponse reissueAndSendTokens(String refreshToken) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private ReissueResponse reissueAndSendTokens(String refreshToken, Long userId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 새로운 Refresh Token 발급 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String reissuedAccessToken = jwtUtil.createAccessToken(jwtUtil.getUserId(refreshToken), jwtUtil.getProviderId(refreshToken), jwtUtil.getRole(refreshToken), jwtUtil.getName(refreshToken)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String reissuedRefreshToken = jwtUtil.createRefreshToken(jwtUtil.getUserId(refreshToken), jwtUtil.getProviderId(refreshToken), jwtUtil.getName(refreshToken)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 새로운 Refresh Token을 DB나 Redis에 저장 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| storeRefreshToken(reissuedRefreshToken); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| storeRefreshToken(reissuedRefreshToken, userId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 기존 Refresh Token 폐기 (DB나 Redis에서 삭제) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| deleteRefreshToken(refreshToken); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,10 @@ | |
|
|
||
| import com.WhoIsRoom.WhoIs_Server.domain.auth.dto.request.MailRequest; | ||
| import com.WhoIsRoom.WhoIs_Server.domain.auth.dto.request.PasswordRequest; | ||
| import com.WhoIsRoom.WhoIs_Server.domain.auth.exception.CustomAuthenticationException; | ||
| import com.WhoIsRoom.WhoIs_Server.domain.auth.service.JwtService; | ||
| import com.WhoIsRoom.WhoIs_Server.domain.auth.service.MailService; | ||
| import com.WhoIsRoom.WhoIs_Server.domain.auth.util.JwtUtil; | ||
| import com.WhoIsRoom.WhoIs_Server.domain.club.model.Club; | ||
| import com.WhoIsRoom.WhoIs_Server.domain.club.repository.ClubRepository; | ||
| import com.WhoIsRoom.WhoIs_Server.domain.member.model.Member; | ||
|
|
@@ -15,12 +18,15 @@ | |
| import com.WhoIsRoom.WhoIs_Server.domain.user.repository.UserRepository; | ||
| import com.WhoIsRoom.WhoIs_Server.global.common.exception.BusinessException; | ||
| import com.WhoIsRoom.WhoIs_Server.global.common.response.ErrorCode; | ||
| import jakarta.servlet.http.HttpServletRequest; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.security.core.context.SecurityContextHolder; | ||
| import org.springframework.security.crypto.password.PasswordEncoder; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| import java.net.http.HttpRequest; | ||
| import java.util.*; | ||
| import java.util.stream.Collectors; | ||
|
|
||
|
|
@@ -33,6 +39,8 @@ public class UserService { | |
| private final MailService mailService; | ||
| private final MemberRepository memberRepository; | ||
| private final ClubRepository clubRepository; | ||
| private final JwtUtil jwtUtil; | ||
| private final JwtService jwtService; | ||
|
|
||
| @Transactional | ||
| public void signUp(SignupRequest request) { | ||
|
|
@@ -169,4 +177,19 @@ private void validateClubExistence(Set<Long> clubIds) { | |
| throw new BusinessException(ErrorCode.CLUB_NOT_FOUND); | ||
| } | ||
| } | ||
|
|
||
| public void deleteAccount (HttpServletRequest request, Long userId) { | ||
| User user = userRepository.findById(userId) | ||
| .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND)); | ||
|
|
||
| userRepository.delete(user); | ||
|
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. 🛠️ Refactor suggestion | 🟠 Major 🧩 Analysis chainMember 엔티티 cascade 설정 확인이 필요합니다. User 삭제 시 연관된 Member 레코드들이 올바르게 cascade 삭제되는지 확인해주세요. 만약 cascade 설정이 없다면 foreign key 제약 위반이 발생할 수 있습니다. 다음 스크립트를 실행하여 User 엔티티의 Member 관계 cascade 설정을 확인하세요: 🏁 Script executed: #!/bin/bash
# User 엔티티에서 Member와의 관계 설정 확인
# User 엔티티 파일에서 @OneToMany 어노테이션과 cascade 설정 찾기
rg -A 5 '@OneToMany.*Member' --type java -g '**/User.java'
# Member 엔티티에서 User와의 관계 설정 확인
rg -A 3 '@ManyToOne.*User' --type java -g '**/Member.java'Length of output: 0 User 엔티티와 Member 엔티티 간 cascade 설정을 아직 확인하지 못했습니다. 전체 코드베이스에서 @onetomany와 @manytoone 어노테이션을 검색해보세요: 🏁 Script executed: #!/bin/bash
# 엔티티 관계 설정 재검증
# OneToMany 어노테이션 전체 검색
rg -C3 "@OneToMany" --type java
# ManyToOne 어노테이션 전체 검색
rg -C3 "@ManyToOne" --type javaLength of output: 1104 Member 관계에 대한 cascade 삭제 설정 추가 필요 🤖 Prompt for AI Agents |
||
|
|
||
| String accessToken = jwtUtil.extractAccessToken(request) | ||
| .orElseThrow(() -> new CustomAuthenticationException(ErrorCode.SECURITY_INVALID_ACCESS_TOKEN)); | ||
| if (accessToken != null) { | ||
| jwtService.invalidAccessToken(accessToken); // Redis에 blacklist 저장 | ||
| } | ||
|
|
||
| SecurityContextHolder.clearContext(); | ||
| } | ||
|
Comment on lines
+181
to
+194
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. 🛠️ Refactor suggestion | 🟠 Major 🧩 Analysis chain회원 탈퇴 로직에 트랜잭션 처리 및 순서 개선 필요 현재 구현에는 몇 가지 개선이 필요한 부분이 있습니다:
다음과 같이 개선을 권장합니다: +@Transactional
public void deleteAccount (HttpServletRequest request, Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));
- userRepository.delete(user);
-
String accessToken = jwtUtil.extractAccessToken(request)
.orElseThrow(() -> new CustomAuthenticationException(ErrorCode.SECURITY_INVALID_ACCESS_TOKEN));
- if (accessToken != null) {
- jwtService.invalidAccessToken(accessToken); // Redis에 blacklist 저장
- }
+ jwtService.invalidAccessToken(accessToken); // Redis에 blacklist 저장
+
+ userRepository.delete(user);
SecurityContextHolder.clearContext();
}Member 엔티티의 cascade 설정을 확인하려면 다음 스크립트를 실행하세요: 🏁 Script executed: #!/bin/bash
# Member 엔티티에서 User와의 관계 설정 확인
ast-grep --pattern $'@ManyToOne$$$
User $$$'
# User 엔티티에서 Member와의 관계 cascade 설정 확인
ast-grep --pattern $'@OneToMany$$$
cascade$$$
Member$$$'Length of output: 0 🏁 Script executed: #!/bin/bash
# User 엔티티의 @OneToMany에서 Member와 cascade 옵션 확인
rg -nP --type java '@OneToMany\s*\([^)]*cascade\s*=\s*[^)]*\)' -C2
# Member 엔티티의 @ManyToOne에서 User 참조 확인
rg -nP --type java '@ManyToOne\s*\([^)]*\)\s*private\s+User' -C2Length of output: 0 🏁 Script executed: #!/bin/bash
# 엔티티 파일 위치 확인
user_file=$(fd -e java User.java | head -n1)
member_file=$(fd -e java Member.java | head -n1)
echo ">>> User 엔티티: $user_file"
grep -n "@OneToMany" -C2 "$user_file"
echo ">>> Member 엔티티: $member_file"
grep -n "@ManyToOne" -C2 "$member_file"Length of output: 449 삭제 로직 수정: 트랜잭션·순서·null·cascade 처리
🤖 Prompt for AI Agents계정 삭제 로직에 여러 개선이 필요합니다. 다음 사항들을 검토해주세요:
다음과 같이 리팩토링을 권장합니다: +@Transactional
public void deleteAccount (HttpServletRequest request, Long userId) {
+ // 1. 토큰 검증 먼저 수행
+ String accessToken = jwtUtil.extractAccessToken(request)
+ .orElseThrow(() -> new CustomAuthenticationException(ErrorCode.SECURITY_INVALID_ACCESS_TOKEN));
+
+ // 2. 사용자 조회 및 삭제
User user = userRepository.findById(userId)
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));
-
userRepository.delete(user);
-
- String accessToken = jwtUtil.extractAccessToken(request)
- .orElseThrow(() -> new CustomAuthenticationException(ErrorCode.SECURITY_INVALID_ACCESS_TOKEN));
- if (accessToken != null) {
- jwtService.invalidAccessToken(accessToken); // Redis에 blacklist 저장
- }
-
+
+ // 3. 토큰 무효화
+ jwtService.invalidAccessToken(accessToken);
+
+ // 4. Refresh token도 삭제 (userId로 조회하여 삭제하는 메서드 필요)
+ // jwtService.deleteRefreshTokenByUserId(userId);
+
+ // 5. Security Context 정리
SecurityContextHolder.clearContext();
}참고: Refresh token 삭제를 위해 JwtService에 userId 기반 삭제 메서드 추가가 필요할 수 있습니다. 🤖 Prompt for AI Agents |
||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,26 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| package com.WhoIsRoom.WhoIs_Server.global.common.redis; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| import jakarta.annotation.PostConstruct; | ||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.extern.slf4j.Slf4j; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.context.annotation.Profile; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.data.redis.core.RedisTemplate; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.stereotype.Component; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| @Slf4j | ||||||||||||||||||||||||||||||||||||||||||||||
| @Component | ||||||||||||||||||||||||||||||||||||||||||||||
| @Profile("local") | ||||||||||||||||||||||||||||||||||||||||||||||
| @RequiredArgsConstructor | ||||||||||||||||||||||||||||||||||||||||||||||
| public class RedisInitializer { | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| private final RedisTemplate<String,String> redisTemplate; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| @PostConstruct | ||||||||||||||||||||||||||||||||||||||||||||||
| public void clearRedis() { | ||||||||||||||||||||||||||||||||||||||||||||||
| redisTemplate.getConnectionFactory() | ||||||||||||||||||||||||||||||||||||||||||||||
| .getConnection() | ||||||||||||||||||||||||||||||||||||||||||||||
| .flushDb(); // 선택한 DB(예: database: 0)만 초기화 | ||||||||||||||||||||||||||||||||||||||||||||||
| log.info("✅ Redis DB 초기화 완료"); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+18
to
+24
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. 연결 자원 해제를 추가해주세요.
-import jakarta.annotation.PostConstruct;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.context.annotation.Profile;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.stereotype.Component;
+import jakarta.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Profile;
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
@@
@PostConstruct
public void clearRedis() {
- redisTemplate.getConnectionFactory()
- .getConnection()
- .flushDb(); // 선택한 DB(예: database: 0)만 초기화
+ try (RedisConnection connection = redisTemplate.getConnectionFactory().getConnection()) {
+ connection.flushDb(); // 선택한 DB(예: database: 0)만 초기화
+ }
log.info("✅ Redis DB 초기화 완료");
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
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.
authToken이 null일 때 NPE 발생합니다.providerId가"localhost"가 아니면authToken은 끝까지 null이고, 곧바로authToken.getAuthorities()로깅에서NullPointerException이 터집니다. 실제 서비스(소셜 로그인 등)에서 곧바로 장애가 나니 null 가드를 추가하거나 기본 토큰을 생성하도록 수정이 필요합니다.📝 Committable suggestion
🤖 Prompt for AI Agents