DevKim

[ Webtooniverse ] 웹툰 상세 페이지 개발 - 웹툰 장르, Review 리스트 본문

Spring Project/Webtooniverse

[ Webtooniverse ] 웹툰 상세 페이지 개발 - 웹툰 장르, Review 리스트

on_doing 2021. 7. 30. 23:33
728x90

[ 설계 ]

기본적으로 장르와 웹툰은 다:다 관계이기 때문에,

WEBTOON_GENRE라는 중간 테이블을 생성하여 1:다 다:1 관계로 풀어서 설계하였다.

불필요한 쿼리문을 없애기위해, 아래와같이 최대한 연관관계는 모두 단방향 매핑으로 걸어주었다.

[ 요구 사항 분석 ]

- /api/v1/webtoon/{id} 로 GET 요청이 들어오면 다음과 같은 정보들을 DTO에 담아서 보내준다.

 

01. 웹툰 기본 정보

- 웹툰의 default 기본값으로, 변하지 않는 정보들이다.

- 단순히 DB에서 id값을 이용하여 가져온다. 

[
	{
    "toonImg" : "웹툰 썸네일 이미지",
    "toonTitle": "여신강림",
    "toonAuthor": "야옹이",
    "toonAge": "12세 이용가",
    "realUrl":"https://comic.naver.com/webtoon/detail?titleId=703846&no=171&weekday=tue",
    "toonContent": "네웹 대표 글로벌 인기작! 주경, 수호, 서준. 세 청춘의 두근두근 눈호강 로맨스~♡"
    "toonWeekday" : "월",
    "toonFlatform" : "네이버",
    "finished": 0 /1
	}
]

 

02. 웹툰의 장르 리스트 만들기

- 중간 테이블 역할을 하는 WebToonGenre 테이블은 조인 테이블의 역할만 수행한다.

즉, 각각의 FK 값 외에 다른 값이 들어가진 않는다.

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Entity
@Table(name = "WEBTOON_GENRE")
public class WebtoonGenre {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "toonGenreId")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="genreId")
    private Genre genre;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "toonId")
    private Webtoon webtoon;

    public WebtoonGenre createWebToonGenre(Genre genre,Webtoon webtoon)
    {
        WebtoonGenre webtoonGenre = new WebtoonGenre();
        webtoonGenre.genre=genre;
        webtoonGenre.webtoon=webtoon;

        return webtoonGenre;
    }
}

- 쿼리를 날려보자

찾고 싶은 웹툰을 조건으로 inner조인을 한 뒤, 그것의 장르 리스트를 가져왔다.

@Query("SELECT wg.genre from WebtoonGenre wg inner join wg.webtoon on wg.webtoon=:webtoon")
List<Genre> findWebToonGenre(@Param("webtoon") Webtoon webtoon);

[ 테스트 코드 ]

- 일단 빠르게 기능들을 확인하기 위해 통합 테스트를 진행하였다.

- H2 DB로 데이터가 잘 매핑되는걸 확인하기 위해 Rollback은 false로 걸어주었다.

@Transactional
@Rollback(value = false)
@SpringBootTest
class WebtoonGenreTest {

    @Autowired
    private GenreRepository genreRepository;

    @Autowired
    private WebtoonRepository webtoonRepository;

    @Autowired
    private ReviewRepository reviewRepository;

    @PersistenceContext
    EntityManager em;

    @Test
    @DisplayName("웹툰의 장르가 잘 찾아와지는지 테스트")
    public void test() throws Exception {
        //given

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

        genreRepository.save(g1);
        genreRepository.save(g2);
        genreRepository.save(g3);


        //웹툰 저장
        Webtoon w1 = createWebtoon("제목1", "작가1", "내용1");
        Webtoon w2 = createWebtoon("제목2", "작가2", "내용2");
        webtoonRepository.save(w1);
        webtoonRepository.save(w2);


        //when
        //연관관계 설정
        WebtoonGenre webtoonGenre1 = new WebtoonGenre();
        WebtoonGenre wg1 = webtoonGenre1.createWebToonGenre(g1, w1);
        WebtoonGenre wg2 = webtoonGenre1.createWebToonGenre(g2, w1);

        WebtoonGenre webtoonGenre2 = new WebtoonGenre();
        WebtoonGenre wg3 = webtoonGenre2.createWebToonGenre(g2, w2);
        WebtoonGenre wg4 = webtoonGenre2.createWebToonGenre(g3, w2);

        em.persist(wg1);
        em.persist(wg2);
        em.persist(wg3);
        em.persist(wg4);


        //then
        List<Genre> webToonGenre = webtoonRepository.findWebToonGenre(w1);
        assertThat(webToonGenre.get(0).getGenreType()).isEqualTo("일상");
        assertThat(webToonGenre.get(1).getGenreType()).isEqualTo("개그");

        List<Genre> webToonGenre2 = webtoonRepository.findWebToonGenre(w2);
        assertThat(webToonGenre2.get(0).getGenreType()).isEqualTo("개그");
        assertThat(webToonGenre2.get(1).getGenreType()).isEqualTo("판타지");

    }

    private Webtoon createWebtoon(String title, String author, String content) {
        return Webtoon.builder()
                .toonTitle(title)
                .toonAuthor(author)
                .toonContent(content)
                .build();
    }

    private Genre createGenre(String type) {
        return Genre.builder()
                .genreType(type)
                .build();
    }

}

03. 리뷰 리스트 가져오기 (08.02 수정)

리뷰와 웹툰은 다:1 단방향 연관관계로, 비교적으로 단순한 구조이다.

 

- 쿼리를 날려보자

  • content 내용이 null이 아닌 리뷰만 가져오도록 수정
@Query("select r from Review r inner join r.webtoon on r.webtoon.id=:toonId and r.reviewContent IS Not null")
List<Review> findReviewByWebToonId(@Param("toonId") Long toonId);

[ 테스트 코드 ]

위와 동일하게 통합 테스트로 진행하였다.

상세페이지를 모두 개발한 뒤, 단위테스트로 바꿔서 다시 테스트 코드를 작성할 예정이다.

    @Test
    @DisplayName("웹툰의 Review 가져오기")
    public void 웹툰_리뷰_리스트_가져오기() throws Exception {

        //given
        //리뷰
        Review review1 = Review.builder()
                .reviewContent("리뷰 내용1")
                .userPointNumber(4.5F)
                .likeCount(13)
                .build();

        Review review2 = Review.builder()
                .reviewContent(null)
                .userPointNumber(4.5F)
                .likeCount(13)
                .build();

        //웹툰
        Webtoon w1 = createWebtoon("제목1", "작가1", "내용1");
        Webtoon w2 = createWebtoon("제목2", "작가2", "내용2");
        em.persist(w1);
        em.persist(w2);


        //유저
        User user = User.builder()
                .userName("홍길동")
                .userEmail("abc@naver.com")
                .userImg(1)
                .userGrade(UserGrade.FIRST)
                .build();

        em.persist(user);

        review1.insertWebToonAndUser(w1, user);
        review2.insertWebToonAndUser(w1, user);

        //when
        em.persist(review1);
        em.persist(review2);

        //then
        List<Review> reviewList = webtoonRepository.findReviewByWebToonId(w1.getId());

        //내용이 null인 리뷰는 나오면 안됨
        assertThat(reviewList.size()).isEqualTo(1);
        assertThat(reviewList.get(0).getReviewContent()).isEqualTo(review1.getReviewContent());

    }
728x90
Comments