feature image

2024年9月19日 | ブログ記事

Web開発初心者4人組が1ヶ月でSNSを作るまで

この記事は夏のブログリレー32日目の記事です。

こんにちは! 24Bのきつねでございます。今年traPで新たに企画された『1-Monthon』という1ヶ月規模のハッカソンにてleaQというtraP部員限定のSNSをチーム開発したので、チームを代表してイベントやプロダクトの紹介をしていこうと思います。

1-Monthon とは

1-Monthon(ワンマンソン)はその名の通り1ヶ月をかけて自由にゲームやサービスを開発するイベントです。traPでは毎年6月と12月に2日間のハッカソンが開催されますが、夏休み前半の1ヶ月をかけてハッカソンをしようという企画が今年新たに立ち上がりました。春ハッカソンでは大岡山キャンパス現地での開発がメインでしたが、今回は時期が夏休みとあって帰省をする部員も多く、また開発が長期に渡るためどのチームも基本的にはオンラインでコミュニケーションを取りながら開発を進めることになりました。

また、1-Monthonでは通常の短期ハッカソンとは異なり多くのチームが一学年、あるいは一人で構成されていました。我々第5班は全員が24B(学士1年)の4人チームで、しかも皆集団開発の経験がほとんどないというかなりの挑戦になりましたが、その環境で全力で開発に取り組んだおかげでそれぞれが大きく成長できたと思います。

1ヶ月がかりの本気の開発とあってとてつもないクオリティの作品の数々で、最終発表会は驚きの連続でした。この後も1-Monthonに関する記事がいくつか投稿されると思いますので、そちらもぜひ見ていってください!

1-Monthon - 東京工業大学デジタル創作同好会traP
『デジタル創作同好会traP』は、東京工業大学で活動するデジタル創作・プログラミング系サークルです。ゲーム制作を中心に、アプリ、音楽(DTM)、グラフィック(イラスト、3Dモデル、ドット絵、動画)などのクリエイティブ活動の他、競技プログラミング(競プロ)やCTFも行っています。

leaQ について

UIと仕様

leaQは、主にスクリーンショットを投稿するための部内SNSです。お気付きの通り、名前は『leak(漏洩)』からとっています。とても物騒ですが、ロゴもUIも至って清潔です。

タイムライン画面

上図はタイムラインの様子です。新たに投稿されたスクリーンショットは全てタイムラインに表示されるようになっています。タイムラインは横スクロールでき、左が新しく、右へ進むほど時系列を遡ることになります。タグの検索結果もほぼ同じ画面構成になっています。

スクリーンショットの投稿時には概要のテキストと複数のタグを自由に登録することができます。traPに存在する7つの班の名称は予め班タグとして特別扱いをしました。具体的にはそれぞれの特色に合わせて色を付け、サイドバーに班タグ検索に飛べるボタンを表示し、アップロード画面で投稿に登録するタグの選択肢にドラフトとして表示しています。これにより、それぞれの班の活動に関連するスクリーンショットの新着投稿をまとめて閲覧しやすくなっています。

アップロード画面

投稿にはスタンプをつけたりコメントを書き込んだりすることができます。スタンプはtraPの既存の部内SNSであるtraQで盛んな文化のひとつであり、一般に提供されているもののほかに部員によって日々新たなおもしろカスタムスタンプが作られていますが、その全てがleaQでも使えるようになっています。また、あとで見返したい投稿をブックマークに登録することができます。

leaQの大きな特徴として、ユーザーの最後の投稿から24時間が経過するとそのユーザーから見たタイムラインの更新が停止し、それ以降の投稿は新たにユーザー自身が投稿を作成するまで閲覧できなくなる仕様になっています。これはBeRealのシステムに着想を得て実装を決めました。詳しくは次章で触れています。

コンセプトと命名

初期のコンセプトは『traP版BeReal』でした。BeRealは即時性によるリアルを追求するSNSとして、日本でも特に若年層の間で社会問題になるほどの広がりを見せています。BeRealの特性をおさらいすると、1日1度ランダムな時刻に一斉に通知が届き、それから2分以内に投稿しなければユーザーの投稿が遅刻として表示されるようになっています。また、ユーザー自身が投稿するまでその日の他ユーザーの投稿を見られないという仕様があり、それがユーザーを虜にする中毒性の根拠になっているとも言われています。

