Skip to content

Commit fa54b45

Browse files
committed
Feat: 스터디룸 채팅 Repository QueryDSL 적용
- RoomChatMessageRepositoryCustom 인터페이스 구현 - QueryDSL로 동적 쿼리 및 Fetch Join 적용 - before 파라미터 완전 지원 (기존 TODO 해결)
1 parent 7e73154 commit fa54b45

File tree

6 files changed

+141
-33
lines changed

6 files changed

+141
-33
lines changed

src/main/java/com/back/domain/chat/service/ChatService.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,9 @@ public ChatPageResponse getRoomChatHistory(Long roomId, int page, int size, Loca
5959
// before 파라미터가 있으면 해당 시점 이전 메시지만 조회
6060
Page<RoomChatMessage> messagesPage;
6161
if (before != null) {
62-
// TODO: before 조건 추가한 Repository 메서드 필요
63-
messagesPage = roomChatMessageRepository.findByRoomIdOrderByCreatedAtDesc(roomId, pageable);
62+
messagesPage = roomChatMessageRepository.findMessagesByRoomIdBefore(roomId, before, pageable);
6463
} else {
65-
messagesPage = roomChatMessageRepository.findByRoomIdOrderByCreatedAtDesc(roomId, pageable);
64+
messagesPage = roomChatMessageRepository.findMessagesByRoomId(roomId, pageable);
6665
}
6766

6867
Page<ChatMessageDto> dtoPage = messagesPage.map(this::convertToDto);
Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,9 @@
11
package com.back.domain.studyroom.repository;
22

33
import com.back.domain.studyroom.entity.RoomChatMessage;
4-
import org.springframework.data.domain.Page;
5-
import org.springframework.data.domain.Pageable;
64
import org.springframework.data.jpa.repository.JpaRepository;
7-
import org.springframework.data.jpa.repository.Query;
8-
import org.springframework.data.repository.query.Param;
95
import org.springframework.stereotype.Repository;
106

11-
import java.time.LocalDateTime;
12-
import java.util.List;
13-
147
@Repository
15-
public interface RoomChatMessageRepository extends JpaRepository<RoomChatMessage, Long> {
16-
17-
// 방별 페이징된 채팅 메시지 조회 (무한 스크롤용)
18-
@Query("SELECT m FROM RoomChatMessage m " +
19-
"WHERE m.room.id = :roomId " +
20-
"ORDER BY m.createdAt DESC")
21-
Page<RoomChatMessage> findByRoomIdOrderByCreatedAtDesc(@Param("roomId") Long roomId, Pageable pageable);
22-
23-
// 특정 타임스탬프 이후의 메시지 조회 (실시간 업데이트용)
24-
@Query("SELECT m FROM RoomChatMessage m " +
25-
"WHERE m.room.id = :roomId " +
26-
"AND m.createdAt > :timestamp " +
27-
"ORDER BY m.createdAt ASC")
28-
List<RoomChatMessage> findByRoomIdAfterTimestamp(@Param("roomId") Long roomId,
29-
@Param("timestamp") LocalDateTime timestamp);
30-
31-
// 방별 최근 20개 메시지 조회
32-
List<RoomChatMessage> findTop20ByRoomIdOrderByCreatedAtDesc(Long roomId);
33-
34-
// 방별 전체 메시지 수 조회
35-
long countByRoomId(Long roomId);
36-
}
8+
public interface RoomChatMessageRepository extends JpaRepository<RoomChatMessage, Long>, RoomChatMessageRepositoryCustom {
9+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.back.domain.studyroom.repository;
2+
3+
import com.back.domain.studyroom.entity.RoomChatMessage;
4+
import org.springframework.data.domain.Page;
5+
import org.springframework.data.domain.Pageable;
6+
7+
import java.time.LocalDateTime;
8+
9+
public interface RoomChatMessageRepositoryCustom {
10+
11+
/**
12+
* 방별 페이징된 채팅 메시지 조회 (최신순)
13+
* @param roomId 방 ID
14+
* @param pageable 페이징 정보
15+
* @return 페이징된 채팅 메시지 목록
16+
*/
17+
Page<RoomChatMessage> findMessagesByRoomId(Long roomId, Pageable pageable);
18+
19+
/**
20+
* 특정 시점 이전의 채팅 메시지 조회 (무한 스크롤용)
21+
* @param roomId 방 ID
22+
* @param before 기준 시점
23+
* @param pageable 페이징 정보
24+
* @return 기준 시점 이전의 메시지 목록
25+
*/
26+
Page<RoomChatMessage> findMessagesByRoomIdBefore(Long roomId, LocalDateTime before, Pageable pageable);
27+
28+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package com.back.domain.studyroom.repository;
2+
3+
import com.back.domain.studyroom.entity.QRoom;
4+
import com.back.domain.studyroom.entity.QRoomChatMessage;
5+
import com.back.domain.studyroom.entity.RoomChatMessage;
6+
import com.back.domain.user.entity.QUser;
7+
import com.querydsl.core.types.dsl.BooleanExpression;
8+
import com.querydsl.jpa.impl.JPAQueryFactory;
9+
import lombok.RequiredArgsConstructor;
10+
import org.springframework.data.domain.Page;
11+
import org.springframework.data.domain.PageImpl;
12+
import org.springframework.data.domain.Pageable;
13+
import org.springframework.stereotype.Repository;
14+
15+
import java.time.LocalDateTime;
16+
import java.util.List;
17+
18+
19+
20+
@Repository
21+
@RequiredArgsConstructor
22+
public class RoomChatMessageRepositoryImpl implements RoomChatMessageRepositoryCustom {
23+
24+
private final JPAQueryFactory queryFactory;
25+
26+
private final QRoomChatMessage message = QRoomChatMessage.roomChatMessage;
27+
private final QRoom room = QRoom.room;
28+
private final QUser user = QUser.user;
29+
30+
@Override
31+
public Page<RoomChatMessage> findMessagesByRoomId(Long roomId, Pageable pageable) {
32+
33+
// 메시지 목록 조회
34+
List<RoomChatMessage> messages = queryFactory
35+
.selectFrom(message)
36+
.leftJoin(message.room, room).fetchJoin() // Room 정보 즉시 로딩
37+
.leftJoin(message.user, user).fetchJoin() // User 정보 즉시 로딩
38+
.where(message.room.id.eq(roomId))
39+
.orderBy(message.createdAt.desc()) // 최신순 정렬
40+
.offset(pageable.getOffset())
41+
.limit(pageable.getPageSize())
42+
.fetch();
43+
44+
// 전체 개수 조회
45+
Long totalCount = queryFactory
46+
.select(message.count())
47+
.from(message)
48+
.where(message.room.id.eq(roomId))
49+
.fetchOne();
50+
51+
return new PageImpl<>(messages, pageable, totalCount != null ? totalCount : 0);
52+
}
53+
54+
@Override
55+
public Page<RoomChatMessage> findMessagesByRoomIdBefore(Long roomId, LocalDateTime before, Pageable pageable) {
56+
57+
// 조건부 WHERE 절 (before가 null이면 조건 제외)
58+
BooleanExpression whereClause = message.room.id.eq(roomId);
59+
if (before != null) {
60+
whereClause = whereClause.and(message.createdAt.lt(before)); // before 시점 이전
61+
}
62+
63+
List<RoomChatMessage> messages = queryFactory
64+
.selectFrom(message)
65+
.leftJoin(message.room, room).fetchJoin()
66+
.leftJoin(message.user, user).fetchJoin()
67+
.where(whereClause)
68+
.orderBy(message.createdAt.desc())
69+
.offset(pageable.getOffset())
70+
.limit(pageable.getPageSize())
71+
.fetch();
72+
73+
// 조건에 맞는 전체 개수
74+
Long totalCount = queryFactory
75+
.select(message.count())
76+
.from(message)
77+
.where(whereClause)
78+
.fetchOne();
79+
80+
return new PageImpl<>(messages, pageable, totalCount != null ? totalCount : 0);
81+
}
82+
83+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.back.global.config;
2+
3+
import com.querydsl.jpa.impl.JPAQueryFactory;
4+
import jakarta.persistence.EntityManager;
5+
import jakarta.persistence.PersistenceContext;
6+
import lombok.RequiredArgsConstructor;
7+
import org.springframework.context.annotation.Bean;
8+
import org.springframework.context.annotation.Configuration;
9+
10+
/**
11+
* QueryDSL 설정
12+
* JPAQueryFactory 빈을 생성하여 QueryDSL 사용 가능하게 함
13+
*/
14+
@Configuration
15+
@RequiredArgsConstructor
16+
public class QueryDslConfig {
17+
18+
@PersistenceContext
19+
private final EntityManager entityManager;
20+
21+
@Bean
22+
public JPAQueryFactory jpaQueryFactory() {
23+
return new JPAQueryFactory(entityManager);
24+
}
25+
}

src/main/java/com/back/global/websocket/config/WebSocketConfig.java renamed to src/main/java/com/back/global/config/WebSocketConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.back.global.websocket.config;
1+
package com.back.global.config;
22

33
import com.back.global.security.CustomUserDetails;
44
import com.back.global.security.JwtTokenProvider;

0 commit comments

Comments
 (0)