Next.js - 라우팅

Gyu·2023년 10월 19일
0

React.js

목록 보기
18/20
post-thumbnail

페이지 라우팅

  • 그냥 리액트에서는 react-route-dom 라이브러러리를 통해 페이지간 라우팅을 구현할 수 있지만, next.js 에서는 pages 폴더 안에 있는 파일을 생성하면 자동으로 라우팅이 구현된다. pages 폴더 안의 파일이름이 해당 페이지의 주소가 된다.(export 이름이 아니라 파일 명임!)
  • pages/index.tsx/ 경로의 파일 렌더링
  • pages/post.tsx/posts 경로
  • pages/posts/index.tsx/posts
  • pages/posts/list.tsx/posts/list
  • 일반적인 웹 사이트에서는 a 태그를 이용해 페이지간 이동을 한다. next.js에서는 react-route-dom과 같이 Link 컴포넌트를 사용해 페이지간 이동을 할 수 있다.
  • next.js 12.2 이전 버전에서는 Link 컴포넌트 사용 시 반드시 a 태그를 감싸야한다. 하지만 12.2이상 버전에서 부터는 a 태그를 안 감사도 된다.
  • Link 컴포넌트는 next/link에서 import 할 수 있으며, 이를 이용해 client-side navigation을 수행 할 수 있다.
  • 클라이언트 사이드 네비게이션이란 js를 이용한 페이지 전환을 의미한다. 이를 이용하면 브라우저에 의해 페이지 전환이 되는 것보다 더 빠르고 사용자에게 좋은 ux를 제공할 수 있다.
  • next.js는 자동으로 코드 스플리팅을 수행한다. 따라서 각 페이지는 필요할 때만 로드된다. 이는 특정 페이지가 렌더링 될 때 다른 페이지의 코드가 초기에 제공되지 않는 다는 것을 의미한다. 사용자가 해당 페이지를 요청 할 때만 그 페이지의 코드가 로딩되며, 이는 각 페이지가 독립되어있고 몇 백개의 페이지가 있어로 페이지 로딩이 빠름을 보장한다는 뜻이다.
  • 게다가 프로덕션 환경에서 브라우저의 viewport에 Link 컴포넌트가 나타나면 next.js는 자동으로 해당 링크의 페이지를 백그라운드에서 prefetch 한다. 때문에 사용자가 해당 링크를 클릭 할 때 쯤에는 해당 페이지의 코드는 이미 백그라운드에서 로드되어 있을 것이다.
  • 예제
    import Link from "next/Link";
    
    export default function App() {
      return (
        <div>
          <h2>Link to 'tomato' Page</h2>
          <Link href="/post">
            post
          </Link>
        </div>
      );
    }

useRouter

  • 가끔은 Link 컴포넌트를 사용하지 않고 함수나 로직 내에서 라우팅을 수행해야 할 때가 있습니다. 이처럼 프로그래밍 적으로 페이지 네비게이션을 하고자 할 경우 next/router의 useRouter 훅을 사용하면 됩니다.
import { useRouter } from "next/router";

export default function App() {
  const router = useRouter();
  return (
    <div>
      <h2>Link to 'tomato' Page</h2>
      <button onClick={() => router.push("/tomato")}>토마토로 가기</button>
    </div>
  );
}
  • router.back() : 직전 페이지로 돌아갑니다.
  • router.push("url") : 지정한 경로로 이동하며 히스토리 스택에 URL를 추가합니다.
  • router.replace("url") : 지정한 경로로 이동하나 히스토리 스택에 URL를 추가하지 않습니다.
  • SEO에 영향을 미치는 크롤러는 router.push() 가 다른 페이지로 이동하는 링크라고 인식하지 않기 때문에 이벤트 핸들러 등에 필요한 경우가 아니라면 Link 컴포넌트를 활용해 시멘틱한 구조를 유지하는 것을 권장

동적 라우팅

  • /note/1/2 같이 url이 정적이 아닌 params가 붙은 동적 url을 라우팅하고자 할 경우, 아래처럼 대괄호 [] 로 파일명을 감싸면 해당 페이지는 동적으로 경로가 지정되는 페이지가 됩니다.
/pages
  ㄴ-- index.jsx
  ㄴ-- note
         ㄴ-- [id].jsx
         ㄴ-- [name].jsx => Error!! # 한 동적 경로에는 한 개의 동적 페이지만이 존재할 수 있습니다.
  • 대괄호 [] 로 파일명을 감싸면 해당 페이지는 동적으로 경로가 지정되는 페이지가 되고, 동적 페이지가 존재하는 경로에 임의의 주소를 대입하면 대입한 주소를 쿼리명으로 갖는 페이지로 이동할 수 있습니다.
  • /notes/1?can=123 를 요청했을 때, router 객체를 통해 query 값을 확인 할 수 있습니다.
// App.tsx
import Link from "next/Link";

export default function App() {
  return (
    <div>
      <h2>Link to 'potato' Page</h2>
      <Link href="/note/1?can=123">
        <a>move to note + id + can</a>
      </Link>
    </div>
  );
}

// note/[id].jsx
import React from "react";
import { useRouter } from "next/router";

