【初めての個人開発】『これおれがわるいんか？』- 超局所的SNS

# はじめに

Webエンジニアを志すにあたり、開発の学習を兼ねて
就職活動用のポートフォリオになればと作成しました。

どんな機能を実装したのか、
自分なりにおさらい/デモンストレーションしてみようという趣旨の記事です。

◾️これおれがわるいんか？

https://koreore.vercel.app/

---

# 自己紹介
東京都在住、29歳男性。Webディレクター/CC字幕制作エディターの経験あり。
現在はWebエンジニアへのキャリアチェンジに向けて学習～就職活動中です。

# Webサービス概要
・X（旧Twitter）ライクなSNS。

・タイムラインには、ユーザーが経験した
「これおれがわるいんか？」と言いたくなるような理不尽な出来事が流れてくる。

・それを "わるくない" と "う～ん" の二択で評価する/される。

---

※実際の画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/9a73b041-6fc9-df80-6dd6-1a07c9370d15.png)

---

# 開発経緯
自分はSNSをほとんどROM専で活用しており、いわゆるフォロワーとの繋がりが皆無です。

そこで、理不尽な出来事が降りかかったとき、
誰にその思いを共有すればよいのか…ということで開発を決意しました。

それ専用のプラットフォームがあれば、他者から "君はわるくない" と支持されることで、
いくらか溜飲を下げることができる＝拠り所になりうると考えました ~~（動機が超後ろ向き）~~ 。

---

# 使用技術
* MongoDB (JSON形式でデータを管理するNoSQLデータベース)
* Express (Webアプリ開発用のフレームワーク)
* React (フロントエンド用のJSライブラリ)
* Node.js (サーバー側でJSを実行できる環境)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/20bb96ba-86a4-3d04-f83b-c9c74aa61a65.png)

※引用元：https://www.bocasay.com/how-does-the-mern-stack-work/

---

JavaScriptで完結するMERNスタックを採用しました。
「Web系には避けて通れない言語である」というのが第一の理由です。第二は、いち早く形にして企業様にお見せすることが目的なので、学習コストを最低限に抑えようという魂胆があります。

ソース管理にはGitHubを、デプロイにはVercelを使用しています。

---

# 事前学習
https://www.udemy.com/course/fullstack-mern-project-course/?couponCode=ACCAGE0923

こちらのUdemy講座を１周
JSについて１か月ほどPaizaで学習
開発開始(Udemy講座を復習)

---

# 基本設計/デザイン
スプレッドシートにざっくりと必要な機能をリストアップし、それをもとにFigmaでデザインしました。
（といってもワイヤーフレームレベルのデザイン）

たしか昔は無料でAdobe XDを使えた気がするんですが、
今は単体プランも終了してるし、サービスも実質終了って感じなんですね。少し寂しい。

---

※Figma
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/b1dba68a-6ea1-1b3c-a197-aef5e1d2a372.png)

---

ここまでで１～２人日くらいかかった気がします。いや、３人日かな…。

最小限の要素で設計していたので、
実際にこのデザインを厳密に再現するようなマークアップはしませんでした。

---

# メイン機能
* ユーザー新規登録/ログイン/情報更新/退会
* メール認証
* 投稿/削除
* タイムライン表示/自分の投稿のみを表示
* 評価ボタン

こうして洗い出してみると少ないですね…。
基本的な機能は問題なく実装できているはずです。

しかし詳細なテストは現状できておらず、
実際に使ってみればボロが出まくるはずです。恐縮です。

---

# 実際に触って遊ぼう
さて、ここからは実際に使用するユーザーを想定して触ってみましょう。
「認知的ウォークスルー」というやつですね。

---

### 新規登録

まずは新規登録。ヘッダーの新規登録ボタンを押して…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/dfae7e36-6553-0d87-33cc-8f7d956196c6.png)

---

仮登録画面へと進みましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/70378475-71d2-e2f1-053a-9f9ca3304e34.png)

ここに自身のアドレスを入力し、送信ボタンを押せば…

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/dec64555-3cde-8be1-8162-1721f1d1d9ad.png)

