どうも!プログラミングの沼にどっぷり浸かっているエンジニアです。 今回は「毎月の給料計算、マジでめんどくさい...」と遠い目をしていた自分を救うべく(あわよくば「すげー!」って言われたい一心で)、「アルバイト給料管理システム」 を爆誕させました。
手作業での管理?Excel?そんなのもう古い!これからはWebアプリの時代ですよ社長! というわけで、開発中に起きた涙あり笑いありのトラブルと、こだわりの技術スタックについて、備忘録も兼ねてブログに残しておきます。
せっかく作るなら、モダンでイケてる技術を使いたいですよね。「流行ってるから」という理由で選んだものもありますが、結果的に大正解でした。
まずは環境構築。「CI/CDパイプライン?なんかカッコいい響きだ」と思って、最初からGithub ActionsとかGCPのプロジェクト設定をガチガチにやりました。 ここでDockerの設定に手こずり、3日くらい溶かしたのは内緒です。
従業員登録とか勤怠入力とか、地味な画面を作っていきます。 「時給って途中で変わるじゃん?」ということに気づき、履歴管理機能を実装した自分を褒めてあげたい。ここでDB設計の重要さを痛感しました。
このシステムの肝です。「15分単位で切り捨て?」「深夜割増?」など、世の中の給料計算がどれだけ複雑かを思い知らされました。
でも、ボタン一つで「計算完了!」って出た時の快感はヤバいです。脳汁出ました。
あと、jsPDFを使ってPDFを生成するところは、座標指定との戦いでした。「あと1ピクセル右...いや左...」みたいな職人芸をしてました。
「個人情報漏洩とか絶対やだ」と思ったので、監査ログ(誰が何をしたか記録するやつ)を実装しました。 あと、本番環境へのデプロイ。これが一番緊張する瞬間。
開発中、何度もPCを窓から投げ捨てそうになりましたが、なんとか乗り越えました。
「v4出たらしいぜ!使うしかねぇ!」と意気揚々と導入したら、設定周りが全然動かない。
エラーログと睨めっこすること数時間...。
解決策: 素直にv3に戻しました。枯れた技術(安定版)を使うの大事。マジで。
安定稼働のために書いたv3用の設定がこちら。シンプル・イズ・ベスト。
// tailwind.config.ts
import type { Config } from "tailwindcss";
const config: Config = {
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}", // ここをちゃんと書かないとスタイルが当たらない罠
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
background: "var(--background)",
foreground: "var(--foreground)",
},
},
},
plugins: [],
};
export default config;
ローカル(M1 Mac)でビルドしたDockerイメージをCloud Runにデプロイしたら、「Exec format error」とか言われて起動しない。
なんで!?と思ったら、M1チップはARMアーキテクチャ、Cloud RunはAMD64アーキテクチャだったんですね...。
解決策: buildxを使って、「AMD64向けにビルドしてね!」と命令する必要がありました。
# これを忘れるとCloud Runくんが食べてくれません
docker buildx build --platform linux/amd64 \
-t asia-northeast1-docker.pkg.dev/kamoit-system/payroll-app/payroll-app:latest \
--push .
「誰かが勝手にデータ消したらどうすんの?」という恐怖心から、操作ログを残すことにしました。 Prismaを使えば意外とサクッと書けました。
// lib/audit.ts
// 悪いことしたらバレるよ?という機能
export async function createAuditLog({
userId,
action,
tableName,
recordId,
oldValues = null, // 変更前の値も残す執念深さ
newValues = null,
ipAddress = null,
}: CreateAuditLogParams) {
try {
await prisma.auditLog.create({
data: {
userId,
action,
tableName,
recordId,
oldValues: (oldValues as Prisma.InputJsonValue) || Prisma.JsonNull,
newValues: (newValues as Prisma.InputJsonValue) || Prisma.JsonNull,
ipAddress,
},
});
} catch (error) {
console.error("ログ保存失敗したけど、メイン処理は止めないよ:", error);
}
}
とりあえず動くものはできましたが、まだまだやりたいことはあります。
今回の開発で、Next.jsとGCPの便利さに感動しつつ、「動かないコードはただの文字の羅列」 という真理に到達しました。 エラーと戦い続けた日々でしたが、動いた時の感動があるからプログラミングはやめられませんね。
もし同じようなシステムを作ろうとしている人がいたら、僕の屍(失敗談)を越えていってください! それでは、また次の開発で会いましょう👋
可茂IT塾ではFlutter/Reactのインターンを募集しています!可茂IT塾のエンジニアの判断で、一定以上のスキルをを習得した方には有給でのインターンも受け入れています。
Read More可茂IT塾ではFlutter/Reactのインターンを募集しています!可茂IT塾のエンジニアの判断で、一定以上のスキルをを習得した方には有給でのインターンも受け入れています。
Read More