๐Ÿ’ฃ Next.js_03 Server-side Rendering/SSR

qhflrnfl4324ยท2022๋…„ 1์›” 4์ผ
2

Next.js

๋ชฉ๋ก ๋ณด๊ธฐ
3/4
post-thumbnail

Next.js

์ฝ”๋”ฉ์•™๋งˆ Next.js ๊ฐ•์˜๋ฅผ ๋ณด๊ณ  ์ •๋ฆฌํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

๐Ÿ”ช ๋กœ๋”ฉ ํ™”๋ฉด ๊ตฌํ˜„

  • API ํ˜ธ์ถœ์„ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ฌ ๋•Œ, ๋กœ๋”ฉ ํ™”๋ฉด์„ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ค€๋‹ค.
// indax.js
import Axios from "axios";
import Head from "next/head";
import { Header, Divider, Loader } from "semantic-ui-react";
import { useEffect, useState } from "react";
import styles from "../styles/Home.module.css";
import ItemList from "../src/component/ItemList";

export default function Home() {
  const [list, setList] = useState([]);
  // ์ฒซ ํ™”๋ฉด์—์„œ ๋กœ๋”ฉ ํ™”๋ฉด์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด isLoading์œผ๋กœ state ์ƒ์„ฑ
  const [isLoading, setIsLoading] = useState(true);

  const API_URL =
    "http://makeup-api.herokuapp.com/api/v1/products.json?brand=maybelline";

  function getData() {
    Axios.get(API_URL).then((res) => {
      console.log(res.data);
      setList(res.data);
      // API ํ˜ธ์ถœ์ด ๋๋‚˜๋ฉด setIsLoading๋Š” false
      setIsLoading(false);
    });
  }

  useEffect(() => {
    getData();
  }, []);

  return (
    <div>
      <Head>
        <title>Home | ๋ณด๋ฆฌ๊ตฌ๋ฆฌ</title>
      </Head>
      {isLoading && (
        <div style={{ padding: "200px 0" }}>
          <Loader inline="centered" active size="big">
            Loading
          </Loader>
        </div>
      )}
      {!isLoading && (
        <>
          <Header as="h3">๋ฒ ์ŠคํŠธ ์ƒํ’ˆ</Header>
          <Divider />
          <ItemList list={list.slice(0, 9)} />
          <Header as="h3">์‹ ์ƒํ’ˆ</Header>
          <Divider />
          <ItemList list={list.slice(9)} />
        </>
      )}
    </div>
  );
}

ํ•ด๋‹น ์ฝ”๋“œ์—์„œ ๊ถ๊ธˆํ–ˆ๋˜ ๋ถ€๋ถ„

// ์ด ๋ถ€๋ถ„์˜ ์ฝ”๋“œ๋ฅผ
  <Header as="h3">๋ฒ ์ŠคํŠธ ์ƒํ’ˆ</Header>
  <Divider />
  <ItemList list={list.slice(0, 9)} />
  <Header as="h3">์‹ ์ƒํ’ˆ</Header>
  <Divider />
  <ItemList list={list.slice(9)} />

{
  !isLoading && <>
  // ์—ฌ๊ธฐ์— ๋„ฃ๋Š” ๊ฒƒ๊ณผ ๋„ฃ์ง€ ์•Š๋Š” ๊ฒƒ์˜ ์ฐจ์ด๊ฐ€ ๋ฌด์—‡์ธ์ง€..
  </>;
}
  • ์™ผ์ชฝ ํ™”๋ฉด์ด {!isLoading} ์ ์šฉ ์ „ / ์˜ค๋ฅธ์ชฝ ํ™”๋ฉด์ด ์ ์šฉ ํ›„
    => {!isLoading} ์„ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„ ๊ฐ™์€ ํ™”๋ฉด์„ ๋ณด์—ฌ์ค€๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋Š”๋ฐ ์•„๋ž˜์— ๊ฐ€๋ ค์ ธ์„œ ๋ณด์ด์ง€ ์•Š์•˜์—ˆ๋‹ค. ์ž˜ ํ™•์ธํ•ด๋ณผ ๊ฒƒ!
  • ์ตœ์ข…์ ์œผ๋กœ ์‚ผํ•ญ์—ฐ์‚ฐ์ž๋กœ ๋ฐ”๊ฟ”์„œ ์ฝ”๋“œ ์ž‘์„ฑ
