はじめに
こんにちは!
この度、未経験からwebエンジニアへの転職をするため( Gallery.ai ) を開発しました。
ガク(@Necono_Engineer)と申します。
プログラミングスクールRUNTEQに入学し、学習をしています。
目次
章 | タイトル | 備考 |
---|---|---|
1 | サービス概要 | このサービスについて |
2 | 開発背景 | 開発に至った理由について |
3 | サービスのUI/UX設計 | 3DUIについて |
4 | メイン機能の使い方 | AI画像生成 & 画像投稿 |
5 | 使用技術一覧 | |
6 | インフラ構成 | |
7 | 使用技術の選定理由 | フロントエンドの採用理由 |
8 | 工夫した点 | 世界観など |
9 | 今後の開発について | |
10 | 終わりに |
1. サービス概要
Gallery.aiは、誰もが簡単にオリジナルのAIイラストを作成し、それらを3Dバーチャルギャラリーで展示・公開できるプラットフォームです。
▼ サービスURL
▼ Githubリポジトリ
2. 開発背景
当サービスは、私が参加したスクールの自主的なチーム開発がきっかけで始まりました。
チーム内での私の役割はフロント実装・UIデザインでしたが、適切な画像をどのように選ぶかが大きな課題でした。
この問題の解決策として、画像生成AIの力を借りました。
AIが生成した画像を使用することで、サービスの全体的なクオリティを向上させることができました。
その結果、「ユニークな世界観」「これまでに見たことがないサービス」といった多くの肯定的なフィードバックをいただけました。
この体験から画像生成AIに興味を持ち始め
「もっと手軽に、ドラマチックな体験として画像生成AIを提供できれば、より多くの人にその魅力を伝えられるのでは?🤔」
という思いが生まれ、「Gallery.ai」の構想に至りました。
3. サービスのUI/UX設計
今回、直感的かつユーザーに楽しんでもらえるインターフェイスを実現するために、React Three Fiber
ライブラリを採用し、Three.js
という3D技術を用いてUI/UXデザインを構築することに焦点を当てました。
4. メイン機能の使い方
AI画像生成 | 画像投稿 |
---|---|
画風を選択してAI画像生成ができます。 | 9枚の中から指定した場所に画像を投稿できます。 |
ログイン/ログアウト機能 | リンクXシェア機能 |
NextAuth.jsを使用したGoogleログインが可能です。 | 自分の個展へのリンクをXでシェアできます。 |
5. 使用技術一覧
項目 | 技術 | バージョン |
---|---|---|
フロントエンド | TypeScript / React / Next.js | 5.3.3 / 18.2.0 / 14.0.4 |
バックエンド | Ruby / Ruby on Rails | 3.2.2 / 7.0.8 |
インフラ | vercel / render | |
データベース | PostgreSQL | |
認証 | NextAuth.js | 4.24.5 |
環境構築 | Docker | |
ストレージ | Cloud Storage for Firebase / localForage | 10.8.0 / 1.10.0 |
Web API | DALL-E3 API / Web Speech API | |
UI構築 | Three.js | 0.160.0 |
CSSフレームワーク | Tailwind CSS / Material-UI | 3.3.7 / 5.15.6 |
6. インフラ構成
7. 使用技術の選定理由
開発環境
使用技術 : Docker
- スクールのカリキュラムでDockerでの環境構築に慣れていたこと、環境ごとの差異を最小限に抑えられることからDocker / docker-composeをベースの技術として選びました。
フロントエンド
使用技術: React / TypeScript / Next.js
- 構想の段階から
Three.js
を導入することを考えていたので、豊富なUIライブラリも含めて使用できるReact
の採用は必須でした。
▼ React学習のためにまとめた記事
- また、
TypeScript
の記事を書いたことがきっかけで型定義の手法を知り、実際に手を動かして学ぶ必要があると感じました。
▼ TypeScript学習のためにまとめた記事
- さらに、開発を始めた頃に
App router
が登場し、従来のPages router
との違いを直接確かめてみたいと思いました。(今回はApp routerを採用しています)
- チーム開発でNext.jsを使用した経験があり、開発に着手しやすかったため
- Next.jsの採用によりルーティング設定が容易になる点が魅力的だったため
など、複数の理由からNext.js
を選定しました。
バックエンド
使用技術: Ruby / Ruby on Rails
- プログラミングスクールで学んできた
Ruby on Rails
を採用することで実装フェーズに迅速に移行できると考えました。
インフラ
使用技術 : vercel / render
-
Vercel
に関してはデプロイ時、Next.js
との相性がいいこと、render
に関しては学習教材やドキュメントの豊富さから採用しました。
認証
使用技術 : NextAuth.js
- 新規ユーザーが簡単に登録手続きを済ませ、サービスにアクセスできるようGoogleログインを容易に実装できる
NextAuth.js
を採用しました。
▼ 参考にした記事
画像用ストレージ
使用技術: Cloud Storage for Firebase
-
Cloud Storage for Firebase
はドキュメントを参照し、必要な設定をプロジェクトに組み込むことで即座に実装が可能だったことから採用しました。
8. 工夫した点
1. サービスの世界観
背景に動く惑星
や星々
を取り入れることで、「宇宙」
をテーマにしていることをユーザーに伝わりやすくしました。
テーマカラーをアクアブルー
にすることでさらに「海中」
のイメージも加え、サービスの広大さと深さを演出しています。
2. TOPページの一覧表示
Cloud Storage for Firebaseを活用して、ランダムに選んだ10枚の画像を取得し、TOPページでCardコンポーネントを使用してユーザーの投稿を一覧表示しています。
const fetchImageUrlsFromStorage = async () => {
const imagesRef = firebaseRef(storage, '/');
try {
const imageRefs = await listAll(imagesRef);
const urlPromises = imageRefs.items.map((itemRef) => getDownloadURL(itemRef));
let urls = await Promise.all(urlPromises);
// ランダムに10枚選択するロジックを追加
urls = urls.sort(() => 0.5 - Math.random()).slice(0, 10);
console.log('effectUrl', urls);
return urls;
} catch (error) {
console.error("Error fetching image URLs from Firebase Storage:", error);
return [];
}
};
TOPページ中央に配置した3D要素は、ユーザーがマウススクロールをすることで回転します。
シンプルな楽しさ
をコンセプトにユーザーが触れるだけで楽しめるようなデザインにこだわりました。
3. 認証機能
今回、NextAuth.js
での認証機能を実装しました。
import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';
import { Session } from "next-auth";
import { JWT } from "next-auth/jwt";
import axios from 'axios';
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
const secret = process.env.NEXTAUTH_SECRET;
interface Account {
access_token?: string;
provider?: string;
}
interface User {
id?: string;
name?: string | null;
email?: string | null;
}
interface MySession extends Session {
accessToken?: string;
user_id?: string;
}
interface MyToken extends JWT {
accessToken?: string;
user_id?: string;
}
// NextAuthの設定
const nextAuthOptions = {
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID || '',
clientSecret: process.env.GOOGLE_CLIENT_SECRET || '',
profile(profile) {
return {
id: profile.sub,
name: profile.name,
email: profile.email,
image: profile.picture,
};
},
}),
],
session: {
strategy: "jwt"as const,
maxAge: 60 * 60 * 24, // 1日の秒数
updateAge: 60 * 60, // 1時間ごとにセッションを更新
},
secret: process.env.NEXTAUTH_SECRET || '',
callbacks: {
async jwt({ token, account, user }: { token: MyToken; account: Account | null; user: User }) {
if (account && user) {
token.id = user.id;
if (account.access_token) {
token.accessToken = account.access_token;
}
if (user.id) {
token.user_id = user.id;
}
}
return token;
},
async session({ session, token }: { session: MySession, token: MyToken }) {
if (token.accessToken) {
session.accessToken = token.accessToken;
}
if (token.user_id) {
session.user_id = token.user_id;
}
return session;
},
async signIn({ user, account }: { user: User, account: Account | null }) {
if (!account) return false;
const provider = account?.provider;
const uid = user?.id;
const name = user?.name;
const email = user?.email;
try {
const response = await axios.post(
`${apiUrl}/auth/${provider}/callback`,
{
provider,
uid,
name,
email
}
);
if (response.status === 200) {
return true;
} else {
return false;
}
} catch (error) {
return false;
}
},
},
};
const handler = NextAuth(nextAuthOptions);
export { handler as GET, handler as POST };
NextAuth.js
の導入により、ログインプロセスが大幅に簡素化されました。
これにより、新規ユーザーがサービスにアクセスしやすくなり、アプリを試すハードルが低下したことで、より多くのユーザーがアプリに触れるようになり、サービスの普及に貢献したと考えています。
4. 説明用ネコ型ロボット「ニャビくん」
マウスの方向を向いてくれます。
5. 生成画像の画風指定 & 音声入力機能
AI画像生成機能では、18種類の異なる画風
から選択して、イラストを作成できます。
さらに、Web Speech APIを活用した音声入力機能
も備えており、マイクボタンを押して話しかけるだけで、AI画像生成が可能です。
9. 今後の改善点
1. リファクタリング
機能の実装に重きを置いて実装を進めすぎたので、今後は他の人がより読みやすく、Reactのコンポーネント思想に準拠した設計をしていこうと考えています。
2. フルスタック化
これまではUIやフロントエンドの構築に多くの時間を割いてきましたが、今後はバックエンドの機能充実にも力を入れる予定です。
10. おわりに
このポートフォリオを作成する過程で、多くの人に助けられました。スクールの同期や講師の方々、リリース前にサービスを触ってくれた方々、忙しい中でも話を聞いてくれたり、アドバイスをくれた皆様に、感謝しています。
本当にありがとうございました!!!🙇♂️
まだまだ未熟なところばかりですので、これからも楽しみながら学習を続けていければと思います!
長くなりましたが、お読みいただいた皆様、ありがとうございました!!
Xもやっているので、よければフォローお願いします🐈