book 조회 api가, borrow가 된 책, 그렇지 않은 책도 보여줘야 한다고 판단하였습니다. 그래서, Query string이 borrowFlag가 추가되었는데요. 백단에서 어떻게 처리를 해야 할까요? 일단, Controller, Service는 인자 하나를 더 받게 바꿔야 할 거고, mapper 인터페이스에서 selectAll 부분도 바꿔야 할 겁니다.
그 전에, 제가 무엇을 해야 할 지 부터 짚고 넘어가 보겠습니다. 일단, bookClass, bookName으로 검색하는 것은 있으니까, 누군가 책을 빌려갔는지 여부를 조회 api의 쿼리 파라미터로 추가해야 합니다.
책을 빌렸는지, 안 빌렸는지 여부는, 내가 책을 빌리려고 할 때, 어떤 책을 빌릴 수 있는 지 알기 위해 필요합니다. 그래서, borrowFlag를 넣었는데요. borrow 테이블에는, 빌린 책의 id가 있습니다.
빌린 책들의 id를 보니까, 2, 3, 4, 10, 13, 14, 15, 25가 있습니다.
다음에 book 전체를 봅시다. 그러면, 2, 3, 4, 10, 13, 14, 15, 16, 18, 19, 21, 22, 25가 있는데요. 이 중에서 빌릴 수 있는 책은 16, 18, 19, 21, 22 이렇게 5권입니다.
만약에 빌릴 수 있는 책만 뽑으려고 한다면 어떻게 하면 될까요? 여러 방법이 있는데요. 저는 not exists 절을 이용하였습니다.
해당 쿼리의 결과는 book_id가 16, 18, 19, 21, 22가 뽑히네요.
물론, outer join을 이용할 수도 있습니다. book left outer join borrow on book.book_id = borrow.book_id 라고 걸었는데요. 이것은 book과 borrow를 join하는데, book에는 있는데 borrow에는 없는 결과도 보존한다는 의미입니다. book_id가 같은 거면 결과에 나타나게 했고요.
그런데, 결과는 어떻게 보존을 할까요? book_id가 book에는 있지만 borrow에 없으면 borrow쪽 필드가 null로 채워 질 겁니다. 그걸 이용합니다. 3번째 줄의 where 절은 book에는 있지만 borrow에는 없는 book_id를 뽑기 위한 것입니다.
그러면, 21, 18, 19, 16, 22 이렇게 5개가 나옵니다. 그런데, trim으로 and 연결을 한다면, left join보다는 exists 절을 활용하는 것이 좀 더 깔끔하게 떨어질 거 같네요.
이제, Controller, Service, Mapper Interface, Mapper xml을 바꿔 보겠습니다.
먼저, GET method로 들어오는 viewBook 메서드의 파라미터를 하나 더 받도록 하겠습니다. borrowFlag를 받아 봅시다. 그러면, 서비스 단에도, borrowFlag를 넘겨줘야 합니다.
bookService의 selectAll 메서드에도 borrowFlag 인자를 추가해 주겠습니다. 다음에, bookMapper의 selectAll에도, borrowFlag를 받게 설정해 보겠습니다.
12번째 줄에 보시면 Book record랑 Boolean borrowFlag 이렇게 2개를 받았음을 알 수 있어요. 여기서 중요한 것은 앞에 Param 어노테이션을 받아버렸다는 것인데요. 이 어노테이션은, 문서를 보면 파라미터의 이름을 지정하는 역할을 함을 알 수 있어요. 12번째 줄에 있는 selectAll 메서드를 보면 record 앞에 @Param("book")이라고 되어 있고, borrowFlag 앞에 @Param("borrowFlag")라고 되어 있어요. 이는, 변수 record를 이름 book으로 붙이고, borrowFlag는 이름 borrowFlag로 붙인다는 의미입니다.
즉, Book 객체 record는 book으로 mapped 되었고, borrowFlag는 borrowFlag로 mapped 된 셈입니다. 상황을 그림으로 표현하면 위와 같습니다. 이제 우리는, book의 bookName과 bookClass, 그리고 borrowFlag를 mapper file에서 써야 하는데요.
Book에는 setter, getter가 모두 있으니, 얘네들을 써 보도록 하겠습니다.
먼저, book.bookClass는 넘어온 book 객체를 book으로 제가 맵핑을 했습니다. 즉, book.bookClass는 넘어온 book 객체에서 bookClass 필드를 의미해요. 만약에, 36번째 줄에서 book_class = #{book.bookClass, 이것을 book_class = #{book.bookCass, 로 바꾸면 어떻게 될까요?
바꾸고 요청을 보내 보면, no getter for property named 'bookCass'라고 예외가 뜨는데요. 이는, Book 클래스에 해당 이름을 가진 필드가 없기 때문이에요. Book 클래스에는 bookClass라는 이름이 있습니다. 그리고 저는, Book 객체를 book으로 맵핑했습니다. Book 클래스에 bookClass 필드를 얻어오는 getter가 정의되어 있다면, book.bookClass 이런 식으로 써도 될 겁니다. 이는 넘어온 Book 객체의 bookClass 필드를 의미해요.
book.bookName은 무엇을 의미할까요? 만약에 bookName이 "C언어"이고, bookClass가 5인 객체가 넘어왔다면?
넘어온 객체의 클래스 값인 5를 의미해요.
그 다음에, 38번째 줄을 봅시다. <if> 절 안에 또 <if> 절이 있는데요. 그 안에 not 하나만 있어요. 이는, borrowFlag가 false이면 앞에 not을 붙이고, 그렇지 않으면 안 붙이기 위해서입니다. 만약에 borrowFlag가 false라면 ~ not exists ~ 이렇게 sql문이 생성이 되고, 반대로 true라면, ~ exist ~ 요렇게 생성이 될 겁니다.
최종적으로 아래와 같이 XML을 작성하시면 됩니다.
딱히 어려운 부분은 없는 거 같아요. book.bookClass 같은 것이 생소할 뿐입니다.
이제 제대로 동작하나 확인해 봅시다.
bookClass가 5이면서, borrowFlag가 false인 책을 찾으라는 요청을 보냈습니다.
16, 18, 19, ... 가 잘 나왔음을 볼 수 있어요.
borrowFlag가 true인 것만 모두 뽑아오는 요청은 어떨까요? 이것도 제대로 수행될까요?
2, 3, 4, 10, ... 이 잘 출력됨을 확인할 수 있습니다.
'웹 > 스프링부트' 카테고리의 다른 글
스프링 시큐리티에서 많이 이용되는 ant 경로 패턴에 대해 알아봅시다. (0) | 2021.08.24 |
---|---|
인가와 관련된 preauthorized 어노테이션을 사용해 봅시다. (0) | 2021.08.21 |
mybatis useGeneratedKeys를 이용해서 auto_increment 값을 얻어옵시다. (3) | 2021.08.13 |
mybatis trim 태그를 prefixoverrides를 이용해서 잘 써먹어 봅시다. (2) | 2021.07.28 |
mybatis mapper location 설정을 쉽고 빠르게 해 봅시다. (0) | 2021.07.26 |
최근댓글