9
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【前編】脱Studio宣言。年間14,280円の固定費を削減するために、Gemini 3.0とReact 19でサイトを再構築した話

Last updated at Posted at 2025-12-03

はじめに

 
普段はFlutterエンジニアとしてモバイルアプリ開発をしている私ですが、個人で管理しているWebサイトの運用コストとカスタマイズ性に、少し前からモヤモヤを抱えていました。

これまではNoCodeツールである Studio を利用していました。

デザイン性が高く、サーバー構築不要で公開できる点は素晴らしいのですが、更新頻度がそこまで高くないサイトに対し、毎月のランニングコストが発生し続ける点に課題を感じていました。
 

「Studio Personalプラン:月額1,190円 × 12ヶ月 = 年間 14,280円」
 

「この1万4千円、技術力で削減できるのではないか?」

「ついでに、最近話題の React 19 や Tailwind CSS をキャッチアップする良い機会ではないか?」
 

そう思い立ち、Studioを卒業し、維持費ほぼ0円 の構成へ移行することを決意しました。
本記事は、Flutterエンジニアが AI(Gemini 3.0)とペアプログラミングをしながら、爆速でWebサイトを再構築した記録の前編です。
 

今回のゴールと技術スタック

【目標】
・ランニングコストの削減: Studioのサーバー代をカットし、ドメイン維持費のみにする。
・モダンな技術の習得: Webフロントエンドの最新トレンドを触る。
・AIの活用: デザインカンプを作らず、AIとの対話だけでコードベースを構築する。

【採用した技術スタック】
Framework: React 19 (Vite) + TypeScript
Styling: Tailwind CSS
Hosting: Firebase Hosting (Sparkプラン / 無料)
Partner: 生成AI (Gemini / Copilot)

 
Flutterエンジニアの視点から見ると、Tailwind CSS の「ユーティリティファースト」な考え方は、Widgetにプロパティを設定していく感覚に近く、CSSファイルを行き来する必要がないため非常に相性が良いと感じました。
 

1. 開発環境の構築:Viteで爆速スタート

まずはプロジェクトの作成です。現在は create-react-app ではなく、Vite(ヴィート) がデファクトスタンダードになりつつあります。

Node.jsのバージョン問題

開発を始めようとした矢先、Node.jsのバージョンエラーに遭遇しました。Viteの最新版は新しいNode.jsを要求するため、v18系から v22系(LTS) へアップグレードを行いました。Web界隈は環境の変化が早いですね。

プロジェクト作成

以下のコマンドひとつで、React + TypeScript の環境が整います。

npm create vite@latest my-project -- --template react-ts

Flutterでいう flutter create です。

フォルダ構成の理解

生成されたプロジェクトを見て、Flutterエンジニアの脳内で以下のようにマッピングしました。

src/main.tsxmain.dart (Entry Point)

src/App.tsxhome_page.dart (Root Widget)

public/assets/ (静的ファイル)

index.htmlandroid/ ios/ (外枠)

2. AIとのペアプログラミング実践

 
今回はデザインカンプ(Figmaなど)を用意せず、「頭の中のイメージをAIに伝えて、コードを出力してもらう」 スタイルで開発を進めました。

デザインとコーディングの一体化

以下は私が実際に投げかけたプロンプトになります。

日本で営業している植物屋のHPを作成してください。カラーはアースカラーをベースに3色までにしてください。セクションは、TOP、about、オンラインストアへの導線、お問い合わせ、営業時間、マップの構成で、下層ページなしのシングルページで作成してください。余白を意識し、モダンなデザインで最小限の情報で構成してください。

すると、以下のトップ画面ともに、かなり実用的でデザイン性のあるサイトが一発でできあがりました。

スクリーン ショット 2025-12-03 に 15.31.50 午後.png
スクリーン ショット 2025-12-03 に 15.36.26 午後.png
スクリーン ショット 2025-12-03 に 15.36.34 午後.png
スクリーン ショット 2025-12-03 に 15.36.39 午後.png

ここから、デザインの微調整や、画像の設置場所、文言について追加の指示を出し、完成に近づけました。
 

複雑なロジックもAI任せ:スクロールアニメーション

Webサイトによくある「スクロールするとふわっと表示されるアニメーション(Fade In)」の実装も、AIに任せました。

Flutterなら AnimationController を書くところですが、Reactでは IntersectionObserver というWeb APIを使います。

AIが書いてくれたカスタムフックがこちらです。

