Wanna be Brilliant Full-Stack Developer

2/17 SpringBoot 인기 페이지 & 프로필 Likes 카운트 구현 본문

Back-End/Spring Boot

2/17 SpringBoot 인기 페이지 & 프로필 Likes 카운트 구현

Flashpacker 2022. 2. 17. 14:53

 


목표

인기 페이지 구현 - 좋아요가 있는 사진들을 순서대로 출력할 예정이다! ( 좋아요가 많은순으로 )ㅍ

이런 쿼리는 만들기 쉽다! 

package com.cos.photogramstart.web;

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.cos.photogramstart.config.auth.PrincipalDetails;
import com.cos.photogramstart.handler.ex.CustomValidationException;
import com.cos.photogramstart.service.ImageService;
import com.cos.photogramstart.web.dto.image.ImageUploadDto;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Controller
public class ImageController {
	
	private final ImageService imageService;

	@GetMapping({"/", "/image/story"})
	public String story() {
		return "image/story";
	}
	
	@GetMapping("/image/popular")
	public String popular() {
		return "image/popular";
	}
	
	@GetMapping("/image/upload")
	public String upload() {
		return "image/upload";
	}
	
	
	@PostMapping("/image")
	public String imageUpload(ImageUploadDto imageUploadDto, @AuthenticationPrincipal PrincipalDetails principalDetails) {
		// 서비스 호출
		
		if(imageUploadDto.getFile().isEmpty()) {
			throw new CustomValidationException("이미지가  첨부되지 않았습니다.",null);
		}
		
		
		imageService.사진업로드(imageUploadDto, principalDetails);
		return "redirect:/user/"+principalDetails.getUser().getId();
	}
}


@GetMapping("/image/popular")
public String popular() {
return "image/popular";
} 여기서 popular로 갈때 인기페이지로 가는데 데이터를 들고가야하니 일단은 Model을 만들것이다!

 

@GetMapping("/image/popular")
public String popular(Model model) {

List<Image> images = imageService.인기사진();

model.addAttribute("images", images );

return "image/popular";
}  이렇게 작성뒤 이미지서비스에 인기사진을 구현해야한다!  근데 애 왜는 API로 안해요? 물어볼수도있지만

API는 데이터를 리턴하는 서버이다. 인기페이지를 갈떄 데이터를 리턴하는 서버를 만들필요는 없다.

Ajax를 할것이 아니기떄문에

모델에 담고 데이터를 그냥 들고가기만 하면된다.

Ajax를 사용하는 경우 API컨트롤러를 만들면된다. 

API로 구현할수도 있지만 그래서 데이터를 리턴하게 만들수도 있지만 JS를 만들어야되고 어렵게 된다! 

API 구현한다면 - 이유 - (브라우저에서 요청하는게 아니라, 안드로이드, iOS요청) 

안드로이드는 IOS에게 HTML페이지를 줄수는 없고 데이터를 줘야하기때문에 해야하지만

지금은 페이지를 리턴해도된다 페이지에 데이터를 만들고 

 

 

우리가 만들어야하는 쿼리는  imageId likeCOunt

                                          2            1

                                          3            2

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

 

이미지컨트롤러에서 인기사진을 요청하면 m.Popular가 image리파지토리를 리턴하니까 

images를 images 두개를 담고 popular페이지로 간다

package com.cos.photogramstart.domain.image;

import java.util.List;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

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> mStory(int principalId, Pageable pageable);
	
	@Query(value = "SELECT i.* FROM image i INNER JOIN (SELECT imageId, COUNT(imageId) likeCount FROM likes GROUP BY imageId) c ON i.id = c.imageId ORDER BY likeCount DESC", nativeQuery =  true)
	List<Image> mPopular();
}

유저 프로필 페이지 좋아요 카운트 구현!

누군가 이미지에 좋아요를 누르면 자신의 프로필 페이지에서도 그 사진의 좋아요가 카운트 되는것을 구현 해보려고한다!

이 like 카운트는 이미지를 셀렉트할때 들고 있다. 

우리가 스토리페이지에서 라이크 카운트가 보이는데 이것을 그대로 사용하면된다! 

 

유저를 셀렉트할떄 DTO 안에 유저가 있으니까 그떄 이미지들을 셀렉트하고 있을떄 이 이미지 안에 likeCount가 있다. 

 

