스프링/스프링부트+JPA - 인스타

스프링부트+JPA - 인스타그램 클론 코딩 09 - 스토리 페이지(API/렌더링/페이징)

H-V 2021. 10. 8. 11:53

이지업 최주호 강사님 강의 참조

 

 

 

 

 

01 스토리 페이지 API 만들기

  • 이미지와 유저정보를 들고 와서 1차로 뿌리자. 
    먼저 로그인된 유저정보와 이미지는 필요없다. 내가 로그인을 한것이기 때문에 내정보가아닌 내가 구독한 다른 사람의 유저정보와 이미지들이 들고 와져야 한다. 즉 2번이 로그인을 했다고 가정하고 1,3번의 스토리 페이지가 불러와져야 한다.
  • 알맞은 유저정보와 이미지를 들고오는 쿼리는 다음과 같다
SELECT * FROM image WHERE userId IN (SELECT toUserId FROM subscribe WHERE fromUserId = 2);

2번이라고 가정하고 1,3 번 구독 유저의 정보와 이미지를 들고 온다.

  • Image.java 클래스가 이미 이미지에대한 클래스이기 때문에 User.java에서 이미지를 들고 올 필요가 없고 Image만 사용해서 이미지 + 유저정보만 들고 오면 된다
*Image.java에 무한참조 방지

	@JsonIgnoreProperties({"images"})
	@JoinColumn(name="userId")
	@ManyToOne(fetch = FetchType.EAGER)
	private User user;

 

 

  • 무한참조 방지 세팅 이후 쿼리를 작동 시키기 위한 ImageRepository 세팅
public interface ImageRepository extends JpaRepository<Image, Integer>{

  @Query(value="SELECT * FROM image WHERE userId IN 
  (SELECT toUserId FROM subscribe WHERE fromUserId = :principalId);", nativeQuery = true)
  List<Image> mStroy(int principalId);
}
  • 이를 호출하는 API 컨트롤러 세팅
@RequiredArgsConstructor
@RestController
public class ImageApiController {
	
	private final ImageService imageService;
	
	@GetMapping("/api/image")
	public ResponseEntity<?> imageStroy(@AuthenticationPrincipal PrincipalDetails principalDetails){
		List<Image> images = imageService.imageStroy(principalDetails.getUser().getId());
		return new ResponseEntity<>(new CMRespDto<>(1, "성공", images), HttpStatus.OK);
	}
}
  • 이에대한 Service 세팅
	//이미지 리스트 뿌리기 
	@Transactional(readOnly = true)
	public List<Image> imageStroy(int principalId){
		List<Image> images = imageRepository.mStroy(principalId);
		System.out.println("imageStroy 함수:"+images);
		return images;
	}
  • 포스트맨으로 테스트 (언제든지 로그인 -> 테스트)

굿!

 

 

02 포토리스트 뷰 렌더링하기

  • story.jsp의 맨 하단에 story.js가 담겨 있다. 이를 세팅하자

1) 스토리 내용을 로드 먼저

// (1) 스토리 로드하기
function storyLoad() {
	$.ajax({
		url:`/api/image`,
		dataType:"json"
	}).done(res=>{
		console.log(res);
	}).fail(error=>{
		console.log("오류:",error);
	});
}

storyLoad();

→ console.log 테스트를 해보면 아래와 같다

 

2) 로드한 내용을 함수 및 변수처리해서 담기

  • story.jsp의 스토리 리스트 부분을 자르기해서 js 함수 'getStroyItem()'에 담기
더보기

function getStoryItem() {
let item =`<!--전체 리스트 아이템-->
<div class="story-list__item">
<div class="sl__item__header">
<div>
<img class="profile-image" src="#"
onerror="this.src='/images/person.jpeg'" />
</div>
<div>TherePrograming</div>
</div>

<div class="sl__item__img">
<img src="/images/home.jpg" />
</div>

<div class="sl__item__contents">
<div class="sl__item__contents__icon">

<button>
<i class="fas fa-heart active" id="storyLikeIcon-1" onclick="toggleLike()"></i>
</button>
</div>

<span class="like"><b id="storyLikeCount-1">3 </b>likes</span>

<div class="sl__item__contents__content">
<p>등산하는 것이 너무 재밌네요</p>
</div>

<div id="storyCommentList-1">

<div class="sl__item__contents__comment" id="storyCommentItem-1"">
<p>
<b>Lovely :</b> 부럽습니다.
</p>

<button>
<i class="fas fa-times"></i>
</button>

</div>

</div>

<div class="sl__item__input">
<input type="text" placeholder="댓글 달기..." id="storyCommentInput-1" />
<button type="button" onClick="addComment()">게시</button>
</div>

</div>
</div>`;
return item;
}

3) return 된 item을 로드 함수에 뿌리기

function storyLoad() {
	$.ajax({
		url:`/api/image`,
		dataType:"json"
	}).done(res=>{
		console.log(res);
		res.data.forEach((image)=>{
			let storyItem = getStoryItem(image);
			$("#storyList").append(storyItem)
		});
	}).fail(error=>{
		console.log("오류:",error);
	});
}

