내가 쓰고있는 향수와 비슷한 결의 향수를 추천해주는 기능이다.
플로우는 다음과 같다.
브랜드 입력 -> 내가 쓰는 향수 선택 -> (향,무드,성별)을 토대로 유사한 향수를 제시해준다.)
원래 별 생각 없이 하루동안 구현했었던 기능이다. 하지만 여러 제약사항이 있다는 것을 뒤늦게 깨닿고 과감하게 새로 만들었다. 또한, 첫번째 기능을 리팩토링하면서 두번째 기능 또한 더 효율적으로 코드를 짤 수 있다고 생각이 들어서 다시 만들었다.
기존 로직
SimilarPerfumeRecommend.java
@Service
public class SimilarPerfumeRecommend {
private final SurveyRepository surveyRepository;
private final SurveyUtil surveyUtil;
private final SurveyService surveyService;
private final PerfumeRepository perfumeRepository;
public SimilarPerfumeRecommend(SurveyRepository surveyRepository, SurveyUtil surveyUtil, SurveyService surveyService,
PerfumeRepository perfumeRepository) {
this.surveyService = surveyService;
this.surveyRepository = surveyRepository;
this.surveyUtil = surveyUtil;
this.perfumeRepository = perfumeRepository;
}
private List<Survey> extractFirstFeature(PerfumeResponseDto perfumeResponseDto) {
return surveyRepository.findByGenderAnswerOrGenderAnswer(surveyService.findSurveyById(perfumeResponseDto.getId()).getGenderAnswer(), "젠더리스");
}
private List<Survey> extractSecondFeature(PerfumeResponseDto perfumeResponseDto) {
return surveyRepository.findByScentAnswer(surveyService.findSurveyById(perfumeResponseDto.getId()).getScentAnswer());
}
private List<Survey> extractThirdFeature(PerfumeResponseDto perfumeResponseDto) {
return surveyRepository.findByMoodAnswerContaining(surveyService.findSurveyById(perfumeResponseDto.getId()).getMoodAnswer());
}
private List<Survey> filterGenderFeature(PerfumeResponseDto perfumeResponseDto) {
return surveyUtil.addList(extractFirstFeature(perfumeResponseDto), surveyRepository.findByGenderAnswer("젠더리스"));
}
public List<Perfume> showSimilarPerfume(PerfumeResponseDto perfumeResponseDto) {
List<Survey> firstComparedList = surveyUtil.compareTwoFilteredSurveyData
(filterGenderFeature(perfumeResponseDto), extractSecondFeature(perfumeResponseDto));
List<Survey> result = surveyUtil.compareTwoFilteredSurveyData(firstComparedList, extractThirdFeature(perfumeResponseDto));
return findExceptRequestedPerfume(findPerfumeData(result),perfumeResponseDto);
}
public List<Perfume> findPerfumeData(List<Survey> surveyList) {
List<Perfume> perfumeList = new ArrayList<>();
for (Survey survey : surveyList) {
perfumeList.add(perfumeRepository.findById(survey.getId()).orElseThrow(PerfumeNotFoundException::new));
}
return perfumeList;
}
private List<Perfume> findExceptRequestedPerfume(List<Perfume> perfumeList, PerfumeResponseDto perfumeResponseDto) {
return perfumeList.stream()
.filter(x -> x.getId() != perfumeResponseDto.getId())
.collect(Collectors.toList());
}
}
따라서,
1. 첫번째 기능을 만들며 만들어두었던 조건 검색을 활용한다.
2. Mood Column에 두가지 이상의 무드가 들어있으면 쪼개서 찾는다.
를 중점으로 코드를 새로 새웠다.
###Service
SimilarPerfumeService.java
@Service
public class SimilarPerfumeService {
private static final String BLANK = "\\s";
private static final int FIRST_MOOD = 0;
private static final int MOOD_COLUMN_SIZE = 1;
private final SurveyService surveyService;
private final SurveyRepository surveyRepository;
private final SurveyUtil surveyUtil;
public SimilarPerfumeService(SurveyService surveyService,
SurveyRepository surveyRepository, SurveyUtil surveyUtil) {
this.surveyService = surveyService;
this.surveyRepository = surveyRepository;
this.surveyUtil = surveyUtil;
}
private List<Survey> findExceptRequestedPerfume(List<Survey> surveyList, SurveyRequestDto surveyRequestDto) {
return surveyList.stream()
.filter(x -> x.getId() != surveyRequestDto.getId())
.collect(Collectors.toList());
}
public List<Perfume> showSimilarPerfume(PerfumeRequestDto perfumeRequestDto) {
Survey survey = surveyService.findSurveyById(perfumeRequestDto.getId());
List<Survey> filteredSurveyList = surveyRepository.findByGenderAnswerAndScentAnswer(survey.getGenderAnswer(), survey.getScentAnswer());
filteredSurveyList = surveyUtil.addList(filteredSurveyList, surveyRepository.findByGenderAnswerAndScentAnswer(SurveyType.GENDERLESS.getValue(), survey.getScentAnswer()));
filteredSurveyList = surveyService.filterByMood(splitMoodAnswer(survey), filteredSurveyList);
filteredSurveyList = findExceptRequestedPerfume(filteredSurveyList, SurveyRequestDto.makeDto(survey));
return surveyService.convertToPerfumeData(filteredSurveyList);
}
public SurveyRequestDto splitMoodAnswer(Survey survey) {
String[] moodAnswerArray = survey.getMoodAnswer().split(BLANK);
if (moodAnswerArray.length == MOOD_COLUMN_SIZE) {
return SurveyRequestDto.builder().moodAnswer(survey.getMoodAnswer()).build();
}
return SurveyRequestDto.builder().moodAnswer(moodAnswerArray[FIRST_MOOD]).build();
}
}
splitMoodAnswer()에서 길이를 확인한 후 두가지 이상으로 이루어져있으면 쪼갠다. 여러 방법이 있겠지만, 첫번째 Mood가 가장 가까운 무드이기 때문에 첫번째 무드로 사용했다.
findExceptRequestedPerfume()은 요청으로 보낸 향수 (내가 갖고 있는 향수)를 제외하고 찾아주는 메서드이다.
-showSimilarPerfume()에서 해당 향수의 향,무드, 성별을 필터링하여 comvertToPerfumeData()를 호출한다.
convertToPerfumeData()는 설문에서 ManyToOne으로 맵핑한 Perfume을 찾아주는 메서드이다.
(수정) splitMoodAnswer()은 유사항수 추천 뿐만 아니라 세부정보 조회에서도 사용할 수 있게 구조를 바꾸었다. SimilarPerfumeService 클래스에서 가질 책임이 아니라고 판단하여 Util클래스로 옮겼다.
테스트코드도 다시 다 짤 생각하니까 막막하다..ㅜ
PostMan 테스트 결과