Project/토이 프로젝트

[Cinemagram] Popular 페이지 렌더링 - (11)

Lea Hwang 2023. 1. 26. 00:32

저번 포스팅까지 해서 좋아요 기능 구현 끝이 났습니다. 이제 드디어 Popular페이지를 구현하도록 하겠습니다.

복습해 보자면, Popular페이지는

좋아요가 있는 사진들을 출력하는 페이지로 좋아요가 많은 순으로 나오게 됩니다. 

이번 포스팅에서는 쿼리를 짜는 부분이 중점적으로 나옵니다. 
최종 쿼리가 아닌 단계별 쿼리를 소개할 예정입니다. 
이 정도는 앞으로도 짤 수 있게 계속해서 연습이 필요합니다.

익숙해져야 할 개념
서브 쿼리
  인라인 뷰 : FROM절에 사용하는 서브 쿼리
  스칼라 서브쿼리 : SELECT절에 사용하는 서브 쿼리

그룹 바이

조인

 

좋아요가 있는 이미지 출력 쿼리

관련 클래스

  • ImageController
  • ImageService
  • ImageRepository

 

로직과 nativeQuery 넣을 위치

ImageController

@GetMapping("/image/popular")
public String popular(@AuthenticationPrincipal CustomUserDetails customUserDetails, Model model) {
    model.addAttribute("sessionId", customUserDetails.getUser().getId());

    List<Image> images = imageService.popularImages();
    model.addAttribute("images", images);
    return "/image/popular";
}

질문 : ApiController에 만들지 않는 이유

api는 데이터를 리턴하는 서버입니다. 여기서는  Ajax를 사용하지 않을 것이므로 ApiContorller를 쓸 필요는 없습니다. 

단순히 model에 데이터를 담아 Html에 뿌려주기만 하면 됩니다.

 

ImageService

@Transactional(readOnly = true)
public List<Image> popularImages() {
    return imageRepository.cPopularImages();
}

가져오기만 할 것이므로 @Transactional(readOnly = true)

 

ImageRepository

public interface ImageRepository extends JpaRepository<Image, Integer> {

    @Query(value = "", nativeQuery = true)
    List<Image> cPopularImages();

}

 

Mysql Workbench에서 쿼리 실습

목표는 좋아요가 있는 이미지를 뽑아내는 것이므로 현재 image, likes테이블을 확인해 보겠습니다. 

SELECT * FROM image;

SELECT * FROM likes;

 

어떤 이미지들을 좋아요 했는지 출력해 보겠습니다. 

SELECT imageId FROM likes;

 

우리는 좋아요가 많은 순으로 출력해야 합니다.

  • 좋아요 카운트를 담을 칼럼이 필요합니다.(가상칼럼)
    • like 받은 수를(likesCount)를 가상칼럼에 넣습니다.
  • 이미지가 중복으로 나오는 것을 방지해야 합니다. (Group By)
    •  imageId를 기준으로 출력합니다.

 

가상 컬럼 생성, (우선) 전부 1로 세팅

SELECT imageId, 1 likesCount FROM likes;

 

여기서 imageId가 중복되어 출력된 것을 확인할 수 있습니다. 이러한 중복을 제거해 볼 수 있는데요.

SELECT imageId, 1 likesCount FROM likes GROUP BY imageId;

 

imageId를 세서 이를 가상칼럼을 만든 likesCount에 넣으면 어떤 이미지를 얼마나 좋아요했는지 알 수 있습니다. 

SELECT imageId, COUNT(imageId) likesCount FROM likes GROUP BY imageId;

 

나아가, 우리가 원하는 건 좋아요가 많은 순으로 출력이므로 내림차순 정렬이 필요합니다. 

SELECT imageId, COUNT(imageId) likesCount FROM likes GROUP BY imageId ORDER BY likesCount DESC;

 

여기서 문제가 발생합니다. 제가 원하는 건 두 칼럼 전부가 아닌  imageId 뿐입니다. 

칼럼을 하나로 만들기 위해 인라인 뷰를 사용하겠습니다. (인라인 뷰는 FROM절에 사용하는 서브 쿼리)

  • likesCount를 ORDER BY로 처리

[주의] FROM절에 별칭 줘야 함 

SELECT imageId
FROM (
SELECT imageId, COUNT(imageId) likesCount 
FROM likes GROUP BY imageId 
ORDER BY likesCount DESC
) c;

이렇게 해서 imageId과 likesCount를 imageId로 합쳤습니다.

 

INNER JOIN으로 합치기

SELECT * FROM image;
SELECT imageId, COUNT(imageId) likeCount FROM likes GROUP BY imageId;

SELECT * FROM image i INNER JOIN (SELECT imageId, COUNT(imageId) likesCount FROM likes GROUP BY imageId) c
ON i.id = c.imageId
ORDER BY likesCount DESC;

 

최종 쿼리였으면 좋겠지만, image 테이블과 likes 테이블에서 계산된 '좋아요' 수를 조인하기에 imageIdlikesCount도 결과에 포함시키므로 수정이 필요합니다.

[최종]

SELECT i.* FROM image i INNER JOIN (SELECT imageId, COUNT(imageId) likesCount FROM likes GROUP BY imageId) c
ON i.id = c.imageId
ORDER BY likesCount DESC;

image 테이블의 데이터를 '좋아요' 수에 따라 정렬하여 모든 필드를 반환하는데, 이때 '좋아요' 수는 결과에 포함되지 않도록 수정했습니다.

 

브라우저 확인

로그인 apple후 Popular페이지 클릭 시

 

좋아요를 많이 받은 이미지 순으로 출력되며, 해당 이미지 클릭 시 이미지를 등록한 유저의 profile페이지로 이동합니다. 

위의 스파이더맨 노 웨이 홈 이미지 클릭시 전환된 화면

 

 

 

 

 

 

 

여기까지 해서 Popular페이지 렌더링을 끝냈습니다. 다음 12번째 포스팅에서는 자잘한 기능들(마우스 hover시 원하는 데이터 나오게, 프로필 이미지 수정)을 서술할 예정입니다.