마지막으로 조회 기능을 추가해 보도록 하자.
조회하는건 두가지 종류가 있다.
1) 게시글을 확인
2) 전체 게시글을 확인
우리가 아무 사이트나 들어가서 보면 게시글 목록이 쭈르륵 나오고 클릭해서 들어가면 해당 게시글을 볼수 있을꺼다. 지금 부터 그 작업을 해보자.
먼저 Dto를 살펴보자.
@Getter @Setter
@AllArgsConstructor
public class BoardResponseDto {
private Long boardId;
private String title;
private String content;
//정적 팩토리 메서드 추가
public static BoardResponseDto FindFromBoard(Board board) {
return new BoardResponseDto(
board.getBoardId(),
board.getTitle(),
board.getContent()
);
}
}
BoardResponseDto를 만들어 주었다. 그런데 지금까지 봤던 Dto와 다르다. 그렇다.
정적팩토리 메서드를 적용하였다. 기존의 PATCH Service를 보자.
public Long updateBoard(BoardPatchDto boardPatchDto, Long boardId) {
Board board = findBoardId(boardId);
board.setTitle(boardPatchDto.getTitle());
board.setContent(boardPatchDto.getContent());
return boardRepository.save(board).getBoardId();
}
잘보면 여기서 Getter와 Setter로 값을 넣어주고 있다.
하지만.
public BoardResponseDto findByBoardId(Long boardId) {
Board board = findBoardId(boardId);
return BoardResponseDto.FindFromBoard(board);
}
여길 살펴보면 Get 부분의 Service 코드가 간결하다 이유는 Dto에서 정적팩토리 메서드로 이미 값을 치환해 줬기 때문이다.
static을 쓰게 되면 클래스에서 바로 호출을 할수 있게 되는데 그러면 위 코드 처럼 간결하게 나오게 된다. 원래는 생성자 호출하고 인스턴스 생성해서 값을 넣어주고 해야겠지? 그러면 지저분해 진다.
여기서 @AllArgsConstructor는 클래스의 모든 변수의 생성자를 만들어 주는 녀석이다. 그리고 이번에는 boardId가 들어가 있는 이유는 게시글을 불러올때 어떤건지 알아야 하기 떄문이다.
뭐야 그럼 POST랑 PATCH는 어떤건지 알 필요가 없을까? 잘 생각해보자. boardId를 우리가 등록하거나 수정하지는 않지만 GET 요청은 화면을 보여주는건데 어떤 게시글인지 알아야 보여줘야 하지 않겠는가? 즉 우리는 GET 화면을 보고 마우스 클릭을 한다는 소리다.
이야기가 길었지만 findByBoardId 메서드에서는 한개의 boardId를 찾기위해 만들었다. 그리고 Controller를 보자.
@GetMapping("/{boardId}")
public ResponseEntity getBoard(@PathVariable("boardId") Long boardId) {
BoardResponseDto boardResponseDto = boardService.findByBoardId(boardId);
return ResponseEntity.status(HttpStatus.OK).body(boardResponseDto);
}
여기는 앞의 내용과 같다.
다음으로는 전체 조회를 해볼꺼다. 개념을 집고 넘어가면.
인터넷을 할때 우리는 1번 페이지 2번페이지 이렇게 한페이지에 30개씩 50개씩 정렬해서 보여주는 화면을 항상 볼것이다. 이것의 이름은 페이지네이션 Pagination 이라고 불린다.
바로 Service로 가자.
public Page<BoardResponseDto> findAllBoards(Pageable pageable) {
Page<Board> boards = boardRepository.findAll(pageable);
return boards.map(BoardResponseDto::FindFromBoard);
}
이것도 간단해 보이지만 우선 설명을 한번 보자.
Page<BoardResponseDto> 가 보이는데 Page는 페이지네이션 결과를 저장하는 메서드이다. 어떻게 처리할까? <BoardResponseDto> 형태로 처리하는거다. 폼이자 양식이라고 생각하자.
그리고 옆에 (Pageable pageable)이 보이는데 Pageable은 페이지네이션에 쓰이는 size, page 같은 기능들을 담고있는 메서드이다. 즉, Page로 처리된 결과가 가고 Pageable이 공정하는 거라고 생각하면 된다. Controller 보면 이해가 빠를수 있다.
매개변수 pageable에 인자가 들어올것 아닌가? 그걸 맞게 가공해서 Repository에서 전부 찾아서(findAll 메서드) boards 에다가 넣어 버린다. 그럼 boards에 페이지네이션 결과가 저장이 될꺼다. 혹시 아니 왜 board에다가 직접 물리면 되지 귀찮게 Dto나 쓰냐 하는 사람있으면 이전 게시글 보고 와라.
자 우리는 boards에 페이지네이션 처리가된 목록들이 저장되어있는걸 알았다 이젠 컨트롤러에 던져야 하는데 리턴을 보자.
map이 보인다 map은 스트림 개념인데 스트림에 약한 사람들을 위해 설명하면 변환기 라고 생각하자 왜 변환기가 필요하냐?
ResponseDto 형식으로 던져야 하는데 지금은 board 형태이지 않은가. 형태가 다르면 당연히 안돌아 가겠지? Integer랑 Long 이랑 다른것 처럼. 그래서 변환기 킨거다.
뒤에 boards.map(BoardResponseDto::FindFromBoard) 부분도 이해 안가는 사람이 있을꺼다.
FindFromBoard가 정적팩토리 메서드 인건 위에서 설명했다.
해석하면
boards의 내용을 BoardResponseDto에 있는 FindFromBoard를 참조해서 변환해라 이 말이다. 이게 싫으면
boards.map(board -> BoardResponseDto.FindFromBoard(board)); 이렇게 써도 된다. 선택은 마음대로..
이렇게 정적 팩토리 메서드 써주면 원래는 하나하나 노가다 하며 Service에 Post랑 Patch 마냥 주렁주렁 달아줘야 하는데 깔끔해 진다.
@GetMapping
public ResponseEntity<Page<BoardResponseDto>> getAllBoards(
@RequestParam(value = "page",defaultValue = "1")int page,
@RequestParam(value = "size",defaultValue = "5")int size) {
Pageable pageable = PageRequest.of(page - 1, size);
Page<BoardResponseDto> boards = boardService.findAllBoards(pageable);
return ResponseEntity.status(HttpStatus.OK).body(boards);
}
자 Controller를 보면 이번에도 생소하지만 이또한 까보면 간단하다.
ResponseEntity<Page<BoardResponseDto>>
ResponseEntity가 요청값을 처리 한다고 말했다. 그걸 Page 형태로 만든거다.
@RequestParam도 처음보는거다. @RequestParam은 클라이언트에서 요청 보낸 값을 우리가 사용할수 있게 변환해주는 장치이다.
예를들어 localhost:8080/api/boards?page=1&size=5 이런식으로 클라이언트가 요청을 하는데
page =1 그리고 size=5 값을 추출 하는 기능이다. 그래서 우리는 int 형으로 인자를 바꿔준거고.
여기서 page는 1번 페이지 2번페이지 같은 개념이고 size는 표시되는 게시글의 수 이다.
PageRequest.of(page - 1, size); 도 이해가 안갈꺼다 문백한 request니깐 알아는 듣겠는데 page 에다가 왜 -1을 할까?
그건 바로 페이지네이션은 0부터 읽기 시작하는데 사용자 입장에서는 0번째 페이지 라고 하면 혼선이 올것이다 보통 1번 페이지 라고 하지 0번 페이지라고는 안하지 않는가?
그래서 사용자가 보는 부분인 @RequestParam(value = "page",defaultValue = "1") 여기서 1로 강제 고정하고 서버에서 보는 부분인 PageRequest.of(page - 1, size); 에서 다시 1을 빼준거다.
여기까지 했으면 이젠 Postman으로 찍어보자.
이렇게 출력이 되게 된다.
그런데 번외로 하고 싶은 말이 있다.
localhost:8080/api/boards?page=1 여기 보면 size가 안보인다 입력하지 않아도 될까?
정답은 해도 되고 안해도 된다. 유명 커뮤니티 사이트 아무곳이나 들어가서 주소창 보면 size 부분이 생략 되어 있는걸 확인할수 있다. 즉, 미리 설정된 값을 뽑아 온다는 말이다. 그런데 이또한 클라이언트 쪽에서 JS로 마음대로 바꿀수도 있다.
다음에는 댓글 기능을 만들어 보겠다.
'스프링boot > 정리' 카테고리의 다른 글
Spring MVC) 사용자를 만들어 보자 1 (0) | 2023.04.18 |
---|---|
Spring MVC) 댓글을 만들어 보자 (1) | 2023.04.09 |
Spring MVC) 게시판을 만들어 보자 3 (0) | 2023.04.07 |
Spring MVC) 게시판을 만들어 보자 2 (1) | 2023.04.05 |
Spring MVC) 게시판을 만들어 보자 1 (1) | 2023.04.05 |