[Next.js V13] 정리 노트 - 2

Next JS 컴포넌트 분리하기

Intro


컴포넌트 분리


React의 하나의 철칙인 컴포넌트 분리를 적용해봅시다. componets폴더를 만듭니다.

tsx
// components/Header.tsx
"use client"

import { useRouter } from "next/navigation";
import { useState } from "react";

export default function Header() {
  const router = useRouter();
  const [location, setLocation] = useState("");
  return (
    <div className="h-64 bg-gradient-to-r from-[#0f1f47] to-[#5f6984] p-2">
      <div className="text-center mt-10">
        <h1 className="text-white text-5xl font-bold mb-2">
          Find your table for any occasion
        </h1>
        {/* SEARCH BAR */}
        <div className="text-left text-lg py-3 m-auto flex justify-center">
          <input
            className="rounded  mr-3 p-2 w-[450px]"
            type="text"
            placeholder="State, city or town"
            value={location}
            onChange={(e) => setLocation(e.target.value)}
          />
          <button className="rounded bg-red-600 px-9 py-2 text-white" onClick={() => {
            if(location === "banana") return;
            router.push("/search");
          }}>
            Let's go
          </button>
        </div>
        {/* SEARCH BAR */}
      </div>
    </div>
  )
}

위의 컴포넌트에서는 훅을 사용하므로 클라이언트 요소로 바꿔주어야 합니다. "use client"를 상단에 입력합니다.

tsx
// page.tsx
import Header from './components/Header'
import NavBar from './components/NavBar'
import RestaurantCard from './components/RestaurantCard'

export default function Home() {
  return (
    <main className="bg-gray-100 min-h-screen w-screen">
      <main className="max-w-screen-2xl m-auto bg-white">
        <NavBar/>
        <main>
          <Header/>
          <div className="py-3 px-36 mt-10 flex flex-wrap justify-center">
            <RestaurantCard/>
          </div>
        </main>
      </main>
    </main>
  )
}

SSR CSR


Client Side Rendering은 특정 페이지를 서버측에 요청합니다. 그런 다음 서버는 계속해서 렌더링할 HTML페이지를 제공합니다. 이 과정에서 클라이언트는 Javascript 번들 구문을 분석해 HTML을 렌더링하게 됩니다. Server Side Rendering을 사용하는 경우 말그대로 서버측에서 HTML을 렌더링하여 클라이언트에게 보내기만 합니다. Next에서는 이 과정 둘다 적용이 가능합니다.

Server Side Rendering의 경우 클라이언트가 구문을 분석할 필요가 없어 초기 로드가 훨씬 빨라집니다. 또한 SEO면에서도 서버에서 렌더링을 끝냈기 때문에 우월합니다.

이전에서 tsx파일에 훅을 적용하기위해 "use client"라고 명시했다면 클라이언트 사이드에서 렌더링을 진행하게 됩니다. 즉, default는 Server Side Rendering입니다.

최종적으로 언제든지 혼용해 사용할 수 있으므로 특정 서버 구성 요소(Component)는 클라이언트 구성 요소(Component)를 렌더링(자식요소)하고, 특정 서버 구성 요소가 다른 서버 구성요소(자식요소)를 렌더링 할 수 있습니다.
하지만 클라이언트 구성 요소는 서버 구성 요소를 렌더링하지 못합니다. 클라이언트 구성 요소 클라이언트 구성 요소(자식요소)만 렌더링 할 수 있습니다.

그런데 밑에 그림을 보시면 클라이언트 요소에서 서버 구성 요소를 자식으로 가지기도 합니다. 실제로는 자식 요소로 전달되기만 할뿐, 서버측에서 렌더링됩니다.

정리하자면 다음과 같습니다.

layout.tsx을 이용해 반복 컴포넌트 줄이기


루트 경로에 layout.tsx의 경우 루트 레이아웃입니다. 즉, 각 페이지마다 공통으로 쓰이는 레이아웃입니다.

tsx
// layout.tsx
import NavBar from './components/NavBar'
import './globals.css'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      {/*
        <head /> will contain the components returned by the nearest parent
        head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
      */}
      <head />
      <body>
        <main className="bg-gray-100 min-h-screen w-screen">
          <main className="max-w-screen-2xl m-auto bg-white">
            <NavBar/>
            {children}
          </main>
        </main>
      </body>
    </html>
  )
}

이 페이지에서는 모든 페이지에서 공통적으로 쓰이는 요소 Header, Footer등 반복되는 컴포넌트를 줄일 수 있죠. 또한 layout.tsx파일은 루트 경로가 아닌 각 페이지별 경로에도 추가하여 해당 페이지의 공통 컴포넌트를 정리할 수도 있습니다.

tsx
// restaurant/[slug]/layout.tsx
import Header from "./components/Header";

export default function RestaurantLayout({
  children,
  // children은 타입이 React.ReactNode
}: {
  children: React.ReactNode
}) {
  return (
    <main>
      <Header/>
      <div className="flex m-auto w-2/3 justify-between items-start 0 -mt-11">
        {children}
      </div>
    </main>
  )
}

head.tsx를 이용해 메타 데이터 정리하기


layout.tsx파일과 같이 루트 경로에 head.tsx파일이 존재합니다.

tsx
// head.tsx
export default function Head() {
  return (
    <>
      <title>Create Next App</title>
      <meta content="width=device-width, initial-scale=1" name="viewport" />
      <meta name="description" content="Generated by create next app" />
      <link rel="icon" href="/favicon.ico" />
    </>
  )
}

head.tsx도 마찬가지로 page 경로 마다 추가하여 사용할 수 있습니다.

public 리소스 디렉토리


public 디렉토리의 경로 보통 리소스 파일을 저장해둡니다. 리소스 파일은 /이름과 같이 접근할 수 있습니다.