Open30

Learn Next.js Getting Started | Work Through

ちーさんちーさん

プロジェクト作成

今回はnext-official-app配下に作成する

npx create-next-app@latest nextjs-dashboard --example "https://github.com/vercel/next-learn/tree/main/dashboard/starter-example" --use-pnpm

結果:OK:Successと表示されている

350MBくらい

ちーさんちーさん

以下ファイルを確認
app\lib\definitions.ts

データベースから返される型を「手動」で定義しているファイルとのこと

ちーさんちーさん

tailwindを利用する

app\page.tsx
export default function Page() {
  return (
    <main className="flex min-h-screen flex-col p-6">
      <div className="flex h-20 shrink-0 items-end rounded-lg bg-blue-500 p-4 md:h-52">
        {/* <AcmeLogo /> */}
      </div>
      <div className="mt-4 flex grow flex-col gap-4 md:flex-row">
        <div className="flex flex-col justify-center gap-6 rounded-lg bg-gray-50 px-6 py-10 md:w-2/5 md:px-20">
+        <div className="relative w-0 h-0 border-l-[15px] border-r-[15px] border-b-[26px] border-l-transparent border-r-transparent border-b-black"/>
          <p className={`text-xl text-gray-800 md:text-3xl md:leading-normal`}>
            <strong>Welcome to Acme.</strong> This is the example for the{' '}
            <a href="https://nextjs.org/learn/" className="text-blue-500">
              Next.js Learn Course
            </a>
            , brought to you by Vercel.
          </p>
・・・

結果:OK(三角形が表示された)

ちーさんちーさん

tailwindからCSSモジュールに置き換える

app\ui\home.module.css
+.shape {
+    height: 0;
+    width: 0;
+    border-bottom: 30px solid black;
+    border-left: 20px solid transparent;
+    border-right: 20px solid transparent;
+  }
app\page.tsx
import Link from 'next/link';
+ import styles from '@/app/ui/home.module.css';
・・・
export default function Page() {
  return (
    <main className="flex min-h-screen flex-col p-6">
      <div className="flex h-20 shrink-0 items-end rounded-lg bg-blue-500 p-4 md:h-52">
        {/* <AcmeLogo /> */}
      </div>
      <div className="mt-4 flex grow flex-col gap-4 md:flex-row">
        <div className="flex flex-col justify-center gap-6 rounded-lg bg-gray-50 px-6 py-10 md:w-2/5 md:px-20">
-         <div className="relative w-0 h-0 border-l-[15px] border-r-[15px] border-b-[26px] border-l-transparent border-r-transparent border-b-black"/>
+         <div className={styles.shape} />
          <p className={`text-xl text-gray-800 md:text-3xl md:leading-normal`}>
            <strong>Welcome to Acme.</strong> This is the example for the{' '}
            <a href="https://nextjs.org/learn/" className="text-blue-500">
              Next.js Learn Course
            </a>
            , brought to you by Vercel.
          </p>
・・・

結果:OK(表示が変わらないことを確認できた)

ちーさんちーさん

CSSモジュール

以下とのこと、どのようなクラス名が生成されているか見てみる

CSS モジュールは各コンポーネントに一意のクラス名を作成するため、スタイルの衝突を心配する必要はありません

結果:OK
ブラウザで確認すると以下のようなクラス名が生成されてた
home-module__7sF-sa__shape

ちーさんちーさん

3章

https://nextjs.org/learn/dashboard-app/optimizing-fonts-images

フォントのデリバリを最適化する仕組みが備わっているとのこと

ビルド時にフォント ファイルをダウンロードし、他の静的アセットとともにホストします。つまり、ユーザーがアプリケーションにアクセスしたときに、パフォーマンスに影響を与えるフォントの追加ネットワーク リクエストは発生しません。

ちーさんちーさん

プライマリフォントの追加

app\ui\fonts.ts
+import { Inter } from 'next/font/google';

+export const inter = Inter({ subsets: ['latin'] });
app\layout.tsx
import '@/app/ui/global.css';
+import { inter } from '@/app/ui/fonts';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
-      <body>{children}</body>
+      <body className={`${inter.className} antialiased`}>{children}</body>
    </html>
  );
}

結果:OK(フォントが適用された

ちーさんちーさん

セカンダリフォントの追加

app\ui\fonts.ts
-import { Inter, Lusitana } from 'next/font/google'
+import { Inter, Lusitana } from 'next/font/google'

export const inter = Inter({ subsets: ['latin'] });
+export const lusitana = Lusitana({
+    weight: ['400', '700'],
+    subsets: ['latin'],
+});
app\page.tsx
        <div className={styles.shape} />
-          <p className={`text-xl text-gray-800 md:text-3xl md:leading-normal`} />
+          <p className={`${lusitana.className} text-xl text-gray-800 md:text-3xl md:leading-normal`}>
・・・
-       </* AcmeLogo / */>
+      <AcmeLogo />
・・・

結果:OK(フォントが適用され、ロゴが表示

ちーさんちーさん

画像のデリバリを開発者が意識することなく最適化する仕組みが備わっているとのこと

Web 開発における大きなトピックであり、それ自体が専門分野であると言えます。これらの最適化を手動で実装する代わりに、next/imageコンポーネントを使用して画像を自動的に最適化できます。

ちーさんちーさん

ヒーローイメージを追加

app\page.tsx
import styles from '@/app/ui/home.module.css';
+import Image from 'next/image';
・・・
          {/* Add Hero Images Here */}
+          <Image
+            src="/hero-desktop.png"
+            width={1000}
+            height={760}
+            className="hidden md:block"
+            alt="Screenshots of the dashboard project showing desktop version"
+          />
+          <Image
+            src="/hero-mobile.png"
+            width={560}
+            height={620}
+            className="block md:hidden"
+            alt="Screenshot of the dashboard project showing mobile version"
+          />

結果:OK(画像が表示
デスクトップ版

モバイル版

ちーさんちーさん

ダッシュボードページの作成

app\dashboard\page.tsx
export default function Page() {
    return <p>Dashboard Page</p>;
  }

結果:OK

ちーさんちーさん

ダッシュボードページに共通レイアウトを追加

app\dashboard\layout.tsx
import SideNav from '@/app/ui/dashboard/sidenav';
 
export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <div className="flex h-screen flex-col md:flex-row md:overflow-hidden">
      <div className="w-full flex-none md:w-64">
        <SideNav />
      </div>
      <div className="flex-grow p-6 md:overflow-y-auto md:p-12">{children}</div>
    </div>
  );
}

結果:OK(invoiceページの追加セクションは投稿を省略)

ちーさんちーさん

部分レンダリング

レイアウトを利用するとダッシュボードページおよび配下ページに自動で適用され、ページ遷移の際にロード済みのレイアウト部分は再レンダリングされない仕組みを備えている

ちーさんちーさん

https://nextjs.org/learn/dashboard-app/navigating-between-pages

LINKコンポーネント

前述の部分レンダリングの実装にあたる仕組み
結果:OK(DevToolでロードを確認すると部分レンダリングされてる)

/app/ui/dashboard/nav-links.tsx
import {
  UserGroupIcon,
  HomeIcon,
  DocumentDuplicateIcon,
} from '@heroicons/react/24/outline';
+import Link from 'next/link';
・・・
-          <a>
+          <Link
            key={link.name}
            href={link.href}
            className="flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3"
          >
            <LinkIcon className="w-6" />
            <p className="hidden md:block">{link.name}</p>
-          </a>
+          </Link>
ちーさんちーさん

データベースをシードする

Vercel Postgres SDKをインストール

pnpm i @vercel/postgres

WARNが2点、発生した)とりあえず進めます

localhost:3000/seedにアクセス
結果:NG:
1.以下メッセージが表示されましたが、「成功」のメッセージが確認できませんでした

Uncomment this file and remove this line. You can delete this file when you are finished


2.コンソールにてエラーが発生しました

Error: Connection terminated unexpectedly
    at un.<anonymous> (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:9496:77)
    at Object.onceWrapper (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:6628:141)
    at un.emit (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:6598:37)
    at x.<anonymous> (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:9340:19)
    at x.emit (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:6598:37)
    at WebSocket.<anonymous> (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:8601:26)
    at callListener (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:13128:18)
    at WebSocket.onClose (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:13067:17)
    at WebSocket.emit (node:events:519:28)
    at WebSocket.emitClose (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:13526:14)
    at TLSSocket.socketOnClose (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:14307:19)
    at TLSSocket.emit (node:events:531:35)
    at node:net:343:12
    at TCP.done (node:_tls_wrap:650:7)
    at TCP.callbackTrampoline (node:internal/async_hooks:130:17)
 ⨯ uncaughtException: Error: Connection terminated unexpectedly
    at un.<anonymous> (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:9496:77)
    at Object.onceWrapper (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:6628:141)
    at un.emit (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:6598:37)
    at x.<anonymous> (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:9340:19)
    at x.emit (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:6598:37)
    at WebSocket.<anonymous> (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:8601:26)
    at callListener (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:13128:18)
    at WebSocket.onClose (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:13067:17)
    at WebSocket.emit (node:events:519:28)
    at WebSocket.emitClose (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:13526:14)
    at TLSSocket.socketOnClose (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:14307:19)
    at TLSSocket.emit (node:events:531:35)
    at node:net:343:12
    at TCP.done (node:_tls_wrap:650:7)
    at TCP.callbackTrampoline (node:internal/async_hooks:130:17)

データベースへの接続に失敗していそうです

ちーさんちーさん

先ほどのWARNでSDKのインストールが正しく行われていない?
pnpm i @vercel/postgresの結果に関しては、同様のissueを発見
https://github.com/vercel/next-learn/issues/916

package.jsonでメッセージに従ってバージョンを調整してみます

dependencies:
- react 19.0.0-rc-cd22717c-20241013
+ react 19.0.0-rc-65a56d0e-20241020
- react-dom 19.0.0-rc-cd22717c-20241013
+ react-dom 19.0.0-rc-65a56d0e-20241020

結果:OK(先ほどの後者のWARNメッセージが発生しないことを確認

ちーさんちーさん

しかりlocalhost:3000/seedにアクセスするとコンソールのエラーは変わりません
やはりデータベースへ接続できないようです

ちーさんちーさん

そもそも以下の箇所をコメントアウトor削除する必用があったことに気が付いた・・

app\seed\route.ts
export async function GET() {
-  return Response.json({
-    message:
-      'Uncomment this file and remove this line. You can delete this file when you are finished.',
-  });
  try {
    await client.sql`BEGIN`;
    await seedUsers();
    await seedCustomers();
    await seedInvoices();
    await seedRevenue();
    await client.sql`COMMIT`;

    return Response.json({ message: 'Database seeded successfully' });
  } catch (error) {
    await client.sql`ROLLBACK`;
    return Response.json({ error }, { status: 500 });
  }
}

結果:OK(成功メッセージを確認、テーブル生成を確認)

ただし先ほどのエラーログは継続していた。
※本セクションとは関係ないエラーなのかもしれない・・
すると、ワーニングの解消も別に不要であったかもしれない・・
とりあえず当セクションは進められるので進めることにする

ちーさんちーさん

7章

※コードdiffを省略
結果:OK(ダッシュボードページに各種コンポーネントが反映されている

ここまでの実装では以下の課題を抱えているとのこと

・要は、このダッシュボート画面においてはパラレルにデータの取得をして差し支えない場面なのに、各コンポーネントに対して1つずつ完了をまってから直接で実行をしていてる
(ウォーターフォール的なアプローチになっているとのこと)