posts

Next + Suspense + SSR

Apr 23, 2026 updated Apr 23, 2026 nextjsreactreact-queryrendering

nextjs 에서 ssr, isr 방식을 올바르게 처리하기 위한 방식과 react 18에서 소개된 suspense 등의 기능 사용 방식을 정리합니다.

Q1. 서버에서 전달받는 html에 왜 CSR 로 요청한 데이터도 모두 들어가있는지?✅

Q2. 서버에서 받은 html 중, 왜 div hidden으로 해서 ssr에서 호출된 데이터가 있는지✅

Q3. react 코드 내 > getServerSideProps 에서 server or client에서 실행된 상황인지 판단 가능 여부 확인✅

Q4. server에서 1번, 클라이언트에서 1번 api 호출하는 건 어떻게 막아야 할지? 방법이 있는지?✅

Q5. 서버 실행 함수( getServerSideProps …) 사용 시, 불필요한 api 호출 막는 방법 정리✅

Q6. 서버에서 렌더링되며 api도 호출하는 컴포넌트가 한 페이지에 2개 이상일 때, html 생성 및 클라이언트로 전달 시점✅

Q7. 서버에서 호출한 api error 처리?✅

Q8. Cache✅

Q9. 유저 인증 관리 - SSR, SSG, ISR 일 때✅

Q10. react-query suspense:true 일 때,api fetch 무한✅

230116 상황 정리

SEO 에 잡히는 데이터 확인 기준 - 첫 경로로 입장 시, 네트워크로 서버에서 전달받은 html

dynamic

CSR로 클라이언트에서 렌더링될 컴포넌트에 대해서만 dynamic으로 감싼다

ssr: false로만 사용한다.

lazy loading, suspense 역할

getStaticProps

SSG, ISR 사용 시 사용하는 함수로, 빌드될 때 한 번만 실행된다.

SSG, ISR에서 호출해야하는 api 들은(SEO 하고자 하는 데이터) 해당 함수에서 fetch 한다.

Suspense

api를 호출하는 컴포넌트에 대해서는 항상 감싼다.

Q1. 서버에서 전달받는 html에 왜 CSR 로 요청한 데이터도 모두 들어가있는지? ✅

A. 정확하지 않은 방식으로 next/dynamic 을 사용하고 있었다. → CSR 컴포넌트는 html에 포함되지 않음.

Next + Suspense + SSR 이미지 1

// Error const LeftBar = dynamic(() => import('components/Suspense/LeftBar/LeftBar'), { suspense: true, ssr: false }); // Right const LeftBar = dynamic(() => import('components/Suspense/LeftBar/LeftBar'), { ssr: false }); ... <Suspense fallback={

Loading LeftBar ...
}> <Suspense fallback={
Loading Feed ...
}> -> suspense: true가 되면, React18 버전에서는 항상 Suspense에 대한 처리를 서버 쪽에서 하기 때문에 해당 컴포넌트는 서버 쪽에서 렌더된다. 즉, ssr:false는 무시된다.

위처럼 작성 시 html 파일에는 ssr 로 서버 쪽에서 렌더된 컴포넌트의 데이터 내용만 추가된다.

또한, 해당 컴포넌트는 클라이언트 사이드에서만 실행, 렌더되는 컴포넌트로서 동작한다. (즉, 로그 출력해도 터미널에서 확인되지 않음)

next/dynamic 자체가 React.lazy와 Suspense의 extension 이기 때문에 클라이언트 사이드에서 api 요청이 완료되지 않았을 때 Suspense의 fallback 컴포넌트가 정상 출력된다.

Next + Suspense + SSR 이미지 2

화면 기록 2023-01-16 오후 2.34.33.mov

Q2. 서버에서 받은 html 중, 왜 div hidden으로 해서 ssr에서 호출된 데이터가 있는지 ✅

A. 서버 측에서 필요한 데이터 수집이 완료되면 인라인 script 에 포함된다.

Suspense로 감싸진 컴포넌트는 필요한 데이터가 수집되지 않으면 fallback ui 가 출력된다.

이후, 필요한 데이터가 수집된다면 해당 컴포넌트는 인라인 script 태그와 함께 스트리밍 된다.

Next + Suspense + SSR 이미지 3

Next + Suspense + SSR 이미지 4

실제 코드에서 아래처럼 suspense > fallback ui를 실제 데이터 html 코드로 바꾸는 부분은 아래와 같습니다.

Next + Suspense + SSR 이미지 5

Q3. react 코드 내 >getServerSideProps에서 server or client에서 실행된 상황인지 판단 가능 여부 확인 ✅

A.getServerSideProps인자 값 ctx.req.url 로 확인 가능

Next + Suspense + SSR 이미지 6

deploy 하여getServerSideProps의 인자값 contex 콘솔 확인하여 확인하였습니다.

navigate를 통해 페이지에 입장한 경우

Next + Suspense + SSR 이미지 7

해당 페이지('/suspense')에 처음 입장한 경우

Next + Suspense + SSR 이미지 8

Q4. server에서 1번, 클라이언트에서 1번 api 호출하는 건 어떻게 막아야 할지? 방법이 있는지? ✅

A. SSR일 때, getServerSideProps 를 쓰지 않으려고 했는데, 사용하여 react-query의 캐싱을 사용하여 위 문제를 해결할 수 있다.

Q5. 서버 실행 함수( getServerSideProps …) 사용 시, 불필요한 api 호출 막는 방법 정리 ✅

상황

getServerSideProps, getStaticProps는 모두 해당 함수가 작성된 컴포넌트가 실행될 때마다 실행된다.

해당 컴포넌트(페이지)에 처음 입성한 경우

navigate를 통해 해당 컴포넌트(페이지)에 입장한 경우

