노마드코더 Next.js

먼지·2022년 3월 3일
0
post-thumbnail

노마드코더 NextJS 시작하기

1. FRAMEWORK OVERVIEW

설치

$ npx create-next-app

$ npx create-next-app --typescript

Library vs Framework

next.js같은 framework에선, 우린 특정한 규칙을 따라서 특정한 걸 해야 함. 그것들을 따랐을 때 정상 작동. 우리가 할 수 있는 유일한 것은 pages 안에서 뭔가를 만드는 것 뿐.

추상화 abstraction

framework는 코드를 어떤 곳에 넣으면, framework가 그 코드를 부르는 형태임.
React.js는 우리가 원할 때 부르고 원할 때 사용하는 library고, framework는 약간 집 같은 것인데, 내가 코드를 적절한 곳에 넣어야 하는 거야. 그리고 기본적으로 내가 그 집을 수정할 수는 없음(싸울 수 없음) 약간 게스트 같은 느낌. 나의 코드가 게스트
우리의 코드를 올바른 위치에 둔다면, framework가 코드를 불러서 사용할 거임(pages/about.js)

라이브러리 vs 프레임워크
https://www.youtube.com/watch?v=t9ccIykXTCM

라이브러리는 우리가 갖다쓰는 것, 프레임워크는 정해진 틀 안에서 커스터마이징

라이브러리와 프레임워크의 주요 차이점

라이브러리와 프레임워크의 주요 차이점은 "Inversion of Control"(통제의 역전)입니다.
라이브러리에서 메서드를 호출하면 사용자가 제어할 수 있습니다.
그러나 프레임워크에서는 제어가 역전되어 프레임워크가 사용자를 호출합니다.

라이브러리
사용자가 파일 이름이나 구조 등을 정하고, 모든 결정을 내림

프레임워크
파일 이름이나 구조 등을 정해진 규칙에 따라 만들고 따름

Pages

pages 폴더 안의 파일의 이름은 그대로 url로 들어감 URL의 이름은 파일명. 반면에 component의 이름은 그닥 중요하지 않고 중요한 건 그게 export default여야 함!

// pages/about.js

export default function Tomato() {
  return <div>about us</div>
}

존재하지 않는 url 접속 시 404 페이지가 뜸.

파일명이 jsx가 될 필요도 없고 react를 import해올 필요도 없음! hooks를 써야 하는 경우 제외

// pages/about.js

// import react from "react";

export default function Home() { ... }

Static Pre Rendering

next.js의 가장 좋은 기능 중 하나는, 앱에 있는 페이지들이 미리 렌더링 된다는 것임. 이것들은 정적(static)으로 생성됨

cra는 client-side render를 하는 앱을 만듦. client-side란 너의 브라우저가 유저가 보는 UI를 만드느 모든 것을 한다는 것을 으미ㅣ함. 소스코드를 보면 div id가 root라는 걸 제외하곤, 모두 다 자바스크립트임. 사실상 비어있는 div
noscript는 유저가 브라우저에서 자바스크립트를 비활성화 했을 때만 나오는 태그

즉, 브라우저가 HTML을 가져올 때 비어있는 div로 가져온다는 걸 의미함. 그 후에 브라우저가 모든 자바스크립트를 요청해서 자바스크립트와 react.js를 실행시키고 그 후에 앱이 유저에게 보이게 됨. UI가 만들어지는 것!
네트워크 상태가 느리면 사람들의 우리의 웹사이트에 들어갔을 때, 어떤 UI도 없이 흰 화면만 보게 될 것임..
이것이 브라우저가 말 그대로 모든 것을 하는 client-side rendering

next.js 웹사이트의 소스코드를 보면 유저가 매우 느린 연결을 하고 있거나, 자바스크립트가 완전히 비활성화 되어 있어도 적어도 유저는 어떠한 HTML은 볼 수 있음. index.js? 페이지는 미리 렌더링 됨. (생성됨)

next.js는 초기 상태로 pre-rendering을 함.

