Skip to content

Commit 53f5868

Browse files
committed
Redis 동시성 해결, 에러코드 추가
1 parent 474748a commit 53f5868

File tree

5 files changed

+85
-55
lines changed

5 files changed

+85
-55
lines changed

build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ dependencies {
4747

4848
//redis
4949
implementation("org.springframework.boot:spring-boot-starter-data-redis")
50+
implementation ("org.redisson:redisson-spring-boot-starter:3.42.0") // redis message broker(lock)
51+
implementation ("org.springframework.session:spring-session-data-redis")
52+
5053

5154
// Security
5255
implementation("org.springframework.boot:spring-boot-starter-security")

src/main/java/cmf/commitField/domain/chat/chatRoom/repository/ChatRoomRepository.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
package cmf.commitField.domain.chat.chatRoom.repository;
22

33
import cmf.commitField.domain.chat.chatRoom.entity.ChatRoom;
4-
import jakarta.persistence.LockModeType;
5-
import jakarta.persistence.QueryHint;
64
import org.springframework.data.domain.Page;
75
import org.springframework.data.domain.Pageable;
86
import org.springframework.data.jpa.repository.JpaRepository;
9-
import org.springframework.data.jpa.repository.Lock;
107
import org.springframework.data.jpa.repository.Query;
11-
import org.springframework.data.jpa.repository.QueryHints;
128
import org.springframework.data.repository.query.Param;
139
import org.springframework.stereotype.Repository;
1410

@@ -17,8 +13,8 @@
1713
@Repository
1814
public interface ChatRoomRepository extends JpaRepository<ChatRoom, Long> {
1915

20-
@Lock(LockModeType.PESSIMISTIC_WRITE)
21-
@QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value ="1000")})
16+
// @Lock(LockModeType.PESSIMISTIC_WRITE)
17+
// @QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value ="1000")})
2218
Optional<ChatRoom> findById(Long aLong);
2319

2420
Optional<ChatRoom> findChatRoomById(Long id);

src/main/java/cmf/commitField/domain/chat/chatRoom/service/ChatRoomServiceImpl.java

Lines changed: 72 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,33 @@
1212
import cmf.commitField.global.error.ErrorCode;
1313
import cmf.commitField.global.exception.CustomException;
1414
import lombok.RequiredArgsConstructor;
15+
import org.redisson.api.RLock;
16+
import org.redisson.api.RedissonClient;
1517
import org.springframework.data.domain.Page;
1618
import org.springframework.data.domain.Pageable;
1719
import org.springframework.stereotype.Service;
20+
import org.springframework.transaction.annotation.Isolation;
1821
import org.springframework.transaction.annotation.Transactional;
1922

2023
import java.time.LocalDateTime;
2124
import java.util.ArrayList;
2225
import java.util.List;
2326
import java.util.Objects;
27+
import java.util.concurrent.TimeUnit;
2428

