DevKim

[ Webtooniverse ] 웹툰 상세 페이지 개발 - 비슷한 장르 웹툰 추천 ( feat. Querydsl ) 본문

Spring Project/Webtooniverse

[ Webtooniverse ] 웹툰 상세 페이지 개발 - 비슷한 장르 웹툰 추천 ( feat. Querydsl )

on_doing 2021. 8. 3. 00:07
728x90

[ 요구 사항 분석 ]

- 현재 페이지의 웹툰과 비슷한 장르의 웹툰들의 정보를 랜덤으로 뿌려준다.

[Querydsl 세팅 ]

매번 2개의 Repository를 의존성으로 받지 않기 위해서,

Data Jpa에서는 Custom Repository를 JpaRepository 상속 클래스에서 사용할 수 있게끔 지원한다.

사실 기본 CRUD도 Querydsl만을 사용하여, 상속/구현 없는 Repository를 만들어 사용할 수 있지만

빠르게 개발하기 위해 기본 CRUD는 JpaRepository의 도움을 받아서 코드를 작성하였다.

리팩토링 단계에서 Querydsl만을 가져가는 것에 대해 고려해보기로하자.

 

 

1. 어느 곳에서나 JPAQueryFactory를 주입받아 Querydsl을 사용할 수 있게끔 Config 파일을 하나 생성한다.

@Configuration
public class QuerydslConfiguration {

    @PersistenceContext
    private EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }
}

 

2. 이어서 Custom을 생성해주고,

public interface WebtoonRepositoryCustom {
    List<Webtoon> findSimilarWebtoonByGenre(String genre,Webtoon webtoon);
}

 

3. 쿼리문을 작성한다.

현재 웹툰의 장르와 같은 웹툰 중에서, random으로 웹툰을 10개씩 뽑아준다.

이때 현재 웹툰과 같은 웹툰을 추천해주면 안되기 때문에, ne 조건에 현재 웹툰을 넣어주었다.

@RequiredArgsConstructor
public class WebtoonRepositoryImpl implements WebtoonRepositoryCustom{

    private final JPAQueryFactory queryFactory;


    @Override
    public List<Webtoon> findSimilarWebtoonByGenre(String genre,Webtoon webtoon) {
        QWebtoon w = new QWebtoon("w");
        QWebtoonGenre wg = new QWebtoonGenre("wg");

        return queryFactory
                .select(wg.webtoon)
                .from(wg)
                .join(wg.genre)
                .on(wg.genre.genreType.eq(genre),wg.webtoon.ne(webtoon))
                .orderBy(NumberExpression.random().asc())
                .limit(10)
                .fetch();
    }
}

[ 비즈니스 로직 ]

네이버 웹툰의 장르는 다음과 같다.

- 에피소드,옴니버스,스토리 라는 다소 포괄적인 장르와

- 일상,개그,판타지.. 등 세부적인 장르로 나뉜다.

 

전자가 추천 장르에 들어가게될 경우,

스토리가 엄청난 비중을 차지하고 있어서 연관된 장르의 웹툰을 보여주긴 힘들 것 같다는 생각이 들어서,

후자의 세부 장르로 필터링하여 추천해주기로했다.

 public List<SimilarGenreToonDto> getSimilarGenre(Long id) {

        //웹툰 찾기
        Webtoon webtoon = webtoonRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException("해당 id의 웹툰은 존재하지 않습니다.")
        );

        List<Genre> genre = webtoonRepository.findWebToonGenre(webtoon);

        List<SimilarGenreToonDto> similarGenreToonDtoList = new ArrayList<>();

        //비슷한 장르의 웹툰 찾기
        List<Webtoon> webtoonList = webtoonRepository.findSimilarWebtoonByGenre(genre.get(1).getGenreType(),webtoon);
        for (Webtoon w : webtoonList) {
            SimilarGenreToonDto similarGenreToonDto = SimilarGenreToonDto.builder()
                    .toonAuthor(w.getToonAuthor())
                    .toonTitle(w.getToonTitle())
                    .toonImg(w.getToonImg())
                    .toonAvgPoint(w.getToonAvgPoint())
                    .toonFlatform(w.getToonFlatform())
                    .toonWeekday(w.getToonWeekday())
                    .totalPointCount(w.getTotalPointCount())
                    .build();

            similarGenreToonDtoList.add(similarGenreToonDto);
        }

        return similarGenreToonDtoList;

    }

[ 통합 테스트 코드 ]

- 더미 데이터가 좀 많은 느낌이 있지만.. 랜덤으로 쏙쏙 골라서 잘 뿌려주는지 확인해보고 싶었다.

이제 기능들이 작동하는 것을 확인했으니, 다음은 단위 테스트로 변경해보자!

@DisplayName("비슷한 장르의 웹툰을 랜덤으로 추천 테스트")
    @Test
    public void test() throws Exception{
        //given
        //웹툰
        Webtoon w1 = createWebtoon("제목1", "작가1", "내용1");
        Webtoon w2 = createWebtoon("제목2", "작가2", "내용2");
        Webtoon w3 = createWebtoon("제목3", "작가3", "내용3");
        Webtoon w4 = createWebtoon("제목4", "작가4", "내용4");
        Webtoon w5 = createWebtoon("제목5", "작가5", "내용5");
        em.persist(w1);
        em.persist(w2);
        em.persist(w3);
        em.persist(w4);
        em.persist(w5);

        //장르 저장
        Genre g1 = createGenre("일상");
        Genre g2 = createGenre("개그");
        Genre g3 = createGenre("판타지");

        em.persist(g1);
        em.persist(g2);
        em.persist(g3);

        //웹툰_장르 설정
        WebtoonGenre wg1 = createWebToonGenre(w1, g1);
        WebtoonGenre wg2 = createWebToonGenre(w1, g2);
        WebtoonGenre wg3 = createWebToonGenre(w2, g1);
        WebtoonGenre wg4 = createWebToonGenre(w2, g2);
        WebtoonGenre wg5 = createWebToonGenre(w3, g3);
        WebtoonGenre wg6 = createWebToonGenre(w3, g2);
        WebtoonGenre wg7 = createWebToonGenre(w4, g1);
        WebtoonGenre wg8 = createWebToonGenre(w4, g3);
        WebtoonGenre wg9 = createWebToonGenre(w5, g1);
        WebtoonGenre wg10 = createWebToonGenre(w5, g2);

        em.persist(wg1);
        em.persist(wg2);
        em.persist(wg3);
        em.persist(wg4);
        em.persist(wg5);
        em.persist(wg6);
        em.persist(wg7);
        em.persist(wg8);
        em.persist(wg9);
        em.persist(wg10);


        //when
        List<SimilarGenreToonDto> toonDtos = webtoonService.getSimilarGenre(w1.getId());

        //then
        assertThat(toonDtos.size()).isEqualTo(2);
        assertThat(toonDtos.get(0).getToonTitle()).isNotEqualTo(w1.getToonTitle());

        for (SimilarGenreToonDto toonDto : toonDtos) {
            System.out.println("toonDto.getToonTitle() = " + toonDto.getToonTitle());
        }

    }
728x90
Comments