인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

Space님의 프로필 이미지
Space

작성한 질문수

실전! 스프링 데이터 JPA

@Transactional 를 통한 update 질문드립니다

해결된 질문

작성

·

492

0

안녕하세요 강사님!! 좋은 강의 항상 감사드립니다!

강의를 보며 서비스를 구현중에 생긴 고민입니다!

JPA 에서 영속성 컨텍스트를 바라보는 범위에 대해 질문드립니다.

respository.save(entity) 기능을 update 할때는 merge 사용하지 않기 위해 

기존에 모르고 사용해왔던 .save 를 제거하고, 메소드에 @Transactional 을 사용하였습니다.

그런데 변경사항이 적용이 안되고 있는데, 영속성 컨텍스트에 등록이 안된상태이거나 범위를 넘어가서 적용이 안되는 것 같습니다.

Controller

@PutMapping("/modify")
public ResponseEntity updateProfile (@RequestPart MultipartFile file) throws IOException {
Member member = Optional.ofNullable(((UserDetailsVO) (SecurityContextHolder.getContext().
getAuthentication().getPrincipal())).getMember())
.orElse(null);
String profileImg = memberService.profileChange(member, file).getProfileImg();
}

Service

@Transactional
public Member profileChange(Member member, MultipartFile file) throws IOException {
...
member.changeProfileImage(memberProfileImg);
return member;
}

API 서버를 생각하고 만들어,  member의 정보를 JWT 토큰에 담아서, Security를 통해 검증합니다.

SpringSecurity를 통해 검증하는 과정에서 MemberJpaRepository를 이용하여 member를 로드해오고 쿼리도 나가게 되지만

Controller에 @transaction 도 붙여보고 profileChange 메소드에서 SecurityContextHolder로 member를 가져와도 변경되지 않았고, 영속성 컨텍스트와 연결이 끊어졌거나 연결이 되지 않는다는 생각이 들었습니다 .

그래서 

Controller에서 MemberId를 전달하여,  profileChange 메소드에서 member 객체를 찾아온 후 변경하고 리턴한다면 저장이 되지만,

Controller에서 SecurityContextHolder를 통해 가져올때 이미 member를 조회하는 쿼리가 나가게 됩니다.

서비스에서 member를 조회할 경우, 영속성 컨텍스트 연결을 위한 조회쿼리를 날리는게 되어서 쿼리가 1+1이 되는데

이런경우엔 save를 통해 변경을 시키는것인지, 이렇게 수정하는게 맞는지 의문이 듭니다. 

답변 3

1

Space님의 프로필 이미지
Space
질문자

궁금했던 부분들이 속시원하게 해결되었습니다

현재 UserDetails 구현체에 Member 엔티티를 포함하는데, 잘못된 관계였네요. 

암달의 법칙에 대해서 강의때도 말씀해 주셨던것 같은데,

다시 혼자 프로젝트 하다보니 너무 쿼리 하나하나나가는게 성능에 영향이 있지않을까 하는 생각에 쓸때없는 집착을 하였던 것 같습니다.

갈피를 못잡던 생각의 방향들이 정리되었습니다.

아직 배우고 익혀야할 관점들이 많다는것을 다시금 느끼네요.

자세한 답변과 유사한 질문 링크까지 정말 감사합니다!!

1

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. Space님

먼저 Member 엔티티 자체를 시큐리티 정보로 직접 사용하는 것은 좋지 않은 방법입니다.

JPA에서 엔티티를 다룰 때 조심해야 하는 부분이 바로 이런 부분인데요.

JPA의 엔티티를 애플리케이션 캐시에 캐싱하거나, 시큐리티 정보 처럼 전역으로 사용되는 곳에 지금처럼 보관하게 되면 이런 혼란들이 생깁니다.

시큐리티에는 Member 엔티티 자체를 넣지 말고, 대신에 인증에 꼭 필요한 정보만 추려서 조회한 다음 AccountDto 처럼 별도의 데이터로 변환해서 사용하는 것을 추천합니다. 인증과 관련된 것을 처리하는데 회원과 관련된 모든 정보가 필요하지는 않으니까요. 그리고 영속성 컨텍스트와 연결이 끊어지기 때문에, 지연로딩 등도 모두 불가능합니다.

따라서 memberId를 기반으로 트랜잭션 안에서 Member를 조회한 다음 변경감지를 통해서 Member를 변경하시면 됩니다.

쿼리가 추가로 나가는 부분이 부담스러울 수 있지만, 암달의 법칙이라고 하지요. 애플리케이션 전체로 보면 회원 정보를 수정하는 경우는 거의 없습니다. 사실 이 부분을 어떻게든 해서 1번으로 줄이더라도, 효용 가치는 미미합니다. 만약 정말 쿼리가 1번 나가게 하고 싶으면 batch sql을 사용하시면 됩니다.

추가로 다음 글을 읽어보시면 도움이 되실거에요.

https://www.inflearn.com/questions/37221

도움이 되셨길 바래요^^

0

김영한님의 프로필 이미지
김영한
지식공유자

네^^! 새해 복 많이 받으세요 ㅎㅎ

Space님의 프로필 이미지
Space

작성한 질문수

질문하기