드디어 댓글 기능을 구현했습니다. 어제는 따로 조회 까지만 했고 오늘 오전에 for문 사용해서 게시글 목록에 댓글까지 전체 조회 하는 기능을 구현했습니다. 이제 이번주차 심화 과제 Lv 1을 조원들과 협력해서 할 예정입니다. Lv1의 경우에는 Spring security를 사용해서 CRUD를 구현하면 됩니다. 시큐리티 부분만 구현하면 나머지는 상대적으로 간단(?)해서 빠르게 구현할 수 있으리라 예상됩니다.

자바 스프링으로 게시판 댓글 구현하기

게시판 댓글 기능 구현

@CommentController

package com.example.post.controller;

import com.example.post.dto.CommentRequestDto;
import com.example.post.dto.CommentResponseDto;
import com.example.post.dto.ResponseDto;
import com.example.post.service.CommentService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

@RequestMapping("/api/comment/")
@RequiredArgsConstructor
@RestController
public class CommentController {

    private final CommentService commentService;

    @PostMapping("/{postId}")
    public CommentResponseDto createComment(@PathVariable  Long postId, @RequestBody CommentRequestDto requestDto, HttpServletRequest request){
        return commentService.createComment(postId, requestDto, request);
    }

    @PutMapping("/{commentId}")
    public ResponseDto updateComment(@PathVariable Long commentId, @RequestBody CommentRequestDto requestDto, HttpServletRequest request) {
        return commentService.updateComment(commentId, requestDto, request);
    }

    @DeleteMapping("/{commentId}")
    public ResponseDto deleteComment(@PathVariable Long commentId, HttpServletRequest request) {
        return commentService.deleteComment(commentId, request);

    }

}

 

  • 나중에 고쳐보고 싶은 점이라면 url을 /{postId}/{commentId}로 해서 더 직관적으로 만들고 싶습니다. 지금은 POSTMAN으로 테스트하고 있어서 괜찮은데 프론트가 합쳐지면 매우 헷갈릴거 같거든요.
  • 하면서 깨달은 중요한점은 @RequestBody 잊지말자!!!! 입니다. 저 어노테이션을 안달아주면 데이터가 json 형식으로 받질 못해서 에러가 납니다.

 

@CommentService

package com.example.post.service;

import com.example.post.dto.CommentRequestDto;
import com.example.post.dto.CommentResponseDto;
import com.example.post.dto.ResponseDto;
import com.example.post.entity.Comment;
import com.example.post.entity.Post;
import com.example.post.entity.User;
import com.example.post.jwt.JwtUtil;
import com.example.post.repository.CommentRepository;
import com.example.post.repository.PostRepository;
import com.example.post.repository.UserRepository;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class CommentService {

    private final JwtUtil jwtUtil;
    private final UserRepository userRepository;
    private final PostRepository postRepository;
    private final CommentRepository commentRepository;

    public CommentResponseDto createComment(Long postId, CommentRequestDto requestDto, HttpServletRequest request) {
        //토큰 가져오기
        String token = jwtUtil.resolveToken(request);
        Claims claims;

        if (token != null) {
            if (jwtUtil.validateToken(token)) {
                claims = jwtUtil.getUserInfoFromToken(token);
            } else {
                throw new IllegalArgumentException("Token Error");
            }

            //토큰의 사용자 정보를 사용하여 DB 조회
            User user = userRepository.findByUsername(claims.getSubject()).orElseThrow(
                    () -> new IllegalArgumentException("사용자가 존재하지 않습니다.")
            );

            //게시글 조회
            Post post = postRepository.findById(postId).orElseThrow(
                    () -> new NullPointerException("게시글이 존재하지 않습니다.")
            );

            Comment comment = new Comment(user.getUsername(), requestDto.getComment(), post.getId());
            commentRepository.save(comment);

            return new CommentResponseDto(comment);
        } else{
            return null;
        }
    }



    public ResponseDto updateComment(Long commentId, CommentRequestDto requestDto, HttpServletRequest request) {
            //토큰 가져오기
            String token = jwtUtil.resolveToken(request);
            Claims claims;

            if(token != null){
                if(jwtUtil.validateToken(token)) {
                    claims = jwtUtil.getUserInfoFromToken(token);
                }else {
                    throw new IllegalArgumentException("Token Error");
                }

                //토큰의 사용자 정보를 사용하여 DB 조회
                User user = userRepository.findByUsername(claims.getSubject()).orElseThrow(
                        () -> new IllegalArgumentException("사용자가 존재하지 않습니다.")
                );

                Comment comment = commentRepository.findById(commentId).orElseThrow(
                        () -> new NullPointerException("댓글이 존재하지 않습니다.")
                );

                comment.update(requestDto);
                return new ResponseDto(HttpStatus.OK.value(),"댓글 수정 성공");
        }
            else{
                return null;
            }
    }

    public ResponseDto deleteComment(Long commentId, HttpServletRequest request) {
        //토큰 가져오기
        String token = jwtUtil.resolveToken(request);
        Claims claims;

        if(token != null){
            if(jwtUtil.validateToken(token)) {
                claims = jwtUtil.getUserInfoFromToken(token);
            }else {
                throw new IllegalArgumentException("Token Error");
            }

            //토큰의 사용자 정보를 사용하여 DB 조회
            User user = userRepository.findByUsername(claims.getSubject()).orElseThrow(
                    () -> new IllegalArgumentException("사용자가 존재하지 않습니다.")
            );

            Comment comment = commentRepository.findById(commentId).orElseThrow(
                    () -> new NullPointerException("댓글이 존재하지 않습니다.")
            );
            commentRepository.deleteById(commentId);
            return new ResponseDto(HttpStatus.OK.value(),"댓글 삭제 성공");

        }else{
            return null;
        }
}
}

 

  • 댓글 작성, 수정, 삭제 자체는 어렵지는 않았습니다. 
  • Dto를 결정할 때, 어떤걸 요청하고 어떤걸 보낼지에 대한 고민은 있었습니다.

 

