Next.js의 SSR(Server Side Rendering)은 기본적인 사전 렌더링 방식으로, 사용자 요청 시마다 서버에서 페이지를 새롭게 렌더링하는 방식이다. 사용자가 요청을 보낼 때마다 서버에서 컴포넌트를 렌더링하고 데이터를 불러와 화면을 업데이트해주는 SSR 설정 방법을 살펴보자.
SSR을 구현하려면 Next.js에서 제공하는 약속된 함수인 getServerSideProps
를 이용한다. 이 함수를 페이지 컴포넌트 외부에 작성하고 export
키워드를 붙여 파일로부터 내보내면, 해당 페이지는 SSR 방식으로 동작하게 된다.
import { ReactNode } from "react";
import style from "./index.module.css"
import SearchableLayout from "@/components/searchable-layout";
import books from "@/mock/books.json";
import BookItem from "@/components/book-item";
import { InferGetStaticPropsType } from "next";
export const getServerSideProps = () => {
const data = "hello"
return {
props:{
data,
},
};
};
export default function Home({
data,
}: InferGetStaticPropsType<typeof getStaticProps>) {
return (
<div>
<section>
<h3>지금 추천하는 도서</h3>
{books.map((book) => (
<BookItem key={book.id} {...book}/>
))}
</section>
<section>
<h3>등록된 모든 도서</h3>
{books.map((book) => (
<BookItem key={book.id} {...book}/>
))}
</section>
</div>
);
}
Home.getLayout = (page: ReactNode) => {
return <SearchableLayout>{page}</SearchableLayout>
};
getServerSideProps
함수는 페이지를 요청해서 Next 서버가 SSR 사전 렌더링 중에 컴포넌트가 실행되기 전에 호출된다. 여기서 페이지에 필요한 데이터를 백엔드 서버나 외부 API로부터 불러와 컴포넌트에 전달할 수 있다.
getServerSideProps
가 실행되어 데이터를 불러오고, 그 후에 Home 컴포넌트가 렌더링된다. 이렇게 사전 렌더링 중에 데이터를 받아 props로 페이지 컴포넌트에 전달하여 브라우저에 렌더링된다.getServerSideProps
함수는 반드시 props
라는 프로퍼티를 포함한 객체를 반환해야 한다. 이는 Next.js가 이 객체의 props
값을 페이지 컴포넌트로 전달하기 때문이다.
export const getServerSideProps = () => {
const data = "hello";
return {
props: {
data,
},
};
};
이렇게 설정하면, data
라는 값이 props
로 전달되어 페이지 컴포넌트에서 사용할 수 있다.
export default function Home({data}:any) {}
한 가지 주의해야 할 점이 있다. getServerSideProps
함수는 사전 렌더링을 하는 그 과정에서 딱 한 번만 실행이 될 거기 때문에 오직 서버측에서만 실행이 되는 함수라는 점이다.
getServerSideProps
함수는 사전 렌더링 과정에서 한 번만 실행되며, 브라우저가 아닌 서버에서만 실행된다. 예를 들어, getServerSideProps
안에 console.log("안뇽")
코드를 작성하면, 이는 서버 콘솔(터미널)에서만 출력되며 요청이 들어올 때마다 새롭게 출력이 됨. 브라우저 콘솔에는 출력되지 않는다. 따라서 브라우저에서 사용하는 window
나 document
객체는 사용할 수 없으며, 만약 접근하려고 하면 undefined
오류가 발생한다.
<서버 터미널>
페이지 컴포넌트는 서버에서 한 번, 브라우저에서 한 번 실행된다. 서버에서는 사전 렌더링을 위해 컴포넌트가 실행되며, 그 후 클라이언트 측에서는 Hydration 과정 중 한 번 더 실행된다. 이로 인해, console.log 구문을 작성하면 서버 터미널과 브라우저 콘솔에 각각 한 번씩 로그가 출력된다.
그렇기 때문에 getServerSideProps
함수 내부에서 브라우저 환경에서만 이용할 수 있는 예를들어 window
객체의 window.locaiton
프로퍼티에 접근 하려고 하면 오류가 발생한다. 왜냐면, JS의 이 window
는 브라우저를 의미한다.
그렇기 때문에 서버 환경에서만 실행되는 getServerSideProps
함수 안에서는 브라우저를 읽을 수 없다. 그렇기 때문에 이 window
라는 값은 결론적으로 undefined
이 되어버리는데 이때 undefined에다가 location이라는 프로퍼티를 꺼내려고 하니까 당연히 오류가 발생하게 되는 것이다.
이렇게 페이지 컴포넌트가 두 번 실행되는 점에서 중요한 점은, 클라이언트에서만 실행되어야 하는 코드를 작성할 때는 브라우저 전용 객체(window, document 등)가 포함된 코드가 서버 측에서 실행되지 않도록 조건을 추가해야 한다는 것이다.
import { useEffect } from "react";
export default function Home() {
useEffect(() => {
console.log(window.location); // 브라우저에서만 실행
}, []);
return <div>클라이언트 전용 코드</div>;
}
useEffect
훅은 컴포넌트가 브라우저에 마운트된 후에 실행되므로 서버에서는 동작하지 않고, 브라우저 환경에서만 실행된다. 이를 통해 window, document와 같은 클라이언트 전용 객체에 안전하게 접근할 수 있다.Next.js는 기본적으로 여러 내장 타입을 제공하여 데이터 타입을 쉽게 관리할 수 있다. 그 중에 InferGetServerSidePropsType
타입은getServerSideProps
함수의 반환 타입을 자동으로 추론해 준다.
그래서 이 타입을 import해와서 사용해준다. 그리고 제네릭으로 <typeof getServerSideProps>
해주면 자동으로 이 함수의 반환값 타입이 추론이 되어서 매개변수 타입으로 정의가 된다.
import { InferGetServerSidePropsType } from "next";
export default function Home({ data }: InferGetServerSidePropsType<typeof getServerSideProps>) {
return <div>{data}</div>;
}
Next.js에서 제공하는 getServerSideProps
함수를 통해 SSR(Server Side Rendering) 방식을 쉽게 설정할 수 있다. SSR은 요청마다 서버에서 데이터를 불러와 페이지를 렌더링하여, 실시간 데이터가 필요한 페이지에 적합하다.
이 함수는 서버에서만 실행되므로 브라우저 환경에서만 사용 가능한 객체(window, document 등)를 직접 사용할 수 없으며, 클라이언트 전용 코드가 필요할 때는 useEffect
훅을 사용하여 브라우저에서만 실행되도록 설정할 수 있다.
또한 Next.js가 제공하는 타입 시스템을 활용하면, InferGetServerSidePropsType
과 같은 타입을 통해 데이터 타입을 자동으로 추론하고 안전하게 사용할 수 있다.
이를 통해 서버 사이드에서 필요한 데이터를 사전에 페칭하고, 클라이언트 측에서 빠르게 데이터를 제공하는 방식으로 SSR의 장점을 최대한 활용할 수 있다. Next.js의 SSR을 잘 활용하면 데이터 로딩 시간이 줄어들어 사용자 경험이 크게 향상되며, 실시간 데이터 갱신이 필요한 페이지에도 유연하게 대응할 수 있다.