NextJS API: next/router

hwisaac·2023년 3월 13일
0

NextJS API(page router)

목록 보기
6/10

참고 (https://velog.io/@hwisaac/NextJS-Routing-intro)

useRouter

어플리케이션의 모든 함수 컴포넌트에서 라우터 객체에 접근하려면, useRouter 훅을 사용할 수 있습니다. 다음 예제를 참고해주세요:

import { useRouter } from 'next/router'

function ActiveLink({ children, href }) {
  const router = useRouter()
  const style = {
    marginRight: 10,
    color: router.asPath === href ? 'red' : 'black',
  }

  const handleClick = (e) => {
    e.preventDefault()
    router.push(href)
  }

  return (
    <a href={href} onClick={handleClick} style={style}>
      {children}
    </a>
  )
}

export default ActiveLink

useRouter는 React 훅으로, 클래스와 함께 사용할 수 없습니다. withRouter를 사용하거나 클래스를 함수 컴포넌트로 래핑해야합니다.

router 객체

useRouterwithRouter에서 반환된 라우터 객체의 정의는 다음과 같습니다:

  • pathname: String - /pages 다음에 오는 현재 라우트 파일의 경로입니다. 따라서 basePath, 로케일, trailing slash (trailingSlash: true)는 포함되지 않습니다.
  • query: Object - 동적 라우트 매개변수를 포함한 쿼리 문자열을 객체로 변환합니다. 페이지가 서버 사이드 렌더링을 사용하지 않는 경우 프리랜더링 중에는 빈 객체가 됩니다. 기본값은 {}
  • asPath: String - basePath 및 로케일이 포함되지 않은 브라우저에서 표시되는 경로입니다.
  • isFallback: boolean - 현재 페이지가 fallback 모드에 있는지 여부입니다.
  • basePath: String - 활성 basePath (활성화되어 있을 경우).
  • locale: String - 활성 로케일 (활성화되어 있을 경우).
  • locales: String[] - 모든 지원되는 로케일 (활성화되어 있을 경우).
  • defaultLocale: String - 현재 기본 로케일 (활성화되어 있을 경우).
  • domainLocales: Array<{domain, defaultLocale, locales}> - 구성된 모든 도메인 로케일.
  • isReady: boolean - 라우터 필드가 클라이언트 측에서 업데이트되고 사용할 준비가 되었는지 여부입니다. 서버 측 조건부 렌더링을 위해 사용하면 안되고, 서버에서 조건부 렌더링을 하는 경우 isReady가 true가 될 때까지 asPath를 사용하지 않는 것이 좋습니다.
  • isPreview: boolean - 애플리케이션이 현재 미리보기 모드인지 여부입니다.

sPath 필드 사용은 서버 측 렌더링이나 자동 정적 최적화로 렌더링되는 경우 클라이언트와 서버 간 불일치를 일으킬 수 있습니다. isReady 필드가 true인 경우까지 asPath 사용을 피하십시오.

router에 포함된 다음 메서드들입니다:

router.push

예시: Using Router
클라이언트 측 전환을 다룹니다. next/link가 충분하지 않은 경우에 유용합니다.

router.push(url, as, options)
  • url: UrlObject | String - 이동할 URL입니다 (UrlObject 속성에 대한 Node.JS URL 모듈 문서를 참조하세요).
  • as: UrlObject | String - 브라우저 URL 표시줄에 표시될 경로에 대한 선택적 장식자입니다. 이전 버전의 Next.js 9.5.3에서는 동적 경로에 사용되었으며, 이전 문서에서 작동 방식을 확인하려면 이전 문서를 참조하세요. 참고: 이 경로가 href에서 제공한 것과 다를 경우 이전 href/as 동작이 이전 문서에서와 같이 사용됩니다.
  • options - 다음 구성 옵션을 가진 선택적 객체입니다:
    • scroll - 선택적 부울, 탐색 후 페이지 상단으로 스크롤링을 제어합니다. 기본값은 true입니다.
    • shallow: getStaticProps, getServerSideProps 또는 getInitialProps를 다시 실행하지 않고 현재 페이지의 경로를 업데이트합니다. 기본값은 false입니다.
    • locale - 선택적 문자열, 새 페이지의 로케일을 나타냅니다.

외부 URL의 경우 router.push를 사용할 필요가 없습니다. 이 경우 window.location이 더 적합합니다.

미리 정의된 라우트인 pages/about.js로 이동하는 예:

import { useRouter } from 'next/router'

export default function Page() {
  const router = useRouter()

  return (
    <button type="button" onClick={() => router.push('/about')}>
      Click me
    </button>
  )
}

동적 경로인 pages/post/[pid].js로 이동하는 예:

import { useRouter } from 'next/router'

export default function Page() {
  const router = useRouter()

  return (
    <button type="button" onClick={() => router.push('/post/abc')}>
      Click me
    </button>
  )
}

인증을 필요로하는 페이지를 위한 pages/login.js로 사용자를 리디렉션하는 예:

import { useEffect } from 'react'
import { useRouter } from 'next/router'

// 여기에서 사용자를 가져와 반환합니다
const useUser = () => ({ user: null, loading: false })

export default function Page() {
  const { user, loading } = useUser()
  const router = useRouter()

  useEffect(() => {
    if (!(user || loading)) {
      router.push('/login')
    }
  }, [user, loading])

  return <p>Redirecting...</p>
}

탐색 후 상태 재설정

Next.js에서 같은 페이지로 이동할 때 React가 언마운트하지 않는 한 페이지의 상태는 기본적으로 재설정되지 않습니다. 이는 부모 컴포넌트가 변경되지 않으면 React가 언마운트하지 않기 때문입니다.

예를 들어, 다음 예제에서 /one/two 사이를 이동하면 useState는 계속 유지됩니다.

// pages/[slug].js
import Link from 'next/link'
import { useState } from 'react'
import { useRouter } from 'next/router'

export default function Page(props) {
  const router = useRouter()
  const [count, setCount] = useState(0)
  return (
    <div>
      <h1>Page: {router.query.slug}</h1>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increase count</button>
      <Link href="/one">one</Link> <Link href="/two">two</Link>
    </div>
  )
}

위의 예제에서 /one/two 사이를 이동하면 count는 재설정되지 않습니다. useState는 컴포넌트의 최상위 React 컴포넌트가 같기 때문에 렌더 사이에 상태가 유지됩니다.

이러한 동작을 원하지 않는 경우 두 가지 옵션이 있습니다.

  1. useEffect를 사용하여 각 상태가 업데이트되도록 수동으로 보장합니다. 위의 예제에서는 다음과 같이 보일 수 있습니다.
useEffect(() => {
  setCount(0)
}, [router.query.slug])
  1. React 키를 사용하여 React에 컴포넌트를 리마운트하도록 지시합니다. 이를 위해 모든 페이지에 대해 사용자 지정 앱을 사용할 수 있습니다.
// pages/_app.js
import { useRouter } from 'next/router'

export default function MyApp({ Component, pageProps }) {
  const router = useRouter()
  return <Component key={router.asPath} {...pageProps} />
}

URL 객체와 함께

URL 객체는 next/link에서와 같은 방식으로 사용할 수 있습니다. URL 및 as 매개변수 모두에서 작동합니다.

import { useRouter } from 'next/router'

export default function ReadMore({ post }) {
  const router = useRouter()

  return (
    <button
      type="button"
      onClick={() => {
        router.push({
          pathname: '/post/[pid]',
          query: { pid: post.id },
        })
      }}
    >
      Click here to read more
    </button>
  )
}

router.replace

next/linkreplace prop과 유사한 방식으로, router.replace는 새 URL 항목을 history 스택에 추가하지 않습니다.

router.replace(url, as, options)

router.replaceAPIrouter.pushAPI와 정확히 동일합니다.

사용법

다음 예시를 살펴봅시다.

import { useRouter } from 'next/router'

export default function Page() {
  const router = useRouter()

  return (
    <button type="button" onClick={() => router.replace('/home')}>
      Click me
    </button>
  )
}

router.prefetch

더 빠른 클라이언트 측 전환을 위해 페이지를 사전 로드합니다. 이 메소드는 next/link가 자동으로 페이지를 사전 로드하기 때문에 next/link 없이 이동하는 경우에만 유용합니다.

이 기능은 프로덕션 모드에서만 지원됩니다. Next.js는 개발 중 페이지를 사전 로드하지 않습니다.

router.prefetch(url, as, options)
  • url - 사전 로드할 URL입니다. 명시적 라우트(e.g. /dashboard)와 동적 라우트(e.g. /product/[id]) 모두 포함될 수 있습니다.
  • as - url의 선택적 데코레이터입니다. Next.js 9.5.3 이전에는 이것이 동적 라우트를 사전 로드하는 데 사용되었습니다. 작동 방식은 이전 문서를 확인하십시오.
  • options - 다음 필드를 허용하는 선택적 개체입니다.
    • locale - 활성 로캘과 다른 로캘을 제공하는 것을 허용합니다. false인 경우 url에 활성 로캘이 포함되어 있어야 합니다.

사용법

로그인 페이지가 있다고 가정해 봅시다. 로그인 후에는 사용자를 대시보드로 리디렉션합니다. 이 경우, 사전 로드를 사용하여 대시보드로 더 빠른 전환을 만들 수 있습니다. 다음 예시를 참조하세요.

import { useCallback, useEffect } from 'react'
import { useRouter } from 'next/router'

export default function Login() {
  const router = useRouter()
  const handleSubmit = useCallback((e) => {
    e.preventDefault()

    fetch('/api/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        /* Form data */
      }),
    }).then((res) => {
      // Do a fast client-side transition to the already prefetched dashboard page
      if (res.ok) router.push('/dashboard')
    })
  }, [])

  useEffect(() => {
    // Prefetch the dashboard page
    router.prefetch('/dashboard')
  }, [])

  return (
    <form onSubmit={handleSubmit}>
      {/* Form fields */}
      <button type="submit">Login</button>
    </form>
  )
}

