[Next.js 마스터리] Next.js페이지 렌더링 -SSR

TK·2024년 1월 18일
0

SSR

(Server Side Rendering, 서버사이드렌더링)

  • 서버 측에서 페이지를 렌더링 하는 것
  1. 브라우저에서 /mypage 페이지 요청

  2. 서버에서 페이지에 필요한 데이터를 불러와 완성된 mypage.html 생성

  3. 서버에서 클라이언트로 mypage.html 반환

  4. 클라이언트는 ㉠렌더링이 완료된 페이지를 화면에 ㉡렌더링 시킴

    ㉠렌더링 : 리액트 컴포넌트와 같이 자바스크립트로 작성되어 있는 페이지를 실제 HTML코드로 변환하는 과정을 말함

    ㉡렌더링 : HTML을 화면(브라우저)에 실제로 그려내는 것을 말함

  5. 4번 이후 화면 상태는 모든 컨텐츠가 다 그려진 상태이지만 인터렉션은 불가능함 (아직 JS파일을 못 받았음)

  6. 서버가 JavaScript Bundle을 보내줌

  7. 클라이언트가 JavaScript코드와 HTML요소들을 서로 연결함 (Hydration, 수화)

  8. 완성된 웹페이지가 화면에 렌더링

  • 전체과정

SSR의 특징

  • 요청시 서버가 즉각적으로 페이지를 생성해 반환하는 렌더링 전략이다.
  • CRS에 비해 SEO 측면에서 월등하다.
  • 첫 페이지 로딩속도가 평균적으로 CRS보다 빠르다.

getServerSideProps

  • Next앱에서 특정 페이지를 SSR방식으로 작동시키는 함수 이름
  • SSR을 위해 서버측에서 페이지 컴포넌트에게 전달할 데이터를 설정하는 함수
  • 반드시 객체를 반환해야함
  • 이 객체는 반드시 props: {}를 포함해야 함
  • 오직 서버에서만 실행됨
  • 이 함수 내에서는 window.location 과 같이 window객체에 접근 불가 (존재하지 않는 값)
export default function Home({ name }) {
  return (
    <div>{ name }</div>
  );
}

export const getServerSideProps = async () => {  // 👈🏻SSR 함수
  return {
    props: {
      name: "KOREA",
    },
  };
};
  • 출력화면
  • 개발자도구 Components 확인
  • 서버측에서 미리 렌더링 되었음을 확인하는 방법
    1. (브라우저 화면)우 클릭 - 페이지소스보기
    2. 개발자도구 - Network - localhost클릭 (요청시 처음 반환한 html파일)

서버측 출력 / 클라이언트측 출력

export default function Page() {
  console.log("2");   
  useEffect(() => {   // 브라우저에서만 출력 (컴포넌트 마운트는 서버측에서 발생하지 않음 )
    console.log("3"); 
  }, []);
  return <div>...</div>;
}
 
export const getServerSideProps = async () => {
  console.log("1");   // 서버에서만 출력 (새로고침할때마다)
  return {
    props: {},
  };
};
  • 서버측 콘솔에서 출력되는 콘솔 메세지 : 1, 2
  • 클라이언트측 콘솔에서 출력되는 콘솔 메세지 : 2, 3

2번의 경우 : 서버에서 렌더링하기 위해 한번 실행되고, 그 후 브라우저에서 Hydration과정 끝난 후 한번더 마운트 되어 실행되기 때문에 양쪽에서 출력 된다. (서버 → 브라우저)


Query String 동적 경로

  • ~/search?q={검색어}
export default function Search() {
  return <div>...</div>;
}
 
export const getServerSideProps = async (context) => {
  const { q } = context.query; // 👈🏻Query String으로 전달된 값 q에 접근
  return {
    props: {},
  };
};

실습


api.js (기존 사용 코드)

import axios from "axios";  // ※ axios 라이브러리 설치하기

// 전체 국가 데이터
export async function fetchCountries() {
  try {
    const response = await axios.get("https://naras-api.vercel.app/all");
    return response.data;
  } catch (e) {
    return [];
  }
}

// 검색 결과 데이터
export async function fetchSearchResults(q) {
  try {
    const response = await axios.get(`
  https://naras-api.vercel.app/search?q=${q}
  `);

    return response.data;
  } catch (e) {
    return [];
  }
}

// 특정 국가 데이터
export async function fetchCountry(code) {
  try {
    const response = await axios.get(
      `https://naras-api.vercel.app/code/${code}`
    );
    return response.data;
  } catch (e) {
    return null;
  }
}

pages/index.js

import { fetchCountries } from "@/api";

export default function Home({ countries }) {
  return (
    <div>
      {countries.map((country) => {
        return <div key={country.code}>{country.commonName}</div>;
      })}
    </div>
  );
}

export const getServerSideProps = async () => {
  // API호출 코드가 필요함
  const countries = await fetchCountries();
  return {
    props: {
      countries,
    },
  };
};
  • 출력 화면
  • 컴포넌트 확인
  • 네트워크 확인
  • 클라이언트측 네트워크 확인 : 아무것도 없음

pages/search/index.js (동적경로 사용)

import { fetchSearchResults } from "@/api";
import SubLayout from "@/components/SubLayout";

export default function Search({ countries }) {
  return (
    <div>
      {countries.map((country) => {
        return <div key={country.code}>{country.commonName}</div>;
      })}
    </div>
  );
}

Search.Layout = SubLayout;

export const getServerSideProps = async (context) => { // 👈🏻 매개변수 이용
  //1.검색결과 api호출
  //2.props리턴
  const { q } = context.query; // 쿼리스트링 꺼내오기
  let countries = [];
  if (q) {
    countries = await fetchSearchResults(q);
  }
  return {
    props: { countries },
  };
};
  • 컴포넌트 확인
  • 네트워크 확인

pages/country/[code].js

import { fetchCountry } from "@/api";
import SubLayout from "@/components/SubLayout";
import { useRouter } from "next/router";

export default function Country({ country }) {
  // 클라이언트에서 사용
  const router = useRouter();
  const { code } = router.query;
  return (
    <div>
      {country.commonName}
      {country.officialName}
    </div>
  );
}

Country.Layout = SubLayout;

export const getServerSideProps = async (context) => {
  // 서버측에서 불러옴
  const { code } = context.params; //url parameter
  let country = null;
  if (code) {
    country = await fetchCountry(code);
  }
  return {
    props: { country },
  };
};
  • 출력 화면

URL parameter 불러오는 방식 차이 이해하기

  • 클라이언트측에서 이용하는 경우 : 컴포넌트 내부에서 불러옴
    (브라우저에서 hydration과정이 일어난 뒤에 동작)
  const router = useRouter(); // 리액트훅을 사용
  const { code } = router.query;
  • 서버측에서 이용하는 경우 : context객체에서 불러옴
  const { code } = context.params;
profile
쉬운게 좋은 FE개발자😺

0개의 댓글