서론
채팅에는 이모지와 댓글이 달려 있을 수 있습니다.
그래서 채팅 목록을 조회하면 해당 채팅에 달려 있는 이모지와 댓글의 개수를 채팅 마다마다 달아서 프론트에 응답을 해야 했습니다. MongoDB 같은 경우는 조인 연산이 없어 MySQL처럼 table 조회 시 다른 table을 조인해서 데이터를 가져오는 방식으로 생각할 수 없었습니다.
처음 생각 한 수정 전 로직에서는 채팅 목록을 불러온 후 채팅 목록만큼 반복문을 돌면서 채팅에 달려있는 이모지와 댓글의 개수만큼 쿼리가 날아가는 문제가 발생했습니다.
그래서 이러한 쿼리 개수를 줄이기 위해 수정한 로직에 대해 글을 작성하려 합니다.
수정 전 로직
특정 channelId에 해당하는 채팅 목록을 불러오고 해당 값들을 DTO로 변환
불러온 채팅 개수만큼 반복문을 돌면서 채팅 id로 각 채팅에 달린 이모지와 댓글 개수를 조회
이러다 보니 채팅 목록 개수만큼 이모지와 댓글 개수를 조회하는 쿼리가 발생
채팅 하나에 이모지와 댓글 개수를 조회하는 쿼리가 발생해서 불러오는 채팅 목록이 N개면 총 2N + 1개의 쿼리문이 발생
public Page<CommunityMessageDto> getMessages(Long channelId, int page, int size) {
Page<CommunityMessageDto> messageDtos = messagesToMessageDtos("message", channelId, page, size);
for (CommunityMessageDto messageDto : messageDtos) {
List<EmojiDto> emojiDtos = emojisToEmojiDtos(messageDto.getMessageId());
messageDto.setEmojis(emojiDtos);
messageDto.setCount(messageRepository.countByParentId(messageDto.getMessageId()));
}
return messageDtos;
}
private Page<CommunityMessageDto> messagesToMessageDtos(String type, Long id, int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
Page<CommunityMessage> messages = null;
if (type.equals("message")) {
messages = messageRepository.findByChannelIdAndDelCheckAndParentId(id, pageable);
} else if (type.equals("comment")) {
messages = messageRepository.findByParentIdAndDelCheckFalse(id, pageable);
}
return (messages != null) ? messages.map(CommunityMessageDto::new) : Page.empty(pageable);
}
private List<EmojiDto> emojisToEmojiDtos(Long messageId) {
List<Emoji> emojis = emojiRepository.findAllByCommunityMessageId(messageId);
return emojis.stream()
.map(EmojiDto::new)
.toList();
}
발생 쿼리
조회하는 채팅 목록 개수가 20개이면 총 41개의 쿼리문이 발생했습니다.
2024-07-02T17:05:00.000+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "channelId" : 4, "delCheck" : false, "parentId" : 0} fields: Document{{}} for class: class harmony.chatservice.domain.CommunityMessage in collection: communityMessages
2024-07-02T17:05:00.012+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 321} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.015+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 321} in collection: communityMessages
2024-07-02T17:05:00.019+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 320} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.021+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 320} in collection: communityMessages
2024-07-02T17:05:00.025+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 319} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.027+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 319} in collection: communityMessages
2024-07-02T17:05:00.030+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 318} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.033+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 318} in collection: communityMessages
2024-07-02T17:05:00.037+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 313} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.039+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 313} in collection: communityMessages
2024-07-02T17:05:00.043+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 312} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.045+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 312} in collection: communityMessages
2024-07-02T17:05:00.048+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 311} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.051+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 311} in collection: communityMessages
2024-07-02T17:05:00.054+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 310} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.056+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 310} in collection: communityMessages
2024-07-02T17:05:00.060+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 309} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.062+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 309} in collection: communityMessages
2024-07-02T17:05:00.065+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 308} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.069+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 308} in collection: communityMessages
2024-07-02T17:05:00.072+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 307} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.074+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 307} in collection: communityMessages
2024-07-02T17:05:00.078+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 306} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.081+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 306} in collection: communityMessages
2024-07-02T17:05:00.084+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 305} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.086+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 305} in collection: communityMessages
2024-07-02T17:05:00.090+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 304} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.092+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 304} in collection: communityMessages
2024-07-02T17:05:00.095+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 303} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.098+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 303} in collection: communityMessages
2024-07-02T17:05:00.101+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 302} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.104+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 302} in collection: communityMessages
2024-07-02T17:05:00.106+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 301} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.109+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 301} in collection: communityMessages
2024-07-02T17:05:00.113+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 300} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.115+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 300} in collection: communityMessages
2024-07-02T17:05:00.118+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 299} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.121+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 299} in collection: communityMessages
2024-07-02T17:05:00.123+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : find using query: { "communityMessageId" : 298} fields: Document{{}} for class: class harmony.chatservice.domain.Emoji in collection: emojis
2024-07-02T17:05:00.126+09:00 DEBUG 18288 --- [nio-7000-exec-1] o.s.data.mongodb.core.MongoTemplate : Executing count: { "parentId" : 298} in collection: communityMessages
문제 원인
현재 쿼리가 많이 발생하는 이유는 각 채팅마다 이모지와 댓글 개수를 조회하는 쿼리가 발생하기 때문입니다.
그래서 이러한 쿼리를 줄이기 위해서 한 번에 이모지와 댓글 개수를 조회하는 방식에 대해서 생각을 했습니다.
이모지와 댓글 개수를 조회할 때 필요한 건 채팅 id 값이고 그러면 이 id 값들을 한 번에 보내서 IN 연산을 사용하여 이모지와 댓글 개수를 한 번에 조회하는 방식으로 로직을 수정했습니다.
수정 후 로직
특정 channelId에 해당하는 채팅 메시지들을 불러오고 해당 값들을 DTO로 변환
getMessageIds() 메서드를 통해 채팅 목록들로부터 채팅의 id 값들을 뽑아서 List에 담음
이모지를 한 번에 조회하기 위해 IN 연산을 사용하여 id값들이 담긴 List를 보내 한번에 채팅에 달린 이모지들을 조회하는 메서드 구현
- getEmojisForMessages(messageIds);
댓글 개수도 똑같이 IN 연산을 사용하여 채팅에 달린 댓글 개수들을 조회하는 메서드 구현
- getCommentCountForMessages(messageIds);
채팅 목록만큼 반복하면서 채팅에 이모지와 댓글 개수를 달아서 프론트에 반환
@Override
public Page<ServerMessageDto> getMessages(Long channelId, int page, int size) {
Page<ServerMessageDto> messageDtos = messagesToMessageDtos("message", channelId, page, size);
List<Long> messageIds = getMessageIds(messageDtos);
Map<Long, List<EmojiDto>> emojiMap = getEmojisForMessages(messageIds);
Map<Long, Long> commentCount = getCommentCountForMessages(messageIds);
for (ServerMessageDto messageDto : messageDtos) {
messageDto.setEmojis(emojiMap.getOrDefault(messageDto.getMessageId(), Collections.emptyList()));
messageDto.setCount(commentCount.getOrDefault(messageDto.getMessageId(), 0L));
}
return messageDtos;
}
private Page<ServerMessageDto> messagesToMessageDtos(String type, Long id, int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
Page<ServerMessage> messages = null;
if (type.equals("message")) {
messages = messageRepository.findByChannelIdAndIsDeletedAndParentId(id, pageable);
} else if (type.equals("comment")) {
messages = messageRepository.findByParentIdAndIsDeleted(id, pageable);
}
return (messages != null) ? messages.map(ServerMessageDto::from) : Page.empty(pageable);
}
private Map<Long, List<EmojiDto>> getEmojisForMessages(List<Long> messageIds) {
List<Emoji> emojis = emojiRepository.findEmojisByServerMessageIds(messageIds);
Map<Long, List<EmojiDto>> emojiMap = new HashMap<>();
for (Long messageId : messageIds) {
List<EmojiDto> emojiDtos = new ArrayList<>();
for (Emoji emoji : emojis) {
if (messageId.equals(emoji.getServerMessageId())) {
EmojiDto emojiDto = EmojiDto.from(emoji);
emojiDtos.add(emojiDto);
}
}
emojiMap.put(messageId, emojiDtos);
}
return emojiMap;
}
private Map<Long, Long> getCommentCountForMessages(List<Long> messageIds) {
List<ServerMessage> messages = messageRepository.findCommentCountByParentIdsAndIsDeleted(messageIds);
Map<Long, Long> commentCount = new HashMap<>();
for (Long messageId : messageIds) {
long count = 0L;
for (ServerMessage message : messages) {
if (message.getParentId().equals(messageId)) {
count += 1;
}
}
commentCount.put(messageId, count);
}
return commentCount;
}
private List<Long> getMessageIds(Page<ServerMessageDto> messageDtos) {
return messageDtos.getContent().stream()
.map(ServerMessageDto::getMessageId)
.collect(Collectors.toList());
}
findCommentCountByParentIdsAndIsDeleted
- @Query를 사용하여 Spring Data MongoDB의 커스텀 쿼리를 사용한 메서드입니다.
- $in 연산자를 통해 parentIds 배열 안에 있는 모든 값을 검색하여 채팅 목록에 대한 댓글 수를 조회합니다.
@Repository
public interface ServerMessageRepository extends MongoRepository<ServerMessage, Long> {
@Query(value = "{ 'parentId': { $in: ?0 }, 'isDeleted': false }")
List<ServerMessage> findCommentCountByParentIdsAndIsDeleted(List<Long> parentIds);
}
findEmojisByServerMessageIds
- @Query를 사용하여 Spring Data MongoDB의 커스텀 쿼리를 사용한 메서드입니다.
- $in 연산자를 통해 messageIds 배열 안에 있는 모든 값을 검색하여 채팅 목록에 대한 이모지를 조회합니다.
@Repository
public interface EmojiRepository extends MongoRepository<Emoji, Long> {
@Query("{'serverMessageId': {$in: ?0}}")
List<Emoji> findEmojisByServerMessageIds(List<Long> messageIds);
}
발생 쿼리
조회하는 채팅 목록 개수에 상관없이 총 3개의 쿼리문이 발생했습니다.
- 채팅 목록 조회
- 채팅에 달린 이모지들 조회
- 채팅에 달린 댓글 개수들 조회
2024-07-02T19:32:10.539+09:00 DEBUG 62488 --- [chat-service] [nio-7070-exec-2] o.s.data.mongodb.core.MongoTemplate : find using query: { "channelId" : 1, "isDeleted" : false, "parentId" : 0} fields: Document{{}} for class: class capstone.chatservice.domain.server.domain.ServerMessage in collection: serverMessages
2024-07-02T19:32:10.685+09:00 DEBUG 62488 --- [chat-service] [nio-7070-exec-2] o.s.data.mongodb.core.MongoTemplate : find using query: { "serverMessageId" : { "$in" : [58, 40, 39, 38, 37, 36, 35, 34, 33, 32]}} fields: Document{{}} for class: class capstone.chatservice.domain.emoji.domain.Emoji in collection: emojis
2024-07-02T19:32:10.699+09:00 DEBUG 62488 --- [chat-service] [nio-7070-exec-2] o.s.data.mongodb.core.MongoTemplate : find using query: { "parentId" : { "$in" : [58, 40, 39, 38, 37, 36, 35, 34, 33, 32]}, "isDeleted" : false} fields: Document{{}} for class: class capstone.chatservice.domain.server.domain.ServerMessage in collection: serverMessages
아쉬운 점
쿼리 개수는 줄였으나 비즈니스 로직단에서 조회한 채팅 목록에 채팅 별로 이모지와 댓글 개수를 달아줘야 돼서 for문 연산이 늘어나게 되었습니다. 하지만 쿼리를 DB에 날리면 DB에 있는 데이터들에서 완탐을 통해 데이터를 불러와 여러 개의 쿼리를 날리기보다는 비즈니스 로직단에서 연산이 늘어나는 게 괜찮다고 생각합니다. 그래도 늘어난 for문 연산 부분은 아쉬워서 좀 더 줄일 수 있는 방법에 대해 생각하려 합니다.
'프로젝트 > FitTrip' 카테고리의 다른 글
개발 기록 - WebSocket & STOMP 개발 이슈 (0) | 2024.07.05 |
---|---|
개발 기록 - MongoDB auto-incremented sequence 적용 (0) | 2024.07.03 |
트러블 슈팅 - 웹소켓 연결 요청 JWT 검증 문제 (0) | 2024.06.30 |
개발 기록 - MongoDB 트랜잭션 도입 (0) | 2024.06.29 |
개발 기록 - 채팅 서비스 몽고DB 데이터 모델링 (1) | 2024.06.28 |