router.beforePopState

일부 경우(예: Custom Server를 사용하는 경우) popstate를 듣고 무언가를 하기를 원할 수 있습니다.

router.beforePopState(cb)
  • cb - popstate 이벤트에 대한 처리를 수행할 함수. 이 함수는 다음과 같은 프로퍼티를 가진 객체 형태의 이벤트 상태를 받습니다:
  • url: String - 새 상태의 경로입니다. 보통 페이지 이름입니다.
  • as: String - 브라우저에서 표시될 URL입니다.
  • options: Object - router.push에서 추가로 전달된 옵션입니다.

cbfalse를 반환하면, Next.js 라우터는 popstate를 처리하지 않으며 해당 경우 처리를 담당해야 합니다. 파일 시스템 라우팅 비활성화를 참조하세요.

사용법

다음 예제와 같이 beforePopState를 사용하여 요청을 조작하거나 SSR 새로고침을 강제할 수 있습니다.

import { useEffect } from 'react'
import { useRouter } from 'next/router'

export default function Page() {
  const router = useRouter()

  useEffect(() => {
    router.beforePopState(({ url, as, options }) => {
      // 이 두 경로만 허용하고 싶습니다!
      if (as !== '/' && as !== '/other') {
        // SSR을 사용하여 잘못된 경로를 404로 렌더링합니다.
        window.location.href = as
        return false
      }

      return true
    })
  }, [])

  return <p>Welcome to the page</p>
}

