스프링 부트 + vue.js 로 게시판 만들기 - 1

2023. 4. 25. 16:07초기 과업/BackEnd

작성자알 수 없는 사용자

728x90
반응형
@GetMapping("/{id}")
public ResponseEntity<Message> viewBoard(@PathVariable("id") Long boardId) {
   return new ResponseEntity<>(new Message(boardService.viewBoard(boardId)), HttpStatus.OK);
}

안녕하세요. 기깔나는 사람들에서 백엔드를 맡고 있는 박영광입니다.

 

오늘은 간단한 게시판 API를 스프링을 통해 구현해보려고 해요. 구현할 API는 아래와 같아요.

 

1. 게시판 글 작성하기

2. 게시판 글 목록 보기

3. 게시판 글 상세 보기

4. 댓글 작성하기

5. 댓글 목록보기

 

위 5가지 API를 구현하고 테스트 코드도 작성할거에요. 그리고 Jacoco를 통해서 Coverage를 90% 이상 맞춰보려고 합니다.


게시판 API 구현

1) 게시판 글 등록

RestController로 Json을 통해 프론트와 통신을 할거에요.

@PostMapping
public ResponseEntity<Message> registerBoard(@RequestBody RegistBoardRequest request, Principal principal) {
   Member member = memberService.getMember(principal.getName());
   return new ResponseEntity<>(new Message(boardService.registerBoard(request, member)), HttpStatus.OK);
}

 

먼저  글 작성 API를 볼게요.

 

로그인 되어 있는 회원만 글을 쓸 수 있도록 하기 위해서 getMember에서  principal 검증을 합니다.

해당하는 member 객체를 찾아서 서비스단으로 넘겨줍니다. 

public RegisterBoardResponse registerBoard(RegistBoardRequest request, Member member) {
   Board board = new Board(request, member);
   return new RegisterBoardResponse(boardRepository.save(board).getId());
}

글 객체를 만들어서 repository에서 저장해주는 간단한 구현이에요. 응답은 작성된 글의 id를 넘겨주도록 했습니다. 

 

@Entity
@Getter
@NoArgsConstructor
public class Board {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "id", nullable = false)
   private Long id;

   @ManyToOne(fetch = FetchType.LAZY)
   @JoinColumn(name ="member_id")
   private Member member;

   private String title;

   @Lob
   private String content;

   public Board(RegistBoardRequest request, Member member) {
      this.member = member;
      title = request.getTitle();
      content = request.getContent();
   }
}

 

게시판 entity는 위와 같이 구성해주었어요. 작성자 정보와 글의 제목, 내용으로 구성되어 있어요.

 

2) 게시판 글  상세 보기

@GetMapping("/{id}")
public ResponseEntity<Message> viewBoard(@PathVariable("id") Long boardId) {
   return new ResponseEntity<>(new Message(boardService.viewBoard(boardId)), HttpStatus.OK);
}
@Transactional(readOnly = true)
public ViewBoardResponse viewBoard(Long boardId) {
   Board board = boardRepository.findById(boardId)
      .orElseThrow(() -> new IllegalArgumentException("잘못된 게시글 id"));
   return new ViewBoardResponse(board);
}

컨트롤러단과 서비스단 코드에요. 별다른 구현 없이 게시판 id 받아서 repository에서 조회해서 반환해주면 됩니다 :)

 

3) 게시판 글 목록 보기

@GetMapping
public ResponseEntity<Message> viewBoardLists(@RequestParam("page") int page, @RequestParam("size") int size) {
   Page<Board> boardPage = boardService.viewBoardLists(page - 1, size);
   Pagination pagination = Pagination.builder()
      .page(page)
      .size(size)
      .totalElements((int)boardPage.getTotalElements())
      .totalPages(boardPage.getTotalPages())
      .build();

   List<ViewBoardResponse> content = boardPage.get()
      .map(ViewBoardResponse::new)
      .collect(Collectors.toList());

   return new ResponseEntity<>(new Message(content, pagination), HttpStatus.OK);
}
public Page<Board> viewBoardLists(int page, int size) {
   return boardRepository.findAllByOrderByIdDesc(PageRequest.of(page, size));
}

글 목록 보기 컨트롤러단과 서비스단 코드에요. 글 목록을 전부 불러와서 볼 수도 있겠지만, 좀 더 깔끔하게 보기 위해 페이징 처리를 해주었어요.

 

페이지와 페이지 사이즈를 파라미터로 받아서 repository에서 조회해 줍니다.