return (
  <div>
    <Head>
      <title>Home | ๋ณด๋ฆฌ๊ตฌ๋ฆฌ</title>
    </Head>
    {isLoading ? (
      <div style={{ padding: "200px 0" }}>
        <Loader inline="centered" active size="big">
          Loading
        </Loader>
      </div>
    ) : (
      <>
        <Header as="h3">๋ฒ ์ŠคํŠธ ์ƒํ’ˆ</Header>
        <Divider />
        <ItemList list={list.slice(0, 9)} />
        <Header as="h3">์‹ ์ƒํ’ˆ</Header>
        <Divider />
        <ItemList list={list.slice(9)} />
      </>
    )}
  </div>
);
  • [id].js๋„ ๋กœ๋”ฉ ํ™”๋ฉด ๊ตฌํ˜„

๐Ÿ”ช ์ •์  ์ƒ์„ฑ / Server Side Rendering

  • Next.js๋Š” ๋ชจ๋“  ํŽ˜์ด์ง€๋ฅผ ์‚ฌ์ „ ๋ Œ๋”๋ง(Pre-rendering) ํ•œ๋‹ค => ๋ฏธ๋ฆฌ HTML ํŒŒ์ผ์„ ๋งŒ๋“ฆ

    • Pre-rendering์€ ๋” ์ข‹์€ ํผํฌ๋จผ์Šค๋ฅผ ๋‚ธ๋‹ค.
    • ๊ฒ€์ƒ‰์—”์ง„์ตœ์ ํ™”(SEO)์—๋„ ์ข‹๋‹ค.
  • Pre-rendering์˜ 2๊ฐ€์ง€ ํ˜•ํƒœ : ์ฐจ์ด์ ์€ ์–ธ์ œ HTML ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๋Š”๊ฐ€

    1. ์ •์  ์ƒ์„ฑ : ์œ ์ €๊ฐ€ ์š”์ฒญํ•˜๊ธฐ ์ „์— ๋ฏธ๋ฆฌ ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค์–ด ๋†“์•„๋„ ์ƒ๊ด€ ์—†์„ ๊ฒฝ์šฐ
    • ํ”„๋กœ์ ํŠธ๊ฐ€ ๋นŒ๋“œํ•˜๋Š” ์‹œ์ ์— HTML ํŒŒ์ผ๋“ค์ด ์ƒ์„ฑ
      => ํ•œ ๋ฒˆ ํŒŒ์ผ๋“ค์„ ์ญ‰ ๋งŒ๋“ค์–ด ๋†“๊ณ  ํ˜ธ์ถœ์ด ๋“ค์–ด์˜ฌ ๋•Œ๋งˆ๋‹ค ์žฌํ™œ์šฉ
    • ๋ชจ๋“  ์š”์ฒญ์— ์žฌ์‚ฌ์šฉ
    • ํผํฌ๋จผ์Šค๋ฅผ ์ด์œ ๋กœ, Next.js๋Š” ์ •์  ์ƒ์„ฑ์„ ๊ถŒ๊ณ 
    • ์ •์  ์ƒ์„ฑ๋œ ํŽ˜์ด์ง€๋“ค์€ CDN์— ์บ์‹œ๊ฐ€ ๋œ๋‹ค.
    • getStaticProps / getStaticPaths
    1. Server Side Rendering (SSR, Dynamic Rendering) : ์š”์ฒญ์ด ์žˆ์„ ๋•Œ๋งˆ๋‹ค ๋‹ค์‹œ ๊ทธ๋ฆด ๊ฒฝ์šฐ
    • ๋งค ์š”์ฒญ๋งˆ๋‹ค HTML์„ ์ƒ์„ฑ
    • CDN์— ์บ์‹œ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์กฐ๊ธˆ ๋Š๋ฆด ์ˆ˜ ์žˆ๋‹ค.
    • ์„œ๋ฒ„๋ฅผ ํ†ตํ•ด Pre-rendering๋œ ํŽ˜์ด์ง€๋Š” ํ•ญ์ƒ ์ตœ์‹  ์ƒํƒœ๋ฅผ ์œ ์ง€
    • getServerSideProps

