오늘 목표
- 숙련주차 완강 -> 다 못 들었고, 다 들을 필요 없을 것 같아서 페이징까지만 봤고, 낼부터 심화 강의로 넘어갈 예정
문제
이번에 뉴스피드 프로젝트를 할 때, 스프링 시큐리티로 인증 후 @AuthenticationPrincipal로 가져온 UserDetails 안에 있는 User 엔티티를 이용해서, 패스워드를 변경하는 API를 만들고 있었다. 근데 update 쿼리가 안 나간다!! 이게 뭐여 하면서 일단은 서비스단에서 User의 nickname을 이용해서 다시 레포지토리에서 가져오는 식으로 해결을 했었다.
오늘 마침 여유도 있겠다 그 부분을 해결해 봤다. 아, 이게 트러블 슈팅이네
일단 에러가 났던 코드는 밑과 같다.
@PatchMapping("/api/users/password")
public ResponseEntity<?> updatePassword(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@Valid @RequestBody UserPasswordUpdateRequestDto requestDto) {
userService.updatePasswordWithUser(userDetails.getUser(), requestDto);
return ResponseEntity.ok().build();
}
우선 컨트롤러 단 메서드이고,
@Transactional
public void updatePasswordWithUser(User user, UserPasswordUpdateRequestDto request) {
// 입력받은 기존 비밀번호와, DB에 저장된 비밀번호가 맞는지 검증
validatePassword(request.getOldPassword(), user.getPassword());
// 새 비밀번호 암호화
String encodedPassword = passwordEncoder.encode(request.getNewPassword());
user.updatePassword(encodedPassword); // 암호화한 비밀번호로 업데이트
}
private void validatePassword(String rawPassword, String userPassword) {
if (!passwordEncoder.matches(rawPassword, userPassword)) {
throw new IllegalArgumentException("잘못된 비밀번호");
}
}
이게 서비스단 메서드다.
분명 200 ok를 응답하는데, update 쿼리가 나가질 않아서 비밀번호가 바뀌질 않았다.
해결 1 - 서비스 단에서 다시 조회 (추천)
그래서 프로젝트때 변경한 코드는 다음과 같다.
@PatchMapping("/api/users/password")
public ResponseEntity<?> updatePassword(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@Valid @RequestBody UserPasswordUpdateRequestDto requestDto) {
userService.updatePassword(userDetails.getUser().getNickname(), requestDto);
return ResponseEntity.ok().build();
}
컨트롤러 단에서는 닉네임을 받고,
@Transactional
public void updatePassword(String nickname, UserPasswordUpdateRequestDto request) {
User user = getValidUser(nickname);
validatePassword(request.getOldPassword(), user.getPassword());
String encodedPassword = passwordEncoder.encode(request.getNewPassword());
user.updatePassword(encodedPassword);
}
private User getValidUser(String nickname) {
return userRepository.findByNickname(nickname)
.orElseThrow(() -> new IllegalArgumentException("잘못된 닉네임"));
}
서비스 단에서는 받은 닉네임으로 DB에서 조회 후 업데이트를 하니 쿼리를 날리길래 이렇게 썼었다.
그리고 오늘 튜터님을 찾아가서 의논해 보니, 원인은 준영속 상태가 되어서 JPA가 관리하지 않게 되었던 것!
그러니까, 시큐리티 단에서 디비를 조회해서 유저 엔티티를 가져온 것은 맞지만, 시큐리티에서 트랜젝션이 끝나면서 유저 엔티티는 JPA의 관리를 받지 않는 준영속 상태가 되어서 그냥 유저 객체가 되어 버렸고, 그걸 @AuthenticationPrincipal 로 가져와서 업데이트를 해봤지만, 이건 그냥 유저 라는 객체의 비밀번호를 바꾼 것일 뿐, 유저 엔티티의 비밀번호를 바꾼 것은 아니라서 200 OK 가 떴지만 비밀번호가 바뀌지 않은 것!
이를 해결하려면 우선 위에서 내가 해결한 방법을 쓰거나(튜터님께서 실무에서는 내가 쓴 방법을 쓴다고 했다), 준영속 상태의 객체를 영속 상태로 전환하는 방법이 있다.
해결 2 - merge로 준영속 객체를 영속 엔티티로 전환
준영속 상태에서 영속 상태로 바꾸는 방법은 다음과 같다.
private final EntityManager em;
@Transactional
public void updatePasswordWithUser(User user, UserPasswordUpdateRequestDto request) {
User userEntity = em.merge(user);
validatePassword(request.getOldPassword(), userEntity.getPassword());
String encodedPassword = passwordEncoder.encode(request.getNewPassword());
userEntity.updatePassword(encodedPassword);
}
스프링은 외부에서 EntityManager 를 주입해 줄 수 있다. 이걸 이용해서, merge(...)라는 메서드를 이용하면 준영속 상태의 객체를 영속 상태의 엔티티로 반환해 준다.
내가 이걸 발견하고 튜터님에게 달려가서 저 이렇게 해결해봤어요!! 하니까 튜터님께서는 실무에서는 Spring data JPA를 쓸 때 EntityManager를 주입받아 쓰진 않는다고 해주셨다 ㅎㅎ,, 그래서 그냥 공부했다 쳤다. 1 지식 적립
<일기>
오늘 새벽 3시까지, 밤에 잠이 안 와서 지인분들 블로그를 돌려보던 중, ㅁㅇ님의 블로그를 봤다가 감동했다...
글 하나하나에 정성을 담아서 오늘 배운 내용과 내일 계획, 간략한 일기를 적어두셨는데 정말 꾸준하게 열심히 적어두셨다.
같이 프로젝트를 하면서 열심히 하신다는 건 알고 있었는데, 뒤에서 이렇게 더 열심히 하고 계실 줄이야,, 반성하게 된다.
나는 열심히 사는 사람들을 정말 좋아하는데, 그중에 특히 알아주지 않아도 꾸준히 묵묵히 노력하는 사람들을 가장 좋아한다. 일단 내가 그런 부류이기도 하고, 또 그래서 이걸 실천하는게 얼마나 대단한 일인지 아니까.
그래서 그런지 나는 이런 류의 사람들을 알아봐주려고 노력한다. 내가 그런 사람이다 보니 내심 알아봐 주었으면 하고, 또 알아봐 주었을 때의 기쁨도 알고 있어서. (TMI지만, 이런 성향 탓에 제일 좋아하는 시가 김춘수의 꽃이다.)
감동도 감동이지만, 긴장감도 느껴졌다.
1등의 자리를 빼앗는 건 2등만이 할 수 있는 건 아니니까, 이렇게 단단히 기본기를 쌓고 달리는 분에게 곧 추월당하겠다 싶어서 나 역시 동기부여가 됐다. 다음에는 얼마나 성장해 있을지 ,, 잘 됐으면 좋겠다. 아니, 나는 나를 거쳐간 모든 사람들이 다 잘 됐으면 좋겠다.
</일기>
'TIL ✍️' 카테고리의 다른 글
23년 12월 1일(금요일) - 43번째 TIL : validation 검증하기 (0) | 2023.12.01 |
---|---|
23년 11월 30일(목요일) - 42번째 TIL (0) | 2023.11.30 |
23년 11월 28일(화요일) - 40번째 TIL (0) | 2023.11.28 |
23년 11월 24일(금요일) - 39번째 TIL (0) | 2023.11.24 |
23년 11월 23일(목요일) - 38번째 TIL (0) | 2023.11.23 |