さて、traPは巨大な創作サークルであり、顔よりアイコンを認識し、本名より活動名で呼び合う独特の文化を内包しています。今回の1-Monthonに限らず、部員はとくに必要がなければ自宅でPCやペンタブに向かってサークルの創作や開発を進めることが一般的です。従って、ある意味でtraPにおけるリアルはむしろスクリーンの中にあり、traP版BeRealをコンセプトとするSNSはセルフィーよりスクリーンショットを投稿するものであれば良いだろう、というのがこのSNSの発想の原点です。

とはいえ、このコンセプトを追求することで、単なる流行りに乗ったネタ開発の域を超え、実際にtraPの創作活動を助太刀するサービスへと昇華させられる可能性をも感じました。traPは音楽、イラスト、ゲーム制作、サービス開発、競技プログラミングなど幅広い分野の創作・開発で技術を磨いた先人に溢れています。しかし、成果物こそ盛んに共有すれど、彼らが如何にして素晴らしい作品を作り上げるのか、そのリアルな制作風景の共有は思うほど活発ではないように感じます。そこで、このSNSはスクリーンショットの投稿という形で創作におけるノウハウを共有・蓄積していくためのツールとしてサークルに貢献できるのではないかと考えました。例えばDAWの画面を覗けば使われている外部音源の名前を知ることができますし、ペイントソフトの画面を覗けばブラシやレイヤー分けなどヒントが詰まっています。もちろん、創作中の風景のみならずプレイ中のゲームのスクリーンショットなども含め、その画面に映るリアルの投稿を通じて部員同士の交流や技術の高め合いの契機を生む役割を果たせれば本望と考えました。

ただし、スクリーンショットのSNSを作成するならば当然情報漏洩の危険性を警戒すべきです。PCやスマートフォンのスクリーンショットには我々が思うより多くの情報が隠れており、その中には個人情報やプライバシーに係る情報、あるいは開発や契約に関わる機密情報なども含まれている可能性があります。こうした危険に対する自戒の意を込め、またスクリーンショットに隠された創作技術を(必ずしも意図せず)共有するという側面も踏まえ、本SNSをleaQと命名しました。traP限定のサービス故に一般的なSNSに比べて情報漏洩のリスクは抑えられるとはいえ、創作技術をリークしつつ本当に秘密にしておきたい情報はリークしないように気を付けて使っていただけたら嬉しいですね。

開発の流れ・実装

チーム全体

7/13に1-Monthonの軽い説明会とチーム初顔合わせがありました。この日に4人顔を合わせてサービスの案を色々出し合い、その中で『traP版BeRealを作る』という今回の開発に繋がる案が有力視され、後日Discordで開いた自主ミーティングで正式に決定されました。

7/30からの期末試験期間を経て8/7に夏休みに突入し、それから数日後の8/12に1-Monthonの制作が開始されました。制作開始前の8/10に大岡山駅前のサイゼリヤにて自主的に食事会を開くなどしてチームの親睦を深めました。

8/25に中間発表、9/8に最終発表がありました。各々先に旅行の予定を入れていたり、自動車教習や資格試験があったりと忙しい中でしたが、traQでコミュニケーションをとりつつ自分にできることを進めていきました。

フロントエンド(文責:きつね)

フロントエンドではViteが提供する環境とテンプレートを基礎としてVue.jsによる開発に取り組みました。Vite環境は、最初のテンプレートの時点でビルドが出来てブラウザにサンプルページが映り、ソースコードをいじれば即座に反映されてブラウザから様子を見られるという優れものです。

ちょっとバグっている気はするけど

Vue.js の特性

Webサイトはサイドバーやボタン、入力フォームなどといった部品の集まりと見ることができます。それぞれの部品の配置はHTML、外見はCSS、機能はJavaScriptを書いて指定するのが最もメジャーな方法と言えるでしょう。Vue.jsでは、それぞれの部品(コンポーネント)に対してひとつのVueファイルを用意し、その中にHTMLとCSSとJavaScriptの全てを以下のように書きます。

<template>
  <!-- ここでHTMLを書く  -->
</template>

<script setup>
  <!-- ここでJavaScriptを書く -->
</script>

<style scoped>
  <!-- ここでCSSを書く -->
</scoped>

実際に(記事投稿時点で)leaQに組み込まれているVueコンポーネントから実例をお見せすると、例えば SearchBox.vue は以下のようになっています。

<template>
  <SearchIcon class="icons" width="20px" height="auto" />
  <input type="text" v-model="searchQuery" placeholder="検索" @keyup.enter="onSearch" />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import SearchIcon from './SearchIcon.vue';
const searchQuery = ref('')
const router = useRouter();

function onSearch() {
  if (searchQuery.value.trim() !== '') {
    router.push(`/tags/${encodeURIComponent(searchQuery.value)}`);
  }
};
</script>