@Repository
public interface BoardRepository extends JpaRepository<Board, Long> {
   Page<Board> findAllByOrderByIdDesc(Pageable pageable);
}

JPA에서 기본적으로 해주기 때문에 위와 같이 파라미터와 반환 타입 맞춰서 해주시면 페이징처리를 할 수 있어요.


댓글 API 구현

1) 댓글 작성 하기

@PostMapping
public ResponseEntity<Message> registerComment(@RequestBody RegisterCommentRequest request, Principal principal) {
   Member member = memberService.getMember(principal.getName());
   return new ResponseEntity<>(new Message(commentService.registerComment(request, member)), HttpStatus.OK);
}

댓글 작성도 글 작성과 마찬가지로 principal을 통해 member객체를 찾아서 댓글을 작성해줬어요.

 

public RegisterCommentResponse registerComment(RegisterCommentRequest request, Member member) {
   Comment parentComment = null;
   if (request.getParentId() != null)
      parentComment = commentRepository.findById(request.getParentId()).orElseThrow(() -> new RuntimeException("부모 댓글 존재 X"));

   if (parentComment != null && parentComment.getDepth() == COMMENT_MAX_DEPTH)
      throw new RuntimeException("대댓글 최대 깊이 제한");

   Board board = boardRepository.findById(request.getBoardId())
      .orElseThrow(() -> new RuntimeException("글 정보 없음"));
   Comment comment = new Comment(request, member, parentComment, board);

   return new RegisterCommentResponse(commentRepository.save(comment).getId());
}

서비스단 코드입니다. 대댓글이 가능하도록, 그 깊이는 최대 5까지만 가능하도록 구현을 해줬어요. 댓글 객체에는 어느 글에 작성되었는지, 댓글의 작성자는 누구인지 필요하기 때문에 모두 찾아서 댓글 엔티티에 넣어주고 저장해주었어요.

 

2) 댓글 목록 보기

public List<ViewCommentResponse> viewCommentsList(Long boardId) {
   return commentRepository.findAllByBoardIdOrderByIdDesc(boardId).stream().map(ViewCommentResponse::new).collect(
      Collectors.toList());
}

현재 생각하고 있는 구현에서는 댓글 상세 조회가 필요 없을 것 같아서 목록 보기만 구현을 했습니다. 글 목록 보기와는 달리 페이징 처리 없이 구현했습니다.

 

 


테스트 코드 작성

테스트 코드는 위에서 언급했듯이 jacoco를 사용해서 검증하도록 할게요. test코드는 반복적인 작업이 많고, jacoco report를 통해 부족한 부분을 채워갈 수 있으니 일부만 보도록 하겠습니다.

 

1) jacoco 

plugins {
   id 'jacoco'
}

build.gradle에 jacoco 플러그인을 추가해줍니다.

 

jacocoTestReport{
   reports {
      html.enabled true
      xml.enabled true
      csv.enabled true
   }

   dependsOn test // tests are required to run before generating the report

   afterEvaluate {
      classDirectories.setFrom(files(classDirectories.files.collect {
         fileTree(dir: it, exclude: [
               "**/config/*",
               "**/jwt/*",
               "**/vue/*",
               "**/webhook/*",
               "**/request/*",
               "**/common/*",
               "**/response/*"
         ])
      }))
   }

   finalizedBy 'jacocoTestCoverageVerification'
}

그리고 저는 커버리지가 필요 없는 것들을 jacoco report에서 제외하기 위해 위와 같은 설정도 추가해주었습니다.

 

2) test 작성

@Test
@DisplayName("댓글 작성")
void commentServiceTest() {
   RegisterCommentRequest request = new RegisterCommentRequest("content", null, basicBoard.getId());

   assertThat(commentService.registerComment(request, basicMember)).isNotNull();
}

위 코드는 댓글 작성을 테스트 하는 코드에요. service에 해당 method를 호출하고 원하는 return이 나왔는지 assertThat을 사용하여 검증해줍니다.

 

3) jacoco report

그리고 jacoco 테스트를 돌려줍니다. 그리고 build > reports > jacoco > test > html 로 이동하면 결과를 확인할 수 있어요.

이런 식으로 어느 정도 커버되었는지 볼 수 있고, 상세 보기를 들어가면 어떤 줄이 커버가 됐는지 안됐는지 확인도 할 수 있어요.

 


회고 제목

오늘은 간단한 게시판 API를 만들어봤어요. 다음 글에는 Vue.js로 프론트엔드를 구성해서 연결해보도록 하겠습니다.

 

 

 

 

 

728x90
반응형