Next.js에서 서버로부터 data를 받아오는 방식으로는 크게 5가지가 있다고 생각한다.
- 서버 컴포넌트에서의 바로 데이터 페칭
- api/route.ts를 활용한 Route Handlers 사용
- server actions를 사용
- SWR 사용
- React Query 사용
각 방식의 특징과 주의점을 우선 살펴본다
1. 서버 컴포넌트에서의 데이터 페칭
// app/page.tsx
async function getData() {
const res = await fetch('https://api.example.com/data')
return res.json()
}
export default async function Page() {
const data = await getData()
return <div>{data.title}</div>
}
이 방식을 채택한다면 SEO에 유리, 자동 캐싱, 클라이언트 번들 크기 감소 라는 장점을 가질 수 있다.
하지만 비동기 방식에 따라 데이터가 페칭 되는 동안 Suspense나 loading.tsx와 같은 것을 생성하지 않았다면 유저는 긴 시간을 기본 로딩 화면만을 보게 되므로 주의점이 필요하다. 따라서 대규모 데이터 페칭을 하는 것이라면 소수의 갯수만 초기에 받아오고 이후에는 pageParams에 따라 추가적으로 가져온다는 조작이 필요할 수 있음
( fetch 주의점 )
Next.js에서 fetch를 사용한다면 자동 캐싱이 되는 것은 서버 컴포넌트에서의 fetch만 그러할 뿐 클라이언트 단에서 사용되는 fetch는 자동 캐싱이 되지 않는다. 따라서 use client 단에서 사용되는 fetch에서 캐싱 작업이 필요하다면 직접 설정해주거나 React Query와 같은 라이브러리를 사용하는 것을 고려해야 함
2. Route Handlers 사용
// app/api/route.ts
export async function GET() {
const data = await db.query('SELECT * FROM users')
return Response.json(data)
}
API 엔드포인트 생성하는 것으로 커스텀 헤더/ 상태코드 처리에 용이하고 복잡한 API 로직 구현이 좋다.
네트워크 통신이 많아질 수 있다는 단점이 있지만, 백엔드 API와의 명확한 분리가 필요하거나 미들웨어적인 처리가 필요할 때 유용.
특히 인증/인가 로직을 처리하거나, 외부 API 응답을 가공해야 할 때 활용도가 높음.
특히 CORS 에러를 route handler를 통해 서버끼리 데이터 통신하는 것으로 해결하기 편함.
이 때 주의할 점은, route handler를 서버 컴포넌트에서 fetch 할 때 경로를 /api/route.ts가 아닌 http://localhost:3000/api/route.ts와 같은 풀 주소를 입력해줘야 경로를 잡아준다. 즉 상대 경로가 아닌 절대 경로를 입력하라는 것인데, 이것은 서버 컴포넌트가 빌드 시점에 실행되는 환경이기 때문이다.
// ❌ 잘못된 방법
const data = await fetch('/api/route')
// ✅ 올바른 방법
const data = await fetch('http://localhost:3000/api/route')
// 또는
const data = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/route`)
3. server action
'use server'
export async function submitData(formData: FormData) {
const name = formData.get('name')
await db.users.create({ data: { name } })
}
server action 을 통해서도 데이터를 받아오는 것이 가능하지만 서버 액션같은 경우에는 기본적으로 post 처리 되어 캐싱이 안된다.
서버 사이드에서 직접 실행되므로 보안적으로 안전하기에 이는 폼 제출 처리에 특화되어 있음.
별 생가 없이 서버 액션으로 데이터를 get 하는 행위를 한다면 데이터 통신 시간 초과로 다운 되는 경우 생길 수 있음
4. SWR (클라이언트단)
const { data, error } = useSWR('/api/user', fetcher)
Vercel에서 만든 데이터 페칭 라이브러리로, React Query와 비슷한 목적을 가지고 있음.
- 캐시 및 자동 재검증
- 낙관적 UI 업데이트
- 포커스시 재검증
- 인터벌 폴링
- 페이지네이션 지원
Next.js와의 통합이 자연스럽고 설정이 간단하다는 장점이 있. 실시간성이 중요한 데이터를 다룰 때 특히 유용
5. React Query 사용(클라이언트단)
const { data, isLoading } = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/api/todos').then(res => res.json())
})
가장 강력한 클라이언트 상태 관리 도구
- 강력한 캐싱 메커니즘
- 백그라운드 업데이트
- 무한 스크롤/페이지네이션
- 낙관적 업데이트
- 재시도 로직
- 복잡한 종속성 관리
SWR보다 더 많은 기능을 제공하지만, 그만큼 설정이 복잡
====
- 정적이고 SEO가 중요한 데이터 → 서버 컴포넌트
- API 중간 처리가 필요한 경우 → Route Handlers
- 폼 제출과 같은 데이터 변경 작업 → Server Actions
- 간단한 실시간 데이터 → SWR
- 복잡한 데이터 관리가 필요한 경우 → React Query