@PostService

package com.example.post.service;

import com.example.post.dto.CommentResponseDto;
import com.example.post.dto.PostRequestDto;
import com.example.post.dto.PostResponseDto;
import com.example.post.dto.ResponseDto;
import com.example.post.entity.Comment;
import com.example.post.entity.Post;
import com.example.post.entity.User;
import com.example.post.entity.UserRoleEnum;
import com.example.post.jwt.JwtUtil;
import com.example.post.repository.CommentRepository;
import com.example.post.repository.PostRepository;
import com.example.post.repository.UserRepository;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Service
@RequiredArgsConstructor
public class PostService {

    private final JwtUtil jwtUtil;
    private final PostRepository postRepository;
    private final UserRepository userRepository;

    private final CommentRepository commentRepository;

    //게시글 작성
    @Transactional
    public PostResponseDto createPost(PostRequestDto requestDto, HttpServletRequest request) {
        //토큰 가져오기
        String token = jwtUtil.resolveToken(request);
        Claims claims;

        //토큰 확인 후 게시글 작성 가능
        if(token != null){
            if(jwtUtil.validateToken(token)) {
                claims = jwtUtil.getUserInfoFromToken(token);
            }else {
                throw new IllegalArgumentException("Token Error");
            }

            //토큰의 사용자 정보를 사용하여 DB 조회
            User user = userRepository.findByUsername(claims.getSubject()).orElseThrow(
                    () -> new IllegalArgumentException("사용자가 존재하지 않습니다.")
            );

            //요청 받은 Dto로 DB에 저장할 객체 만들기
            Post post = postRepository.saveAndFlush(new Post (requestDto, user.getId(), user.getUsername()));

            return new PostResponseDto(post, null);
        } else {
            return null;
        }

    }

    //게시글 전체 조회 USER/ADMIN 상관 없음
    public List<PostResponseDto> getPostList() {
        List<Post> postList = postRepository.findAllByOrderByModifiedAtDesc();

        if(postList.isEmpty()){
            throw new NullPointerException("게시글이 존재하지 않습니다.");
        }

        List<PostResponseDto> postResponseDtoList = new ArrayList<>();
        //댓글을 각 게시물마다 조회해서 넣어주는건 에바
        for (Post post : postList) {

            List<CommentResponseDto> commentResponseDtoList = new ArrayList<>();
            Long postId = post.getId();
            List<Comment> comments = commentRepository.findAllByPostId(postId);
            for (Comment comment : comments) {
                CommentResponseDto commentResponseDto = new CommentResponseDto(comment);
                commentResponseDtoList.add(commentResponseDto);
            }

            postResponseDtoList.add(new PostResponseDto(post,commentResponseDtoList ));
        }
        return postResponseDtoList;
    }

