web +a

스프링부트 ~22 | 댓글 CRUD를 위한 Entity, Repository & Test

냥냥체뤼 2022. 7. 26. 15:21

각 Article에는 Comment가 달리기 마련이다.

이 기능을 추가하기 위해서 Comment Entity와 Repository를 준비해보자.

 

이전에 Article Entity 작성했던 것을 떠올리면서, 어떤 데이터가 필요할지(:작성한대로 DB table이 생성됨)생각하며 Comment Entity를 구현하면 된다.

 

 


아래 내용들을 알고 들어가자

 

∨ DB의 데이터는 id로 관리된다. 

- 데이터 자기 자신을 가리키는 id

- 그리고 대상을 가리키는 id

이것이 즉 PK와 FK다.

- PK : primary key

- FK : foreign key

 

∨ 추가하려는 기능은 댓글 기능이다. 

댓글과 게시글의 관계는?

- 게시글 입장에서는 일대다 관계이고 @OneToMany

- 댓글 입장에서는 다대일 관계이다 @ManyToOne

 


댓글 Entity 설계

- 하나의 댓글은 어떤 하나의 게시글에 달라는 것이다. 

- 각 댓글마다 Article의 id가 부여되겠다.

Article Comment
id : Long
title : String
content : String
id : Long
article : Article
nickname : String
body : String

 

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

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    // Comment 입장에서 Article과의 관계는 ManyToOne이다
    // FK가 article_id column에 들어간다
    // JPA는 자동으로 table 조인을 해주기 때문에 Article을 통으로 넣는다.
    // 반면 Mybatis나 JDBC는 조인을 해주지 않아서 article_id를 바로 담는 것이 표준이다.
    @ManyToOne
    @JoinColumn(name = "article_id")
    private Article article;
    
    @Column
    private String nickname;
    @Column
    private String body;
}

 

 

참고) 더미데이터 추가

더보기

Article에 더미데이터를 추가해놓고 실습했던 것처럼 Comment 더미데이터도 같은 방식으로 추가한다.

INSERT INTO comment(id, article_id, nickname, body) VALUES(2, 5, '예니', '악플금지!!');

참고) SQL 연습

더보기

4번 게시글의 모든 댓글

SELECT
    *
FROM
    comment
WHERE
    article_id = 4
;

 

닉네임이 "예니"인 모든 댓글

SELECT
    *
FROM
    comment
WHERE
    nickname = '예니'
;

 

 

 


댓글 Repository 설계

이전에 Entity Repository는 CrudRepository를 사용했었다.

이번에는 JpaRepository를 사용한다.

* CrudRepository를 확장한 PagindAndSortingRepository를 확장한 것이다. 데이터 crud뿐 아니라 특정 페이지의 데이터 조회, 정렬 등 제공한다.

더보기

Repositofy <= CrudRepository <= CrudRepository <= PagindAndSortingRepository <= JpaRepository

 

지난번에 articleRepository를 구현할 때에는 그냥 extends만 해주고 딱히 구현한 부분은 없었다.

지금 commentRepository에는 메서드를 작성해줄 것이다.

recall) 댓글 Entity를 잘 구현했나 확인하면서 sql 쿼리 연습을 했다. 

- n번 게시글의 모든 댓글을 조회하는 쿼리

- 닉네임 '~~'의 모든 댓글을 조회하는 쿼리

 

DB랑 접촉하는 Repository의 역할을 톡톡히 수행하는 '쿼리 메서드'를 작성해줄 것이다.

쿼리문을 메서드로 수행하게 하는 방식을 두가지 새로 배웠다.

- 메서드에 @Query() 어노테이션을 붙이고,  괄호 안에 수행시킬 쿼리문을 넣어주면 된다.

- 해당 메서드를 통해 수행할 쿼리를 xml로 작성해두는 방법도 있다.

public interface CommentRepository extends JpaRepository<Comment, Long> {
    // 네이티브 쿼리문을 직접 작성
    @Query(value =
            "SELECT * " +
            "FROM comment " +
            "WHERE article_id = :articleId",
            nativeQuery = true) // true로 해주어야 해당 sql문이 직접 동작한다
    List<Comment> findByArticleId(Long articleId);
    
    // 네이티브 쿼리문을 xml로 작성
    List<Comment> findByNickname(String nickname);
}

* @Query() 내부 변수명과 메서드의 파라미터명을 일치시켜야 인식한다.

* 만약 인식하지 못할 경우 메서드 파라미터에 @Param("articleId") Long articleId라고 명시해준다.

 

메서드로 수행할 sql을 xml로 작성하는 예시는 접은글에 넣겠다.

더보기

작성 위치 ../resources/META-INF/orm.xml (디렉토리명, 파일명 지켜야 한다)

이때 작성하는 xml은 외우지 말고,

구글에 orm native quary 또는 orm.xml example 이런 식으로 검색해서 쓰면 된다.

 

수행하는 쿼리문은 named-native-query 내부를 보면 된다.

name="Comment.findByNickname" : Comment 리포지토리의 findByNickname 메서드

result-class="com.example.firstproject.entity.Comment" : Comment 엔티티를 반환한다는 것

그리고나서 아래와 같이, 수행할 쿼리문을 작성해주면 된다.

<?xml version="1.0" encoding="utf-8" ?>
<entity-mappings xmlns="https://jakarta.ee/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>

 

쿼리가 복잡하거나 최적화가 필요하면 xml, 단순한 쿼리는 @Query 어노테이션을 이용할 수 있겠다. 

정해진 좋은 방법은 없다. 

 

 

 


Test

 

리포지토리에 대한 테스트를 진행할 것이기 때문에, 

@DataJpaTest를 붙여주자.

 

@DataJpaTest
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, "예니", "오늘은 좋은날!!");
            Comment b = new Comment(2L, article, "죠지", "노래불러요");
            Comment c = new Comment(3L, article, "젤리젤리", "나는 콜라 마셔욥");
            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, "귀여운거 보고가세요", "그건바로 옌이!!");
            List<Comment> expected = Arrays.asList();
            // 검증
            assertEquals(expected.toString(), comments.toString(), "1번 글은 댓글이 없음");
        }
    }
    @Test
    @DisplayName("특정 닉네임의 모든 댓글 조회")
    void findByNickname() {
        /* Case 1: "예니"의 모든 댓글 조회 */
        {
            // 준비
            String nickname = "예니";
            // 수행
            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(), "예니의 모든 댓글을 출력");
        }
    }
}

 

 

 

이번에도 역시 스스로하는 도전과제.. 

이 강의 코드는 안따라하고 있어서 그냥 이런 테케를 만들 수 있구나 정도만 보고 넘어가겠다.

 

findByArticleId()의 테스트케이스 3, 4, 5번

case 3: 9번 게시글의 모든 댓글 조회

case 4: 9999번 게시글의 모든 댓글 조회

case 5: -1번 게시글의 모든 댓글 조회

 

findByNickname()의 테스트케이스 2, 3, 4, 5번

case 2: "Kim"의 모든 댓글 조회

case 3: null 의 모든 댓글 조회

case 4: " "의 모든 댓글 조회

case 5: "i"가 들어간 닉네임의 모든 댓글 조회

반응형