이건 SEO에 정말 좋음. 구글 같은 검색엔진에게도 유저에게도 너무 좋음. 유저들이 너의 웹사이트에 가면 이미 무언가가 있다는 거지.

Styles

CSS Modules

이름은 상관없음 중요한 건 .module.css 패턴

// NavBar.module.css

.link {
  color: black;
  text-decoration: none;
}

.active {
  color: rosybrown;
}
// components/NavBar.js
import Link from 'next/link';
import { useRouter } from 'next/router';

import styles from './NavBar.module.css';

export default function NavBar() {
  const router = useRouter();
  return (
    <nav>
      <Link href="/">
        <a className={`${styles.link} ${router.pathname==='/' ? styles.active : ''}`}>
          Home
        </a>
      </Link>
      <Link href="/about">
        <a className={[
          styles.link,
          router.pathname==='/about' ? styles.active : '',
        ].join('')}>About</a>
      </Link>
    </nav>
  )
}

페이지가 빌드될 때, 클래스이름을 무작위로 바꿔주므로 충돌을 겪지 않아도 됨.

하지만, 두 개의 파일이 필요하고 클래스 이름을 기억해야 한다는 단점이 있음. 특히 조건자는 복잡.

Styles JSX

NextJS 고유의 방법. 독립적임

NavBar anchor들에 스타일 영향을 미치지 못함 태그 이름으로 타켓팅

// pages/index.js

import NavBar from '../components/NavBar';

export default function Home() {
  return (
    <div>
      <NavBar />
      <h1>Hello</h1>

      <style jsx>{`
        a {
          color: white;
        }
      `}</style>
    </div>
  )
}

다른 코드에서 active라는 이름의 클래스를 갖고 있어도 NavBar의 스타일은 적용 x. 이 스타일들은 오직 이 컴포넌트 내부로 범위가 한정됨

// components/NavBar.js

export default function NavBar() {
  const router = useRouter();
  return (
    <nav>
      <Link href="/">
        <a className={router.pathname==='/' ? 'active' : ''}>Home</a>
      </Link>
      <Link href="/about">
        <a className={router.pathname==='/about' ? 'active' : ''}>About</a>
      </Link>

      <style jsx>{`
        a {
          text-decoration: none;
        }
        .active {
          color: rosybrown;
        }
      `}</style>
    </nav>
  )
}

자바스크립트 문자열이기 때문에 props를 이런 식으로 사용 가능

...
<style>{`
  .active {
    color: ${props.color};
  }
`}</style>

Built-In CSS Support (내장 CSS 지원)

Next.js를 사용하면 JavaScript 파일에서 CSS 파일을 가져올 수 있습니다.
이것은 Next.js가 import 개념을 JavaScript 이상으로 확장하기 때문에 가능합니다.
https://nextjs.org/docs/basic-features/built-in-css-support#css-in-js

Adding Component-Level CSS
Next.js는[name].module.css 파일 명명 규칙을 사용하여 CSS Module을 지원합니다.

Sass Support
Next.js를 사용하면.scss 및.sass 확장자를 모두 사용하여 Sass를 가져올 수 있습니다.

CSS-in-JS
격리된 범위 CSS에 대한 지원을 제공하기 위해 styled-jsx를 번들로 제공합니다. 목표는 불행히도 서버 렌더링을 지원하지 않고 JS 전용인 Web Components와 유사한 "Shadow CSS"를 지원하는 것입니다.

< style jsx>{`
p {
color: blue;
}
div {
background: red;
}
@media (max-width: 600px) {
div {
background: blue;
}
}
`}< /style>

styled-jsx
https://github.com/vercel/styled-jsx

Custom App

styled jsx는 컴포넌트에 scoped(한정되어 적용) 됨. 하지만 어쩔 땐 font-family, reset CSS 등의 Global(전역) Styles를 추가하고 싶을 것임!
'global'이라는 Prop을 추가하면 됨.

<style jsx global>{`
  a {
    ...
`}</style>