2529
@Service
2630
@RequiredArgsConstructor
2731
public class ChatRoomServiceImpl implements ChatRoomService {
2832

2933
private final ChatRoomRepository chatRoomRepository;
34+
35+
private final ChatMessageRepository chatMessageRepository;
36+
3037
private final UserRepository userRepository;
38+
3139
private final UserChatRoomRepository userChatRoomRepository;
32-
private final ChatMessageRepository chatMessageRepository;
40+
41+
private final RedissonClient redissonClient;
3342

3443
@Override
3544
@Transactional
@@ -61,38 +70,6 @@ public void createRoom(ChatRoomRequest chatRoomRequest, Long userId) {
6170
userChatRoomRepository.save(userChatRoom);
6271
}
6372

64-
@Override
65-
@Transactional
66-
public void joinRoom(Long roomId, Long userId) {
67-
// 유저 조회
68-
User findUser = userRepository.findById(userId)
69-
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_USER));
70-
71-
// room 조회
72-
ChatRoom chatRoom = chatRoomRepository.findById(roomId)
73-
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_ROOM));
74-
75-
// 현재 인원 수 조회
76-
Long currentUserCount = userChatRoomRepository.countByChatRoomId(roomId);
77-
// 채팅방이 꽉 찼을 경우 예외 처리
78-
if (currentUserCount >= chatRoom.getUserCountMax()) {
79-
throw new CustomException(ErrorCode.ROOM_USER_FULL);
80-
}
81-
82-
// 이미 참여한 방인지 확인
83-
if (userChatRoomRepository.existsByChatRoomIdAndUserId(roomId, userId)) {
84-
throw new CustomException(ErrorCode.ALREADY_JOIN_ROOM);
85-
}
86-
87-
88-
// user_chatroom 관계 생성
89-
UserChatRoom userChatRoom = UserChatRoom.builder()
90-
.user(findUser)
91-
.chatRoom(chatRoom)
92-
.build();
93-
userChatRoomRepository.save(userChatRoom);
94-
}
95-
9673
// 방 조회 DTO 변환 메서드 추출
9774
private static List<ChatRoomDto> getChatRoomDtos(Page<ChatRoom> all) {
9875
List<ChatRoomDto> chatRoomList = new ArrayList<>();
@@ -110,6 +87,7 @@ private static List<ChatRoomDto> getChatRoomDtos(Page<ChatRoom> all) {
11087
return chatRoomList;
11188
}
11289

90+
11391
// 채팅방 전체 조회
11492
@Override
11593
@Transactional(readOnly = true)
@@ -135,12 +113,57 @@ public List<ChatRoomDto> getUserByRoomPartList(Long userId, Pageable pageable) {
135113
return getChatRoomDtos(allByUserIdAndUserChatRooms);
136114
}
137115

116+
@Override
117+
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
118+
public void joinRoom(Long roomId, Long userId) {
119+
RLock lock = redissonClient.getLock("joinRoomLock:" + roomId);
120+
try {
121+
boolean available = lock.tryLock(1, TimeUnit.SECONDS);
122+
123+
if (!available) {
124+
throw new CustomException(ErrorCode.FAILED_GET_LOCK);
125+
}
126+
// 유저 조회
127+
User findUser = getUser(userId);
128+
129+
// room 조회
130+
ChatRoom chatRoom = chatRoomRepository.findById(roomId) // lock (기존)
131+
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_ROOM));
132+
133+
// user_chatroom 현재 인원 카운트 (비즈니스 로직)
134+
Long currentUserCount = userChatRoomRepository.countNonLockByChatRoomId(roomId); // lock (기존)
135+
136+
List<Long> userChatRoomByChatRoomId = userChatRoomRepository
137+
.findUserChatRoomByChatRoom_Id(roomId);
138+
139+
if (userChatRoomByChatRoomId.contains(userId)) {
140+
throw new CustomException(ErrorCode.ALREADY_JOIN_ROOM);
141+
}
142+
143+
// chatroom 입장
144+
if (currentUserCount >= chatRoom.getUserCountMax()) {
145+
throw new CustomException(ErrorCode.ROOM_USER_FULL);
146+
}
147+
148+
UserChatRoom userChatRoom = UserChatRoom.builder()
149+
.user(findUser)
150+
.chatRoom(chatRoom)
151+
.build();
152+
userChatRoomRepository.save(userChatRoom);
153+
// 비즈니스 로직 끝
154+
} catch (InterruptedException e) {
155+
throw new CustomException(ErrorCode.FAILED_GET_LOCK);
156+
} finally {
157+
lock.unlock();
158+
}
159+
160+
161+
}
162+
138163
@Override
139164
@Transactional
140165
public void outRoom(Long userId, Long roomId) {
141-
ChatRoom room = chatRoomRepository
142-
.findChatRoomById(roomId)
143-
.orElseThrow(() -> new CustomException(ErrorCode.NO_ROOM_FOUND));
166+
ChatRoom room = getChatRoom(roomId);
144167
// 방장이 아니라면
145168
if (!Objects.equals(room.getRoomCreator(), userId)) {
146169
userChatRoomRepository.deleteUserChatRoomByChatRoom_IdAndUserId(roomId, userId);
@@ -153,13 +176,10 @@ public void outRoom(Long userId, Long roomId) {
153176

154177
}
155178

156-
157179
@Override
158180
@Transactional
159181
public void deleteRoom(Long userId, Long roomId) {
160-
ChatRoom room = chatRoomRepository
161-
.findChatRoomById(roomId)
162-
.orElseThrow(() -> new CustomException(ErrorCode.NO_ROOM));
182+
ChatRoom room = getChatRoom(roomId);
163183
//방장이 아닐 경우, 삭제 불가
164184
if (!Objects.equals(room.getRoomCreator(), userId)) {
165185
throw new CustomException(ErrorCode.NOT_ROOM_CREATOR);
@@ -169,8 +189,19 @@ public void deleteRoom(Long userId, Long roomId) {
169189
userChatRoomRepository.deleteUserChatRoomByChatRoom_Id(roomId);
170190
chatRoomRepository.deleteById(roomId);
171191

172-
}
192+
}
193+
194+
private User getUser(Long userId) {
195+
return userRepository.findById(userId)
196+
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_USER));
197+
}
173198

174199

200+
private ChatRoom getChatRoom(Long roomId) {
201+
return chatRoomRepository
202+
.findChatRoomById(roomId)
203+
.orElseThrow(() -> new CustomException(ErrorCode.NONE_ROOM));
204+
205+
}
175206

176207
}