router.back

히스토리에서 뒤로 이동합니다. 브라우저의 뒤로 가기 버튼을 누르는 것과 동일합니다. window.history.back()을 실행합니다.

사용법

import { useRouter } from 'next/router'

export default function Page() {
  const router = useRouter()

  return (
    <button type="button" onClick={() => router.back()}>
      Click here to go back
    </button>
  )
}

router.reload

현재 URL을 다시 로드합니다. 브라우저의 새로고침 버튼을 누르는 것과 동일합니다. window.location.reload()를 실행합니다.

사용법

import { useRouter } from 'next/router'

export default function Page() {
  const router = useRouter()

  return (
    <button type="button" onClick={() => router.reload()}>
      Click here to reload
    </button>
  )
}

router.events

예: 페이지 로딩 인디케이터

Next.js 라우터 내에서 발생하는 다양한 이벤트를 수신할 수 있습니다. 지원되는 이벤트의 목록은 다음과 같습니다.

  • routeChangeStart(url, { shallow }) - 라우트 변경이 시작될 때 발생
  • routeChangeComplete(url, { shallow }) - 라우트 변경이 완전히 완료되면 발생
  • routeChangeError(err, url, { shallow }) - 라우트 변경 중 오류가 발생하거나 라우트 로드가 취소될 때 발생합니다.
    • err.cancelled - 탐색이 취소되었는지 여부를 나타냅니다.
  • beforeHistoryChange(url, { shallow }) - 브라우저 히스토리 변경 전에 발생합니다.
  • hashChangeStart(url, { shallow }) - 해시는 변경되지만 페이지는 변경되지 않을 때 발생합니다.
  • hashChangeComplete(url, { shallow }) - 해시가 변경되지만 페이지는 변경되지 않을 때 발생합니다.

참고: 여기서 urlbasePath를 포함한 브라우저에 표시되는 URL입니다.

사용 방법

예를 들어 라우터 이벤트 routeChangeStart를 수신하려면 pages/_app.js를 열거나 생성하여 다음과 같이 이벤트를 구독합니다.

