👻

ハッカソン @TwoGateDev Camp 2023 Summer に参加した話

2023/09/05に公開

はじめに

8月23日から8月29日(土日を除く)5日間 のハッカソンに参加してきました!💪

https://x.com/twogate_devcamp/status/1698972201922482453?s=46&t=Np9LdrYQGYxhAXUz99WGug

参加したハッカソン

TwoGateDev Camp 2023 Summer は現役の高専生か高専卒の学生が対象になるハッカソン
株式会社 TwoGate には多数の高専卒のプログラマ-が在籍している

今年のテーマは 「 地元の魅力を発信する LINEミニアプリ開発 」  
基本 2人1チームで構成され、メンバーの地元の魅力を発信するアプリの開発を目指しました。


開発したもの

私の地元である笠間市の魅力をアピールする公式アカウントを作りました!
私たちのグループは地元の魅力を伝えることはもちろんですが、ユーザーにいかに地元に来てもらって笠間市の笠間稲荷神社周辺を丸ごと楽しんでもらうことを目的としました。

タイムテーブル

  • オンライン期間
    • 一日目 (開発)
    • 二日目 (開発)
    • 三日目 (開発)

ここで土日を挟む

  • オフライン期間
    • 四日目 (開発 + プレゼン + 夕食会)
    • 五日目 (結果発表 + 座談会)

開発の流れ

アイデアだし

まずは、アイディア出しを行いました。

  • ターゲットユーザーは誰なのか
  • 利用シーンはどこなのか
  • エントリーポイントをどうするのか
  • コンセプトはなにか

的な感じで Notion でまとめながら行いました。


機能実装

次に、どのような機能を実装するのかを決めました。

要件定義、コア機能の選定、優先順位付けを行いました。

一応設計を載せときます。

要件定義は以下の記事を参考にして行いました。
例を出しながら解説されているためわかりやすかったです。

https://zenn.dev/sutamac/articles/351cb3c7ea66ba


開発

次に、開発をしました。

LINE Messaging API を利用したことがなかったので、最初は苦労しました。
リッチメニューの作り方やタッチアクションの割り当てなど、少し直感的でない方法で行うので苦労しました。

Messaging API を利用した LINEBot の開発には、Next を利用し、Hosting Service には Vercel を利用しました。
デプロイや preview 環境での実行確認などが簡単に行えるのでおすすめです。

スタンプラリー機能の実装のために LIFF を利用し、実装は Next.jsで行いました。

LINE の開発にも普段から利用している Next を利用できたので、コマンドやビルドの設定などには苦労したものの開発は比較的に順調に行えました。


デザインの苦労

一番苦労したのはデザイン部分です。

LINE には Flex Message[1] というものがあります。
これを利用すれば幅の広いカードのデザインを行えますが、これがかなり馴染めませんでした。
しかもローカルで開発する時に、記述して、スマホで確認して のこの2工程にかなりの時間がかかります。
エラーになった時に、該当箇所を探すのにかなり時間がかかる時もありました。(TSがより好きになる)
これがものすごくストレスでした。 ホットリロードなどを行えるようにコマンドを充足できるスキルがあれば。。。

リッチメニュー


実際に作ったリッチメニュー

お店の情報カードのサンプル画像


実際に作ったお店の情報カード

LINEのリッチテキストはjson形式で記述します。
これをなるべく再利用できるコンポーネントとして tsx形式 で記述しました。
少し記述を間違えると、 POSTリクエストでエラーが発生します。
間違えている箇所がなかなか判明しないためかなり苦労しました。
以下に お店の情報のカードのコードの示しますが、長くなっているのでみなくても大丈夫ですw

サンプルコード
import { FlexBox, FlexBubble, FlexButton } from '@line/bot-sdk';

export type ShopCardType = {
  thumbnailURL?: string;
  name?: string;
  description?: string;
  openingHours?: string;
  regularHoliday?: string;
  location?: string;
  detailButtonAction: ButtonAction;
};

