20220228のdockerに関する記事は6件です。

Postgresのデータベース作成時のエラー対処

概要 下記のコマンドを実行すると、エラーが出てしまう。 $ docker-compose exec workspace psql -U default -h postgres psql: error: could not translate host name "postgres" to address: Name or service not known docker-compose upでコンテナを作成している。 しかし、docker-compose psするとpostgresの部分の「State」が「Exit 1」となっている。 $ docker-compose up -d workspace php-fpm nginx postgres Creating network "laravel-app_default" with the default driver Creating laravel-app_workspace_1 ... done Creating laravel-app_php-fpm_1 ... done Creating laravel-app_nginx_1 ... done Creating laravel-app_postgres_1 ... done $ docker-compose ps Name Command State Ports ------------------------------------------------------------------------------------------------------------ laravel-app_nginx_1 /docker-entrypoint.sh ngin ... Up 0.0.0.0:80->80/tcp,:::80->80/tcp laravel-app_php-fpm_1 docker-php-entrypoint php-fpm Up 0.0.0.0:9001->9000/tcp,:::9001->9000/tcp laravel-app_postgres_1 docker-entrypoint.sh postgres Exit 1 laravel-app_workspace_1 docker-php-entrypoint php-fpm Up 9000/tcp 原因 下記のログで「pg_tblspc」、「pg_replslot」がないので開けないと言われておりますが、正直わかってないです。引き続き調査しておりますが、分かり次第更新したいと思っております。。 解決法(※暫定処置) docker-compose logsでエラーの内容を確認する。 $ docker-compose logs postgres_1 | postgres_1 | PostgreSQL Database directory appears to contain a database; Skipping initialization postgres_1 | postgres_1 | 2022-02-27 05:31:40.430 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432 postgres_1 | 2022-02-27 05:31:40.430 UTC [1] LOG: listening on IPv6 address "::", port 5432 postgres_1 | 2022-02-27 05:31:40.433 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432" postgres_1 | 2022-02-27 05:31:40.460 UTC [1] LOG: could not open directory "pg_tblspc": No such file or directory postgres_1 | 2022-02-27 05:31:40.468 UTC [23] LOG: database system shutdown was interrupted; last known up at 2022-02-23 11:11:08 UTC postgres_1 | 2022-02-27 05:31:42.871 UTC [23] LOG: could not open directory "pg_tblspc": No such file or directory postgres_1 | 2022-02-27 05:31:42.881 UTC [23] LOG: could not open directory "pg_tblspc": No such file or directory postgres_1 | 2022-02-27 05:31:42.882 UTC [23] FATAL: could not open directory "pg_replslot": No such file or directory postgres_1 | 2022-02-27 05:31:42.883 UTC [1] LOG: startup process (PID 23) exited with exit code 1 postgres_1 | 2022-02-27 05:31:42.883 UTC [1] LOG: aborting startup due to startup process failure postgres_1 | 2022-02-27 05:31:42.888 UTC [1] LOG: database system is shut down プロジェクト配下のlaravel-app/dataディレクトリ を削除する。 docker-compose down --rmi all --volumes --remove-orphansコマンドでコンテナを一掃する。 再度docker-compose up -d workspace php-fpm nginx postgresを実行でコンテナを再作成する。 docker-compose psで再度「State」を確認する。 $ docker-compose ps Name Command State Ports ----------------------------------------------------------------------------------------------------------- laravel-app_nginx_1 /docker-entrypoint.sh ngin ... Up 0.0.0.0:80->80/tcp,:::80->80/tcp laravel-app_php-fpm_1 docker-php-entrypoint php-fpm Up 0.0.0.0:9001->9000/tcp,:::9001->9000/tcp laravel-app_postgres_1 docker-entrypoint.sh postgres Up 0.0.0.0:5432->5432/tcp,:::5432->5432/tcp laravel-app_workspace_1 docker-php-entrypoint php-fpm Up 9000/tcp docker-compose exec workspace psql -U default -h postgresを再度実行で作成できた。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo for mysql failed: Name or service not known の対処法

エラー内容 ./vender/bin/sail artisan migrate を実行すると、なぜか SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo for mysql failed: Name or service not known こんなエラーが出ちゃいました。 前提として、 このコマンドの前に、色々キャッシュクリアなどやっていたら バグって、ページが表示されなくなるエラーを起こしていました。 そこで、新たなlaravelを立ち上げてmigrateしようと思っていた時に このエラーが起きました。 原因 前状況を考えると、キャッシュクリアなどで データベースが接続されていないのかな?と思い ./vender/bin/sail mysql match-app1 とDBを確認しようとしました。 すると service "mysql" is not running container と、違うエラーが発生したので。これは データベースが接続できていないのかも。 と思い、色々dockerとDBを繋ぐ方法を実行してみたものの 上手くいかず。。。 結局これで解決した あまりにも解決できなかったので、病みそうになっていたところ 適当に docker-compose up -d と打ち込んでみました。 それまでは ./vender/bin/sail up -d でやっていたのですが。 そして、dockerを立ち上げてから ./vender/bin/sail artisan migrate を実行してみたら、なんと上手くいきました!! Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (61.42ms) Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table (43.90ms) Migrating: 2019_08_19_000000_create_failed_jobs_table Migrated: 2019_08_19_000000_create_failed_jobs_table (45.03ms) Migrating: 2019_12_14_000001_create_personal_access_tokens_table Migrated: 2019_12_14_000001_create_personal_access_tokens_table (70.68ms) Migrating: 2022_02_28_155100_create_user_lists_table Migrated: 2022_02_28_155100_create_user_lists_table (24.82ms) 久しぶりに、この色取り取りコードが見れてよかったです。 踏み込んで考えてみる さて、このままよかったと終わってもいいのですが なぜ、docker-compose up -dだと上手くいって ./vender/bin/sailだと、エラーが起きてしまったのかについて考えてみます。 ここを理解するには、そもそも composeとvenderの違いについて知る必要がありそうです。 sailスクリプトは、docker-compose.ymlファイルで定義されたDockerコンテナを操作するための便利なメソッドをCLIで提供します。 公式によると、sailコマンドはymlファイル操作用ぽいですね。 すみません。ちょっと時間かかりそうなので また、追々理解していくとします! とりあえず、今は終わらせることに集中していきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(今更だけど)Dockerの基本概念について理解してみた

