[Tip] cursor๊ธฐ๋ฐ ํ์ด์ง
๊ฒ์ํ ๋ง๋ค๊ธฐ ๋ฏธ์ ์ ํ๋ ์ค ๊ตฌ๊ธ, ๋ค์ด๋ฒ, ์นด์นด์ค ๋ฑ ํ์ฌ ์๋น์ค๋ฅผ ์์ฉ์ค์ธ IT ๋๊ธฐ์ ๋ค์ ์ด๋ป๊ฒ ํ์ด์ง์ ํ ๊น? ์ ๋ํด ๊ณ ๋ฏผํ๋ฉฐ ๊ธ์ ์์ฑํ๊ฒ ๋์์ต๋๋ค. ๊ธ์ ์ ๋ฐ์ ์ผ๋ก ๊น๋ฏผ์ CTO๋์ ๊ธ์ ๋ฐํ์ผ๋ก ์์ฑ๋์์ต๋๋ค.
1. offset๊ธฐ๋ฐ ํ์ด์ง
offset ๊ธฐ๋ฐ ํ์ด์ง์ ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๋ฐ์ดํฐ๊ฐ ๋ช ๋ฒ์งธ ์์น์ ์กด์ฌํ๋์ง์ ์ค์ ์ ๋ก๋๋ค. ๋ฐ์ดํฐ๋ฅผ ์ฒ์๋ถํฐ ์ค์บํด์ ํด๋น ํ์ด์ง๊น์ง ์ด๋ ํ ํ์ํ ๋ฐ์ดํฐ๋ฅผ ์ฐพ์ ๋ฐํํฉ๋๋ค. ๋ณดํธ์ ์ผ๋ก ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๋ ํ์ด์ง ๋ฐฉ์์ ๋๋ค. * Page Navigator๋ผ ๋ถ๋ฆฌ๋ ์๋์ ๊ฐ์ ๋ฐ(bar)๋ฅผ ์ฌ์ฉํฉ๋๋ค.
offset๊ธฐ๋ฐ ํ์ด์ง์ ์ฟผ๋ฆฌ๋ ์๋์ ๊ฐ์๋ฐ์, OFFSET๊ณผ LIMIT, ์ ๋ ฌ ๊ธฐ์ค์ ์ฃผ๊ณ ํด๋น ํ์ด์ง๋ฅผ ์ฐพ์์ต๋๋ค. ๋ฐ๋ผ์ ์ฐ๋ฆฌ๊ฐ ์ฐพ๋ ๋ฐ์ดํฐ๊ฐ ์กด์ฌํ๋ ํ์ด์ง๋ฅผ ์๊ณ ์๋ค๋ฉด ๊ณง๋ฐ๋ก ์ด๋ํ ์ ์์ต๋๋ค. ํ์ง๋ง ์ด๋ ์ค์๊ฐ์ผ๋ก ๋ฐ์ดํฐ๊ฐ ์ ๋ก๋ ๋๋ ์๋น์ค์์ ๋ฐ์ดํฐ์ ์ค๋ณต๊ณผ ์ฑ๋ฅ ๋ฌธ์ ๋ผ๋ ๋ ๊ฐ์ง ๋จ์ ์ด ์กด์ฌํฉ๋๋ค.
๋ฐ์ดํฐ ์ค๋ณต
ํ์ด์ง ์์ฒญ ์ฌ์ด ๋ฐ์ดํฐ์ ๋ณํ(์ถ๊ฐ/์ญ์ )๊ฐ ์๋ ๊ฒฝ์ฐ ์ค๋ณต ๋ฐ์ดํฐ๊ฐ ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค. ์๋ฅผ ๋ค์ด 1ํ์ด์ง์์ 10๊ฐ์ row๋ฅผ ๋ถ๋ฌ์์ ์ํ์ ๋ณด์ฌ์ฃผ๊ณ ์์ ๋ ์ด์์๊ฐ 5๊ฐ์ ์ํ์ ์๋ก ์ถ๊ฐํ๋ค๋ฉด ๊ณ ๊ฐ์ด 2ํ์ด์ง๋ก ๋์ด๊ฐ์ ๋ 1ํ์ด์ง์์ ๋ดค๋ ๋ค์ชฝ 5๊ฐ ์ํ์ด ์ค๋ณต์ผ๋ก ๋ณด์ผ ์ ์์ต๋๋ค. ๋ง์ฝ ๋ฐ์ดํฐ์ ์ถ๊ฐ, ์ญ์ ๊ฐ ๋น๋ฒํ ์๋น์ค๋ผ๋ฉด ๊ณ ๊ฐ๋ค์ ์ข์ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ง์ง ๋ชปํ ๊ฒ์ ๋๋ค.
์ฑ๋ฅ ๋ฌธ์
๋ํ ์ฑ๋ฅ ๋ฌธ์ ๋ ์กด์ฌํ๋๋ฐ์, ๊ตฌ๊ธ ํ์ด์ง ์ค 1์ต ๋ฒ์งธ ํ์ด์ง์ ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๋ฐ์ดํฐ๊ฐ ์กด์ฌํ๋ค๊ณ ๊ฐ์ ํ๊ฒ ์ต๋๋ค. ์ด๋ ํ ํ์ด์ง์ 10๊ฐ์ฉ ๋ฐ์ดํฐ๊ฐ ์จ๋ค๊ณ ๊ฐ์ ํ๋ฉด ์ฝ 10์ต๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ ํ 10๊ฐ์ ๋ฐ์ดํฐ๋ง ๊ฐ์ ธ์จ๋ค. ์ฆ, ์ ๋ ฌ๊ธฐ์ค(order by)์ ๋ํด ํด๋น row๊ฐ ๋ช ๋ฒ์งธ ์์์ธ์ง ์์ง ๋ชปํ๋ฏ๋ก OFFSET ๊ฐ์ ์ง์ ํ์ฌ ์ฟผ๋ฆฌ๋ฅผ ํ๋ค๊ณ ํ์ ๋ ์ง์ ๋ OFFSET๊น์ง ๋ชจ๋ ๋ง๋ค์ด ๋์ ํ ์ง์ ๋ ๊ฐฏ์๋ฅผ ์ํํ์ฌ ์๋ฅด๋ ๋ฐฉ์์ด๋ค. ๋ฐ๋ผ์ ์ฑ๋ฅ์ ์ด์ ๋น๋กํด์ ๋จ์ด์ง๊ฒ ๋๋ค. ์ด์ ๊ดํด ์๋ ์ ์ ๋ฆฌ๋ ์ฌ์ดํธ๊ฐ ์๋ค.
Why Order By With Limit and Offset is Slow - Faster Pagination in Mysql
Paging using LIMIT and OFFSET clauses in MySQL can be very slow. In this article we describe the seek method that allows a faster, more stable paging performance.
www.eversql.com
2. ์ปค์ ๊ธฐ๋ฐ ํ์ด์ง
PK๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ์ด์ง๋ฅผ ๊ตฌํ๋ ๋ฐฉ์. ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๋ฐ์ดํฐ๊ฐ '์ด๋ค ๋ฐ์ดํฐ์ ๋ค์'์ ์๋์ง์ ์ง์คํ๋ค. ์ธ์คํ ๊ทธ๋จ, ํ์ด์ค ๋ถ ๋ฑ ๋์ฉ๋ ๋ฐ์ดํฐ, ๋๊ท๋ชจ ์น ์ฌ์ดํธ์์ ์ด๋ฅผ ์ฌ์ฉํ๋ค.
/article?page=1&limit=10
SELECT * FROM article WHERE id <= {cursor} ORDER BY id DESC LIMIT {limit}
์๋๋ Offset๊ณผ Cursor๊ธฐ๋ฐ ํ์ด์ง์ ๋ํ ํจ์จ์ฑ์ ๋ํ๋ด๋ ๊ทธ๋ํ์ ๋๋ค. ์ด๋ ๋ฐ์ดํฐ๊ฐ ๋ง์ผ๋ฉด ๋ง์์๋ก ์ฑ๋ฅ์ ํฐ ์ํฅ์ ๋ผ์น๊ฒ ๋๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
์ฌ๊ธฐ์ ์ฒซ ํ์ด์ง ์กฐํํ ๋์ ๋๋ฒ์งธ ํ์ด์ง๋ถํฐ ์กฐํํ ๋ ์ฌ์ฉ๋๋ ์ฟผ๋ฆฌ๊ฐ ๋ฌ๋ผ ๋์ ์ฟผ๋ฆฌ๊ฐ ํ์ํ๋ฐ์. (์ฒซ ํ์ด์ง๋ฅผ ์กฐํํ ๋๋ ๊ธฐ์ค์ด ๋๋ id ๊ฐ์ ์ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.) ๊ทธ๋์ ์๋์ ๊ฐ์ ์ฝ๋๊ฐ ์ฌ์ฉ๋์์ต๋๋ค.
์ปค์ ๊ธฐ๋ฐ ํ์ด์ง์ ์ฌ์ฉํ๋ฉด offset pagination์ ์กด์ฌํ๋ 2๊ฐ์ง ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค. ๋ฌผ๋ก ์ปค์ํ์ด์ง๋ ๋ง๋ฅ์ ์๋๋ค.
์ผ๋จ ์ปค์๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์ ์ ๋ ฌ๋ ๊ธฐ์ค์ด ํ์ํ๋ฐ ๋ง์ฝ ์ ๋ ฌ ๊ธฐ์ค์ด ์ฌ๋ฌ๊ฐ๊ฑฐ๋ ์ปฌ๋ผ์ ๊ฐ์ด UNIQUEํ์ง ์์ ๊ฒฝ์ฐ ์ ์์ ์ธ ์๋์ ๊ธฐ๋ํ๊ธฐ ์ด๋ ต๋ค. ๋ํ ์กฐํ๋ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ํ ์กฐํ๋ง ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ ํ์ด์ง ๋๋ฒ๋ง ๊ตฌํ์ด ํ๋ค๊ณ ์ฌ๋ฌํ์ด์ง๋ฅผ ๋๊ธฐ๊ธฐ๋ ์ฝ์ง์๋ค. ์ฆ ๋๋ณด๊ธฐ ํน์ ๋ฌดํ์คํฌ๋กค ๋ฐฉ์์ ํ์ด์ง ์ฒ๋ฆฌ์ ์ต์ ํ๋์ด์๋ ์ ์ด๋ค.
- ์ธ๋ฑ์ค๊ฐ ์ ์ฉ๋ ๊ฐ์ ๋น๊ตํ๊ธฐ ๋๋ฌธ์ ํ ์ด๋ธ ํ์ค์บ์ ํ์ง ์์ต๋๋ค.
- ๋ฐ์ดํฐ ์ฐ๊ธฐ๊ฐ ๋น๋ฒํ ํ ์ด๋ธ์ด์ฌ๋ ๋ค์ ํ์ด์ง๋ค์ด์ ์กฐํ ์ ๊ฐ์ด ๋๋ฝ๋์ง ์์ต๋๋ค.
์ปค์ ๊ธฐ๋ฐ ํ์ด์ง๋ค์ด์ (Cursor-based Pagination) ๊ตฌํํ๊ธฐ
์ฌ์ค ์ฒ์์๋ ์ด ์ฃผ์ ๋ก ํฌ์คํธ๋ฅผ ์ฐ๋ ค๊ณ ํ๋๊ฑด ์๋๊ณ Apollo GraphQL ์์ ์ปค์ ๊ธฐ๋ฐ ํ์ด์ง๋ค์ด์ ๊ตฌํ ์ ์ฃผ์ ๋ก ๊ธ์ ์ฐ๋ ค๊ณ ํ์ต๋๋ค. ๊ทธ๋ฐ๋ฐ ๋ง์ ์ฐพ์๋ณด๋ ๋ฐฑ์๋-ํ๋ก ํธ์๋๋ฅผ ํจ๊ป ๊ณ ๋ ค
velog.io
ํ์ง๋ง ๋ชจ๋ ๋์ ์ด ๋ฐฉ์์ ์ฌ์ฉํ๋ฉด ์ ์ฒด ํ์ด์ง ๊ฐฏ์๋ฅผ ์กฐํํ ์๋ ์๊ฒ ๋๋ค. ๋์ฉ๋ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๋ ์๋น์ค์์๋ ์ฃผ๋ก ๋๋๊ทธ ์ก์ ์ผ๋ก๋ง ๋ค์ ํ์ด์ง๋ฅผ ํ์ํฉ๋๋ค.
3. ์์ฉํ์ค์ธ ์๋น์ค๋ค์ ํ์ด์ง ๋ฐฉ์
์ค์ ์๋น์ค์ค์ธ ๊ธฐ์ ๋ค์ ์ด๋ค ๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๊ณ ์๋์ง ๊ถ๊ธํด ์ฐพ์๋ณด์์ต๋๋ค. ๊ตฌ๊ธ, ๋ทํ๋ฆญ์ค, ์ธ์คํ๊ทธ๋จ, ํ์ด์ค๋ถ ๋ฑ. ์ด๋ฅผ ํ๋์ฉ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
3-1. ๊ตฌ๊ธ
2022๋ 3์ ๊ธฐ์ค ๊ตฌ๊ธ์ messi๋ฅผ ๊ฒ์ํ๋ฉด ์ฝ 2์ต 8์ฒ 500๋ง๊ฑด์ ๋ฐ์ดํฐ๊ฐ ๊ฒ์๋ฉ๋๋ค. ํ ํ์ด์ง์ 10๊ฐ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ค๋ค๊ณ ๊ฐ์ ํ๋ฉด ์ฝ 2์ฒ 8500๋ง๊ฐ์ ํ์ด์ง๊ฐ ์กด์ฌํ๊ฒ ๋๋๋ฐ, ์ด๋ฅผ ์ธ๋ฑ์ค๋ฅผ ํตํด ๊ฒ์ํ๋ฉด ๋งค์ฐ ๋๋ฆฐ. ๋ฌผ๋ก ๊ตฌ๊ธ ์์ฒด ๊ฒ์ ์์คํ ์ด ์กด์ฌํ๊ฒ ์ง๋ง
\
๊ตฌ๊ธ์ ์์ฒด ๊ฒ์ ์์ง์ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ ๊ฒ์ ์กฐ๊ฑด์ด ๋งค๋ฒ ๋ฐ๋๊ธฐ ๋๋ฌธ์ ์ธ๋ถ ๊ท์น์ ํ์ ํ๋ ๊ฒ์ ํฐ ์๋ฏธ๊ฐ ์์ต๋๋ค. ๋ค๋ง ํ์ฌ ์ฌ์ง์ ๊ฒ์ ๊ธฐ์ค์์ ํ์ด์ง๋ฅผ ๋ณด๋ฉด 0๋ถํฐ ์์ํ๋ ๊ฒ์ ์ ์ ์๋๋ฐ 10์ 2๋ฒ, 20์ 3๋ฒ, 30์ 4๋ฒ ํ์ด์ง๋ฅผ ๋ํ๋ ๋๋ค.
์ฌ๋ API๋ Json ์๋ต๊ฐ์ ๋ค์ ์กฐํ์ ์ฌ์ฉ๋ cursor ์์น๋ฅผ ๋ด๋ ค์ฃผ๊ณ ์์ต๋๋ค.
users.list API method
Lists all users in a Slack team.
api.slack.com
{
"ok": true,
"members": [
{
"id": "USLACKBOT",
"team_id": "T0G9PQBBK",
"name": "slackbot",
...
},
{
"id": "W07QCRPA4",
"team_id": "T0G9PQBBK",
"name": "glinda",
...
}
],
"cache_ts": 1498777272,
"response_metadata": {
"next_cursor": "dXNlcjpXMDdRQ1JQQTQ="
}
}
https://api.slack.com/methods/users.list
users.list API method
Lists all users in a Slack team.
api.slack.com
3-2. ์นด์นด์ค
์นด์นด์ค์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ์๋์ ๊ฐ์ ๊ฒ์ ์กฐ๊ฑด์ ์ ๊ณตํฉ๋๋ค.
์นด์นด์ค
https://developers.kakao.com/tool/rest-api/open/get/v2-search-web
์นด์นด์ค ๋ํ ์ต๋ ๊ฒ์ ํ์ด์ง๋ฅผ ์ ํํด ๋์๋๋ฐ์, 51ํ์ด์ง๊ฐ ๋๋ฉด ๋ ์ด์ ๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํ์ง ์์ต๋๋ค.
4. Conclusion
์ ํต์ ์ธ ํ์ด์ง๋ค์ด์ ๋ฐฉ์์ ์ค๋ซ๋์ ๋คํธ์ํฌ ๋ญ๋น๋ฅผ ์ค์ฌ์ฃผ๋ ๊ธฐ๋ฅ์ ๋ด๋นํด์๋ค. ํ์ง๋ง ์ค์๊ฐ์ฑ์ ๋๋ SNS ์๋น์ค์ ๋ฑ์ฅ์ผ๋ก ๋ฆฌ์์ค๊ฐ ์์ฃผ ์์ /์์ฑ/์ญ์ ๋๋ ์ํฉ์ด ๋์ด๋์ ์ค๋ณต ์ ์ก์ ๊ฐ๋ฅ์ฑ์ด ์ผ์ก๋ค. ํธ์ํฐ๋ ์ปค์ ๊ธฐ๋ฐ ํ์ด์ง๋ค์ด์ ์ ํตํด ์ค์๊ฐ์ผ๋ก ๋ณํํ๋ ํ์๋ผ์ธ ์์์ ๋ฆฌ์์ค์ ์ค๋ณต ์ ์ก์ ํจ๊ณผ์ ์ผ๋ก ๋ง์๋๋ค.
5. Reference
๊น๋ฏผ์ CTO๋ | ์ปค์ ๊ธฐ๋ฐ ํ์ด์ง๋ค์ด์ |
Markus Winand | OFFSET is bad for skipping previous rows |
How to Implement Cursor Pagination Like a Pro
So you’ve decided to implement cursor pagination in your website. Well, you’ve come to the right place! (If you’re not entirely convinced…
medium.com
https://mariadb.com/kb/en/pagination-optimization/
Pagination Optimization
Pagination, not with OFFSET, LIMIT
mariadb.com
ใ ในใ |