하지만 페이지별로 생각해봐야 함. 우리가 /about 페이지로 넘어가면 우린 다른 페이지에 있음 그래서 렌더링되고 있는 컴포넌트 또한 다른 아이임.
모든 페이지들을 설정할 수 있는 지점을 원함 - App Component는 NextJS가 모든 페이지를 렌더링할 수 있게 하는 일종의 어떤 컴포넌트의 청사진.
무조건 _app.js 이 이름! 왜냐면 nextjs는 랜더링되기 전에 먼저 app을 봄

한 prop은 component, 다른 하나는 page. 이건 프레임워크가 정한 것임

// pages/_app.js

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp;

Typescript 사용시

import { AppProps } from 'next/dist/shared/lib/router/router'

export default function App({ Component, pageProps }: AppProps) {
}

Recap

  • 라이브러리와 프레임워크의 차이점
    - 라이브러리는 우리가 호출하는 것. 프레임워크는 코드를 특정 장소에 넣고 룰에 따르면 프레임워크가 우리 코드를 호출하는 것
    - 단지 코드를 올바른 장소에 넣게만 하면 모든 게 우링 위해 일어날 것임
    - pages/index.js -> /index (X) -> /

  • 리액트와 넥스트의 차이
    - cra에서는 모든 게 클라이언트 사이드 렌더링(CSR)
    - nextjs에선 유저가 거기 가기도 전에 페이지가 미리 만들어짐(pre render). 그래서 유저가 NextJS에 의해 만들어진 페이지에 갈 때, 우리 컨포넌트의 초기 상태는 자동적으로 렌더링된 상태가 됨. (HTML로 렌더링되어짐)

    • Rehydration 단계? NextJS가 백엔드상에서 ReactJS를 돌리고 있고, NextJS가 페이지를 pre-generate(사전생성) 할 거고 그건 HTML 페이지가 되고 유저는 그 HTML을 보게 될 것임. 하지만 유저가 모든 자바스크립트를 다운로드 한 후에, ReactJS가 다시 주도권을 가져와서 모든 게 일반적인 ReactJS처럼 동작하는 것
  • module css
    - XXXX.module.css ( .hello {} )

    • import styles from './XXXX.module.css'
  • styled jsx

    • style 태그를 열고 작성. 이건 React 태그가 아니고 일반 HTML 태그임
    • global은 이 styles를 "모두"에게 적용시켜줌. 근데 NextJS를 다룰 때에는 하나의 큰 어플리케이션이 아닌 각각의 나뉘어진 "페이지들"을 생각해야 함.
  • custom

    • 커스터마이즈하길 원한다면, NextJS가 페이지를 렌더링하기 위한 템플릿을 설정하길 원한다면 스스로 _app.js 파일을 정의하면 됨.
    • 복붙할 필요가 없다는 건 좋은 생각이지만, 페이지가 다 NavBar를 갖지 않아서 좋은 방법이 아닐 수도 있음. 고로 우리의 어플리케이션의 디자인 구조?에 관해서 생각하기
import '../styles/globals.css';

import NavBar from '../components/NavBar';

function MyApp({ Component, pageProps }) {
  return (
    <>
      <NavBar />
      <Component {...pageProps} />
    </>
  );
}

export default MyApp;

컴포넌트 이름은 중요치 않음. 하지만 파일 이름은 중요!(_app.js) NextJS가 파일을 보는 방식임.
프레임워크는 네 코드를 호출할 거다!

2. PRACTICE PROJECT