はじめに アプリケーションの開発を行うエンジニアであっても、インフラに関する事もある程度は知っておく必要がありそう…。 というわけで今だと普通になったコンテナについて理解を深めておこうと思い、Dockerの基本概念について理解してみたので、その備忘録を残す。 前提条件として以下のように、Linux環境(この記事のLinux環境はCentos7.9)にdockerがインストールできている事が必要。 [root@control-plane docker-kubernetes]# docker version Client: Docker Engine - Community Version: 20.10.7 API version: 1.41 ... Server: Docker Engine - Community Engine: Version: 20.10.7 API version: 1.41 (minimum version 1.12) ... ※dockerのLinux環境(Centos7.9)へのインストール方法はdocker のインストールを参照。 ※以下では基本的に英語のリファレンスを参照しているが、https://matsuand.github.io/docs.docker.jp.onthefly/ だと日本語なので読みやすいかもしれない。 まずはともあれDockerでhello world!をやってみる やる事は単純で以下のコマンドを実行するだけ。コマンドを実行すると、hello-worldというDockerイメージをpullされ、Hello from Docker!というメッセージが表示される。 [root@control-plane docker-kubernetes]# docker run hello-world Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world 2db29710123e: Pull complete Digest: sha256:97a379f4f88575512824f3b352bc03cd75e239179eea0fecc38e597b2209f49a Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/ よくあるhello world!はこれでおしまい。 ※上記のままだとstoppedのコンテナが残り続けるので、以下のようにdocker container pruneを実行して停止しているコンテナをすべて削除しておくのがおススメ。 [root@control-plane docker-kubernetes]# docker ps -al CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 47a398fa7687 hello-world "/hello" 9 minutes ago Exited (0) 9 minutes ago thirsty_maxwell [root@control-plane docker-kubernetes]# docker container prune WARNING! This will remove all stopped containers. Are you sure you want to continue? [y/N] y Deleted Containers: 47a398fa7687a745813641fa241c02a52a91da8f3ba4ed3f42f6d1fad6c58021 ... Total reclaimed space: 0B 参考:docker ps 参考:docker container prune Dockerとは? Dockerとは、と言われたらコンテナ型の仮想環境という事になる(らしい)。virtualboxやVMwareと同様に仮想環境である事は同じだが、コンテナ型というのが大きく異なっている(コンテナ型って?についてはを参照)。 なんでDockerが登場したのか?という背景としては、今までのシステム開発・運用での課題が分かると分かりやすい気がしたのでその観点でまとめてみると以下のようになるだろう。 # 今までは… Dockerになると… 1 この構築手順、昔はうまくいって動いていたのに今は動かない(再現性が担保できていなかった…) 実行環境の冪等性が確保できる(再現性を担保できるように!) 2 自分の手元の環境ではうまく動くのに別の人の環境では動かない(環境のコピーが簡単にできていなかった…) 環境の移転・コピーが簡単にできる(同じ環境を簡単にコピーして展開可能に!) 3 環境(インフラ)を作り直すたびに手順が変わってよく分からなくなる(手順の管理が面倒だった…) 環境(インフラ)の構成をコード化できる(手順がコード化されるのでversion管理システムで簡単に管理!) ※上記の表の1・2についてはImmutable Infrastracture(普遍的なインフラ)に、3についてはInfrastracture as Code(IaC)(インフラのコード化)に、それぞれ関連している。 Dockerの仕組み 以下の図のように、Dockerはホストマシン(Windows・Mac・CentOSなど)にインストールして利用する。 最もコアな部分はデーモンと記載した部分で、ここが各種制御を行うコントローラーとして動く。 上記の図に登場するものについては以下の通り。 イメージ実行環境(アプリケーションなど何かしらを実行する仮想環境)のテンプレート。Dockerは仮想環境であると言ったが、その仮想環境の構成のテンプレートがイメージと呼ばれるもの。 例えば、Node.jsが実行できるようにするにはCentOS7.9にNode/npm/yarnをインストールするのような手順が必要だが、このような手順を毎回実行するのは面倒だしあらかじめそういう環境を用意しておいて欲しいとなった時には、Node.jsが実行できるようになった状態の環境のテンプレートとして、Docker Hubのcimg/nodeを利用する事でそれが可能になる(Docker Hubは以下に出てくるレジストリの1つ)。 コンテナ実行環境(仮想環境)の実態(イメージに基づいて実際に構築された仮想環境そのもの)。 レジストリイメージを保存しておく場所(クラウド上)。 例としてはDocker HubやAWSのECR(Elastic Container Registry)などがある。 コンテナと仮想マシンの違い 一言で言うと、カーネル(OSのコアの部分)を共有しているかどうか。 分かりにくいと思うので図示してみると、以下のようにそれぞれ仕組みが異なっている。 上記の図の中で、仮想マシンの場合は、 ホストOS・ゲストOSでそれぞれでカーネルを持つ(ゲストOSのためのカーネルは別にインストールされる) →起動に時間がかかる(ただし逆に、完全にOS(カーネル)が違う環境を構築する事ができる) という特徴があるのに対し、コンテナの場合は、 コンテナ上(コンテナ型仮想環境)では、そのカーネルをホストOSと共有して動作する →起動が速い という特徴がある。このカネールの共有・非共有が根本的な違い。 参考:NEC がおすすめするコンテナサービスとは DockerイメージとDockerコンテナの違いについて 混乱しがちなDockerイメージとDockerコンテナについて、定義について少し追加で整理しておく。 1 2 Dockerイメージ 実行環境(アプリケーションなどを何かしらを実行する仮想環境)を定義したものDocker Hubなどのレジストリと呼ばれる所に保管されている Dockerコンテナ Dockerイメージを実際に実行してできる実行環境(アプリケーションなどを何かしらを実行する仮想環境)そのもの 図示してみると以下の図のようなイメージになる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerの基本概念について理解する

はじめに アプリケーションの開発を行うエンジニアであっても、インフラに関する事もある程度は知っておく必要がありそう…。 というわけで今だと普通になったコンテナについて理解を深めておこうと思い、Dockerの基本概念について理解してみたので、その備忘録を残す。 前提条件として以下のように、Linux環境(この記事のLinux環境はCentos7.9)にdockerがインストールできている事が必要。 [root@control-plane docker-kubernetes]# docker version Client: Docker Engine - Community Version: 20.10.7 API version: 1.41 ... Server: Docker Engine - Community Engine: Version: 20.10.7 API version: 1.41 (minimum version 1.12) ... ※dockerのLinux環境(Centos7.9)へのインストール方法はdocker のインストールを参照。 ※以下では基本的に英語のリファレンスを参照しているが、https://matsuand.github.io/docs.docker.jp.onthefly/ だと日本語なので読みやすいかもしれない。 まずはともあれDockerでhello world!をやってみる やる事は単純で以下のコマンドを実行するだけ。コマンドを実行すると、hello-worldというDockerイメージをpullされ、Hello from Docker!というメッセージが表示される。 [root@control-plane docker-kubernetes]# docker run hello-world Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world 2db29710123e: Pull complete Digest: sha256:97a379f4f88575512824f3b352bc03cd75e239179eea0fecc38e597b2209f49a Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/ よくあるhello world!はこれでおしまい。 ※上記のままだとstoppedのコンテナが残り続けるので、以下のようにdocker container pruneを実行して停止しているコンテナをすべて削除しておくのがおススメ。 [root@control-plane docker-kubernetes]# docker ps -al CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 47a398fa7687 hello-world "/hello" 9 minutes ago Exited (0) 9 minutes ago thirsty_maxwell [root@control-plane docker-kubernetes]# docker container prune WARNING! This will remove all stopped containers. Are you sure you want to continue? [y/N] y Deleted Containers: 47a398fa7687a745813641fa241c02a52a91da8f3ba4ed3f42f6d1fad6c58021 ... Total reclaimed space: 0B 参考:docker ps 参考:docker container prune Dockerとは? Dockerとは、と言われたらコンテナ型の仮想環境という事になる(らしい)。virtualboxやVMwareと同様に仮想環境である事は同じだが、コンテナ型というのが大きく異なっている(コンテナ型って?についてはを参照)。 なんでDockerが登場したのか?という背景としては、今までのシステム開発・運用での課題が分かると分かりやすい気がしたのでその観点でまとめてみると以下のようになるだろう。 # 今までは… Dockerになると… 1 この構築手順、昔はうまくいって動いていたのに今は動かない(再現性が担保できていなかった…) 実行環境の冪等性が確保できる(再現性を担保できるように!) 2 自分の手元の環境ではうまく動くのに別の人の環境では動かない(環境のコピーが簡単にできていなかった…) 環境の移転・コピーが簡単にできる(同じ環境を簡単にコピーして展開可能に!) 3 環境(インフラ)を作り直すたびに手順が変わってよく分からなくなる(手順の管理が面倒だった…) 環境(インフラ)の構成をコード化できる(手順がコード化されるのでversion管理システムで簡単に管理!) ※上記の表の1・2についてはImmutable Infrastracture(普遍的なインフラ)に、3についてはInfrastracture as Code(IaC)(インフラのコード化)に、それぞれ関連している。 Dockerの仕組み 以下の図のように、Dockerはホストマシン(Windows・Mac・CentOSなど)にインストールして利用する。 最もコアな部分はデーモンと記載した部分で、ここが各種制御を行うコントローラーとして動く。 上記の図に登場するものについては以下の通り。 イメージ実行環境(アプリケーションなど何かしらを実行する仮想環境)のテンプレート。Dockerは仮想環境であると言ったが、その仮想環境の構成のテンプレートがイメージと呼ばれるもの。 例えば、Node.jsが実行できるようにするにはCentOS7.9にNode/npm/yarnをインストールするのような手順が必要だが、このような手順を毎回実行するのは面倒だしあらかじめそういう環境を用意しておいて欲しいとなった時には、Node.jsが実行できるようになった状態の環境のテンプレートとして、Docker Hubのcimg/nodeを利用する事でそれが可能になる(Docker Hubは以下に出てくるレジストリの1つ)。 コンテナ実行環境(仮想環境)の実態(イメージに基づいて実際に構築された仮想環境そのもの)。 レジストリイメージを保存しておく場所(クラウド上)。 例としてはDocker HubやAWSのECR(Elastic Container Registry)などがある。 コンテナと仮想マシンの違い 一言で言うと、カーネル(OSのコアの部分)を共有しているかどうか。 分かりにくいと思うので図示してみると、以下のようにそれぞれ仕組みが異なっている。 上記の図の中で、仮想マシンの場合は、 ホストOS・ゲストOSでそれぞれでカーネルを持つ(ゲストOSのためのカーネルは別にインストールされる) →起動に時間がかかる(ただし逆に、完全にOS(カーネル)が違う環境を構築する事ができる) という特徴があるのに対し、コンテナの場合は、 コンテナ上(コンテナ型仮想環境)では、そのカーネルをホストOSと共有して動作する →起動が速い という特徴がある。このカネールの共有・非共有が根本的な違い。 参考:NEC がおすすめするコンテナサービスとは DockerイメージとDockerコンテナの違いについて 混乱しがちなDockerイメージとDockerコンテナについて、定義について少し追加で整理しておく。 1 2 Dockerイメージ 実行環境(アプリケーションなどを何かしらを実行する仮想環境)を定義したものDocker Hubなどのレジストリと呼ばれる所に保管されている Dockerコンテナ Dockerイメージを実際に実行してできる実行環境(アプリケーションなどを何かしらを実行する仮想環境)そのもの 図示してみると以下の図のようなイメージになる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

