✏️ Next.js

[pages] Shallow Routing

category
✏️ Next.js
date
thumbnail
slug
next-js-pages-shallow-routing
author
status
Public
tags
pages router
summary
type
Post
 

Shallow Routing

getServerSideProps / getStaticProps 등을 다시 실행시키지 않고, 현재 상태를 잃지 않고 url을 바꾸는 방법
 
상태는 유지하면서 url만 바꾸고 싶은 경우에 사용한다.
⇒ 사용자가 어떤 동작을 했고, 그 기록을 query로 남기고 싶을 때
⇒ query로 남기면 사용자가 새로고침을 해도 유지된다.
⇒ 하지만 Data fetching을 일으키고 싶지 않다면 shallow routing 사용!
 
 

url을 바꾸는 3가지 방식

  1. location.replace(’url’) : 로컬 state 유지 안 됨(리렌더)
  1. router.push(url) : 로컬 state 유지 / data fetching은 일어남
  1. router.push(url, as, { shallow: true }) : 로컬 state 유지 / data fetching 안 일어남
      • 동일한 페이지에서 쿼리만 달라져야 함
      • ex) data/info?name=Lami 는 가능한데 data/age처럼 쿼리가 아니라 아예 다른 페이지면 적용이 안 된다.
 
 

Example

 
전체 예시 코드
import { useRouter } from 'next/router';
import { useState } from 'react';

export async function getServerSideProps() {
  console.log('server');

  return {
    props: {},
  };
}

export default function ShallowRouting() {
  const router = useRouter();
  const [clicked, setClicked] = useState(false);
  const { status = 'initial' } = router.query;

  return (
    <>
      <h1>Clicked: {String(clicked)} </h1>
      <h1>Status: {status}</h1>
      <button
        onClick={() => {
          alert('edit');
          setClicked(true);
          location.replace('/shallow-routing?status=editing');
        }}>
        edit(replace)
      </button>
      <span> 로컬 state 유지 안 (리렌더)</span>
      <br />
      <button
        onClick={() => {
          alert('edit');
          setClicked(true);
          router.push('/shallow-routing?status=editing');
        }}>
        edit(router.push)
      </button>
      <span> 로컬 state 유지 / data fetching은 일어남</span>
      <br />
      <button
        onClick={() => {
          alert('edit');
          setClicked(true);
          router.push('/shallow-routing?status=editing', undefined, {
            shallow: true,
          });
        }}>
        edit(shallow)
      </button>
      <span> 로컬 state 유지 / data fetching 안 일어남</span>
    </>
  );
}
pages/shallow-routing/index.js
 
다음과 같이 한번에 각각의 경우를 확인하기 위한 예시이기에 전체 코드가 길다.
notion image
 
 
조금씩 나눠서 살펴보자.
먼저 실행 결과 화면을 보면 Clicked: false, Status: initial 이 있다. 이는 초기 상태를 뜻하는 화면이다.
Clicked 의 false 는 useState의 초기값인 false이고, Status의 initial 은 router.query를 통해 가져오지만 초기 상태는 initial 로 지정했다. 따라서 쿼리가 없다면 Status 의 상태(콜론 옆 문구)는 initial 이 된다.
여기서 url을 바꾸는 3가지의 방법을 살펴보려 한다.
각 버튼을 누르면 alert로 edit을 띄우게 되며, setState 함수를 통해 Clicked의 상태를 true로 바꾸고,
/shallow-routing?status=editing 로 현재 페이지에서 쿼리를 가진 페이지로 이동하게 된다.
이때의 화면에 나오는 결과를 살펴보자.
 
아, data fetching이 일어나는지를 확인하기 위해 getServerSideProps 를 이용해서 터미널에 server 라는 메세지를 띄운다. 버튼을 눌러 페이지를 이동했을 때 터미널에 server 메세지가 뜬다면 data fetching이 일어났다는 뜻이다.
 
 

1️⃣ location.replace

 
notion image
 