component 안에 들어가는 모든 것들이 우리의 html의 head 안에 보여짐. ```javascript import Head from 'next/head';

export default function Home() {
return (

<div>
  <Head>
    <title>Home | Next Movies</title>
  </Head>
  <h1>Home</h1>
</div>

)
}

![](https://velog.velcdn.com/images%2Fvanillovin%2Fpost%2F50f609e4-f5fd-436b-9f0a-a210de391d2b%2Fimage.png)

Head (next/head)
페이지 head에 엘리먼트를 추가하기 위한 내장 컴포넌트를 노출합니다.
head에 태그가 중복되지 않도록 하려면 다음 예제와 같이 태그가 한 번만 렌더링되도록 하는 key 속성을 사용할 수 있습니다.
이 경우 두 번째 meta property="og:title"만 렌더링됩니다. 중복 키 속성이 있는 메타 태그는 자동으로 처리됩니다.

// key를 지정해주지 않으면 meta og:title가 중복해서 2번 랜더링됩니다.
// (title은 지정하지 않아도 2번 랜더링 되지 않음)
< Head>
< title>My page title< /title>
< meta property="og:title" content="My page title" key="title" />
< /Head>
< Head>
< meta property="og:title" content="My new title" key="title" />
< /Head>

https://nextjs.org/docs/api-reference/next/head

IMDB Mobile App Design 이미지
https://dribbble.com/shots/11413278-Imdb-mobile-app-design

-

The Movie DB API 키 확인
https://www.themoviedb.org/settings/api

Get Popular (Movies)
`https://api.themoviedb.org/3/movie/popular?api_key=${API_KEY}`
https://developers.themoviedb.org/3/movies/get-popular-movies

Image (No Img Element)
HTML img 엘리먼트가 이미지를 표시하는 데 사용되었습니다. 더 나은 성능과 자동 이미지 최적화를 위해 Next.js의 내장 Image 컴포넌트를 사용하십시오.

import Image from 'next/image'

< Image
src="https://example.com/test"
alt="Landscape picture"
width={500}
height={500}
/>

https://nextjs.org/docs/messages/no-img-element


### Redirect and Rewrite 
API key를 공개하고 싶지 않거나 사용량 제한 문제로 인한 남용 등의 이유로 숨겨야 할 때 nextjs에서는!?
![](https://velog.velcdn.com/images%2Fvanillovin%2Fpost%2Fc07eb7cc-26ac-496e-9653-98b928120b78%2Fimage.png)

next.config.js
먼저 API key를 숨기지 않는 redirect가 있음. 여기에는 서버가 있고 실행되는 중

redirects는 url로 갈 때, rewrites는 유저를 redirect 시키기는 하지만 url은 변하지 않음. redirect와 비슷하긴 한데 유저가 URL 변화를 볼 수 없음
멋진 건 next.js가 이 request를 masking 한다는 것!
말하자면 가짜 fetching URL을 가지게 된 것....ㅏ 
```javascript
const nextConfig = {
  reactStrictMode: true,
  async redirects() {
    return [
      {
        // redirection 스텝1. source를 찾고 destination으로 보내기?
        // pattern matching 또한 지원함. 주소 뒤에 별표 붙이면 모든 걸 catch
        source: '/contact', // 'old-blog/:path' and 'old-blog/:path*'
        destination: '/form', // 'new-sexy-blog/:path' and 'new-sexy-blog/:path*'
        // redirection이 permanent(영구적)인지 아닌지에 따라서, 
        // 브라우저나 검색엔진이 이 정보를 기억하는지 여부가 결정됨.
        permanent: false,
      },
      // {
      //   source
      // }
    ]
  },
  async rewrites() {
    return [
      {
		source: '/api/movies',
        destination: `movie api & api key`
      }
    ]
  }
}

// pages/index.js
export  default function Home() {
  useEffect(() => {
    (async () => {
      const { results } = await (await fetch(`/api/movies`)).json();
      setMovies(results);
    })();
  }, [])

// .env
API_KEY=''

// next.config.js
/** @type {import('next').NextConfig} */
const API_KEY = process.env.API_KEY;

const nextConfig = {

Server Side Rendering

넥스트의 함수! 우리 페이지가 오직 server side render만 할지 선택할 수 있게됨. 근데 nextjs는 우리 app을 pre-render 해주고 초기화면으로 html을 줌
로딩을 보기 싫은 사람도 잇을 거임. 그 사람들은 fetch라던지 server에서 일어나는 data 관련된 작업을 모두 한 다음에 API가 모두 완료됐을 때 페이지를 render하려고 할 것. (노마드코더 사이트는 기본적으로 shell을 render. navigation, folder,.. 모든 걸 다. 그러고 나서 데이터가 들어옴!)
data가 들어오는 동시에 render - getServerSideProps라는 function을 export!

export default function Home() {
  const [movies, setMovies] = useState([]);

  useEffect(() => {
    (async () => {
      const { results } = await (await fetch(`/api/movies`)).json();
      setMovies(results);
    })();
  }, [])

  return (
    <div className="container">
      <Seo title="Home" />
      {!movies && <h4>Loading...</h4>}
      {movies?.map((movie) => (
        <div className="movie" key={movie.id}>
          <img src={`https://image.tmdb.org/t/p/w500/${movie.poster_path}`} />
          <h4>{movie.original_title}</h4>
        </div>
      ))}
       <style jsx>{`...`}</style>
    </div>
  )
}


