人材マッチングサービスの別々だった 3 つのアプリを 1 つに統合し、検索・応募・チャット・課金の仕組みを整備

業務システムWeb アプリAI 活用LINEミニアプリ

概要

副業・フリーランスの人材と企業をつなぐマッチングサービスを運営する企業様の案件です。求職者向け・企業向け・運営向けの 3 つの画面が、それぞれ別のアプリとして作られていたものを 1 つのアプリにまとめ、その上で人材検索、求人への応募、企業と人材のやり取り(チャット)、有料プランによる情報の出し分け、LINE からの登録までを順に作り込みました。

使った技術は、画面とサーバーの処理に Next.js(React のフレームワーク)、データベースとログインの仕組みに Supabase(PostgreSQL のデータベースと認証のサービス)、公開先に Vercel です。

課題

  • 求職者向け・企業向け・運営向けの 3 つのアプリが別々にあり、データベースへの接続部分・設定値・データの型の定義がそれぞれに重複していた
  • 公開の設定(デプロイ)を 3 つ別々に管理していて、共通の変更をするたびに 3 か所を直す必要があった
  • 企業が求人票を一から書くのに手間がかかり、掲載が進みにくかった
  • 求職者の多くが LINE から来るため、LINE の中で登録からログインまで済ませられる導線が必要だった
  • 有料・無料で企業に見せる人材情報の範囲を変えたいが、出し分けの仕組みがなかった

作ったもの

1 つのアプリへの統合

3 つのアプリを、URL の区切り(/user・/company・/admin)ごとに分けた 1 つのアプリにまとめました。ログイン画面も /login/user・/login/company・/login/admin にそろえています。データベースへの接続、定数、型の定義は共通の置き場所に 1 つだけ置き、画面ごとの部品は利用者の種類ごとのフォルダに分けて、1 つにまとめても中身が混ざらないようにしました。公開の設定も 1 つになり、本番用とプレビュー用のブランチを分けて自動で公開されるようにしています。

求職者向けの画面

  • 登録とマイページ: 名前・連絡先・ログイン ID を入れて登録し、経歴・スキル・希望する働き方などをマイページで書き足せます
  • 求人を探して応募: 職種・稼働量・報酬などで絞り込み、求人の詳細からそのまま応募メッセージを添えて応募できます
  • 企業とのやり取り: 応募した求人や、企業から届いたスカウトごとに会話の画面があり、未読の件数も分かります
  • 設定: メールアドレス・パスワードの変更や退会ができます。変更や退会の前には、今のパスワードをもう一度入れてもらいます

企業向けの画面

  • 人材検索: 職種・稼働形態・地域などの条件や、経歴・スキル・自己紹介に含まれる言葉で人材を探し、気になる人材を保存できます
  • 案件の作成: 質問に選択肢で答えていくチャット形式の画面で条件を入力すると、AI が案件のタイトルと本文の下書きを作ります。企業はそれを確かめて直してから公開します
  • 応募者とスカウトの管理: 応募者の一覧とメッセージの確認、人材へのスカウト、そこからの会話ができます
  • 料金プランの案内: 無料と有料の 2 つのプランの違いを比べられるページを用意しました

運営向けの管理画面

画面 できること
ユーザー管理 人材の一覧・検索・編集、企業に見せるかどうかの切り替え(まとめて選択も可能)、認定バッジの付与
企業管理 企業の一覧、審査の状態の変更(承認するとログイン ID 付きのメールを自動送信)、ログインの可否、有料プランの切り替え、削除
求人管理 求人の一覧・編集、運営による企業と求人の追加
分析 登録数の推移、アクティブなユーザー・企業の数、プロフィールの記入率、求人ごとの閲覧数・応募数・応募率

外部サービスとのつなぎ込み

  • LINE ミニアプリ: LINE の中から登録とログインができ、登録の状態に応じて LINE の画面下のメニューが「登録する」から「サービスを開く」に切り替わります
  • 会社名の入力補助: 国の法人情報の公開 API(gBizINFO)を使い、会社名を打つと候補が出て、法人番号で会社を特定できます
  • 通知: 新しい企業の登録や、企業が人材を「気になる」に入れたときなどに、社内チャットとタスク管理ツールに自動で知らせます
  • 募集ページからの一括登録: 外部の募集ページのフォームに会社情報と求人の内容を入れると、AI による求人票の作成、企業アカウントの発行、求人の公開までを 1 回の申し込みで済ませる受付口も作りました

工夫したところ

