blog-hero-img

Next.js Appフォルダでの変更点を20個、時短で紹介

pen-icon2023.12.12rewrite-icon2024.3.18

この記事は約3分で読めます

Profile Pic

この記事の筆者:三好アキ


🔹 Amazonベストセラー1位を複数回獲得している『はじめてつくるReactアプリ with TypeScript』著者。

🔹 自身のJavaScript挫折経験をもとに、HTMLとCSSの知識だけで本格的なアプリ開発を始められる入門書を多数執筆中。合計著作は22冊を超える。

🔹 専門用語なしでプログラミングを教えるメソッドに定評があり、1200人以上のビギナーを、最新のフロントエンド開発入門に成功させる。


Amazon著者ページはこちら → amazon.co.jp/stores/author/B099Z51QF2



React、Next.js、TypeScriptなどのお役立ち情報や実践的コンテンツを、ビギナー向けにかみ砕いて無料配信中。登録はこちらから → 無料メルマガ登録

『Next.jsでつくるフルスタックアプリ』

本記事で紹介していることをよりくわしく知りたい方は、今月(2023/12)リリースした下記書籍を参考にしてください。

最新のNext.js(バージョン14)でバックエンドとフロントエンドの両面を開発して、フルスタックアプリを完成させます。

Next.jsでつくるフルスタックアプリ

【最新バージョンのNext.jsとAppフォルダで、フルスタックアプリを自力で作る力を手にいれる】

1980円0円

Amazonのページに移動する

1 フォルダ構成(フロントエンド)

Appフォルダではフォルダ名がURLとして使われ、コードはその中に作ったpage.jsに書きます。

動的ページ(ブログの記事ページやアイテムページなど)では、[id][slug]のように[ ]を使った名前をフォルダ名として使います。

URL 従来(Pagesフォルダ) v13以降(Appフォルダ)
/ /pages/index.js /app/page.js
/blog /pages/blog.js /app/blog/page.js
/blog/2024-12-1-cafe-book /pages/blog/[id].js /app/blog/[id]/page.js

2 フォルダ構成(バックエンド)

API/バックエンド開発を行う場合も、フォルダの名前がURLに使われるという規則は同じです。

従来(Next.jsバージョン12以前)はapiというフォルダを作るのがマストでしたが、Appフォルダではapiフォルダ作成が必須ではありません。route.jsという名前のファイルを作れば、それがRoute Handlersとして機能します。

URL 従来(Pagesフォルダ) v13以降(Appフォルダ)
/api/item/create /pages/api/item/create.js /app/api/item/create/route.js
/api/article/2024-12-1-cafe-book /pages/api/article/[id].js /app/api/article/[id]/route.js
/api/blog/readsingle/20240914-studying-english /pages/api/blog/readsingle/[id].js /app/api/blog/readsingle/[id]/route.js

3 サーバーコンポーネントとクライアントコンポーネントの比較

Appフォルダ内ではReactサーバーコンポーネントがデフォルトです。

目的 サーバーコンポーネント クライアントコンポーネント
データを取得する ×
バックエンドのリソースに直接アクセスする ×
onClickなどのユーザー操作機能 ×
useStateやuseEffectの使用 ×

4 従来のReactを使う方法(use client)

デフォルトのReactサーバーコンポーネントでuseStateuseEffectなどを使いたい場合には、ファイル冒頭に"use client"と書き、従来のReactコンポーネント(クライアントコンポーネント)へと変更します。

"use client"

import { useState } from "react"

