Super Kawaii Cute Cat Kaoani [인스타그램 클론 코딩][Spring boot] 11. 댓글 구현

[인스타그램 클론 코딩][Spring boot] 11. 댓글 구현

2024. 3. 1. 14:18
728x90
SMALL

📌 Comment Entity 구현

package yerong.InstagramCloneCoding.domain.comment;

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
public class Comment extends BaseTimeEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "comment_id")
    private Long id;

    @Column(length = 100, nullable = false)
    private String content;

    @JsonIgnoreProperties("images")
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "image_id")
    private Image image;
}
  • User와 Image와 연관관계 설정해줌

 

📌 CommentRepository 구현

package yerong.InstagramCloneCoding.repository.comment;

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.comment.Comment;

public interface CommentRepository extends JpaRepository<Comment, Long> {
}

 

 

📌 Image 와 연관관계 설정

package yerong.InstagramCloneCoding.domain.image;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.persistence.*;
import lombok.*;
import yerong.InstagramCloneCoding.domain.BaseTimeEntity;
import yerong.InstagramCloneCoding.domain.comment.Comment;
import yerong.InstagramCloneCoding.domain.likes.Likes;
import yerong.InstagramCloneCoding.domain.user.User;

import java.util.ArrayList;
import java.util.List;

@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Builder
@Entity
public class Image extends BaseTimeEntity {


    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "image_id")
    private Long id;

    private String caption;
    private String postImageUrl;

    @JsonIgnoreProperties({"images"}) //user에 있는 images는 무시
    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

    //좋아요
    @JsonIgnoreProperties({"image"})
    @OneToMany(mappedBy = "image")
    private List<Likes> likes = new ArrayList<>();

    @Transient
    private int likeCount;

    @OrderBy("id DESC ")
    @JsonIgnoreProperties({"image"})
    @OneToMany(mappedBy = "image")
    private List<Comment> commentList;

    public void setLikeCount(int count) {
        this.likeCount = count;
    }


}
  • comment와 연관관계 설정
    • comment에 들어가면 image가 또 있으므로 무한참초를 막기 위해 JsonIgnoreProperties 설정을 해줌
    • OrderBy("id DESC") 로 id 순으로 정렬

 

📌 CommentReqDto 구현

package yerong.InstagramCloneCoding.web.dto.comment;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.*;

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CommentReqDto {
    private Long imageId;

    @NotBlank
    private String content;
}
  • REQUEST DTO

 

📌 CommentResDto 구현

package yerong.InstagramCloneCoding.web.dto.comment;


import lombok.*;

@Builder
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class CommentResDto {
    private Long id;
    private String content;
    private String username;
    private Long userId;
}
  • RESPONSE DTO

📌 CommentApiController 구현

package yerong.InstagramCloneCoding.web.api;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import yerong.InstagramCloneCoding.config.auth.PrincipalDetails;
import yerong.InstagramCloneCoding.handler.exception.CustomValidationException;
import yerong.InstagramCloneCoding.service.CommentService;
import yerong.InstagramCloneCoding.web.dto.CMRespDto;
import yerong.InstagramCloneCoding.web.dto.comment.CommentReqDto;
import yerong.InstagramCloneCoding.web.dto.comment.CommentResDto;

import java.util.HashMap;
import java.util.Map;

@RequiredArgsConstructor
@RestController
@Slf4j
public class CommentApiController {

    private final CommentService commentService;

    @PostMapping("/api/comment")
    public ResponseEntity<CMRespDto<?>> commentWrite(
            @Valid @RequestBody CommentReqDto commentDto,
            BindingResult bindingResult,
            @AuthenticationPrincipal PrincipalDetails principalDetails){
        if(bindingResult.hasErrors()){
            Map<String, String> errorMap = new HashMap<>();

            for(FieldError error : bindingResult.getFieldErrors()){
                errorMap.put(error.getField(), error.getDefaultMessage());
                log.info(error.getDefaultMessage());
            }
            throw new CustomValidationException("유효성 검사 실패", errorMap);
        }
        else {
            CommentResDto commentResDto = commentService.commentWrite(commentDto, principalDetails.getUser().getId());
            return new ResponseEntity<CMRespDto<?>>(new CMRespDto<>(1, "댓글 작성 성공", commentResDto), HttpStatus.CREATED);

        }
    }
    @DeleteMapping("/api/comment/{id}")
    public ResponseEntity<CMRespDto<?>> commentDelete(
            @PathVariable Long id
    ){
        log.info("=====id : " + id + "-======");
        commentService.commentDelete(id);
        return new ResponseEntity<CMRespDto<?>>(new CMRespDto<>(1, "댓글 삭제 성공", null), HttpStatus.OK);

    }
}
  • 댓글을 쓰고 지우는 Controller 설정
  • @Valid를 통해 DTO에 설정해둔 유효성 검사를 해준다.

 

📌 ImageDto에 CommentList 추가

