https://github.com/ekj1003/spring-plus

제목의 keyword, 할일의 createdAt, 담당자의 nickname으로 일정을 검색하는 기능을 구현하는데 몇가지 문제가 발생했다.
Todo는 Todo의 댓글인 comments와 담당자인 managers와 연관관계가 존재한다. TodoQueryRepositoryImpl.java에서 todo를 가져오기 위해, comment와 manager 테이블과 join하여 코드를 작성했다.
http://localhost:8080/todos/search/projections?createdAt=2024-10-10&nickname=imadmin
이런 식으로 GET 요청을 보냈는데, contents에는 아무것도 들어있지 않은 빈 리스트만 있었다. 즉, 요청에 해당하는 todo가 걸러지지 않았다.
join은 manager, comment가 없는 todo의 경우 조회되지 않는다는 것을 깨달았다.
comment와 manager 테이블과의 조인 방식을 그냥 join이 아니라 leftJoin으로 불러와야한다.
leftJoin은 주 테이블(여기서는 todo)과 관련된 다른 테이블(managers, comments, user) 간의 관계에서 모든 주 테이블의 데이터를 가져오고, 관련된 데이터가 없으면 NULL로 처리하는 조인 방식이다.
이 방식을 사용하는 이유는, 일정(todo)이 존재하지만 해당 일정에 관련된 담당자(manager) 또는 댓글(comment) 이 없을 수도 있기 때문이다. leftJoin을 사용하면, 이런 경우에도 일정을 포함해 모든 결과를 가져올 수 있다.
TodoQueryRepositoryImpl.java
@Override
public Page<TodoProjectionDto> searchTodos(Pageable pageable, String keyword, LocalDate createdAt, String nickname) {
List<TodoProjectionDto> todos = queryFactory
.select(Projections.constructor(TodoProjectionDto.class,
todo.title,
manager.countDistinct().as("managerCount"),
comment.countDistinct().as("commentCount")
))
.from(todo)
.leftJoin(todo.managers, manager)
.leftJoin(manager.user, user)
.leftJoin(todo.comments, comment)
.where(
allConditions(keyword, createdAt, nickname)
)
.groupBy(todo.id)
.orderBy(todo.createdAt.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
Long total = queryFactory
.select(Wildcard.count)
.from(todo)
.leftJoin(todo.managers, manager)
.leftJoin(manager.user, user)
.where(
allConditions(keyword, createdAt, nickname)
)
.fetchOne();
return new PageImpl<>(todos, pageable, total);
}
담당자는 todo에 여러명 배치될수 있는데, nickname을 요청으로 보내면 해당 nickname을 포함한 담당자가 배치된 todo를 불러와야한다. 하지만, 403 Forbidden 에러가 발생하였다.
.leftJoin(todo.managers, manager)
.leftJoin(manager.user, user)
.leftJoin(todo.comments, comment)
manager.user까지 leftJoin을 한다.
manager와 연관된 사용자(user)의 닉네임으로도 검색을 가능하게 하기 위해서이다.
Todo는 여러 Manager와 연결되어 있고, 각 Manager는 사용자(User) 정보도 가지고 있다. 닉네임으로 검색할 때, 사용자의 닉네임을 통해서 일정(todo)을 필터링하기 위해 manager.user.nickname이 필요하다.