// 이름은 정말 중요. 다른 걸로 바꾸면 안 됨
export async function getServerSideProps() {
  	// 이 자리에 어떤 코드를 쓰던지 간에 그 코드는 server에서 돌아감. client 쪽이 아닌 server 에서 작동. 이걸 이용해 API key를 숨길 수 있음
	// API key를 여기 쓴다면 절대 client에게 보여지지 않음. 이건 오직 백엔드 server에서만 실행됨!
}

이름이 제일 중요하고, export도 빼먹지 않기.
이 함수는 object를 하나 리턴하는데 그 object는 props라고 불리는 key 혹은 property를 가짐. 이 props 안에 우리의 result가 들어감
props라는 key 안에는 원하는 데이터를 아무거나 넣을 수 잇음

  • getServerSideProps는 server에서 실행될 것임.
  • 여기서 무엇을 return하던 이걸 props로써 page에게 줌

우리가 /(index.js) 홈페이지로 갈 때 일어나는 일은?! next.js가 Home을 받아서 render하기 위해

export default function MyApp({ Component, pageProps }) {
  return (
    <Layout>
      <Home {...pageProps} /> // <- 이 자리에 넣고 Home의 getServerSideProps를 호출함!
    </Layout>
  )
}

그리고 api를 요청해서 이 props를 return. 그러면 nextjs가 그 props를 이 자리에 넣을 것임.
<Home {...pageProps} <-요기 />
그 결과는 Home({results <- 에서 나타날 거고 }) {...}

getServerSideProps 이 function은 오직 sever side에서만 실행된다.
nextJS는 프론트엔드만 다루는 것이 아님 서버와 관련된 기능들도 많음
이제 이 페이지(index.js)는 100% 서버사이드에서 render될 것임.
에러 - 오직 absolute URL만 지원됨?
왜냐면 fetch('/api/movies') 이 주소는 front에서만 작동함. 앞에 localhost 추가하기

getServerSideProps
페이지에서 getServerSideProps(서버 측 렌더링)라는 함수를 export하는 경우 Next.js는 getServerSideProps에서 반환된 데이터를 사용하여 각 request에서 이 페이지를 pre-render합니다. getServerSideProps는 서버 측에서만 실행되며 브라우저에서는 실행되지 않습니다.
https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props

getServerSideProps를 사용하여 request시 데이터 fetch하기
다음 예는 request 시 데이터를 fetch하고 결과를 pre-render하는 방법을 보여줍니다.

function Page({ data }) {
// Render data...
}

// This gets called on every request
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch(`https://.../data`)
const data = await res.json()

// Pass data to the page via props
return { props: { data } }
}

export default Page

https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props#using-getserversideprops-to-fetch-data-at-request-time

로딩같은 걸 안 해도 되지만 api가 돌아오기 전에 화면에 아무것도 안 보일 거라는 말. 항상 server side rendering을 하고 싶은지. 즉, 데이터가 유요할 때 화면이 보여지게 되는 게 좋은지? 또는 loading 화면을 보여준 다음 데이터를 받는 것이 좋은지 선택을 하기

진짜 멋진건,,,,,, 어느 시점엔 reactJS가 프론트의 주도권을 다시 가질거라는것(클라이언트 사이드에서)

여기서 nextJS의 역할은 nextJS가 백엔드에서 받아온 props를 return해서 여기에 가져다 주면, 그럼 reactJS가 저 props를 가져와서 그걸 가지고 results array를 뽑아주는 것! 이걸 가지고 프론트에서 아무거나 다 할 수 있음.