権限の確かめ方を 1 つの考え方にそろえた

運用の途中で外部のセキュリティ診断を受け、ブラウザに渡している公開用の鍵で一部のテーブルが読み書きできてしまうことが分かりました。そこで、データベース側では「全テーブルで、ブラウザから直接の読み書きをすべて断る」設定にし、データの読み書きはすべてサーバーを通す形に切り替えました。そのうえで、運営向けの処理は「運営としてログインしているか」、企業向けの処理は「自社のデータか」を、各処理の最初に必ず確かめる共通の関数にまとめています。

同じ見直しで、「非公開にした人材でも、ID を指定すれば企業から詳細が見えてしまう」問題も見つけて直しました。人材の詳細だけでなく、お気に入りへの追加、紹介の依頼、閲覧の記録といった関連する処理すべてで、非公開の人材を対象にできないよう確かめを入れています。

有料・無料の出し分けをサーバー側で 1 か所にまとめた

無料プランの企業には、人材の名前は名字の頭文字だけ、自己紹介は冒頭だけを見せ、連絡先やポートフォリオは隠します。応募メッセージも冒頭の 100 文字だけを見せます。この処理は画面ではなくサーバー側の 1 つの関数で行うので、画面の作りによって情報が漏れることがありません。また、LINE の ID や運営用のメモなどの内部の情報は、有料・無料にかかわらず企業には渡さないようにしています。チャットやスカウトの送信も、有料プランでない場合はサーバー側で断ります。

データベースの制約で重複や不整合を防いだ

  • 同じ企業から同じ人材へのスカウトや、同じ人材の重複したお気に入りは、データベースの一意の制約(同じ組み合わせを 2 件入れられない決まり)で防いでいます
  • チャットのメッセージは「応募の会話」か「スカウトの会話」のどちらか一方にだけ属するよう、データベースの検査の条件で決めています
  • 退会したときに関連するデータが残らないよう、テーブル同士のつながり(外部キー)に「元のデータを消したら一緒に消す」設定を付け、退会の処理でも関連するテーブルを明示的に消すようにしました

途中で失敗しても中途半端なデータを残さない

アカウントの作成は「ログイン用のアカウント」と「プロフィール」の 2 段階で行います。プロフィールの保存に失敗したときは、先に作ったログイン用のアカウントを消して元に戻します。募集ページからの一括登録では、AI による求人票の作成をアカウント作成より先に行い、AI が失敗したときには何も作らないようにしました。ログイン ID が他の企業とぶつかったときは、乱数を足して作り直す処理を入れています。

LINE からの申告をそのまま信じない

LINE ミニアプリから送られてくる「この LINE の利用者です」という情報は、そのまま使わず、LINE の確認用の仕組みに問い合わせて本物かどうかを確かめてから、アカウントと結び付けています。最初は登録画面とアカウント連携の一部でこの確かめが足りていなかったため、両方とも同じやり方にそろえました。

外部サービスが不調でも本体を止めない

社内チャットとタスク管理ツールへの通知は、10 秒で打ち切る時間制限と、失敗したときのやり直しを入れ、相手のサービスが遅くても登録の処理そのものは止まらないようにしています。今後使う予定の SMS(ショートメッセージ)の送信処理も先に作り、「同じ番号へは 24 時間に 5 通まで」という送信先の制限を分かりやすい日本語のエラーで返すことや、通信のやり直しで同じ SMS が 2 回送られないよう、1 回ごとの識別用の値を付けることまで用意しました。

テストと点検の仕組み

自動テストの仕組み(Vitest)を入れ、外部サービスとの境目にある処理から順にテストを書きました。有料・無料の出し分け、LINE の確認、SMS の送信、AI による求人票の作成(必須項目が欠けたときのエラーや、生成に失敗したときの扱い)、ログイン ID・パスワードの自動発行などです。あわせて、型の決まりを守らない書き方を警告ではなくエラーとして止めるようにし、ログに出していたパスワードやメールアドレスを消しました。データベースの変更はすべて日付つきの変更ファイル(マイグレーション)として残し、どの順番で今の形になったかを追えるようにしています。

事業の方向転換に合わせて減らす作業もした

サービスの方向が「フリーランス向けの案件の紹介」に絞られたのに合わせて、使わなくなった機能やテーブル、データベースの列を、画面・処理・データベースの順に削除しました。削除の前には影響する範囲を示し、データが消える操作は事前に確認してから行うことをプロジェクトのルールとして決めています。

← プロジェクト一覧へ戻る