From 7f151509782864f1d799edf076b5967d7f344347 Mon Sep 17 00:00:00 2001 From: gihhyeon Date: Fri, 7 Nov 2025 14:57:14 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=ED=95=B4=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/PlaceReadServiceTest.java | 88 ++++++++++--------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/src/test/java/com/matzip/place/application/PlaceReadServiceTest.java b/src/test/java/com/matzip/place/application/PlaceReadServiceTest.java index 96cc8dd..99ee0f0 100644 --- a/src/test/java/com/matzip/place/application/PlaceReadServiceTest.java +++ b/src/test/java/com/matzip/place/application/PlaceReadServiceTest.java @@ -8,6 +8,7 @@ import com.matzip.place.infra.repository.PlaceRepository; import com.matzip.user.domain.User; import com.matzip.user.infra.UserRepository; +import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -17,9 +18,12 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +@Slf4j @SpringBootTest public class PlaceReadServiceTest { @@ -57,46 +61,46 @@ void setUp() { .build()); } -// @Test -// @DisplayName("상세 페이지를 조회하면 조회수가 1 증가한다") -// void getPlaceDetail_incrementsViewCount() { -// // given -// int initialViewCount = placeRepository.findById(testPlace.getId()).get().getViewCount(); -// assertThat(initialViewCount).isEqualTo(0); -// -// // when -// placeReadService.getPlaceDetail(testPlace.getId(), testUser.getId()); -// -// // then -// Place updatedPlace = placeRepository.findById(testPlace.getId()).get(); -// assertThat(updatedPlace.getViewCount()).isEqualTo(initialViewCount + 1); -// } - -// @Test -// @DisplayName("여러 요청이 동시에 들어와도 조회수가 정확하게 증가한다") -// void getPlaceDetail_concurrentAccess() throws InterruptedException { -// // given -// int numberOfThreads = 10; // 10개의 동시 요청 시뮬레이션 -// ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads); -// CountDownLatch latch = new CountDownLatch(numberOfThreads); -// -// // when -// for (int i = 0; i < numberOfThreads; i++) { -// executorService.submit(() -> { -// try { -// // 각 스레드가 getPlaceDetail 메서드를 호출 -// placeReadService.getPlaceDetail(testPlace.getId(), testUser.getId()); -// } finally { -// latch.countDown(); -// } -// }); -// } -// -// latch.await(); -// executorService.shutdown(); -// -// // then -// Place finalPlace = placeRepository.findById(testPlace.getId()).get(); -// assertThat(finalPlace.getViewCount()).isEqualTo(numberOfThreads); -// } + @Test + @DisplayName("상세 페이지를 조회하면 조회수가 1 증가한다") + void getPlaceDetail_incrementsViewCount() { + // given + int initialViewCount = placeRepository.findById(testPlace.getId()).get().getViewCount(); + assertThat(initialViewCount).isEqualTo(0); + + // when + placeReadService.getPlaceDetail(testPlace.getId(), testUser.getId()); + + // then + Place updatedPlace = placeRepository.findById(testPlace.getId()).get(); + assertThat(updatedPlace.getViewCount()).isEqualTo(initialViewCount + 1); + } + + @Test + @DisplayName("여러 요청이 동시에 들어와도 조회수가 정확하게 증가한다") + void getPlaceDetail_concurrentAccess() throws InterruptedException { + // given + int numberOfThreads = 10; // 10개의 동시 요청 시뮬레이션 + ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads); + CountDownLatch latch = new CountDownLatch(numberOfThreads); + + // when + for (int i = 0; i < numberOfThreads; i++) { + executorService.submit(() -> { + try { + // 각 스레드가 getPlaceDetail 메서드를 호출 + placeReadService.getPlaceDetail(testPlace.getId(), testUser.getId()); + } finally { + latch.countDown(); + } + }); + } + + latch.await(); + executorService.shutdown(); + + // then + Place finalPlace = placeRepository.findById(testPlace.getId()).get(); + assertThat(finalPlace.getViewCount()).isEqualTo(numberOfThreads); + } } From cfbcfbdd1f2c512350035a3a9c18f21f563ab286 Mon Sep 17 00:00:00 2001 From: gihhyeon Date: Fri, 7 Nov 2025 15:19:43 +0900 Subject: [PATCH 2/4] =?UTF-8?q?PlaceReadServiceTest=20=EB=B9=84=EB=8F=99?= =?UTF-8?q?=EA=B8=B0=20=EB=A1=9C=EC=A7=81=20=EB=8C=80=EA=B8=B0=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - latch.await() 이후 await().untilAsserted()를 사용하여, 비동기 DB 업데이트(조회수 증가)가 완료될 때까지 대기하도록 변경 --- build.gradle | 3 +++ .../application/PlaceReadServiceTest.java | 19 +++++++++++++++---- src/test/resources/application.yml | 2 +- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 051e04c..1fb7abe 100644 --- a/build.gradle +++ b/build.gradle @@ -44,6 +44,9 @@ dependencies { implementation("io.jsonwebtoken:jjwt-api:0.12.3") runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.3") runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.12.3") + + testCompileOnly 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' } tasks.named('test') { diff --git a/src/test/java/com/matzip/place/application/PlaceReadServiceTest.java b/src/test/java/com/matzip/place/application/PlaceReadServiceTest.java index 99ee0f0..abadcb8 100644 --- a/src/test/java/com/matzip/place/application/PlaceReadServiceTest.java +++ b/src/test/java/com/matzip/place/application/PlaceReadServiceTest.java @@ -72,8 +72,12 @@ void getPlaceDetail_incrementsViewCount() { placeReadService.getPlaceDetail(testPlace.getId(), testUser.getId()); // then - Place updatedPlace = placeRepository.findById(testPlace.getId()).get(); - assertThat(updatedPlace.getViewCount()).isEqualTo(initialViewCount + 1); + await().atMost(5, TimeUnit.SECONDS) // 최대 5초 대기 + .untilAsserted(() -> { // 이 조건이 통과될 때까지 반복 + Place updatedPlace = placeRepository.findById(testPlace.getId()).get(); +// log.info("[단일] Awaitility 폴링 중... 현재 조회수: {}", updatedPlace.getViewCount()); + assertThat(updatedPlace.getViewCount()).isEqualTo(1); + }); } @Test @@ -83,12 +87,14 @@ void getPlaceDetail_concurrentAccess() throws InterruptedException { int numberOfThreads = 10; // 10개의 동시 요청 시뮬레이션 ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads); CountDownLatch latch = new CountDownLatch(numberOfThreads); +// log.info("[동시] 테스트 시작. ({}개 스레드)", numberOfThreads); // when for (int i = 0; i < numberOfThreads; i++) { executorService.submit(() -> { try { // 각 스레드가 getPlaceDetail 메서드를 호출 +// log.info("[동시] 스레드 {} -> getPlaceDetail() 호출", Thread.currentThread().getId()); placeReadService.getPlaceDetail(testPlace.getId(), testUser.getId()); } finally { latch.countDown(); @@ -98,9 +104,14 @@ void getPlaceDetail_concurrentAccess() throws InterruptedException { latch.await(); executorService.shutdown(); + log.info("[동시] 10개 스레드 모두 호출 완료"); // then - Place finalPlace = placeRepository.findById(testPlace.getId()).get(); - assertThat(finalPlace.getViewCount()).isEqualTo(numberOfThreads); + await().atMost(10, TimeUnit.SECONDS) // 10개 스레드이므로 넉넉하게 10초 대기 + .untilAsserted(() -> { // viewCount가 10이 될 때까지 반복 + Place finalPlace = placeRepository.findById(testPlace.getId()).get(); +// log.info("[동시] Awaitility 폴링 중... 최종 조회수: {}", finalPlace.getViewCount()); + assertThat(finalPlace.getViewCount()).isEqualTo(numberOfThreads); + }); } } diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 011aca1..5c33de3 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -10,7 +10,7 @@ spring: ddl-auto: create-drop properties: hibernate: - dialect: org.hibernate.dialect.MySQLDialect + dialect: org.hibernate.dialect.H2Dialect format_sql: true highlight_sql: true From 6d92081b39ac87c53e7ce05b292995ce203cf5d9 Mon Sep 17 00:00:00 2001 From: gihhyeon Date: Fri, 7 Nov 2025 15:28:08 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/matzip/place/application/PlaceReadServiceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/matzip/place/application/PlaceReadServiceTest.java b/src/test/java/com/matzip/place/application/PlaceReadServiceTest.java index abadcb8..3e967b2 100644 --- a/src/test/java/com/matzip/place/application/PlaceReadServiceTest.java +++ b/src/test/java/com/matzip/place/application/PlaceReadServiceTest.java @@ -104,7 +104,7 @@ void getPlaceDetail_concurrentAccess() throws InterruptedException { latch.await(); executorService.shutdown(); - log.info("[동시] 10개 스레드 모두 호출 완료"); +// log.info("[동시] 10개 스레드 모두 호출 완료"); // then await().atMost(10, TimeUnit.SECONDS) // 10개 스레드이므로 넉넉하게 10초 대기 From 20670dcb6cc8087d01a3f2837350950338d52bec Mon Sep 17 00:00:00 2001 From: gihhyeon Date: Fri, 7 Nov 2025 15:31:00 +0900 Subject: [PATCH 4/4] =?UTF-8?q?H2Dialect=20->=20MySQLDialect=EB=A1=9C=20?= =?UTF-8?q?=EC=9E=AC=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 5c33de3..011aca1 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -10,7 +10,7 @@ spring: ddl-auto: create-drop properties: hibernate: - dialect: org.hibernate.dialect.H2Dialect + dialect: org.hibernate.dialect.MySQLDialect format_sql: true highlight_sql: true