๊ทธ๋Ÿผ ์ •์  ์ƒ์„ฑ๊ณผ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง์„ ์–ด๋–ป๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ์„๊นŒ?

  • ์ฒซ ํŽ˜์ด์ง€์˜ ๊ฒฝ์šฐ, ํ™”๋ฉด์„ ๊ทธ๋ฆฌ๋Š” API๋ฅผ ๋น„๋™๊ธฐ๋กœ ํ˜ธ์ถœ
    => ์ผ๋‹จ ์ฒซ ํ™”๋ฉด์— ๋“ค์–ด์˜จ ๋‹ค์Œ ์ƒํ’ˆ ๋ฆฌ์ŠคํŠธ๋“ค์„ ๋ฐ›๊ธฐ ์œ„ํ•ด ํ˜ธ์ถœํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์ •์  ์ƒ์„ฑ์œผ๋กœ๋„ ์ถฉ๋ถ„ํ•˜๋‹ค
  • ์ƒ์„ธ ํŽ˜์ด์ง€์˜ ๊ฒฝ์šฐ, ๋งค๋ฒˆ ํ•ด๋‹น ์ƒํ’ˆ์— ๋Œ€ํ•œ ๋‚ด์šฉ์ด ๋ฐ”๋€๋‹ค.
    => ํ™”๋ฉด์ด ๋ณด์ด๊ธฐ ์ „์— ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์–ด์•ผ ํ•œ๋‹ค.
    => ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์–ด์•ผ ๊ฒ€์ƒ‰์—”์ง„์—์„œ ์ฝ์„ ์ˆ˜ ์žˆ๊ณ , ์นด์นด์˜คํ†ก์ด๋‚˜ ํŽ˜๋ถ์œผ๋กœ ๊ณต์œ ํ–ˆ์„ ๋•Œ ์ •๋ณด๊ฐ€ ๋‚˜์˜จ๋‹ค.
    => ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง์œผ๋กœ ๊ตฌํ˜„
// [id].js
import Axios from "axios";
import Head from "next/head";
import Item from "../../src/component/Item";

//  getServerSideProps๋ฅผ ํ†ตํ•ด item์„ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
// ์‘๋‹ต๊ฐ’์„ item์˜ props๋กœ ๋„˜๊ฒจ์ค„ ์ˆ˜ ์žˆ๋‹ค.
const Post = ({ item }) => {
  // ์•„์ดํ…œ์ด ์žˆ์œผ๋ฉด ์•„์ดํ…œ์„ ๊ทธ๋ ค์ค€๋‹ค.
  // ํŽ˜์ด์ง€ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ํ™•์ธํ–ˆ์„ ๋•Œ ๋ฐ์ดํ„ฐ๋“ค์ด ์ž˜ ๋‚˜์˜จ๋‹ค๋ฉด ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง์ด ์ž˜ ๋™์ž‘ํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ
  // ์ด ๋ฐ์ดํ„ฐ๋ฅผ ์ด์šฉํ•ด SEO, ๊ณต์œ  ๋“ฑ์ด ์ž˜ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค
  // ๊ทธ๋ฆฌ๊ณ  ํ•ญ์ƒ ์ตœ์‹  ์ƒํƒœ๋ฅผ ์œ ์ง€๋˜๋ฏ€๋กœ ๊ฐ€๊ฒฉ์ด๋‚˜ ํŒ๋งค์ƒํƒœ ๋“ฑ์ด ๋ณ€๊ฒฝ๋˜์–ด๋„ ๋ฐ”๋กœ ์ ์šฉ๋œ๋‹ค.
  // ๋‹ค๋งŒ ์š”์ฒญ ํšŸ์ˆ˜๊ฐ€ ๋Š˜์–ด๋‚˜ ํผํฌ๋จผ์Šค๋Š” ๋–จ์–ด์ง„๋‹ค
  return (
    <>
      {item && (
        <>
          <Head>
            <title>{item.name}</title>
            <meta name="description" content="item.description"></meta>
          </Head>
          <Item item={item} />
        </>
      )}
    </>
  );
};

export default Post;

// getServerSideProps๋กœ ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ด
// context๋Š” params, ์š”์ฒญ, ์‘๋‹ต, ์ฟผ๋ฆฌ ๋“ฑ์˜ ์ •๋ณด๊ฐ€ ๋‹ด๊ฒจ์ ธ์„œ ์˜จ๋‹ค.
export async function getServerSideProps(context) {
  const id = context.params.id;
  const apiUrl = `http://makeup-api.herokuapp.com/api/v1/products/${id}.json`;
  const res = await Axios.get(apiUrl);
  const data = res.data;

  return {
    props: {
      item: data,
    },
  };
}

0๊ฐœ์˜ ๋Œ“๊ธ€