【個人開発】Next.jsでのWebサイト制作 #1　概要とlayout.tsxの実装

## はじめに
今回から数回の記事に分けて、個人開発したWebサイトの紹介をしたいと思います！

2024年から本格的にフロントエンドを学び始め、一番最初の成果物になります。

間違った技術の使い方をしている部分等ありましたら、ぜひコメント等でご指摘いただければ幸いです！

## 成果物
早速ですが、こちらが成果物です！
友人（ラクロス選手）のWebサイトを制作しました！！

https://ty-official-hp-14.vercel.app


### プロジェクト名
Tetsuya Yamada Official Home Page

### きっかけ
プロジェクト名にもあるTetsuya Yamada(以降、ヤマディー)は、私の高校からの友人です。

ヤマディーは大学卒業後、某メガベンチャーに入社しましたが、
2028年LA五輪のラクロス代表を目指すために、1年未満で退社し、カナダに渡り、ラクロスに専念する道を選びました。

彼の行動力と夢に向かって挑戦する姿は、ソフトウェアエンジニアを目指す自分の行動のきっかけにもなりました。

どうせ何かを作るなら自分の技術力アップだけでなく、誰かのためになるものを作ろうと思い、彼への恩返しも兼ねてWebサイトを作らせてもらうことになりました。


### 使用技術
言語：TypeScript
フレームワーク：Next.js(App Router), React
スタイル：Tailwind CSS
その他：microCMS、EmailJS
デプロイ：Vercel

### サイトの構成

* レイアウト （ヘッダー、フッター）
* Top（トップページ）
* Profile （自己紹介ページ）
* Gallery （ギャラリーページ）
* Blog （ブログページ）
* Contact （問合せページ）

## レイアウト
では早速、レイアウト部分の説明からさせていただきます。

レイアウト部分はNext.jsプロジェクトにおいて共通のレイアウトを記載できる「layout.tsx」（app直下）に実装しました。
layout.tsx内でHeaderコンポーネントとFooterコンポーネントを呼び出しています。
```tsx
import type { Metadata } from "next";

import "./globals.css";
import Header from "./components/layouts/Header/Header";
import Footer from "./components/layouts/Footer";
import "animate.css";

export const metadata: Metadata = {
  title: "Yamada Tetsuya Official",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="ja">
      <body>
        <div className=" bg-[url('/images/bg/bg.jpeg')] text-white ">
          <Header />
          {children}
          <Footer />
        </div>
      </body>
    </html>
  );
}

```

ファイル構造は以下の通りです。
HeaderコンポーネントとFooterコンポーネントは、app/components/layoutsに置きました。
app/compenentsフォルダは、プロジェクト全体の共通部品を管理するフォルダです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817219/720a316e-578b-a941-44f7-81e671864b0e.png)

### ヘッダー
ヘッダーはごく普通のヘッダーです。

タイトルの横にメニューリストがあり、それぞれのページに遷移するようになっております。（Next.jsのLinkタグ使用）
（アイコン画像はNext.jsのImageタグ使用）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817219/5efd28c2-3ca9-23a1-6a79-cddd68ab2e4e.png)

またメニューリストはレスポンシブ対応しており、一定の画面幅を下回ると、ハンバーガーメニューに置き換わるように実装しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817219/e3b4137f-9fd0-ff02-0a2c-a0cbd743a1e6.png)

