Skip to content

フロントエンド実践 - ポートフォリオを作る

参考リポジトリ

実際のWebサイトは、HTML, CSS, JavaScriptを直接書くだけでなく、Webフレームワークを利用して作られています。

今回は、JavaScriptのフレームワークであるNext.jsと、UIを補完するReactライブラリを使って、自分だけのポートフォリオサイトを作ります。

詳しい文法は、React References を適宜参照してください。

demo


目次


  1. Next.js入門

    1-1. Webフレームワークとは

    1-2. TypeScriptでの型定義

    1-3. Next.jsプロジェクトの作成

    1-4. ファイル構成

  2. Next.jsの設計思想

    2-1. ライフサイクルとは

    2-2. RSC(React Server Components)

  3. UIを作る

    3-1. 画面全体の構成

    3-2. メインページの作成

    3-3. ヘッダー・フッターの作成

    3-4. ページを増やす

    3-5. faviconの設定

  4. UIは状態(state)に依存する

    4-1. ブラウザが状態を保持する仕組み

    4-2. ライトモード・ダークモード対応の実装

    4-3. Reactの状態管理

    4-4. デベロッパーツールを使う


1. Next.js入門

1-1. Webフレームワークとは

Web開発に必要な機能が予めパッケージ化されたもの。さまざまなWebフレームワーク・ライブラリが生まれ、進化し続けている。

  • フロントエンド系フレームワーク

    UIの更新、コンポーネント化、ルーティング、状態管理など。

    • Next.js(UIでReactを使用、言語はJavaScript/TypeScript)
    • Vue.js(JavaScript)
  • バックエンド系フレームワーク

    DB接続、APIルーティング、セキュリティ対策(認証)など。

    • Django, Flask, FastAPI(Python)
    • Ruby on Rails(Ruby)
    • Laravel(PHP)
    • Express, NestJS(JavaScript/TypeScript)

TIP

フロントエンド

ユーザから見える部分、操作性。ボタンのデザイン、アニメーション、フォームの入力チェックなど。

バックエンド

ユーザに見えない裏側の部分。実際の処理やデータ管理、認証など。

1-2. TypeScriptでの型定義

Next.jsフレームワークを用いたフロントエンドの開発では、JavaScriptにを追加した、TypeScriptが使われることが多い。

WARNING

TypeScriptは、フレームワークではなく言語

「型」の重要性

とは、コンピュータにデータの扱い方を教えるための、データが持つ性質のこと。整数型int、浮動小数点型float、文字列型string、真偽値boolなど。

コンピュータの型付けの方法は、言語によって異なり、次の2種類に分けられる。

  • 動的型付け

    実行時に型が決まる。型を書かなくて良い。Python, JavaScriptなど。

    python
    x = 10
    x = "hello"
  • 静的型付け

    コンパイル時(実行前)に型が決まる。型を明示する必要があるが、コンパイル時にエラーを発見できる。TypeScript, Java, Cなど。

    js
    let x: number = 10;
    x = "hello"; // ←エラー

Web開発においては、次のような理由から型定義が重要である。

  1. バグの早期発見

    静的型付けでは、型が壊れるとコンパイルエラーになり、エラーの箇所を特定できるため、デバッグやリファクタリングが容易になる。

    動的型付けでは、実行時に初めてUndefinedなどのエラーが発生するため、バグの原因の特定が難しい。

  2. 可読性/保守性の向上

    型を明示するため、変数や関数がどのようなデータを扱うのか明確になる(可読性↑)。これにより、他人や将来の自分がコードを理解しやすくなる(保守性↑)。

TIP

リファクタリング

アプリケーションの外部から見た動作や機能を変えずに、内部のソースコードを整理・改善する作業。

1-3. Next.jsプロジェクトの作成

  1. Node.jsバージョン確認

    $ node -v で、Node.jsのバージョンが【20.11.1】以上であることを確認する。

    WARNING

    Node.js をインストールしていない方は、1.開発環境の構築 を参照してください。

  2. Next.js関連のコマンドのインストール

    $ npm i -g create-next-app

    を実行して、Next.jsを立ち上げるためのコマンドをインストールする。

  3. ターミナルで、プロジェクトのディレクトリを置く場所に移動

    WARNING

    WindowsでWSLを使用している方

    Windows側のディレクトリ(/mnt/c/...)で、4のコマンドを実行すると非常に遅くなります。

    WSLのホームディレクトリに移動し($ cd ~)、適当なディレクトリを作り、次に進みましょう。

  4. アプリケーションの作成

    $ npx create-next-app@latest <プロジェクト名>

    を実行すると、Next.js製アプリの雛形(Next.js + React + 設定済み環境)が作られる。

    install

  5. 依存関係のインストール(後述)

    $ npm install
  6. 開発用サーバlocalhostの起動

    アプリのディレクトリ下(/portfolio)で、以下を実行する。

    $ npm run dev

    npmrundev

    TIP

    ポート番号を自分で指定したい場合は、

    $ npm run dev -- --port 3001

    とする。

  7. ブラウザで、http://localhost:3000 にアクセスし、次の画面が表示されれば完了。

    local

