728x90
320x100
⚡️ 좋아요 구현
📌 Likes 엔티티 구현
package yerong.InstagramCloneCoding.domain.likes;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.persistence.*;
import lombok.*;
import yerong.InstagramCloneCoding.domain.BaseTimeEntity;
import yerong.InstagramCloneCoding.domain.image.Image;
import yerong.InstagramCloneCoding.domain.user.User;
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Builder
@Entity
@Table(
uniqueConstraints = {
@UniqueConstraint(
name = "likes_uk",
columnNames = {"image_id", "user_id"}
)
}
)
public class Likes extends BaseTimeEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "likes_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "image_id")
private Image image;
//User를 가져오면 안에 있는 image도 가져오게 돼서 image의 정보는 가져오지 않게 해야 함
@JsonIgnoreProperties("images")
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "user_id")
private User user;
}
- Like가 MariaDB, MySQL에서 키워드로 쓰이기 때문에 테이블이 안 만들어진다. ➡️ likes
- 하나의 이미지엔 여러 좋아요가 달릴 수 있음
- 하나의 유저는 여러 게시글의 좋아요를 누를 수 있음
- 좋아요에서 user를 가져오면 user와 연관관계가 설정된 image도 가져오게 돼서 이 image를 가져오지 못하게 해야 함
📌 Likes Repository
package yerong.InstagramCloneCoding.repository.likes;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import yerong.InstagramCloneCoding.domain.likes.Likes;
public interface LikesRepository extends JpaRepository<Likes, Long> {
@Modifying
@Query(value = "INSERT INTO likes(image_id, user_id, createdAt) VALUES (:imageId, :principalId, now())", nativeQuery = true)
int mLikes(Long imageId, Long principalId);
@Modifying
@Query(value = "DELETE FROM likes WHERE image_id = :imageId AND user_id = :principalId", nativeQuery = true)
int mUnLikes(Long imageId, Long principalId);
}
- 좋아요 및 좋아요 취소를 nativeQuery를 통해서 구현
- 생성 시간을 못가져오므로 insert 쿼리에서 같이 날려야 한다
📌 LikeServiceImpl
package yerong.InstagramCloneCoding.service.impl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import yerong.InstagramCloneCoding.domain.image.Image;
import yerong.InstagramCloneCoding.domain.user.User;
import yerong.InstagramCloneCoding.handler.exception.CustomException;
import yerong.InstagramCloneCoding.handler.exception.CustomValidationApiException;
import yerong.InstagramCloneCoding.repository.image.ImageRepository;
import yerong.InstagramCloneCoding.repository.likes.LikesRepository;
import yerong.InstagramCloneCoding.repository.user.UserRepository;
import yerong.InstagramCloneCoding.service.LikeService;
@Service
@RequiredArgsConstructor
public class LikeServiceImpl implements LikeService {
private final LikesRepository likesRepository;
@Transactional
@Override
public void like(Long imageId, Long principalId){
likesRepository.mLikes(imageId, principalId);
}
@Transactional
@Override
public void unlike(Long imageId, Long principalId){
likesRepository.mUnLikes(imageId, principalId);
}
}
📌 ImageApiController
@PostMapping("/api/image/{imageId}/likes")
public ResponseEntity<CMRespDto<?>> imageLikes(
@PathVariable Long imageId,
@AuthenticationPrincipal PrincipalDetails principalDetails
){
likeService.like(imageId, principalDetails.getUser().getId());
return new ResponseEntity<CMRespDto<?>>(new CMRespDto<>(1, "좋아요 성공", null), HttpStatus.CREATED);
}
@DeleteMapping("/api/image/{imageId}/likes")
public ResponseEntity<CMRespDto<?>> imageUnLikes(
@PathVariable Long imageId,
@AuthenticationPrincipal PrincipalDetails principalDetails
){
likeService.unlike(imageId, principalDetails.getUser().getId());
return new ResponseEntity<CMRespDto<?>>(new CMRespDto<>(1, "좋아요 취소 성공", null), HttpStatus.OK);
}
- 이 사진의 id와 로그인된 user의 id가 필요함
⚡️ 인기 페이지 구현
- 좋아요가 많은 순서대로 출력
📌 PopularImageDto
package yerong.InstagramCloneCoding.web.dto.image;
import lombok.*;
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class PopularImageDto {
private Long id;
private String caption;
private String postImageUrl;
private Long userId;
private String username;
private String profileImageUrl;
}
📌 ImageServiceImpl
@Override
public List<PopularImageDto> popularImage(){
List<Image> images = imageRepository.mPopular();
return images.stream().map(image -> {
return PopularImageDto.builder()
.id(image.getId())
.caption(image.getCaption())
.postImageUrl(image.getPostImageUrl())
.userId(image.getUser().getId())
.username(image.getUser().getUsername())
.profileImageUrl(image.getUser().getProfileImageUrl())
.build();
}).collect(Collectors.toList());
}
📌 ImageController
@GetMapping("/image/popular")
public String popular(
@AuthenticationPrincipal PrincipalDetails principalDetails,
Model model
){
List<PopularImageDto> popularImageDtos = imageService.popularImage();
model.addAttribute("popularDtos", popularImageDtos);
model.addAttribute("principal", principalDetails.getUser());
return "views/image/popular";
}
⚡️ User 프로필 페이지 좋아요 구현
➡️ 이렇게 프로필에서 사진에 마우스를 뒀을 때 보이는 좋아요 개수 구현
📌 UserServiceImpl
@Override
@Transactional
public UserProfileDto profile(Long pageUserId, Long pageOwnerId){
UserProfileDto dto = new UserProfileDto();
User pageUser = userRepository.findById(pageUserId).orElseThrow(() -> new CustomException("해당 프로필 페이지는 없는 페이지입니다."));
dto.setUser(pageUser);
dto.setImageCount(pageUser.getImages().size());
dto.setPageOwnerState(pageOwnerId.equals(pageUserId));
int state = subscribeRepository.mSubscribeState(pageOwnerId, pageUserId);
int count = subscribeRepository.mSubscribeCount(pageUserId);
dto.setSubscribeState(state==1);
dto.setSubscribeCount(count);
userEntity.getImages().forEach((image)->{
image.setLikeCount(image.getLikes().size()));
});
return dto;
}
- 이런 식으로 for문으로 image를 찾아서 해당 좋아요수를 하나하나 구하는 방법
⚡️ 프로필 사진 등록
➡️ 로그인된 회원만 프로필 사진 변경이 되어야 한다.
📌 UserApiController
@PutMapping("/api/user/{principalId}/profileImageUrl")
public ResponseEntity<CMRespDto<?>> changeProfileImage(
@PathVariable Long principalId,
MultipartFile profileImageFile,
@AuthenticationPrincipal PrincipalDetails principalDetails
){
User user = userService.changeProfileImage(principalId, profileImageFile);
principalDetails.setUser(user);
return new ResponseEntity<CMRespDto<?>>(new CMRespDto<>(1, "프로필 사진 변경 성공", null), HttpStatus.OK);
}
📌 UserServiceImpl
@Value("${file.path}")
private String uploadFolder;
@Override
@Transactional
public User changeProfileImage(Long principalId, MultipartFile profileImageFile){
UUID uuid = UUID.randomUUID();
String imageFileName = uuid + "_" + profileImageFile.getOriginalFilename();
Path imageFilePath = Paths.get(uploadFolder+imageFileName);
try{
Files.write(imageFilePath, profileImageFile.getBytes());
}
catch (Exception e){
e.printStackTrace();
}
User user = userRepository.findById(principalId).orElseThrow(() -> new CustomValidationApiException("찾을 수 없는 Id 입니다."));
user.setProfileImageUrl(imageFileName);
return user;
}
- 게시글을 등록할 때와 똑같은 방법으로 imageUrl을 등록해준다.
📌 User
public void setProfileImageUrl(String profileImageUrl){
this.profileImageUrl = profileImageUrl;
}
- User 엔티티에 profileImageUrl을 설정해주는 코드를 추가해준다
끄읏!
PREV
NEXT
728x90
반응형
'프로젝트 > 인스타그램 클론 코딩' 카테고리의 다른 글
[인스타그램 클론 코딩][Spring boot] 12. AOP 처리 (0) | 2024.03.01 |
---|---|
[인스타그램 클론 코딩][Spring boot] 11. 댓글 구현 (0) | 2024.03.01 |
[인스타그램 클론 코딩][Spring boot] 9. 스토리(메인) 페이지 (0) | 2024.02.22 |
[인스타그램 클론 코딩][Spring boot] 8. 구독 정보 뷰 렌더링 (0) | 2024.02.21 |
[인스타그램 클론 코딩][Spring boot] 7. 프로필 페이지 (2) | 2024.02.20 |