package yerong.InstagramCloneCoding.web.dto.image;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.*;
import yerong.InstagramCloneCoding.domain.comment.Comment;
import yerong.InstagramCloneCoding.domain.user.User;
import yerong.InstagramCloneCoding.web.dto.comment.CommentResDto;

import java.util.List;

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class ImageDto {

    private Long id;
    private String caption;
    private String postImageUrl;
    private Long userId;
    private String username;
    private String profileImageUrl;
    private boolean likeState;
    private int likeCount;

    private List<CommentResDto> commentList;
}
  • ImageDto에도 CommentList 추가해줌

 

📌 CommentService 구현

package yerong.InstagramCloneCoding.service.impl;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import yerong.InstagramCloneCoding.domain.comment.Comment;
import yerong.InstagramCloneCoding.domain.image.Image;
import yerong.InstagramCloneCoding.domain.user.User;
import yerong.InstagramCloneCoding.handler.exception.CustomApiException;
import yerong.InstagramCloneCoding.handler.exception.CustomException;
import yerong.InstagramCloneCoding.repository.comment.CommentRepository;
import yerong.InstagramCloneCoding.repository.image.ImageRepository;
import yerong.InstagramCloneCoding.repository.user.UserRepository;
import yerong.InstagramCloneCoding.service.CommentService;
import yerong.InstagramCloneCoding.web.dto.comment.CommentReqDto;
import yerong.InstagramCloneCoding.web.dto.comment.CommentResDto;

@Service
@RequiredArgsConstructor
@Slf4j
public class CommentServiceImpl implements CommentService {
    private final CommentRepository commentRepository;
    private final UserRepository userRepository;
    private final ImageRepository imageRepository;

    @Transactional
    @Override
    public CommentResDto commentWrite(CommentReqDto requestDto, Long principalid){
        User user = userRepository.findById(principalid).orElseThrow(() -> new CustomException("해당 User를 찾을 수 없습니다."));
        Image image = imageRepository.findById(requestDto.getImageId()).orElseThrow(() -> new CustomException("해당 이미지를 찾을 수 없습니다."));

        Comment comment = Comment.builder()
                .content(requestDto.getContent())
                .user(user)
                .image(image)
                .build();
        commentRepository.save(comment);

        return CommentResDto.builder()
                .id(comment.getId())
                .content(comment.getContent())
                .username(comment.getUser().getUsername())
                .build();
    }
    @Transactional
    @Override
    public void commentDelete(Long commentId){
        try {

            Comment comment = commentRepository.findById(commentId).orElseThrow(() -> new CustomException("해당 댓글을 찾을 수 없습니다."));

            commentRepository.delete(comment);

        }catch (Exception e){
            throw new CustomApiException(e.getMessage());
        }
    }
}

 

 

📌 ImageService 에 Comment 저장하는 코드 추가

 @Transactional(readOnly = true)
    @Override
    public Page<ImageDto> imageStory(Long principalId, Pageable pageable){
        Page<Image> images = imageRepository.mStory(principalId, pageable);
        return images.map(image -> {
            boolean likeState = false;
            for(Likes like : image.getLikes()) {
                if(like.getUser().getId() == principalId) {
                    likeState = true;
                    break;
                }
            }
            List<CommentResDto> commentResDtoList = image.getCommentList().stream().map(comment -> {
                return CommentResDto.builder()
                        .id(comment.getId())
                        .content(comment.getContent())
                        .username(comment.getUser().getUsername())
                        .userId(comment.getUser().getId())
                        .build();
            }).collect(Collectors.toList());
            return ImageDto.builder()
                    .id(image.getId())
                    .caption(image.getCaption())
                    .postImageUrl(image.getPostImageUrl())
                    .userId(image.getUser().getId())
                    .username(image.getUser().getUsername())
                    .profileImageUrl(image.getUser().getProfileImageUrl())
                    .likeState(likeState)
                    .likeCount(image.getLikes().size())
                    .commentList(commentResDtoList)
                    .build();
        });
    }

 

 

댓글 구현까지 끝!!

 

 


PREV

 

[인스타그램 클론 코딩][Spring boot] 10. 좋아요 및 인기 페이지 구현

⚡️ 좋아요 구현 📌 Likes 엔티티 구현 package yerong.InstagramCloneCoding.domain.likes; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import jakarta.persistence.*; import lombok.*; import yerong.InstagramCloneCoding.domain.Bas

nyeroni.tistory.com

NEXT

 

[인스타그램 클론 코딩][Spring boot] 12. AOP 처리

📌 유효성 검사 자동화 AOP : Aspect Orientied Programing - 관점 지향 프로그래밍 관점 지향 프로그래밍 + 객체 지향 프로그래밍 함께 사용할 것 ✅ 로그인 전처리 : 유효성 검사, 보안처리 username, password

nyeroni.tistory.com

728x90
SMALL

 

728x90
LIST

BELATED ARTICLES

more