1-4. ファイル構成

VSCode で、先ほど作成したportfolioディレクトリを開いて、Next.js製プロジェクトのファイル構成を確認する。

TIP

先ほどのターミナルで、/portfolio にいる状態で、

$ code .

を実行すれば、一発でVSCodeに飛べる。

appディレクトリ

ブラウザでlocalhost:3000にアクセスした際に描画された画面のHTMLは、app/layout.tsxおよびapp/page.tsx内に書かれている。

app

→ 実際に、この中身を編集していく。

package.json

中身は次のようになっている。

json
{
  "name": "my-app",
  "version": "0.1.0",
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  },
  "dependencies": {
    "next": "14.x",
    "react": "18.x",
    "react-dom": "18.x"
  }
}

チーム開発では、この設計書を他者がクローン(次回!)することで、プロジェクトの実行環境を再現することができる。

  • "script"

    コマンドのショートカット一覧。例えばnpm run devは、

    json
    "scripts": {
      "dev": "next dev"
    }

    なので、next devを実行しているのと同じ。

  • "dependencies"

    Next.jsプロジェクトは、外部ライブラリ(Reactなど)に依存している。その外部ライブラリのバージョンを明記しておく。

    この依存関係(外部ライブラリのバージョン情報)の通りに、ライブラリ・モジュールをインストールするコマンドが以下。

    $ npm install

    これを実行すると、node_modules(= インストールしたライブラリの実体)が作られる。

TIP

「外部ライブラリ」はどこにある?

インターネット上のnpmというサーバに、何百万個ものライブラリが置かれている。世界中の人が作ったコードが、無料で置いてある。React, Next, Axios, Lodashなど。

$ npm install で、どこかのnpmサーバにHTTPでアクセスしている。

TIP

JSON(JavaScript Object Notation)

データフォーマット(≠プログラミング言語)の一つ。{ key: value } の書き方をする。

json
{
  "name": "骨なしチキン",
  "age": 19
}

