nextjs에서도 기존 react에서 마찬가지로 client 환경에서의 데이터 fetch가 필요한데
해당 과정을 편하게 해줄 도구로
swr과 react query가 있다
이 둘의 특징을 표로 정리해보았다.
특징 SWR, React Query
| 개발사 | Vercel (Next.js 개발사) | TanStack |
| 번들 크기 | 약 4KB (더 작음) | 약 13KB |
| API 복잡도 | 간단하고 직관적 | 더 복잡하지만 기능이 풍부 |
| 설정 | 최소한의 설정 필요 | 더 많은 초기 설정 필요 |
| 캐싱 전략 | 단순한 stale-while-revalidate 전략 | 더 세밀한 캐싱 제어 가능 |
| 에러 처리 | 기본적인 에러 처리 | 더 강력하고 상세한 에러 처리 |
| 뮤테이션 | 기본 지원, 추가 설정 필요 | 내장 뮤테이션 지원 |
| DevTools | 공식 DevTools 없음 | 강력한 내장 DevTools 제공 |
| Next.js 통합 | 더 나은 통합 (Vercel 제품) | 좋은 통합, 약간의 추가 설정 필요 |
| 학습 곡선 | 낮음 | 중간 ~ 높음 |
| 성능 | 일반적으로 가벼움 | 기능이 많아 약간의 오버헤드 가능 |
| 사용 사례 | 간단한 데이터 fetching, 작은 프로젝트 | 복잡한 데이터 관리, 대규모 애플리케이션 |
위 특징들을 근거로
- 간단한 API: SWR은 React Query에 비해 더 간단하고 직관적인 API를 제공합니다. 이는 빠른 개발과 유지보수에 도움이 됩니다.
- 가벼운 라이브러리: SWR은 React Query보다 더 작고 가벼운 라이브러리입니다. 이는 프로젝트의 번들 크기를 줄이는 데 도움이 됩니다.
- Next.js와의 통합: SWR은 Vercel에서 개발한 라이브러리로, Next.js와 매우 잘 통합됩니다. Next.js 프로젝트에서 특히 유용할 수 있습니다.
- 충분한 기능: 대부분의 일반적인 데이터 fetching 시나리오에 대해 SWR은 충분한 기능을 제공합니다. 캐싱, 자동 재검증, 페이지네이션 등의 기본적인 기능들을 포함하고 있습니다.
- 학습 곡선: SWR은 React Query에 비해 학습 곡선이 낮습니다. 이는 팀 전체의 생산성을 높이는 데 도움이 될 수 있습니다.
위와 같은 이유로 이번 프로젝트에서 SWR을 사용하기로 했다
실제로 현재 npm 사용량을 비교해보면

출처: https://npmtrends.com/react-query-vs-swr
와 같은 결과를 볼 수 있는데 이미 swr이 react-query의 사용자 수를 뛰어넘고 있었다
dev tool이 지원이 안되는게 아쉽지만 일단 한번 써봐야겠다
SWR의 캐시 구조
SWR은 기본적으로 메모리 내 캐시를 사용합니다1. 이 캐시는 JavaScript의 Map 객체를 기반으로 하며, 키-값 쌍으로 데이터를 저장합니다4. 현재 SWR의 캐시에는 크기 제한이 없습니다4.
가비지 컬렉션 부재의 영향
가비지 컬렉션 기능이 없으면 다음과 같은 상황이 발생할 수 있습니다:
- 메모리 사용량 증가: 사용하지 않는 데이터가 계속 캐시에 남아 있어 메모리 사용량이 증가할 수 있습니다.
- 성능 저하: 캐시 크기가 계속 커지면 캐시 검색 및 관리에 더 많은 시간이 소요될 수 있습니다.
대응 방안
SWR에서 가비지 컬렉션 기능이 없을 때 취할 수 있는 몇 가지 대응 방안이 있습니다:
1. 수동 캐시 관리
mutate 함수를 사용하여 더 이상 필요하지 않은 데이터를 수동으로 삭제할 수 있습니다
import { mutate } from 'swr'
// 특정 캐시 항목 삭제
mutate('/api/user', null, false)
// 여러 캐시 항목 삭제
const clearUserData = async () => {
const keys = cache.keys()
const userKeys = Array.from(keys).filter(key => key.startsWith('/api/user/'))
await Promise.all(userKeys.map(key => mutate(key, null, false)))
}
2. 커스텀 캐시 프로바이더 구현
SWR의 provider 옵션을 사용하여 커스텀 캐시 프로바이더를 구현할 수 있습니다 이를 통해 캐시 크기 제한이나 만료 정책 등을 직접 구현할 수 있습니다.
const createCacheWithCleanup = (maxSize = 100) => {
const cache = new Map()
return {
get: (key) => cache.get(key),
set: (key, value) => {
if (cache.size >= maxSize) {
const oldestKey = cache.keys().next().value
cache.delete(oldestKey)
}
cache.set(key, value)
},
delete: (key) => cache.delete(key)
}
}
const config = {
provider: () => createCacheWithCleanup(100)
}
3. 주기적인 캐시 정리
애플리케이션 레벨에서 주기적으로 캐시를 정리하는 로직을 구현할 수 있습니다
const cleanupCache = () => {
const now = Date.now()
cache.forEach((value, key) => {
if (now - value.timestamp > MAX_AGE) {
cache.delete(key)
}
})
}
// 주기적으로 cleanupCache 함수 실행
setInterval(cleanupCache, CLEANUP_INTERVAL)
4. 페이지 전환 시 캐시 정리
라우트 변경 시 특정 캐시 항목을 삭제하거나 전체 캐시를 초기화하는 방법도 고려할 수 있습니다.
import { useRouter } from 'next/router'
import { cache } from 'swr'
function MyApp({ Component, pageProps }) {
const router = useRouter()
useEffect(() => {
const handleRouteChange = () => {
cache.clear() // 전체 캐시 초기화
}
router.events.on('routeChangeComplete', handleRouteChange)
return () => {
router.events.off('routeChangeComplete', handleRouteChange)
}
}, [router])
return <Component {...pageProps} />
}
이러한 방법들을 통해 SWR에서 가비지 컬렉션 기능이 없더라도 효과적으로 캐시를 관리할 수 있습니다. 프로젝트의 규모와 특성에 따라 적절한 전략을 선택하여 구현하는 것이 중요합니다.
swr은 자동 gc기능이 지원이 안된다고 하는데 이런 환경에서 캐시가 계속 쌓이게 될 경우 성능의 저하가 발생할 수 있기 때문에 위와 같은 방법을 쓰면 해소할 수 있다고 한다. 작업하면서 신경써봐야겠다.
'개발 > NEXTJS' 카테고리의 다른 글
| 사용자 avata image 변경했을 때 (0) | 2025.01.31 |
|---|---|
| 좋은 블로그 글 모음 (1) | 2025.01.27 |
| 병렬라우팅과 일반 컴포넌트 방식 비교 (1) | 2025.01.22 |
| 바뀐 Nextjs 외부 Image 도메인 설정 (0) | 2025.01.21 |
| supabase nextjs 인증 구축하기 (3) | 2025.01.15 |