shallow = true

Next + Suspense + SSR 이미지 9

Next + Suspense + SSR 이미지 10

참고 :https://dev.to/arianhamdi/react-query-v4-ssr-in-next-js-2ojj

서버 실행 함수의 props를 확인하여 커스텀 로직 함수 작성

추가해야하는 이유

shallow 프로퍼티는 /pages/1 → /pages/2 로의 이동에 대해서는 서버 실행 함수가 호출되지 않게 하지만, /home → /pages 로의 이동은 감지하지 못하여 서버 실행 함수가 호출된다.

참고

Q6. 서버에서 렌더링되며 api도 호출하는 컴포넌트가 한 페이지에 2개 이상일 때, html 생성 및 클라이언트로 전달 시점? ✅

A. 서버에서 렌더링되는 데이터 호출이 모두 완료되어야 클라이언트에 html이 전달된다.

Q7. 서버에서 호출한 api error 처리? ✅

getServerSideProps안에서prefetchQuery대신fetchQuery를 사용한다.

이유 :prefetchQuery는 데이터, 에러를 반환하지 않는다.

404 에러 처리 방법

참고 :https://dev.to/arianhamdi/react-query-v4-ssr-in-next-js-2ojj#404

Q8. Cache ✅

nextjs 공식 cache 처리 문서https://nextjs.org/docs/going-to-production#caching

Next + Suspense + SSR 이미지 11

getServerSideProps, getInitialProps 를 쓸 때는 next start 했을 때 next.config.js에 생성된 기본 Cache-control 헤더 값을 사용한다.

getServerSideProps 를 사용하며 다른 캐시 행동 양식을 정하고 싶을 때는 해당 함수 안에서 setHeader 함수를 사용하여 설정할 수 있다.

// This value is considered fresh for ten seconds (s-maxage=10). // If a request is repeated within the next 10 seconds, the previously // cached value will still be fresh. If the request is repeated before 59 seconds, // the cached value will be stale but still render (stale-while-revalidate=59). // // In the background, a revalidation request will be made to populate the cache // with a fresh value. If you refresh the page, you will see the new value. export async function getServerSideProps({ req, res }) { res.setHeader( 'Cache-Control', 'public, s-maxage=10, stale-while-revalidate=59' ) return { props: {}, } }

next/image 캐싱 문서https://nextjs.org/docs/api-reference/next/image#caching-behavior

→ 결과입니다.

react-query에서 제공하는 캐싱 전략과 next에서 디폴트로 제공하는 캐싱 전략을 사용한다. (추가로 변경, 사용할 일은 없을 것 같다)

Q9. 유저 인증 관리 - SSR, SSG, ISR 일 때 ✅

공식문서에서 유저 인증 Authentication 관련하여 2가지 방식을 제안합니다.

데이터 fetching 전략에 따라 선택하여 사용하기를 안내하고 있습니다.

1.Authenticating Statically Generated Pages

client side에서 user data를 fetching할 때 사용 가능한 패턴

= 페이지에 서버 실행 함수 (getServerSideProps등) 이 없는 경우

장점 : TTI (Time to Interactive) 시점이 빠르다.

원인 : 페이지가 static한 페이지이기 때문에 페이지는 global CDN에서 생성되며 next/link 를 사용해 preloading 되기 때문에

2.Authenticating Server-Rendered Pages*****

server side에서 유저 data api fetch 하여 인증 관리하는 방식

장점

유저 정보가 없을 때 리다이렉트 시키기 전에 보여지지 않아야 하는 컨텐츠가 잠시 보이는 등, 깜빡거리는 현상을 방지해줍니다.

원인 : 유저 인증 체크가 렌더링 이전 과정인 getServerSideProps에서 일어나기 때문에 렌더링 자체를 방지합니다.

위와 같은 이유로 TTFB 시간이 빠릅니다.

아래는 session을 사용할 때의 예시입니다.

import withSession from '../lib/session' export const getServerSideProps = withSession(async function ({ req, res }) { const { user } = req.session if (!user) { return { redirect: { destination: '/login', permanent: false, }, } } return { props: { user }, } })

Next/Image 최적화 관련 내용 및 과금 정책

Next/Image 는 새로운 source 이미지에 대한 요청이 있을때 새로운 최적화된 이미지를 생성후 edge network 라고 하는 cdn 으로 배포함 과금 정책이 5000장의 이미지 이후 부터는 1000장당 50불 과금됨 .. 무조건 아무 이미지에 다 최적화를 사용하면 안될듯 함

Q10. react-query suspense:true 일 때,api fetch 무한 ✅

[상황 발생 조건] - CSR로 로드되는 컴포넌트

getServerSideProps 등의 서버 실행 함수에서 데이터를 미리 fetch 하지 않는 경우 +

컴포넌트 내의 react-query > suspense: true 옵션을 건 경우

dynamic으로 불러진 상황 + ssr:false

dynamic으로 불러지지 않은 상황

suspense로 잘 감싸진 상황

정상 동작 (CSR로 동작함)

정상 동작 (CSR로 동작함)

suspense로 잘 감싸지지 않은 상황

에러(1)

무한 fetch 에러(2)

[에러 설명]

에러(1)

원인 : suspense:true 로 설정되어 데이터가 불러오기 전에 출력해야할 fallback ui가 없어 발생한 오류

에러(2)

상황 : 앱 자체가 계속 다시 rerender 되면서 query 가 무한으로 불린다.

react-query 에 enabled:false 를 준 상황에서는 아예 query 가 호출되지 않는다.

refetchonMount: false + enabled: false 일 때는 아예 마운트되어도 실행되지 않도록 함.

[꼭 지켜야 할 점]

suspense:true 로 설정한 경우에는 Suspense로 잘 감싸야 한다.