diff --git a/README.md b/README.md
index 92d4cd6..2a427a1 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-

+
Dev Race ๐งโ๐ป
์ค์๊ฐ ์ฝ๋ฉ ๊ฒฝ์ ๋ฐ ์ฑํ
์๋น์ค
@@ -25,15 +25,16 @@
## ๐ Documents
+- ๊ธฐ๊ฐ : 2024.04.29 ~ 06.16
- #### [PM] ๊ธฐํ ๋ช
์ธ์
- - ๊ธฐ๋ฅ ์์ธ ๋ช
์ธ์

- - WBS ๋ช
์ธ์

+ - ๊ธฐ๋ฅ ์์ธ ๋ช
์ธ์

+ - WBS ๋ช
์ธ์

- #### [BE] API ๋ช
์ธ์
- - Rest API ๋ช
์ธ์

- - WebSocket API ๋ช
์ธ์

- - Swagger API ๋ช
์ธ์

+ - Rest API ๋ช
์ธ์

+ - WebSocket API ๋ช
์ธ์

+ - Swagger API ๋ช
์ธ์

@@ -41,15 +42,15 @@
## ๐ป Architecture
- ### System Architecture
-
+
- ### Network Architecture
-
+
- ### Notification in Architecture

-→ Slack Notifications sent by the GitHub Actions :
+→ Slack Notifications sent by the GitHub Actions :
`๐ก PR Open` `๐ฌ PR Review` `โ CI/CD Fail` `โ
CI/CD Success`
@@ -67,13 +68,13 @@ Backend|Security|Database|Deployment|Other|
- Documentation : Notion, Swagger
- Notification : Slack
```
-→ ***Version*** : Java 17 ยท Spring Boot 3.1.11
+→ ***BE Version*** : Java 17 ยท Spring Boot 3.1.11
## ๐๏ธ Database
-
+
@@ -177,7 +178,7 @@ ex) [#32] Feat: ์์
๋ก๊ทธ์ธ ๋ฐ ํ์๊ฐ์
๊ธฐ๋ฅ
```
-โโโ .ebextensions-dev
+โโโ .ebextensions
โ โโโ 00-set-timezone.config
โ โโโ 01-set-swapmemory.config
โ โโโ 02-rabbitmq.config
@@ -314,5 +315,5 @@ ex) [#32] Feat: ์์
๋ก๊ทธ์ธ ๋ฐ ํ์๊ฐ์
๊ธฐ๋ฅ
## ๐จโ๐ฉโ๐งโ๐ง Team
| [์ฌํ์ง](https://github.com/tkguswls1106) | [์ฅ์ค์](https://github.com/JunSang1121) | [์ ์ฐ์ฌ](https://github.com/zzangjyj0818) | [๊ถ์ธ์ฐ](https://github.com/inwoo0206) | [์ด์ ํฅ](https://github.com/Hyanggggg) |
| :----------------------------------------: | :----------------------------------------: | :----------------------------------------: | :----------------------------------------: | :----------------------------------------: |
-| 
- Team Leader - |
|
|
|
|
+| 
- Team Leader - |
|
|
|
|
| Project Manager,
DevOps Engineer,
BE Developer | Backend Developer | Frontend Developer | Frontend Developer | Designer |
diff --git a/src/main/java/com/sajang/devracebackend/repository/UserRoomBatchRepository.java b/src/main/java/com/sajang/devracebackend/repository/UserRoomBatchRepository.java
index 6a0d996..fa326ba 100644
--- a/src/main/java/com/sajang/devracebackend/repository/UserRoomBatchRepository.java
+++ b/src/main/java/com/sajang/devracebackend/repository/UserRoomBatchRepository.java
@@ -41,4 +41,22 @@ public int getBatchSize() {
}
});
}
+
+ public void batchDelete(List userRoomList) {
+ String sql = "DELETE FROM user_room WHERE user_room_id = ?";
+
+ jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
+
+ @Override
+ public void setValues(PreparedStatement ps, int i) throws SQLException {
+ UserRoom userRoom = userRoomList.get(i);
+ ps.setLong(1, userRoom.getId());
+ }
+
+ @Override
+ public int getBatchSize() {
+ return userRoomList.size();
+ }
+ });
+ }
}
diff --git a/src/main/java/com/sajang/devracebackend/service/impl/AuthServiceImpl.java b/src/main/java/com/sajang/devracebackend/service/impl/AuthServiceImpl.java
index 628ad0c..8f2e71f 100644
--- a/src/main/java/com/sajang/devracebackend/service/impl/AuthServiceImpl.java
+++ b/src/main/java/com/sajang/devracebackend/service/impl/AuthServiceImpl.java
@@ -9,6 +9,7 @@
import com.sajang.devracebackend.dto.auth.TokenDto;
import com.sajang.devracebackend.dto.user.UserResponseDto;
import com.sajang.devracebackend.repository.UserRepository;
+import com.sajang.devracebackend.repository.UserRoomBatchRepository;
import com.sajang.devracebackend.repository.UserRoomRepository;
import com.sajang.devracebackend.response.exception.Exception400;
import com.sajang.devracebackend.security.jwt.TokenProvider;
@@ -33,6 +34,7 @@ public class AuthServiceImpl implements AuthService {
private final UserService userService;
private final UserRepository userRepository;
private final UserRoomRepository userRoomRepository;
+ private final UserRoomBatchRepository userRoomBatchRepository;
private final TokenProvider tokenProvider;
@@ -121,10 +123,12 @@ public TokenDto reissue(ReissueRequestDto reissueRequestDto) { // Refresh Token
@Override
public void withdrawal() {
User user = userService.findLoginUser();
+ List userRoomList = user.getUserRoomList();
// ์์ UserRoom ์ญ์ - hard delete
- List userRoomList = user.getUserRoomList();
- userRoomRepository.deleteAll(userRoomList);
+ // userRoomRepository.deleteAll(userRoomList); // JPA์ deleteAll()์ SQL ์ฟผ๋ฆฌ๊ฐ UserRoom ๊ฐ์๋งํผ ๋ ์๊ฐ๋ ๋ฌธ์ ๊ฐ ์์.
+ // userRoomRepository.deleteAllInBatch(userRoomList); // JPA์ deleteAllInBatch()์ 10000๊ฐ ์ด์์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ stackoverflow ์๋ฌ๊ฐ ๋ฐ์ํจ.
+ userRoomBatchRepository.batchDelete(userRoomList); // JDBC์ batch delete๋ฅผ ํ์ฉํ์ฌ, ๋์ฉ๋ Batch ์ญ์ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํจ. -> DB ์ฌ๋ฌ๋ฒ ์ ๊ทผ ๋ฐฉ์ง & ์ฑ๋ฅ ํฅ์
// ๋ถ๋ชจ User ์ญ์ - soft delete
user.deleteAccount();
diff --git a/src/main/java/com/sajang/devracebackend/service/impl/UserRoomServiceImpl.java b/src/main/java/com/sajang/devracebackend/service/impl/UserRoomServiceImpl.java
index 74083be..914f86c 100644
--- a/src/main/java/com/sajang/devracebackend/service/impl/UserRoomServiceImpl.java
+++ b/src/main/java/com/sajang/devracebackend/service/impl/UserRoomServiceImpl.java
@@ -113,9 +113,8 @@ public void usersEnterRoom(Long roomId) {
.build())
.collect(Collectors.toList());
- // JDBC batch insert๋ก UserRoom ํ๋ฒ์ ์ ์ฅ (์ฌ๋ฌ๋ฒ DB์ ์ ๊ทผํ๋๊ฒ์ ๋ฐฉ์ง.)
- // userRoomRepository.saveAll(userRoomList);
- userRoomBatchRepository.batchInsert(userRoomList); // ์ JPA์ saveAll()์ ๊ธฐ๋ณธํค๊ฐ IDENTITY ์ ๋ต์ธ ํ
์ด๋ธ์๋ batch ์ฒ๋ฆฌ๊ฐ ์ ์ฉ๋์ง์์.
+ // userRoomRepository.saveAll(userRoomList); // JPA์ saveAll()์ ๊ธฐ๋ณธํค๊ฐ IDENTITY ์ ๋ต์ธ ํ
์ด๋ธ์๋ batch ์ฒ๋ฆฌ๊ฐ ์ ์ฉ๋์ง์๋ ๋ฌธ์ ๊ฐ ์์.
+ userRoomBatchRepository.batchInsert(userRoomList); // JDBC์ batch insert๋ฅผ ํ์ฉํ์ฌ, ๋์ฉ๋ Batch ์ ์ฅ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํจ. -> DB ์ฌ๋ฌ๋ฒ ์ ๊ทผ ๋ฐฉ์ง & ์ฑ๋ฅ ํฅ์
// Room RoomState=START๋ก ์์
room.updateRoomState(RoomState.START);
diff --git a/src/test/java/com/sajang/devracebackend/service/AuthServiceTest.java b/src/test/java/com/sajang/devracebackend/service/AuthServiceTest.java
new file mode 100644
index 0000000..6a146ce
--- /dev/null
+++ b/src/test/java/com/sajang/devracebackend/service/AuthServiceTest.java
@@ -0,0 +1,107 @@
+package com.sajang.devracebackend.service;
+
+import com.sajang.devracebackend.domain.Room;
+import com.sajang.devracebackend.domain.User;
+import com.sajang.devracebackend.domain.enums.Role;
+import com.sajang.devracebackend.domain.mapping.UserRoom;
+import com.sajang.devracebackend.repository.RoomRepository;
+import com.sajang.devracebackend.repository.UserRepository;
+import com.sajang.devracebackend.repository.UserRoomBatchRepository;
+import com.sajang.devracebackend.repository.UserRoomRepository;
+import com.sajang.devracebackend.security.jwt.TokenProvider;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+// @SpringBootTest
+public class AuthServiceTest {
+
+ @Autowired
+ private AuthService authService;
+ @Autowired
+ private UserRepository userRepository;
+ @Autowired
+ private RoomRepository roomRepository;
+ @Autowired
+ private UserRoomRepository userRoomRepository;
+ @Autowired
+ private UserRoomBatchRepository userRoomBatchRepository;
+ @Autowired
+ private TokenProvider tokenProvider;
+ @Autowired
+ private JdbcTemplate jdbcTemplate;
+
+
+ // @Test
+ @Transactional
+ @DisplayName("ํ์ํํด - JDBC Batch Delete Test") // UserRooms Batch Delete ์๊ฐ ์ธก์
+ void withdrawal_Test() {
+
+ // ๊ธฐ์ด ๋ฐ์ดํฐ ๊ด๋ฆฌ
+ Integer inputUserRoomsCount = 10000; // 10000๊ฐ์ UserRooms ๋๋ฏธ๋ฐ์ดํฐ ์์ฑ
+ Long userId = 1L; // user_id=1์ธ User ์ํฐํฐ ํ์ฉ ์์
+ Long roomId = 1L; // room_id=1์ธ Room ์ํฐํฐ ํ์ฉ ์์
+
+ // UserRoom ๋๋ฏธ๋ฐ์ดํฐ ์์ฑ
+ Integer startUserRoomsCount = userRoomRepository.findAll().size(); // ์ด๊ธฐ UserRooms ๋ฐ์ดํฐ ๊ฐ์ ์ธก์
+ List userRoomList = makeFakeUserRooms(userId, roomId, inputUserRoomsCount);
+ userRoomBatchRepository.batchInsert(userRoomList);
+
+ // ๋ก๊ทธ์ธ
+ String jwt = tokenProvider.generateAccessToken(userId, Role.ROLE_USER);
+ Authentication authentication = tokenProvider.getAuthentication(jwt); // ์ฌ์ฉ์๋ฅผ ์ธ์ฆ
+ SecurityContextHolder.getContext().setAuthentication(authentication); // SecurityContextHolder์ ์ธ์ฆ ์ ๋ณด๋ฅผ ์ค์
+
+ // 'ํ์ํํด' -> UserRooms ๋๋ฏธ๋ฐ์ดํฐ ์ญ์
+ LocalDateTime startTime = LocalDateTime.now(); // ์์์๊ฐ ๊ธฐ๋ก
+ authService.withdrawal();
+ LocalDateTime endTime = LocalDateTime.now(); // ์ข
๋ฃ์๊ฐ ๊ธฐ๋ก
+ Integer endUserRoomsCount = userRoomRepository.findAll().size(); // ์ญ์ ํ UserRooms ๋ฐ์ดํฐ ๊ฐ์ ์ธก์
+
+ // ์คํ์๊ฐ ์ถ๋ ฅ
+ String printTime = getPrintTime(startTime, endTime);
+ System.out.println("\n< JDBC Batch Delete ์ฌ์ฉ (JPA deleteAll X, JPA deleteAllInBatch X) >");
+ System.out.println(String.format("- %d๊ฐ ๋ฐฉ์ ํ์ํํด ์คํ์๊ฐ:", inputUserRoomsCount) + printTime); // ์ถ๋ ฅ
+
+ // DB ๋กค๋ฐฑ ๊ฒ์ฆ
+ assertThat(startUserRoomsCount).isEqualTo(endUserRoomsCount); // UserRooms ๋๋ฏธ๋ฐ์ดํฐ ์ญ์ ๊ฒ์ฆ
+ }
+
+
+ // ========== ์ ํธ์ฑ ๋ฉ์๋ ========== //
+
+ public List makeFakeUserRooms(Long userId, Long roomId, int userRoomsCount) { // UserRooms ๋๋ฏธ๋ฐ์ดํฐ ์์ฑ
+ User user = userRepository.findById(userId).orElseThrow();
+ Room room = roomRepository.findById(roomId).orElseThrow();
+
+ List userRoomList = new ArrayList<>();
+ for(int i=0; i userList = makeFakeUsers(inputUsersCount);
userRepository.saveAll(userList);
@@ -56,38 +62,38 @@ void usersEnterRoom_Test() {
// Room์ waiting ์
๋ฐ์ดํธ
String waiting = userIdsString;
- Long roomId = 5L; // room_id=5์ธ Room ์ํฐํฐ
String sql = "UPDATE room SET waiting = ? WHERE room_id = ?";
jdbcTemplate.update(sql, waiting, roomId);
- // ๋ค์ค ์ฌ์ฉ์ ๋์์
์ฅ
+ // waiting(์
์ฅ ๋๊ธฐ์ด)์ '๋ค์ค ์ฌ์ฉ์ ๋์์
์ฅ' -> UserRooms ๋๋ฏธ๋ฐ์ดํฐ ์์ฑ
LocalDateTime startTime = LocalDateTime.now(); // ์์์๊ฐ ๊ธฐ๋ก
userRoomService.usersEnterRoom(roomId);
LocalDateTime endTime = LocalDateTime.now(); // ์ข
๋ฃ์๊ฐ ๊ธฐ๋ก
// ์์ฑํ UserRooms ์ญ์ (์์ ์ํฐํฐ)
List userRoomList = roomRepository.findById(roomId).orElseThrow().getUserRoomList();
- userRoomRepository.deleteAll(userRoomList);
+ deleteFakeUserRooms(userRoomList);
+ Integer endUserRoomsCount = userRoomRepository.findAll().size(); // ์ญ์ ํ UserRooms ๋ฐ์ดํฐ ๊ฐ์ ์ธก์
// ์์ฑํ Users ์ญ์ (๋ถ๋ชจ ์ํฐํฐ)
deleteFakeUsers(userList);
Integer endUsersCount = userRepository.findAll().size(); // ์ญ์ ํ User ๋ฐ์ดํฐ ๊ฐ์ ์ธก์
// ์คํ์๊ฐ ์ถ๋ ฅ
- Duration duration = Duration.between(startTime, endTime); // ์
์ฅ ์๋น์ค ๋ฉ์๋ ์คํ์๊ฐ ์ธก์
- long milliseconds = duration.toMillis(); // ๋ฐ๋ฆฌ์ด(ms)
- double seconds = milliseconds / 1000.0; // ๋ฐ๋ฆฌ์ด๋ฅผ ์ด๋ก ๋ณํ(s)
+ String printTime = getPrintTime(startTime, endTime);
System.out.println("\n< JDBC Batch Insert ์ฌ์ฉ (JPA saveAll X) >");
- System.out.println(String.format("- %d๋ช
๋์์
์ฅ ์คํ์๊ฐ: %dms (%.2fs)\n", inputUsersCount, milliseconds, seconds)); // ์ถ๋ ฅ
+ System.out.println(String.format("- %d๋ช
๋์์
์ฅ ์คํ์๊ฐ:", inputUsersCount) + printTime); // ์ถ๋ ฅ
+ // DB ๋กค๋ฐฑ ๊ฒ์ฆ
+ assertThat(startUserRoomsCount).isEqualTo(endUserRoomsCount); // UserRooms ๋๋ฏธ๋ฐ์ดํฐ ์ญ์ ๊ฒ์ฆ
assertThat(startUsersCount).isEqualTo(endUsersCount); // Users ๋๋ฏธ๋ฐ์ดํฐ ์ญ์ ๊ฒ์ฆ
}
// ========== ์ ํธ์ฑ ๋ฉ์๋ ========== //
public List makeFakeUsers(int usersCount) { // Users ๋๋ฏธ๋ฐ์ดํฐ ์์ฑ
- List userList = new ArrayList<>();
Long fakeNum = 1L;
+ List userList = new ArrayList<>();
for(int i=0; i makeFakeUsers(int usersCount) { // Users ๋๋ฏธ๋ฐ์ดํฐ ์
.build();
userList.add(user);
}
-
return userList;
}
public void deleteFakeUsers(List userList) { // Users ๋๋ฏธ๋ฐ์ดํฐ ์ญ์
userRepository.deleteAll(userList);
}
+
+ public void deleteFakeUserRooms(List userRoomList) { // UserRooms ๋๋ฏธ๋ฐ์ดํฐ ์ญ์
+ userRoomBatchRepository.batchDelete(userRoomList);
+ }
+
+ public String getPrintTime(LocalDateTime startTime, LocalDateTime endTime) { // ์คํ์๊ฐ ์ถ๋ ฅ ๋ฉ์ธ์ง ๋ฐํ
+ Duration duration = Duration.between(startTime, endTime); // ๋ฉ์๋ ์คํ์๊ฐ ์ธก์
+ long milliseconds = duration.toMillis(); // ๋ฐ๋ฆฌ์ด(ms)
+ double seconds = milliseconds / 1000.0; // ๋ฐ๋ฆฌ์ด๋ฅผ ์ด๋ก ๋ณํ(s)
+ String printTime = String.format(" %dms (%.2fs)\n", milliseconds, seconds);
+ return printTime;
+ }
}