전체 코드에서 해당 경우만을 나타내도록 잘라내었다.
import { useRouter } from 'next/router';
import { useState } from 'react';

export async function getServerSideProps() {
  console.log('server');

  return {
    props: {},
  };
}

export default function ShallowRouting() {
  const router = useRouter();
  const [clicked, setClicked] = useState(false);
  const { status = 'initial' } = router.query;

  return (
    <>
      <h1>Clicked: {String(clicked)} </h1>
      <h1>Status: {status}</h1>
      <button
        onClick={() => {
          alert('edit');
          setClicked(true);
          location.replace('/shallow-routing?status=editing');
        }}>
        edit(replace)
      </button>
      <span> 로컬 state 유지 안 (리렌더)</span>
    </>
  );
}
 
 
버튼을 누르자,
notion image
notion image
 
 
  1. Clicked: false
      • 버튼을 눌러 setState 함수가 동작해 true 로 바뀌었으나, 페이지가 리렌더 되어 state가 유지되지 않아 false 를 갖고 있다.
  1. Status: editing
      • /shallow-routing?status=editing 로 이동했기에 router.query에서 staus의 값을 저장하므로 editing을 갖고 있다.
  1. 터미널에서 server 출력
      • 브라우저가 리렌더 되었으며 getServerSideProps 가 동작해 터미널에 server 를 출력했다.
 
 

2️⃣ router.push

 
notion image
 
import { useRouter } from 'next/router';
import { useState } from 'react';

export async function getServerSideProps() {
  console.log('server');

  return {
    props: {},
  };
}

export default function ShallowRouting() {
  const router = useRouter();
  const [clicked, setClicked] = useState(false);
  const { status = 'initial' } = router.query;

  return (
    <>
      <h1>Clicked: {String(clicked)} </h1>
      <h1>Status: {status}</h1>
      <button
        onClick={() => {
          alert('edit');
          setClicked(true);
          router.push('/shallow-routing?status=editing');
        }}>
        edit(router.push)
      </button>
      <span> 로컬 state 유지 / data fetching은 일어남</span>
    </>
  );
}
 
 
버튼을 누르자,
notion image
notion image
 
 
  1. Clicked: true
  1. Status: editing
  1. 터미널에서 server 출력
 
router.push 는 리렌더링이 아니기 때문에 버튼 클릭을 통해 바뀐 state는 유지가 되지만 data fetching이 일어나 터미널에 server 를 출력했다.
 
 
 

3️⃣ shallow ⇒ router.push(url, as, { shallow: true })

 
notion image
 
import { useRouter } from 'next/router';
import { useState } from 'react';

export async function getServerSideProps() {
  console.log('server');

  return {
    props: {},
  };
}

export default function ShallowRouting() {
  const router = useRouter();
  const [clicked, setClicked] = useState(false);
  const { status = 'initial' } = router.query;

  return (
    <>
      <h1>Clicked: {String(clicked)} </h1>
      <h1>Status: {status}</h1>
      <button
        onClick={() => {
          alert('edit');
          setClicked(true);
          router.push('/shallow-routing?status=editing', undefined, {
            shallow: true,
          });
        }}>
        edit(shallow)
      </button>
      <span> 로컬 state 유지 / data fetching 안 일어남</span>
    </>
  );
}
 
버튼을 누르자,
notion image
notion image
 
 
  1. Clicked: true
  1. Status: editing
  1. 터미널에 server 가 출력되지 않음
 
⇒ 클릭을 통해 바꾼 state도 잘 갖고 있고, data fetching이 일어나지 않아 터미널에 server 가 출력되지 않았다.
 
 
 

결론

처음에 말했다시피 Shallow Routing 은 getServerSideProps / getStaticProps 등을 다시 실행시키지 않고, 현재 상태를 잃지 않고 url을 바꾸는 방법이다.
상태를 유지하며 쿼리를 통해 url을 바꾸고 싶을 때 사용할 수 있다.
아마 모달 같은 곳에서 주로 사용하지 않을까 싶다!
 
 

참고