board 라는 게시판 데이터가 쌓이게 되면 두가지의 api가 필요하게 된다.
첫번재는 게시판 목록을 보는 API와 두번째는 하나의 board를 상세하게 보는 API이다.
그래서 여기서는 두 개의 API를 구현한다.
- 목록보기: /api/boards
- 상세보기: /api/board/{id}
persistence 구현
id를 입력으로 해서 하나의 board를 가져오는 api와 모든 board를 가져오는 두개의 select 쿼리문을 Mybatis 쿼리 구문으로 작성한다.
1 2 3 4 5 6 7 8 9 10 11 |
@Select({"<script>", "SELECT * from board", "order by id desc", "</script>"}) List<boardvo> findBoard(); @Select({"<script>", "SELECT * from board", "where id = #{id}", "</script>"}) BoardVO findOneBoard(int id); |
Controller 구현
목록보기, 상세보기에 해당하는 두개의 api를 구현한다.
1 2 3 4 5 6 7 8 9 |
@GetMapping("/board/{id}") public BoardVO findOne(@PathVariable int id) { return boardMapper.findOneBoard(id); } @GetMapping("/boards") public List<boardvo> findAllBoard() { return boardMapper.findBoard(); } |
Test
서버를 리스타트하고 postman으로 테스트한다.
목록보기 api를 테스트한 결과이다. 목록은 여러개의 결과가 오므로 리턴타입이 array가 오게 된다.

상세 보기 api를 테스트한 결과이다. 객체 한개가 리턴되므로 리턴타입이 json 이다.

목록보기 페이지네이션
목록이 많아지면 페이지네이션을 구현해야 한다. 클라이언트에서는 페이지네이션을 구현하기 위해서 3가지 필수요소가 있다.
- 현재 페이지 넘버
- 페이지 사이즈
- 전체 갯수
3번째 페이지에 10개를 가져온다고 하면 아래와 같다.
- 현재 페이지 넘버: 3
- 페이지 사이즈: 10
- 전체 갯수: 서버로 부터 리턴받아야 함.
MariaDB에서는 LIMIT offset, size 라는 쿼리문이 있으므로 위의 값으로 변환하면 아래와 같이 게산된다.
- offset: (현재 페이저 넘버 – 1) * 페이지 사이즈 (offset는 0부터 시작) => 20
- size: 페이지 사이즈 => 10
Controller에 page_number와 page_size 두 개의 파라메터를 받고 둘다 null 이 아니면 offset을 계산해서 페이징을 수행하고, 둘 중 하나라도 값이 null 이면 전체 목록을 리턴하도록 한다.
1 2 3 4 5 6 7 8 |
@GetMapping("/boards") public List<BoardVO> findAllBoard(@RequestParam @Nullable Integer page_number, @RequestParam @Nullable Integer page_size) { Integer offset = null; if (page_number != null && page_size != null) { offset = (page_number - 1) * page_size; } return boardMapper.findBoard(offset, page_size); } |
Mapper 쿼리문에는 if 구문에서 offset과 page_size 둘 다 모두 null 이 아닐 경우에만 LIMIT 구문을 실행한다.
1 2 3 4 5 6 7 8 |
@Select({"<script>", "SELECT * from board", "order by id desc", "<if test='offset != null and page_size != null'>", "LIMIT #{offset}, #{page_size}", "</if>", "</script>"}) List<BoardVO> findBoard(Integer offset, Integer page_size); |
페이징을 테스트하면 다음과 같다.

하지만, 이렇게만 하면 페이지네이션을 구현할 수 있을까? 할수 없다.
왜냐하면 게시판 전체 갯수를 리턴하지 않았기 때문이다.
페이지를 리턴해야 하는 페이징 방식에서는 이것을 하기 위해서 게시판 데이터를 쿼리후에 다시 게시판의 전체 갯수를 쿼리하는 쿼리를 두 번 수행하고 두 개의 쿼리가 모두 성공해야 처리할 수 있도록 transaction 처리를 해야 했다.
물론 지금도 동일하게 처리할 수 있다. 하지만 SPA 프레임웍에서는 REST api를 비동기로 여러번 호출할 수 있으므로 굳이 이것을 동일한 APi로 묶을 필요는 없다. 게시판 갯수만 쿼리하는 별도의 api를 만들어서 제공하면 클라이언트가 두번 쿼리하면 되는것이다. 정답이 있는것이 아니므로 어떤것이 더 확장성과 유지보수성이 좋은것인가를 따져보고 결정하면 된다.
SRP의 원칙처럼 단일 책임을 가지도록 API를 설계하면 필요한 것들으 조합해서 사용하면 되므로 당연이 작게 쪼개는게 확장성이 더 좋다. 게시판 갯수를 리턴하는 api를 추가한다. url 은 /api/board/count 로 GET 방식으로 만든다.
1 2 3 4 |
@Select({"<script>", "SELECT count(*) from board", "</script>"}) Integer countBoard(); |
1 2 3 4 |
@GetMapping("/board/count") public Integer countBoard() { return boardMapper.countBoard(); } |