<style scoped>
input {
  height: 40px;
  width: 720px;
  margin: 0 10px;
}
</style>

こうして作ったコンポーネントは、より上位のコンポーネントから呼び出したり、上位のコンポーネントに向けて情報を送ったりすることができます。例えば、画面全体のコンポーネント MainView.vue からサイドバーのコンポーネント SideBar.vue を呼び出し、その中ではさらにサイドバーに連なるボタンのコンポーネント SideButton.vue を呼び出す、というような具合です。Vue.jsによる開発では、こうしたVueコンポーネントを組み合わせることでUIを作っていきます。

APIとの接続

Webサービスの開発は主にバックエンド開発とフロントエンド開発に分かれます。一般に、バックエンド開発はデータを処理・保存するサーバー側のシステムを、フロントエンド開発はUIを表示するためにユーザー側のデバイスで実行されるシステムを手掛けます。フロントエンドがバックエンドと正確に情報をやりとりをするためにバックエンド側が用意してくれる「操作」と「結果」の組の一覧をAPIと呼びます。飲食店に例えるなら「注文すべき名前」と「届く料理」の組を記したお品書きがAPIにあたります。

一般的にWeb APIでは POST(送信)GET(受信)DELETE(削除)PATCH(編集)などの限られた方法(HTTPメソッド)を指定してAPIにアクセスします。JavaScriptでは fetch() 関数が使えて、HTTPメソッド・URL・渡したいデータなどを引数に渡して fetch() を実行することでバックエンドとの情報の送受信ができます。ただし、APIの利用に必要な予備処理を毎回書くのは面倒なので、例えば getPostData(id=15) とだけ書けば実行できるように、それぞれの処理を関数にまとめて一発で呼び出せるようにしておきます。これがいわゆるAPIラッパーです。

開発ペース

leaQのGitリポジトリ(traP部内のみ共有)のコミットグラフを見返すと、タグ検索(9/4)、ブックマーク(9/6)、スタンプ(9/6)、無限スクロール(9/7)、コメント(9/7)、アップロード画面(9/8 朝)とフロントエンドのメジャーな機能実装のことごとくが最終発表会の9/8の直前数日になされていることが分かります。UIのデザインは概ね私の担当だったのですが、8/25の中間発表の直前にレイアウトの原型が出来上がったはいいものの、8/26から29にかけて文系教養の集中講義が入っていてほとんど開発に関わることができず、それがようやく終わり投稿ごとのコンポーネントの配置を色々と試していたら気が付けば最終発表が目前に迫っていた、という次第です。バックエンド担当が先に立てておいてくれたAPIという道標を頼りにソースコードの乱雑さに目もくれず全力疾走、今思い返しても冷や汗が出ますね。

右も左も分からない状態から始めるならば、少しずつ知識の貯金を作っていくにつれて開発のスピードは速まっていくものだと思います。ただし、これほどの急加速は健康を害しかねないのであまりおすすめはしません。今回運良く完成まで漕ぎ着けることが出来たのは、バックエンド担当がAPIの整備をほとんど終えてくれていたこと、早いうちにVue.jsの扱いをある程度把握できていたことなどの好条件が重なった結果です。

バックエンド(文責:あきも)

技術選定

バックエンドでは部内で標準言語として広く使われているGoを採用しました。フロントエンドもですが、成果物はNeoShowcase(以下NS)にデプロイしています。

NeoShowcaseとは?
traP部員が自由に使える作品公開プラットフォームです。シンプルなWebサイトならカップ麺を作っている間に公開することができるような手軽さと、上級者向けのカスタマイズ性を兼ね備えたサービスとなっています。NSの開発に関するブログ記事もあるので、興味がある方は読んでみてください。

【NeoShowcase】traPには内製の作品公開プラットフォームがあります
はじめに | 『デジタル創作』同好会traPとは 私たち『デジタル創作同好会traP』は、Webアプリやゲームの制作を中心に活動する、デジタル創作・プログラミングの総合サークルです。 traPについて東京工業大学デジタル創作同好会traPは、東京工業大学・大岡山キャンパスを拠点に活動する創作・プログラミングの総合サークルです。アプリ・ゲームの制作を中心に、音楽(DTM)、グラフィック(イラスト、3DCG、ドット絵、動画)などの創作活動に加え、Webインフラや競技プログラミング・サイバーセキュリティ(CTF)、機械学習(Kaggle)などに関する活動も行っています。 こうした創作活動のほかにも、大学サークル間の交流イベントの主催や、中高生向けプログラミング教室などといった社会貢献活動も行っています。 traPの班 * アルゴリズム(競プロ)班 * グラフィック班 * ゲーム班 * サウンド班 * CTF班 * SysAd班 * Kaggle班 traPには大きく分けてこの7つの班が設けられており、自分の興味に合わせて所属することができます。ここでは各班の活動を簡単に紹介します。よ

