Next.jsのrouter.pushとrouter.replaceの違いとそれぞれの最適な使い道

## この記事について
Next.jsの router オブジェクトに存在するメソッドの `router.push` と `router.replace` が似ていて、どちらを使用するのがベストなのか分からなくなる場面があった為、それぞれの違いと最適な使い道についてNext.jsの実装を参考にしながらまとめました。

## [router.push](https://nextjs.org/docs/api-reference/next/router#routerpush)

### 構文
```js
router.push(url, as, options)
```

### できること
指定したリンクにぺージ遷移ができます。

### 型
`router.push`の返り値の型は`Promise`で、ジェネリクスで渡している型は`boolean`です。


```js
push(url: Url, as?: Url, options?: TransitionOptions): Promise<boolean>;
```

### 使用例
```js
import { useRouter } from 'next/router'

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

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

### 向いている使い方
- プロジェクト内でページ遷移したいとき
- ドキュメントによると外部リンクを指定する場合は、`window.location`を使うと良いとの記述がありました
    - `router.push`で外部リンクを指定しても、内部の実装では`window.location.href`で遷移する処置を書いていたので使用しても問題なさそうです（[該当コード](https://github.com/vercel/next.js/blob/canary/packages/next/shared/lib/router/router.ts#L832)）

> router.push外部URLに使用する必要はありません。
window.locationは、これらの場合に適しています。
https://nextjs.org/docs/api-reference/next/router


## [router.replace](https://nextjs.org/docs/api-reference/next/router#routerreplace)

### 構文
```js
router.replace(url, as, options)
```

### できること
指定したリンクにぺージ遷移ができます。

`router.push`との違いは、指定したURLをhistoryのスタックに追加せずに上書きしてページ遷移する点です。

NextはURLのhistoryを`window.history`で管理していますが、`router.replace`は直接historyを上書きする感じで実装されていそうでした（[該当コード](https://github.com/vercel/next.js/blob/canary/packages/next/shared/lib/router/router.ts#L1266)）

### 型
`router.replace`の返り値の型は`Promise`で、ジェネリクスで渡している型は`boolean`です。
`router.push`の型と同一です。


```js
replace(url: Url, as?: Url, options?: TransitionOptions): Promise<boolean>;
```

### 使用例
```js
import { useRouter } from 'next/router'

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

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

### 向いている使い方
- ユーザに無効なURLにアクセスされた場合に、指定ページにリダイレクトするとき（`/400`、`/500`等）

#### 向いてる場面の例
仕様：ログインしないと閲覧できないページ(`/profile/detail`)があり、ログインしていない状態でアクセスされたら`/400`に遷移させる

#### pushの場合だとこうなります

```text
「/profile/detail」 にアクセス → 「/400」 にリダイレクト

history： [/profile/detail, 400]
```
#### 結果
`/400` からブラウザバックすると`/profile/detail`に戻れる

#### replaceの場合だとこうなります

```text
「/profile/detail」 にアクセス → 「/400」 にリダイレクト

history： [400]
```

#### 結果
historyが上書きされるので`/400` からブラウザバックできない


## まとめ
- 基本ページ遷移には`router.push`を使用するで問題ない
- アクセス制限したいページがあり、リダイレクト処理を行う場合は`router.replace`が向いている

## おまけ
- `router.back`は`window.history.back`と全く同じ（[該当コード](https://github.com/vercel/next.js/blob/canary/packages/next/shared/lib/router/router.ts#L785)）

- `router.reload`は` window.location.reload`と全く同じ（[該当コード](https://github.com/vercel/next.js/blob/canary/packages/next/shared/lib/router/router.ts#L778)）

- Nextはスクロール位置を`sessionStorage`に保存してそう（[該当コード](https://github.com/vercel/next.js/blob/canary/packages/next/shared/lib/router/router.ts#L802)）
> localStorage に保存されたデータに期限がないのに対して、sessionStorage に保存されたデータはページのセッションが終了するときに消去されます。
ページのセッションはブラウザを開いている限り、ページの再読み込みや復元を越えて持続します。
> 
> 参考： [MDN](https://developer.mozilla.org/ja/docs/Web/API/Window/sessionStorage)




## 不明だった事
- `router.push`や`router.replace`などのメソッドが`Promise`を返している理由が不明でした。どなたかご存知の方いたら教えてくださると嬉しいです！！



## 参考
[Next.js Docs](https://nextjs.org/docs/api-reference/next/router)
[Next.js Github](https://github.com/vercel/next.js/blob/canary/packages/next/shared/lib/router/router.ts)