๊ณ ๊ฐ ๋ช ๋จ ํ๋ฅผ ๋ง๋ค์๋ค.
๊ทธ๋ผ ์ด์ 10๋ช ์ ์ ๋ณด๋ง ๋ณด์ด๋๋ก
๋ง๋ค๊ณ ์ถ๋ค.
1. ๋์ํ๋ฉด
2. ํ์ด์ง๋ค์ด์
๊ฒ์ํด์ ์ฐพ์๋ณธ ํ์ด์ง๋ค์ด์ ์, ๋ฐฉ๋ํ ์ ๋ณด ์ค ์ฌ์ฉ์์๊ฒ ํ์ํ ์ ๋ณด๋ฅผ ์ ๋ณํ์ฌ ์ผ๋ถ๋ถ๋ง ๋ณด์ฌ์ฃผ๋ ๊ธฐ์ ์ ๋งํ๋ ๊ฒ ๊ฐ๋ค. ์๋ฌด๋๋ ๊ตฌ๊ธ์ ๊ฒ์ํ์ ๋ ๊ตฌ๊ธ์ด ๊ฐ์ง๊ณ ์๋ ๋ชจ๋ ์ ๋ณด๋ฅผ ๋ค ๋ณด์ฌ์ค๋ค๋ฉด ์๋ง ์ฌ์ฉ์์ ๋ฉ๋ชจ๋ฆฌ๊ฐ ๊ฐ๋นํ์ง ๋ชปํ ๊ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ ๋ฌด์๋ณด๋ค ๊ธฐ๋ฅ์ ์๋ฏธ๊ฐ ์์ด์ง ๋ฏ ํ๋ค. ๊ทธ๋์ ์ด ํ์ด์ง๋ค์ด์ ๊ธฐ๋ฅ์ ํ์ฉํ์ฌ ์ฌ์ฉ์์ ๋ฉ๋ชจ๋ฆฌ์ ์๊ฐ์ ์๋ผ๋ ๋ฏ ํ๋ค.
์ผ๋จ ๋ด๊ฐ ํ ํ๋ก์ ํธ๋ ์ด๋ฏธ ๊ณ ๊ฐ ์ ๋ณด๊ฐ ๊ธฐ์ ๋ ์ํ์ด๊ณ ๊ทธ๊ฑธ ๊ด๋ฆฌํ๋ ๊ฒ์ด๋ค. ๊ฑฐ๊ธฐ๋ค ๊ณ ๊ฐ ๋ช ๋จ์ด ๊ทธ๋ฆฌ ๋ฐฉ๋ํ ์์ด ์๋๋ค. ๊ทธ๋์ ๊ทธ๋ฆฌ ์ด๋ ต์ง ์์ ํ์ด์ง๋ค์ด์ ์ ๊ตฌํํด๋ ๋๊ฒ ๋ค๋ ์๊ฐ์ ํ๋ค. ์๋ ๋งํฌ๋ฅผ ํ๋ ๊ฑธ์ด๋๋๋ฐ ๊ต์ฅํ ๋ค์ํ ๋์์ธ์ ์ค๋ช ํด์ฃผ์ จ๋ค. ๊ฑฐ๊ธฐ์ ๋๋ [์ด์ - ์ดํ]๋ก ํ์ด์ง๋ฅผ ๋๊ธธ ์ ์๋ ํํ์ ํ์ด์ง๋ค์ด์ ์ ๋ง๋ค์ด๋ณด๊ธฐ๋ก ์๊ฐํ๋ค. ๊ทธ๋ฆฌ๊ณ ํ์ํ ๊ฒฝ์ฐ ๊ฒ์์ ํด์ ๊ณ ๊ฐ์ ์ฐพ์ผ๋ฉด ๋๊ธฐ ๋๋ฌธ์ ๊ตณ์ด ์ซ์๋ก ํ์ด์ง๋ฅผ ๋๋ฅด๋ ํํ๊ฐ ํ์ํ์ง ์๋ค๊ณ ์๊ฐํ๋ค.
๊ฒฐ๋ก ์ ์๋ฒ๋ก๋ถํฐ ๊ณ ๊ฐ์ ๋ํ ์ ๋ณด๋ฅผ ๋ชจ๋ ๋ฐ์์ฌ ๊ฒ์ด๋ค. ๊ทธ๋ฌ๋ฉด ํ๋ก ํธ์์ 10๊ฐ์ฉ ์ฌ๋ผ์ด์ฑํด์ ๋ณด์ฌ์ฃผ๋ ๋ฐฉ์์ผ๋ก ํด๋ณผ ๊ฒ์ด๋ค.
- ํ์ด์ง๋ค์ด์ ๋์์ธ
UI ๋์์ธ ๊ฐ์ด๋ : Pagination
ํ์ด์ง๋ค์ด์ , ๋ฌดํ ์คํฌ๋กค, ๋ ๋ณด๊ธฐ ๋ฒํผ์ ๊ดํ์ฌ UI cheat sheet: pagination, infinite scroll and the load more button ์ฝํ ์ธ ๊ฐ ํฌํจ๋ UI๋ฅผ ์ค๊ณํ๋ ๊ฒฝ์ฐ ์ฝํ ์ธ ๊ฐ ๋ง์ ๋๋ ์ด ์ธ ๊ฐ์ง ํจํด ์ค ํ๋๋ฅผ ์ ํ
brunch.co.kr
- ํ์ด์ง๋ค์ด์ ๋์ ์๋ฆฌ
ํ์ด์ง๋ค์ด์ (Pagination)
Pagination์ ๊ฐ๋ Pagination์ด๋, ๋ง์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ถ์ ์ผ๋ก ๋ถ๋ฌ์ค๋ ๊ธฐ์ ์ ์๋ฏธํ๋ค. ์ฐ๋ฆฌ๊ฐ ์ผ๋ฐ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ฌ ๋, ๋ง์ฝ์ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๊ฒ ๋๋ฉด ๋งค์ฐ ๋นํจ์จ์ ์ผ ๊ฒ์ด๋ค. ๋ฐ์ด
nx006.tistory.com
- ์คํ์ ๊ธฐ๋ฐ ํ์ด์ง๋ค์ด์
์ปค์ ๊ธฐ๋ฐ ํ์ด์ง๋ค์ด์ (Cursor-based-pagination) vs ์คํ์ ๊ธฐ๋ฐ ํ์ด์ง ๋ค์ด์ (offset-based-pagination
Pagination? ํ์ ๋ ๋คํธ์ํฌ ์์์ ํจ์จ์ ์ผ๋ก ํ์ฉํ๊ธฐ ์ํด ํน์ ํ ์ ๋ ฌ ๊ธฐ์ค์ ๋ฐ๋ผ ๋ฐ์ดํฐ๋ฅผ ๋ถํ ํ์ฌ ๊ฐ์ ธ์ค๋ ๊ฒ์ด๋ค. ์๋ฒ์ ์ ์ฅ์์๋ ํด๋ผ์ด์ธํธ์ ์ ์ฅ์์๋ ํน์ ํ ์ ๋ ฌ ๊ธฐ์ค์ ๋ฐ
0soo.tistory.com
3. ์ ์ฒด ์ฝ๋
// ํ์ด์ง state
const [page, setPage] = useState(1);
const pageSize = 10;
const totalPages = Math.max(1, Math.ceil(rows.length / pageSize));
const startIndex = (page - 1) * pageSize;
const pagedRows = rows.slice(startIndex, startIndex + pageSize);
<div className="table-footnote">
<div className="pagination">
<button
type="button"
disabled={page === 1}
onClick={() => setPage((prev) => Math.max(1, prev - 1))}
>
‹
</button>
<div className="page-indicator">{page} / {totalPages}</div>
<button
type="button"
disabled={page === totalPages}
onClick={() => setPage((prev) => Math.min(totalPages, prev + 1))}
>
›
</button>
</div>
</div>
</div>
4. ์ฝ๋
const [page, setPage] = useState(1);
page์ ์ด๊ธฐ๊ฐ์ 1๋ก ์ค์ ํ๋ค. ์๋ง ํ์ด์ง๊ฐ ๋์ด๊ฐ๋ฉด setPage๋ก 1์ด ์๋ 2, 3์ผ๋ก ๋ณ๊ฒฝ๋ ๊ฒ์ด๋ค.
const pageSize = 10;
10๊ฐ์ ์ ๋ณด๋ฅผ ๋ณด์ฌ์ฃผ๊ธฐ ์ํด pageSize๋ 10์ผ๋ก ์ง์ ํ๋ค. ๊ทธ๋ฐ๋ฐ ์ด ์ฝ๋๋ฅผ ์ ๋๋ค๊ณ ํด์ 10๊ฐ์ ์ ๋ณด๊ฐ ๋ณด์ด๋ ๊ฒ์ ์๋๋ค. ์ ํํ๋ ๋ค์ ์ฝ๋์์ ์ฐ๊ธฐ ์ํด ์ค์ ํ ๋ณ์์ด๋ค.
const totalPages = Math.max(1, Math.ceil(rows.length / pageSize));
์ ์ฒด ํ์ด์ง๋ฅผ ์ด๋๊น์งํ ๊ฑด์ง ์ ํ๋ ๋ถ๋ถ์ด๋ค. pageSize๋ ์์์ 10์ผ๋ก ์ ํ๋ค. ๊ทธ๋ฆฌ๊ณ rows.length๋ ๊ฐ์ ธ์จ ์ ์ฒด ๊ณ ๊ฐ ๋ชฉ๋ก์ ๊ธธ์ด๊ฐ ๋๋ค. ๊ทธ๋ฌ๋ฉด ์๋ฅผ ๋ค์ด 57๊ฐ์ ์ ๋ณด๊ฐ ์๋ค๊ณ ๊ฐ์ ํด๋ณด์. rows.length๊ฐ 57์ด๊ณ 10์ผ๋ก ๋๋๋ฉด 5.7์ด ๋๋ค.
Math.ceil์ ๋ฐ์ฌ๋ฆผ์ ๋งํ๋ค. 5.7์ ๋ฐ์ฌ๋ฆผํ๋ฉด 6์ด ๋๋ค. ๊ทธ๋ฌ๋ฉด ์ฝ๋๋ฅผ ๋ค์ ์ ์ด๋ณด์. Math.max(1, 6)์ด ๋๋ค. ์ด ๋ง์ ์ ์ด๋ totalPages๊ฐ 1 ๋ฐ์ผ๋ก ๋จ์ด์ง์ง ์๋๋ค๋ ๊ฒ์ด๋ค. ์ ์ด๋ ๊ฒ ์ค์ ํ๋๋ฉด ํ๋ฉด์ ๋ณด๋ฉด 1/4 ์ด๋ฐ์์ผ๋ก ์ ํ์๋ค. ํ์ด์ง๊ฐ ์ด์ ์ ๋๊ธฐ๋ค๊ฐ 1๋ณด๋ค ๋ฐ์ธ 0์ผ๋ก ๋จ์ด์ง๋ฉด ์๋๊ธฐ ๋๋ฌธ์ ๋ฐฉ์ด ์ฐจ์์ผ๋ก ์ฝ๋๋ฅผ ์ด๋ ๊ฒ ์์ฑํ๋ค.
const startIndex = (page - 1) * pageSize;
startIndex๋ ๊ณ ๊ฐ ์ ๋ณด๋ฅผ ์ด๋์ ์๋ฅผ๊ฑด์ง ์ ํ๋ ๋ณ์์ด๋ค. 57๊ฐ์ ์ ๋ณด๊ฐ ์๋ค๊ณ ๊ฐ์ ํ๋ฉด ์ฒ์์ 0๋ฒ์งธ์์ ๋ณด์ฌ์ค์ผํ๋ค. ๊ทธ๋ฆฌ๊ณ ๋๋ฒ์งธ ํ์ด์ง์์ 10์์ ๋ 10๊ฐ์ ์ ๋ณด๋ฅผ ๋ณด์ฌ์ค์ผํ๋ค. ์ด๋ฐ์์ผ๋ก ์ด๋ค ์ ๋ณด๋ถํฐ ๋ณด์ฌ์ค์ง ์ ํ๊ธฐ ์ํด startIndex๋ฅผ ์ค์ ํด์ค์ผํ๋ค.
page๋ 1์ด ์ด๊ธฐ๊ฐ์ผ๋ก ์ค์ ํ๋ค. page๊ฐ 1์ธ ๊ฒฝ์ฐ, startIndex๋ (1-1) x 10์ผ๋ก 0์ด ๋๋ค. page๊ฐ 2์ธ ๊ฒฝ์ฐ, startIndex๋ (2-1) x 10์ผ๋ก 10์ด ๋๋ค. page๊ฐ 3์ธ ๊ฒฝ์ฐ, startIndex๋ (3-1) x 10์ผ๋ก 20์ด ๋๋ค.
const pagedRows = rows.slice(startIndex, startIndex + pageSize);
์ด์ ์์ ์ ์ ๋ณ์๋ค๋ก ์ฌ๋ผ์ด์ฑ์ ํด๋ณด์.
startIndex๊ฐ 0์ด๋ผ๋ฉด, rows.slice(0, 10);์ด ๋๋ค. ๊ทธ๋๊น ์ ์ฒด ๊ณ ๊ฐ ๋ชฉ๋ก์ ๋ค๊ณ ์จ rows์์ 0๋ฒ์งธ๋ถํฐ 9๋ฒ์งธ ์๋ฅด๊ฒ ๋๋ค. ์ด๋ ์ฃผ์ํ ์ ์ ์ฌ๋ผ์ด์ฑ์ startIndex+pageSize -1๋ก ๋ง์ง๋ง์ผ๋ก ํฌํจํ๋ ์ธ๋ฑ์ค๊ฐ ๋๋ค. ๊ทธ๋์ 9๋ฒ์งธ๊น์ง ๋ณด์ฌ์ฃผ๊ฒ ๋๋ค.
๊ทธ๋ ๊ฒ ๋๋ ๋ถ๋ถ์ pagedRows๊ฐ ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ฌ๋ผ์ด์ฑ์ ์๋ ๋งํผ ์๋ผ์ฃผ๊ธฐ ๋๋ฌธ์ ๋ง์ง๋ง ํ์ด์ง๊ฐ 10๊ฐ๊ฐ ์๋๋ค๊ณ ํด๋ ๋ฌธ์ ์๋ค.
<button
type="button"
disabled={page === 1}
onClick={() => setPage((prev) => Math.max(1, prev - 1))}
>
‹
</button>
< ๋ฒํผ์ ํ์ด์ง๊ฐ 1์ด๋ฉด ์ด์ ์ ๋๋ฅผ ํ์๊ฐ ์๊ธฐ์ disabled๋ก ๋๋ฅผ ์ ์๊ฒ ์ฒ๋ฆฌํ๋ค. setPage((prev) => Math.max(1, prev - 1)) ์ด ์ฝ๋๋ ์๋์ ๋ ์์ธํ ์ดํด๋ณด๊ฒ ๋ค.
<div className="page-indicator">{page} / {totalPages}</div>
ํ์ฌ ํ์ด์ง์ ์ ์ฒด ํ์ด์ง๋ฅผ ๋ํ๋ด๊ธฐ ์ํด ์ ์๋ค. {ํ์ฌ ํ์ด์ง} / {์ ์ฒด ํ์ด์ง}
<button
type="button"
disabled={page === totalPages}
onClick={() => setPage((prev) => Math.min(totalPages, prev + 1))}
>
‹
</button>
< ๋ฒํผ์ totalPages ๋ณด๋ค ๋ ๋ค๋ก ๊ฐ ์ ์๋๋ก disabled๋ก ๋ํ๋๋ค.
5. ํจ์ ํํ๋ก ์ํ๋ฅผ ์ ๋ฐ์ดํธ
setPage((prev) => Math.max(1, prev - 1))
setPage๋ useState์์ ์ค์ ํ ๊ฒ์ด๋ค. ์ด๋ฐ์์ผ๋ก ํจ์ ํํ๋ก ์ ๋ฐ์ดํธํ๋ ๊ฒฝ์ฐ, ๋ฆฌ์กํธ์์ ๊ฐ์ฅ ์ต์ ์ ์ด์ ์ํ๊ฐ์ prev์ ๋ฃ์ด์ค๋ค. ์ด๋ ๊ฒ ์ฝ๋๋ฅผ ์์ฑํ๋ฉด ์ข์ ์ ์ด ์ฌ์ฉ์๊ฐ ์ฐ์ํด์ ๋น ๋ฅด๊ฒ ๋ฒํผ์ ๋๋ฅด๋ ๊ฒฝ์ฐ ์ฆ๊ฐ๊ฐ ๊ผฌ์ด์ง ์๊ณ ๋ฌธ์ ์์ด ์ฒ๋ฆฌ๋ ์ ์๋ค.
์ฌ์ค ์ด ์ฝ๋๋ AI๊ฐ ์์ ํด์ค ์ฝ๋์ด๋ค. ๊ทธ๋ผ ์ ์ ์ง์ ์์ฑํ ์ฝ๋๋ ์ด๋ค ์ฝ๋์๋์ง ๋น๊ตํด๋ณด๊ฒ ๋ค.
setPage(page + 1)
์ด๋ ๊ฒ ๋ฐ๋ก ๊ฐ์ ๋ฃ๋ ํ์์ผ๋ก ์ฝ๋๋ฅผ ์์ฑํ๋ค. ์ด ์ฝ๋๋ ์ง๊ธ ๋ด๊ฐ ๋ณด๊ณ ์๋ ํ์ด์ง์์ +1์ ํ ๊ฒ์ด๋ค.
ํ์คํ AI๊ฐ ์์ ํด์ค ์ฝ๋๊ฐ ์์ ์ฑ์ด ๋์ ๋ฏ ํ๋ค. ์ด๋ ๊ฒ ์๋ก์ด ๊ฑธ ๋ ํ๋ ๋ฐฐ์๊ฐ๋ค.