이전 글: 레시피 CRUD 기능 구현 (tistory.com)
프론트와 협업중 우리의 기존 서비스 flow에 문제점이 있다는 것을 발견했다.
기존 flow에서는 채팅 진행 도중 레시피 저장기능에는 문제가 없었지만,
이미 끝난 채팅, 즉 저장된 채팅에서 레시피를 저장하려면 프론트에서 String 형을 여러 List로 쪼개주는 과정이 필요했다.
하지만 이 과정은 프론트에 너무 큰 부담을 주었고,
기존 gpt의 응답을 내가 원하는 형식으로 쪼개는 메서드를 작성해본 백엔드 측에서 해주는게 더 좋다는 결론을 내렸다.
고로 다음과 같은 코드를 추가했다.
public class ChatRecipeRequestDto {
String menu;
String recipe;
}
Dto 형식은 위와 같다. 프론트는 가지고 있는 정보를 menu와 recipe에 그대로 넣어주기만 하면 된다.
어렵게 가공할 필요가 없다!!
@Transactional
public ResponseEntity<?> saveChatAsRecipe(ChatRecipeRequestDto requestDto, HttpSession session) {
String recipe_info = requestDto.getRecipe();
Recipe recipe = new Recipe();
recipe.setMenu(requestDto.getMenu());
recipe.setUser(getUserFromSession(session));
recipe = recipeRepository.save(recipe);
List<String> ingredients = new ArrayList<>();
List<String> instructions = new ArrayList<>();
parseRecipe(ingredients, instructions, recipe_info); //전달받은 레시피를 원하는 형식으로 바꿔줌
List<Ingredients> ingredientsList = new ArrayList<>();
for (String ingredient : ingredients) {
Ingredients newIngredient = new Ingredients();
newIngredient.setIngredient(ingredient);
newIngredient.setRecipe(recipe);
ingredientsList.add(newIngredient);
}
ingredientsRepository.saveAll(ingredientsList);
List<RecipeInfo> recipeInfoList = new ArrayList<>();
for (String recipeInfo : instructions) {
RecipeInfo info = new RecipeInfo();
info.setInformation(recipeInfo);
info.setRecipe(recipe);
recipeInfoList.add(info);
}
recipeInfoRepository.saveAll(recipeInfoList);
return new ResponseEntity<>(new StatusResponseDto( requestDto.getMenu() + " 레시피를 저장했습니다.", 200), HttpStatus.OK);
}
private void parseRecipe(List<String> ingredients, List<String> instructions, String recipe) {
int ingredientStartIndex = recipe.indexOf("재료:");
if (ingredientStartIndex != -1) {
int ingredientEndIndex = recipe.indexOf("레시피:", ingredientStartIndex);
if (ingredientEndIndex != -1) {
String ingredientsSection = recipe.substring(ingredientStartIndex + 4, ingredientEndIndex).trim();
extractItems(ingredientsSection, ingredients);
}
}
int instructionStartIndex = recipe.indexOf("레시피:");
if (instructionStartIndex != -1) {
String instructionsSection = recipe.substring(instructionStartIndex + 5).trim();
extractItems(instructionsSection, instructions);
}
}
private static void extractItems(String section, List<String> items) {
// Split the section into lines
String[] lines = section.split("\\n");
// Extract items from each line
for (String line : lines) {
// Remove the numbering (e.g., "1. ", "2. ") and trim the line
String item = line.replaceAll("^\\d+\\.\\s*", "").trim();
items.add(item);
}
}
- saveChatAsRecipe()는 saveRecipe() 메서드를 기반으로 작성되었다.
parseRecipe()를 통해 재료와 조리 과정 리스트로 나눠진 문자열 리스트들을 저장한다. - parseRecipe()는 "재료:" 와 "레시피" 뒤에있는 문자열만 추려서
extractItems()을 통해 재료, 조리과정 리스트를 작성해주다. - extractItems()는 특정 형식으로 전달되는 문자열을 가공하기 위해 만들었다.
줄바꿈을 먼저 감지하여 배열에 저장후, 저장한 배열에서 숫자 리스트를 제거한다.
애초에 String 형으로 보내줄꺼면 gpt 응답을 ResponseDto로 만들어줄 필요가 없다!
고로 ChatGptService의 메서드도 아래와 같이 바꿔주었다.
public String extractRecipePromptByString(GptRecipeRequestDto requestDto) {
String ingredientsQuestion = realIngredientsQuestionBuilder(requestDto); // 질문 형성
ChatCompletionDto ingredientsCompletionDto = chatCompletionDtoBuilder(ingredientsQuestion); // 요청 형식 형성
Map<String, Object> ingredientsResultMap = prompt(ingredientsCompletionDto); // gpt api에 요청 및 반환
String realIngredients = extractRealIngredients(extractContent(ingredientsResultMap)); // 반환형에서 응답만 추출
String recipeQuestion = recipeQuestionBuilder(requestDto.getMenu(), realIngredients);
ChatCompletionDto recipeChatCompletionDto = chatCompletionDtoBuilder(recipeQuestion);
Map<String, Object> recipeResultMap = prompt(recipeChatCompletionDto);
return extractContent(recipeResultMap);
}
다음 블로그를 보면 위의 코드가 이해 될 것이다. GPT-4: 재료->음식->레시피 기능 구현 (tistory.com)
이렇게 프론트에서 더욱 이용하기 쉬운 코드가 완성되었다. 곧 완성될 demo 버전이 기대된다.
'프로젝트 > AI명종원' 카테고리의 다른 글
next.js로 프론트엔드 개발하기 (0) | 2024.06.14 |
---|---|
채팅 CRUD 기능 구현 (0) | 2024.05.18 |
레시피 CRUD 기능 구현 (0) | 2024.05.17 |
GPT-4: 재료->음식->레시피 기능 구현 (1) | 2024.05.17 |
GPT-3.5: 재료->음식->레시피 기능 구현 (0) | 2024.05.16 |