백엔드/스프링 부트 3_자바 백엔드

14. 댓글 엔티티와 리파지터리 만들기

smallsilver_west 2025. 1. 11. 17:08

14.1 댓글 기능의 개요

 

댓글과 게시글의 관계

그림 1) 게시물과 댓글의 관계

 

| One to Many (일대다, 1:N): 하나의 게시글과 수많은 댓글

| Many to One (다대일, N:1): 여러 댓글과 하나의 게시글

 

기본키

테이블에서 자료를 대표하는 속성

 

대표키

기본키 중에서 동일 테이블 내에 중복된 값이 없는 속성

 

외래키

하나의 테이블에서 다른 테이블의 특정 속성과 같은 연관 대상이 있는 경우 이를 가리키는 속성

 

엔티티

DB 데이터를 담는 자바 객체로 엔티티를 기반으로 해 테이블을 생성

 

리포지터리

엔티티를 관리하는 인터페이스로 데이터 CRUD 등의 기능을 제공하는 역할

 

Comment 엔티티와 Article 엔티티의 관계

CommentRepository는 CrudRepository 대신 JpaRepository를 상속

JpaRepository는 ListCrudRepository와 ListPagingAndSortingRepository를 상속받는 인터페이스

그림 2) Repository 상속 구조

 

* CRUD뿐만 아니라 엔티티를 페이지 단위로 조회 및 정렬하는 기능 제공

* JPA에 특화된 여러 기능 제공

 

14.2 댓글 엔티티 만들기

 

댓글 엔티티 생성

@Entity
@Getter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Comment {

    @Id //대표키
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne //두개의 엔티티를 ManytoOne 관계로 설정하는 어노테이션
    @JoinColumn(name = "article_id") //외래키 생성 어노테이션
    private Article article;

    @Column
    private String nickname;

    @Column
    private String body;
}

 

더미 데이터 추가

//기존 데이터
INSERT INTO article(title, content) VALUES('가가가가', '1111');
INSERT INTO article(title, content) VALUES('나나나나', '2222');
INSERT INTO article(title, content) VALUES('다다다다', '3333');

//article 테이블에 데이터 추가
INSERT INTO article(title, content) VALUES('당신의 인생 영화는?', '댓글 고');
INSERT INTO article(title, content) VALUES('당신의 소울 푸드는?', '댓글 고고');
INSERT INTO article(title, content) VALUES('당신의 취미는?', '댓글 고고고');

//4번 게시글의 댓글 추가
INSERT INTO comment(article_id, nickname, body) VALUES (4, 'Park', '굿 윌 헌팅');
INSERT INTO comment(article_id, nickname, body) VALUES (4, 'Kim', '아이 엠 샘');
INSERT INTO comment(article_id, nickname, body) VALUES (4, 'Choi', '쇼생크 탈출');

//5번 게시글의 댓글 추가
INSERT INTO comment(article_id, nickname, body) VALUES (5, 'Park', '치킨');
INSERT INTO comment(article_id, nickname, body) VALUES (5, 'Kim', '샤브샤브');
INSERT INTO comment(article_id, nickname, body) VALUES (5, 'Choi', '초밥');

//6번 게시글의 댓글 추가
INSERT INTO comment(article_id, nickname, body) VALUES (6, 'Park', '조깅');
INSERT INTO comment(article_id, nickname, body) VALUES (6, 'Kim', '유튜브 시청');
INSERT INTO comment(article_id, nickname, body) VALUES (6, 'Choi', '독서');

 

댓글 조회 쿼리

SELECT * FROM comment WHERE article_id = 4; //특정 게시글의 댓글 조회
SELECT * FROM COMMNET WHERE nickname = 'Park'; //특정 닉네임의 댓글 조회

 

14.3 댓글 리포지터리 만들기

 

댓글 리포지터리 생성

@Repository
public interface CommentRepository extends JpaRepository<Comment, Long> {
    //특정 게시글의 댓글 조회
    @Query(value =
            "SELECT * " +
            "FROM comment " +
            "WHERE article_id = :articleId",
            nativeQuery = true)
    List<Comment> findByArticleId(Long articleId);
    