=> 결국 우리가 하는 건, page가 유저에게 보여지기 전에 props를 받아오는 function을 만드는 거였음!
여기서 뭐가 return되던, 우리 페이지의 props값으로 사용될거이
더이상 로딩은 없는 대신에 만약 API load가 느리다면 유저가 아무것도 보지 못한 채로 오래 기다려야 한다는 단점이 있으
이런 약간의 협상을 하는 거지!!!!! nav bar와 footer, 그리고 가운데에 loading을 보여주는 것과 로딩없이 api가 완료되도록 기다린 후 모든 정보를 보여주는 것 중 선택은 우리의 몫 ^^

nextJS가 자동으로 props들을 넣어줄 거고, readtJS가 받아다가 흡수한다고 말할 수 있음. 우리가 물을 마시는 것처럼 reactJS가 이 props를 hydrate할 거야.

결론..
server side rendering을 할 것인지, static shell같은걸 가지고 데이터를 나중에 불러올 것인지 하나를 선택해..!

우리가 한 예시는 유저가 만약 자바스크립트를 비활성화 했더라도 작동될 거임 왜냐면 단지 html으로 이루어져 있으니까

Recap

클라이언트 사이드 렌더링 vs 서버 사이드 only 렌더링
getServerSideProps 함수를 활성하하기 전까지는 사전 생성된 html 페이지에 데이터가 포함되지 않았었음. Next.js는 페이지를 미리 html로 export해줌
React.js의 처리가 완려될 때까지 기다려야 하고 유저는 잠깐 혹은 api에서 데이터를 받아올 때까지 로딩중 단계를 보게됨.
항상 html이 완전한 상태로 준비되었으면 한다면! 즉, 유저가 접속했을 때 모든 데이터가 페이지에 들어있고 유저가 "로딩중" 상태를 보지 않았으면 한다면 이때 getServerSideProps를 사용하는 것

getServerSideProps에는 fetch를 통해 데이터베이스를 불러오는 등 원하는 모든 걸 할 수 있음. 이건 프론트에 보이지 않고 백엔드에서만 작동. 그리고 데이터가 모두 준비가 되었을 때 그제서야 유저들에게 페이지가 보이게 됨

준비가 다 되면 모든 데이터를 한 번에 보게 됨. 당연히 search engine은 이걸 아주 좋아함 ^^

