はじめに
所属する会社が提供しているサービスでReactを使っており、私はカスタマーサクセスなのでガチエンジニアではないけどもトラブルシューティングや仕様の理解でソースを読むべき機会は多々ありました。ベンチャー企業でもあり、エンジニアのリソースが限られていることから、より業務範囲を広げるため、また、エンジニアとのよりスムーズなコミュニケーションのために言語理解を深める必要がありました。
バックエンドの実装やAIモデルの構築等はそれなりに経験があるものの、フロントエンドはほぼ未経験(とはいえ、宣言的なプログラム言語でいうとFlutterは少しだけ書けるので、全くの素人よりは多少のアドバンテージがあるかもしれません)であっため、必要に迫られて2022年の後半からReactを勉強し始めました。その過程で自分のサービス作れそうだな! 作りたいな! という気持ちになり、サービスを作りながら本ページを記載しています。
バックエンドまで作り込みたかったのですが、平日は本業が忙しく、ほぼ週末の限られた時間で開発しているため、一旦はAWS Amplifyで手抜きしました(といってもAmplify自体もさほどかんたんではないです)。
免責事項
- React・Javascript経験はたかだか数ヶ月程度なので内容に誤りがありましたらドシドシ(でもやさしく)ご指摘ください
- 本ページは所属と一切関係なく、個人の学習のために作成したものとなります
本記事のモチベーション
上述のとおり、フロントエンドはさっぱりな私でも、書籍等で学習しながら、AmplifyというBaaSを用いて比較的短期間でサービスを立ち上げることができました。
まだまだ本格的に皆様に提供できれレベルにないことは100も承知なのですが、誰もがものづくりに参加できる時代に、ぜひ、ものづくりの楽しさをしってもらいたいと思い記載しております。
ただし、少し厳しい事を言うと、本のタイトルにありがちな「誰でも1日で~」みたいなものは、自由度の高いサービスを開発したいであればあまり期待されない方が良いのかなと思っています。Amplifyを使っても、トラブル起こりまくりです。
開発・運用を継続することは楽なことではありません。それでも粘り強く、楽しんで取り組める人はきっとものづくりの本当の楽しさにたどり着けるのではと考えております。このような記事の発信を通じて、これからがんばろうという人々の助けになったり、ものづくりを楽しんでいる人々と繋がれたらうれしぃなあと考えております。
よろしければTwitterやFacebookでもつながってください! 一緒に開発してくださる方、英語学習や読書が好きな方、飲み友になってくれる方も募集中ですw
成果物
いろいろなフィルターによりいろいろなグループで本を紹介しあえるサイト
※まだまだ全然未完成です。これからブラッシュアップしていきますm(_ _)m
開発しながら思ったこと
-
アイデアだけ練っているだけでは何も始まらない、そんなことやってるやつはうじゃうじゃ存在する
- 逆にそこからきちんと形にする、動けるやつは案外少ないのではないか
-
改善は継続的に(グロースが一番大変であり、ここはこのサイトもこれから苦労することになるはず)
-
鉄は熱いうちに打て(モチベーション持続期間の観点から、MVPをある程度仕上げるまでは短期間に実装することをおすすめする)
-
モチベーションを保つために同僚や友人に見せてドヤるとよい(やり過ぎるとたぶんひかれる・・・)
-
ソフトウェアでもハードウェアでも、フロントエンドでもサーバーサイドでも、ものづくりは楽しい
開発の進め方
一人なので大したことはしてません。コード書いてはコミット→GithubにPushの繰り返し。
ある程度作ったら、近所のイケメンお兄さんに見てもらって、いろいろと意見をもらって修正修正また修正。
出会った本たち
- モダンJavaScriptの基本から始める React実践の教科書 (最新ReactHooks対応) | じゃけぇ(岡田 拓巳) | コンピュータ・IT | Kindleストア | Amazon
- 初めてのGraphQL ―Webサービスを作って学ぶ新世代API | Eve Porcello, Alex Banks, 尾崎 沙耶, あんどうやすし |本 | 通販
りあクト! 本は出先で読むには表紙が恥ずかしいんですが、内容は素晴らしいものでした。ただ、初心者には少しむずかしいので、私のような素人さんには、「モダンJavaScriptの基本から~」本と「React実践ハンズオン」本から読み始めることをおすすめします。「CSS設計」本はCSSも素人な私にとっては早すぎました… ただ、CSSはマジで設計を考えないとぐちゃぐちゃになるので、そのあたりの考え方はとても参考になりました。
サービスを作る上で学んだこと一覧
ここからは細かい開発手法なりReactの技術要素について記載します。
プロトタイピング
様々な手法やツールがありますが、私はiPadのGoodNotesを使って画面デザインのラフスケッチ(と呼べるレベルではないただのラクガキ)やDBのテーブル設計等を行いました。仕事としてちゃんとやるならFigmaのようなプロトタイピングツールでも使ったほうが良いんでしょうが、そこの学習コストもそれなりに掛かり、デザインセンスも壊滅的であることを考えると私には暫くはこれで良いように感じました。
本検索で利用したAPI
Google Books APIsを使いました。本の登録数が少ないとのことですが、まぁ一旦は他に良いものがなさそうなので良しとします(後々、自分で本を登録できる機能を作る予定です)。
- Overview | Google Books APIs | Google Developers https://developers.google.com/books/docs/overview
Hook
- まずはuseState
- 初心者でもわかるReact Hook のuseStateを使い方 | アールエフェクト https://reffect.co.jp/react/react-hook-usestate-understand#useStateboolean
- useEffectもよく使う
- 初心者でも簡単にわかるReact useEffect, useLayoutEffectの使い方 | アールエフェクト https://reffect.co.jp/react/react-useeffect-understanding#useEffect-2
React Router
-
SPAでのルーティングは避けてとおれない
- React Router v6 はじめでもわかるルーティングの設定方法の基礎 | アールエフェクト https://reffect.co.jp/react/react-router-6#NavLink
React-hook-form
フォーム作りたかったらとりあえず使っときましょう。自前でゴリゴリ書くより、だいぶシンプルに記載できると思います。
デザイン、レイアウト
- Chakra-UIの基本を押さえる
- この本について|Chakra UIの歩き方 & Tips集 https://zenn.dev/
- [React]Chakra Uiのすすめ - Qiita https://qiita.com/seitamuro/items/c880708cf9b345335022
- Responsive Styles - Chakra UI https://chakra-ui.com/docs/styled-system/features/responsive-styles
- ヘッダーがあるポートフォリオ的なレイアウトを組みたい場合
- Part3 『入門』Reactポートフォリオ作成(リンク設定、各コンポーネントのCSS)|Taku|note https://note.com/takuya814/n/n3da9cb89ea5e
- Chakar-UIでのヘッダデザインの参考に(code-sandbox)
- フォームを作る際の参考に
- remによるサイズ指定
- Amplifyコンポーネントのデザイン変更
- CSSのカスタムプロパティってなんぞ・・・
- CSSの変数(カスタムプロパティ)便利な使い方を詳しく解説 | コリス https://coliss.com/articles/build-websites/operation/css/css-variables.html
- Amplify FrameworkのUIコンポーネントをカスタマイズして、テーマカラーの変更と日本語化をしてみた | DevelopersIO https://dev.classmethod.jp/articles/how-to-customize-amplify-framework-new-ui-component/
- Amplify UI – 新しいAuthenticatorコンポーネントのスタイル設定を試してみた | DevelopersIO https://dev.classmethod.jp/articles/tried-new-amplify-ui-styling-with-angular/
- CSSのカスタムプロパティってなんぞ・・・
- Googleフォント
- Next.js + Chakra UI でGoogle Fontsのフォントを利用する方法 - Qiita https://qiita.com/manak1/items/de1a15446c6154bed699
レンダリング
サービスのコードが大したこと無いうちはさほど問題にならないかもしれませんが、それなりの規模になってくるとpropsでコンポーネントのサブツリーにデータをバケツリレーで渡すことが限界になってcontextやreduxについて調べたり、レンダリング起こしまくってmemo化含む再レンダリング抑止の仕組みを調べたりすることがあると思います。その際には以下のリンクが役に立ちます。
- Reactのレンダリングに関する完全ガイド - Qiita https://qiita.com/hellokenta/items/6b795501a0a8921bb6b5
- 状態管理についてはいきなりreduxとか使うよりまずはcontextを理解することからかなと思います。
- memo化は再レンダリング抑止にパーフェクトな手法ではないけど、した方が良いよね。
- ただし、propsの比較においてはjavascriptのオブジェクトの比較を理解しておく必要あり。
- Contextプロバイダ直下のコンポーネントではmemo化しとけ、と記載があります。
- ただし、memo化によるオブジェクト比較にもそれなりのコストが掛かることは知っておくべき模様です。
状態保持-応用-
とりあえずRedux使っとけば良いんではないでしょうか。
- Step 05 - State Management via Redux | Journal-AWS-Amplify-Tutorial https://richardzcode.github.io/Journal-AWS-Amplify-Tutorial/step-05/
- Reac初心者でも読めば必ずわかるReactのRedux講座 | アールエフェクト https://reffect.co.jp/react/react-redux-for-beginner
- Redux Toolkitをさわってみる | CYOKODOG https://www.cyokodog.net/blog/redux-tookit/
- Redux入門者向け初めてのRedux ToolkitとRedux Thunkの非同期処理 | アールエフェクト https://reffect.co.jp/react/redux-toolkit#Redux_Toolkit
流れ的には、
-
状態の貯蔵庫であるストアを作る。
// app/store.js import { configureStore } from '@reduxjs/toolkit' export const store = configureStore({ reducer: {}, })
-
作ったストアでReact Appを囲む(React Appの中でストアに蓄積した値を使えるようにする)。
import { store } from './app/store' import { Provider } from 'react-redux' ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )
-
更新する対象毎(対象とは、例えば、ログイン情報、カウンターの値、いいね!の値など)にスライスを作る。スライスの中にはReduxで保持したい変数の初期値と、その更新方法をアクションとして定義する。
import { createSlice } from '@reduxjs/toolkit' const initialState = { value: 0, } export const counterSlice = createSlice({ name: 'counter', initialState, reducers: { increment: (state) => { // Redux Toolkit allows us to write "mutating" logic in reducers. It // doesn't actually mutate the state because it uses the Immer library, // which detects changes to a "draft state" and produces a brand new // immutable state based off those changes state.value += 1 }, decrement: (state) => { state.value -= 1 }, incrementByAmount: (state, action) => { state.value += action.payload }, }, }) // Action creators are generated for each case reducer function export const { increment, decrement, incrementByAmount } = counterSlice.actions export default counterSlice.reducer
-
store.jsを作ってスライスreducer関数をインポートし、ストアに追加する。reducerパラメータ内にフィールドを定義することで、このスライスのreducer関数を使用してその状態のすべての更新を処理するようにストアに指示することができる。
-
ここまで出来たら利用の準備は完了。Reduxで扱いたい変数を展開するjs/jsx/tsxファイルのなかでuseSelector を使ってストアからデータを読み、useDispatch を使ってアクションをディスパッチする。
import React from 'react' import { useSelector, useDispatch } from 'react-redux' import { decrement, increment } from './counterSlice' export function Counter() { const count = useSelector((state) => state.counter.value) const dispatch = useDispatch() return ( <div> <div> <button aria-label="Increment value" onClick={() => dispatch(increment())} > Increment </button> <span>{count}</span> <button aria-label="Decrement value" onClick={() => dispatch(decrement())} > Decrement </button> </div> </div> ) }
ちなみに、非同期処理を書きたいときはToolkitに組み込まれているRedux thunkを使う。使い方は以下のとおり。
AWS
Amplify
認証周りはAmplify UI v1だとデザイン等、自由度が低いので、最初からv2を使うことをおすすめします。私のように時間を無駄にすることになります^^;
-
Amplify UI v2 が出たので v1 からアップグレードしてみた – TechHarmony https://blog.usize-tech.com/upgrade-amplify-ui-v2/
-
Amplify UI を使ったログイン画面(Amplify UIその2) Authenticator - Qiita https://qiita.com/ssugimoto/items/d0bc7540493499b6b154#%E6%97%A5%E6%9C%AC%E8%AA%9E%E3%81%AE%E3%81%BF%E3%81%AA%E3%82%89%E3%81%B0%E4%BB%A5%E4%B8%8B%E3%81%AB%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%A7%E4%BA%88%E3%82%81%E7%94%A8%E6%84%8F%E3%81%95%E3%82%8C%E3%81%9F%E8%BE%9E%E6%9B%B8%E3%81%AB%E5%9F%BA%E3%81%A5%E3%81%84%E3%81%9F%E6%97%A5%E6%9C%AC%E8%AA%9E%E8%A1%A8%E7%A4%BA%E3%81%8C%E3%81%A7%E3%81%8D%E3%82%8B
-
ページング
登録された本の情報をX件ずつ取得し、「前へ/次へ」でページングできるようにするための実装です。全レコード数をページ分割したい数で割って実現する方式やAppSyncだとnextTokenを使った方法がある。
- AppSyncとDynamoDBでページネーションを実装する|ryohei_nt|note https://note.com/ryoppei/n/n2c381f434069
- Implementing pagination with AWS AppSync - DEV Community https://dev.to/onlybakam/implementing-pagination-with-aws-appsync-5d08?utm_source=dormosheio&utm_campaign=dormosheio
-
環境変数
諸々、外部に見えてはいけない変数やコードが動作する場所に応じて値を変えるべき変数は環境変数としてセットしましょう。
- 環境変数 - AWS Amplify https://docs.aws.amazon.com/ja_jp/amplify/latest/ug/environment-variables.html
- aws amplifyでReact動かしたい時、環境変数の設定方法 - Qiita https://qiita.com/kumemaru/items/9762bc20e1576c055c47
AppSync&GraphQL&DynamoDB
まずMVPを仕上げたかったため、バックエンドはAWS Amplifyを使って楽しました。Amplifyでは、AppSyncというGraphQLをかんたんに使えるようにしてくれるサービスを用い、私のように初心者でもかんたんにスキーマの定義やクエリの記述が可能です。Amplifyを使う方でGraphQLに知見のない方は、一度、以下に目を通すことをおすすめします。
- Amplify で開発してみる #1 / API (GraphQL) その1 テーブル一つの実装 | ノースディテール https://www.northdetail.co.jp/blog/905/
- 【AWS Amplify ノウハウ】 3. GraphQL Schemaの type に @model を付けなかった時の注意すべきこと | DevelopersIO https://dev.classmethod.jp/articles/amplify-tips-series-3/
- GraphQL API の設計 - AWS AppSync https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/designing-a-graphql-api.html
- リゾルバとか最初は?でしたが、以下あたりをよんでざっくり理解できました。DynamoDBに対するoperationについては、PutItem/GetItem/UpdateItemがあることを理解しておきましょう。そして、クライアントからのリクエストはマッピングテンプレートを介してマッピングドキュメントに変換され、それがDynamoDBへのリクエストになることも理解しておく必要があるでしょう。
- チュートリアル: DynamoDB リゾルバー - AWS AppSync https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/tutorial-dynamodb-resolvers.html
- リゾルバーのマッピングテンプレートの概要 - AWS AppSync https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/resolver-mapping-template-reference-overview.html#aws-appsync-resolver-mapping-template-reference-overview
- AWS再入門ブログリレーAppSync編 | DevelopersIO https://dev.classmethod.jp/articles/relay-re-introduction-2019-appsync/#toc-4
Cognito
まったく知らないわけにもいかないのがCognito。どのような仕組みで動いているかはなんとなくでも知っておくべきでしょう。
ユーザープール、IDプール、トークン周り基本的な概念は頭に入れておいた方がトラブルシューティングにも強くなれると思います。
- Cognitoで学ぶ認証・認可 in AWS - Qiita https://qiita.com/saki-engineering/items/b327f93fe7f027913bd7
この他、IAMやDynamoDBなどは基本的な知識だけでも押させておくと、トラブったときにテンパらなくて良いと思います。知っておかないと死ぬ知識ではないですし、他に良い記事が死ぬほどあるので、ここでは詳細は割愛します。
外部認証プロバイダー
-
Google
-
[こちら](The Complete Guide to Implement Social Login With AWS Amplify https://www.prplbx.com/resources/blog/social-login-with-aws-amplify-guide/)を参考に実装。
- ただし、めっちゃハマった。やり方自体はネットにいろいろ転がっているが、もっともハマったのはリダイレクト先の設定。
- aws-exports.jsのリダイレクトの設定がなんど直接書き換えてもamplify push/pullすると元に戻ってしまう。
- こんなときはcliでの設定が誤っている可能性があるため、/amplify/backend/auth/プロジェクト名/parameters.json のoAuthMetadataに書かれている内容とcli-inputs.jsonに記載の内容をダイレクトに書き換えると成功した。cliからリダイレクト先は追加・編集はできても削除はできない模様。
-
その他
-
TSX,JSXでこれってどういう意味だ? どうやって書くんだ?って内容が書かれていてとても勉強になった
-
Google Books APIご利用時の参考に
- JavaScript で Google Books APIs 使って本を検索して結果一覧を表示するツールを作ってみた https://zenn.dev/phi/articles/javascript-tool-google-books-api
-
npmで適当にモジュールインストールとかしているといずれ死ぬので以下あたり目を通しておくこと
- cdkでよくあると思う npm WARN をとにかく解消してみた | DevelopersIO https://dev.classmethod.jp/articles/fix-npm-warn-on-cdk/
- npmの依存関係について勘違いしていたこと https://zenn.dev/estra/articles/npm-about-dependencsies
-
お問い合わせフォーム作るときにメールサーバーとかめんどくせぇ…ってなったときに
- React-hook-formとEmailJSで楽勝
-
ドメインの取得と設定
-
お名前.comで取得した独自ドメインのサブドメインをAmplify Consoleで割り当てる - Qiita https://qiita.com/w2or3w/items/ddb7c4c1a602470ef96b
※お名前.comは分かりづらいはメールはスパムだわでイラつくので、別のサービスを使ったほうがよいかもしれない・・・
-
実装のテクニック
- code sandboxから実装例をパクる(ただしロジックを理解していないと応用も効かないため、後々弄る可能性があるのならコードを理解すること)
- AmplifyもReactもどんどん新しい/優れた実装がでるため、下手にQiiitaとか参照するよりは公式ドキュメントを読んだほうが早い場合が多い
はまったときは
-
teratail【テラテイル】|ITエンジニア特化型Q&Aサイト https://teratail.com/
-
useEffectの第一引数にasync関数を直に掛けないよ! これめっちゃハマりました。
- React.useEffect でハマったポイントのまとめ https://snamiki1212.com/react-useeffect-pitfall
-
Amplify
-
Amplify Japan User Group
何回か助けていただきました。若干、過疎ってますが… 盛り上がって欲しい。
-
Amplifyはバックエンドのデプロイでハマりやすい印象。ハマった場合、あまりにトラブルシューティングに時間がかかるようであれば1から作り直したほうが早い場合が多い(トラブルシューティングには結局、それなりにAWSの知識が必要になる)
-
GraphQLの401 {errorType: "UnauthorizedException", message: "You are not authorized to make this call.} がでた際は恐らくAPIキーの認証期限切れ
-
AppSyncの画面に行ってAPIキーの期限を延長する必要あり
-
デフォルトでは7日間で期限切れとなるため、amplify update apiで変更する必要あり
参考) Amplify CLIでAppSync APIキーの有効期限を延長する方法 | まむんがITブログ https://mamunga.com/amplify-cli-appsync-api-key
-
-
認証画面あたりをいじる際には「@aws-amplify/ui-react」のバージョン1と2でけっこう差異があるため、2へのバージョンアップには多少の書き換えが必要
-
reactのv17からv18も軽い気持ちでアップデートするとnode_modulesの依存関係で軽く死ねるので注意すること
-
-
画像が表示されない
- Reactでimg src=”image.png”で画像が表示されないときの対処法 | Shin_Code http://shincode.info/2021/08/17/cant-display-image-with-react/
-
[WARN read-shrinkwrap This version of npm is compatible with lockfileVersion@1, but package-lock.json was generated for lockfileVersion@2. I'll try to do my best with it!]みたいなエラーが出る
- 何がI'll try to do my bestだよ、、、と思いつつ、Node.jsとnpmのバージョンについて、ローカルとAmplifyでそれぞれ食い違いがないか確認してみると良いかもしれません。各バージョンでlockfileVersionが異なってくるそうで、お使いのバージョンに対応したlockfileVersionになっていないと本エラーが発生します。
- package-lock.json の lockfileVersion が変更されてしまう場合の対処法 - Qiita https://qiita.com/k_kind/items/b0c4a2c2074934082b58
- node.js - Is there any way to fix package-lock.json lockfileVersion so npm uses a specific format? - Stack Overflow https://stackoverflow.com/questions/64813775/is-there-any-way-to-fix-package-lock-json-lockfileversion-so-npm-uses-a-specific
- 参考: リリース一覧 | Node.js https://nodejs.org/ja/download/releases/
- 何がI'll try to do my bestだよ、、、と思いつつ、Node.jsとnpmのバージョンについて、ローカルとAmplifyでそれぞれ食い違いがないか確認してみると良いかもしれません。各バージョンでlockfileVersionが異なってくるそうで、お使いのバージョンに対応したlockfileVersionになっていないと本エラーが発生します。
その他知っておいた方が良い知識
- パッケージ管理上必須知識
- package.jsonとpackage-lock.jsonの運用方法について – もばらぶエンジニアブログ https://engineering.mobalab.net/2019/08/08/package-json%E3%81%A8package-lock-json%E3%81%AE%E9%81%8B%E7%94%A8%E6%96%B9%E6%B3%95%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6/
- ググり方
- 抽象化して調べることができるようにしておく
- 極論的な例えだが、例えば、自身のファイルパスなどが含まれたエラーメッセージをGoogleの検索ボックスに入れてもろくな回答を得られないことと同様に、細微な情報を含めて検索すると本質的な回答に辿り着かないことが多い(経験論)
- 抽象化して調べることができるようにしておく
- パクり方
- それっぽいワードで日本語を混ぜずに英語だけでググる
- code sandboxのサンプルソースが出てくるので、ある程度、読んで理解したうえでアレンジを加えてパクる
此処から先は少し高度なので一旦MVPができたあとで良い
- [for performance]useCallbackを使って同一オブジェクトを生成し、memoで再レンダリングが起きないようにする
- useCallbackはとにかく使え! 特にカスタムフックでは - uhyo/blog https://blog.uhy.ooo/entry/2021-02-23/usecallback-custom-hooks/
- React.memo/useMemo/useCallbackを使ってサイトを最適化 | RyotArchLog https://ryotarch.com/javascript/react/react-memo-usememo-usecallback-improve-performance/
最後に
サービスを作ってみて思うことは、率直に自分が考えたアイデアを形にするのはとても楽しいことだと思いました。同時に、普段、開発のメンバがいかに大変な思いをして改善を続けてくれているのかもわかり、感謝や尊敬の念も深まりました。
今回は開発の知識を深めたので、開発サイドのメンバとより円滑な意思疎通ができれば良いなと思いますが、CSの役割としては組織の活動を全方位的に円滑にすることも含まれていると思いますので、経営や営業の知識も絶えず吸収していきたいなと思いました。
最後に、本サービスはまだ全然未完成ですし、Reactの知識もまだまだこれからです。AWS等インフラ知識もより高める必要があります。これら全てにより磨きをかけるため、サービスも本記事の内容も、都度、アップデートできればと考えております。
皆様のお時間を本記事に割いていただきどうもありがとうございました。