Chapter 06

チャットアプリのパーツを作ろう!

ますみ / 生成AIエンジニア
ますみ / 生成AIエンジニア
2024.12.21に更新

コンポーネントとは?

コンポーネントという言葉を聞いたことがありますか?これは、ウェブページを作るときの便利な「部品」のようなもので、ヘッダーやフッター、あるいはメインコンテンツなど、ページのさまざまな部分を簡単に再利用できます。

このコンポーネントの素晴らしいところは、部品の再利用によりコードをすっきりさせることと、それにより修正やテストが楽になることです。コンポーネントを上手に設計するコツは2つあります。1つは再利用性を意識することで、もう1つは外部から簡単に情報を渡せるようにしておくことです。

このコンポーネントの素晴らしいところは、部品の再利用によりコードをすっきりさせることと、それにより修正やテストが楽になることです。

つまり、「使い回しが効く」設計を心がけると、アプリケーション全体がグッと使いやすくなります。

今回作るパーツの概要

まずは、コンポーネントの概念を理解するために、見た目だけのコンポーネントを作ってみましょう!

  1. Header(ヘッダーコンポーネント)
  2. ChatMessage(メッセージ表示コンポーネント)
  3. ChatForm(ユーザー入力コンポーネント)

ヘッダーコンポーネント作成方法

まずは、チャットページのヘッダーコンポーネントを作ります。

app/components/layout/Header.tsx
export default function Header() {
  return (
    <header>
      <h1
        style={{
          background: "#006BD6",
          fontSize: "28px",
          color: "white",
          padding: "8px 10px",
          boxShadow: "0 2px 4px rgba(0, 0, 0, 0.2)",
        }}
      >
        チャットルーム
      </h1>
    </header>
  );
}

Header がコンポーネント名です。ヘッダーコンポーネントをlayout.tsxにインポートして、app/配下のページにヘッダーを適用させます。

app/layout.tsx
import "./globals.css";
import { ReactNode } from "react";
import Header from "@/components/layout/Header";  // 追加

export default function Layout({ children }: { children: ReactNode }) {
  return (
    <>
      <html lang="ja">
        <body>
          <Header />    // 追加
          <main style={{ background: "#F1F3F7", width: "100%", height: "100vh" }} >{children}</main>
        </body>
      </html>
    </>
  );
}

ユーザー入力コンポーネントの作成方法

次に、チャットページのユーザー入力コンポーネントを作ります。

app/components/chat/ChatForm.tsx
export default function ChatForm() {
  return (
    <div
      style={{
        position: "fixed",
        bottom: 0,
        width: "100%",
        padding: 20,
        background: "#fff",
      }}
    >
      <div style={{ display: "flex", gap: 10 }}>
        <input
          type="text"
          placeholder="メッセージを入力..."
          style={{
            width: "100%",
            padding: 10,
            borderRadius: 10,
            border: "1px solid #ccc",
          }}
        />
        <button
          style={{
            padding: 10,
            background: "#006BD6",
            color: "white",
            borderRadius: 10,
            border: "none",
          }}
        >
          送信
        </button>
      </div>
    </div>
  );
}

メッセージ表示コンポーネントの作成方法

画像を使う際にはnext/image コンポーネントをインポートして最適化できます。

ここでは、Doodle Ipsumというサイトランダムな画像をアイコンとして取得しています。

app/components/chat/ChatMessage.tsx
import Image from "next/image";

export default function ChatMessage() {
  return (
    <div style={{ padding: "30px 20px", height: "100%" }}>
      {/* 相手側のメッセージ */}
      <div style={{ display: "flex", gap: 10 }}>
        {/* サイトのURLでダミー画像を持ってくる */}
        <Image
          src="https://doodleipsum.com/700/avatar-2?i=0639d368201785f32891763286f61ca0"
          alt=""
          width={50}
          height={50}
        />
        <div
          style={{
            padding: "10px 20px",
            marginTop: 5,
            background: "#fff",
            borderRadius: 10,
            lineHeight: 1.5,
            height: "fit-content",
          }}
        >
          こんにちは!
        </div>
      </div>
      {/* 自分のメッセージ */}
      <div
        style={{
          display: "flex",
          gap: 10,
          justifyContent: "flex-end",
          marginTop: 20,
        }}
      >
        <div
          style={{
            padding: "10px 20px",
            marginTop: 5,
            background: "#006BD6",
            borderRadius: 10,
            lineHeight: 1.5,
            height: "fit-content",
            color: "white",
          }}
        >
          こんにちは!
        </div>
      </div>
    </div>
  );
}

外部URLを使った画像の最適化

ただ、このままでは以下のようなエラーになります。

Server Error
Error: Invalid src prop (https://placeimg.com/140/140/any) on `next/image`, hostname "placeimg.com" is not configured under images in your `next.config.js`
See more info: https://nextjs.org/docs/messages/next-image-unconfigured-host

https://nextjs.org/docs/app/api-reference/next-config-js/images

Imageコンポーネントで外部の画像を使うには、使用する外部ドメインをnext.config.js に設定しておく必要があります。

ここで使用したい画像パスは、「https://doodleipsum.com/700/avatar-2?i=0639d368201785f32891763286f61ca0」です。

そのため、protocolには「https」、hostname には「doodleipsum.com」を設定します。

/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "doodleipsum.com",
      },
    ],
  },
};

export default nextConfig;

2つのコンポーネントの組み合わせ方法

ChatMessageコンポーネントとChatFormコンポーネントを組み合わせれば、アプリの骨組みは完成です!

app/page.tsx
import { ChatForm } from "@/components/chat/ChatForm";
import { ChatMessage } from "@/components/chat/ChatMessage";

export default function Home() {
  return (
    <>
      <ChatMessage />
      <ChatForm />
    </>
  );
}

参考サイト

https://qiita.com/KokiSakano/items/834958e4ac3cbacfad3a

宣伝:もしもよかったらご覧ください^^

AIとコミュニケーションする技術(インプレス出版)』という書籍を出版しました🎉

これからの未来において「変わらない知識」を見極めて、生成AIの業界において、読まれ続ける「バイブル」となる本をまとめ上げました。

かなり自信のある一冊なため、もしもよろしければ、ご一読いただけますと幸いです^^