π‘무ν μ€ν¬λ‘€μ΄λ?
μΈνΌλνΈ μ€ν¬λ‘€(Infinite Scroll)μ μ¬μ©μκ° μ€ν¬λ‘€μ λ΄λ¦΄ λλ§λ€ μλ‘μ΄ μ½ν μΈ λ₯Ό μλμΌλ‘ λ‘λνλ κΈ°μ μ λλ€. νμ΄μ§λ₯Ό λͺ μμ μΌλ‘ λκΈ°λ νμ΄μ§λ€μ΄μ λ°©μκ³Ό λ¬λ¦¬, μ¬μ©μκ° λμμ΄ μ½ν μΈ λ₯Ό νμν μ μλλ‘ μ€κ³λ λ°©μμ λλ€.
μΈνΌλνΈ μ€ν¬λ‘€μ λ€μκ³Ό κ°μ μ΄μ μ΄ μμ΅λλ€.
- λκΉ μλ μ¬μ©μ κ²½ν(UX) μ 곡
- νμ΄μ§ λ‘λ© μμ΄ μλ‘μ΄ μ½ν μΈ νμ
- μ¬μ©μμ 체λ₯ μκ° μ¦κ°
- λͺ¨λ°μΌ νκ²½μμ μ§κ΄μ μΈ λ΄λΉκ²μ΄μ κ°λ₯
μ΄ κΈ°μ μ μμ λ―Έλμ΄ νΌλλ λ΄μ€ μ¬μ΄νΈ λ± μ§μμ μΈ μ½ν μΈ μλΉκ° νμν νκ²½μμ λ§μ΄ μ¬μ©λ©λλ€.
π€κ³ λ―Όν μ
νμ΄μ§λ€μ΄μ vs 무ν μ€ν¬λ‘€
- μΈνΌλνΈ μ€ν¬λ‘€μ νμ΄μ§λ€μ΄μ λ³΄λ€ μ§κ΄μ μ΄μ§λ§, νΉμ λ°μ΄ν°λ‘ μ΄λνλ κΈ°λ₯μ΄λ νν°λ§, κ²μ κΈ°λ₯μ΄ μΆκ°λμμ κ²½μ° κ΅¬ν λμ΄λκ° μ΄λ €μμ§ μ μμ΅λλ€.
- νμ΄μ§λ€μ΄μ μ κ²½μ°μλ μλ‘κ³ μΉ¨μμλ νμ¬ νμ΄μ§λ₯Ό μ μ§νλλ° μ΄λ ΅μ§ μμ§λ§, 무ν μ€ν¬λ‘€μ κ²½μ°μλ ꡬνλ°©λ²μ λν κ³ λ―Όμ΄ νμν©λλ€.
μ±λ₯ μ΅μ ν
- 무ν μ€ν¬λ‘€λ‘ μΈν΄ λ§μ μμ DOMμ΄ μμ±λ μ μμΌλ©°, 무νν λ°μ΄ν°μ κ²½μ° μΉ λΈλΌμ°μ μ μ¬κ°ν λΆνλ₯Ό μ€ μ μμ΅λλ€. νΌλλ λ©μ μ κ°μ΄ 무νν λ°μ΄ν°λ κ°μμ€ν¬λ‘€ κΈ°λ₯μ κ³ λ €ν΄λ³΄μμΌ ν©λλ€.
- μ€ν¬λ‘€κ³Ό κ΄λ ¨λ μ½λκ° μλͺ» μμ±λμμ κ²½μ° λ§μ μμ APIνΈμΆμ λ°μμμΌ λ¬Έμ λ₯Ό μΌμΌν¬ μ μμ΅λλ€. μ΄λ° κ²½μ°λ₯Ό λ°©μ§νκ³ μ λλ°μ΄μ€λ μ€λ‘νλ§κ³Ό κ°μ μ΅μ νκ° νμν©λλ€.
μ¬μ©μ κ²½ν
- μ μ μ λ³λ€λ₯Έ μ‘μ μ΄ μμ΄ μ€ν¬λ‘€ μ΄λ²€νΈλ§μΌλ‘ λ°μ΄ν°κ° ν¨μΉλκΈ° λλ¬Έμ, λ°μ΄ν°κ° ν¨μΉ λ λ μ€μΌλ ν€μ΄λ μ€νΌλμ κ°μ΄ λ°μ΄ν°κ° λ°μμ€λ μ€μ΄λΌλ νΌλλ°±μ΄ νμν©λλ€.
βοΈκ΅¬ν λ΄μ©
useInfiniteFetcher
const useInfiniteFetcher = () => {
const [state, setState] = useState<FetchState<Datum>>({
state: 'idle',
data: [],
})
const fetchNextPage = useCallback(async () => {
setState(prev => ({
...prev,
state: 'loading',
}))
const nextPageData = await generatePageData()
setState(prev => {
const nextData = [...(prev.data || []), nextPageData]
return {
data: nextData,
state: 'fetched',
}
})
}, [])
return {
...state,
fetchNextPage,
}
}
useInfiniteFetcher
ν
μ μΈνΌλνΈ μ€ν¬λ‘€μ μν λ°μ΄ν°λ₯Ό κ΄λ¦¬νλ 컀μ€ν
ν
μ
λλ€.
- μν(
state
)λ₯Ό κ΄λ¦¬νμ¬ λ°μ΄ν° λ‘λ©(loading
), λ‘λ μλ£(fetched
), μ΄κΈ° μν(idle
)λ₯Ό ꡬλΆνλ©° fetchNextPage
ν¨μλ₯Ό νΈμΆνλ©΄ λΉλκΈ°μ μΌλ‘ λ°μ΄ν°λ₯Ό κ°μ Έμ(state λ³κ²½ ν) κΈ°μ‘΄ λ°μ΄ν°μ μΆκ°ν©λλ€.
TQ
λ₯Ό μ¬μ©νλ κ²½μ° useInfiniteQueryλ₯Ό
νμ©ν΄ μμ κ°μ λμμ νλ ν¨μλ₯Ό νμ©ν μλ μμ΅λλ€. μμλ‘ μ ν
μ μ¬μ©ν΄ λλ€ ν λ°μ΄ν°λ₯Ό λ°ννλ promise
ꡬννμμ΅λλ€.
μ ν μμ μΆκ°μ μΈ κΈ°λ₯μ λ£λλ€λ©΄, μλ¬ μ²λ¦¬μ μ€λ³΅ μμ²μ λ§κ³ , νμ¬ λ³΄κ³ μλ νμ΄μ§λ₯Ό κ΄λ¦¬ν μ μλλ‘ κΈ°λ₯μ μΆκ°ν μ μμ κ²μΌλ‘ μμλ©λλ€.
useInfiniteScroll
const useInfiniteScroll = () => {
const { data, state, fetchNextPage } = useInfiniteFetcher()
const moreRef = useRef<HTMLDivElement>(null)
const {
entries: [entry],
} = useIntersectionObserver(moreRef, ioOptions)
const isIntersecting = entry?.isIntersecting
useEffect(() => {
if (isIntersecting) fetchNextPage()
}, [isIntersecting])
return { data, state, moreRef }
}
export default useInfiniteScroll
μ΄μ μ μκ°ν useIntersectionObserverλ₯Ό νμ©ν ν
μ
λλ€. λ°μ΄ν° κ°μ₯νλ¨μ νλ μμλ₯Ό λ£κ³ , κ·Έ μμκ° νμ¬ λ·°ν¬νΈμ 보μ΄λ©΄ useInfiniteFetcherμ
fetchNextPageλ₯Ό
νΈμΆνλλ‘ νμμ΅λλ€.
infinite scroll
μ μ λ κ°μ ν
μ νμ©νλ©΄ μ½κ² ꡬνν μ μμ΅λλ€. νμ§λ§, μλΉμ€λ§λ€ μ€ν¬λ‘€ μ μ§λ, λ°μ΄ν° νν°λ§ λ° κ²μ κΈ°λ₯, κ°μμ€ν¬λ‘€ λ±μ μꡬμ¬νμ΄ μκΈΈ μ μμ΅λλ€. κ·Έλ΄ λλ μ λ κ°μ ν
μ κΈ°λ³ΈμΌλ‘ κ°κ° μꡬμ¬νμ λ§λ κΈ°λ₯μ μΆκ°ν νμκ° μμ΅λλ€.
'κ°λ°' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
[UI] Scroll Spy [UI μμ λ§λ€κΈ°] (0) | 2025.03.30 |
---|---|
[UI] Scroll Box (0) | 2025.03.28 |
[UI] ν΄ν μ»΄ν¬λνΈ (feat: UI μ§μ λ§λ€κΈ°) (0) | 2025.01.18 |
[UI] μμ½λμΈ μ»΄ν¬λνΈ (feat: UI μμ μ§μ λ§λ€κΈ°) (0) | 2025.01.06 |
[κ°μμ€ν¬λ‘€] κ°μμ€ν¬λ‘€ μ§μ κ°λ°νκΈ° (2) | 2024.10.19 |