Skip to content

Commit b07e9c0

Browse files
authored
[Fix] 리뷰 조회 방식 변경 (#238)
* 리뷰 조회 방식 변경 # Conflicts: # src/test/java/com/backend/domain/review/controller/ApiV1ReviewControllerTest.java * 리뷰 조회 방식 변경
1 parent b5b62af commit b07e9c0

File tree

8 files changed

+143
-11
lines changed

8 files changed

+143
-11
lines changed

src/main/java/com/backend/domain/review/controller/ApiV1ReviewController.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import org.springframework.security.core.userdetails.User;
1414
import org.springframework.web.bind.annotation.*;
1515

16+
import java.util.List;
17+
1618
@RestController
1719
@RequestMapping("/api/v1/reviews")
1820
@RequiredArgsConstructor
@@ -38,6 +40,12 @@ public ResponseEntity<RsData<ReviewResponse>> getReview(@PathVariable Long revie
3840
return ResponseEntity.ok(RsData.ok("리뷰를 성공적으로 조회했습니다.", response));
3941
}
4042

43+
@GetMapping("/products/{productId}")
44+
public ResponseEntity<RsData<List<ReviewResponse>>> getReviewsByProductId(@PathVariable Long productId) {
45+
List<ReviewResponse> responses = reviewService.getReviewsByProductId(productId);
46+
return ResponseEntity.ok(RsData.ok("리뷰를 성공적으로 조회했습니다.", responses));
47+
}
48+
4149
@PutMapping("/{reviewId}")
4250
public ResponseEntity<RsData<ReviewResponse>> updateReview(@AuthenticationPrincipal User user, @PathVariable Long reviewId, @RequestBody ReviewRequest request) {
4351
Member member = getMember(user);

src/main/java/com/backend/domain/review/entity/Review.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public class Review extends BaseEntity {
2020
@Column(name = "is_satisfied", nullable = false)
2121
private Boolean isSatisfied;
2222

23-
@OneToOne(fetch = FetchType.LAZY)
23+
@ManyToOne(fetch = FetchType.LAZY)
2424
@JoinColumn(name = "product_id")
2525
private Product product;
2626

src/main/java/com/backend/domain/review/repository/ReviewRepository.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import com.backend.domain.review.entity.Review;
44
import org.springframework.data.jpa.repository.JpaRepository;
55

6-
import java.util.Optional;
6+
import java.util.List;
77

88
public interface ReviewRepository extends JpaRepository<Review, Long> {
9-
Optional<Review> findByProductId(Long productId);
9+
List<Review> findAllByProductId(Long productId);
1010
}

src/main/java/com/backend/domain/review/service/ReviewService.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
import org.springframework.stereotype.Service;
1616
import org.springframework.transaction.annotation.Transactional;
1717

18+
import java.util.List;
19+
import java.util.stream.Collectors;
20+
1821
@Service
1922
@RequiredArgsConstructor
2023
@Transactional
@@ -32,10 +35,6 @@ public ReviewResponse createReview(Long memberId, ReviewRequest request) {
3235
Product product = productRepository.findById(request.productId())
3336
.orElseThrow(() -> new ReviewException(RsStatus.PRODUCT_NOT_FOUND));
3437

35-
reviewRepository.findByProductId(product.getId()).ifPresent(review -> {
36-
throw ReviewException.alreadyExists();
37-
});
38-
3938
Review review = Review.builder()
4039
.reviewer(member)
4140
.product(product)
@@ -56,6 +55,14 @@ public ReviewResponse getReview(Long reviewId) {
5655
return ReviewResponse.from(review);
5756
}
5857

58+
@Transactional(readOnly = true)
59+
public List<ReviewResponse> getReviewsByProductId(Long productId) {
60+
List<Review> reviews = reviewRepository.findAllByProductId(productId);
61+
return reviews.stream()
62+
.map(ReviewResponse::from)
63+
.collect(Collectors.toList());
64+
}
65+
5966
public ReviewResponse updateReview(Long memberId, Long reviewId, ReviewRequest request) {
6067
Review review = reviewRepository.findById(reviewId)
6168
.orElseThrow(ReviewException::reviewNotFound);

src/main/resources/application-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ spring:
2525
ddl-auto: create
2626
logging:
2727
level:
28-
org.hibernate.orm.jdbc.bind: TRACE
28+
org.springframework: DEBUG
2929
org.hibernate.orm.jdbc.extract: TRACE
3030
org.springframework.transaction.interceptor: TRACE
3131
com.back: DEBUG

src/test/java/com/backend/domain/review/controller/ApiV1ReviewControllerTest.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import com.backend.domain.review.entity.Review;
1313
import com.backend.domain.review.repository.ReviewRepository;
1414
import com.backend.global.elasticsearch.TestElasticsearchConfiguration;
15-
import com.backend.global.redis.TestRedisConfiguration;
1615
import com.backend.global.security.JwtUtil;
1716
import com.fasterxml.jackson.databind.ObjectMapper;
1817
import org.junit.jupiter.api.BeforeEach;
@@ -39,7 +38,7 @@
3938
@SpringBootTest
4039
@AutoConfigureMockMvc
4140
@ActiveProfiles("test")
42-
@Import({TestElasticsearchConfiguration.class, TestRedisConfiguration.class})
41+
@Import({TestElasticsearchConfiguration.class, com.backend.global.redis.TestRedisConfig.class, com.backend.global.config.TestRedissonConfig.class})
4342
@Transactional
4443
class ApiV1ReviewControllerTest {
4544

@@ -107,7 +106,7 @@ void createReview_Success() throws Exception {
107106
.andExpect(jsonPath("$.data.isSatisfied").value(true));
108107

109108
// DB 검증
110-
Review review = reviewRepository.findByProductId(testProduct.getId()).orElseThrow();
109+
Review review = reviewRepository.findAllByProductId(testProduct.getId()).get(0);
111110
assertThat(review.getComment()).isEqualTo("Great product!");
112111
assertThat(review.getReviewer().getId()).isEqualTo(testUser.getId());
113112
}
@@ -137,6 +136,36 @@ void getReview_Success() throws Exception {
137136
.andExpect(jsonPath("$.data.reviewerNickname").value(testUser.getNickname()));
138137
}
139138

139+
@Test
140+
@DisplayName("상품 ID로 리뷰 목록 조회 성공")
141+
void getReviewsByProductId_Success() throws Exception {
142+
// Given
143+
reviewRepository.save(Review.builder()
144+
.reviewer(testUser)
145+
.product(testProduct)
146+
.comment("First review")
147+
.isSatisfied(true)
148+
.build());
149+
150+
reviewRepository.save(Review.builder()
151+
.reviewer(anotherUser)
152+
.product(testProduct)
153+
.comment("Second review")
154+
.isSatisfied(false)
155+
.build());
156+
157+
// When & Then
158+
mockMvc.perform(get("/api/v1/reviews/products/{productId}", testProduct.getId())
159+
.header("Authorization", "Bearer " + testUserToken))
160+
.andDo(print())
161+
.andExpect(status().isOk())
162+
.andExpect(jsonPath("$.resultCode").value("200"))
163+
.andExpect(jsonPath("$.data").isArray())
164+
.andExpect(jsonPath("$.data.length()").value(2))
165+
.andExpect(jsonPath("$.data[0].comment").value("First review"))
166+
.andExpect(jsonPath("$.data[1].comment").value("Second review"));
167+
}
168+
140169
@Test
141170
@DisplayName("리뷰 수정 성공")
142171
void updateReview_Success() throws Exception {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.backend.global.config;
2+
3+
import org.redisson.Redisson;
4+
import org.redisson.api.RedissonClient;
5+
import org.redisson.config.Config;
6+
import org.springframework.beans.factory.annotation.Value;
7+
import org.springframework.boot.test.context.TestConfiguration;
8+
import org.springframework.context.annotation.Bean;
9+
10+
@TestConfiguration
11+
public class TestRedissonConfig {
12+
13+
@Value("${spring.data.redis.host}")
14+
private String redisHost;
15+
16+
@Value("${spring.data.redis.port}")
17+
private int redisPort;
18+
19+
private static final String REDISSON_HOST_PREFIX = "redis://";
20+
21+
@Bean
22+
public RedissonClient redissonClient() {
23+
Config config = new Config();
24+
config.useSingleServer()
25+
.setAddress(REDISSON_HOST_PREFIX + redisHost + ":" + redisPort);
26+
27+
return Redisson.create(config);
28+
}
29+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.backend.global.redis;
2+
3+
import jakarta.annotation.PostConstruct;
4+
import jakarta.annotation.PreDestroy;
5+
import org.springframework.beans.factory.annotation.Value;
6+
import org.springframework.boot.test.context.TestConfiguration;
7+
import org.springframework.context.annotation.Bean;
8+
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
9+
import org.springframework.data.redis.core.RedisTemplate;
10+
import org.springframework.data.redis.serializer.StringRedisSerializer;
11+
import redis.embedded.RedisServer;
12+
13+
import java.io.IOException;
14+
15+
@TestConfiguration
16+
public class TestRedisConfig {
17+
18+
private final RedisServer redisServer;
19+
20+
public TestRedisConfig(@Value("${spring.data.redis.port}") int redisPort) {
21+
try {
22+
this.redisServer = new RedisServer(redisPort);
23+
} catch (IOException e) {
24+
throw new RuntimeException(e);
25+
}
26+
}
27+
28+
@PostConstruct
29+
public void startRedis() {
30+
try {
31+
this.redisServer.start();
32+
} catch (Exception e) {
33+
throw new RuntimeException(e);
34+
}
35+
}
36+
37+
@PreDestroy
38+
public void stopRedis() {
39+
try {
40+
this.redisServer.stop();
41+
} catch (Exception e) {
42+
throw new RuntimeException(e);
43+
}
44+
}
45+
46+
@Bean
47+
public LettuceConnectionFactory redisConnectionFactory(@Value("${spring.data.redis.host}") String host, @Value("${spring.data.redis.port}") int port) {
48+
return new LettuceConnectionFactory(host, port);
49+
}
50+
51+
@Bean
52+
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
53+
RedisTemplate<String, Object> template = new RedisTemplate<>();
54+
template.setConnectionFactory(connectionFactory);
55+
template.setKeySerializer(new StringRedisSerializer());
56+
template.setValueSerializer(new StringRedisSerializer());
57+
return template;
58+
}
59+
}

0 commit comments

Comments
 (0)