Project 하던 중, 정상적인 값이 조회되는데 DB값은 안바뀌는 상황이 있었다. 그에 대해 이야기 해보려고 한다.
전제
안드로이드 측에서 kakao accessToken을 주면
- DB에 kakaoUser가 이미 존재 -> 바로 서버 accessToken 및 refreshToken 발급 (httpstatus 200 내려줌)
- DB에 kakaoUser가 존재 x 즉 첫 방문 User -> signToken을 발급해서 닉네임, 이메일 등 부가 정보를 입력하는 화면으로 이동 후, 앞서 발급한 signToken + 부가 정보를 받아서 회원 가입 (httpstatus 404 내려줌 -> 즉 exception 터트림)
+ 추가적으로 만약 회원 탈퇴를 할 시, 중복 가입 방지를 위해 탈퇴한 회원은 한달 동안 가입을 못하게 방지해놓았음.
@Transactional
public AuthMessage loginAccess(SocialLoginRequest socialLoginRequest) {
OAuthSocialEmailResponse response = fetchSocialEmail(socialLoginRequest); //kakaoId가져오기
String socialEmail = response.socialEmail();
checkIsSleepingUser(socialEmail); //휴면 유저인지 확인
Optional<User> findUser = userRepository.findBySocialEmail(socialEmail);
checkIsRevokeUser(socialEmail); //탈퇴 유저인지 확인 **
AuthMessage loginMessage;
if(findUser.isPresent()) {
User user = findUser.get();
checkUserStatus(user);
user.updateLastLoginDate();
user.updateFcmToken(socialLoginRequest.fcmToken());
JwtDto jwtDto = login(LoginRequest.toLoginRequest(user));
loginMessage = new AuthMessage(
jwtDto,
StatusCode.LOGIN.getMessage()
);
} else {
String signToken = jwtProvider.createSignToken(socialEmail);
SignToken signTokenResponse = new SignToken(signToken);
log.info("signTokenResponse={}", signTokenResponse.signToken());
throw new AuthException(StatusCode.NEED_TO_SIGNUP, signTokenResponse); // SignToken 반환
}
return loginMessage;
}
다른 코드들은 상관이 없고, checkIsRevokeUser 쪽 코드를 보면
@Transactional
public void checkIsRevokeUser(String socialEmail) {
Optional<RevokeUser> checkUser = revokeUserRepository.findBySocialEmail(customEncryptUtil.hash(socialEmail));
if (checkUser.isEmpty()) return;
RevokeUser revokeUser = checkUser.get();
boolean isRevokedExpired = LocalDateTime.now().isAfter(revokeUser.getRevokedAt().plusMonths(1));
if (isRevokedExpired) {
revokeUser.deleteRevokeUser();
}
else throw new UserException(StatusCode.REVOKE_USER);
}
@Entity
@AllArgsConstructor
@Builder
@NoArgsConstructor
@Getter
public class RevokeUser {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "revoke_user_id")
private Long id;
private String socialEmail;
private LocalDateTime revokedAt;
public void deleteRevokeUser() {
this.socialEmail=null;
this.revokedAt=null;
}
}
따라서 이와같이 revoke유저가 재가입을 요청할 때, 한달이 넘었는지 확인하고 revokeUser에 있는 user정보를 삭제하도록 함. 하지만 이때 revokeUser의 값이 null로 안바뀌는 문제가 발생함.
왜 ??
필자는 위의 사진과 같이 정상적인 값이 잘 나오는 것을 인지하고, @Transaction의 문제인지 생각도 하지 못했다....
한 2일정도 앓다가 유레카를 외쳤었다..! ㅋㅋㅋㅋㅋㅋㅋ
순서를 보자면
revokeUser는 탈퇴한 유저이기 때문에 User Table의 값이 없다. 따라서 signToken을 발급해준다. 이것은 앞서 말했듯이 404라는 exception을 터트림
transaction은 무엇인가? -> 로직을 수행할 때, 예외를 만나면 rollback을 함으로써 db의 값을 보존시켜줌
내가 커스텀한 exception이어도 내가 예상한 값이 반환이 되어도, exception은 exception인 것이다. 따라서 rollback이 되고있던 것이였다...!!!
따라서 내가 한 조치는
@Transactional(noRollbackFor = {AuthException.class})
회원가입 기능을 하는 매서드에 noRollbackFor을 추가했다 !
저기능은 내가 커스텀한 Exception(AuthException)만을 제외하고 문제가 터질시 rollback시켜주는 기능이였다.
저 한줄을 추가하니 null로 안바뀌던 DB가 null로 바뀌었다.
깨달은점 :
@Transaction의 남용 금지. 내 Exception도 컨트롤 하면서 사용하자..
내가 예상한 정상적인 값이여도 내가 터트린 Exception인지 의심하자..
유레카는 엄청난 희열이구나..
'Project' 카테고리의 다른 글
[Project] Spring + Stomp 테스트 하는 과정.. (실시간 채팅 구현) (4) | 2024.02.27 |
---|---|
[Project] 멀티 모듈을 왜 쓸까? (멀티 모듈 도입 전) (1) | 2024.01.15 |
[Project] google Oauth2 로그인 시 refreshToken을 받는 방법 (최초 로그인에서 저장을 못했을 때) (0) | 2023.11.29 |
[Project] Redis(Elasticache Redis)로 RefreshToken을 관리하기 (1) | 2023.11.25 |
Apache JMeter란? + 부하 테스트, 사용법 (0) | 2023.08.22 |