Skip to content

Commit 8a67f5f

Browse files
authored
[Refactor#216 (#221)
* fix : bugs of testCase, init data * fix : bug * fix: message * refactor : parseIngredients
1 parent 2e3c3c3 commit 8a67f5f

File tree

3 files changed

+326
-281
lines changed

3 files changed

+326
-281
lines changed

src/main/java/com/back/domain/cocktail/dto/CocktailDetailResponseDto.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package com.back.domain.cocktail.dto;
22

3+
import com.back.domain.cocktail.service.CocktailService;
4+
5+
import java.util.List;
6+
37
public record CocktailDetailResponseDto(
48
Long cocktailId,
59
String cocktailName,
@@ -9,7 +13,7 @@ public record CocktailDetailResponseDto(
913
String alcoholBaseType,
1014
String cocktailImgUrl,
1115
String cocktailStory,
12-
String ingredient,
16+
List<CocktailService.IngredientDto> ingredient,
1317
String recipe
1418
) {
1519
}

src/main/java/com/back/domain/cocktail/service/CocktailService.java

Lines changed: 125 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
import org.springframework.transaction.annotation.Transactional;
1818
import org.springframework.util.CollectionUtils;
1919

20-
import java.util.List;
21-
import java.util.Map;
22-
import java.util.NoSuchElementException;
20+
import java.util.*;
21+
import java.util.regex.Matcher;
22+
import java.util.regex.Pattern;
2323
import java.util.stream.Collectors;
2424

2525
@Service
@@ -33,93 +33,92 @@ public class CocktailService {
3333
@Transactional(readOnly = true)
3434
public Cocktail getCocktailById(Long id) {
3535

36-
return cocktailRepository.findById(id)
37-
.orElseThrow(() -> new IllegalArgumentException("User not found. id=" + id));
36+
return cocktailRepository.findById(id)
37+
.orElseThrow(() -> new IllegalArgumentException("Cocktail not found. id=" + id));
3838
}
3939

40-
// 칵테일 무한스크롤 조회
41-
@Transactional(readOnly = true)
42-
public List<CocktailSummaryResponseDto> getCocktails (Long lastId, Integer size)
43-
{ // 무한스크롤 조회, 클라이언트 쪽에서 lastId와 size 정보를 받음.(스크롤 이벤트)
44-
int fetchSize = (size != null) ? size : DEFAULT_SIZE;
45-
46-
List<Cocktail> cocktails;
47-
if (lastId == null) {
48-
// 첫 요청 → 최신 데이터부터
49-
cocktails = cocktailRepository.findAllByOrderByIdDesc(PageRequest.of(0, fetchSize));
50-
} else {
51-
// 무한스크롤 → 마지막 ID보다 작은 데이터 조회
52-
cocktails = cocktailRepository.findByIdLessThanOrderByIdDesc(lastId, PageRequest.of(0, fetchSize));
53-
}
54-
return cocktails.stream()
55-
.map(c -> new CocktailSummaryResponseDto(c.getId(), c.getCocktailName(), c.getCocktailNameKo(), c.getCocktailImgUrl(), c.getAlcoholStrength().getDescription()))
56-
.collect(Collectors.toList());
40+
// 칵테일 무한스크롤 조회
41+
@Transactional(readOnly = true)
42+
public List<CocktailSummaryResponseDto> getCocktails(Long lastId, Integer size) { // 무한스크롤 조회, 클라이언트 쪽에서 lastId와 size 정보를 받음.(스크롤 이벤트)
43+
int fetchSize = (size != null) ? size : DEFAULT_SIZE;
44+
45+
List<Cocktail> cocktails;
46+
if (lastId == null) {
47+
// 첫 요청 → 최신 데이터부터
48+
cocktails = cocktailRepository.findAllByOrderByIdDesc(PageRequest.of(0, fetchSize));
49+
} else {
50+
// 무한스크롤 → 마지막 ID보다 작은 데이터 조회
51+
cocktails = cocktailRepository.findByIdLessThanOrderByIdDesc(lastId, PageRequest.of(0, fetchSize));
5752
}
53+
return cocktails.stream()
54+
.map(c -> new CocktailSummaryResponseDto(c.getId(), c.getCocktailName(), c.getCocktailNameKo(), c.getCocktailImgUrl(), c.getAlcoholStrength().getDescription()))
55+
.collect(Collectors.toList());
56+
}
5857

59-
// 칵테일 검색기능
60-
@Transactional(readOnly = true)
61-
public List<Cocktail> cocktailSearch (String keyword){
62-
// cockTailName, ingredient이 하나만 있을 수도 있고 둘 다 있을 수도 있음
63-
if (keyword == null || keyword.trim().isEmpty()) {
64-
// 아무 검색어 없으면 전체 반환 처리
65-
return cocktailRepository.findAll();
66-
} else {
67-
// 이름 또는 재료 둘 중 하나라도 매칭되면 결과 반환
68-
return cocktailRepository.findByCocktailNameContainingIgnoreCaseOrIngredientContainingIgnoreCase(keyword, keyword);
69-
}
58+
// 칵테일 검색기능
59+
@Transactional(readOnly = true)
60+
public List<Cocktail> cocktailSearch(String keyword) {
61+
// cockTailName, ingredient이 하나만 있을 수도 있고 둘 다 있을 수도 있음
62+
if (keyword == null || keyword.trim().isEmpty()) {
63+
// 아무 검색어 없으면 전체 반환 처리
64+
return cocktailRepository.findAll();
65+
} else {
66+
// 이름 또는 재료 둘 중 하나라도 매칭되면 결과 반환
67+
return cocktailRepository.findByCocktailNameContainingIgnoreCaseOrIngredientContainingIgnoreCase(keyword, keyword);
7068
}
69+
}
7170

72-
// 칵테일 검색,필터기능
73-
@Transactional(readOnly = true)
74-
public List<CocktailSearchResponseDto> searchAndFilter (CocktailSearchRequestDto cocktailSearchRequestDto){
75-
// 기본값 페이지/사이즈 정하기(PAGE 기본값 0, 사이즈 10)
76-
int page = cocktailSearchRequestDto.getPage() != null && cocktailSearchRequestDto.getPage() >= 0
77-
? cocktailSearchRequestDto.getPage() : 0;
78-
79-
int size = cocktailSearchRequestDto.getSize() != null && cocktailSearchRequestDto.getSize() > 0
80-
? cocktailSearchRequestDto.getSize() : DEFAULT_SIZE;
81-
82-
// searchAndFilters에서 조회한 결과값을 pageResult에 저장.
83-
Pageable pageable = PageRequest.of(page, size);
84-
85-
// 빈 리스트(null 또는 [])는 null로 변환
86-
List<AlcoholStrength> strengths = CollectionUtils.isEmpty(cocktailSearchRequestDto.getAlcoholStrengths())
87-
? null
88-
: cocktailSearchRequestDto.getAlcoholStrengths();
89-
90-
List<CocktailType> types = CollectionUtils.isEmpty(cocktailSearchRequestDto.getCocktailTypes())
91-
? null
92-
: cocktailSearchRequestDto.getCocktailTypes();
93-
94-
List<AlcoholBaseType> bases = CollectionUtils.isEmpty(cocktailSearchRequestDto.getAlcoholBaseTypes())
95-
? null
96-
: cocktailSearchRequestDto.getAlcoholBaseTypes();
97-
98-
// Repository 호출
99-
Page<Cocktail> pageResult = cocktailRepository.searchWithFilters(
100-
cocktailSearchRequestDto.getKeyword(),
101-
strengths, // List<AlcoholStrength>
102-
types, // List<CocktailType>
103-
bases, // List<AlcoholBaseType>
104-
pageable
105-
);
71+
// 칵테일 검색,필터기능
72+
@Transactional(readOnly = true)
73+
public List<CocktailSearchResponseDto> searchAndFilter(CocktailSearchRequestDto cocktailSearchRequestDto) {
74+
// 기본값 페이지/사이즈 정하기(PAGE 기본값 0, 사이즈 10)
75+
int page = cocktailSearchRequestDto.getPage() != null && cocktailSearchRequestDto.getPage() >= 0
76+
? cocktailSearchRequestDto.getPage() : 0;
77+
78+
int size = cocktailSearchRequestDto.getSize() != null && cocktailSearchRequestDto.getSize() > 0
79+
? cocktailSearchRequestDto.getSize() : DEFAULT_SIZE;
80+
81+
// searchAndFilters에서 조회한 결과값을 pageResult에 저장.
82+
Pageable pageable = PageRequest.of(page, size);
83+
84+
// 빈 리스트(null 또는 [])는 null로 변환
85+
List<AlcoholStrength> strengths = CollectionUtils.isEmpty(cocktailSearchRequestDto.getAlcoholStrengths())
86+
? null
87+
: cocktailSearchRequestDto.getAlcoholStrengths();
88+
89+
List<CocktailType> types = CollectionUtils.isEmpty(cocktailSearchRequestDto.getCocktailTypes())
90+
? null
91+
: cocktailSearchRequestDto.getCocktailTypes();
92+
93+
List<AlcoholBaseType> bases = CollectionUtils.isEmpty(cocktailSearchRequestDto.getAlcoholBaseTypes())
94+
? null
95+
: cocktailSearchRequestDto.getAlcoholBaseTypes();
96+
97+
// Repository 호출
98+
Page<Cocktail> pageResult = cocktailRepository.searchWithFilters(
99+
cocktailSearchRequestDto.getKeyword(),
100+
strengths, // List<AlcoholStrength>
101+
types, // List<CocktailType>
102+
bases, // List<AlcoholBaseType>
103+
pageable
104+
);
106105

107-
//Cocktail 엔티티 → CocktailResponseDto 응답 DTO로 바꿔주는 과정
108-
List<CocktailSearchResponseDto> resultDtos = pageResult.stream()
109-
.map(c -> new CocktailSearchResponseDto(
110-
c.getId(),
111-
c.getCocktailName(),
112-
c.getCocktailNameKo(),
113-
c.getAlcoholStrength().getDescription(),
114-
c.getCocktailType().getDescription(),
115-
c.getAlcoholBaseType().getDescription(),
116-
c.getCocktailImgUrl(),
117-
c.getCocktailStory()
118-
))
119-
.collect(Collectors.toList());
120-
121-
return resultDtos;
122-
}
106+
//Cocktail 엔티티 → CocktailResponseDto 응답 DTO로 바꿔주는 과정
107+
List<CocktailSearchResponseDto> resultDtos = pageResult.stream()
108+
.map(c -> new CocktailSearchResponseDto(
109+
c.getId(),
110+
c.getCocktailName(),
111+
c.getCocktailNameKo(),
112+
c.getAlcoholStrength().getDescription(),
113+
c.getCocktailType().getDescription(),
114+
c.getAlcoholBaseType().getDescription(),
115+
c.getCocktailImgUrl(),
116+
c.getCocktailStory()
117+
))
118+
.collect(Collectors.toList());
119+
120+
return resultDtos;
121+
}
123122

124123
// private <T> List<T> nullIfEmpty(List<T> list) {
125124
// return CollectionUtils.isEmpty(list) ? null : list;
@@ -132,7 +131,7 @@ public CocktailDetailResponseDto getCocktailDetailById(Long cocktailId) {
132131
.orElseThrow(() -> new NoSuchElementException("칵테일을 찾을 수 없습니다. id: " + cocktailId));
133132

134133
// ingredient 분수 변환
135-
String formattedIngredient = convertFractions(cocktail.getIngredient());
134+
List<IngredientDto> formattedIngredient = parseIngredients(convertFractions(cocktail.getIngredient()));
136135

137136
return new CocktailDetailResponseDto(
138137
cocktail.getId(),
@@ -171,4 +170,46 @@ private String convertFractions(String ingredient) {
171170

172171
return ingredient;
173172
}
173+
174+
// Ingredient DTO
175+
public record IngredientDto(
176+
String ingredientName,
177+
String amount,
178+
String unit
179+
) {
180+
}
181+
182+
private List<IngredientDto> parseIngredients(String ingredientStr) {
183+
if (ingredientStr == null || ingredientStr.isBlank()) return Collections.emptyList();
184+
185+
List<IngredientDto> result = new ArrayList<>();
186+
String[] items = ingredientStr.split(",\\s*");
187+
188+
for (String item : items) {
189+
String[] parts = item.split(":");
190+
if (parts.length != 2) continue;
191+
192+
String name = parts[0].trim();
193+
String amountUnit = parts[1].trim();
194+
195+
// (숫자 + 선택적 분수) + (공백) + (단위)
196+
Pattern pattern = Pattern.compile(
197+
"^([0-9]*\\s*[½⅓⅔¼¾⅛⅜⅝⅞]?)\\s*(.*)$",
198+
Pattern.UNICODE_CHARACTER_CLASS
199+
);
200+
Matcher matcher = pattern.matcher(amountUnit);
201+
202+
if (matcher.matches()) {
203+
String amount = matcher.group(1).trim();
204+
String unit = matcher.group(2).trim();
205+
206+
result.add(new IngredientDto(name, amount, unit));
207+
} else {
208+
// 패턴 매치 실패 시 전체를 amount로 처리
209+
result.add(new IngredientDto(name, amountUnit, ""));
210+
}
211+
}
212+
213+
return result;
214+
}
174215
}

0 commit comments

Comments
 (0)