データベースはNSがビルトインでサポートするMariaDBを、GoからのDB操作にはGORMを利用しています。

Cloudflare R2の採用

NSのファイルシステムは再起動で初期化されてしまいます。しかし画像の保存に使えるようなオブジェクトストレージは部内サービスにはないため、外部のサービスを利用する必要がありました。どこのサービスを使うかについては迷う暇もなく、しーぴーさん(@cp20)からのアドバイスが降ってきます。

もちろんこれを見て即決したわけではありませんが、当面はタダで使えそうな上にCloudflareなので信頼性も高いということでCloudflare R2を採用しました。R2はAmazon S3互換なので、ローカルでは同じくS3互換のMinIOを利用しています。

認証

SNSを作る上で難しいポイントの一つがアカウント周りだと思います。が、部員はそれぞれ固有のtraQ IDを持っているのでゼロからアカウントの仕組みを作る必要はありませんでした。
更に、NSには部員認証のオプションがあります。これを必須にすることでNSが勝手に認証してリクエストにユーザー名が書かれたヘッダーを付与してくれるため、非常に簡単にログインユーザーを判別することができます。一方で、この認証方法は開発環境のブラウザで使うのには向いていません。ローカルのブラウザでNSの認証を再現するためには

といった方法が考えられます。そもそもNSに頼らずtraQ IDのOAuthを使うという手もあります。一番楽そうなのは「ブラウザ拡張機能でヘッダーを付与する」ですが開発期間中は思いつかず、「自動的にヘッダーを付与するプロキシサーバーを立てる」はサクッとできそうだったのでこの方法を選択しました。

スタンプ機能

前述の通り、leaQの特徴の一つに「traQのスタンプが使える」というものがあります。スタンプの画像自体はtraQ APIで取得できますが、

といった要件を満たすため、現在は起動時にtraQから全てのスタンプの情報を持ってくるという力技を使っています。ただこの方法だと新規作成されたスタンプには再起動するまで対応できないので、別の方法を考えても良いかもしれません。

画像の圧縮

leaQは画像主体のサービスであり、Cloudflare R2の無料枠を利用しているのでファイルサイズは小さい方が良いです。初めはWebPにしてサイズを削減しようと思っていましたが、Pure GoではWebPのエンコードができないため一旦見送りました。代わりにPNGやJPEGのままで気持ち程度の圧縮をしています。が、色数の多いPNG画像ではMB単位になってしまい、Macでのスクショ(影が付いてるやつ)は外側が透過されているのでJPEGにするわけにもいきません。ということで、1-Monthon期間が終わってからは改めてWebPの導入準備を進めています。NSにはリポジトリ内のファイルを見て†良い感じ†にビルドしてくれる設定があるので現在はそれを利用していますが、WebPのエンコードにはlibwebpが必要なので今後は自分で書いたDockerfileを使うことになりそうです。

今後の展望

1-Monthonとしての開発は終了しましたが、leaQはチームメンバーによる自主開発が継続して進められています。先日モバイル版に対応し、この後もコメントへの返信やブックマークの共有などの機能拡充を予定しています。

現在のサークル内での認知度はそれほど高くはないですが、今後leaQのユーザー数を徐々に増加させ、最終的にはtraQに次ぐ部内SNSに仕上げようというささやかな野望があります。叶うかどうかは分かりませんが、ひとつのSNSを開発・運営する経験というのは滅多にない貴重なものですので、出来るところまではやってみたいと思っています。

感想

kitsne

チームリーダーとフロントエンドを担当しました。初顔合わせの時に私が軽いノリで提案した『traP版BeReal』がまさかここまで立派なサービスとして日の目を見るとは思っていなかったので、とても嬉しいです。8月中旬は予定が重なりバックエンドに比較して進捗を生めませんでしたが、さみだれくんの協力もあり1-Monthon当日の発表前までソースコードを睨みつつ頑張った結果なんとか間に合わせることができてホッとしています。この1ヶ月の間にVue.jsやFigmaなど初めてのツールや言語にたくさん触れて急成長できた気がします。思うようにいかず焦慮に苛まれることもありましたが、終わってみればとんでもなく楽しい開発体験でした! チームメイトと1-Monthon運営の皆様に感謝です。