src/main/java/cmf/commitField/domain/chat/userChatRoom/repository/UserChatRoomRepository.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
package cmf.commitField.domain.chat.userChatRoom.repository;
22

33
import cmf.commitField.domain.chat.userChatRoom.entity.UserChatRoom;
4-
import jakarta.persistence.LockModeType;
5-
import jakarta.persistence.QueryHint;
64
import org.springframework.data.jpa.repository.JpaRepository;
7-
import org.springframework.data.jpa.repository.Lock;
85
import org.springframework.data.jpa.repository.Query;
9-
import org.springframework.data.jpa.repository.QueryHints;
106
import org.springframework.data.repository.query.Param;
117
import org.springframework.stereotype.Repository;
128

@@ -15,8 +11,8 @@
1511
@Repository
1612
public interface UserChatRoomRepository extends JpaRepository<UserChatRoom, Long> {
1713

18-
@Lock(LockModeType.PESSIMISTIC_WRITE)
19-
@QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = "1000")})
14+
// @Lock(LockModeType.PESSIMISTIC_WRITE)
15+
// @QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = "1000")})
2016
Long countByChatRoomId(Long roomId); // 비관적 락
2117

2218
// @Query("select count(*) from UserChatRoom u where u.chatRoom.id = ?1 ")
@@ -32,7 +28,8 @@ public interface UserChatRoomRepository extends JpaRepository<UserChatRoom, Long
3228
boolean existsByChatRoomIdAndUserId(Long roomId, Long userId);
3329
// 특정 방에 참여한 모든 UserChatRoom 관계 조회
3430
List<UserChatRoom> findByChatRoom_Id(Long chatRoomId);
35-
31+
@Query("select u.user.id from UserChatRoom u where u.chatRoom.id = ?1")
32+
List<Long> findUserChatRoomByChatRoom_Id(Long chatRoomId);
3633

3734

3835
}

src/main/java/cmf/commitField/global/error/ErrorCode.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ public enum ErrorCode {
5151

5252
//chatMessage
5353
EMPTY_MESSAGE(HttpStatus.BAD_REQUEST, "채팅 메시지는 공백으로 보낼 수 없습니다."),
54-
CHAT_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 채팅방의 메시지들을 찾지 못했습니다.");
54+
CHAT_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 채팅방의 메시지들을 찾지 못했습니다."),
55+
56+
// Lock
57+
FAILED_GET_LOCK(HttpStatus.LOCKED, "락을 획득하지 못했습니다.");
5558

5659
private final HttpStatus httpStatus;
5760
private final String message;

0 commit comments

Comments
 (0)