    @Transactional(readOnly = true)
    //게시글 상세 조회 USER/ADMIN 상관 없음
    public PostResponseDto getPost(Long id) {
        Post post = postRepository.findById(id).orElseThrow(
                () -> new NullPointerException("해당 게시글은 존재하지 않습니다.")
        );

        List<CommentResponseDto> commentResponseDtoList = new ArrayList<>();

        for (int i =0; i<post.getComments().size(); i++){
            Comment comment = post.getComments().get(i);
            commentResponseDtoList.add(new CommentResponseDto(comment));
        }
        return new PostResponseDto(post, commentResponseDtoList);

    }

    @Transactional
    //게시글 수정 USER/ADMIN 권한 설정
    public PostResponseDto updatePost(Long id, PostRequestDto requestDto, HttpServletRequest request) {
        //request에서 token 가져오기
        String token = jwtUtil.resolveToken(request);
        Claims claims;

        //토큰이 있는 경우에만 게시글 수정 가능
        if(token != null){
            if(jwtUtil.validateToken(token)) {
                //토큰에서 사용자 정보 가져오기
                claims = jwtUtil.getUserInfoFromToken(token);
            } else {
                throw new IllegalArgumentException("Token Error");
            }

            //토큰에서 가져온 사용자 정보를 사용하여 DB 조회
            User user = userRepository.findByUsername(claims.getSubject()).orElseThrow(
                    () -> new IllegalArgumentException("사용자가 존재하지 않습니다.")
            );

            //게시글 있는지 조회
            Post post = postRepository.findByIdAndUserId(id, user.getId()).orElseThrow(
                    () -> new NullPointerException("해당 게시글은 존재하지 않습니다.")
            );


            //사용자 권한 가져와서 Admin 이면 다 수정, USER 이면 본인이 작성한 글만 수정
            UserRoleEnum userRoleEnum = user.getRole();

            if(userRoleEnum == UserRoleEnum.USER) {
                String Username = user.getUsername();
                if(!post.getUsername().equals(Username)){
                    throw new IllegalArgumentException("본인이 작성한 게시글만 수정 가능합니다.");
                }
            }
            post.update(requestDto);
            List<Comment> commentList = commentRepository.findAllByPostId(id);
            List<CommentResponseDto> commentResponseDtoList = new ArrayList<>();

            for (Comment comment : commentList) {
                commentResponseDtoList.add(new CommentResponseDto(comment));
            }
            return new PostResponseDto(post, commentResponseDtoList);
        } else {
            return null;
        }
    }

    @Transactional
    public ResponseDto deletePost(Long id, HttpServletRequest request) {
        //request에서 token 가져오기
        String token = jwtUtil.resolveToken(request);
        Claims claims;

        //토큰이 있는 경우에만 게시글 수정 가능
        if(token != null) {
            if (jwtUtil.validateToken(token)) {
                //토큰에서 사용자 정보 가져오기
                claims = jwtUtil.getUserInfoFromToken(token);
            } else {
                throw new IllegalArgumentException("Token Error");
            }

            //토큰에서 가져온 사용자 정보를 사용하여 DB 조회
            User user = userRepository.findByUsername(claims.getSubject()).orElseThrow(
                    () -> new IllegalArgumentException("사용자가 존재하지 않습니다.")
            );

            //게시글 있는지 조회
            Post post = postRepository.findByIdAndUserId(id, user.getId()).orElseThrow(
                    () -> new NullPointerException("해당 게시글은 존재하지 않습니다.")
            );


            //사용자 권한 가져와서 Admin 이면 다 수정, USER 이면 본인이 작성한 글만 수정
            UserRoleEnum userRoleEnum = user.getRole();

            if (userRoleEnum == UserRoleEnum.USER) {
                String Username = user.getUsername();
                if (!post.getUsername().equals(Username)) {
                    throw new IllegalArgumentException("본인이 작성한 게시글만 삭제 가능합니다.");
                }
            }
            postRepository.deleteById(id);
            return new ResponseDto(HttpStatus.OK.value(), "게시글 삭제 성공");
        } else {
            return null;
        }
    }
}

 

  • 기존에 구현한 기능에서 조회 기능에 댓글만 추가했습니다.

 

JPA 연관관계

이건 좀 고민 더 해보고 정리해보려고 합니다. 

+ Recent posts