본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성하였습니다.




패스트캠퍼스의 '9개 도메인 프로젝트로 끝내는 백엔드 웹 개발 (Java/Spring) 초격차 패키지 Online' 강의 수강 18일차!
오늘은 커뮤니티 피드 서비스의 게시글과 댓글 도메인의 api를 구현하는 작업을 진행하였다.
게시글 업데이트 기능을 구현하고 테스트하던 중, 이상한 부분이 발견되었다.
콘솔에 출력된 쿼리문을 보니 post 정보와 user 정보를 조회한 후, 한 번 더 post 정보를 조회하고서 update를 진행하고 있었다.이미 post에 대한 조회는 앞서 끝낸 상태인데 왜 한 번 더 조회를 한 것일까?
바로, postRepositoryImpl(PostRepository의 구현체)에서 사용하고 있는 postEntity가 영속성 컨텍스트에 포함되지 않았기 때문이다.
JpaRepository의 save() 메소드를 살펴보면,
@Transactional
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null");
if (entityInformation.isNew(entity)) {
entityManager.persist(entity);
return entity;
} else {
return entityManager.merge(entity);
}
}
id가 있는 경우에는 merge() 메소드가 실행되는데, merge는 해당 id의 객체를 find해서 영속성 컨텍스트에 포함시킨 후 update를 진행하기 때문에 원치 않는 조회 쿼리가 한 번 더 실행한 것이다.
이러한 문제를 해결하기 위해 JPQL을 이용한 쿼리문을 직접 작성하고서 id가 있다면 직접 작성한 쿼리문을, id가 없다면 JPA의 save()를 실행하도록 하였다.
@Modifying
@Query(value = "UPDATE PostEntity p "
+ "SET p.content = :#{#postEntity.getContent()}, "
+ "p.state = :#{#postEntity.getState()}, "
+ "p.updDt = now() "
+ "WHERE p.id = :#{#postEntity.id}}")
void updatePostEntity(PostEntity postEntity);
게시글 및 댓글의 '좋아요'를 나타내는 LikeEntity의 경우, 복합키 엔티티이기 때문에 JPA의 save()를 사용하면 불필요한 select가 수행된다.
EntityManager의 persist()를 직접 호출하여서 insert만 진행되도록 수정하였다.
@PersistenceContext
private final EntityManager entityManager;
public void like(Post post, User user) {
LikeEntity likeEntity = new LikeEntity(post, user);
entityManager.persist(likeEntity);
jpaPostRepository.updateLikeCount(new PostEntity(post));
}
엔티티를 분리하니 JPA의 변경 감지 기능도 사용하지 못하고 추가적인 작업들이 더 늘어나는 문제가 생겼다.
하지만 이러한 문제를 감수하면서도 엔티티를 분리해서 사용하는 것이 좋은 이유는 도메인과 서비스 레이어의 변경이 잘 일어나지 않는다는 것이다.
이러한 방법은 동시성 문제에도 장점을 가지고 있다고 한다. 관련된 내용을 더 알아봐야겠다.