[ 클라이언트 ] 

BoardView.js

import Comment from './Comment';
<Comment id={id} cookies={cookies} />

 

 

Comment.js

 

게시물 ID를 props로 받습니다.

 

상태 변수

    let [newComment, setNewComment] = useState('');  // 댓글 입력 필드의 현재 값을 저장
    let [comments, setComments] = useState([]);  // 해당 게시물과 관련된 댓글 목록을 저장

 

useEffect

컴포넌트가 마운트될 때 실행되는 useEffect 훅입니다.

댓글을 가져오는 fetchComments 함수를 호출합니다.

   useEffect(() => {
        fetchComments();
    }, []);

 

댓글을 가져오는 비동기 함수

요청 헤더에 토큰을 담아 get요청을 보내고 받아온 응답 데이터 즉, 댓글 목록을 상태변수 comments에 저장합니다.

    let fetchComments = async () => {
        try {
            let response = await axios.get(
                `http://localhost:8082/api/v1/auth/n/comment/${id}`, 
                {
                    headers: {
                        'Authorization': 'Bearer ' + cookies.accessToken
                    }
                }
            );
            setComments(response.data); // 응답 데이터(댓글 목록)를 상태 변수에 저장
        } catch (error) { 
            console.error("Error fetching comments:", error); 
        }
    };

 

댓글 제출을 처리하는 비동기 함수

제출할 댓글 데이터를 commentData 객체로 만들어주고, 댓글 내용을 포함시킵니다.

해당 id 게시글로 post 요청을 하는데 마찬가지로 요청 헤더에 토큰을 담아 보내고, 요청 본문에는 댓글 데이터인 commentData를 포함시킵니다.

받아온 응답 데이터 즉, 새로운 댓글을 기존 댓글 목록에 추가해줘야겠죠.

성공적으로 댓글이 추가되고나면 입력 필드를 초기화 시켜주는 것도 잊지 않습니다.

  const handleCommentSubmit = async () => { 
        const commentData = { // 제출할 댓글 데이터를 객체로 만들고
            comment: newComment // 새로운 댓글을 포함
        };

        try {
            const response = await axios.post( 
                `http://localhost:8082/api/v1/auth/n/saveComment/${id}`, // 해당 id 게시글로 요청
                commentData, // 요청 본문에 댓글 데이터를 포함
                {
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': 'Bearer ' + cookies.accessToken 
                    }
                }
            );

            setComments([...comments, { ...commentData, id: response.data }]); // 새로운 댓글을 기존 댓글 목록에 추가
            setNewComment(''); // 입력 필드를 초기화
            console.log('Comment saved:', response.data); 
            alert('댓글이 성공적으로 작성되었습니다.'); 
        } catch (error) { 
            console.error("Error saving comment:", error);
            alert('댓글 작성 중 오류가 발생했습니다.'); 
        }
    };

 

입력 필드가 변경될 때 호출되는 함수

    const handleChange = (e) => { 
        setNewComment(e.target.value); // 입력 필드의 값을 newComment 상태 변수에 저장
    };

 

댓글 표시

comments 배열을 순회하여 각 댓글을 리스트 항목으로 렌더링합니다.

comments.map((comment, index)

 

 


 

 

[ 서버 ] 

  1. 사용자가 특정 게시물에 댓글을 작성하고, 이를 POST 요청으로 보냅니다.
  2. 요청은 "/saveComment/{board_id}" 경로로 전달되며, 경로 변수 {board_id}는 해당 게시물의 ID를 나타냅니다.
  3. 요청 본문에는 댓글 데이터가 JSON 형식으로 포함됩니다.
  4. 컨트롤러 메서드는 경로 변수와 요청 본문을 각각 boardId와 commentDTO로 추출합니다.
  5. commentService.saveComment(boardId, commentDTO)를 호출하여 댓글을 저장합니다.
  6. 저장된 댓글 정보가 CommentDTO 형태로 클라이언트에게 응답으로 반환됩니다.

CommentEntity.java

package com.example.demo.entity;

import jakarta.persistence.*;
import lombok.*;

import java.sql.Date;

@Entity(name = "comment")
@Getter @Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class CommentEntity {
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	
	@Column(name = "user_id")
	private Long userId;

	@Column(name = "board_id")
	private Long boardId;

	@Column(name = "comment_date", nullable = false)
	private Date commentDate;
	
	@Column(name = "comment", nullable = false)
	private String comment;
	
	// UserEntity와 다대일 관계 & 조인
	@ManyToOne
	@JoinColumn(name = "user_id", insertable = false, updatable = false)  // 작성하고 추가 할 수 없다.
	private UserEntity user;
	
	// BoardEntity와 다대일 관계
	@ManyToOne
	@JoinColumn(name = "board_id", insertable = false, updatable = false)
	private BoardEntity board;

}

 

CommentDTO.java

package com.example.demo.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*;

import java.util.Date;

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

	private Long id;
	
	@JsonProperty("user_id")
	private Long userId;

	@JsonProperty("board_id")
	private Long boardId;
	
	@JsonProperty("comment_date")
	private Date commentDate;

	@JsonProperty("comment")
	private String comment;
}

 

CommentRepository. java

package com.example.demo.repository;

import com.example.demo.entity.BoardEntity;
import com.example.demo.entity.CommentEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface CommentRepository extends JpaRepository<CommentEntity, Long> {

    List<CommentEntity> findByBoard(BoardEntity board);

}

 