    //특정 닉네임의 댓글 조회
    List<Comment> findByNickname(String nickname);
}
<?xml version="1.0" encoding="utf-8" ?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence/orm
                 https://jakarta.ee/xml/ns/persistence/orm/orm_3_0.xsd"
                 version="3.0">
    <named-native-query
            name="Comment.findByNickname"
            result-class="com.example.firstproject.entity.Comment">
        <query>
            <![CDATA[
                SELECT * FROM comment WHERE nickname = :nickname
            ]]>
        </query>
    </named-native-query>
</entity-mappings>
<named-native-query
        name = "쿼리_수행_대상_엔티티.메소드_이름"
        result-class = "쿼리_수행_결과_반환_타입_전체_패키지_경로">
    <query>
        <![CDATA[
            <!-- 쿼리 --> //쿼리 태그
        ]]>
    </query>
</named-native-query>

 

댓글 리포지터리 테스트 코드

@DataJpaTest //@SpringBootTest가 아닌 @DataJpaTest를 이용해 JPA와 연동
class CommentRepositoryTest {
    @Autowired
    CommentRepository commentRepository;

    @Test
    @DisplayName("특정 게시글 댓글 조회") //테스트 결과에 출력하는 메소드 이름을 변경하는 어노테이션
    void findByArticleId() {
    
        /* Case 1: 4번 게시글의 모든 댓글 조회*/
        {
            //입력 데이터
            Long articleId = 4L;
            //실제 데이터
            List<Comment> comments = commentRepository.findByArticleId(articleId);
            //예상 데이터
            Article article = new Article(4L, "당신의 인생 영화는?", "댓글 고");
            Comment a = new Comment(1L, article, "Park", "굿 윌 헌팅");
            Comment b = new Comment(2L, article, "Kim", "아이 엠 샘");
            Comment c = new Comment(3L, article, "Choi", "쇼생크 탈출");
            List<Comment> expected = Arrays.asList(a, b, c);
            //비교 및 검증
            assertEquals(expected.toString(), comments.toString(), "4번 글의 모든 댓글을 출력!");
        }
        
        /* Case 2: 1번 게시글 댓글 조회*/
        {
            //입력 데이터 준비
            Long articleId = 1L;
            //실제 데이터
            List<Comment> comments = commentRepository.findByArticleId(articleId);
            //예상 데이터
            Article article = new Article(1L, "가가가가", "1111");
            List<Comment> expected = Arrays.asList();
            //비교 및 검증
            assertEquals(expected.toString(), comments.toString(), "1번 글은 댓글이 없음");
        }
    }

    @Test
    @DisplayName("특정 닉네임 댓글 조회")
    void findByNickname() {
    
        /* Case 1: "Park"의 모든 댓글 조회 */
        {
            //입력 데이터 준비
            String nickname = "Park";
            //실제 데이터
            List<Comment> comments = commentRepository.findByNickname(nickname);
            //예상 데이터
            Comment a = new Comment(1L, new Article(4L, "당신의 인생 영화는?", "댓글 고"), nickname, "굿 윌 헌팅");
            Comment b = new Comment(4L, new Article(5L, "당신의 소울 푸드는?", "댓글 고고"), nickname, "치킨");
            Comment c = new Comment(7L, new Article(6L, "당신의 취미는?", "댓글 고고고"), nickname, "조깅");
            List<Comment> expected = Arrays.asList(a, b, c);
            //비교 및 검증
            assertEquals(expected.toString(), comments.toString(), "Park의 모든 댓글을 출력!");
        }
    }
}

 

'백엔드 > 스프링 부트 3_자바 백엔드' 카테고리의 다른 글

13. 테스트 코드 작성하기  (1) 2025.01.11
12. 서비스 계층과 트랜잭션  (0) 2025.01.11
11. HTTP와 REST 컨트롤러  (0) 2025.01.04
10. RESET API와 JSON  (0) 2025.01.04
9. CRUD와 SQL 쿼리 종합  (0) 2025.01.04