```tsx
import Image from "next/image";
import Link from "next/link";

import HeaderHamburger from "./HeaderHamburger";

// ヘッダー

function Header() {
  return (
    <header className="text-black hadow-md">
      <div className="container mx-auto px-3 py-4 flex justify-between items-center">
        <div className="flex items-center space-x-4">
          {/* ロゴ１ */}
          <Image
            src="/images/top/img11.png"
            alt="teamIcon"
            width={100}
            height={100}
            className="hidden lg:block w-24 h-24"
          />
          {/* ロゴ２ */}
          <Image
            src="/images/top/img12.jpeg"
            alt="teamIcon"
            width={100}
            height={100}
            className="hidden lg:block w-24 h-24"
          />
          {/* メインタイトル */}
          <Link
            href="/"
            className="text-4xl xl:text-6xl font-bold hover:text-yellow-500"
          >
            Tetsuya Yamada
          </Link>
        </div>
        {/* メニューバー（画面サイズがlg以上の時) */}
        <nav className="hidden lg:flex space-x-8">
          <Link
            href="/"
            className="hover:underline underline-offset-8 decoration-yellow-500 text-xl xl:text-3xl font-bold"
          >
            Top
          </Link>
          <Link
            href="/profile"
            className="hover:underline underline-offset-8 decoration-yellow-500 text-xl xl:text-3xl font-bold"
          >
            Profile
          </Link>
          <Link
            href="/gallery"
            className="hover:underline underline-offset-8 decoration-yellow-500 text-xl xl:text-3xl font-bold"
          >
            Gallery
          </Link>
          <Link
            href="/blog"
            className="hover:underline underline-offset-8 decoration-yellow-500 text-xl xl:text-3xl font-bold"
          >
            Blog
          </Link>
          <Link
            href="/contact"
            className="hover:underline underline-offset-8 decoration-yellow-500 text-xl xl:text-3xl font-bold"
          >
            Contact
          </Link>
        </nav>
        {/* ハンバーガーメニュー（画面サイズがlg以下の時） */}
        <HeaderHamburger />
      </div>
    </header>
  );
}

export default Header;

```
また、ハンバーガーメニューは、useStateを使用し、ハンバーガーアイコンのクリック状態を管理することで、クリックに応じてメニューが開閉するように実装しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817219/d2f7aecb-9495-c667-43c8-8678d653e671.png)

```tsx
import Image from "next/image";
import Link from "next/link";

import HeaderHamburger from "./HeaderHamburger";

// ヘッダー

function Header() {
  return (
    <header className="text-black hadow-md">
      <div className="container mx-auto px-3 py-4 flex justify-between items-center">
        <div className="flex items-center space-x-4">
          {/* ロゴ１ */}
          <Image
            src="/images/top/img11.png"
            alt="teamIcon"
            width={100}
            height={100}
            className="hidden lg:block w-24 h-24"
          />
          {/* ロゴ２ */}
          <Image
            src="/images/top/img12.jpeg"
            alt="teamIcon"
            width={100}
            height={100}
            className="hidden lg:block w-24 h-24"
          />
          {/* メインタイトル */}
          <Link
            href="/"
            className="text-4xl xl:text-6xl font-bold hover:text-yellow-500"
          >
            Tetsuya Yamada
          </Link>
        </div>
        {/* メニューバー（画面サイズがlg以上の時) */}
        <nav className="hidden lg:flex space-x-8">
          <Link
            href="/"
            className="hover:underline underline-offset-8 decoration-yellow-500 text-xl xl:text-3xl font-bold"
          >
            Top
          </Link>
          <Link
            href="/profile"
            className="hover:underline underline-offset-8 decoration-yellow-500 text-xl xl:text-3xl font-bold"
          >
            Profile
          </Link>
          <Link
            href="/gallery"
            className="hover:underline underline-offset-8 decoration-yellow-500 text-xl xl:text-3xl font-bold"
          >
            Gallery
          </Link>
          <Link
            href="/blog"
            className="hover:underline underline-offset-8 decoration-yellow-500 text-xl xl:text-3xl font-bold"
          >
            Blog
          </Link>
          <Link
            href="/contact"
            className="hover:underline underline-offset-8 decoration-yellow-500 text-xl xl:text-3xl font-bold"
          >
            Contact
          </Link>
        </nav>
        {/* ハンバーガーメニュー（画面サイズがlg以下の時） */}
        <HeaderHamburger />
      </div>
    </header>
  );
}

export default Header;

```

### フッター

次にフッターの説明です。

フッターには、メニューと各 SNSへのリンクを記載しました。
特記するような技術は使用していませんので、コードの掲載は割愛します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817219/94806b64-e390-693e-6c7d-b4227160df2f.png)

## 最後に
最後までお読みいただきありがとうございました！

#1はレイアウト部分の説明までとさせていただきます。

#2では、共通機能（プロジェクト全体で使用しているアニメーション機能の実装）について紹介予定です。

ありがとうございました！！！