const Hello = () => {
    const [data, setData] = useState()

    return (
        <div>
            ...

5 URLパスの取得(フロントエンド)

next/navigationを使って次のように書きます。

import { usePathname } from "next/navigation"

const Hello = () => {
    const pathname = usePathname()
    console.log(pathname)    // /blog/first-article

    return(
        <div>
            ...

従来(Next.jsバージョン12以前)は次のように書いていました。

import { useRouter } from "next/router"

const Hello = () => {
    const router = useRouter()
    console.log(router.asPath)      // /blog/first-article
    
    return(
        <div>
            ...

6 リダイレクト(フロントエンド)

next/navigationを使って次のように書きます。

import { useRouter } from "next/navigation"

const Hello = () => {
    const router = useRouter()
    router.push("/success")
    OR
    router.replace("/success")

    return(
        <div>
            ...

従来(Next.jsバージョン12以前)は次のように書いていました。

import { useRouter } from "next/router"

const Hello = () => {
    const router = useRouter()
    router.push("/success")
    OR
    router.replace("/success")
    
    return(
        <div>
            ...

7 APIルートの作り方(GETリクエスト)

Appフォルダ内でAPI開発をするときのコードも大きく変わっています。下記はGETリクエストを行う最もベーシックなコードです。

// app/api/hello/route.js

import { NextResponse } from "next/server"

export async function GET() {
    return NextResponse.json("ハロー")
}

従来(Next.jsバージョン12以前)は次のように書いていました。

// pages/api/hello.js

export default function hello(req, res){
    return res.json("ハロー")
}

Next.jsでつくるフルスタックアプリ

【最新バージョンのNext.jsとAppフォルダで、フルスタックアプリを自力で作る力を手にいれる】

1980円0円

Amazonのページに移動する

8 APIルートのPOSTとPUTとDELETEメソッド

HTTPメソッドの変更は、GETPOSTDELETEを明示して行います。

export async function POST() {
    return NextResponse.json("ハロー")
}
export async function DELETE() {
    return NextResponse.json("ハロー")
}

9 リクエストBodyの解析方法

Appフォルダではrequest.json()を使います。

import { NextResponse } from "next/server"

export async function POST(request) {
    const reqBody = await request.json()
    console.log(reqBody)        // リクエストbodyの中身が表示
    return NextResponse.json("ハロー")
}

従来(Next.jsバージョン12以前)は次のように書いていました。

export default function hello(req, res){
    console.log(req.body)      // リクエストbodyの中身が表示
    return res.json("ハロー")
}

10 バックエンドでのパラメータ取得(context)

バックエンド側でURLのパラメータを取得したい場合、context.params.idを使って次のようにします。

なおcontextはカッコ内の2つ目の値として書く必要があるので、ここではrequestも書いています。

import { NextResponse } from "next/server"

export async function GET(request, context) {
    console.log(context.params.id) 
    return NextResponse.json("ハロー")
}

従来(Next.jsバージョン12以前)はreq.query.idで取得をしていました。

const getSingleItem = async(req, res) => {
    console.log(req.query.id)
    return res.json("ハロー")
}

export default getSingleItem

11 _app.jsの廃止とlayout.jsの導入

従来(Next.jsバージョン12以前)、すべてのページで適用したいスタイルや設定などは_app.jsで行っていましたが、Appフォルダではlayout.jsがその代わりを果たします。

layout.jsという名前のファイルを作ると、そのフォルダ配下のすべてのファイルに適用されます。

(下記例ではblogフォルダ内にlayout.jsを作ると、配下にあるsummerwinterにも適用されます。)

layout.jpg

12 getStaticPathsの廃止とgenerateStaticParamsの導入

動的ページのURL生成・登録のために使われていたgetStaticPathsは、generateStaticParamsに変更されました。

const SingleBlog = () => {
    return (
        <h1>SingleBlogページ</h1>
    )
}

export default SingleBlog

export async function generateStaticParams() {

    // マークダウンファイルや外部データベースなどからURL名/ファイル名を取得するコード
    
    return paths
}

従来(getStaticPaths)は次のように書いていました。

const SingleBlog = () => {
    return (
        <h1>SingleBlogページ</h1>
    )
}

export default SingleBlog

export async function getStaticPaths() {

    // マークダウンファイルや外部データベースなどからURL名/ファイル名を取得するコード

    return {
        paths: paths,  
    }
}

13 getStaticPropsの廃止

スタティック・サイトの動的ページのデータ取得時に使われていたgetStaticPropsや、サーバーサイドレンダリング時のデータ取得に使われていたgetServerSidePropsは廃止され、データベースへの直接アクセスや、JavaScriptのfetch()でデータ取得を行えるようになりました。

(*このfetch()はNext.jsによって高機能化されており、キャッシュの設定なども行えます)

const getSingleItem = async(id) => {
    const response = await fetch("http://localhost:3000/api/item/readsingle/${id}") 
    const jsonData = await response.json() 
    const singleItem = jsonData.singleItem         
    return singleItem 
}

const ReadSingleItem = async(context) => {                        
    const singleItem = await getSingleItem(context.params.id)     
    return (
        <div>
            ....

14 プロジェクト全体に適用したい機能(middleware.js)

middleware.jsという名前のファイルを作ると、ユーザーからのアクセスはすべてこのmiddleware.jsを通過します。アクセス認証やリダイレクト処理のコードを書くときに便利です。

ポイントはappフォルダと同じ階層に作ること、そして名前を必ずmiddleware.jsにすることです。

middleware1.jpg

15 middleware.jsの適用範囲を制限する

middleware.jsはデフォルトでは、全ルートに対するすべてのリクエストが通過する設定になっています。

もしmiddleware.jsの働きを特定のルートだけに制限したい場合は、matcherを使いましょう。

(*フォルダ配下すべてに適用したい場合は:path*と書きます)

// middleware.js

import { NextResponse } from "next/server"

export async function middleware() {
    return NextResponse.next()
}

export const config = {
    matcher: ["/about", "/api/item/create", "/api/item/update/:path*"],
}

16 メタデータの設定方法

ページタイトルやdescriptionなどのメタデータは、従来(Next.jsバージョン12以前)は<Head>を使っていましたが、Appフォルダでは次のように設定します。

// app/page.js

export const metadata = {
    title: "トップページ",
    description: "弊社はAI分野のリーディングカンパニーです。",
}

const Index = () => {
    return (
        <div>
            ...

データによって表示内容の変わる動的ページの場合は、generateMetadataを使い、データ取得のコードを書く必要があります。

// app/blog/[id]/page.js

export async function generateMetadata() {

    // マークダウンファイルや外部データベースなどからデータを取得するコード
    
    return { 
        title: data.title,
        description: data.excerpt,
    }
}

const SingleBlog = async() => {
    return (
        <div>
            ...

しかし実はもっと簡単に設定する方法もあり、それはクライアント・コンポーネントにも使える方法です。下記記事を参考にしてください。

17 ローディングの設定方法

APIなどからデータを取得するファイルと同じフォルダ内にloading.jsという名前のファイルを作ると、データ取得中にはそれが表示されます。

loading.jpg

18 404ページの設定方法

not-found.jsappフォルダ内に作ると、404ページとして機能します。

404-1.jpg

次のように表示されます。

404-2.jpg

19 next/imageをheightとwidthの設定なしで使う

next/imageではheightwidthの設定がマストですが、importで読み込んだ画像の場合は、それらの設定は不要になります。

import Image from "next/image"
import profilePic from "./myself.png"
 
const Hello = () => {
    return (
        <Image src={profilePic} />
    )
}

Next.jsでつくるフルスタックアプリ

【最新バージョンのNext.jsとAppフォルダで、フルスタックアプリを自力で作る力を手にいれる】

1980円0円

Amazonのページに移動する

20 環境変数の使い方 

.env.localファイル内で設定した環境変数は、process.env.変数名で呼び出せます。

// .env.local

URL = https://nextjsbook-fullstack-app-folder.vercel.app

呼び出し。

fetch(`${process.env.URL}/api/item/readall`)

しかしこれはサーバーサイドのコードでしか呼び出せないため、クライアント(ブラウザ)サイドのファイルから呼び出したい場合はNEXT_PUBLIC_をつけます。

// .env.local

NEXT_PUBLIC_URL = https://nextjsbook-fullstack-app-folder.vercel.app

呼び出し。

fetch(`${process.env.NEXT_PUBLIC_URL}/api/item/readall`)

21 開発環境と本番環境(Vercel)のURLを自動で切り替える

手元のコンピューターでの開発時にバックエンドのURLとしてhttp://localhost:3000を使っている場合、デプロイのたび、URLをVercelが割り当てている公開URLに変更する必要があり、手間がかかります。

開発環境でのURLは.env.developmentに、本番環境でのURLは.env.productionに次のように設定し、プロジェクト内のURLをすべてprocess.env.変数名に置き換えると、手元のコンピューターでは.env.developmentのURLが、デプロイ時には.env.productionのURLを、Vercelが自動で切り替えてくれます。

env-vari-dev-pro.jpg  

// .env.development

NEXT_PUBLIC_URL=http://localhost:3000
// .env.production

NEXT_PUBLIC_URL=https://nextjsbook-fullstack-app-folder.vercel.app

22 フォントの設定方法

最適化されたGoogle fontがnext/fontで簡単に使えます。

フォント設定は、サイトやアプリ全体に適用するのが普通なので、多くの場合layout.jsに書きます。

// app/layout.js

import { Eczar } from "next/font/google"
// ⬆フォントを読み込み

const font = Eczar({ subsets: ["latin"] })
// ⬆フォントの設定

export default function RootLayout({ children }) {
  return (
      <html>
                   // ⬇フォントを適用
          <body className={font.className}>
              {children}
          </body>
      </html>
  )
}

以下、ビギナーの方がよく挙げる不明点を2つ解説します。

フォント名について

2文字以上のフォント名、たとえば「Shantell Sans」や「 Advent Pro」のようなフォントは、間にアンダースコアを挟んで次のように書きます。

import { Shantell_Sans } from "next/font/google"

const font = Shantell_Sans({ subsets: ["latin"] })

...
import { Advent_Pro } from "next/font/google"

const font = Advent_Pro({ subsets: ["latin"] })

...

subsetsについて

「subsets」とは必要な文字だけを抜き出すことです。

文字には「A、B、C、a、b、c....」といったアルファベットの文字だけでなく、キリル文字やギリシャ文字、ハングル、ひらがな、カタカナ、漢字といった表記法もあります。

フォントによっては、アルファベットだけでなくギリシャ文字なども含まれているものがあり、その場合ギリシャ文字の読み込みの分だけ負荷が上がるので、「subsets」でアルファベットの読み込みだけを指定しましょう。

アルファベットだけを読み込むときには、上記例のようにsubsets: ["latin"]と書きます。

*フォントによっては、次のようにweightの設定がマストの場合もありますが、その場合はエラーが表示されるので、それに従って修正しましょう。

import { Klee_One } from "next/font/google"

const font = Klee_One({ 
    subsets: ["latin"], 
    weight: "400",
})

...

23 フォルダ名をURLに反映させない方法

appフォルダ内では、フォルダの名前がURLに反映されます

たとえば次のように、マーケティング(marketing)とセールス(sales)フォルダを作成し、その中にaboutprofitフォルダを作ったとします。

file-folder-1.jpg

URLは次のようになります。

app/marketing/about/page.js/marketing/about

app/sales/profit/page.js/sales/profit

しかし、グループ分けや整理整頓のためにフォルダを作りたい場合もあります。つまりURLに反映されないフォルダを作りたいときです。

その場合は、下図のように丸カッコでフォルダ名をはさみましょう。

file-folder-2.jpg

このようにすると、フォルダ名がURLに反映されなくなり、URLは次のようになります。

app/(marketing)/about/page.js/about

app/(sales)/profit/page.js/profit

Next.jsでつくるフルスタックアプリ

【最新バージョンのNext.jsとAppフォルダで、フルスタックアプリを自力で作る力を手にいれる】

1980円0円

Amazonのページに移動する

24 Server ActionsとAPI、どっちを使う?

Server Actionsを使えば、API(Route Handler)を作らなくてもダイレクトにデータベースとの連携やCRUD操作が可能ですが、それぞれに強み/弱みがありますす

API(Route Handlers) Server Actions
特徴 汎用的 限定的
用途 データベースとの複雑な連携操作があるケース シンプルなCRUD操作が主のケース
メリット  APIを、他のアプリケーションやモバイルアプリも利用可 コードの記述量を抑え、コンパクトかつ短時間で開発可
デメリット コードの記述量や開発工程が増える Next.jsでしか利用できない
Profile Pic

メルマガ配信中
(from 三好アキ/エンジニア)


React、Next.js、TypeScriptなど最新のウェブ開発のお役立ち情報を、ビギナー向けにかみ砕いて無料配信中。
(*配信はいつでも停止できます)