-
概要 (개요)
이번 포스팅에서는 기존에 완성한 게시판 기능 위에 댓글(Comment) 기능을 추가했습니다.
이번 구현의 핵심은 하나의 게시글(Board)에 여러 개의 댓글(Comment)이 달릴 수 있는 일대다(1:N) 데이터베이스 관계를 JPA의 @ManyToOne 어노테이션을 활용해 객체지향적으로 매핑하고, 외래키(FK)를 제어하는 방법을 학습하는 것입니다. -
テーブル設計와 関連付け (테이블 설계와 연관 관계)
데이터베이스 관점에서 BOARDS 테이블과 COMMENTS 테이블은 board_id라는 외래키를 통해 부모와 자식 관계로 연결됩니다. JPA에서는 이를 자바 객체 참조 방식으로 매핑합니다.
📝 댓글 엔티티 구현 (Comment.java)
@Entity
@Table(name = "COMMENTS")
@Getter
@NoArgsConstructor
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String content; // 댓글 내용
private String writer; // 댓글 작성자 (세션 닉네임)
// 💡 핵심: 多対一(ManyToOne) 릴레이션 설정
// 여러 댓글(Many)은 하나의 게시글(One)에 소속됩니다.
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "board_id") // DB 상에 board_id라는 외래키(FK) 컬럼 생성
private Board board;
@Builder
public Comment(String content, String writer, Board board) {
this.content = content;
this.writer = writer;
this.board = board;
}
}
- Repository & Service 구현
① Repository 확장 (CommentRepository.java)
특정 게시글에 달린 댓글들만 쏙 필터링해서 꺼내오기 위해 JPA의 쿼리 메서드 규칙(findByBoardId)을 정의했습니다.
public interface CommentRepository extends JpaRepository<Comment, Long> {
// board_id를 기준으로 댓글 리스트를 조회하는 메서드 자동 생성
List<Comment> findByBoardId(Long boardId);
}
② 비즈니스 로직 작성 (CommentService.java)
댓글을 등록할 때 원본 게시글 객체(Board)를 먼저 조회한 뒤, 댓글 엔티티를 빌더로 조립할 때 부모 객체로 매핑하여 저장합니다.
@Service
@RequiredArgsConstructor
public class CommentService {
private final CommentRepository commentRepository;
private final BoardRepository boardRepository;
/**
* 댓글 작성 처리 (부모 엔티티와 연동)
*/
@Transactional
public Long writeComment(Long boardId, String content, String writer) {
Board board = boardRepository.findById(boardId)
.orElseThrow(() -> new IllegalArgumentException("存在しない投稿です。ID: " + boardId));
Comment comment = Comment.builder()
.content(content)
.writer(writer)
.board(board) // 연관 관계 매핑!
.build();
return commentRepository.save(comment).getId();
}
}
- ViewController 데이터 통합 렌더링
사용자가 게시글 상세 페이지에 접근했을 때, 글 내용(board)뿐만 아니라 해당 글에 소속된 댓글 리스트(comments)까지 한 보따리에 담아서 화면(Thymeleaf)으로 전달하도록 수정했습니다.
@GetMapping("/board/detail")
public String detailPage(@RequestParam("id") Long id, Model model, HttpSession session) {
// 1. 원본 게시글 데이터 조회
Board board = boardService.findPostById(id);
model.addAttribute("board", board);
// 2. 해당 게시글의 자식 댓글 리스트 조회 (추가된 로직)
List<Comment> comments = commentService.findCommentsByBoardId(id);
model.addAttribute("comments", comments);
model.addAttribute("session", session);
return "detail";
}