커뮤니티 프로젝트

댓글 기능 개발 (댓글 저장 부분)

쭈녁 2022. 12. 2. 18:41

Comment 엔티티

@Data
@Entity
public class Comment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotEmpty
    private String contents;

    @ManyToOne
    @JoinColumn(name="postId")
    private Post post;

    @ManyToOne
    @JoinColumn(name = "memberId")
    @JsonIgnoreProperties({"post"})
    private Member member;

    @CreationTimestamp
    private Timestamp created;

    @UpdateTimestamp
    private Timestamp updated;

}

댓글 내용에 해당하는 contents는 Null이 될수 없도록 @NotEmpty 에노테이션을 썼다

댓글의 경우 작성자와 해당 댓글이 쓰여진 게시물의 정보가 포함되어야 함으로 Post객체와 Member객체를 인스턴스로 넣었고 이 둘을 @ManyToOne을 통해  댓글과 게시글, 유저 객체의 관계를 多:1 관계로 묶었다. 게시물과 유저가 여러 댓글을 가지고 있을 수 있기 때문이다. Java는 객체지향언어이다. Java의 특장점은 서로 다른 객체를 ID로 관계성을 부여할 때 객체 자체(게시글, 유저)를 클래스에 넣을수 있다.

 

private Post post;

private Member member;

위 2줄은 게시글과 유저 객체를 그대로 참조하도록 작성된 부분이다.

 

DB에 저장될 때 어떠한 값을 해당 컬럼에 저장할지를 지정하는

@JoinColumn(name = "컬럼 명")  를 사용하여 DB에는 Post, Member의 ID 가 저장되고 Java에서는 객체로 저장된다.

 

Post 엔티티

@Entity
@Data
public class Post {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "memberId")
    private Member member;

    @OneToMany(mappedBy = "post", fetch = FetchType.EAGER)
    @OrderBy("id desc")
    private List<Comment> comment;



    @ColumnDefault("0")
    private Integer likeCount;


    @NotBlank(message = "제목을 입력해주세요")
    private String title;
    @Lob
    @NotBlank(message = "내용을 입력해주세요")
    private String contents;

    @ColumnDefault("0")
    private Integer view;//조회수

    @CreationTimestamp
    private Timestamp created;

    @UpdateTimestamp
    private Timestamp updated;

    @ColumnDefault("FREE")
    private String categoryType;
    
}

@OneToMany를 통해 1:多 (게시물:댓글) 게시물 하나에 여러 댓글이 있을수 있기때문에 이와 같이 작성하였다.

Post와 Comment는 양방향 연관관계이고 mappedby로 post를 지정하여 comment를 연관관계 주인으로 지정해준다.

( 연관관계의 주인만 데이터베이스 연관관계와 매핑되고 외래 키를 관리(등록, 수정, 삭제)할 수 있다. 반대 쪽은 읽기만 할 수 있다.)

Post.html

<form th:action="@{|/post/${post.id}/comment|}"  th:object="${comment}" method="post">

        <hr class="my-4">
            <div class="card">
                <div class="card-body"><textarea type="text" class="form-control" rows="1" th:field="*{contents}"th:errorclass="field-error"></textarea></divclass>
                    <div class="field-error" th:errors="*{contents}" />
                    <div class="card-footer">
                        <button class="btn btn-primary" type="sumit">댓글 작성</button>
                    </div>
                </div>
            </div>
        <br/>
        </form>
        <div class="card">
            <div class="card-header">
                댓글 리스트
                <ul id="comment--box" class="list-group">
                    <tr th:each="comment : ${post.comment}">
                        <li id="comments--1" class="list-group-item d-flex justify-content-between">
                            <div th:text="${comment.contents}"></div>
                            <div class="d-flex">
                                <div class="font-italic" th:text="${comment.member.nickName}">
                                </div>
                                <button class="badge">삭제</button>
                            </div>
                        </li>
                    </tr>
                </ul>
            </div>

post.html 하단에 댓글을 표시하도록 만들었다. Controller단에서 댓글을 읽어오는것이 아닌 Post객체의 List<Comment>에서 하나씩 찾아오도록 반복문으로 작성하였다.

<tr th:each="comment : $post.comment">를 통해 반복문을 수행하는 대상을 post.comment로 지정해주고 이를 변수 comment로 지정했다. 이 comment 변수를 <div th:text = ${comment.contents}">를 통해 comment객체에서 contents(댓글 내용)을 하나씩 택스트로 가져오도록 작성하였다.

<form th:action="@{|/post/${post.id}/comment|}"  th:object="${comment}" method="post"> 에서 th:acthion에 경로를 설정하여 해당 URL로 오는 PostMapping을 처리 할 수 있도록 하였다.

 

CommentRepository


public interface SpringDataJpaCommentRepository extends JpaRepository<Comment, Long> {

}

 저장, 변경, 삭제만 사용할 예정이라서 JPA리포지토리를 상속받아 사용하였다. 

 

CommentService

@Service
@RequiredArgsConstructor
public class CommentService {

    private final SpringDataJpaCommentRepository repository;

    public Comment save(Comment comment, Member member, Post post) {
        comment.setMember(member);
        comment.setPost(post);
        return repository.save(comment);
    }

    public void update(Long commentId, CommentUpdateDto updateParam) {
        Comment comment = repository.findById(commentId).orElseThrow();
        comment.setContents(updateParam.getContents());
    }


}

Service단에서는 유저정도와 게시물을 받아 먼저 comment에 세팅 한 후 repository에 일괄 저장하도록 작성하였다.

 

PostController(댓글 부분)


    @Transactional
    @PostMapping("/post/{postId}/comment")
    public String comment(Comment comment, @AuthenticationPrincipal PrincipalDetail principalDetail, @PathVariable Long postId) {

        Post post = postService.findById(postId).orElseThrow();
        Member member = principalDetail.getMember();
        commentService.save(comment, member, post);
        
        return "redirect:/post/{postId}";
    }

}

위 HTML 문서에서 action으로 해당 경로로 넘어올때 PostMapping을 통해 댓글을 저장 할 수 있도록 작성했다.

commentService.save의 경우 comment와 member, post 3가지 파라미터를 받아야 함으로 principalDetail에서 유저정보를 가져와 member에, 해당URL의 파라미터 정보를 @PathVariable로 받아 해당 postId를 통해 찾은 객체를 post에 넣어 댓글을 저장 하도록 작성하였다.

 

마치며

 

오늘 개발일지 부분에서 어려웠던 부분은 특정 URL에서 다른 URL 경로로 요청을 보낼 방법을 찾는 부분에서 어려움이 많았던 것 같다. 구글링을 통해 th:action 을 써서 <form> 안의 부분을 action에 표기한 URL로 요청 보낼 수 있다는 점을 배웠다.  

앞으로 댓글 수정, 삭제 부분을 개발할 예정이고, 진행 중 돌아보면서 확인된 부분은 게시물, 댓글 부분에 Validator가 적용되지 않은 부분이 있어 수정을 진행해야 할 것으로 보인다.

'커뮤니티 프로젝트' 카테고리의 다른 글

좋아요 기능 개발  (0) 2022.12.12
댓글 삭제 개발  (0) 2022.12.07
회원정보 수정  (0) 2022.11.30
JpaRepository, Querydsl, 게시물 CRUD  (1) 2022.11.24
Spring Security 회원가입 및 로그인  (0) 2022.11.23