어플리케이션의 모든 함수 컴포넌트에서 라우터 객체에 접근하려면, 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
를 사용하거나 클래스를 함수 컴포넌트로 래핑해야합니다.
useRouter
와 withRouter
에서 반환된 라우터 객체의 정의는 다음과 같습니다:
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에 포함된 다음 메서드들입니다:
예시: 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
이 더 적합합니다.
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 컴포넌트가 같기 때문에 렌더 사이에 상태가 유지됩니다.
이러한 동작을 원하지 않는 경우 두 가지 옵션이 있습니다.
useEffect
를 사용하여 각 상태가 업데이트되도록 수동으로 보장합니다. 위의 예제에서는 다음과 같이 보일 수 있습니다.useEffect(() => {
setCount(0)
}, [router.query.slug])
// pages/_app.js
import { useRouter } from 'next/router'
export default function MyApp({ Component, pageProps }) {
const router = useRouter()
return <Component key={router.asPath} {...pageProps} />
}
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>
)
}
next/link
의 replace prop
과 유사한 방식으로, router.replace
는 새 URL 항목을 history
스택에 추가하지 않습니다.
router.replace(url, as, options)
router.replace
의 API
는 router.push
의 API
와 정확히 동일합니다.
다음 예시를 살펴봅시다.
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.replace('/home')}>
Click me
</button>
)
}
더 빠른 클라이언트 측 전환을 위해 페이지를 사전 로드합니다. 이 메소드는 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>
)
}
일부 경우(예: Custom Server를 사용하는 경우) popstate
를 듣고 무언가를 하기를 원할 수 있습니다.
router.beforePopState(cb)
cb
- popstate
이벤트에 대한 처리를 수행할 함수. 이 함수는 다음과 같은 프로퍼티를 가진 객체 형태의 이벤트 상태를 받습니다:url
: String
- 새 상태의 경로입니다. 보통 페이지 이름입니다.as
: String
- 브라우저에서 표시될 URL입니다.options
: Object
- router.push
에서 추가로 전달된 옵션입니다.cb
가 false
를 반환하면, 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>
}
히스토리에서 뒤로 이동합니다. 브라우저의 뒤로 가기 버튼을 누르는 것과 동일합니다. 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>
)
}
현재 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>
)
}
예: 페이지 로딩 인디케이터
Next.js
라우터 내에서 발생하는 다양한 이벤트를 수신할 수 있습니다. 지원되는 이벤트의 목록은 다음과 같습니다.
routeChangeStart(url, { shallow })
- 라우트 변경이 시작될 때 발생routeChangeComplete(url, { shallow })
- 라우트 변경이 완전히 완료되면 발생routeChangeError(err, url, { shallow })
- 라우트 변경 중 오류가 발생하거나 라우트 로드가 취소될 때 발생합니다.err.cancelled
- 탐색이 취소되었는지 여부를 나타냅니다.beforeHistoryChange(url, { shallow })
- 브라우저 히스토리 변경 전에 발생합니다.hashChangeStart(url, { shallow })
- 해시는 변경되지만 페이지는 변경되지 않을 때 발생합니다.hashChangeComplete(url, { shallow })
- 해시가 변경되지만 페이지는 변경되지 않을 때 발생합니다.참고: 여기서
url
은basePath
를 포함한 브라우저에 표시되는 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} />
}
라우터 객체에서 액세스 가능한 일부 메소드는 Promise
를 반환합니다. ESLint
규칙 no-floating-promises
가 활성화되어 있으면 해당 줄 또는 전역에서 비활성화하는 것이 좋습니다.
애플리케이션이 이 규칙이 필요한 경우 Promise
를 void
하거나 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>
}
useRouter
가 적합하지 않은 경우 withRouter
를 사용하여 동일한 라우터 객체를 모든 컴포넌트에 추가할 수 있습니다.
import { withRouter } from 'next/router'
function Page({ router }) {
return <p>{router.pathname}</p>
}
export default withRouter(Page)
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
를 사용하여 모든 컴포넌트에 동일한 라우터 객체를 추가하는 방법에 대해 알게 되었을 것입니다. 감사합니다!