CommentService.java

package com.example.demo.service;

import com.example.demo.config.SecurityUtil;
import com.example.demo.dto.CommentDTO;
import com.example.demo.entity.BoardEntity;
import com.example.demo.entity.CommentEntity;
import com.example.demo.entity.UserEntity;
import com.example.demo.mapper.CommentMapper;
import com.example.demo.repository.BoardRepository;
import com.example.demo.repository.CommentRepository;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.sql.Date;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class CommentService {
	
	@Autowired
	private CommentRepository commentRepository;

	@Autowired
	private UserRepository userRepository;

	@Autowired
	private BoardRepository boardRepository;

	@Autowired
	private CommentMapper commentMapper;


	// 댓글 작성
	public CommentDTO saveComment(Long boardId, CommentDTO commentDTO) {

		// 현재 사용자의 ID를 가져옴
		String currentUserId = SecurityUtil.getCurrentUserId();

		// 로그인된 사용자가 없는 경우 예외 처리
		if (currentUserId == null || "anonymousUser".equals(currentUserId)) {
			throw new RuntimeException("로그인한 사용자가 없습니다.");
		}

		// 현재 사용자의 ID로 회원 정보를 가져옴
		Optional<UserEntity> userOptional = Optional.ofNullable(userRepository.findByUserId(currentUserId));

		// 사용자가 존재하지 않는 경우 예외 처리
		UserEntity user = userOptional.orElseThrow(() -> new RuntimeException("사용자 정보를 찾을 수 없습니다."));
		BoardEntity board = boardRepository.findById(boardId)
				.orElseThrow(() -> new IllegalArgumentException("Board not found"));

		CommentEntity commentEntity = CommentEntity.builder()
				.userId(user.getId())
				.user(user)
				.boardId(board.getId())
				.board(board)
				.comment(commentDTO.getComment())
				.commentDate(Date.valueOf(LocalDate.now()))
				.build();

		commentRepository.save(commentEntity);

		return CommentMapper.instance.commentToDto(commentEntity);
	}


	public List<CommentDTO> getCommentsByBoard(Long boardId) {
		// 게시판 정보 찾기
		BoardEntity board = boardRepository.findById(boardId)
				.orElseThrow(() -> new IllegalArgumentException("Board not found"));

		// 댓글 목록 조회 및 변환
		return commentRepository.findByBoard(board).stream()
				.map(commentMapper::commentToDto)
				.collect(Collectors.toList());
	}

}

 

CommentController.java

package com.example.demo.controller;

import com.example.demo.dto.CommentDTO;
import com.example.demo.service.CommentService;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/v1/auth/n")
public class CommentController {

    @Autowired
    private CommentService commentService;

    @Autowired
    private UserService userService;


    /**
     * 기능 : 댓글 작성
     * url : /createComment
     * request data : 댓글 작성 아이디, 강아지 이름, 내용, 날짜, 시간
     * response data : 댓글 작성 성공
     */
    @PostMapping("/saveComment/{board_id}")
    public CommentDTO saveComment(@PathVariable("board_id") Long boardId, @RequestBody CommentDTO commentDTO) {
        return commentService.saveComment(boardId, commentDTO);
    }



    /**
     기능 : 댓글 조회
     url : /comment/board?id={id}
     request data :
     response data :
     */
    @GetMapping("/comment/{id}")
    public List<CommentDTO> comment(@PathVariable("id") Long id) {
        return commentService.getCommentsByBoard(id);
    }

}

 

 


 

 

💣 [ 트러블 슈팅 ]   org.springframework.web.bind.MissingServletRequestParameterException 

문제

Required request parameter 'userId' for method parameter type Long is not present

org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'userId' for method parameter type Long is not present

 

원인

@RequestParam - userId를 비필수 설정을 하지 않아서 발생한 에러였습니다.

@RequestParam 어노테이션에서 required 속성을 사용하여 해당 파라미터가 반드시 필요한지 여부를 설정할 수 있습니다. 기본값은 true이며, 이는 파라미터가 누락되면 MissingServletRequestParameterException 예외가 발생합니다.

반면에 required=false로 설정하면, 해당 파라미터가 요청에 포함되지 않아도 예외가 발생하지 않고, 메서드의 매개변수에는 null이나 기본값이 할당됩니다.

 

해결

userId가 처음에 화면에 들어올 때는 필수가 아니기 때문에 required=false로 비필수 설정했습니다.

 

 


 

 

💣 [ 트러블 슈팅 ]   댓글 작성 => boardId가 저장안되는 이슈 

문제

boardId를 빼고 댓글이 저장되어 특정 게시글에 댓글이 저장되지 않았습니다.

 

원인

DB엔 boardId가 있는데 Entity에는 user_id처럼 board_id를 작성하지 않은 게 원인이었습니다..

 

해결

Entity와 DTO에 boardId 컬럼을 추가해주고

서비스단에서 boardId도 빌드해주었습니다.

 

결과

댓글 작성을 해보니 이제 board_id도 저장되는 것이 확인됩니다.

'Project 댕린이집' 카테고리의 다른 글

[소셜로그인] 카카오 로그인  (0) 2024.07.03
[알림장] 작성 / 조회  (0) 2024.07.01
[게시판] 좋아요  (0) 2024.06.28
[게시판] 조회수  (0) 2024.06.27
[게시판] 게시글 삭제  (0) 2024.06.26
xoo | 수진