学生エンジニアのためのチャットコミュニティプラットフォーム「はかくら」をNext.js + TypeScript + AtomicDesign + Firebase9 + Docker + Material UI + Storybookで作った

はじめに ※今回の開発は、株式会社OwN様からいただいた技術課題の一環で行ったものになります。 リポジトリURL こちらになります。 https://github.com/FAL-coffee/hacker-class-room 自己紹介 兼業で個人サービスの開発・運営・保守を行っております。 フロントエンジニアのふぁると申します。 統合テストのクラウド管理・実行プラットフォーム「Itamaster」を運営しております。 よろしくお願いいたします。 【Twitterリンク】 https://twitter.com/@itamaster_ 【Itamaster公開記事リンク】 https://qiita.com/Itamaster/items/f821be4c33caab640a93 【Itamaster】 https://itamaster.work テスト管理プラットフォーム Itamaster 最近、フロントエンドのみですがOSSとして公開を行いました。 知見の浅い当時書いたコードなので、コードやディレクトリは綺麗ではないですが、i18nの実装、stripe連携やvuetifyの導入等には役立てていただける点があると嬉しく思います! https://github.com/FAL-coffee/itamaster-front サービスの概要 「プログラミングの授業が始まるが、ついていけるのか不安......」 「情報処理の勉強が難しい。もしかして、こんなに苦労しているのは私だけ?」 「ゲーム作りに興味があって、プログラミングを始めてみたいけど、何から始めればいいのかわからない」 「ネットで見た通りにやったのに、環境構築でつまずいてしまった。」 「プログラミングの知見を活かして、学生起業を行いたい。仲間をどうやって見つければいいだろうか?」 そのような若い学生エンジニアにありがちな悩みを解決するために、私ははかくらを開発しました。 はかくら(Hacker Class Room)は、学生エンジニアのために開発したコミュニティサービスです。 使用技術 Next.js ( TypeScript ) --人気のOSSからディレクトリ構造を参考にし実装。src/を採用 Firebase v9 --サーバーサイドとして利用 Firestore --NoSQLのDBとして利用 Firebase Authentication --Google Loginに利用 Docker --開発用のコンテナとして利用 Material UI --主にatomsコンポーネントとして利用 Storybook --コンポーネントのカタログとして利用 Jest --Component単位のテストを実装(jest, enzyme) Cypress --E2Eテストに利用 GitHubActions--継続的インテグレーション(CI)による、UT自動化 vercel --ホスティングに利用 採用アーキテクチャ GitHub-Flow --導入・運用の簡単さを理由に起用。単純なコミットを繰り返す開発フローにも最適だった Atomic Design --コンポーネントのディレクトリデザイン手法。単純にAtomicDesignが好き issueドリブン開発 --残り作業タスクをブランチの一単位として利用できる 開発のきっかけ きっかけとしては、上述させていただいた株式会社OwN様のツイートに私が反応しました。 お話を続ける中で、技術課題として以下のような要件でシステム開発を行ってほしい、と言っていただき、一か月の期間内で開発を始めました。 要件 12 ~ 22歳のエンジニアを対象としたチャットサービスであること それらが抱える潜在的な課題を定義し、解決することを目的とした機能を実装する Googleログインを使ってユーザー認証・登録を行うこと 技術要件 TypeScriptを開発に用いていること DBに情報を保存できること セキュリティに配慮した設計・実装であること 任意要件 CI/CDパイプラインが設定されていること チーム開発を意識したアーキテクチャであること OAuth/OIDCのクライアントを自前で実装すること アプリケーションをコンテナ化すること 依存の少ないアーキテクチャであること ニーズ調査 要件を満たすために最初に行ったのは、「12 ~ 22歳のエンジニアがどのような課題を抱えているのか」の調査です。 私は12~22歳という年齢を指定されていることから、対象をその中でも更に「学生」と定義しました。 私の出身高校には情報科が設置されております。 現職で該当科の担任を受け持っておられる先生との縁が現在も続いているので、先生や生徒数名を対象にインタビューをお願いし、現在の状況やプログラミング・情報の授業中に難しく思った点を主に教えていただきました。 その結果をもとに、学生エンジニアの成長を取り巻く環境として、以下のような状況を発見しました。 クラスという区切りでは人数が少なく、向上心のある学生が高め合えるような仲間と巡り合えないことが多々ある 一部の生徒を除き、ほとんどの学生エンジニアは課題に直面した際は周囲の友人や、教職員に対し質問や相談を行う事で情報を得ている 同様に、明確な目的や目標を持っていない学生エンジニアが多く見られる それらは、以下のようなリスクを持ち合わせていると考えました。 周囲の人間が回答が出来なかった際、本人が課題解決へのモチベーションを低下させてしまう場合がある 周囲の友人や仲間がだらけている場合、雰囲気に流される者が一定数発生してしまう場合がある。それは、自身の成長が順調でない時に顕著である 明確な目的、目標を持たず、学習に対して意味を見出していない場合、モチベーションの低下に繋がる場合がある それらを私は潜在的な課題であると定義し、リスクを解決するために必要な要素として、周囲の人間関係や、慢性的に目にする情報の質の向上を図る必要があると考えました。 具体的には、以下のような環境が不足していると考えました。 質問を行った場合でも、誰かが必ず答えたり、得意分野を教え合うことの出来る環境 自分がだらけてしまうときに、同世代の向上心のある学生エンジニアとコミュニケーションを取ることで、目標設定や意識の改善を図ることの出来る環境 高め合う事の出来る仲間と繋がることの出来る環境 それらを潜在課題と定義し、要件定義フェーズを完了しました。 各技術, アーキテクチャの選定理由について Docker コンテナ技術としてDockerを採用した理由としては、連携可能なソフトウェアが多いこと、デファクトスタンダードであり、情報量や導入事例の観点から見て大きなメリットが見込めることです。 他候補としては、Containerd, lxc, lxd等がありました。 Firebase、Firestore サーバーサイド技術としてFirebaseを採用した理由としては、以下です。 私の技術スタッツ上の問題から、サーバーサイドの実装を行う場合、納期を超過してしまうリスクがあった。 他Baasと比較し、Firebaseが今回には最適であると判断したため。 かみ砕いて記述させていただきます。 私の技術スタッツ上の問題から、サーバーサイドの実装を行う場合、納期を超過してしまうリスクがあった。 今回の開発は、GoogleLoginを可能としたチャットサービスです。 現職ではCRUD処理を主とする業務システムの開発が主であるほか、バックエンドはフロントエンドと比較し得意ではないため、私のスキル不足の原因からバックエンドを自前実装することは工数上リスクであると考えました。 バックエンドを実装する場合、Express(node.js), Laravel(PHP)を候補としていました。 またその場合は、socket.io等の外部サービスを利用することでチャット機能を比較的容易に実装可能であるようです。 (socket.ioとは、リアルタイムWeb技術の一つであるwebsocket通信を利用したjsライブラリで、リアルタイムの双方向通信を実装可能なようです。) 参考サイト: ExpressとPassportでOpenID Connect認証を実装する OpenID Connect実装調査 (oidc-provider編) IDトークンが分かれば OpenID Connect が分かる 他Baasと比較し、Firebaseが今回には最適であると判断したため。 GoogleLogin, リアルタイムのチャット機能ともに、特異な仕様ではないため、実装事例の多い技術を活用しようと思いました。 Firebase Authentication(FirebaseのOAuthクライアント)は、OAuth2.0, OAuth/OIDCの標準を満たしています。 (→Firebase Authentication) セキュリティ上のリスクは小さく、情報量が多い・公式ドキュメントがとても充実しているため、Firebaseを採用しました。 また、他Baasでは、PobNub, Milkcocoa等を候補として選定を行いましたが、情報量・導入事例の多さによるセキュリティ面の安心感・公式ドキュメントの観点から見て、Firebaseを活用するメリットが大きいと感じました。 参考サイト: Firestore導入前に知ってほしい。3層に分解して、メリット・デメリット比較と使いどころを考える MilkcocoaでBaaSを体験!~バックエンドの仕組みと使い方~第8回 リアルタイム系BaaSの徹底比較! 国内外BaaS 3社の特徴を理解して,技術選定の幅を広げよう! Material UI React版のマテリアルデザインのデザインライブラリです。 Atoms単位、Molecules単位のコンポーネントを提供しており、ドキュメントも充実しております。 Vue.jsのデザインライブラリのVuetify, Reactだとantや、bootstrap系にも種類は豊富ですが、その中からMUIを選定した理由としては以下があります。 非常に高いカスタマイズ性 私自身のマテリアルデザインへの慣れ 情報量・導入事例 Storybook コンポーネントカタログのデファクトスタンダードです。 Jest( +enzyme ) Jestは最もメジャーなJavaScriptのテスティングフレームワークです。 enzymeはReactのテストユーティリティで、jestと連携させることでshallowレンダリングやフルマウント等を使い分けることが出来ます。 Cypress CypressはE2Eテストのライブラリです。 localhost:3000にアクセスして、このボタン押したらURLがー......とかをテスト出来ます。 実行過程を動画としてエクスポートしたり、リアルタイムで画面への実行を監視したり、初めて使ったのですが仰天しました。 GitHubActions 現在はまだjestのみですが、自動テストを行っています。 変更に対して他コンポーネントでのエラー、バグを検知出来るため、とても助かっております。 vercel ホスティングはVercelを利用しました。 選定基準としてはCDパイプラインを握りやすく、セキュリティが強固であり、無料で使え、情報が多いものが良いと思っておりました。 vercelの採用理由としてはそれらに加え、Next.jsにフォーカスされていることや、UIがわかりやすい等です。 他の選定候補としては、Netlify, AWS amplify, cloud run, github pages, Cloudflare Pages, Firebase Hosting等がありました。 なるべく設定が簡単なものを、と思うと更に絞られますが、基本的には特別大きく変わるものは無い印象です。 vercelがNext.jsにフォーカスされてるのでvercelでいいや vercel最高! vercelの無料のhobbyプランでは商用利用が規約上出来ないため、今後見直しは必要かも GitHub-Flow Git-Flowと迷いましたが、Git-Flowを個人でやるのはちょっと面倒過ぎたため、GitHub-Flowにしました。 現に、スプリントを早めに回しながら小規模リリースを繰り返しているので、GitHub-Flowにしたのは正解だったと思います。 Issueドリブン開発 タスクをまずissueに起こし、トピックブランチをfeature/#xxx_add_xxxxxxみたいに切る使い方をしています。 残りのタスクが見えるため、イマイチ頭の回らない時でも、その日行うものとして簡単な作業を選べたり、逆算して行動が出来るためこちらも割りと正解でした。 ただ、issueに起こすほどでもない小さなコミットをどうするか、は課題だったと思います。 私はfeature/odd_add_xxxxxxxxx雑用ブランチとして切っていますが、、、 うーん、、、あまり芸術的じゃない、、、 Atomic Design コンポーネントのディレクトリデザイン手法です。 好みなので導入しました。 マイクロフロントエンド沼にハマりたいのでAtomicDesignを私は足掛かりにしながらもっと知見を深めたいです。 実装中の参考サイト、紆余曲折など Firebase導入 1. 必要ライブラリのインストール npm i firebase @types/firebase 概要:firebaseのライブラリ、型定義をインストール 2.  .env.localの作成 .env.local NEXT_PUBLIC_FIREBASE_API_KEY="" NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN="" NEXT_PUBLIC_FIREBASE_PROJECT_ID="" NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET="" NEXT_PUBLIC_FIREBASE_MESSAGE_SENDER_ID="" NEXT_PUBLIC_FIREBASE_APP_ID="" NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID="" ⇑こちらですが、NEXT_PUBLIC_を文頭に置かなければソースコードから読めません。(APIルートだと読めるんだったっけ......?) 少しの間、ここに詰まった経験があります。 3. process参照のために必要なパッケージをインストール npm i --save-dev @types/node 4. 設定ファイルの作成 oukayuka/ReactFirebaseBook React + TypeScript + Firebase環境を超高速で作る Firebase * GoogleLogin 参考サイト: 公式ドキュメント(Firebase-GoogleSignin) FirebaseでOAuth2の認証・認可を30分で実装する 【完全版】ReactのFirebase Authentication(認証)を基礎からマスターする 【完全版】ReactのFirebase Authentication(認証)を基礎からマスターする AuthProviderの作成 概要:user情報をcontext化 ログイン・ログアウト Next.jsの共通コンポーネントのPropsに型をつける方法 v8→v9リリース Next.jsでFirebase Authenticationを使う(with Context API) ウェブサイトで Firebase Authentication を使ってみる 認証パッケージについて 認証パッケージ Firebase認証 Firebase + Firestore * チャット機能 参考サイト: React Firebase入門 Realtime Databaseでchatアプリ(一覧) ReactとFirebaseを使ってチャットアプリにアイコン画像を追加する方法 React.js + firebaseでリアルタイムチャットアプリ作った。 TypeScript & React & Firebase で何かつくってみる3 React Hooks firestore, vue.jsでリアルタイム同期のチャットを実装してみる [チュートリアル形式] onSnapshotにより、特定のチャットルームのmessagesというコレクションを監視し、messagesにデータが追加された時のみ処理を行う旨の記述です。 const q = query( collection(db, "chats", `${router.query.id}`, "messages"), orderBy("postedAt", "desc"), limit(10) ); onSnapshot(q, (snapshot) => { snapshot .docChanges() .reverse() .forEach(async (change) => { if (change.type === "added") { pageでmessagesが更新された時、チャットを表示するコンポーネントでは最下まで自動でスクロールします。 const ref = createRef<HTMLDivElement>(); const scrollToButtom = useCallback(() => { ref!.current!.scrollIntoView({ behavior: "smooth", block: "end", }); }, [ref]); useEffect(() => { if (!!props.messages) scrollToButtom(); }, [props.messages, scrollToButtom]); MUI + Storybook 以下のようにしました。 .storybook/main.ts const path = require("path"); module.exports = { stories: [ "../src/components/**/*.stories.@(js|jsx|ts|tsx)", "../src/components/**/stories.@(js|jsx|ts|tsx)", ], addons: ["@storybook/addon-actions", "@storybook/addon-essentials"], webpackFinal: async (config) => { config.resolve.alias = { ...config.resolve.alias, "@": path.resolve(__dirname, "../src/"), "@components": path.resolve(__dirname, "../src/components/"), "@types": path.resolve(__dirname, "../src/types/index.ts"), "@fixtures": path.resolve(__dirname, "../src/fixtures/index.ts"), }; return config; }, }; webpackFinal以降では、webpackの設定をオーバーライドし、エイリアスの設定を行っております。 .storybook/preview.tsx import CssBaseline from "@mui/material/CssBaseline"; import { ThemeProvider } from "@mui/material/styles"; import { StylesProvider } from "@mui/styles"; import { createTheme } from "@mui/material"; import * as NextImage from "next/image"; const theme = createTheme({ palette: { primary: { light: "#226cd6", main: "#1976d2", dark: "#1565c0", }, secondary: { light: "#ba68c8", main: "#9c27b0", dark: "#7b1fa2", }, error: { light: "#ef5350", main: "#d32f2f", dark: "#c62828", }, warning: { light: "#ff9800", main: "#ed6c02", dark: "#e65100", }, info: { light: "#03a9f4", main: "#0288d1", dark: "#01579b", }, success: { light: "#4caf50", main: "#2e7d32", dark: "#1b5e20", }, background: { default: "#fff", }, }, }); export const parameters = { actions: { argTypesRegex: "^on[A-Z].*" }, docs: { inlineStories: false, iframeHeight: "700px", }, }; const OriginalNextImage = NextImage.default; Object.defineProperty(NextImage, "default", { configurable: true, value: (props) => <OriginalNextImage {...props} unoptimized />, }); const withThemeProvider = (Story, context) => { return ( <StylesProvider injectFirst> <ThemeProvider theme={theme}> <CssBaseline /> <Story {...context} /> </ThemeProvider> </StylesProvider> ); }; export const decorators = [withThemeProvider]; かみ砕いていきます。 export const parameters = { actions: { argTypesRegex: "^on[A-Z].*" }, docs: { inlineStories: false, iframeHeight: "700px", }, }; parametersとして、actions-addonのマッピングと、docs-addonのinlineStories:falseを指定しています。 ※ver6.2.x, .3.x系では不具合が発生することがあるようなので、注意してください。 => https://github.com/storybookjs/storybook/issues/15143 inlineStoriesについて Inline rendering In Storybook’s Canvas, all stories are rendered in the Preview iframe for isolated development. In Storybook Docs, when inline rendering is supported by your framework, inline rendering is used by default for performance and convenience. However, you can force iframe rendering with docs: { inlineStories: false } parameter, or inline={false} in MDX. インラインレンダリング StorybookのCanvasでは、孤立した開発のために、すべてのストーリーはプレビューiframeでレンダリングされます。Storybook Docs では、フレームワークでインラインレンダリングがサポートされている場合、パフォーマンスと利便性のために、デフォルトでインラインレンダリングが使用されます。しかし、docs を使用して iframe レンダリングを強制することができます。{inlineStories: false } パラメータ、または MDX で inline={false} を指定することで、iframe レンダリングを強制することができます。 inline renderingが発生する事で、Material uiのコンポーネントを利用しているコンポーネントのdocsが表示できない場合がありました。 私はdocs.inlineStoriesをfalseにすることで解決しました。 原因については、判明次第追記しようと思います。 const OriginalNextImage = NextImage.default; Object.defineProperty(NextImage, "default", { configurable: true, value: (props) => <OriginalNextImage {...props} unoptimized />, }); Next/Imageのタグを利用して画像を読み込む場合、このようにするとエラーが出ません。 (デフォルトでは確かエラーを吐いてNext/Imageタグを読み込めませんでした。) const withThemeProvider = (Story, context) => { return ( <StylesProvider injectFirst> <ThemeProvider theme={theme}> <CssBaseline /> <Story {...context} /> </ThemeProvider> </StylesProvider> ); }; export const decorators = [withThemeProvider]; MUIのテーマプロバイダです。 previewファイルで読み込まなければ、theme.color系が使えないはず Genericsを使ったComponentのStories化 今回はジェネリクスは使っていませんが、以下のようにするとキレイに書けます https://qiita.com/Itamaster/items/ca5b2b05144b17427cb3 Jest + enzyme react17系はenzymeのadapterがまだ公式から出ていないため、以下を利用しました。 https://www.npmjs.com/package/@wojtekmaj/enzyme-adapter-react-17 設定 .jest/jest.config.js module.exports = { roots: ["../"], transform: { "^.+\\.(j|t)sx?$": "babel-jest", "^.+\\.(ts|tsx)$": "ts-jest", }, globals: { "ts-jest": { tsconfig: "<rootDir>/tsconfig.jest.json", }, }, moduleNameMapper: { "@/(.+)": "<rootDir>/../src/$1", "@fixtures": "<rootDir>/../src/fixtures/index.ts", "@components/(.+)": "<rootDir>/../src/components/$1", "\\.(css|scss)$": "<rootDir>/../node_modules/jest-css-modules" }, testPathIgnorePatterns: ["/node_modules/"], }; .jest/setupTests.js import { configure } from "enzyme"; import Adapter from "@wojtekmaj/enzyme-adapter-react-17"; configure({ adapter: new Adapter() }); .jest/tsconfig.jest.json { "extends": "../tsconfig.json", "compilerOptions": { "jsx": "react", } } これでshallow, mount使えるので便利です LICENCE とりあえずMITライセンスにしました。 alias設定 とりあえずtsconfがこんな感じです tsconfig.json { "compilerOptions": { "baseUrl": ".", "esModuleInterop": true, "module": "commonjs", "jsx": "preserve", "lib": [ "es2019", "dom" ], "sourceMap": true, "target": "es6", "noUnusedLocals": true, "resolveJsonModule": true, "skipLibCheck": true, "allowJs": true, "strict": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "moduleResolution": "node", "isolatedModules": true, "paths": { "@/*": [ "./src/*" ], "@components/*": [ "./src/components/*" ], "@types": [ "./src/types/index.ts" ], "@fixtures": [ "./src/fixtures/index.ts" ], "@hooks": [ "./src/hooks/index.ts" ], "@routes": [ "./src/constants/routes.ts" ] }, "incremental": true }, "include": [ "next-env.d.ts", "src/**/*.ts", "src/**/*.tsx" ], "exclude": [ "node_modules" ] } AtomicDesign(ディレクトリ構造) AtomicDesignの導入・運用は、なるべく多くのOSSを参考にし行いたかったのですが、star数の多いリポジトリがなかなか見つからず... (思想から大好きなんですが、react,vue共に公式リファレンスではコンテキスト別でのコンポーネント分割を推奨しています。落ち目なんでしょうか......) 良いリポジトリ等、コメント欄を使って皆さまと共有していければ嬉しく思います......! https://github.com/saleor/saleor-storefront https://github.com/danilowoz/react-atomic-design/tree/master/src/components/templates 電気通信事業開業届について 現在はサービスはβ版で、機能も少ない現在ですが、次回にサービス公開記事を投稿する頃にはチャットルームの作成、検索、運営等が出来るほか、DM機能も利用可能になる予定です。 して法律上の話なのですが、DM機能やオープンでないチャットルーム機能等のあるサービスは「電気通信事業」と見做されるため、総務省への届け出を行い、認可される必要があります。(ご注意ください) 各自治体に届け出用のフォーマットがあるため、住んでいる都道府県を検索ワードに含めて調べてみてください。 現時点でもDM機能は実装しているのですが、認可をまだいただけていないため、機能自体をフリーズしています。届け出は既に行いました!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