const NoteId = () => {
  const router = useRouter();
  console.log({ query: router.query, router: router })
  return (
    <div>Note</div>
  )
}
  • 위와 같이 [id].jsx 파일을 만듬으로 인해 /notes/1과 같은 주소에 대한 페이지를 받아볼 수 있었지만, /notes/1/2와 같이 param이 하나 더 추가 된다면, 해당 페이지는 404에러를 보여줍니다. 몇개의 params가 주소 뒤에 붙을지 모르기 때문에, 모든 params에 대한 페이지 접속을 허가하기 위해서는 catch all routes 기능을 사용할 수 있습니다.
  • 아래와 같이 전개 연산자를 사용해 파일이름을 지으면 여러 개의 params를 받을 수 있습니다.
/pages
  ㄴ-- index.jsx
  ㄴ-- /note
         ㄴ-- [[...params]].jsx
import React from "react";
import { useRouter } from "next/router";

const Notes = () => {
  const router = useRouter();
  const { params } = router.query;
  console.log({ params })
  return (
    <div>Note</div>
  )
}

export default Notes;

// /notes/1/1?can=123 접속 시
// => params: (2) ["1", "1"]
  • params는 배열 형식으로 router.query를 통해 뽑아볼 수 있으며, 이름은 params가 아닌 어떤 형태로도 괜찮습니다.
  • 다시 폴더구조를 살펴보면 [...params]가 또다른 배열인 []로 감싸져 있습니다. pages/notes
    경로 아래에 , index.jsx 파일 또한 필요 없습니다. notes 경로 아래에서 일어나는 모든 페이지는 [[...params]].jsx파일만 수정하면 됩니다. index.js를 만들면 오류가 생기므로, catch all routes를 사용할 때는 index.js를 사용하지 않습니다.

API Routes

  • Next.js는 API Routes라는 것을 지원합니다. API Routes는 Node.js 서버리스 함수이며, 이를 통해 API endpoint를 쉽게 만들 수 있습니다.

API Routes 만들기

  • API Routes만드는 법은 간단합니다. pages/api 폴더 내에 아래와 같은 형식의 함수를 만들면 됩니다.
    // req = HTTP incoming message, res = HTTP server response
    export default function handler(req, res) {
      // ...
    }
  • 이렇게 만들어진 함수는 서버리스 함수로 배포됩니다.
  • 예제
    // pages/api/hello.ts
    
    import type { NextApiRequest, NextApiResponse } from 'next'
     
    type ResponseData = {
      message: string
    }
     
    export default function handler(
      req: NextApiRequest,
      res: NextApiResponse<ResponseData>
    ) {
      res.status(200).json({ message: 'Hello from Next.js!' })
    }
    
    // http://localhost:3000/api/hello 호출 시 
    // { message: 'Hello from Next.js!' } 응답 확인
  • 주의 사항
    • API Routes는 getStaticProps 혹은 getStaticPaths 함수 내부에서 사용하면 안 됩니다. 이 방식 보다는 직접 서버 사이드 코드를 getStaticProps 혹은 getStaticPaths 함수 내부에 작성하는 것이 좋습니다.
    • getStaticPropsgetStaticPaths 함수는 오직 서버 사이드에서만 실행되며, 이 함수들은 브라우저를 위한 JS 번들에 포함되지 않기 때문에 API Routes는 이 함수들 내부에서 사용하면 안 됩니다.

💡 Serverless란?

서버(Server) + 리스(Less)의 합성어라 간혹 '서버가 없다'라고 문자 그대로 이해할 수 있지만, 절대 그렇지 않다. 서버리스(Serverless)는 클라우드 컴퓨팅의 모델 중 하나로 개발자가 서버를 직접 관리할 필요가 없는 아키텍처를 의미한다.

예를들어, 서버의 사용자가 1000명이 될 걸 예상하고 그에 맞는 용량의 서비스를 구입했다면 실제 사용자가 1000명이든 0명이든 같은 금액을 내야 할 것이다. 이는 크던 작던 손실을 일으키기 마련이다. 하지만 서버리스는 동적으로 서버의 자원을 할당한다. 사용자가 없다면 자원을 할당하지 않고 대기하다 요청이 들어오면 그 때 자원을 할당해서 요청을 처리하고 다시 대기 상태로 들어가게 된다. 즉, 자원을 효율적으로 사용할 수 있는 것이다.

비용 또한 대기상태를 제외한 실제 사용 자원에 대해서만 청구되기 때문에 굉장히 경제적이며,
해당 서버는 클라우드 제공 기업에서 전적으로 관리하기 때문에 개발자는 스케일링, 업데이트, 백업, 보안 등 서버에 대해 일절 관리하거나 신경 쓸 필요가 없어 비즈니스 로직에 집중하여 개발을 할 수 있다.

서버리스 아키텍쳐의 대표적인 구현방식은 두 가지가 있다. 바로, FaaS (Function as a Service)와 BaaS (Backend as a Service) 다. 구현방식은 두 가지로 나뉘지만, 일반적으로 서버리스라고 하면 FaaS를 가리킨다.

FaaS는 Function 즉, 함수를 서비스로 제공한다. 사용자가 작성한 코드(백엔드)를 서버리스 제공자의 서버에 업로드하면 해당 서버는 업로드한 코드를 함수 단위로 쪼개 대기상태로 두게된다.
그러다 요청이 들어오면 서버가 대기상태에 두었던 함수를 실행시켜 처리한 후 작업이 끝나면 다시 대기상태로 만드는 구조이다. 비용은 함수 호출 횟수에 따라 청구된다.

profile
애기 프론트 엔드 개발자

0개의 댓글