
BoardController.java
@GetMapping("list")
public Map<String, Object> list(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(value = "type", required = false) String type,
@RequestParam(defaultValue = "", value = "keyword", required = false) String keyword) {
return service.list(page, type, keyword);
}
type, keyword๋ ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์๋ก ์ ๋ฌ๋์ง ์์ ์ ์์ต๋๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก๋ null์ผ ์ ์์ต๋๋ค. keyword ๊ฒฝ์ฐ์๋ null์ด ์๋ ๋น ๋ฌธ์์ด๋ก ์ค์ ํฉ๋๋ค.BoardService.java
public Map<String, Object> list(Integer page, String type, String keyword) {
// ํ์ด์ง ๋ฒํธ
~~~~
Integer countAll = mapper.countAllWithSearch(searchType, keyword);
// ๊ฒ์์ด
return Map.of("pageInfo", pageInfo,
"boardList", mapper.selectAllPaging(offset, type, keyword));
}
BoardMapper.java
@Select("""
<script>
SELECT b.id,
b.title,
m.nick_name writer
FROM board b JOIN member m ON b.member_id = m.id
<trim prefix="WHERE" prefixOverrides="OR">
<if test="searchType != null">
<bind name="pattern" value="'%' + keyword + '%'" />
<if test="searchType == 'all' || searchType == 'text'">
OR b.title LIKE #{pattern}
OR b.content LIKE #{pattern}
</if>
<if test="searchType == 'all' || searchType == 'nickName'">
OR m.nick_name LIKE #{pattern}
</if>
</if>
</trim>
ORDER BY b.id DESC
LIMIT #{offset}, 10
</script>
""")
List<Board> selectAllPaging(Integer offset, String searchType, String keyword);
@Select("""
<script>
SELECT COUNT(b.id)
FROM board b JOIN member m ON b.member_id = m.id
<trim prefix="WHERE" prefixOverrides="OR">
<if test="searchType != null">
<bind name="pattern" value="'%' + keyword + '%'" />
<if test="searchType == 'all' || searchType == 'text'">
OR b.title LIKE #{pattern}
OR b.content LIKE #{pattern}
</if>
<if test="searchType == 'all' || searchType == 'nickName'">
OR m.nick_name LIKE #{pattern}
</if>
</if>
</trim>
</script>
""")
Integer countAllWithSearch(String searchType, String keyword);
1. selectAllPaging :
board ํ
์ด๋ธ๊ณผ member ํ
์ด๋ธ์ ์กฐ์ธํ์ฌ ๊ฒ์๋ฌผ์ ID, ์ ๋ชฉ, ์์ฑ์ ๋๋ค์์ ์กฐํํฉ๋๋ค.<trim> :prefix : WHERE ์ ์ ์กฐ๊ฑด๋ถ๋ก ์ถ๊ฐํฉ๋๋ค.prefixOverrides : ๋ถํ์ํ OR์ ์ ๊ฑฐํฉ๋๋ค.<if> : searchType์ด null์ด ์๋ ๋ keyword๋ฅผ LIKE ๊ตฌ๋ฌธ์ ๋ง๊ฒ ๋ณํํ๊ณ searchType์ด all(์ ์ฒด)์ด๊ฑฐ๋ text(๊ธ(์ ๋ชฉ+๋ด์ฉ))์ด๋ฉด ์ ๋ชฉ ๋๋ ๋ด์ฉ์์ ํค์๋๋ฅผ ์กฐํํฉ๋๋ค. searchType์ด all(์ ์ฒด)์ด๊ฑฐ๋ nickName(๋๋ค์)์ด๋ฉด ๋๋ค์์์ ํค์๋๋ฅผ ์กฐํํฉ๋๋ค. 2. countAllWithSearch :
BoardList.jsx
export function BoardList() {
const navigate = useNavigate();
const [boardList, setBoardList] = useState([]);
const [pageInfo, setPageInfo] = useState({});
const [searchParams] = useSearchParams();
const [searchType, setSearchType] = useState("all");
const [searchKeyword, setSearchKeyword] = useState("");
useEffect(() => {
axios.get(`/api/board/list?${searchParams}`).then((res) => {
setBoardList(res.data.boardList);
setPageInfo(res.data.pageInfo);
});
setSearchType("all");
setSearchKeyword("");
const typeParam = searchParams.get("type");
const keywordParam = searchParams.get("keyword");
if (typeParam) {
setSearchType(typeParam);
}
if (keywordParam) {
setSearchKeyword(keywordParam);
}
}, [searchParams]); //์์กด์ฑ(dependency)๊ฐ ์๋ค๋ฉด ๋ณ๊ฒฝ๋ ๋๋ง๋ค ํจ์๋ฅผ trigger๋ฅผ ํฉ๋๋ค.
// ์ด ํ์ด์ง ๋ฒํธ
const pageNumbers = [];
for (let i = pageInfo.leftPageNumber; i <= pageInfo.rightPageNumber; i++) {
pageNumbers.push(i);
}
function handleSearchClick() {
navigate(`/?type=${searchType}&keyword=${searchKeyword}`);
}
function handlePageButtonClick(pageNumber) {
searchParams.set("page", pageNumber);
navigate(`/?${searchParams}`);
}
return (
<Box mt={"30px"}>
<Box>๊ฒ์๋ฌผ ๋ชฉ๋ก</Box>
<Box>
{boardList.length === 0 && <Center>์กฐํ๋ ๊ฒฐ๊ณผ๊ฐ ์์ต๋๋ค.</Center>}
{boardList.length > 0 && (
<Table>
<Thead>
<Tr>
<Th>NO</Th>
<Th>์ ๋ชฉ</Th>
<Th>
<FontAwesomeIcon icon={faUserPen} />
</Th>
</Tr>
</Thead>
<Tbody>
{boardList.map((board) => (
<Tr
cursor={"pointer"}
_hover={{ bgColor: "gray.200" }}
onClick={() => navigate(`/board/${board.id}`)}
key={board.id}
>
<Td>{board.id}</Td>
<Td>{board.title}</Td>
<Td>{board.writer}</Td>
</Tr>
))}
</Tbody>
</Table>
)}
</Box>
{/*๊ฒ์*/}
<Center>
<Box mt={"30px"}>
<Flex>
<Box>
<Select
value={searchType}
onChange={(e) => setSearchType(e.target.value)}
>
<option value={"all"}>์ ์ฒด</option>
<option value={"text"}>๊ธ(์ ๋ชฉ+๋ด์ฉ)</option>
<option value={"nickName"}> ์์ฑ์</option>
</Select>
</Box>
<Box>
<Input
value={searchKeyword}
onChange={(e) => setSearchKeyword(e.target.value)}
placeholder={"๊ฒ์์ด๋ฅผ ์
๋ ฅํ์ธ์."}
/>
</Box>
<Box>
<Button onClick={handleSearchClick}>
<FontAwesomeIcon icon={faMagnifyingGlass} />
</Button>
</Box>
</Flex>
</Box>
</Center>
{/*ํ์ด์ง๋ค์ด์
*/}
<Center>
<Box mt={"30px"}>
{/*๋ง์ฝ ์ด์ ๋ฒํผ์ด ๋ณด์ด๋ฉด ์ฒ์ ๋ฒํผ๋ ๋ณด์ธ๋ค. */}
{pageInfo.prevPageNumber && (
<>
<Button onClick={() => handlePageButtonClick(1)}>
<FontAwesomeIcon icon={faAnglesLeft} />
</Button>
<Button
onClick={() => handlePageButtonClick(pageInfo.prevPageNumber)}
>
<FontAwesomeIcon icon={faAngleLeft} />
</Button>
</>
)}
{/*ํ์ด์ง ๋ฒํธ*/}
{pageNumbers.map((pageNumber) => (
<Button
mr={"10px"}
onClick={() => handlePageButtonClick(pageNumber)}
key={pageNumber}
colorScheme={
pageNumber == pageInfo.currentPageNumber ? "teal" : "gray"
}
>
{pageNumber}
</Button>
))}
{/*๋ง์ฝ ๋ค์ ๋ฒํผ์ด ๋ณด์ด๋ฉด ๋งจ๋ ๋ฒํผ๋ ๋ณด์ธ๋ค. */}
{pageInfo.nextPageNumber && (
<>
<Button
onClick={() => handlePageButtonClick(pageInfo.nextPageNumber)}
>
<FontAwesomeIcon icon={faAngleRight} />
</Button>
<Button
onClick={() => handlePageButtonClick(pageInfo.lastPageNumber)}
>
<FontAwesomeIcon icon={faAnglesRight} />
</Button>
</>
)}
</Box>
</Center>
</Box>
);
}
์กฐํ ๊ฒฐ๊ณผ ์์ ๋ ์๋ด ๋ฉ์ธ์ง : boardList์ ๊ธธ์ด๊ฐ 0๋ณด๋ค ํฌ๋ฉด ํ
์ด๋ธ์ ์กฐํ๋ ๋ชฉ๋ก์ด ๋์ค๊ณ 0 ์ด๋ฉด "์กฐํ๋ ๊ฒฐ๊ณผ๊ฐ ์์ต๋๋ค." ์๋ด ๋ฉ์ธ์ง๊ฐ ๋์ต๋๋ค.
๊ฒ์ ํ ํ์ด์ง ์ด๋ : handlePageButtonClick(pageNumber) : searchParams ๊ฐ์ฒด์ ์๋ page, type, keyword์ ๊ฐ์ด ์ฟผ๋ฆฌ ์คํธ๋ง์ผ๋ก ํฌํจ๋๊ธฐ ๋๋ฌธ์ pageNumber์ ๋ฐ๋ผ ์กฐํํฉ๋๋ค.
์ฃผ์ ๋ณต์ฌ ํ ์ ํญ์ ์ด๋ ์ input์ ๋จ๊ธฐ๊ธฐ๊ณ home ํด๋ฆญ ์ ๊ฒ์ tyep๊ณผ keyword๋ฅผ ์ด๊ธฐํํฉ๋๋ค.
const [searchType, setSearchType] = useState("all");
const [searchKeyword, setSearchKeyword] = useState("");
useEffect(() => {
axios.get(`/api/board/list?${searchParams}`).then((res) => {
setBoardList(res.data.boardList);
setPageInfo(res.data.pageInfo);
});
setSearchType("all"); //์ด๊ธฐํ
setSearchKeyword(""); //์ด๊ธฐํ
const typeParam = searchParams.get("type");
const keywordParam = searchParams.get("keyword");
if (typeParam) {
setSearchType(typeParam);
}
if (keywordParam) {
setSearchKeyword(keywordParam);
}
}, [searchParams]);
<Select
value={searchType}
onChange={(e) => setSearchType(e.target.value)}
>
<option value={"all"}>์ ์ฒด</option>
<option value={"text"}>๊ธ(์ ๋ชฉ+๋ด์ฉ)</option>
<option value={"nickName"}> ์์ฑ์</option>
</Select>
</Box>
<Box>
<Input
value={searchKeyword}
onChange={(e) => setSearchKeyword(e.target.value)}
placeholder={"๊ฒ์์ด๋ฅผ ์
๋ ฅํ์ธ์."}
/>
</Box>