Wanna be Brilliant Full-Stack Developer

2/21 SpringBoot 뷰 렌더링 완료하기! 본문

Back-End/Spring Boot

2/21 SpringBoot 뷰 렌더링 완료하기!

Flashpacker 2022. 2. 21. 13:22


내가 쓴 댓글을 글에 표시하는걸 해보려고하는데 유저 정보를 들고올때 유저정보를 다 들고오는것이아니라 이 유저가 등록한 사진들 images필요 없기 때문에 

@JsonIgnoreProperties를 Comment.java에 올리는데 

package com.cos.photogramstart.domain.comment;

import java.time.LocalDateTime;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.PrePersist;

import com.cos.photogramstart.domain.image.Image;
import com.cos.photogramstart.domain.likes.Likes;
import com.cos.photogramstart.domain.user.User;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
public class Comment {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY) 
	private int id;
	
	@Column(length =100, nullable = false)
	private String content;
	
	@JsonIgnoreProperties({"images"})
	@JoinColumn(name ="usrId")
	@ManyToOne(fetch = FetchType.EAGER)
	private User user;
	
	@JoinColumn(name ="imageId")
	@ManyToOne(fetch = FetchType.EAGER)
	private Image image;
	
	
	private LocalDateTime createDate;
	
	@PrePersist
	public void createDate() {
		this.createDate = LocalDateTime.now();
	}
}

이제는 데이터를 뿌리기만 하면되는데

	$.ajax({
		type:"post",
		url: "/api/comment",
		data: JSON.stringify(data),
		contentType: "application/json; charset=utf-8",
		dataType:"json"
	}).done(res=> {
		console.log("성공",res);
	}).fail(error => {
		console.log("오류",error);
		
	});

여기서 성공했으면 res에 데이터에 댓글내용이 있는데 res에 data.content, res에 data.id, res에 data.user.username

이 필요하다 

이걸 이제 샘플

// (4) 댓글쓰기
function addComment(imageId) {

   let commentInput = $(`#storyCommentInput-${imageId}`);
   let commentList = $(`#storyCommentList-${imageId}`);

   let data = {
      imageId:imageId,
      content: commentInput.val()
   }

   if (data.content === "") {
      alert("댓글을 작성해주세요!");
      return;
   }
   
   $.ajax({
      type:"post",
      url: "/api/comment",
      data:JSON.stringify(data),
      contentType:"application/json; charset=utf-8",
      dataType:"json"
      
   }).done(res=>{
      //console.log("성공",res);
      
      let comment = res.data;
      
      let content = `
        <div class="sl__item__contents__comment" id="storyCommentItem-${comment.id}""> 
          <p>
            <b>${comment.user.username} :</b>
            ${comment.content}
          </p>
          <button><i class="fas fa-times"></i></button>
        </div>
`;
   commentList.prepend(content);
   
   }).fail(error=>{
      
      console.log("오류",error);
   })


   commentInput.val("");  //인풋 필드를 깨끗하게 비워준다.
}

// (5) 댓글 삭제
function deleteComment() {

}

페이지에 올떄 같이 댓글정보도 가져와야하니까

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

또한 연관관계도 댓글이 하나이니까 OneToMany가들어가고 

포린키도 코멘트에 이미지가 포린키가 되고 포린키에 대한 자바 변수를 mappedBy에 저거주는것이다. 연관관계의 주인이 아니라는것을 이것을 또한 LazyLoading이 될것이다. ( 필요할때만 떙겨오는)

만약에 Eager이면 쓸대없이 인기페이지 갈떄도 댓글내용까지 가져가기때문에  그럴필요가 없기떄문으 Lazy로한다

 

또한 무한참조가 일어났다는것을 확인 한경우에는 

이미지를 셀렉트했을떄 Comment를 셀렉트하는데 Comment를 셀렉트할때 id와 comment를 가져오는데 User내용하고 이미지내용을 가져오는데 유저내용의  images는 이그노어가 되고 있는데 image내용의 image는 이그노어가 안되어있기 떄문에 

 

엄청 복잡하다! 

이런거떄문에 리액트쓰는게 좋다. 진짜 간단한페이지 아니고는 AJAX보다는 리액트가 좋은데 지금 이 개념을 알고있어야 리액트가 얼마나 더좋은지 알게된다! 

 


이제 댓글 삭제를 해보려고한다 

이 X 버튼을 누구나 볼수 있는것이 아니라 본인이어야한다! 

본인이어야하는데 getStoryItem을 보면 principalId를 안받아온다. 

로그인한 유저의 아이디와 코멘트의 유저 아이디와 비교를 해서 같을때 X가나오고 다를떄 x가 안나오도록 구분하도록 설정해야한다! 

그러면 principalId를 어떻게 가져올수 있을까? 간단하다! 

Header.jsp로 간다!

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>

<sec:authorize access="isAuthenticated()">	<!-- 인증된 정보에 접근 하는 방법 , 세션에 접근하는 방법 -->
	<sec:authentication property="principal" var="principal"/> <!-- principal은 세션정보에 접근이된다. 키워드  -->
