Skip to content

Instantly share code, notes, and snippets.

View khg0712's full-sized avatar
👶
Baby Developer

Hugo Kim khg0712

👶
Baby Developer
View GitHub Profile
// SSR 시점에 Router 싱글톤에 접근할 때 Next.js page 컴포넌트 예시
import Router from 'next/router'
export default function page() {
return (
<div>
{/* 아래 에러 발생
* Error: No router instance found.
* You should only use "next/router" on the client side of your app.
* /}
// SSR 코드
// packages/next/server/render.tsx
// renderToHTML의 인터페이스는 생략.
// https://github.com/vercel/next.js/blob/v12.3.4/packages/next/server/render.tsx#L351
export async function renderToHTML() {
// CSR과 다르게 SSR에선 ServerRouter라는 별개의 class로 Router를 만든다.
// ServerRouter parameter는 생략.
const router = new ServerRouter()
// packages/next/client/index.ts
// Router 싱글톤을 저장하는 전역 객체
// https://github.com/vercel/next.js/blob/v12.3.4/packages/next/client/index.tsx#L67
export let router: Router
// AppContainer에서 RouterContext에 Router 싱글톤 주입하는 코드
// https://github.com/vercel/next.js/blob/v12.3.4/packages/next/client/index.tsx#L296
function AppContainer({
children,
// packages/next/client/router.ts
// import Router from 'next/router' 했을 때 접근하는 값.
// Export the singletonRouter and this is the public API.
export default singletonRouter as SingletonRouter
// packages/next/client/router.ts
// Router 싱글톤을 저장하는 객체
// https://github.com/vercel/next.js/blob/v12.3.4/packages/next/client/router.ts#L20
const singletonRouter: SingletonRouterBase = {
router: null, // holds the actual router instance
readyCallbacks: [],
ready(cb: () => void) {
if (this.router) return cb()
if (typeof window !== 'undefined') {
// packages/next/client/index.ts
// Router 싱글톤을 저장하는 전역 객체
// https://github.com/vercel/next.js/blob/v12.3.4/packages/next/client/index.tsx#L67
export let router: Router
// hydrate 함수에서 Router 싱글톤이 초기화(createRouter)되는 코드 호출
// https://github.com/vercel/next.js/blob/v12.3.4/packages/next/client/index.tsx#L863
export async function hydrate(opts?: { beforeRender?: () => Promise<void> }) {
// ...
// packages/next/client/with-router.tsx
// withRouter 코드
// https://github.com/vercel/next.js/blob/v12.3.4/packages/next/client/with-router.tsx#L19
import { useRouter } from './router'
export default function withRouter<
P extends WithRouterProps,
C extends BaseContext = NextPageContext
>(
// packages/next/client/router.ts
// useRouter 코드
// https://github.com/vercel/next.js/blob/v12.3.4/packages/next/client/router.ts#L132
export function useRouter(): NextRouter {
return React.useContext(RouterContext)
}
// packages/next/client/index.tsx
// https://github.com/vercel/next.js/blob/v12.3.4/packages/next/client/index.tsx
// 최초 hydration을 위해서 사용
// 내부에서 render 함수 호출
// 함수 내부는 생략
// https://github.com/vercel/next.js/blob/v12.3.4/packages/next/client/index.tsx#L756
export async function hydrate(opts?: { beforeRender?: () => Promise<void> }) {}
// packages/next/client/next.js
// https://github.com/vercel/next.js/blob/v12.3.4/packages/next/client/next.js#L12
// Next.js client side initialize 코드
initialize({})
.then(() => hydrate())
.catch(console.error)