eスポーツ英会話®︎【公式】|ゲームで学ぶオンライン英会話
eスポーツ英会話は、ボイスチャットで会話しながら生きた英語に触れられる、eスポーツを通して様々な学びが得られるオンライン英会話スクールです。
https://esports-english-service.gecipe.jp/
ゲシピ株式会社でテックリードをしている吉永です。 現在、私は主力サービスであるeスポーツ英会話の開発にフルスタックエンジニアとして携わっています。
この記事は、弊社の掲げるValuesの一つである「早く失敗する」という考え方を前提に執筆しています。
もちろん、単に失敗するだけでは意味がありません。技術選定においてチャレンジした結果、失敗した技術に対して「なぜこの技術で失敗したのか」を深く考察し、その学びを次に活かすことが重要だと考えています。
2022年1月頃にeスポーツ英会話の開発が始まって以来、技術面で様々なチャレンジを行い、それに伴う成功と失敗を積み重ねてきました。
今回は、開発開始から2年間の採用技術を振り返りながら、フロントエンド開発における弊社なりの正解を解説していきます。
本記事では技術選定の失敗にも触れますが、特定のライブラリを否定する意図はありません。単に現在の組織構造に合わなかったという観点から執筆しています。
ゲシピでは、「ゲームの世界で学ぶオンライン英会話スクール」であるeスポーツ英会話を提供しています。
これまでに20万回以上のレッスンを提供し、数百名を超えるコーチが在籍。数千名以上の学生や社会人が日々レッスンを受講しています。厳しい選考(採用率約5%)を通過したバイリンガルコーチが、子供たちに質の高い英会話レッスンを提供しています。
フォートナイトやマインクラフトなどの人気ゲームに対応しており、子供たちが好きなゲーム内の会話を英語で表現することで、話すことへの抵抗感を減らし、自然なコミュニケーションスキルを身につけられるサービスです。
弊社の現在の開発体制は以下のとおりです。
現状の組織の特徴として、業務委託のメンバーが大きな割合を占めています。後述しますが、これらのメンバーの特性を活かし、小規模ながら開発効率を向上させる仕組みの一環として、採用する技術を選定しています。
eスポーツ英会話では、3つの主要なWebアプリケーションを提供しています。レッスン募集のランディングページや「生徒達のリアルな声を届けるメディア「The Stories(ストーリーズ)」など、他にも様々な開発物がありますが、本記事ではこれら3つのWebアプリケーションに焦点を当て、その技術構成を紹介します。
admin=eスポーツ英会話運営のメンバーが使用するツールです。 レッスンの管理やクラスの管理、コーチや生徒の情報を参照することができます。 開発したツールの中で一番最初に着手されました。
コーチツールは、レッスンを提供する先生(コーチ)が使用するツールです。このツールでは、レッスンとクラスの管理、勤怠の記録、レッスン後のレポート作成、さらに支払い情報の確認が可能です。
生徒が受講に使用するツールです。主に、受講中のクラス管理、スケジュール確認、出欠管理が可能です。
開発中のため画像はまだありませんが、これが最新のツールとなります。
以下に、おおまかな技術構成を示します。 続いて、この図を参照しながら、各技術構成の詳細を説明していきます。
フロントエンドでは、全てのプロダクトでTypeScript + Next.jsの構成を採用しています。3つのプロダクト間で主な違いは、スタイリングとUIライブラリの選択にあります。このセクションでは、特徴的なライブラリの選定理由とその採用結果を振り返ります。
モノリポ開発のため、PNPMを採用しています。各プロダクトのディレクトリにpackage.jsonを配置し、共通のスキーマや定数は共通パッケージとしてワークスペースに切り出しています。他のライブラリよりも高速なインストールが開発体験の向上につながると考えています。
弊社では以前、eブカツや(部活動のようにチームを組めるマッチングサービス)eスポーツジム(eスポーツタイトルが主軸のネットカフェ事業)などのサービス開発でVue.jsやNuxt.jsを使用していましたが、eスポーツ英会話事業を機にNext.jsへ移行しました。長年使用したVue.jsからReactへの転換には、以下の理由があります:
長期的なパフォーマンス、TypeScriptとの親和性、優れた開発体験を考慮し、Next.jsの採用を決定しました。
Adminツールでは、デザインリソースを最小限に抑えたいという理由からUIライブラリを採用しました。
MUIで表現しづらい部分は、必要に応じて個別にスタイルを追加しています。フォーム作成時は、React Hook FormやZodと組み合わせて使用しています。
最新のプロダクトである生徒ツールでは、CSS Modulesを採用しています。
これまでの経験から、肥大化したapp.cssや、実際には共通でないcommon.cssなど、様々なCSSの問題に直面してきました。SCSSとBEMによる厳格な命名規則の維持は困難ですが、CSS Modulesはハッシュ付与により命名の衝突を防げます。
さらに、happy-css-modulesを使用することで、クラス名に自動で型が付与されます。コードジャンプにも対応しているため、私のCSSに関する課題感を解消するのに最適だと判断しました。Next.jsがデフォルトでサポートしている点も、happy-css-modulesの導入だけで同様の環境を再現できる魅力的な要素です。
test.module.css
.testStyle {
color: red;
}
.testStyle {
color: red;
}
test.module.css.d.ts
declare const styles:
& Readonly<{ "testStyle": string }>
;
export default styles;
declare const styles:
& Readonly<{ "testStyle": string }>
;
export default styles;
test.tsx
import styles from "test.module.css"
<div
className={clsx(
styles.testStyle,
>test</div>
import styles from "test.module.css"
<div
className={clsx(
styles.testStyle,
>test</div>
生徒ツールでは、UIライブラリとしてReact Ariaを採用しています。React Ariaの特徴は、スタイルが設定されていないUIライブラリであることです。
1からコンポーネントを作成するのは手間がかかり、アクセシビリティの考慮も加えると際限がないと感じています。そのため、UIライブラリの利点を活かしつつ、スタイルをカスタマイズできる柔軟性が欲しいと考え、React Ariaの採用を決めました。
現在の実装では、React Ariaで作成したコンポーネントにCSS Modulesでスタイルを適用しています。この組み合わせにより、機能性とデザインの自由度を両立しています。
ESLintのプラグインとして、以下のようなものを採用しています。
unused-imports(未使用importの削除)
eslint-plugin-import(importの並び替え)
unused-imports(未使用importの削除)
eslint-plugin-import(importの並び替え)
configは全てのプロジェクトで統一したいため、sharedパッケージに配置し、pnpmのワークスペースで管理しています。 このconfigを後から採用したプロダクトでは、まずwarningを出す形で許容し、時間の空いた際に徐々に修正を進めています。 今後は、Biomeの採用も検討したいと考えています。
TSRestは聞き慣れないライブラリかもしれませんが、zodでリクエストのスキーマを管理しつつ、AxiosのクライアントをRPCライクに生成してくれるものです。公式サイトとテスト用の環境を下記に添付しています。
このやや尖ったライブラリを採用するにあたり、最悪の場合は元の構成に戻すか別のものに乗り換える必要性を考慮しました。しかし、以下の点から採用を決定しました:
これらの特徴から、TSRestで作成したリソースが無駄になる可能性は低いと判断しました。万が一不便な点が出てきた場合でも、生成されたOpenAPIスキーマを使って元の構成に戻せるという安心感がありました。ただ、今のところ問題なく運用できています。
Formikなどと比較して特別な採用理由はありませんが、zodとの相性が良く、高いカスタマイズ性を持つRHFを選択しました。複雑なフォームにはRHFを、単純なstate管理にはuseStateを使用しています。
TsRest導入以前はOpenAPI + OpenAPI generatorの構成を使用していました。これはOpenAPIのスキーマから型付きのAxiosクライアントを生成してくれるものです。しかし、スキーマ管理のレビューが十分に行き届かない時期があり、OpenAPIの管理が疎かになり、次第に形骸化していきました。
さらに、OpenAPI generatorで型を生成する際に、日本語との相性が悪いのか不具合が発生することがありました。これらの問題から、zodを使用してリクエストやレスポンスを管理できる代替案を検討したいと考えるようになりました。
コーチツールでは、adminツールとは異なり、より複雑なレイアウトが必要になると予想されたため、Styled Componentsを採用しました。
既存の知見があったため選択しましたが、命名の複雑化や、スタイル定義の場所を確認しないとレンダリングされるタグが分かりにくいなど、適切な技術選定ではなかったと感じるようになりました。また、スタイル定義関数に何でもpropsで渡せる柔軟性は、可読性や保守性を損なう原因にもなりました。
これらの理由から、現在はCSS Modulesへの移行を進めています。
現在はコーチツールとAdminツールで、Page Routerを使用しています。
レイアウト管理やルーティングをApp Routerに任せることで、今後海外展開した際のi18n対応や、SEO的なこれまで考慮しなくても良かった部分を考慮する場合に役立てていきたいと考えています。
AppRouterを採用するにあたって、コンポーネントやHooksを配置するためのディレクトリ構成も見直されました。
元々はコロケーションパターンをディレクトリ構成に採用していました。
Next14になってからこのパターンを採用した事例を目にしましたが、14以前でもpageExtensionsを使用することで、類似のディレクトリ構成を再現することが可能です。
https://nextjs.org/docs/pages/api-reference/next-config-js/pageExtensions
実際に以下のようなディレクトリ構成になっていました。
この構成は、特定のディレクトリ以下に全てのファイルを置き、依存関係を明確にすることができるメリットがあります。
その代わり、共通化するべきパーツを共通化できないという難点もあるため、同じコンポーネントが各種ページディレクトリ直下に置かれてしまう問題も発生しました。それによりAのコンポーネントを更新したが、Bのコンポーネントの追従を忘れてしまうといった問題も起きかねません。
これらの観点から、コロケーションパターンは短期的に考えた場合高い生産力を出すことができるが、アプリケーションが大きくなるにつれて保守が厳しくなるのではないかと感じました。
そのため、現在はfeaturesディレクトリを採用しています。
そのドメインに関連するコンポーネントを入れていくだけで良いので、今のところは命名に悩むこともなくうまく運用できています。
現在の開発チームの強みは、使いたい技術を提案すれば、議論を経て採用できる機会が豊富なことです。実際、TsRestやReact Ariaは、業務委託のメンバーが現状の課題を踏まえて提案してくれた技術です。このような技術的チャレンジへの柔軟性は、スタートアップならではの特徴かもしれませんが、今後も大切にしたい文化だと考えています。
一方で課題もあります。先述のOpenAPIの形骸化は、テストや技術負債の返済にも共通する問題です。StorybookやChromaticは、導入時は意欲的でしたが、私一人での管理には限界を感じ、現在は一時的に使用を中止しています。
これらのツールを業務委託メンバーも含めて管理できる体制の構築が必要です。hygenなどのテンプレートエンジンの活用で部分的に解決できるかもしれませんが、テストの重要性をチーム全体で共有し、その必要性を理解することは避けられません。
また、限られた人的リソースの中で、これらの開発にいかに時間を割くかも、今後の重要な課題です。
最後までお読みいただき、ありがとうございました。これらの技術について一緒に考え、事業を創造していただける方を現在募集しています。興味を持っていただけましたら、ぜひご応募ください!