API 실습 2

변현섭·2023년 6월 14일
0

저번 포스팅에서 이야기한대로 이번 시간에는 직접 Comments 엔티티에 대한 Controller, Repository, Service를 만들어볼게요. 일단 아래에 Comment 엔티티가 추가된 코드의 깃허브 링크를 올려두었으니 참고해주세요.
>> Comment가 추가된 코드
이번인 두 가지 API를 설계해보았는데요. Post 요청으로 댓글을 작성하는 API와 Get 요청으로 특정 member의 댓글 기록을 조회하는 API입니다. 이번에도 구체적인 설명은 생략하고 새로운 개념에 대해서만 간략히 짚고 넘어갈게요.

Ⅰ. Comment 클래스

Member는 Board와 일대다 관계를 맺고 있고, Board와 Comments도 다시 일대다 관계를 맺고 있다. 또한 Member와 Comments도 일대다 관계이다. 즉, Comments 테이블에는 Member와 Board 테이블의 기본키를 외래키로 사용하고 있는 것이다.

당연히 Member와 Board 클래스에서 Comment를 List로 관리해야 한다.

1) Member 클래스


Member 클래스와 Comment 클래스의 관계는 일대다이므로, Comment가 주인이 되고, Member는 mappedBy될 것이다. Member 클래스 측에서는 Comment들을 List로 관리해야 한다.

2) Board 클래스


Board 클래스와 Comment 클래스의 관계는 일대다이므로, Comment가 주인이 되고 Board는 mappedBy될 것이다. Board 클래스 측에서는 Comment들을 List로 관리해야 한다.

Ⅱ. Comment Cotroller "comment" 요청

1. 선언 전 어노테이션

자세히 들여다 보면 못 보던 어노테이션이 있다. 바로 @RequestMapping 어노테이션인데, 이는 아래 컨트롤러에 정의된 모든 요청 URI에 "/comment"가 자동으로 추가된다는 의미 정도로 생각하면 된다. 쉽게 말해 댓글을 작성하는 API를 호출하려면, http://localhost:8080/comment를 입력해야 한다는 것이다.

2. addComment()

1) PostCommentReq


Post 요청을 위해선 boardId와 email, reply가 Json 형식으로 요청 본문에 입력되어야 한다.

2) commentService.addComment()


입력받은 boardId로는 게시글을 찾고, email로는 멤버를 찾아 comment를 빌드하고 있다.

Ⅲ. Comment Controller "list-up" 요청


이 API는 두 가지 버전으로 설계하였다.

1) commentService.getComments() 첫번째 버전


멤버 ID를 받아 멤버와 댓글을 찾고 있다. 결과적으로는 댓글을 단 게시글의 제목과 댓글 내용이 List형태로 반환된다. 근데 이 API에는 문제점이 하나 있다. 무엇일까?

예를 들어 Chrome이라는 멤버가 여러 게시글에 댓글을 달았다고 해보자. 간혹 한 게시글에 여러 번 댓글을 다는 경우도 있었을 것이다.

Chrome이라는 멤버가 게시글 1,2,3번에 한번씩 댓글을 달고, 2번 게시글에 댓글을 한번 더 달았다. 이 때 list-up 요청에 대한 출력 결과는 아래와 같이 나타난다.

Hi! My Name is Bora라는 하나의 게시글에 댓글을 여러 번 쓴 것뿐인데, 마치 다른 글에 있는 댓글 같이 출력된다는 점이 문제이다. 물론, 큰 문제는 아니라고 여겨지지만, 가독성을 떨어뜨리는 요소임은 분명하다.

2) commentService.getComments() 두번째 버전

첫번째 버전에 비해 코드가 많이 복잡할 수 있다.

① Map

  • 게시글의 제목을 key로 갖고, comments를 value로 갖는 commentsByTitle이라는 Map을 선언하였다.
  • Map을 통해 같은 제목의 게시글에 작성된 댓글을 grouping하고 있다.

② for-each문

  • entry는 반복 중인 개별 항목을 의미한다. entry는 commentsByTitle.entrySet의 값을 순서대로 갖게 된다.
  • entry.getKey()를 사용하여 현재 항목의 키 즉, 게시글 제목을 가져온다. 이 값을 title 변수에 저장한다.
  • entry.getValue()를 사용하여 현재 항목의 value 즉, 해당 게시글에 대한 댓글 목록을 가져온다.
  • .stream()을 사용하여 댓글 목록을 스트림으로 변환한다.
  • .map(Comment::getReply)를 사용하여 각 comments들을 해당 comment의 reply 필드 값으로 변환한다. 이를 통해 각 comments의 reply 값으로 이루어진 스트림이 생성된다.
  • .collect(Collectors.toList())를 사용하여 스트림의 요소를 리스트로 수집한다. 이 리스트는 각 게시글에 대한 모든 comments의 reply 필드 값으로 이루어져 있다.
  • new GetCommentRes(title, replies)를 사용하여 현재 게시글 제목과 해당 게시글의 댓글 목록을 가지고 GetCommentRes 객체를 생성하고 있다.
  • 생성된 GetCommentRes 객체를 getCommentRes 리스트에 추가합니다.

    여기서 GetCommentRes와 getCommentRes는 완전히 다른 것이다. GetCommentRes는 게시글 제목과, 댓글 내용을 포함하는 자료형이고, getCommentRes는 GetCommentRes형 자료들을 모아둔 List의 이름이다. 보통 List의 이름을 변수명 뒤에 -s를 붙여 네이밍하는 것이 관례인데, getCommentRes에 뒤에 -s를 붙일 수 없어 다소 혼란이 야기될 수 있는 이름이 되어버린 것이다.

이제 API 테스트의 결과가 어떻게 바뀌는지 살펴보자. 이번에는 Chrome이 1번 게시글에 댓글을 3번이나 달고, 2,3번 게시글에 한번씩 댓글을 달았다.

postman에서 실행한 결과는 아래와 같다.

보다시피, 하나의 게시글에 달린 댓글끼리 잘 그룹화되어 결과가 출력된다. API 연습을 더 해보고 싶다면, 댓글을 삭제 및 수정하는 API도 작성해보기 바란다. 이에 대한 코드는 제공되지 않을 예정이지만, 충분히 할 수 있을 것이다.

profile
Java Spring, Android Kotlin, Node.js, ML/DL 개발을 공부하는 인하대학교 정보통신공학과 학생입니다.

0개의 댓글