Blog 게시판 만들기 (11) - 검색 기능 만들기

bethe·2022년 9월 12일
0

Springboot

목록 보기
43/46

📝 search UI

참고 : https://www.w3schools.com/bootstrap5/bootstrap_navbar.php

<div class="container"> 안에

	<form class="d-flex">
		<input class="form-control me-2" type="text" placeholder="Search">
		<button class="btn btn-primary" type="button">Search</button>
	</form>

를 넣자. container class가 화면의 70~80%를 차지하는 반응형인데, container 밖에 폼을 넣으면 반응형 적용이 안 된다.


input태그(inline-block)임에도 서치 칸이 길쭉한 이유는 form-control이 block속성이라 그렇다.
flex는 fixed나 absolute와 다르게 자기 부모의 크기를 벗어날 수 없으므로 form을 감싸는 박스를 작게 만들어 크기를 지정할 수 있다.

		<div style="width: 300px">
			<form class="d-flex">
				<input class="form-control me-2" type="text" placeholder="Search">
				<button class="btn btn-primary" type="button">Search</button>
			</form>
		</div>

오른쪽 배치를 위해 <div>를 하나 더 만들어 flex적용.
button 타입도 submit으로 수정

	<br/>
	<div class="d-flex justify-content-end">
		<div style="width: 300px">
			<form class="d-flex">
				<input class="form-control me-2" type="text" placeholder="Search">
				<button class="btn btn-primary" type="submit">Search</button>
			</form>
		</div>
	</div>



📝 update 기능 만들기

1. main.jsp

1) method 지정

select할 것이므로 GET 요청이다.

<form class="d-flex" method="get" action="/keyword=?">

2) action 지정

쿼리스트링의 경우 form태그에서 사용시 주소에 적지 않아도 된다.
form태그의 input타입의 name에 걸면 된다. GET요청이기 때문에 데이터를 주소에 들고 가기 때문이다. (localhost:8000/?keyword=사용자입력값이 되는 것)

			<form class="d-flex" method="get" action="/">
				<input class="form-control me-2" type="text" placeholder="Search" name="keyword">
				<button class="btn btn-primary" type="button">Search</button>
			</form>

2. Controller : if else문으로 실행될 DAO 설정

이제 페이지 주소는 이런 형태가 된다. ?page=0&keyword=스프링
검색을 할 경우 주소에 page값은 넘기지 않게 되므로 if문에 의해 page는 디폴트값 0이 된다. (받고싶으면 main.jsp에 <input type="hidden" value="0" name="page"/>를 넣으면 된다.)

keyword가 null || keyword.isEmpty()일 때와 그렇지 않을 때 실행되는 DAO가 달라야 한다.

    @Override
    public boolean isEmpty() {
        return value.length == 0;
    }
  • 특징 : " "(공백)을 blank(공백인정X)로 보지 않고 비어있지 않다고 본다.
    StringUtils.isEmpty("") = true
    StringUtils.isEmpty(" ") = false
// 1번째 ?page=0&keyword=스프링
// 검색을 하면 page 값을 안 넘기므로 디폴트값 0이 들어옴
@GetMapping({ "/", "/boards" })
	public String getBoardList(Model model, Integer page, String keyword) { 
		if (page == null) {
			page = 0;
		}
		Integer startNum = page * 3;

		if (keyword == null || keyword.isEmpty()) {
			List<MainView> boardsList = boardsDao.findAll(startNum);
			PagingView paging = boardsDao.paging(page);

			// DB에서 쿼리를 만들어 넣어주기 힘드니까 Java에서 나머지 변수들을 설정

			paging.makeBlockInfo();

			model.addAttribute("boardsList", boardsList);
			model.addAttribute("paging", paging);
			return "boards/main";
		} else {
			
		}
		
	}

else문은 동적쿼리를 만든 뒤 작성해보겠다.

3. 동적쿼리

keyword가 !=null이면 Search를 한 것이므로
findAll 쿼리와 paging 쿼리를 수정해주어야 한다.

[ 방법은 2가지 : 1. 쿼리 하나 더 만들기 2. 동적 쿼리 사용]
1. findAll쿼리는 새 쿼리를 만들어서 적용해보고
2. paging은 동적쿼리를 사용해 적용해보겠다.

1) findSearch 쿼리 작성

	<select id="findSearch"
		resultType="site.metacoding.red.domain.boards.mapper.MainView">
		SELECT b.id, b.title, u.username
		FROM boards b
		INNER JOIN
		users u
		ON b.usersId = u.id
        WHERE title like '%'||#{keyword}||'%'
		ORDER BY b.id DESC
		OFFSET #{startNum} ROWS
		FETCH NEXT 3 ROWS ONLY
	</select>

2) paging 쿼리 수정

	<select id="paging"
		resultType="site.metacoding.red.domain.boards.mapper.PagingView">
		SELECT totalCount,
		totalPage,
		currentPage,
		decode(currentPage, 0, 1, 0) first,
		decode(currentPage, totalPage-1, 1,
		0) last
		FROM
		(
		select count(*) totalCount, ceil(count(*)/3) totalPage,
		#{page} currentPage, 0
		first, 0 last
        FROM boards
		<if test="keyword != null">
			WHERE title like '%'||#{keyword}||'%'
		</if>
		)
	</select>

3. Controller : 완성

