익명으로 게시글을 작성하고, 댓글을 작성할수 있는 아주 간단한 CRUD 게시판을 구현했다.
로그인도 만들지 않은 정말 단순 그 자체인 연습 프로젝트였는데, 생각보다 좋은 경험이 될수 있었다.
이번엔 해당 게시판에서, 불필요한 쿼리가 나가는 부분을 줄여보고자 한다.
먼저 어느 메소드에서 쿼리가 나가는지 확인을 해보기 위해 로그를 작성할 필요가 있었다. 메소드의 시작과 끝에 각각 로그를 붙여주고 싶었는데, 컨트롤러에서 메서드 하나하나에 log.info()를 붙여주는건 너무 비효율적이라 AOP를 적용해보고자 했다.
AOP에 대한 개략적인 이해보다는, 이번엔 그저 코드를 가져와 사용만 해보았다.
package hj.community.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
@Aspect
@Slf4j
public class MethodStartEndTraceAop {
@Around("execution(* hj.community.controller..*(..))")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable{
log.info("--method {} start--", joinPoint.toString());
try{
return joinPoint.proceed();
} finally {
log.info("--method {} end--", joinPoint.toString());
}
}
}
이렇게 controller쪽에 Around 애노테이션을 붙여줘서, 메소드의 시작과 끝에 로그가 찍히게 된다.
먼저, 게시글을 조회하게 되면 Question에 대한 정보를 가져오기 위한 쿼리가 하나 나간다. 그 다음에, 댓글 리스트를 따로 조회하기 때문에 그를 위한 쿼리가 한번 더 나간다. 이를 한번에 가져 올 수 있을 것 같았다.
해당 부분을 담당하는 컨트롤러는 위와 같이 매핑되어 있다. foundQuetion.getAnswerList()에서 쿼리가 추가 발생 하는 것 같다.
게시글을 조회할땐 댓글을 항상 같이 조회하므로, foundQuestion을 불러올때 answerList도 같이 불러와보자.
@Override
public Optional<Question> findById(Long id) {
Question question = em.find(Question.class, id);
return Optional.ofNullable(question);
}
기존 findById 메소드다. 정말 단순하게 entityManager에서 find를 호출했었다.
@Override
public Optional<Question> findById(Long id) {
// Question question = em.find(Question.class, id);
Question question = em.createQuery("select q from Question q join fetch q.answerList where q.id= :id", Question.class)
.setParameter("id", id)
.getSingleResult();
return Optional.ofNullable(question);
}
위와 같이 fetch join을 통해 한번의 쿼리로 불러오게 처리했다.
실제로도 기존과 다르게 한번의 쿼리로 잘 처리가 되는 모습이다.
..였는데 새로 게시글을 작성하자, findById 메서드에서 No entity found for query 오류가 발생했다.
내가 사용한건 그냥 join 이였고, left join을 사용하지 않아서 댓글이 없는 게시글을 가져올 수 없었다. left를 붙여서 해결했다.