</sec:authorize>

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Photogram</title>

	<!-- 제이쿼리 -->
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
	
	<!-- Style -->
	<link rel="stylesheet" href="/css/style.css">
	<link rel="stylesheet" href="/css/story.css">
	<link rel="stylesheet" href="/css/popular.css">
	<link rel="stylesheet" href="/css/profile.css">
	<link rel="stylesheet" href="/css/upload.css">
	<link rel="stylesheet" href="/css/update.css">
	<link rel="shortcut icon" href="/images/insta.svg">
	
	<!-- Fontawesome -->
	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.2/css/all.min.css" />
	<!-- Fonts -->
	<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;500;600;700&display=swap" rel="stylesheet">
</head>

<body>
	
	<header class="header">
		<div class="container">
			<a href="/" class="logo">
				<img src="/images/logo.jpg" alt="">
			</a>
			<nav class="navi">
				<ul class="navi-list">
					<li class="navi-item"><a href="/">
							<i class="fas fa-home"></i>
						</a></li>
					<li class="navi-item"><a href="/image/popular">
							<i class="far fa-compass"></i>
						</a></li>
					<li class="navi-item"><a href="/user/${principal.user.id}">
							<i class="far fa-user"></i>
						</a></li>
				</ul>
			</nav>
		</div>
	</header>

principal 저 값을 자바스크립트로 잠깐 담아두면된다! 

<body>

	<!--  principalId 담아두는 곳 -->
	<input type="hidden"  id="principalId" value="${principal.user.id }" />

이렇게 담아두면 어디에서든지 자바스크립트에서 꺼내쓸수 있다.

 

 

 // (0) 현재 로긴한 사용자 아이디 
 let principalId = $("#principalId").val();
 
 alert(principalId);

이렇게 로그인한 아이디가 나온다! 

 

이렇게하면 principalId가 꺼내쓸수 있다는것이니까 이것을 활용해 IF문을 작성할것인데 

      <div id="storyCommentList-${image.id}">`;
      
         image.comments.forEach((comment)=>{
            item +=`<div class="sl__item__contents__comment" id="storyCommentItem-${comment.id}">
            <p>
               <b>${comment.user.username} :</b> ${comment.content}
            </p>`;

			if(principalId == comment.user.id) {
				item += `<button onclick="deleteComment(${comment.id})">
                   					 <i class="fas fa-times"></i>
            					</button>`;
			}

           
            
            item += `
         </div>`;
            
         });   
      
      item+=`
      
      </div>

pricnipaldId와 Comment의 user.id가 같으면 그때만 x버튼이 나오도록 만든다! 

이제 버튼의 이벤트를 발생하기 위해서 onclick하면 함수 이름을 만들어노은 delteComment를 입력하고 삭제할떄 자신이 들고와야한는건 comment.id 를 가져오면 삭제할수 있다.

 

그래서 function에도 commentId를 받아주고 Ajax호출하면 끝난다. 

function deleteComment(commentId) {
	$.ajax({
		type: "delete",
		url: `/api/comment/${commentId}`,
		dataType: "json"
	}).done(res=>{
		console.log("성공", res);
		$(`#storyCommentItem-${commentId}`).remove();
	}).fail(error=>{
		console.log("오류", error);
	})
}

CommentApiController 에서 댓글 삭제를 호출해야한다. 넘거야하는건 id값

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

CommentService 에 댓글 삭제에가서 id 값을 받고 혹시 내부적으로 터지면 Exception으로 잡아주면되는데

	@Transactional
	public void 댓글삭제(int id) {
		try {
			commentRepository.deleteById(id);
		} catch (Exception e) {
			throw new CustomApiException(e.getMessage());
		}

이런식으로 다처리해주고 댓글 삭제가 되고 리턴할때 돌려줄것이 없으면 null 하고 댓글삭제 성공으로 변경한다! 

 

댓글이 StoryCommentitem 댓글 번호로 되어있기떄문에 개를 찾아서 리무브하면 댓글 삭제를 구현할수 있다. 

// (5) 댓글 삭제
function deleteComment(commentId) {
	$.ajax({
		type: "delete",
		url: `/api/comment/${commentId}`,
		dataType: "json"
	}).done(res=>{
		console.log("성공", res);
		$(`#storyCommentItem-${commentId}`).remove(); // 댓글삭제 
	}).fail(error=>{
		console.log("오류", error);
	})
}

근데 다른 게시글의 댓글을 삭제하려고하면 안된다? 

왜그러냐면 지금 나온글은 저 x가 없기 떄문 <button> <i class="fas fa-times"></i></button>

지금 나온글은 내가 나온글이 뻔하니까 이글이 로그인한 사용자와동일한지 체크할 필요가 없다. 

 


다음으로는 우리가 코멘트를 받을때 CommentDTO를 유효성 체크를해야한다!