else문 채우기, paging 파라매터값 수정

	@GetMapping({ "/", "/boards" })
	public String getBoardList(Model model, Integer page, String keyword) { 
		if (page == null) {
			page = 0;
		} 
		Integer startNum = page * 3;

		if (keyword == null  || keyword.isEmpty() ) {
			List<MainView> boardsList = boardsDao.findAll(startNum);
			PagingView paging = boardsDao.paging(page, null);
            //키워드는 무조건 받게 되어 있으므로 keyword가 없을 땐 null이라도 받게 해줘야 함

			// DB에서 쿼리를 만들어 넣어주기 힘드니까 Java에서 나머지 변수들을 설정

			paging.makeBlockInfo();

			model.addAttribute("boardsList", boardsList);
			model.addAttribute("paging", paging);

		} else {
			List<MainView> boardsList = boardsDao.findSearch(startNum, keyword);
			PagingView paging = boardsDao.paging(page, keyword); // 또는 keyword대신 null 넣기

			// DB에서 쿼리를 만들어 넣어주기 힘드니까 Java에서 나머지 변수들을 설정

			paging.makeBlockInfo();

			model.addAttribute("boardsList", boardsList);
			model.addAttribute("paging", paging);
		}
		return "boards/main"; 
	}

4.DAO 수정

파라미터가 두개일 경우 Mybatis에서 인식을 하지 못하기 때문에 @param을 사용한다.
@Param 어노테이션을 붙이면 본인이 원하는 명으로 mapper에서 사용할 수 있다.

public interface BoardsDao {

	public PagingDto paging(@Param("page") Integer page, @Param("keyword") String keyword);
	public void insert(Boards boards); 
	public Boards findById(Integer id);
	public List<MainDto> findAll(int startNum);
	public List<MainDto> findSearch(@Param("startNum") int startNum, @Param("keyword") String keyword);
	public void update(Boards boards); 
	public void delete(Integer id);
}

5. jsp - paging 수정

<ul class="pagination">
			<li class='page-item ${paging.first ? "disabled" : ""}'><a class="page-link"
				href="?page=${paging.currentPage-1}&keyword=${paging.keyword}">Prev</a></li>
			<c:forEach var="num" begin="${paging.startPageNum}" end="${paging.lastPageNum}" step="1">
				<li class='page-item ${paging.currentPage == num-1 ? "active" : ""}'><a class="page-link"
					href="?page=${num-1}&keyword=${paging.keyword}">${num}</a></li>
			</c:forEach>
			<li class='page-item ${paging.last ? "disabled" : ""}'><a class="page-link"
				href="?page=${paging.currentPage+1}&keyword=${paging.keyword}">Next</a></li>
		</ul>

❓❗️그런데 이 경우 keyword로 검색은 되나 다음 페이지로 넘어갈 경우 keyword값이 keyword=로 null이 되어 전체검색 페이지로 넘어간다.

🔔 PagingView paging = boardsDao.paging(page, keyword);에서 paging에 keyword가 담겨있지 않기 때문이다. (DB에서 keyword가 select되지 않기 때문) Mapper는 작동되어 keyword로 검색한 페이지가 나오지만 <-> 검색된 페이지의 Page Bar에는 Model에 담겨 전송될 데이터 중 keyword값이 없어 keyword=값으로 나오는 것이다.
💻 따라서 PagingView에 keyword 변수를 만들어주고, makeBlockInfo()keyword값을 직접 받아 pagingkeyword값이 생기도록 한 뒤, Model로 View에 전달하도록 해주어야 한다.


6. keyword 추가

1) PagingView 코드

package site.metacoding.red.domain.boards.mapper;

import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
public class PagingView {
	private String keyword;
	private Integer currentBlock; // 변수
	private Integer blockCount; // 상수 / 한 블락의 페이지 넘버 수 개수(5) 1-5, 6-10
	private Integer startPageNum; // 변수 1 -> 6 -> 11
	private Integer lastPageNum; // 변수 5 -> 10 -> 15
	private Integer startNum;
	private Integer totalCount;
	private Integer totalPage;
	private Integer currentPage;
	private boolean isLast; // getter가 만들어지면 getisLast()가 아닌 isLast() 이름으로 만들어짐 -> el표현식에서는 last로 찾아짐
	private boolean isFirst; // getter가 만들어지면 getisFirst()가 아닌isFirst() 이름으로 만들어짐 -> el표현식에서는 last로 찾아짐

	public void makeBlockInfo(String keyword) {
		this.keyword = keyword;
		this.blockCount = 5;

		this.currentBlock = currentPage / blockCount;
		this.startPageNum = 1 + blockCount * currentBlock;
		this.lastPageNum = (startPageNum + blockCount) -1;

		if (totalPage < lastPageNum) {
			this.lastPageNum = totalPage;
		}
	}
}

2) Controller 코드

	@GetMapping({ "/", "/boards" })
	public String getBoardList(Model model, Integer page, String keyword) { 
		if (page == null) {
			page = 0;
		} 
		Integer startNum = page * 3;

		if (keyword == null  || keyword.isEmpty() ) {
			List<MainView> boardsList = boardsDao.findAll(startNum);
			PagingView paging = boardsDao.paging(page, null);
            //키워드는 무조건 받게 되어 있으므로 keyword가 없을 땐 null이라도 받게 해줘야 함

			// DB에서 쿼리를 만들어 넣어주기 힘드니까 Java에서 나머지 변수들을 설정

			paging.makeBlockInfo();

			model.addAttribute("boardsList", boardsList);
			model.addAttribute("paging", paging);

		} else {
			List<MainView> boardsList = boardsDao.findSearch(startNum, keyword);
			PagingView paging = boardsDao.paging(page, keyword); // 또는 keyword대신 null 넣기

			// DB에서 쿼리를 만들어 넣어주기 힘드니까 Java에서 나머지 변수들을 설정

			paging.makeBlockInfo();

			model.addAttribute("boardsList", boardsList);
			model.addAttribute("paging", paging);
		}
		return "boards/main"; 
	}
profile
코딩을 배우고 기록합니다. 읽는 사람이 이해하기 쉽게 쓰려고 합니다.

0개의 댓글