11
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

概要

2024年6月21日、Anthoropic社から「Claude 3.5 Sonnet」がリリースされました。
その際に、「Artifacts」という機能が登場し、生成されたコードや図を専用のウィンドウに表示することが可能になりました。
Introducing Claude 3.5 Sonnet \ Anthropic

本記事は、Claude 3.5 Sonnet Artifactsを利用した単語帳アプリ作成の記録です。

環境

  • マシン:Mac mini (チップ:Apple M2 Pro)
  • エディタ:Cursor (バージョン:0.34.4)

言語・パッケージ等

  • Node.js: 22.2.0
  • React: 18.3.1
  • Typescript: 5.5.2
  • Vite: 5.3.2
  • json-server: 1.0.0-beta.1
  • Tailwind CSS: 3.4.4

アプリの構成

  • バックエンド:json-server
  • フロントエンド:React + TypeScript + Vite + shadcn/ui

アプリ作成手順

Claude 3.5 Sonnet ArtifactsにReactコードを生成させる

まずはClaudeにログインし、Artifactsモードをオンにします。
画面左下、自分自身のアカウント名の右にある「▽」 を押下し、「Feature Preview」を選択します。
Claude_setting.png

出現したモーダルの「Artifacts」のトグルボタンを「On」にすると、Artifactsモードを利用できるようになります。
Claude_Artifacts.png

さて、適当にプロンプトを入力すると、画面右半分のウィンドウにコードを出力していきます。
Claude_Code.png

コードの生成が終了すると、画面イメージを表示してくれます。
Claude_screen_image.png

この画面イメージは操作可能で、例えばテキストエリアに文字を入力して「追加」ボタンを押下すると...
Claude_input.png

単語が追加されました。
Claude_input_result.png

このように、生成された画面イメージを操作しながら確認し、適宜追加で指示をしていきます。

注意点
Claudeの実行環境にインストールされていないパッケージを使用しようとすると、画面イメージが表示されません。
Claud_error.png

そのため、今回は以下の方針でコードを生成させました。

  • Claudeにはアプリの画面を1枚ずつ生成させ、ルーティングは記述させないようにする
  • ルーティングはreact-router-domを用いて、Claudeが生成したコードに対し、手作業でコードを追記する

Viteを用いてアプリを構築する

Viteを使用する理由

以前、Claude 3 Opusを利用してアプリ作成をした際にNext.jsを用いたところ、Server-side Rendering/Client-side Renderingを考慮しないコードが量産されて苦労しました。
その際にClaudeは比較的新しいフレームワークのコード生成が苦手そうに感じたので、今回はオーソドックスなReactコードを生成させることにしました。

そこで、Claude 3.5 Sonnetが出力したコードに柔軟に対応できるフレームワークとしてViteを採用しました。

shadcn/uiの導入

ClaudeはUIコンポーネントにshadcn/uiを用いてきたので、公式サイトを参考に、Viteにshadcn/uiを追加します。
Vite - shadcn/ui

ただし、公式サイトに書かれていることをそのまま実行すると、以下のようなディレクトリ構造になったので、

.
├── @
│   ├── components
│   │   └── ui
│   └── lib
│       └── utils.ts
(以下省略)

componentsディレクトリとlibディレクトリは手作業でsrcディレクトリ以下に移動しました。

.
├── src
│   ├── components
│   │   └── ui
│   │       ├── alert-dialog.tsx
│   │       ├── alert.tsx
│   │       ├── badge.tsx
│   │       ├── button.tsx
│   │       ├── card.tsx
│   │       ├── input.tsx
│   │       └── textarea.tsx
│   ├── lib
│   │   └── utils.ts
(以下省略)

npx shadcn-ui@latest addコマンドでコンポーネントを追加すると、@ディレクトリ以下のcomponents/uiディレクトリ以下にtsxファイルが追加されますが、都度src配下のcomponents/uiディレクトリに移動しました。

ページ作成およびルーティング設定

srcディレクトリ配下にpagesフォルダを作成し、Claudeに生成させたコードを1枚ずつ格納します。

.
├── src
│   ├── components
│   ├── lib
│   ├── assets
│   ├── pages
│   │   ├── FlashcardPage.tsx
│   │   ├── SubjectListPage.tsx
│   │   ├── TagListPage.tsx
│   │   └── TestPage.tsx
│   ├── App.css
│   ├── App.tsx
│   ├── index.css
│   ├── main.tsx
│   ├── types.ts
│   └── vite-env.d.ts
(以下省略)

次に、コンポーネント同士を連携させ、ルーティングを設定していきます。

main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import { BrowserRouter } from "react-router-dom";


ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>,
)

App.tsx
import SubjectListPage from './pages/SubjectListPage'
import FlashcardPage from './pages/FlashcardPage';
import TestPage from './pages/TestPage';
import TagListPage from './pages/TagListPage';
import './App.css'
import { Routes, Route } from "react-router-dom";

function App() {

  return (
    <div>
      <Routes>
        <Route path="/" element={<SubjectListPage/>}/>
        <Route path="/flashcard">
          <Route path=":subjectId" element={<FlashcardPage/>}/>
        </Route>
        <Route path="/test">
          <Route path=":subjectId" element={<TestPage/>}/>
        </Route>
        <Route path="/tag" element={<TagListPage/>}/>
      </Routes>
    </div>
  )
}

export default App

Claudeの生成したコードはボタンの押下で画面遷移をするようになっていたので、react-router-domのuseNavigateを利用します。

SubjectListPage.tsx
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import axios from "axios";

// 省略

const SubjectListPage = () => {
  const navigate = useNavigate();

  // 省略
  
  const goToTagList = () => {
    navigate('/tag');
  };
  
  return (
    <div className="container mx-auto p-4">
        <div className="flex justify-between items-center mb-4">
        <h1 className="text-2xl font-bold mb-4">単語帳アプリ</h1>
            <Button onClick={goToTagList} variant="outline">
                <Tag className="mr-2 h-4 w-4" />
                タグ一覧
            </Button>
        </div>

        {/* 省略 */}
      
    </div>
  );
};

export default SubjectListPage;

json-server設定

Claudeが最初に生成したコードはテストデータがハードコーディングされていたので、以下の記事を参考にjson-serverを構築します。
json-serverの使い方まとめ【解説】

Claudeにお願いすると、json-server用のJSONファイルを書いてくれるので、そのままdb.jsonにコピペします。
Claude_json_server.png

更に、Claudeにお願いすると、すでに生成してくれたコードを修正して、json-serverとの繋ぎ込みを記述してくれます。
Claude_code_update.png
コードをコピペして、ルーティング設定を書き加えました。

参考:アプリケーション画面

トップページ
Flashcard_app_top.png

単語帳ページ
Flashcard_app_flashcards.png

テストページ
Flashcard_app_test.png

タグ一覧ページ
Flashcard_app_tags.png

感想

体感2、3時間程度でアプリを作成できました。
画面デザインのクオリティも満足しています。
作成の過程でエラーに悩まされるイライラが激減し、アプリ作成の楽しい部分だけを煮詰めたような製作になりました。
今後のアプリの個人製作はClaude 3.5 Sonnet Artifactsが必須になりそうです。

11
11
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
11
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?