storyLoad();

→ 여기까지하면 DB에 저장된 이미지 개수만큼 뿌려진다.

 

스크롤이 생겼다! 개수만큼 다 뿌려졌다.

→ 여기서 이제 유저가 올린 이미지 + 정보를 정확하게 불러와야한다. 'getStroyItem()'가 가진 JSP 내용을 수정하여 정확한 정보를 받아오도록 수정

더보기

function getStoryItem(image) {
let item =`<!--전체 리스트 아이템-->
<div class="story-list__item">
<div class="sl__item__header">
<div>
<img class="profile-image" src="/upload/${image.user.profileImageUrl}"
onerror="this.src='/images/person.jpeg'" />
</div>
<div>${image.user.username}</div>
</div>

<div class="sl__item__img">
<img src="/upload/${image.postImageUrl}" />
</div>

<div class="sl__item__contents">
<div class="sl__item__contents__icon">

<button>
<i class="fas fa-heart active" id="storyLikeIcon-1" onclick="toggleLike()"></i>
</button>
</div>

<span class="like"><b id="storyLikeCount-1">3 </b>likes</span>

<div class="sl__item__contents__content">
<p>${image.caption}</p>
</div>

굿

 

 

03 페이징 처리

  • ImageApiController에 페이징 세팅
@RequiredArgsConstructor
@RestController
public class ImageApiController {
	
	private final ImageService imageService;
	
	@GetMapping("/api/image")
	public ResponseEntity<?> imageStroy(@AuthenticationPrincipal PrincipalDetails principalDetails,
			@PageableDefault(size=3) Pageable pagealbe){
		Page<Image> images = imageService.imageStroy(principalDetails.getUser().getId(),pagealbe);
		return new ResponseEntity<>(new CMRespDto<>(1, "성공", images), HttpStatus.OK);
	}
}
  1. @PageableDefault - 이 어노테이션으로 페이징시에 게시물 개수 지정, 정렬 기준, 정렬 방법 지정 가능
  2. Pageable - 페이징과 관련된 파라미터들(page,size,sort)를 사용 할 수 있게 만들어 준다.
  3. Page는 리스트 형식으로 오기때문에 List 대신 Page 객체를 받아 준다.

 

  • Service에서 페이징 받기
	//이미지 리스트 뿌리기 
	@Transactional(readOnly = true)
	public Page<Image> imageStroy(int principalId, Pageable pageable){
		Page<Image> images = imageRepository.mStroy(principalId, pageable);
//		System.out.println("imageStroy 함수:"+images);
		return images;
	}
  • Repository에서 똑같이 받기
public interface ImageRepository extends JpaRepository<Image, Integer>{

  @Query(value="SELECT * FROM image WHERE userId IN 
  (SELECT toUserId FROM subscribe WHERE fromUserId = :principalId) ORDER BY id DESC",
  nativeQuery = true)
  Page<Image> mStroy(int principalId, Pageable pageable);
}
  • story.js 에서 로드 함수 포이치를 data가 아닌 content로 전환
function storyLoad() {
	$.ajax({
		url:`/api/image`,
		dataType:"json"
	}).done(res=>{
		console.log(res);
		res.data.content.forEach((image)=>{
			let storyItem = getStoryItem(image);
			$("#storyList").append(storyItem)
		});
	}).fail(error=>{
		console.log("오류:",error);
	});
}

storyLoad();

 

 

04 스크롤 페이징

  • story.js에서 스크롤 페이징 함수 정의가 필요한데 그전에 스크롤의 높이, 문서의 높이, 윈도우의 높이가 필요하다
$(window).scroll(() => {
	console.log("윈도우 scrollTop ", $(window).scrollTop());
	console.log("문서의 높이", $(document).height());
	console.log("윈도우 높이", $(window).height());
});

  • 높이들은 고정인 반면에 scrollTop은 계속해서 변하는걸 볼 수 있다. 즉 공식 (문서의 높이 - 윈도우 높이) = ScrollTop 을 찾으면 된다. 
// (2) 스토리 스크롤 페이징하기
$(window).scroll(() => {
	let checkNum = $(window).scrollTop() - ($(document).height() - $(window).height());
	console.log(checkNum);
});

  • 맨 밑의 값으로 if문을 걸면 되지만 페이지 시작 부분을 지정 후 로드시에 페이지가 증가 시켜 나오도록 해야한다
let page = 0;

function storyLoad() {
	$.ajax({
		url:`/api/image?page=${page}`,
$(window).scroll(() => {
/*	console.log("윈도우 scrollTop ", $(window).scrollTop());
	console.log("문서의 높이", $(document).height());
	console.log("윈도우 높이", $(window).height());*/
	
	let checkNum = $(window).scrollTop() - ($(document).height() - $(window).height());
	console.log(checkNum);
	
	if(checkNum < 1 && checkNum > -1){
		page++;
		storyLoad();
	}
});

굿 잘 받아온다!