import { useEffect } from 'react'
import { useRouter } from 'next/router'

export default function MyApp({ Component, pageProps }) {
  const router = useRouter()

  useEffect(() => {
    const handleRouteChange = (url, { shallow }) => {
      console.log(
        `App is changing to ${url} ${
          shallow ? 'with' : 'without'
        } shallow routing`
      )
    }

    router.events.on('routeChangeStart', handleRouteChange)

    // If the component is unmounted, unsubscribe
    // from the event with the `off` method:
    return () => {
      router.events.off('routeChangeStart', handleRouteChange)
    }
  }, [])

  return <Component {...pageProps} />
}

이 예제에서는 구독하려는 이벤트에 대해 Custom App(pages/_app.js)을 사용합니다. 이유는 페이지 내비게이션에서 제거되지 않기 때문입니다. 그러나 귀하의 애플리케이션에서는 모든 구성 요소에서 라우터 이벤트를 구독할 수 있습니다.

라우터 이벤트는 컴포넌트가 마운트될 때 (useEffect 또는 componentDidMount / componentWillUnmount) 또는 이벤트가 발생할 때 명령적으로 등록해야합니다.

예를 들어, 두 번 클릭하여 라우트 로드를 취소하면 (예: 두 번 빠르게 링크를 클릭), routeChangeError가 발생합니다. 그리고 전달된 err에는 true로 설정된 취소된 속성이 포함됩니다. 아래 예제와 같습니다.

import { useEffect } from 'react'
import { useRouter } from 'next/router'

export default function MyApp({ Component, pageProps }) {
  const router = useRouter()

  useEffect(() => {
    const handleRouteChangeError = (err, url) => {
      if (err.cancelled) {
        console.log(`Route to ${url} was cancelled!`)
      }
    }

    router.events.on('routeChangeError', handleRouteChangeError)

    // If the component is unmounted, unsubscribe
    // from the event with the `off` method:
    return () => {
      router.events.off('routeChangeError', handleRouteChangeError)
    }
  }, [])

  return <Component {...pageProps} />
}

잠재적인 ESLint 오류

라우터 객체에서 액세스 가능한 일부 메소드는 Promise를 반환합니다. ESLint 규칙 no-floating-promises가 활성화되어 있으면 해당 줄 또는 전역에서 비활성화하는 것이 좋습니다.

애플리케이션이 이 규칙이 필요한 경우 Promisevoid하거나 async 함수를 사용하여 Promise를 대기 한 다음 함수 호출을 void해야합니다. 이는 onClick 핸들러 내부에서 메서드가 호출될 때 적용되지 않습니다.

영향을 받는 메서드는 다음과 같습니다.

  • router.push
  • router.replace
  • router.prefetch

가능한 해결책

import { useEffect } from 'react'
import { useRouter } from 'next/router'

// Here you would fetch and return the user
const useUser = () => ({ user: null, loading: false })

export default function Page() {
  const { user, loading } = useUser()
  const router = useRouter()

  useEffect(() => {
    // disable the linting on the next line - This is the cleanest solution
    // eslint-disable-next-line no-floating-promises
    router.push('/login')

    // void the Promise returned by router.push
    if (!(user || loading)) {
      void router.push('/login')
    }
    // or use an async function, await the Promise, then void the function call
    async function handleRouteChange() {
      if (!(user || loading)) {
        await router.push('/login')
      }
    }
    void handleRouteChange()
  }, [user, loading])

  return <p>Redirecting...</p>
}

withRouter

useRouter가 적합하지 않은 경우 withRouter를 사용하여 동일한 라우터 객체를 모든 컴포넌트에 추가할 수 있습니다.

사용법

import { withRouter } from 'next/router'

function Page({ router }) {
  return <p>{router.pathname}</p>
}

export default withRouter(Page)

TypeScript

withRouter와 함께 클래스 컴포넌트를 사용하려면 해당 컴포넌트가 router prop을 수용해야합니다.

import React from 'react'
import { withRouter, NextRouter } from 'next/router'

interface WithRouterProps {
  router: NextRouter
}

interface MyComponentProps extends WithRouterProps {}

class MyComponent extends React.Component<MyComponentProps> {
  render() {
    return <p>{this.props.router.pathname}</p>
  }
}

export default withRouter(MyComponent)

이제 withRouter를 사용하여 모든 컴포넌트에 동일한 라우터 객체를 추가하는 방법에 대해 알게 되었을 것입니다. 감사합니다!

0개의 댓글