これで…

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/a72fb28a-d9f8-7a58-49f7-a996f9c8b636.png)

仮登録用のURLが届く！というわけです。

---
ここでの処理を簡単に説明すると…

1. uuidというモジュールで識別子を生成し…
1. 受け取ったアドレスと一緒にDBへ登録します
1. そうしたら、識別子込みのURLをメールで送信して…
1. その識別子がDBにあるかを判定し、新規登録ページに遷移する

という流れになります。

それではURLをクリックしてみましょう。

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/c72c2b88-1f32-1438-c5e1-5b88a1f60b5d.png)

よかった！問題なく遷移できました。
それでは適当に入力して登録しちゃいましょう。

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/a25e3ad8-2d3d-3838-a0dc-c136a0fc05fc.png)

登録が完了すると、ホームに戻ります。ヘッダーから「新規登録」「ログイン」ボタンが消えていますね！

---

問題なく登録できると、loginCallというactionCallが発火し、ログインできるようになっています。
いちいち入力しなおす必要がなくて便利ですね～！

それでは次に、プロフィール情報を確認しましょう。
フッター右下の人型アイコンを押下すれば遷移します。

---

### プロフィール
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/d21f2926-b143-75c8-2461-8bd2d8321d69.png)

こちらが私のプロフィール画面となります。

---

まだ何も投稿していないので、マイポストには何も表示されていませんね。

すべて問題なく正常に表示されているようで…

おっと、どうやら生年月日を誤入力してしまったようです。
編集ボタンを押して修正しましょう。

（なお、パスワードは暗号化しているため、便宜的に「●●●●●●」と表示しています。
DBから直接取得しようものなら、とんでもない数の「●」が並ぶことに…
かといってデコードするのもセキュリティの観点でNGな気がしますので）

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/0415626c-347c-7dd9-eeb3-4b27b24649ed.png)

正しい生年月日に修正しましょう。

---

えっと、私の誕生日はたしか2月30日…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/253cc1ec-6a63-584f-b30c-75ca83131448.png)

---

失礼しました。このように、日付が妥当であるかのバリデーションチェックも実装しています。
これは受け取った数値でDate型のオブジェクトを作成し、isValidメソッドで判定しています。

それでは、生年月日を普通に変更しましょう。１月１日に設定します。

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/4dd24c6a-9635-8b06-125e-a31a0587a870.png)

更新に成功すると、プロフィール画面にリダイレクトします。
生年月日が無事に変更されていることがわかりますね。

しかし、遷移後数秒は変更前の情報が表示されます。これはUXガタ落ち案件です。
けれど、こういうのをいちいち拾っていくと今回の趣旨からブレるのでひとまず置いておきます。

---

### 投稿
肝の機能です。投稿するには、まずフッター真ん中の「＋」ボタンを押します。

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/f0f45adb-9736-a2aa-69a6-49bc66bb4cb3.png)

すると、投稿画面に遷移します。
ここにため息が出るような出来事を入力し、タイムラインへとブチ流すのです。

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/22728b86-31f6-5770-b31a-c3711a71cf09.png)

ハアハア…思った以上に熱が入ってしまった。
あれ？でも、これ以上入力できない…。

---

それもそのはず、最大で140文字になるよう設定しています。
入力内容はuseRefで常に監視しており、あと何文字入力可能かもリアルタイムでレンダリングされます。
それでは、140文字以内に修正しましょう。

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/8e3cd19f-26a2-b21b-a7b4-18e7f8a0289e.png)

~~内容変わってんじゃん~~

投稿ボタンを押しましょう。

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/9983942f-748d-e0d1-d342-ba484bc07d6a.png)

するとホームにリダイレクトします。いちばん上に、先ほどの投稿が表示されていますね。
せっかくなので、適当にあと３つくらい投稿します。そしたらプロフィール画面を見てみましょう。

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/d449278e-00a1-e641-420d-4a820cc45a9d.png)

