Nextjs Server Fetching
Nextjs Server Fetching Test
2025.09.07
- nextjs
- react
- framework
목차
Nextjs Server Side fetching cache test
Nextjs Server Fetching cache를 실제로 테스트해보았다.
일단 요청시점을 return 하는 api를 만들었다. (Nextjs의 api routes 기능을 사용)
// app/api/clock route.ts
import { NextResponse } from 'next/server'
export async function GET() {
// 매 요청마다 값이 달라지는지 확인하기 위한 타임스탬프
return NextResponse.json({
now: new Date().toISOString(),
})
}
그리고 위 api를 호출하는 페이지를 만들었다. a는 10초 캐시, b는 매번 새 요청을 하도록 설정했다.
여기서 중요한 사항은 해당 작업은 server component에서만 가능하다.
// app/rsc-cache/page.tsx
import Link from 'next/link'
export default async function Page() {
// A) 10초 캐시
const a = await fetch('http://localhost:3000/api/clock', {
next: { revalidate: 10 },
}).then((r) => r.json())
// B) 매번 새 요청
const b = await fetch('http://localhost:3000/api/clock', {
next: { revalidate: 0 },
}).then((r) => r.json())
return (
<div style={{ padding: 20 }}>
<p>a: {JSON.stringify(a, null, 2)}</p>
<p>b: {JSON.stringify(b, null, 2)}</p>
<br />
<Link href="/rsc-cache">재요청</Link>
</div>
)
}
결과는 다음과 같다.

재요청
을 눌렀을 때 a는 10초 이내에 재요청하면 캐시된 값이 나오고, 10초가 지나면 새로운 값이 나오고, b는 재요청
을 누를 때마다 매번 새로운 값이 나오는 것을 확인할 수 있었다.
Link 컴포넌트 요청 경로에 매번 query string을 다르게 주면 어떻게 될까?
<Link href={/rsc-cache?ts={Date.now()}}>재요청</Link>
처럼 query string을 다르게 주어도 결과는 똑같았다.
10초 캐시한 값은 여전히 10초 이내에 재요청하면 캐시된 값이 나오고, 10초가 지나면 새로운 값이 나오고, b는 재요청
을 누를 때마다 매번 새로운 값이 나오는 것을 확인할 수 있었다.
// app/rsc-cache/page.tsx
import Link from 'next/link'
export default async function Page() {
// A) 10초 캐시
const a = await fetch('http://localhost:3000/api/clock', {
next: { revalidate: 10 },
}).then((r) => r.json())
// B) 매번 새 요청
const b = await fetch('http://localhost:3000/api/clock', {
next: { revalidate: 0 },
}).then((r) => r.json())
return (
<div style={{ padding: 20 }}>
<p>a: {JSON.stringify(a, null, 2)}</p>
<p>b: {JSON.stringify(b, null, 2)}</p>
<br />
<Link href={`/rsc-cache?ts=${Date.now()}`}>재요청</Link>
</div>
)
}
Server Fetching 한 데이터를 Context 에 주입하여 Client Component에서 사용하면 어떻게 될까?
일단 TimeContext, TimeProvider를 만들었다.
'use client'
import { createContext } from 'react'
export type TimeValue = { a: string; b: string }
export const TimeContext = createContext<TimeValue | null>(null)
export const TimeProvider = ({ initialValues, children }: { initialValues: TimeValue; children: React.ReactNode }) => {
return <TimeContext.Provider value={{ a: initialValues.a, b: initialValues.b }}>{children}</TimeContext.Provider>
}
생성한 TimeProvider에 server fetching 한 데이터를 initialValues로 주입하였다. (Child 컴포넌트에서 TimeContext를 사용하여 값을 읽는다.)
import Link from 'next/link'
import { TimeProvider } from './_components/store'
import { Child } from './_components/Child'
export default async function Page() {
// A) 10초 캐시
const a = await fetch('http://localhost:3000/api/clock', {
next: { revalidate: 10 },
}).then((r) => r.json())
// B) 매번 새 요청
const b = await fetch('http://localhost:3000/api/clock', {
next: { revalidate: 0 },
}).then((r) => r.json())
return (
<div>
<TimeProvider
initialValues={{
a: String(a.now),
b: String(b.now),
}}
>
<>
<div style={{ padding: 20 }}>
<p>a: {JSON.stringify(a)}</p>
<p>b: {JSON.stringify(b)}</p>
<br />
<Link href={`/rsc-cache?ts=${Date.now()}`}>재요청</Link>
</div>
<div>
<Child />
</div>
</>
</TimeProvider>
</div>
)
}
Child 컴포넌트는 다음과 같다. 그리고 router.refresh() 버튼을 만들어서 눌렀을 때 Context 값이 바뀌는지 확인해보았다.
'use client'
import { useContext } from 'react'
import { TimeContext } from './store'
import { useRouter } from 'next/navigation'
export const Child = () => {
const time = useContext(TimeContext)
const router = useRouter()
return (
<div style={{ padding: 20 }}>
<p>a : {time?.a}</p>
<p>b : {time?.b}</p>
<br />
<button
onClick={() => {
router.refresh()
}}
>
router.refresh() 버튼
</button>
</div>
)
}
결과는 다음과 같다.

router.refresh()
버튼을 눌렀을 때 a는 10초 이내에 재요청하면 캐시된 값이 나오고, b는 router.refresh()
버튼을 누를 때마다 매번 새로운 값이 나오는 것을 확인할 수 있었다.
즉, server fetching 한 데이터를 Context 에 주입하여 Client Component에서 사용해도 동일하게 동작하는 것을 확인할 수 있었다.
Context의 값을 Client Component의 state에 복사하여 사용해도 동일하게 동작할까?
기존 Child
컴포넌트에 const [stateA, setStateA] = useState(time?.a)
와 같이 context 값을 state에 복사하여 테스트 해보았다.
'use client'
import { useContext } from 'react'
import { TimeContext } from './store'
import { useRouter } from 'next/navigation'
import { useState } from 'react'
export const Child = () => {
const time = useContext(TimeContext)
const router = useRouter()
const [stateA, setStateA] = useState(time?.a)
return (
<div style={{ padding: 20 }}>
<p>a : {time?.a}</p>
<p>stateA : {stateA}</p>
<p>b : {time?.b}</p>
<br />
<button
onClick={() => {
router.refresh()
}}
>
router.refresh() 버튼
</button>
</div>
)
}
결과는 다음과 같다.

router.refresh()
버튼을 눌렀을 때 context 에서 복사한 stateA 값은 최초 렌더링 시점의 값에서 변하지 않는 것을 확인할 수 있었다.
state를 동기화 하려면 어떻게 해야할까?
useEffect
를 사용하여 context 값이 바뀔 때 state를 동기화 하면 된다.
useEffect(() => {
setStateA(time?.a)
}, [time?.a])
이 방법외에도 component의 key를 강제로 변경하여 컴포넌트를 리마운팅 할 수 있다. 하지만 이방법은 비용적 측면에서 권장되지 않는다.