iT邦幫忙

0

從 13 秒到 <1 秒:我用 AI 重寫 Google Apps Script 記帳系統

  • 分享至 

  • xImage
  •  

從 13 秒到 <1 秒:我用 AI 重寫 Google Apps Script 記帳系統

本文僅分享技術心得與效能改進經驗。
核心重點在於如何運用 AI 輔助完成重構與技術選型,純屬開發者個人實驗紀錄。


🚀 專案總覽

  • 舊版問題:GAS + 試算表 → 1 萬筆資料載入要 13 秒
  • 新版改進:Next.js + Supabase → 載入時間 <1 秒,效能提升約 13 倍
  • 開發方式:AI 輔助重構、全程自動化提示
  • 技術棧:Next.js 14、React 19、Supabase (PostgreSQL)、Tailwind CSS
  • 核心心得:試算表不適合作為資料庫,效能瓶頸無法靠程式優化解決

一、舊版痛點:13 秒的等待

過去版本(v2.56)使用 Google Apps Script + Google 試算表 當作資料來源。

每次讀取 1 萬筆交易資料時,都要耗時 13 秒以上。
當切換記帳本時,還得再次等待,導致整體體驗極差。

為什麼會這麼慢?

// v2.56 Code.gs - 查詢交易記錄
function getTransactions(bookId) {
  const sheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName('transactions');
  const data = sheet.getDataRange().getValues(); // 讀取所有資料
  const transactions = [];

  for (let i = 1; i < data.length; i++) {
    const row = data[i];
    if (row[1] === bookId) {
      transactions.push({
        id: row[0],
        bookId: row[1],
        amount: row[2],
        category: row[3],
        description: row[4],
        date: row[5],
        userId: row[6],
      });
    }
  }

  transactions.sort((a, b) => new Date(b.date) - new Date(a.date));
  return transactions;
}

效能瓶頸分析:

  • getDataRange().getValues() 每次都抓整張試算表
  • 線性搜尋 O(n) 時間複雜度
  • 沒有索引、快取或查詢優化
  • 試算表 API I/O 延遲高

實測數據

資料筆數 載入時間
1,000 筆 約 2 秒
5,000 筆 約 6 秒
10,000 筆 約 13 秒
20,000 筆 超過 25 秒

二、GAS 架構的限制

1️⃣ 檔案扁平、難維護

📁 v2.56
├── Code.gs
├── Database.gs
├── Utils.gs
├── Report.gs
└── HTML × 20

沒有資料夾結構,找程式碼要靠關鍵字。

2️⃣ 無 TypeScript

沒有型別檢查,容易出現低級錯誤。

3️⃣ 前端限制

HTML Service 不能使用 React / npm,CSS 與 JS 整合困難,手機版不支援 RWD。

4️⃣ 開發體驗差

  • 只能在瀏覽器編輯
  • 沒有 Git 版本控制
  • 部署要手動發布
  • 無法多人協作

三、重構的轉捩點

當資料量達 1 萬筆時,系統幾乎無法使用。
每次載入都要 13 秒,快取也救不了。

這時候我意識到:

  1. 架構才是問題本質
  2. 試算表不能再當資料庫
  3. 新需求(即時通知、多人協作)難以實作

👉 最終決定重寫系統。


四、新架構選型:Next.js + Supabase

🧩 評估過的選項

方案 優點 缺點 結論
優化 GAS 不需重寫 效能天花板 ❌ 放棄
Firebase 雲端整合佳 NoSQL 不適合關聯資料 ❌ 放棄
MERN Stack 彈性高 成本高、部署麻煩 ❌ 放棄
Next.js + Supabase 現代化、快速上手 需學習新技術 ✅ 採用

💡 為什麼選 Next.js?

  • 可同時處理前端與 API(全端框架)
  • Server Components 提升效能
  • App Router 檔案路由直覺
  • 自動代碼分割、部署簡單

💾 為什麼選 Supabase?

  • 開源、支援 PostgreSQL
  • 適合記帳類型的關聯資料
  • 自動產生 REST API
  • Row Level Security (RLS) 提供資料層級安全控管
  • 免費版已足夠個人專案使用

五、技術實作示範

Supabase 初始化

// lib/supabase/server.ts
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'

export async function createClient() {
  const cookieStore = await cookies()
  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return cookieStore.getAll()
        },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value, options }) =>
            cookieStore.set(name, value, options)
          )
        },
      },
    }
  )
}

前端頁面

// app/transactions/page.tsx
'use client'
import { useEffect, useState } from 'react'
import { createClient } from '@/lib/supabase/client'

export default function TransactionsPage() {
  const [transactions, setTransactions] = useState([])
  const [loading, setLoading] = useState(true)

  useEffect(() => { loadTransactions() }, [])

  async function loadTransactions() {
    const supabase = createClient()
    const { data } = await supabase.from('transactions')
      .select('*')
      .order('date', { ascending: false })
    setTransactions(data || [])
    setLoading(false)
  }

  if (loading) return <div>載入中...</div>

  return (
    <div>
      <h1>交易記錄</h1>
      {transactions.map(t => (
        <div key={t.id}>
          {t.date} - {t.description} - ${t.amount}
        </div>
      ))}
    </div>
  )
}

六、AI 輔助開發心得

這次重構幾乎全程由 AI 參與協助,加速至少 70% 開發時間。

項目 AI 輔助 我的人工作業
資料表設計 Schema 初稿 審查調整
CRUD API 自動生成 測試與修正
前端頁面 元件雛形 美化與邏輯補強
認證 Supabase Auth 整合 加上邀請制
分帳邏輯 提示演算法 驗證與優化

實例:AI 生成 CRUD API

// app/api/transactions/route.ts
import { createClient } from '@/lib/supabase/server'
import { NextResponse } from 'next/server'

export async function GET() {
  const supabase = await createClient()
  const { data: { user } } = await supabase.auth.getUser()
  if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })

  const { data, error } = await supabase
    .from('transactions')
    .select('*')
    .eq('user_id', user.id)
    .order('date', { ascending: false })

  if (error) return NextResponse.json({ error: error.message }, { status: 500 })
  return NextResponse.json(data)
}

七、重構成果

指標 v2.56 (GAS) v3.38 (Next.js + Supabase)
資料載入時間 13 秒 <1 秒
程式結構 扁平 模組化
開發效率 高(AI 協助)
擴充性 高(API + DB)
部署方式 手動 Vercel 自動部署


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言