정말 멋진 건 NextJS가 이 Props들을 여기 NEXT_DATA 부분에 넣어준다는 거야!
<script id="_NEXT_dATA" type="application/json">{...
자동적으로 그렇게 되는데, 이 부분을 ReactJS가 장악하는 거지. ReactJS가 로드되면 백엔드에서 만들어진 모든 state들을 장악해서 ReactJS를 이요한 상호작용이 가능해짐 Boo-yah!

hydration 정의 - https://www.youtube.com/watch?v=7mkQi0TlJQo

getServerSideProps 추가 설명
https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props

언제 getServerSideProps를 사용해야 하나요?
request time에 반드시 데이터를 fetch해와야 하는 페이지를 pre-render해야 하는 경우에만 getServerSideProps를 사용해야 합니다.
데이터를 pre-render할 필요가 없다면 client side에서 데이터를 가져오는 것을 고려해야 합니다.

클라이언트 측에서 데이터 가져오기 (Fetching data on the client side)
페이지에 자주 업데이트되는 데이터가 포함되어 있고 데이터를 pre-render할 필요가 없는 경우 클라이언트 측에서 데이터를 가져올 수 있습니다.
1. 먼저 데이터가 없는 페이지를 즉시 표시합니다.
2. 페이지의 일부는 Static Generation을 사용해 pre-render할 수 있습니다.
3. 없는 데이터를 위해 loading 상태를 표시할 수 있습니다.
4. 그런 다음 클라이언트 측에서 데이터를 가져와 준비가 되면 표시합니다.

이 접근 방식은 예를 들어 사용자 대시보드 페이지에 적합합니다.
왜냐하면 대시보드는 사용자별 비공개 페이지이기 때문에 SEO와는 관련이 없으며 페이지를 미리 렌더링할 필요가 없습니다. 또한 데이터는 자주 업데이트되므로 요청 시 데이터를 가져와야 합니다.

getServerSideProps가 오류 페이지를 렌더링합니까?
getServerSideProps 내부에서 오류가 발생하면 pages/500.js 파일이 표시됩니다.
500 page(서버 렌더링 오류 페이지)는 사용자가 커스터 마이징 할 수 있습니다.
개발 중에는 이 파일이 사용되지 않고 대신 개발 오버레이가 표시됩니다.

Dynamic Routes

Dynamic URL 다루는 법을 알아볼 거예용~
cra의 react-router-dom 세계에선 URL을 /movies/:id 이런 식으로 특정
Nestes Router(중첩 라우터)
페이지가 하나밖에 없다면 폴더를 만들어주지 않아도 됨.

// pages/movies/index.js
export default function All() {
  return "movies index";
}

// pages/movies/all.js
export default function All() {
  return "all";
}


이제 우리는 어떻게 Next.js에 URL에 변수가 필요한 것을 알려줄 수 있을까?
대괄호 안에 우리가 사용하고자 하는 변수명을 적기!

// pages/movies/[id].js

Link에도 as props 사용 가능
https://nextjs.org/docs/api-reference/next/link

HTML5부터 a태그에 div 및 flow content를 넣어도 문제 없다고 하네요 !
http://html5doctor.com/block-level-links-in-html-5/

실수로 { Link } 라 작성해서 에러났엇음

// pages/index.js
import Link from 'next/link'

navigating 하는 다른 방법은 nextjs의 router hook!

import { useRouter } from 'next/router';

export default function Home() {
  const router = useRouter();
  const onClick = (movieID) => {
    router.push(`/movies/${movieID}`);
  }
  ... 
}

API에 rewrite 하나 더 만들기! source랑 destination에 들어가는 명칭을 동일하게 해야 함.

next.config.js를 수정하면 서버를 껐다가 다시 시작해야함!

Next.js의 route 시스템은 route.push를 하거나 Link를 사용하는 외에도 URL에 정보를 숨겨 보낼 수 있음

URL에서 URL로 state를 넘겨주는 방법과 그걸 유저로부터 숨기는 방법은?!
push를 할 때에 우리에겐 선택지가 있음. url을 string이 아닌 객체로 보내줄 수도 잇

import { useRouter } from 'next/router';

export default function Home() {
  const router = useRouter();
  const onClick = (movieID) => {
    router.push({
      pathname: `/movies/${movieID}`,
      query: {
        id, 
        title: 'tomato'
      }
    });
  }
  ... 
}

이렇게하면 /movies/564?title=tomato. URL에 보이지 않도록 숨기려면? 두 번째 옵션인 "as"를 이용하기. "as"는 브라우저의 URL을 마스킹함.

// pages.index.js

router.push({
  pathname: `/movies/${movieID}`,
  query: {
	id, 
	title: 'tomato'
  }
}, `/movies/${movieID}`); // <- as option

// or

{movies.map(movie => (
  <Link
    as={`moveis/${movie.id}`}
    href={{
      pathname: `/movies/${movie.id}`,
      query: {
      id: movie.id,
      title: 'tomato'
    },
  }}>

router 객체를 들여다보면 우리가 담아준 데이터를 확인할 수 있음

const router = useRouter();
console.log(router);
/*
Object
...
▼ query:
  ▶ id: "1231",
  ▶ title: "tomato"
reload: f ()
...
*/

router.query.title은 유저가 홈페이지에서 상세페이지로 넘어올 때에만 존재함. (시크릿 창은 loading)

// pages/movies/[id].js

import { useRouter } from 'next/router'

export default function Detail() {
  const router = useRouter();
  return (
    <div>
      <h1>{router.query.title || 'Loading...'}</h1>
    </div>
  )
}

Catch All

catch-all URL은 뭐든 캐치해내는 URL
/movies/title/23432/a/b/c/d/e/..
항상은 아니지만 필요할 때가 있음.
우리는 영화 제목을 url에 넣고 싶어서 필요함. 이러면 SEO에 아주 좋음!

검색엔진 최적화(SEO) 기본 가이드

홈페이지에서 클릭해서 들어오지 않더라도 상세페이지에서 영화 제목을 볼 수 있음

일단 파일명 변경하기. [id].js /movies/:id 는 id라는 단 한 개의 변수만 받고 있음.
=> [...id].js
...을 붙이면 "Spider-Man:.." 등의 제목이 들어간 URL로도 상세페이지에 접근할 수 있음.

/movies/movietitle/23414 접속 후 console에 router를 찍어보면 query의 id를 보면 두 개가 들어있는 걸 볼 수 있음. 더이상 string 값이 아님.

console.log(router);
/*
...
▼ query: {id: Array(2)}
  ▶ id: (2) ['movietitle', '23414']
...
*/

[...id].js 여기에 "id"라고 하는 건 의미가 맞지 않으니까 variables나 parameters 같은 다른 걸로 바꿔줄 수 있음.

그런데 만약 누가 Incognito 모드에서 접속하면 에러가 발생할 거임. 왜냐면 이 페이지가 백엔드에서 pre-render 되기 때문. server에서 미리 렌더링 된다구~

그리고 server에는 router.query.params가 아직 존재하지 않음. router.query.params는 서버에서 아직 배열이 아니니까 구조분해할당 문법을 사용할 수 없음.

// || 논리연산자로 수정!
const [title, id] = router.query.params || [];

원한다면 getServerSideProps를 여기에서도 사용할 수 있음. 그럼 request에 대한 정보와 영화 제목도 얻을 수 있겠지
Next.js가 server-side context를 제공해줌

만약 유저에게 절대로 로딩 단계를 보여주고 싶지 않고 SEO에 아주아주 최적화되게 만들고 싶다면 getServerSide 사용하면됨.
이 경우엔 단순히 영화제목과 ID를 얻기 위해 사용했음. API로 데이터를 fetch를 해오기 위함이 아닌 조금 더 빠르게 데이터를 가져오기 위함.

export default function Detail({params}) {
  // const router = useRouter();
  const [title, id] = params; 
  return (
    <div>
      <h4>{title || 'Loading...'}</h4>
    </div>
  )
}

export function getServerSideProps({params:{params}}) {
  return {
    props: {
      params,
    },
  };
}

컴포넌트 내부에서 router를 사용하면 router는 프론트에서만 실행이됨. 위에 코드 getServerSideProps에선 API를 패치하지 않고 URL 정보를 가져오는 데에만 사용함.

이전에는 Detail 컴포넌트 내부에서 router를 사용했음. 하지만 컴포넌트 내부에 들어있는 router는 클라이언트 사이드에서만 실행이 돼

404 Pages

404 페이지 커스텀하기! pages/404.js 만들면 끝!!!!

export default function NotFound() {
  return (
    <div>What are u doing here?</div>
  )
}

이 강의 이후. NextJS 를 더 공부하고 싶다면?

  • 현 NextJS 기초 이론 강의를 마치신 후. 캐럿마켓 클론코딩을 수강하시면 됩니다.
  • 캐럿마켓 클론코딩 강의에서는...
    - NextJS를 사용하여 풀스택 서버리스 애플리케이션을 구축하는 방법에 대해 배웁니다.
    - NextJS의 Static Regeneration, Server Side Rendering, Incremental Static Regeneration 을 배웁니다.
    - NextJS로 Stateless severless authentication 을 구축합니다.
    - Prisma 를 사용하여 PlanetScale의 Serverless SQL Database 와 작업합니다.
    - 라이브 스트리밍 및 이미지 호스팅을 위해 Cloudflare를 사용합니다.
    - TailwindCSS v3 를 사용하여 UI를 빌드합니다.
    - Suspense, SSR Streaming 및 React Server Components와 같은 최신 React 18 기능을 사용하게 됩니다!
profile
꾸준히 자유롭게 즐겁게

0개의 댓글