学生エンジニアのためのチャットサービスをNext.js + TypeScript + AtomicDesign + Firebase9 + Dockerで作った

はじめに ※今回の開発は、株式会社OwN様からいただいた技術課題の一環で行ったものになります。 サービス名 hacker-class-roomを略して、「はかくら」です! リポジトリURL こちらになります。 https://github.com/FAL-coffee/hacker-class-room 自己紹介 兼業で個人サービスの開発・運営・保守を行っております。 フロントエンジニアのふぁると申します。 統合テストのクラウド管理・実行プラットフォーム「Itamaster」を運営しております。 よろしくお願いいたします。 【Twitterリンク】 https://twitter.com/@itamaster_ 【Itamaster公開記事リンク】 https://qiita.com/Itamaster/items/f821be4c33caab640a93 【Itamaster】 https://itamaster.work テスト管理プラットフォーム Itamaster 最近、フロントエンドのみですがOSSとして公開を行いました。 知見の浅い当時書いたコードなので、コードやディレクトリは綺麗ではないですが、i18nの実装、stripe連携やvuetifyの導入等には役立てていただける点があると嬉しく思います! https://github.com/FAL-coffee/itamaster-front サービスの概要 「プログラミングの授業が始まるが、ついていけるのか不安......」 「情報処理の勉強が難しい。もしかして、こんなに苦労しているのは私だけ?」 「ゲーム作りに興味があって、プログラミングを始めてみたいけど、何から始めればいいのかわからない」 「ネットで見た通りにやったのに、環境構築でつまずいてしまった。」 「プログラミングの知見を活かして、学生起業を行いたい。仲間をどうやって見つければいいだろうか?」 そのような若い学生エンジニアにありがちな悩みを解決するために、私ははかくらを開発しました。 はかくら(Hacker Class Room)は、学生エンジニアのために開発したコミュニティサービスです。 使用技術 Next.js ( TypeScript ) --人気のOSSからディレクトリ構造を参考にし実装。src/を採用 Firebase v9 --サーバーサイドとして利用 Firestore --NoSQLのDBとして利用 Firebase Authentication --Google Loginに利用 Docker --コンテナ Material UI --主にatomsコンポーネントとして利用 Storybook --コンポーネントのカタログとして利用 Jest --Component単位のテストを実装(jest, enzyme) Cypress --E2Eテストに利用 GitHubActions--継続的インテグレーション(CI)による、UT自動化 vercel --ホスティングに利用 採用アーキテクチャ GitHub-Flow --導入・運用の簡単さを理由に起用。単純なコミットを繰り返す開発フローにも最適だった Atomic Design --コンポーネントのディレクトリデザイン手法。単純にAtomicDesignが好き issueドリブン開発 --残り作業タスクをブランチの一単位として利用できる 開発のきっかけ きっかけとしては、上述させていただいた株式会社OwN様のツイートに私が反応しました。 お話を続ける中で、技術課題として以下のような要件でシステム開発を行ってほしい、と言っていただき、一か月の期間内で開発を始めました。 要件 12 ~ 22歳のエンジニアを対象としたチャットサービスであること それらが抱える潜在的な課題を定義し、解決することを目的とした機能を実装する Googleログインを使ってユーザー認証・登録を行うこと 技術要件 TypeScriptを開発に用いていること DBに情報を保存できること セキュリティに配慮した設計・実装であること 任意要件 CI/CDパイプラインが設定されていること チーム開発を意識したアーキテクチャであること OAuth/OIDCのクライアントを自前で実装すること アプリケーションをコンテナ化すること 依存の少ないアーキテクチャであること ニーズ調査 要件を満たすために最初に行ったのは、「12 ~ 22歳のエンジニアがどのような課題を抱えているのか」の調査です。 私は12~22歳という年齢を指定されていることから、対象をその中でも更に「学生」と定義しました。 私の出身高校には情報科が設置されております。 現職で該当科の担任を受け持っておられる先生との縁が現在も続いているので、先生や生徒数名を対象にインタビューをお願いし、現在の状況やプログラミング・情報の授業中に難しく思った点を主に教えていただきました。 その結果をもとに、学生エンジニアの成長を取り巻く環境として、以下のような状況を発見しました。 クラスという区切りでは人数が少なく、向上心のある学生が高め合えるような仲間と巡り合えないことが多々ある 一部の生徒を除き、ほとんどの学生エンジニアは課題に直面した際は周囲の友人や、教職員に対し質問や相談を行う事で情報を得ている 同様に、明確な目的や目標を持っていない学生エンジニアが多く見られる それらは、以下のようなリスクを持ち合わせていると考えました。 周囲の人間が回答が出来なかった際、本人が課題解決へのモチベーションを低下させてしまう場合がある 周囲の友人や仲間がだらけている場合、雰囲気に流される者が一定数発生してしまう場合がある。それは、自身の成長が順調でない時に顕著である 明確な目的、目標を持たず、学習に対して意味を見出していない場合、モチベーションの低下に繋がる場合がある それらを私は潜在的な課題であると定義し、リスクを解決するために必要な要素として、周囲の人間関係や、慢性的に目にする情報の質の向上を図る必要があると考えました。 具体的には、以下のような環境が不足していると考えました。 質問を行った場合でも、誰かが必ず答えたり、得意分野を教え合うことの出来る環境 自分がだらけてしまうときに、同世代の向上心のある学生エンジニアとコミュニケーションを取ることで、目標設定や意識の改善を図ることの出来る環境 高め合う事の出来る仲間と繋がることの出来る環境 それらを潜在課題と定義し、要件定義フェーズを完了しました。 各技術, アーキテクチャの選定理由について Docker コンテナ技術としてDockerを採用した理由としては、連携可能なソフトウェアが多いこと、デファクトスタンダードであり、情報量や導入事例の観点から見て大きなメリットが見込めることです。 他候補としては、Containerd, lxc, lxd等がありました。 Firebase、Firestore サーバーサイド技術としてFirebaseを採用した理由としては、以下です。 私の技術スタッツ上の問題から、サーバーサイドの実装を行う場合、納期を超過してしまうリスクがあった。 他Baasと比較し、Firebaseが今回には最適であると判断したため。 かみ砕いて記述させていただきます。 私の技術スタッツ上の問題から、サーバーサイドの実装を行う場合、納期を超過してしまうリスクがあった。 今回の開発は、GoogleLoginを可能としたチャットサービスです。 現職ではCRUD処理を主とする業務システムの開発が主であるほか、バックエンドはフロントエンドと比較し得意ではないため、私のスキル不足の原因からバックエンドを自前実装することは工数上リスクであると考えました。 バックエンドを実装する場合、Express(node.js), Laravel(PHP)を候補としていました。 またその場合は、socket.io等の外部サービスを利用することでチャット機能を比較的容易に実装可能であるようです。 (socket.ioとは、リアルタイムWeb技術の一つであるwebsocket通信を利用したjsライブラリで、リアルタイムの双方向通信を実装可能なようです。) 参考サイト: ExpressとPassportでOpenID Connect認証を実装する OpenID Connect実装調査 (oidc-provider編) IDトークンが分かれば OpenID Connect が分かる 他Baasと比較し、Firebaseが今回には最適であると判断したため。 GoogleLogin, リアルタイムのチャット機能ともに、特異な仕様ではないため、実装事例の多い技術を活用しようと思いました。 Firebase Authentication(FirebaseのOAuthクライアント)は、OAuth2.0, OAuth/OIDCの標準を満たしています。 (→Firebase Authentication) セキュリティ上のリスクは小さく、情報量が多い・公式ドキュメントがとても充実しているため、Firebaseを採用しました。 また、他Baasでは、PobNub, Milkcocoa等を候補として選定を行いましたが、情報量・導入事例の多さによるセキュリティ面の安心感・公式ドキュメントの観点から見て、Firebaseを活用するメリットが大きいと感じました。 参考サイト: Firestore導入前に知ってほしい。3層に分解して、メリット・デメリット比較と使いどころを考える MilkcocoaでBaaSを体験!~バックエンドの仕組みと使い方~第8回 リアルタイム系BaaSの徹底比較! 国内外BaaS 3社の特徴を理解して,技術選定の幅を広げよう! Material UI React版のマテリアルデザインのデザインライブラリです。 Atoms単位、Molecules単位のコンポーネントを提供しており、ドキュメントも充実しております。 Vue.jsのデザインライブラリのVuetify, Reactだとantや、bootstrap系にも種類は豊富ですが、その中からMUIを選定した理由としては以下があります。 非常に高いカスタマイズ性 私自身のマテリアルデザインへの慣れ 情報量・導入事例 Storybook コンポーネントカタログのデファクトスタンダードです。 Jest( +enzyme ) Jestは最もメジャーなJavaScriptのテスティングフレームワークです。 enzymeはReactのテストユーティリティで、jestと連携させることでshallowレンダリングやフルマウント等を使い分けることが出来ます。 Cypress CypressはE2Eテストのライブラリです。 localhost:3000にアクセスして、このボタン押したらURLがー......とかをテスト出来ます。 実行過程を動画としてエクスポートしたり、リアルタイムで画面への実行を監視したり、初めて使ったのですが仰天しました。 GitHubActions 現在はまだjestのみですが、自動テストを行っています。 変更に対して他コンポーネントでのエラー、バグを検知出来るため、とても助かっております。 vercel ホスティングはVercelを利用しました。 選定基準としてはCDパイプラインを握りやすく、セキュリティが強固であり、無料で使え、情報が多いものが良いと思っておりました。 vercelの採用理由としてはそれらに加え、Next.jsにフォーカスされていることや、UIがわかりやすい等です。 他の選定候補としては、Netlify, AWS amplify, cloud run, github pages, Cloudflare Pages, Firebase Hosting等がありました。 なるべく設定が簡単なものを、と思うと更に絞られますが、基本的には特別大きく変わるものは無い印象です。 vercelがNext.jsにフォーカスされてるのでvercelでいいや vercel最高! vercelの無料のhobbyプランでは商用利用が規約上出来ないため、今後見直しは必要かも GitHub-Flow Git-Flowと迷いましたが、Git-Flowを個人でやるのはちょっと面倒過ぎたため、GitHub-Flowにしました。 現に、スプリントを早めに回しながら小規模リリースを繰り返しているので、GitHub-Flowにしたのは正解だったと思います。 Issueドリブン開発 タスクをまずissueに起こし、トピックブランチをfeature/#xxx_add_xxxxxxみたいに切る使い方をしています。 残りのタスクが見えるため、イマイチ頭の回らない時でも、その日行うものとして簡単な作業を選べたり、逆算して行動が出来るためこちらも割りと正解でした。 ただ、issueに起こすほどでもない小さなコミットをどうするか、は課題だったと思います。 私はfeature/odd_add_xxxxxxxxx雑用ブランチとして切っていますが、、、 うーん、、、あまり芸術的じゃない、、、 Atomic Design コンポーネントのディレクトリデザイン手法です。 好みなので導入しました。 マイクロフロントエンド沼にハマりたいのでAtomicDesignを私は足掛かりにしながらもっと知見を深めたいです。 実装中の参考サイト、紆余曲折など Firebase導入 1. 必要ライブラリのインストール npm i firebase @types/firebase 概要:firebaseのライブラリ、型定義をインストール 2.  .env.localの作成 .env.local NEXT_PUBLIC_FIREBASE_API_KEY="" NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN="" NEXT_PUBLIC_FIREBASE_PROJECT_ID="" NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET="" NEXT_PUBLIC_FIREBASE_MESSAGE_SENDER_ID="" NEXT_PUBLIC_FIREBASE_APP_ID="" NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID="" ⇑こちらですが、NEXT_PUBLIC_を文頭に置かなければソースコードから読めません。(APIルートだと読めるんだったっけ......?) 少しの間、ここに詰まった経験があります。 3. process参照のために必要なパッケージをインストール npm i --save-dev @types/node 4. 設定ファイルの作成 oukayuka/ReactFirebaseBook React + TypeScript + Firebase環境を超高速で作る Firebase * GoogleLogin 参考サイト: 公式ドキュメント(Firebase-GoogleSignin) FirebaseでOAuth2の認証・認可を30分で実装する 【完全版】ReactのFirebase Authentication(認証)を基礎からマスターする 【完全版】ReactのFirebase Authentication(認証)を基礎からマスターする AuthProviderの作成 概要:user情報をcontext化 ログイン・ログアウト Next.jsの共通コンポーネントのPropsに型をつける方法 v8→v9リリース Next.jsでFirebase Authenticationを使う(with Context API) ウェブサイトで Firebase Authentication を使ってみる 認証パッケージについて 認証パッケージ Firebase認証 Firebase + Firestore * チャット機能 参考サイト: React Firebase入門 Realtime Databaseでchatアプリ(一覧) ReactとFirebaseを使ってチャットアプリにアイコン画像を追加する方法 React.js + firebaseでリアルタイムチャットアプリ作った。 TypeScript & React & Firebase で何かつくってみる3 React Hooks firestore, vue.jsでリアルタイム同期のチャットを実装してみる [チュートリアル形式] onSnapshotにより、特定のチャットルームのmessagesというコレクションを監視し、messagesにデータが追加された時のみ処理を行う旨の記述です。 const q = query( collection(db, "chats", `${router.query.id}`, "messages"), orderBy("postedAt", "desc"), limit(10) ); onSnapshot(q, (snapshot) => { snapshot .docChanges() .reverse() .forEach(async (change) => { if (change.type === "added") { pageでmessagesが更新された時、チャットを表示するコンポーネントでは最下まで自動でスクロールします。 const ref = createRef<HTMLDivElement>(); const scrollToButtom = useCallback(() => { ref!.current!.scrollIntoView({ behavior: "smooth", block: "end", }); }, [ref]); useEffect(() => { if (!!props.messages) scrollToButtom(); }, [props.messages, scrollToButtom]); MUI + Storybook 以下のようにしました。 .storybook/main.ts const path = require("path"); module.exports = { stories: [ "../src/components/**/*.stories.@(js|jsx|ts|tsx)", "../src/components/**/stories.@(js|jsx|ts|tsx)", ], addons: ["@storybook/addon-actions", "@storybook/addon-essentials"], webpackFinal: async (config) => { config.resolve.alias = { ...config.resolve.alias, "@": path.resolve(__dirname, "../src/"), "@components": path.resolve(__dirname, "../src/components/"), "@types": path.resolve(__dirname, "../src/types/index.ts"), "@fixtures": path.resolve(__dirname, "../src/fixtures/index.ts"), }; return config; }, }; webpackFinal以降では、webpackの設定をオーバーライドし、エイリアスの設定を行っております。 .storybook/preview.tsx import CssBaseline from "@mui/material/CssBaseline"; import { ThemeProvider } from "@mui/material/styles"; import { StylesProvider } from "@mui/styles"; import { createTheme } from "@mui/material"; import * as NextImage from "next/image"; const theme = createTheme({ palette: { primary: { light: "#226cd6", main: "#1976d2", dark: "#1565c0", }, secondary: { light: "#ba68c8", main: "#9c27b0", dark: "#7b1fa2", }, error: { light: "#ef5350", main: "#d32f2f", dark: "#c62828", }, warning: { light: "#ff9800", main: "#ed6c02", dark: "#e65100", }, info: { light: "#03a9f4", main: "#0288d1", dark: "#01579b", }, success: { light: "#4caf50", main: "#2e7d32", dark: "#1b5e20", }, background: { default: "#fff", }, }, }); export const parameters = { actions: { argTypesRegex: "^on[A-Z].*" }, docs: { inlineStories: false, iframeHeight: "700px", }, }; const OriginalNextImage = NextImage.default; Object.defineProperty(NextImage, "default", { configurable: true, value: (props) => <OriginalNextImage {...props} unoptimized />, }); const withThemeProvider = (Story, context) => { return ( <StylesProvider injectFirst> <ThemeProvider theme={theme}> <CssBaseline /> <Story {...context} /> </ThemeProvider> </StylesProvider> ); }; export const decorators = [withThemeProvider]; かみ砕いていきます。 export const parameters = { actions: { argTypesRegex: "^on[A-Z].*" }, docs: { inlineStories: false, iframeHeight: "700px", }, }; parametersとして、actions-addonのマッピングと、docs-addonのinlineStories:falseを指定しています。 ※ver6.2.x, .3.x系では不具合が発生することがあるようなので、注意してください。 => https://github.com/storybookjs/storybook/issues/15143 inlineStoriesについて Inline rendering In Storybook’s Canvas, all stories are rendered in the Preview iframe for isolated development. In Storybook Docs, when inline rendering is supported by your framework, inline rendering is used by default for performance and convenience. However, you can force iframe rendering with docs: { inlineStories: false } parameter, or inline={false} in MDX. インラインレンダリング StorybookのCanvasでは、孤立した開発のために、すべてのストーリーはプレビューiframeでレンダリングされます。Storybook Docs では、フレームワークでインラインレンダリングがサポートされている場合、パフォーマンスと利便性のために、デフォルトでインラインレンダリングが使用されます。しかし、docs を使用して iframe レンダリングを強制することができます。{inlineStories: false } パラメータ、または MDX で inline={false} を指定することで、iframe レンダリングを強制することができます。 inline renderingが発生する事で、Material uiのコンポーネントを利用しているコンポーネントのdocsが表示できない場合がありました。 私はdocs.inlineStoriesをfalseにすることで解決しました。 原因については、判明次第追記しようと思います。 const OriginalNextImage = NextImage.default; Object.defineProperty(NextImage, "default", { configurable: true, value: (props) => <OriginalNextImage {...props} unoptimized />, }); Next/Imageのタグを利用して画像を読み込む場合、このようにするとエラーが出ません。 (デフォルトでは確かエラーを吐いてNext/Imageタグを読み込めませんでした。) const withThemeProvider = (Story, context) => { return ( <StylesProvider injectFirst> <ThemeProvider theme={theme}> <CssBaseline /> <Story {...context} /> </ThemeProvider> </StylesProvider> ); }; export const decorators = [withThemeProvider]; MUIのテーマプロバイダです。 previewファイルで読み込まなければ、theme.color系が使えないはず Genericsを使ったComponentのStories化 今回はジェネリクスは使っていませんが、以下のようにするとキレイに書けます https://qiita.com/Itamaster/items/ca5b2b05144b17427cb3 Jest + enzyme react17系はenzymeのadapterがまだ公式から出ていないため、以下を利用しました。 https://www.npmjs.com/package/@wojtekmaj/enzyme-adapter-react-17 設定 .jest/jest.config.js module.exports = { roots: ["../"], transform: { "^.+\\.(j|t)sx?$": "babel-jest", "^.+\\.(ts|tsx)$": "ts-jest", }, globals: { "ts-jest": { tsconfig: "<rootDir>/tsconfig.jest.json", }, }, moduleNameMapper: { "@/(.+)": "<rootDir>/../src/$1", "@fixtures": "<rootDir>/../src/fixtures/index.ts", "@components/(.+)": "<rootDir>/../src/components/$1", "\\.(css|scss)$": "<rootDir>/../node_modules/jest-css-modules" }, testPathIgnorePatterns: ["/node_modules/"], }; .jest/setupTests.js import { configure } from "enzyme"; import Adapter from "@wojtekmaj/enzyme-adapter-react-17"; configure({ adapter: new Adapter() }); .jest/tsconfig.jest.json { "extends": "../tsconfig.json", "compilerOptions": { "jsx": "react", } } これでshallow, mount使えるので便利です LICENCE とりあえずMITライセンスにしました。 alias設定 とりあえずtsconfがこんな感じです tsconfig.json { "compilerOptions": { "baseUrl": ".", "esModuleInterop": true, "module": "commonjs", "jsx": "preserve", "lib": [ "es2019", "dom" ], "sourceMap": true, "target": "es6", "noUnusedLocals": true, "resolveJsonModule": true, "skipLibCheck": true, "allowJs": true, "strict": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "moduleResolution": "node", "isolatedModules": true, "paths": { "@/*": [ "./src/*" ], "@components/*": [ "./src/components/*" ], "@types": [ "./src/types/index.ts" ], "@fixtures": [ "./src/fixtures/index.ts" ], "@hooks": [ "./src/hooks/index.ts" ], "@routes": [ "./src/constants/routes.ts" ] }, "incremental": true }, "include": [ "next-env.d.ts", "src/**/*.ts", "src/**/*.tsx" ], "exclude": [ "node_modules" ] } AtomicDesign(ディレクトリ構造) AtomicDesignの導入・運用は、なるべく多くのOSSを参考にし行いたかったのですが、star数の多いリポジトリがなかなか見つからず... (思想から大好きなんですが、react,vue共に公式リファレンスではコンテキスト別でのコンポーネント分割を推奨しています。落ち目なんでしょうか......) 良いリポジトリ等、コメント欄を使って皆さまと共有していければ嬉しく思います......! https://github.com/saleor/saleor-storefront https://github.com/danilowoz/react-atomic-design/tree/master/src/components/templates 電気通信事業開業届について 現在はサービスはβ版で、機能も少ない現在ですが、次回にサービス公開記事を投稿する頃にはチャットルームの作成、検索、運営等が出来るほか、DM機能も利用可能になる予定です。 して法律上の話なのですが、DM機能やオープンでないチャットルーム機能等のあるサービスは「電気通信事業」と見做されるため、総務省への届け出を行い、認可される必要があります。(ご注意ください) 各自治体に届け出用のフォーマットがあるため、住んでいる都道府県を検索ワードに含めて調べてみてください。 現時点でもDM機能は実装しているのですが、認可をまだいただけていないため、機能自体をフリーズしています。届け出は既に行いました!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む