LoginSignup
3
0

4年前のプログラミング合宿で作成したプロダクトのリプレイス(その1)

Posted at

概要

4年前当時GAOGAOゲートで開発したwebアプリケーションを新たな技術スタックで作り直していきます

そもそもGAOGAOゲートとは

そもそもゲートとは何かを簡単に説明します。
ゲートとは一言で言うとアプリ開発合宿、プログラミング修行です。プログラミングをはじめ、ソフトウェアエンジニアとして活躍するためのスキルを身につけることができるカリキュラムになっています
私は2020年1月に参加して、日本で1ヶ月、ベトナムで1ヶ月プログラミングの基礎学習とオリジナルアプリ開発を行いました。

当時作成したアプリ

そんなGAOGAOゲートで当時開発経験がない状態で作成したのがシンプルな画像投稿アプリです
主な機能は下記の通りです

  1. 画像投稿
  2. チャンネル作成機能
  3. リアルタイム更新

リプレイス

当時作成したアプリを新しく作り直していきたいと思います
今回はリアルタイム更新のロジック部分を作成していきます

as is

以前作成したwebアプリは主に下記技術を使用していました。
簡単にまとめると、見た目はbladeでサーバー側で生成していました。
投稿部分はjsでポーリングをして、一定期間ごとにリクエストを送り、リアルタイムな更新を実現していました

to be

今回は下記を使って新たに作り直していきます

  • supabse
  • TypeScript
  • Next.js
  • React

パッケージのバージョン

主なパッケージのバージョンは下記の通りです

node: 18.18.2
react: 18.2.43
next": 14.0.4
typescript: 5.3.3

PJ作成

下記を実行してPJ作成

npx create-next-app image-upload-app

supabaseの設定

チャンネルがそれぞれあり、各チャンネルに紐づいたPostが存在する構成となっています

Channel用のDB(Postを紐付け)

添付画像のような構成で作成しました

スクリーンショット 2023-12-21 22.10.50.png

Post用のDB

添付画像のような構成で作成しました

スクリーンショット 2023-12-21 22.09.51.png

チャンネルに紐づいたメッセージ取得(初期表示時)

初期表示時はserver componentでチャンネルに紐づいたメッセージを取得しています
そちらのコードが下記となります

import { notFound } from 'next/navigation'
import type { Database } from '../../../database.types'
import Posts from '../components/posts'

type Post = Database['public']['Tables']['posts']['Row']

type PageProps = {
  channelId: string
}

async function fetchPosts(channelId: string) {
  const res = await fetch(
    `${process.env.base_url}/rest/v1/posts?select=*`,
    {
      headers: new Headers({
        apikey: process.env.apikey as string,
      }),
      cache: 'no-store',
    }
  )
  const posts: Post[] = await res.json()
  return posts
}

export default async function channelDetailPage({ channelId }: PageProps) {
  const posts = await fetchPosts(channelId)
  if (!posts) return notFound()

  return (
    <div className="mt-16 p-8">
      <Posts posts={posts} channelId={params.channelId} />
    </div>
  )
}

チャンネルに紐づいたメッセージのリアルタイム更新

クライアントコンポーネント内でsupabaseのリアルタイムリスナーを使用して、postsという変数に保存しています。
以前はポーリングを使用していましたが、今回はsupabaseの仕組みを利用させてもらいました
新規メッセージが投稿された場合(現時点では未実装)はリアルタイムに更新されるようになっています

'use client';
import type { Database } from '../../../database.types'
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
import { useEffect, useState } from 'react'

type Post = Database['public']['Tables']['posts']['Row']

type PageProps = {
  posts: Post[]
  channelId: string
}

export default function Posts({ posts: initialPosts, channelId }: PageProps) {
  const [posts, setPosts] = useState<Post[]>(initialPosts)

  useEffect(() => {
    const supabase = createClientComponentClient<Database>();
    const newPosts = supabase.channel('custom-filter-channel')
    .on(
      'postgres_changes',
      { event: '*', schema: 'public', table: 'posts' },
      (payload) => {
        console.log('Change received!', payload)
        const newPost = payload.new as Post;

        switch(payload.eventType) {
          case 'UPDATE': {
            setPosts((prevPosts) => prevPosts.map(x => x.id === newPost.id ? newPost : x))
            break;
          }

          case 'INSERT': {
            setPosts((prevPosts) => [...prevPosts, newPost])
            break;
          }

          case 'DELETE': {
            const oldPost = payload.old as Post;
            setPosts((prevPost) => prevPost.filter(x => x.id !== oldPost.id))
            break;
          }
        }
      }
    )
    .subscribe()

    return () => {
      newPosts.unsubscribe()
    }
  }, [])

  return (
    <div className="mt-16 p-8">
      {posts.map(x => (
        <div key={x.id}>
          <p>{x.text}</p>
          {x.image ? <img src={x.image} alt=""></img> : null}
          <p>{x.created_at}</p>
        </div>
      ))}
    </div>
  )
}

デモ

DBを直接いじって試したところ、リアルタイムに更新できています

output.gif

まとめ

本記事ではリアルタイム更新の部分を作成したので、下記の機能を実装できたら、その2以降を書いていきます

  1. 画像投稿
  2. チャンネル作成機能
  3. 認証部分
  4. 全体的なUIの実装
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0