2022. 12. 19. 01:11ㆍProject/시네마그램
이번 포스팅은 어떤 기능을 구현할까 고민이 있었습니다. 남은 기능 중 큰 꼭지인 팔로우 기능을 구현하고자 했지만
이번에는 조금 쉬운 기능을 구현하며 잠시 쉬어가는 챕터로 만들기로 결정했습니다.
Feed 페이지 랜더링
Cinemagram의 Feed 페이지란?
로그인 한 유저가 팔로우한 유저들이 올린 이미지를 볼 수 있는 페이지입니다.
그럼 Feed 페이지로 갈 때 들고 가야 할 데이터는 어떤 게 있을까요?
- User 정보
- Image 정보
- Image
- caption
- (좋아요, 댓글은 나중에 구현)
어떤 쿼리를 짜야하는가?
관련 Repository에 nativeQuery를 짜려고 하는데, 이때 어떤 쿼리를 넣어야 할까요?
❗❗ 당장 어떤 쿼리를 짜야할까 생각하지 말고 다음의 스텝을 따라가 보자 ❗❗
1. 과연 나는 어떤 걸 출력하길 원하는지
2. 1번의 결과를 얻기 위해서는 어떤 테이블, 어떤 칼럼들이 필요한지
3. 그럼 WHERE절에 어떤 쿼리가 들어가야 하는지
단계적으로 생각하고 쿼리를 하나씩 짜 보자
현재 팔로우 상황
fromUserId = 2에 주목해서 저 빨간 박스의 의미를 보면
"userId=2가 팔로우하고 있는 userId는 1번과 3번"이라는 뜻입니다.
어떤 유저의 어떤 이미지를 출력할 것인가
- 로그인 한 유저 자신이 올린 이미지는 Feed에서 볼 필요가 없다.
- 팔로우한 유저들이 올린 이미지가 Feed에 나와야 한다.
여기까지 해서 어떤 데이터를 출력해야 할지 단계별로 알아보았습니다. 차근차근 쿼리를 작성해보도록 하겠습니다.
1. 로그인 한 유저(userId=2 가정)의 팔로우 정보 확인 후 팔로우한 유저 id를 리턴
* 우리는 아직 팔로우 기능 구현 전이므로, Postman으로 작업했습니다.
SELECT toUserId FROM follow WHERE fromUserId = 2;
2. 1,3번 유저가 올린 이미지를 출력
SELECT * FROM image WHERE userId IN (1,3);
3. 이제 1번, 2번 쿼리를 조합하면 됩니다. (서브 쿼리)
SELECT * FROM image WHERE userId IN (SELECT toUserId FROM follow WHERE fromUserId = 2);
쿼리 작성이 끝났습니다. 이제 이 쿼리를 작동시키기 위해서 Repository에 등록해보겠습니다.
ImageRepositry
public interface ImageRepository extends JpaRepository<Image, Integer> {
@Query(value = "SELECT * FROM image WHERE userId IN (SELECT toUserId FROM follow WHERE fromUserId = :sessionId)", nativeQuery = true)
List<Image> cFeed (@Param(value = "sessionId") int sessionId);
}
ImageRepository에 nativeQuery만 적으면 끝? 아니죠! 이를 호출하는 API 부분을 생성해보겠습니다.
우선 껍데기 부분만 만들어 놓고 다시 와서 채워보겠습니다.
ImageApiController
@RequiredArgsConstructor
@RestController
public class ImageApiController {
@GetMapping("/api/feed")
public ResponseEntity<?> Feed(){
return null;
}
}
Service 호출
ImageService
@Transactional(readOnly = true)
public List<Image> Feed(int sessionId) {
List<Image> images = imageRepository.cFeed(sessionId);
return images;
}
Service부분을 만들었더니 Controller의 해당 함수에서 어떤 데이터를 넘기고, 결과로 받아야 하는지 알게되었습니다.
ImageApiController 추가
@GetMapping("/api/feed")
public ResponseEntity<?> Feed(@AuthenticationPrincipal CustomUserDetails customUserDetails){
List<Image> images = imageService.Feed(customUserDetails.getUser().getId());
return new ResponseEntity<>(new ResDto<>(1,"피드 성공", images), HttpStatus.OK);
}
확인
Pageable을 사용해 Pagination 구현
추가적으로, 스크롤을 내리면 Image가 이어서 나오는 페이징 기능도 구현해보도록 하겠습니다.
- @PageableDefault() Pageable pageable
- JS > window의 scroll이벤트
- $(window). scroll(() => { });
Pageable
Pageable 참고 문서:
Pageable을 Controller 매개변수에 추가합니다.
ImageApiController
@GetMapping("/api/feed")
public ResponseEntity<?> Feed(@AuthenticationPrincipal CustomUserDetails customUserDetails,
@PageableDefault(size=4, sort="id", direction = Sort.Direction.DESC) Pageable pageable){
Page<Image> images = imageService.Feed(customUserDetails.getUser().getId(), pageable);
return new ResponseEntity<>(new ResDto<>(1,"피드 성공", images), HttpStatus.OK);
}
- import 주의
- import org.springframework.data.domain.Pageable;
- size=4
- 한 번에 4개의 Image를 출력하겠다.
- sort="id", direction = Sort.Direction.DESC
- id를 기준으로 내림차순 정렬해서 출력하겠다. (최신순)
ImageService 호출
@Transactional(readOnly = true)
public Page<Image> Feed(int sessionId, Pageable pageable) {
Page<Image> images = imageRepository.cFeed(sessionId, pageable);
return images;
}
- @Transactional(readOnly = true)
- 읽기 전용
- 영속성 콘텍스트에서 변경 감지 후 더티 체킹 해서 DB에 Flush(반영)하는 과정을 안 함 → 성능 향상
ImageRepository
public interface ImageRepository extends JpaRepository<Image, Integer> {
@Query(value = "SELECT * FROM image WHERE userId IN (SELECT toUserId FROM follow WHERE fromUserId = :sessionId)", nativeQuery = true)
Page<Image> cFeed (@Param(value = "sessionId") int sessionId, @Param(value = "pageable") Pageable pageable);
}
기존 코드에 Pageable 코드만 추가하였습니다. 그럼 어떤 식으로 가져오는 걸까요?
cFeed를 가지고 올 때 4건씩 Image를 id를 기준으로 최신순으로 정렬해서 가지고 옵니다.
size를 4로 설정했으므로 4까지의 이미지만 나옵니다.
window의 scroll이벤트
이제 window의 scroll이벤트를 통해
- $(window).scroll(() => { });
스크롤 시 size에 맞는 Image를 추가적으로 더 로드해 볼텐데요.
$(window).scrollTop()
$(document).height()
$(window).height());
를 활용해서
현재 스크롤 위치가 거의 0이 될 때, 다음 page를 로드하게끔 만들었습니다.
let checkNum = $(window).scrollTop() - ($(document).height() - $(window).height());
if(checkNum < 1 && checkNum > -1) {
page++;
storyLoad();
}
여기까지 Feed 페이지 렌더링 구현이 끝났습니다. Feed 페이지 렌더링으로 push하였습니다.
이어서 Popular 페이지까지 구현하려고 했지만, 이 페이지는 좋아요를 받은 이미지를 출력하는 부분이므로
다음 포스팅에서 진행하도록하겠습니다.
- 좋아요 기능 구현 후
- Popular 페이지 랜더링
'Project > 시네마그램' 카테고리의 다른 글
[Cinemagram] 팔로우 기능 구현 - (9) (1) | 2022.12.26 |
---|---|
[Trouble Shooting] JPA DTO Mapping - QLRM 라이브러리 사용 (0) | 2022.12.23 |
[Cinemagram] Image 업로드 및 렌더링 (feat. OSIV) - (7) (0) | 2022.12.14 |
[Cinemagram] JPA Native Query 팔로우 구현 및 예외처리 - (6) (0) | 2022.12.11 |
[Refactoring] Spring Data JPA Auditing - 시간 한 곳에서 관리 (0) | 2022.12.11 |