Skip to content

Commit dd9fa7a

Browse files
authored
[Feat] '가게 조회 API'에 카테고리 필터링 추가
2 parents bc69c0a + 0dfa9de commit dd9fa7a

File tree

8 files changed

+121
-30
lines changed

8 files changed

+121
-30
lines changed

src/main/java/eatda/controller/store/StoreController.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ public ResponseEntity<ImagesResponse> getStoreImages(@PathVariable long storeId)
2323
}
2424

2525
@GetMapping("/api/shops")
26-
public ResponseEntity<StoresResponse> getStores(@RequestParam @Min(1) @Max(50) int size) {
27-
return ResponseEntity.ok(storeService.getStores(size));
26+
public ResponseEntity<StoresResponse> getStores(@RequestParam @Min(1) @Max(50) int size,
27+
@RequestParam(required = false) String category) {
28+
return ResponseEntity.ok(storeService.getStores(size, category));
2829
}
2930

3031
@GetMapping("/api/shops/{storeId}")

src/main/java/eatda/domain/store/StoreCategory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public enum StoreCategory {
1212
CHINESE("중식"),
1313
JAPANESE("일식"),
1414
WESTERN("양식"),
15-
CAFE("카페"),
15+
CAFE("카페/디저트"),
1616
OTHER("기타");
1717

1818
private final String categoryName;

src/main/java/eatda/repository/store/StoreRepository.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package eatda.repository.store;
22

33
import eatda.domain.store.Store;
4+
import eatda.domain.store.StoreCategory;
45
import eatda.exception.BusinessErrorCode;
56
import eatda.exception.BusinessException;
67
import java.util.List;
@@ -19,4 +20,6 @@ default Store getById(Long id) {
1920
Optional<Store> findByKakaoId(String kakaoId);
2021

2122
List<Store> findAllByOrderByCreatedAtDesc(Pageable pageable);
23+
24+
List<Store> findAllByCategoryOrderByCreatedAtDesc(StoreCategory category, Pageable pageable);
2225
}

src/main/java/eatda/service/store/StoreService.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111
import eatda.controller.store.StoreSearchResponses;
1212
import eatda.controller.store.StoresResponse;
1313
import eatda.domain.store.Store;
14+
import eatda.domain.store.StoreCategory;
1415
import eatda.repository.store.CheerRepository;
1516
import eatda.repository.store.StoreRepository;
1617
import eatda.storage.image.ImageStorage;
1718
import java.util.List;
1819
import java.util.Optional;
1920
import lombok.RequiredArgsConstructor;
2021
import org.springframework.data.domain.Pageable;
22+
import org.springframework.lang.Nullable;
2123
import org.springframework.stereotype.Service;
2224

2325
@Service
@@ -36,13 +38,21 @@ public StoreResponse getStore(long storeId) {
3638
}
3739

3840
// TODO : N+1 문제 해결
39-
public StoresResponse getStores(int size) {
40-
return storeRepository.findAllByOrderByCreatedAtDesc(Pageable.ofSize(size))
41+
public StoresResponse getStores(int size, @Nullable String category) {
42+
return findStores(size, category)
4143
.stream()
4244
.map(store -> new StorePreviewResponse(store, getStoreImageUrl(store).orElse(null)))
4345
.collect(collectingAndThen(toList(), StoresResponse::new));
4446
}
4547

48+
private List<Store> findStores(int size, @Nullable String category) {
49+
if (category == null || category.isBlank()) {
50+
return storeRepository.findAllByOrderByCreatedAtDesc(Pageable.ofSize(size));
51+
}
52+
return storeRepository.findAllByCategoryOrderByCreatedAtDesc(
53+
StoreCategory.from(category), Pageable.ofSize(size));
54+
}
55+
4656
public ImagesResponse getStoreImages(long storeId) {
4757
Store store = storeRepository.getById(storeId);
4858
List<String> imageUrls = cheerRepository.findAllImageKey(store)

src/test/java/eatda/controller/store/StoreControllerTest.java

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import eatda.controller.BaseControllerTest;
77
import eatda.domain.member.Member;
88
import eatda.domain.store.Store;
9+
import eatda.domain.store.StoreCategory;
910
import java.time.LocalDateTime;
1011
import org.junit.jupiter.api.Nested;
1112
import org.junit.jupiter.api.Test;
@@ -43,12 +44,14 @@ class GetStore {
4344
class GetStores {
4445

4546
@Test
46-
void 음식점_목록을_최신순으로_조회한다() {
47+
void 모든_카테고리의_음식점_목록을_최신순으로_조회한다() {
4748
Member member = memberGenerator.generate("111");
4849
LocalDateTime startAt = LocalDateTime.of(2025, 7, 26, 1, 0, 0);
49-
Store store1 = storeGenerator.generate("111", "서울 강남구 대치동 896-33", startAt);
50-
Store store2 = storeGenerator.generate("222", "서울 강남구 대치동 896-34", startAt.plusHours(1));
51-
Store store3 = storeGenerator.generate("333", "서울 강남구 대치동 896-35", startAt.plusHours(2));
50+
Store store1 = storeGenerator.generate("112", "서울 강남구 대치동 896-33", StoreCategory.KOREAN, startAt);
51+
Store store2 = storeGenerator.generate("113", "서울 성북구 석관동 123-45", StoreCategory.OTHER,
52+
startAt.plusHours(1));
53+
Store store3 = storeGenerator.generate("114", "서울 강남구 역삼동 678-90", StoreCategory.KOREAN,
54+
startAt.plusHours(2));
5255
cheerGenerator.generateCommon(member, store1, "image-key-1");
5356
cheerGenerator.generateCommon(member, store2, "image-key-2");
5457
cheerGenerator.generateCommon(member, store3, "image-key-3");
@@ -69,6 +72,38 @@ class GetStores {
6972
() -> assertThat(response.stores().get(1).id()).isEqualTo(store2.getId())
7073
);
7174
}
75+
76+
@Test
77+
void 특정_카테고리의_음식점_목록을_최신순으로_조회한다() {
78+
Member member = memberGenerator.generate("111");
79+
LocalDateTime startAt = LocalDateTime.of(2025, 7, 26, 1, 0, 0);
80+
Store store1 = storeGenerator.generate("112", "서울 강남구 대치동 896-33", StoreCategory.CAFE, startAt);
81+
Store store2 = storeGenerator.generate("113", "서울 성북구 석관동 123-45", StoreCategory.OTHER,
82+
startAt.plusHours(1));
83+
Store store3 = storeGenerator.generate("114", "서울 강남구 역삼동 678-90", StoreCategory.CAFE,
84+
startAt.plusHours(2));
85+
cheerGenerator.generateCommon(member, store1, "image-key-1");
86+
cheerGenerator.generateCommon(member, store2, "image-key-2");
87+
cheerGenerator.generateCommon(member, store3, "image-key-3");
88+
89+
int size = 2;
90+
StoreCategory category = StoreCategory.CAFE;
91+
92+
StoresResponse response = given()
93+
.queryParam("size", size)
94+
.queryParam("category", category.getCategoryName())
95+
.when()
96+
.get("/api/shops")
97+
.then()
98+
.statusCode(200)
99+
.extract().as(StoresResponse.class);
100+
101+
assertAll(
102+
() -> assertThat(response.stores()).hasSize(size),
103+
() -> assertThat(response.stores().get(0).id()).isEqualTo(store3.getId()),
104+
() -> assertThat(response.stores().get(1).id()).isEqualTo(store1.getId())
105+
);
106+
}
72107
}
73108

74109
@Nested

src/test/java/eatda/document/store/StoreDocumentTest.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package eatda.document.store;
22

33

4-
import static org.mockito.ArgumentMatchers.anyInt;
54
import static org.mockito.ArgumentMatchers.anyLong;
65
import static org.mockito.ArgumentMatchers.anyString;
76
import static org.mockito.Mockito.doReturn;
@@ -23,6 +22,7 @@
2322
import eatda.document.RestDocsRequest;
2423
import eatda.document.RestDocsResponse;
2524
import eatda.document.Tag;
25+
import eatda.domain.store.StoreCategory;
2626
import eatda.exception.BusinessErrorCode;
2727
import eatda.exception.BusinessException;
2828
import io.restassured.http.ContentType;
@@ -101,7 +101,9 @@ class GetStores {
101101
.tag(Tag.STORE_API)
102102
.summary("음식점 목록 조회")
103103
.queryParameter(
104-
parameterWithName("size").description("조회할 음식점 개수 (최소 1, 최대 50)")
104+
parameterWithName("size").description("조회할 음식점 개수 (최소 1, 최대 50)"),
105+
parameterWithName("category")
106+
.description("음식점 카테고리(기본값: 전체) (한식,중식,일식,양식,디저트/카페,기타)").optional()
105107
);
106108

107109
RestDocsResponse responseDocument = response()
@@ -117,13 +119,14 @@ class GetStores {
117119

118120
@Test
119121
void 음식점_목록_최신순으로_조회() {
122+
int size = 2;
123+
StoreCategory category = StoreCategory.CAFE;
120124
StoresResponse response = new StoresResponse(List.of(
121125
new StorePreviewResponse(2L, "https://example.image", "농민백암순대", "강남구", "대치동", "한식"),
122126
new StorePreviewResponse(1L, "https://example.image", "석관동떡볶이", "성북구", "석관동", "한식")
123127
));
124-
doReturn(response).when(storeService).getStores(anyInt());
128+
doReturn(response).when(storeService).getStores(size, category.getCategoryName());
125129

126-
int size = 2;
127130
var document = document("store/get", 200)
128131
.request(requestDocument)
129132
.response(responseDocument)
@@ -132,16 +135,18 @@ class GetStores {
132135
given(document)
133136
.contentType(ContentType.JSON)
134137
.queryParam("size", size)
138+
.queryParam("category", category.getCategoryName())
135139
.when().get("/api/shops")
136140
.then().statusCode(200);
137141
}
138142

139143
@EnumSource(value = BusinessErrorCode.class, names = {"PRESIGNED_URL_GENERATION_FAILED"})
140144
@ParameterizedTest
141145
void 음식점_목록_조회_실패(BusinessErrorCode errorCode) {
142-
doThrow(new BusinessException(errorCode)).when(storeService).getStores(anyInt());
143-
144146
int size = 2;
147+
StoreCategory category = StoreCategory.CAFE;
148+
doThrow(new BusinessException(errorCode)).when(storeService).getStores(size, category.getCategoryName());
149+
145150
var document = document("store/get", errorCode)
146151
.request(requestDocument)
147152
.response(ERROR_RESPONSE)
@@ -150,6 +155,7 @@ class GetStores {
150155
given(document)
151156
.contentType(ContentType.JSON)
152157
.queryParam("size", size)
158+
.queryParam("category", category.getCategoryName())
153159
.when().get("/api/shops")
154160
.then().statusCode(errorCode.getStatus().value());
155161
}

src/test/java/eatda/fixture/StoreGenerator.java

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,26 @@ public StoreGenerator(StoreRepository storeRepository) {
2626
}
2727

2828
public Store generate(String kakaoId, String lotNumberAddress) {
29-
Store store = Store.builder()
29+
Store store = create(kakaoId, lotNumberAddress, DEFAULT_CATEGORY);
30+
return storeRepository.save(store);
31+
}
32+
33+
public Store generate(String kakaoId, String lotNumberAddress, LocalDateTime createdAt) {
34+
Store store = create(kakaoId, lotNumberAddress, DEFAULT_CATEGORY);
35+
DomainUtils.setCreatedAt(store, createdAt);
36+
return storeRepository.save(store);
37+
}
38+
39+
public Store generate(String kakaoId, String lotNumberAddress, StoreCategory category, LocalDateTime createdAt) {
40+
Store store = create(kakaoId, lotNumberAddress, category);
41+
DomainUtils.setCreatedAt(store, createdAt);
42+
return storeRepository.save(store);
43+
}
44+
45+
private Store create(String kakaoId, String lotNumberAddress, StoreCategory category) {
46+
return Store.builder()
3047
.kakaoId(kakaoId)
31-
.category(DEFAULT_CATEGORY)
48+
.category(category)
3249
.phoneNumber(DEFAULT_PHONE_NUMBER)
3350
.name(DEFAULT_NAME)
3451
.placeUrl(DEFAULT_PLACE_URL)
@@ -37,12 +54,5 @@ public Store generate(String kakaoId, String lotNumberAddress) {
3754
.latitude(DEFAULT_LATITUDE)
3855
.longitude(DEFAULT_LONGITUDE)
3956
.build();
40-
return storeRepository.save(store);
41-
}
42-
43-
public Store generate(String kakaoId, String lotNumberAddress, LocalDateTime createdAt) {
44-
Store store = generate(kakaoId, lotNumberAddress);
45-
DomainUtils.setCreatedAt(store, createdAt);
46-
return storeRepository.save(store);
4757
}
4858
}

src/test/java/eatda/service/store/StoreServiceTest.java

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import eatda.controller.store.StoreResponse;
1212
import eatda.domain.member.Member;
1313
import eatda.domain.store.Store;
14+
import eatda.domain.store.StoreCategory;
1415
import eatda.exception.BusinessErrorCode;
1516
import eatda.exception.BusinessException;
1617
import eatda.service.BaseServiceTest;
@@ -59,26 +60,51 @@ class GetStore {
5960
class GetStores {
6061

6162
@Test
62-
void 음식점_목록을_최신순으로_조회한다() {
63+
void 모든_카테고리의_음식점_목록을_최신순으로_조회한다() {
6364
Member member = memberGenerator.generate("111");
6465
LocalDateTime startAt = LocalDateTime.of(2025, 7, 26, 1, 0, 0);
65-
Store store1 = storeGenerator.generate("농민백암순대", "서울 강남구 대치동 896-33", startAt);
66-
Store store2 = storeGenerator.generate("석관동떡볶이", "서울 성북구 석관동 123-45", startAt.plusHours(1));
67-
Store store3 = storeGenerator.generate("강남순대국", "서울 강남구 역삼동 678-90", startAt.plusHours(2));
66+
Store store1 = storeGenerator.generate("농민백암순대", "서울 강남구 대치동 896-33", StoreCategory.KOREAN, startAt);
67+
Store store2 = storeGenerator.generate("석관동떡볶이", "서울 성북구 석관동 123-45", StoreCategory.OTHER,
68+
startAt.plusHours(1));
69+
Store store3 = storeGenerator.generate("강남순대국", "서울 강남구 역삼동 678-90", StoreCategory.KOREAN,
70+
startAt.plusHours(2));
6871
cheerGenerator.generateCommon(member, store1, "image-key-1");
6972
cheerGenerator.generateCommon(member, store2, "image-key-2");
7073
cheerGenerator.generateCommon(member, store3, "image-key-3");
71-
7274
int size = 2;
7375

74-
var response = storeService.getStores(size);
76+
var response = storeService.getStores(size, null);
7577

7678
assertAll(
7779
() -> assertThat(response.stores()).hasSize(size),
7880
() -> assertThat(response.stores().get(0).id()).isEqualTo(store3.getId()),
7981
() -> assertThat(response.stores().get(1).id()).isEqualTo(store2.getId())
8082
);
8183
}
84+
85+
@Test
86+
void 특정_카테고리의_음식점_목록을_최신순으로_조회한다() {
87+
Member member = memberGenerator.generate("111");
88+
LocalDateTime startAt = LocalDateTime.of(2025, 7, 26, 1, 0, 0);
89+
Store store1 = storeGenerator.generate("112", "서울 강남구 대치동 896-33", StoreCategory.KOREAN, startAt);
90+
Store store2 = storeGenerator.generate("113", "서울 성북구 석관동 123-45", StoreCategory.OTHER,
91+
startAt.plusHours(1));
92+
Store store3 = storeGenerator.generate("114", "서울 강남구 역삼동 678-90", StoreCategory.KOREAN,
93+
startAt.plusHours(2));
94+
cheerGenerator.generateCommon(member, store1, "image-key-1");
95+
cheerGenerator.generateCommon(member, store2, "image-key-2");
96+
cheerGenerator.generateCommon(member, store3, "image-key-3");
97+
int size = 2;
98+
StoreCategory category = StoreCategory.KOREAN;
99+
100+
var response = storeService.getStores(size, category.getCategoryName());
101+
102+
assertAll(
103+
() -> assertThat(response.stores()).hasSize(size),
104+
() -> assertThat(response.stores().get(0).id()).isEqualTo(store3.getId()),
105+
() -> assertThat(response.stores().get(1).id()).isEqualTo(store1.getId())
106+
);
107+
}
82108
}
83109

84110
@Nested

0 commit comments

Comments
 (0)