という書き方をする。JSONの他には、XML(HTMLのようにタグで囲む記法)、YAML(YAML Ain't Markup Language)などがある。


2. Next.jsの設計思想

Next.jsは、既存のReactという革命的なUIライブラリを使って、Webアプリケーションを開発できるようにした、フレームワークである。

TIP

UI(User Interface)

Webアプリ・サービスと、ユーザーとを繋ぐ接点。つまり画面操作、ボタンや文字、そのデザインのこと。UIを通じて得られる「体験・使い心地」全体をUX(User Experience)という。

  • 2013年、旧Facebook社がReactライブラリを開発し、「UIをコンポーネントで組む」という概念が生まれた。

  • 2016年、この設計思想をWebアプリ開発で実用化すべく、Vercel社がNext.jsフレームワークを開発した。

2-1. ライフサイクルとは

Webページにおける処理がどういう順番で進んで、どのタイミングで何が起こるか、という流れのこと。

一般的に、フロントエンドのライフサイクルは、次のようになる。

  1. クライアントがリクエストを送信(URLが叩かれる)

  2. サーバがレスポンスを返す(HTML, CSS, JS)

  3. ブラウザが、HTMLをパースしてDOM生成

  4. CSSを適用

  5. レンダリング

  6. JSが実行される

  7. ユーザの操作によっては再レンダリング

これは、2〜6がクライアントが行われるため、①データ取得が遅い、②セキュリティ的にクライアントに寄っている、という問題点がある。

特に、「6. JSが実行される」においては

  1. データ取得(fetch
  2. UI組み立て
  3. イベント処理(クリックなど)
  4. DOM更新

これら全てのロジックがクライアント側で実行されるため、データ取得の完了を待つ必要があり、初期表示が遅くなる。

2-2. RSC(React Server Components)

前述の問題を解消するため、Next.jsフレームワークは、Reactライブラリの思想を拡張+実用化した。

  • UI(状態)をコンポーネントで分割して管理する(Reactライブラリが持っていた思想)

  • そのうち「データ取得+UI生成」のJSをサーバ側で実行する(= Server Components

  • ブラウザは、返ってきたServer Componentsの表示(JSは返ってこない)、Client ComponentsのJS実行、その他イベント処理をするだけ

というライフサイクルが実現した。

TIP

Client Components

ブラウザ側でJSが実行されるもの。プログラム内で "use client"; と明示する。


3. UIを作る

3-1. 画面全体の構成

一般的なWebページは、ヘッダーコンテンツフッターで構成されている。

  • ヘッダー:サイト名、ロゴ、ナビゲーションメニューなどを表示する領域

  • コンテンツ:ページごとに内容が変わる中心部分

  • フッター:コピーライト、問い合わせ先、補助リンクなどを表示する領域

content

画面遷移を伴うWebサイトでは、ヘッダーやフッターを共通コンポーネントとして切り分け、複数ページで再利用する。

これにより、デザインを統一化しやすくなり、修正も1箇所で済むようになる。

3-2. メインページの作成

Next.js(React)においては、次のように、すべてのUIがコンポーネント(関数)として設計されている。戻り値にHTMLが書かれる。

js
export default function xxx() {
   return ...
}

JSの export とは

js
const msg = "hello";

と変数を宣言したとき、これは他のファイルからは見えない。これを、

js
// file.js

export const msg = "hello";

とすることで、

js
// file2.js

import { msg } from "./file.js";

console.log(msg); // hello

のように外部ファイルで import して参照できるようになる。

export, import によって、コンポーネントの依存関係(= どこでどのコンポーネントを再利用するか)を管理できる。

メインページのコンポーネントを編集

app/page.tsxHome() 内を編集し、メインページを構築する。

js
export default function Home() {
  return (
    <div className="intro-wrap">
      <h1 className="intro-title">自己紹介</h1>
      <p className="intro-lead">はじめまして、骨なしチキンです!</p>

      <section className="intro-section">
        <h2>取り組んでいること</h2>
        <ul className="intro-list">
          <li>Web開発</li>
          <li>プログラミングサークルの運営</li>
        </ul>
      </section>

      <section className="intro-section">
        <h2>SNS</h2>
        <ul className="intro-list">
          <li>
            <a href="https://x.com/74rina_">Xアカウント</a>
          </li>
        </ul>
      </section>
    </div>
  );
}

className で要素を指定し、そこに当てるCSSを/app/globals.cssに書く。

css
@import "tailwindcss";

body {
  color: var(--foreground);
  background: var(--background);
  font-family: Arial, Helvetica, sans-serif;
  margin: 0;
}

main {
  min-height: calc(100vh - 160px);
}

.intro-wrap {
  max-width: 760px;
  margin: 0 auto;
  padding: 24px 16px 64px;
  line-height: 1.7;
}

.intro-title {
  font-size: 36px;
  margin: 0 0 12px;
}

...

3-3. ヘッダー・フッターの作成

app/layout.tsxRootLayout() 内を編集し、ヘッダー・フッターのコンポーネントとする。

js
export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
        <header className="flex h-20 items-center justify-between border-b border-neutral-200 px-6">
          <p className="text-base font-semibold">ポートフォリオ</p>
          <nav className="flex items-center gap-4 text-sm text-neutral-600">
            <a className="hover:text-neutral-900" href="/">
              Home
            </a>
          </nav>
        </header>

        <main>{children}</main>

        <footer className="flex h-20 items-center justify-center border-t border-neutral-200 px-6 text-sm text-neutral-500">
          <p>© 2026 骨なしチキン</p>
        </footer>
      </body>
    </html>
  );
}
  • className=" " に直接CSSを書くことも可能(インラインスタイルシート)。

  • {children} の中身には、app/page.tsxdefault export した関数(今回はHome())の戻り値が埋め込まれる。これはNext.jsの仕様。

つまり、次のようにページが構築されている。

<RootLayout>
   <Home />
</RootLayout>

po1

3-4. ページを増やす

自己紹介の Home ページに加えて、作品を置く Works ページを作る。

app/配下にworksというディレクトリを新規作成し、その中にpage.tsxというファイルを作成する。その中に、Worksページのコンポーネントを次のように作成する。

js
export default function WorksPage() {
  return (
    <div className="intro-wrap">
      <h1 className="intro-title">作品</h1>
      <p className="intro-lead">骨なしチキンの制作物をまとめます。</p>

      <section className="intro-section">
        <h2>Qiita記事</h2>
        <a href="https://qiita.com/74rina_/items/415208959e7616e427a6">
          TryHackMeの解説記事
        </a>
        <br></br>
        <a href="https://qiita.com/74rina_/items/b87fc9e7036a5cb30d77">
          自作OSにファイルシステムを実装する
        </a>
      </section>
    </div>
  );
}

また、ヘッダーにWorksへのリンクを追加する。

html
<a className="hover:text-neutral-900" href="/works"> Works </a>

po2

3-5. faviconの設定

faviconとは、ブラウザのタブのアイコンのこと。

favicon

通常は、HTML内に

html
<link rel="icon" href="favicon.ico" />

を追加することで設定できる。

今回は、Next.jsがデフォルトでfavicon.icoの設定スクリプトを用意しているため、画像を差し替えるだけで良い。

TIP

拡張子 .ico

普通の画像(png, jpg)ではなく、アイコン用の拡張子。

  • 複数のサイズ(16*16, 32*32など)を1ファイルに入れられる。状況に応じて最適なサイズを使ってくれる。

  • 背景透過に対応。

などの機能を持つ。

4. UIは状態(state)に依存する

4-1. ブラウザが状態を保持する仕組み

WebサイトのUIは、状態(state)に依存することを意識する。

INFO

状態とは

ユーザーがログイン中か、新たに投稿されたか、カートに追加されたか、などを表す変数のこと。

そこで、クライアント(ブラウザ)とサーバの双方が、状態を管理する仕組みを持っている。状態をどの期間・範囲まで永続化させるのか、も重要。

以下は、ブラウザが保持する情報(変数)の種類と、その永続範囲である。

種類タブ閉じたらリロードしたらタブ間共有
Cookie△(期限次第)
localStorage
sessionStorage××
メモリ×××

4-2. ライトモード・ダークモード対応の実装

画面のライトモード・ダークモードを切り替えられるボタンを実装する。

本質は、「ページをリロードしたり、タブを閉じたりしても、ライト・ダークの設定が適用されたままにする」部分である。

値の永続範囲が最も大きい localStorage を、次のように使用する。

  1. 過去の設定(ライト or ダーク)を取得

    tsx
     useEffect(() => {
       const storedTheme = localStorage.getItem(storageKey);
       ...
     }, []);
  2. その設定を適用させる

    tsx
    const initialTheme = storedTheme ?? (media.matches ? "dark" : "light");
    setTheme(initialTheme);
    applyTheme(initialTheme);
  3. ユーザーがボタンを押して切り替えた瞬間

    tsx
    localStorage.setItem(storageKey, nextTheme);

4-3. Reactの状態管理

UIは状態に依存している(再掲)。

  1. 状態(state)が変わる
  2. UIが自動で再計算される

Reactでは、これを次のように書くことができる。

tsx
function Counter() {
  const [count, setCount] = useState(0);

  return <button onClick={() => setCount(1)}>{count}</button>;
}
s;

TIP

Reactの useState()

  • 引数 :初期値

  • 戻り値:[現在の状態, 状態を更新する関数]

tsx
const [state, setState] = useState(initialValue);

「状態を更新する関数」が呼ばれた瞬間、Reactは再レンダリングを行う。すなわち、function Counter()が再度実行される。

4-4. デベロッパーツールを使う

Cookie や localStorage などの「ブラウザ自体が持つ値」を調べたり、ブラウザが送信するリクエスト・レスポンスの中身を見たりするときは、ブラウザのデベロッパーツール(開発者ツール)を使う。

  • (Windows)Ctrl + Shift + I

  • (Mac)Cmd + Option + I

TIP

Web開発の途中でエラーが発生したら、まずは開発者ツールを開いて、エラーログを読む!

Networkタブ

ブラウザが、サーバとやり取りした結果を見ることができる。

以下は、サーバから受け取ったファイル一覧。

net

また次のように、ファイルの中身(以下はHTTPレスポンスヘッダ)も確認できる。

net

Applicationタブ

ブラウザの記憶装置のもつ値を、すべて見ることができる。

storage

Consoleタブ

JavaScript実行環境(> の行が入力部分)。

console

Web開発では、プログラム内に

js
console.log("debug log");

とデバッグログを入れておき、開発者ツールのConsoleタブで、そのログを確認する。

お疲れさまでした!

今回の成果物に対し、

  • UI、Webデザインを凝ってみる

  • CSSでアニメーションをつける

などの拡張を施し、ぜひ自分のポートフォリオサイトを公開してみましょう!

このような静的サイトは、GitHub Pages で簡単にデプロイすることができます。

参考文献