先ほどは「まだ投稿がありません」と表示されていましたが、最新の投稿が表示されていますね。
次に、「もっと見る」ボタンを押してみましょう。

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/31f07066-fa93-d14c-fb53-3e0677ba114a.png)

自分のポストが表示されました。

---

それでは、この中の一つを削除してみましょう。
ポストの右上にあるゴミ箱ボタンをクリックすると…

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/6e6f8ac5-2209-33ed-5067-f745c917bd23.png)

確認ダイアログが表示されます。フールプルーフというやつですね。
では、削除ボタンを押しましょう。

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/8c58bfb4-ec94-4d29-ebda-fbdcdc620daf.png)

ポスト一覧にリダイレクトされるので、選択した投稿が削除されているか確認しましょう。
私の場合、選択した「おばあさんに声をかけた～」投稿が削除されています。問題なさそうです。

---

### 評価ボタン
肝の一つ、評価ボタンです。

当初の予定では、評価ボタンを押下するとボタンがグラフに変形し、
評価の比率をわかりやすく伝えるアニメーションを実装する予定でした。

しかし、１～２日頑張ってもうまく動かせられなかったので、ひとまず諦めました。
形にするのが最優先です（自分に言い聞かせてます）。

---

自分の投稿も評価できるようにしているので、試してみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/93e9c252-11de-e5f3-d083-8733252d8aec.png)

これを…

---

こう！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/7e211368-e4bc-cac7-1abd-2e4a4e35496c.png)


今回は「わるくない」を押しました。すると数字が表示されましたね。これが、そのポストの評価数です。

ポストスキーマにある評価プロパティ(配列）に、評価したユーザーのIDが追加され、
そのlengthを取得しています。

評価済の投稿は、レンダリング時に最初から評価数が表示されるようになっています。

これにて肝の機能紹介は終わりです。

---

### ログアウト

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/6228c22f-8ae2-8031-4b83-a8f499dcbb28.png)

プロフィール画面の下のほうにボタンがあるので、押します。

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/7b9fbae2-dbcc-e7f4-d8be-80bff0d219da.png)

ヘッダーにボタンが表示されました。無事ログアウトできているようです。
ちなみに未ログイン時にプロフィールへ飛ぼうとすると、ホーム画面にリダイレクトされます。

---

### 退会

退会も同様です。プロフィール画面の退会ボタンを押すと…

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/daeaaa64-2430-9399-355b-5d3112a1c921.png)

ダイアログが表示されます。未練はありませんのでボタンを押しましょう。

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3514795/c7af7832-f05e-5718-b760-ee98b62ae042.png)

ホームにリダイレクトしました。

---

タイムラインには、先ほど投稿したものも見当たりません。
投稿もすべて削除できているようです。

---

# 工数
* 基本設計：1人日
* デザイン：1人日
* 開発：15人日

合計 約17人日

厳密に管理していないので、かなり感覚に頼った数字です。

---

開発は9月頭に開始し、25日にデプロイしました。
自分の生活習慣や予定を加味すると、それでだいたい15人日です。

その後もちょくちょく修正してますが、多めに見積もっても20人日いかないくらいだと思います。

これがもし実務経験ありのプロフェッショナルなら、
５日とかからないんじゃないかと思います。

スーパーつよつよエンジニアになるまで、まだまだ先は長そうです。

---

# 苦労したこと
たくさんありますが、特に印象的なものを３つ挙げます。

---

### ①タイムラインの無限スクロール
無限スクロール自体は、Intersection Observer APIを使用して
「HTMLの指定した要素が画面に表示されたら、投稿を10件表示」のように実装しました。

しかし、「初めから表示されている10件」が繰り返し、無限に表示されてしまう問題にぶち当たりました。

原因は、渡すべきデータを渡していないことでした。

「取得した投稿の中の、最後の投稿の作成日時(Date型)をAPIに渡し、
それ以前に投稿された最新の10件を取得」という処理が必要なのですが、
頭ではなんとなくわかっていても、形にするのには数時間を要しました。
なんとなくわかっているだけで手を動かしても、結果を得ることはできませんでした。

ここで学んだのは、「処理内容を言語化してから実装する」方法が
自分には合っているんじゃないか、ということです。

