09009
[Spring] 예제 2 - 검색 기능 구현 본문
제목, 내용, 작성자를 기준으로 검색하는 쿼리 작성
-- 검색 (제목에 '안녕'이라는 글자가 들어있는 게시물 추출)
SELECT fb.* FROM fp_board fb
WHERE fb.title LIKE '%'|| '안녕' ||'%'
ORDER BY id DESC;
-- 닉네임 검색
-- fp_board에 값이 없으므로 조인이나 서브쿼리 이용
SELECT fb.*
FROM fp_board fb JOIN fp_member fm
ON fb.member_id = fm.id
WHERE fm.nickname LIKE '%'||'브'||'%'
;
-- 닉네임 검색 (서브쿼리)
SELECT fb.* FROM fp_board fb
WHERE (SELECT fm.nickname FROM fp_member fm WHERE fm.id = fb.member_id) LIKE '%'||'브'||'%'
문자열 연산 쿼리
✍ mainPage.jsp
검색창에 해당 항목을 기준으로 검색하면 페이지는 mainPage에서 mainPage로 이동한다.
- 웹 링크에 검색어가 나오고 키워드 자체가 보안이 중요한 항목이 아니기 때문에 get 방식으로 요청해도 상관없다.
- name 속성은 select 태그에 붙이도록 한다.
<form action="./mainPage" method="get">
<div class="row mt-3"><!-- 검색 -->
<div class="col-2">
<select name="searchType" class="form-select">
<option value="title" selected>제목</option>
<option value="content">내용</option>
<option value="nickname">작성자</option>
</select>
</div>
<div class="col-8">
<input name="searchWord" type="text" class="form-control">
</div>
<div class="col-2 d-grid">
<button class="btn btn-primary">검색</button>
</div>
</div>
</form>
내용으로 검색
✍ BoardController
parameter가 받아지지 않으면 null값으로 설정된다.( String이므로 null값 허용)
searchType에 따라 실행할 쿼리만 변경해주면 된다.
@RequestMapping("mainPage")
public String mainPage(Model model,
@RequestParam(value = "page", defaultValue = "1") int page,
String serachType,
String searchWord
) {
List<Map<String, Object>> list = boardService.getBoardList(page, searchType, searchWord);
int boardCount = boardService.getBoardCount();
int totalPage = (int)Math.ceil(boardCount / 10.0);
// 1 2 3 4 5 6 7 8 9 10
int startPage = ((page - 1) / 5) * 5 + 1;
int endPage = ((page - 1) / 5+1) * 5;
// endPage가 totalPage
if (endPage > totalPage) {
endPage = totalPage;
}
model.addAttribute("list", list); //request 객체에 담는다고 생각.
model.addAttribute("totalPage", totalPage);
model.addAttribute("currentPage", page);
model.addAttribute("startPage", startPage);
model.addAttribute("endPage", endPage);
return "board/mainPage";
}
✍ BoardServiceImpl
public List<Map<String, Object>> getBoardList(int pageNum, String searchType, String searchWord) {
// List는 object를 주로 담진 않는다. 일률적으로 담는다.
List<Map<String, Object>> list = new ArrayList<>();
List<BoardDto> boardDtoList = boardSqlMapper.selectAll(pageNum, searchType, searchWord);
for(BoardDto boardDto : boardDtoList) {
// 한 바퀴 돌때마다 map이 생성되어야 한다. map은 키가 중복되면 안된다.
Map<String, Object> map = new HashMap<>();
int memberId = boardDto.getMember_id();
System.out.println(memberId);
MemberDto memberDto = memberSqlMapper.selectById(memberId);
System.out.println(memberDto);
map.put("memberDto", memberDto);
map.put("boardDto", boardDto);
list.add(map);
}
return list;
}
mapper에서 올바르지 않은 코드
public List<BoardDto> selectAll(int pageNum, String searchType, String searchWord);
✍ BoardSqlmapper.xml
mybatis에 파라미터 하나를 전송할 경우에는 상관없다
mybatis에서 2개 이상의 파라미터는 받을 수 없으므로 2개 이상을 보낼때는 param annotation을 붙여주어야 한다.
1. objectDto를 선언하여 값을 세팅후 파라미터로 넘기는 방법
2. hashMap을 생성하여 키 값 세팅하여 하나의 파라미터로 넘기는 방법이 있는데
위 두 방법을 사용하는 것보다 mybatis에서 제공하는 어노테이션을 사용하도록 한다.
수정된 올바른 코드
public List<BoardDto> selectAll(
@Param("pageNum") int pageNum,
@Param("searchType") String searchType,
@Param("searchWord") String searchWord);
검색 쿼리 다시 작성
-- 기본 쿼리
SELECT fb.*
FROM fp_board
fb INNER JOIN fp_member fm ON fm.id = fb.member_id
ORDER BY fb.id DESC
;
-- 제목에 안녕 들어간 데이터 출력
SELECT fb.*
FROM fp_board
fb INNER JOIN fp_member fm ON fm.id = fb.member_id
WHERE 1 = 1
AND fb.title LIKE '%'||'안녕'||'%'
ORDER BY fb.id DESC
;
-- 내용에 '안녕' 들어간 데이터 출력
SELECT fb.*
FROM fp_board
fb INNER JOIN fp_member fm ON fm.id = fb.member_id
WHERE 1 = 1
AND fb.content LIKE '%'||'안녕'||'%'
ORDER BY fb.id DESC
;
동적 쿼리 조건문
cdata 안에 동적 쿼리를 작성하면 컴파일 시 에러가 발생하므로 cdata의 위치를 수정해야한다.
✍ BoardSqlmapper.xml
동적쿼리 작성
<select id="selectAll" resultType="com.ja.finalproject.dto.BoardDto">
SELECT t2.* FROM (
SELECT t1.*, ROWNUM rnum FROM(
SELECT fb.*
FROM fp_board
fb INNER JOIN fp_member fm ON fm.id = fb.member_id
WHERE 1 = 1
<if test="searchType != null and searchWord != null">
<choose>
<when test="searchType == 'title'">
AND fb.title LIKE '%'|| #{searchWord} ||'%'
</when>
<when test="searchType == 'content'">
AND fb.content LIKE '%'|| #{searchWord} ||'%'
</when>
<when test="searchType == 'nickname'">
AND fm.nickname LIKE '%'|| #{searchWord} ||'%'
</when>
</choose>
</if>
ORDER BY fb.id DESC
) t1
) t2
<![CDATA[
WHERE t2.rnum >= ((#{pageNum}-1)*10)+1 AND t2.rnum <= #{pageNum}*10
]]>
</select>
otherwise는 위 상황에서는 필요없다.
검색 창에서 해당 항목으로 검색할 때 한 페이지에 나오는 게시글의 수도 달라지기 때문에
getBoardCount 메서드도 수정해주어야 한다.
✍ BoardSqlmapper
// 글 개수 가져오는 것
public int getBoardCount(@Param("searchType") String searchType, @Param("searchWord") String searchWord);
<select id="getBoardCount" resultType="int">
SELECT COUNT(*) FROM
fp_board fb INNER JOIN fp_member fm
ON fm.id = fb.member_id
</select>
분기문 작성
<select id="getBoardCount" resultType="int">
SELECT COUNT(*) FROM
fp_board fb INNER JOIN fp_member fm
ON fm.id = fb.member_id
<if test="searchType != null and searchWord != null">
<choose>
<when test="searchType == 'title'">
AND fb.title LIKE '%'|| #{searchWord} ||'%'
</when>
<when test="searchType == 'content'">
AND fb.content LIKE '%'|| #{searchWord} ||'%'
</when>
<when test="searchType == 'nickname'">
AND fm.nickname LIKE '%'|| #{searchWord} ||'%'
</when>
</choose>
</if>
</select>
✍ BoardServiceImpl
public int getBoardCount(String searchType, String searchWord) {
return boardSqlMapper.getBoardCount(searchType, searchWord);
}
✍ Boardcontroller
int boardCount = boardService.getBoardCount(searchType, searchWord);
@RequestMapping("mainPage")
public String mainPage(Model model,
@RequestParam(value = "page", defaultValue = "1") int page,
String searchType,
String searchWord
) {
List<Map<String, Object>> list = boardService.getBoardList(page, searchType, searchWord);
int boardCount = boardService.getBoardCount(searchType, searchWord);
int totalPage = (int)Math.ceil(boardCount / 10.0);
// 1 2 3 4 5 6 7 8 9 10
int startPage = ((page - 1) / 5) * 5 + 1;
int endPage = ((page - 1) / 5+1) * 5;
// endPage가 totalPage
if (endPage > totalPage) {
endPage = totalPage;
}
model.addAttribute("list", list); //request 객체에 담는다고 생각.
model.addAttribute("totalPage", totalPage);
model.addAttribute("currentPage", page);
model.addAttribute("startPage", startPage);
model.addAttribute("endPage", endPage);
return "board/mainPage";
}
페이지를 클릭하면 링크가 풀리는 문제 해결
String searchQueryString = "";
if (searchType != null && searchWord != null) {
searchQueryString += "&searchType=" + searchType;
searchQueryString += "&searchWord=" + searchWord;
}
model.addAttribute("searchQueryString", searchQueryString);
@RequestMapping("mainPage")
public String mainPage(Model model,
@RequestParam(value = "page", defaultValue = "1") int page,
String searchType,
String searchWord
) {
List<Map<String, Object>> list = boardService.getBoardList(page, searchType, searchWord);
int boardCount = boardService.getBoardCount(searchType, searchWord);
int totalPage = (int)Math.ceil(boardCount / 10.0);
// 1 2 3 4 5 6 7 8 9 10
int startPage = ((page - 1) / 5) * 5 + 1;
int endPage = ((page - 1) / 5+1) * 5;
// endPage가 totalPage
if (endPage > totalPage) {
endPage = totalPage;
}
model.addAttribute("list", list); //request 객체에 담는다고 생각.
model.addAttribute("totalPage", totalPage);
model.addAttribute("currentPage", page);
model.addAttribute("startPage", startPage);
model.addAttribute("endPage", endPage);
String searchQueryString = "";
if (searchType != null && searchWord != null) {
searchQueryString += "&searchType=" + searchType;
searchQueryString += "&searchWord=" + searchWord;
}
model.addAttribute("searchQueryString", searchQueryString);
return "board/mainPage";
}
✍ mainPage.jsp
<div class="row"><!-- 버튼 -->
<div class="col-6 mx-auto">
<nav aria-label="Page navigation example">
<ul class="pagination mb-0">
<c:choose>
<c:when test="${startPage <= 1}">
<li class="page-item disabled"><a class="page-link" href="./mainPage?page=${startPage-1}${searchQueryString}"><</a></li>
</c:when>
<c:otherwise>
<li class="page-item"><a class="page-link" href="./mainPage?page=${startPage-1}${searchQueryString}"><</a></li>
</c:otherwise>
</c:choose>
<!-- 페이지 번호 -->
<c:forEach begin="${startPage }" end="${endPage }" var="index">
<c:choose>
<c:when test="${index == currentPage}">
<li class="page-item active"><a class="page-link" href="./mainPage?page=${index }${searchQueryString}">${index }</a></li>
</c:when>
<c:otherwise>
<li class="page-item"><a class="page-link" href="./mainPage?page=${index }${searchQueryString}">${index }</a></li>
</c:otherwise>
</c:choose>
</c:forEach>
<c:choose>
<c:when test="${endPage >= totalPage}">
<li class="page-item disabled"><a class="page-link" href="./mainPage?page=${endPage+1}${searchQueryString}">></a></li>
</c:when>
<c:otherwise>
<li class="page-item"><a class="page-link" href="./mainPage?page=${endPage+1}${searchQueryString}">></a></li>
</c:otherwise>
</c:choose>
</ul>
</nav>
</div>
'Back-End > Spring' 카테고리의 다른 글
[Spring] 예제 1 연습 - 테이블 설계 (0) | 2023.05.23 |
---|---|
[Spring] 예제 2 - 파일 업로드 (0) | 2023.05.23 |
[Spring] 예제 2 - html escape (0) | 2023.05.22 |
[Spring] 예제 2 - 페이징 처리 (0) | 2023.05.22 |
[Spring] 예제 2 - (중요) 카테고리 속성을 적용한 회원가입 구현 수정 (0) | 2023.05.22 |