<a href="#" class=""> <i class="fas fa-heart"></i><span>${image.likeCount}</span>

이렇게 해도 효과가 없는 이유는 likeCount정보는 DB에 있는 정보가 아니라 DB에 끌고와봤자 없는 값이다.

우리가 likeCount를 어떻게 만들었냐면 이미지API컨트롤러에서 이미지 스토리를 가져올떄 강제로 우리가 사이즈를 재서 가져왔는데 .

	images.forEach((image)-> {
			
			image.setLikeCount(image.getLikes().size());
			
			image.getLikes().forEach((like) -> {
				if(like.getUser().getId() == principalId) { // 해당 이미지에 좋아요한 사람들을 찾아서 현재로그인 한사람이 좋아요 한것인지 비교
					image.setLikeState(true);
				}
			});
			
		});

마찬가지로 우리가 이걸 또 만들어내면된다. 

유저서비스로 가서 회원프로필로 갈때에 DTO에 하나씩 집어넣었다. 

이 DTO에는 값을 넣을수가 없다. 왜냐하면 이미지가 들고 있는 likeCount니까

만약에 DTO에다가 private int likeCount라고 하면 LikeCount를 한건밖에 못받는다 애는 이미지마다 다 가지고 있어야하는것이기 때문이다.

어떻게 하는것이 가장 좋은 로직이 나오냐면 외부에서 연산하는것이 더 빠르다

 

package com.cos.photogramstart.service;

import java.util.function.Supplier;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.cos.photogramstart.domain.subscribe.SubscribeRepository;
import com.cos.photogramstart.domain.user.User;
import com.cos.photogramstart.domain.user.UserRepository;
import com.cos.photogramstart.handler.ex.CustomException;
import com.cos.photogramstart.handler.ex.CustomValidationApiException;
import com.cos.photogramstart.web.dto.user.UserProfileDto;

import lombok.RequiredArgsConstructor;


@RequiredArgsConstructor
@Service
public class UserService {

	private final UserRepository userRepository;
	private final SubscribeRepository subscribeRepository;
	private final BCryptPasswordEncoder bCryptPasswordEncoder;
	
	@Transactional(readOnly = true)
	public UserProfileDto 회원프로필(int pageUserId, int principalId) {
		UserProfileDto dto = new UserProfileDto();
		
		//SELECT * FROM image WHERE userId = :userId;
		User userEntity = userRepository.findById(pageUserId).orElseThrow(()-> {
			throw new CustomException("해당 프로필 페이지는 없는 페이지입니다.");
		});
		
		dto.setUser(userEntity);
		dto.setPageOwnerState(pageUserId == principalId);
		dto.setImageCount(userEntity.getImages().size());
		
		int subscribeState = subscribeRepository.mSubscribeState(principalId, pageUserId);
		int subscribeCount = subscribeRepository.mSubscribeCount(pageUserId);
		
		dto.setSubscribeState(subscribeState == 1);
		dto.setSubscribeCount(subscribeCount);
		
		//좋아요 카운트 추가하기
		userEntity.getImages().forEach((image)->{
			image.setLikeCount(image.getLikes().size());
		});
		
		return dto;
	}
	
	
	
	@Transactional
	public User 회원수정(int id, User user ) {
		//1. 영속화
		//1. 무조건 찾았다. 걱정마 get() 2. 못찾았어 익섹션 발통시킬때 orElseThrow()
		User userEntity = userRepository.findById(id).orElseThrow(() -> {return new CustomValidationApiException("찾을 수 없는 id입니다."); });
			
	
	
		//2. 영속화된 오브젝트를 수정 - 수정이 완료되면 더티체킹 ( 업데이트 완료) 
		userEntity.setName(user.getName());
		
		String rawPassword = user.getPassword();
		String encPassword = bCryptPasswordEncoder.encode(rawPassword);
		
		userEntity.setPassword(encPassword);
		userEntity.setBio(user.getBio());
		userEntity.setWebsite(user.getWebsite());
		userEntity.setPhone(user.getPhone());
		userEntity.setGender(user.getGender());
		return userEntity;
		
	}//더티체킹이 일어나서 업데이트가 완료됨. 
}