const generateShopCard = ({
  thumbnailURL = 'https:www.sample/sample.jpg',
  name,
  description,
  openingHours,
  regularHoliday,
  location,
  detailButtonAction,
}: ShopCardType): FlexBubble => {
  console.log('thumbnailURL', thumbnailURL);
  return {
    type: 'bubble',
    body: {
      type: 'box',
      layout: 'vertical',
      contents: [
        ShopThumbnail(thumbnailURL),
        {
          type: 'box',
          layout: 'vertical',
          contents: [
            {
              type: 'box',
              layout: 'vertical',
              contents: [
                ShopName(name),
                ShopDescription(description),
                ShopOpeningHours(openingHours),
                ShopRegularHoliday(regularHoliday),
              ],
              paddingStart: '18px',
              paddingEnd: '18px',
              paddingBottom: '30px',
            },
            {
              type: 'box',
              layout: 'vertical',
              contents: [],
              borderColor: '#ffffff',
              width: '100%',
              borderWidth: '1px',
            },
            {
              type: 'box',
              layout: 'horizontal',
              contents: [
                {
                  type: 'box',
                  layout: 'vertical',
                  contents: [DetailButton(detailButtonAction)],
                },
                {
                  type: 'box',
                  layout: 'vertical',
                  contents: [],
                  height: '50px',
                  width: '1px',
                  borderColor: '#ffffff',
                  borderWidth: '1px',
                  backgroundColor: '#ffffff',
                },
                ShopLocation(location),
              ],
              width: '100%',
              height: '30%',
            },
          ],
          position: 'absolute',
          offsetBottom: '0px',
          offsetStart: '0px',
          offsetEnd: '0px',
          backgroundColor: '#03303Acc',
          paddingTop: '20px',
        },
        {
          type: 'box',
          layout: 'vertical',
          contents: [
            {
              type: 'text',
              text: 'お土産',
              color: '#ffffff',
              align: 'center',
              size: 'xs',
              offsetTop: '3px',
            },
          ],
          position: 'absolute',
          cornerRadius: '20px',
          offsetTop: '18px',
          backgroundColor: '#ff334b',
          offsetStart: '18px',
          height: '25px',
          width: '53px',
        },
      ],
      paddingAll: '0px',
    },
  } as FlexBubble;
};

export default generateShopCard;

... 省略

プレゼン準備

最後に、プレゼン準備です。

実際に作ったスライドを載せときます!

https://www.canva.com/design/DAFtlduLAbk/p6mr7QOumo5Yi_JRX_Evqw/view?utm_content=DAFtlduLAbk&utm_campaign=designshare&utm_medium=link&utm_source=publishsharelink


感想

一番に来るのは悔しさです。
なぜなら、優勝することができなかったためです。
私はこの去年もこのイベントに参加して優勝していたため、今年も優勝することを目標に頑張っていました。
しかし、私たちのプロダクトは 地元の魅力を伝えるもの としては弱く、優勝を逃してしまいました。
アイディアや機能設計は私が行っていたのですが、私の考えがずれていたと反省しています。私たちのプロダクトは、地元の魅力を伝えることを一番にしていません。
私の地元にある 「 笠間稲荷神社 」 にはすでにきていますが、その周辺のお店には人が流れないという地元の課題がありました。
自らの地元の課題に向き合った結果、魅力を伝えるだけでは集客には繋がらないのではないか、だったら、魅力も伝えることも重要だが実際に足を運んでもらい、周辺お店にも目を向けさせる施策を一番に考えるべきだとなってしまいました。
この考えがテーマとずれてしまったと思っています。
それに、機能やアプリの体験の面でも、ユーザーが視覚的に楽しめるもの、体験的に楽しめるものを考えられなかったなと思っています。ユーザーの目線に立って考えられていなかったなと思っています。😭


ここまでは反省です。
これでは暗いまま終わってしまうので、次は明るいことを書きますね!


感想 明るい感想 😁

まずは、すごく楽しかったです!
このイベントは人と話す機会がたくさん持てました!
オンライン期間の晩御飯会でボイスチャット上で話していたため、現地で話す時にハードルが高くなく、会場で晩御飯や、イベント後の座談会でとても楽しく会話ができました。
技術系の会話もしますが、全国から人が集まっていたこともありほとんどが地元の話をしてました。
九州はラーメンが。。。五平餅は愛知県の。。。などなど自分の知らない地域のことがたくさん知れました。
私はオンラインでの会話の方が苦手としているので、オフラインでの交流が生まれるこのようなハッカソンはとても自分に合っていて楽しかったです!
しかし、一つ言うなら私は見た目が怖いと言われるので、高専一年生に圧をかけていなかったか不安です。。w

最後に

株式会社 TwoGate さんから最後に参加賞をいただきました!
なんと 技術書一冊 (5000円まで) 太っ腹!🥰

私が選んだ技術書は届いたら追記します!

会社さんの雰囲気、イベントの雰囲気含めとても良いハッカソンなので、参加できる方は参加してみてください!!

脚注
  1. Flex Message https://developers.line.biz/ja/docs/messaging-api/using-flex-messages/ ↩︎

Discussion