---

### ②ボタン変形アニメーション

先述したとおり、実装を諦めた部分です。

評価ボタンの押下後、評価の割合を計算してスタイルに落とし込み、棒グラフのように変形させる…
までは実装できたのですが、右側のボタンを押しても左側が変形してしまうのです。
（ただし、ページを更新すると左側が変形している正しい状態で表示される）

このあたりはデータのやり取りが想像以上に複雑で、
これ以上時間を割けないと判断し、泣く泣く断念しました。

ここで気持ちよくアニメーションさせてユーザーのボタン押下欲を刺激したかったので、
今でも悔しいです。

---

### ③デプロイ
Qiitaなどで個人開発系の記事を拝見すると、皆さん必ずと言っていいほどデプロイにぶち当たっています。
例にもれず、私もデプロイに２日ほど費やしました。

なぜそんなにかかったのか？ 答えは「何も知らなかったから」です。

プログラミングやフレームワークの学習はしていても、
デプロイに必要な知識はまったく異なるように感じました。

とにかく自分と似たようなフレームワークでの導入事例を探し、
エラーメッセージを読み、検索～AIアシスタントであたりをつけて修正…
というようなことを繰り返しました。

実のところ、いきあたりばったりすぎて
最も学習した手ごたえのない部分でもあります。

---

# 頑張ったこと

つまり注目してもらいたいことです。
せっかくなので、こちら２点だけ挙げさせていただきます。

---

### ①無限スクロール

「苦労したこと」にも書きましたが、これを実装できたときにはかなりの達成感がありました。
そもそも無限スクロールの仕組みを考えたこともなく、
実装できなかった「ボタンアニメーション」よりも不安な部分でした。

しかし、オブザーバーAPIの存在を知れば
無限スクロールの実態はそこまで複雑ではありませんでした。
（そのかわり、データの流れで苦戦したわけですが…）

---

### ②メール認証

無限スクロールと同じような理由です。
ふだんから何気なく利用しているメール認証ですが、
仕組みについては考えたこともありません。
正直なところ、実装を省いてもいいと考えていました。

しかし、メール認証があるのとないのでは
「それっぽさ」がかなり違ってくるな…と判断し、実装を決意。

新たにスキーマを作り、そこにアドレスと識別子を格納して
URLに埋め込んだ識別子から判定する…という仕組みが判明すれば
実装自体はそれほど難しくありませんでした。

---

# AIとのつきあい方

日常的に使用しているPerplexity（パープレ様）にご協力いただきました。
実装したい機能を伝えれば、その方法を提案してくれるのが本当に助かります。
通常ならポチポチと事例の検索をしていくところ、パープレ様はソース元URL込みでまとめてくれるので
それだけで効率が100倍上がっている気がします。

とはいえ生成したコードをまるっとコピペなんてことは致しません。
それで完結するなら何も(？)問題ありませんが、実際は必ず問題が発生します（指示が下手なだけ？）。

提案されたコードをきちんと咀嚼して、理解したうえで
自分が求めているソースコードに最適化させることがミソだと考えています。
（理解しておかないと修正時にめちゃくちゃなコストがかかることは自明）

そして、それを自分のものにできれば
その次からは何も参照しない＝スピーディーなコーディングができるようになるはずです。

---

# おわりに
ひとまず形にすることはできましたが、何も参照せずにそらで開発できるか…でいうと、厳しいです。
実務でググる（死語？）ことが制限されることはありえないと思うので一見問題なさそうですが、
「何も見ずにそらで記述できるか」で開発スピードに雲泥の差が生まれることは間違いないと思います。
（「ほとんどのエンジニアは"プロググり師"」…と揶揄られてるのを目にしたので、
その先を目標にしたいです）

今回の開発では「フレームワーク」「データの受け渡し」「エラーハンドリング」「デプロイ」など、
開発における概念を理解するうえで、私にとってとても有意義なものになりました。

まだ就職も決まっていないかけだしの身ですが、精進していこうと思います。

最後までご覧いただき、ありがとうございました！