samidare

frontendを担当しました。全体を通しての感想としては、もっと制作に貢献すべきだったなといった感じです。普段ゲーム開発をメインにしていてwebアプリ開発は初めてで、右も左も分からないままスタートしました。一応なろう講習会などでVue.jsの書き方を学んだりしましたが、結局chatgpt君ときつね君に頼りっきりになってしまったと思います(てかなんかVue.jsむずくないか.....?)。認証とかサーバー関連のことは全く分からなくて、メールボックスに来るgitからのissue立てやコミットの通知が来るたびにチームメンバーの優秀さを実感してました。一応今回フロントエンドの基礎などを学んだりしましたが、このまま一切活用せず腐らせるのももったいないので、どこかのタイミングでまたwebアプリ開発に取り組んでみたいと思います。改めて、チームメンバーおよび1-Monthonの運営の皆さん、一ヶ月間ありがとうございました!

mumumu

leaQではbackendを担当しました。
春ハッカソンで少しだけフロントエンドの経験はあったものの、バックエンドは何もわからず右も左もわからない状態から始まりました。
chatgptとcopilotとakimo君に頼りっぱのワンマンソンでした。
とにかくAIに投げて動くものを作ってはakimo君にレビューしてもらってました。
認証とか全然分からないのでakimo君がやってくれてマジ助かりました。
AIを取り上げられたらコードをかける自信がないです笑
初めにどうやってデータを持つか、どういうapiが必要なのかを決めておくべきだったなと思ったことと、適当にAIに投げすぎるとほかの部分とうまくかみ合わなくなるので自分で全部を書けはするようになりたいな~というのが反省点です。
最終発表の日かなりギリギリでしたがなんとか動くものが出来て良かったです!!
楽しかった~

akimo

バックエンドを担当したあきもです。初めて触ったプログラミング言語はGoで、高校生の頃はGoで競プロをしていました(ずっと灰だったけど)。Webアプリの開発経験はないもののよく技術系の記事を読んでいたため、開発未経験の割には知識がある謎の人として参加しました。leaQではmumumu君が書いたコードのレビューをしつつ、NSの認証やデプロイ、Cloudflare R2の導入といった外部サービスに依存する部分を中心に担当しています。
悲しいことに僕は集中講義くらいしか外出の予定がなかったため、リアルが充実している3人を横目にパソコンカタカタしていました……。が、1-Monthonがないと夏休みの進捗がnilになっていたでしょうから参加して良かったです。
反省点はいくつかありますが、一番ひどかったのはNSの認証をローカルで再現するために立てたプロキシサーバーの実装が雑すぎたことでしょうか。度々不具合を起こした挙句、終いには「ローカルでは検証できてないけど本番環境では動くやろ!w」とぶっつけ本番で投稿のテストをすることになってしまいました。

テストで投稿第1号を取ってしまった

1-Monthonの開発期間は1ヶ月とはいえ、準備期間も1ヶ月用意されていたのでこの辺りは事前に考えておくべきだったと反省しています。

おわりに

とても長くなってしまいましたが、ここまで読んでいただきありがとうございました。traP公式ブログは幅広い分野の創作・開発に関する知見の宝庫です。色々と探索していっていただけると嬉しいです! 夏のブログリレー、明日の担当は@o_ER4先輩、@Alt--er先輩、@mehm8128先輩です。お楽しみに!

kitsne icon
この記事を書いた人
kitsne

24B / ゲーム SysAd 時々 サウンド

akimo icon
この記事を書いた人
akimo

24B。いろいろ(CTF / Kaggle / SysAd / アルゴリズム / グラフィック / サウンド)ゆるくやってます

mumumu icon
この記事を書いた人
mumumu

samidare icon
この記事を書いた人
samidare

この記事をシェア

このエントリーをはてなブックマークに追加
共有

関連する記事

2024年9月17日
1か月でゲームを作った #BlueLINE
Komichi icon Komichi
2024年8月21日
【最新版 / 入門】JUCEを使ってVSTプラグインを作ろう!!!!【WebView UI】
kashiwade icon kashiwade
2021年8月12日
CPCTFを支えたWebshell
mazrean icon mazrean
2022年9月26日
競プロしかシラン人間が web アプリ QK Judge を作った話
tqk icon tqk
2022年9月16日
5日でゲームを作った #tararira
Komichi icon Komichi
2024年8月29日
クロスコンパイルRust
H1rono_K icon H1rono_K
記事一覧 タグ一覧 Google アナリティクスについて 特定商取引法に基づく表記