Skip to content

Commit c440d7b

Browse files
authored
Merge pull request #82 from CommitField/feat/#20
Feat/#20
2 parents e00466b + 53f5868 commit c440d7b

File tree

7 files changed

+88
-57
lines changed

7 files changed

+88
-57
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/config/RedisConfig.javaβ€Ž

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
88
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
99
import org.springframework.data.redis.core.RedisTemplate;
10+
import org.springframework.data.redis.core.StringRedisTemplate;
1011
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
1112
import org.springframework.data.redis.serializer.StringRedisSerializer;
1213

@@ -34,7 +35,7 @@ public RedisConnectionFactory redisConnectionFactory() {
3435
configuration.setHostName(host);
3536
configuration.setPort(port);
3637
return new LettuceConnectionFactory(configuration);
37-
38+
}
3839
@Bean
3940
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
4041
StringRedisTemplate template = new StringRedisTemplate();

β€Ž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;

β€Žsrc/main/resources/application.ymlβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ spring:
1818
jpa:
1919
open-in-view: false
2020
hibernate:
21-
ddl-auto: create
21+
ddl-auto: update
2222
properties:
2323
hibernate:
2424
default_batch_fetch_size: 100

0 commit comments

Comments
Β (0)