import { useState, useEffect, useRef } from 'react';

// AIが提案してくれたカスタムフック
const useFadeIn = (): [boolean, React.RefObject<HTMLDivElement>] => {
  const [isVisible, setIsVisible] = useState(false);
  const domRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const observer = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          setIsVisible(true);
        }
      });
    }, { threshold: 0.1 });

    const currentRef = domRef.current;
    if (currentRef) {
      observer.observe(currentRef);
    }

    return () => {
      if (currentRef) {
        observer.unobserve(currentRef);
      }
    };
  }, []);

  return [isVisible, domRef];
};

このフックのおかげで、コンポーネント側では const [isVisible, ref] = useFadeIn(); と書くだけでアニメーションが実装できました。

型定義による「Any」撲滅

開発中、データの受け渡し部分でTypeScriptの型エラー(警告)が出ましたが、ここもAIの出番です。
「このオブジェクトに適切な型定義をつけて」と頼むことで、interfacetype を即座に生成。


// 画面遷移用の型定義(Enum的な使い方)
type PageName = 'home' | 'sprout' | 'gaonyou';

type HeaderProps = {
  navigateTo: (page: PageName) => void;
  currentPage: PageName;
};

このように型をカチッと決めることで、IDEの補完も効くようになり、開発効率が上がりました。

3. React 19の新機能「Metadata」を先取りする

 
今回の開発で特筆すべき点は、React 19 の新機能を使ったSEO対策です。

これまでのReact(v18以前)では、SPAでページごとに <title><meta description> を書き換えるために、react-helmet などの外部ライブラリを導入するのが定石でした。

しかし、React 19からは 「コンポーネント内に <title> タグなどを直接書けば、Reactが自動で <head> 内に移動(Hoisting)してくれる」 ようになりました。

Seoコンポーネントの実装
外部ライブラリを使わず、標準機能だけで実装したSEOコンポーネントがこちらです。

type SeoProps = {
  title?: string;
  description?: string;
  path?: string;
  ogImage?: string;
};

export const Seo = ({ title, description, path, ogImage }: SeoProps) => {
  const siteUrl = 'https://mysite.com';
  // ... デフォルト値の設定ロジック ...

  return (
    <>
      {/* React 19 ではここに書くだけでOK! */}
      <title>{pageTitle}</title>
      <meta name="description" content={pageDescription} />

      {/* OGP設定 */}
      <meta property="og:title" content={pageTitle} />
      <meta property="og:image" content={pageImage} />
      
      {/* Twitter Card */}
      <meta name="twitter:card" content="summary_large_image" />
    </>
  );
};

 
これを各ページコンポーネントに配置するだけで、動的にタイトルが切り替わります。依存関係(package.json)が1つ減る、非常に嬉しいアップデートです。

4. スマホ実機でのレスポンシブ確認

PCのブラウザについている「スマホ検証モード」は便利ですが、実機での指の操作感や、微妙な色味の違いは分かりません。

特に今回はスマホユーザーがメインターゲットだったため、開発中の実機確認が必須でした。

Viteでは、以下のコマンドを実行するだけで、同一Wi-Fi内のスマホからアクセス可能になります。

npm run dev -- --host

-- の後ろにオプションをつけるのがポイントです。
ターミナルに表示された Network: http://192.168.X.X:5173/ というURLにスマホからアクセスすれば、PCでコードを保存した瞬間にスマホ側もホットリロードされます。

これにより、「PCでは良い感じだったけど、スマホだと余白が狭すぎる」といったTailwindの調整(md:p-8 などの書き分け)がスムーズに進みました。
 

前編のまとめ

ここまでの作業で、ローカル環境にてReact製のモダンなWebサイトが完成しました。
 

Studioからの脱却: デザインの自由度と拡張性を手に入れた。
コスト: ここまでの開発費は0円。
AI活用: デザインカンプなしでも、対話しながら納得のいくUIが作れた。
React 19: react-helmet 不要のシンプルなSEO管理を実現。
 

しかし、まだ公開には至っていません。

後編では、これを Firebase Hosting にデプロイし、Xserverで管理している独自ドメインを接続して、維持費ほぼ0円の運用環境を構築する手順を解説します。

特に、「Studioやレンタルサーバーを解約しつつ、ドメイン権限だけを移管してFirebaseに繋ぐ」というDNS設定周りは、失敗するとサイトが見られなくなる落とし穴があります。そのあたりを重点的にまとめたいと思います。

 

【後編に続く】

9
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?