Google AntigravityとGET SHIT DONEとGemini 3 Flashで開発してみる 1

目的

最近、AIの利用料金が上がったり、利用制限がきつくなったりしている。なるべく低コストで実行したい。トークン毎の単価が安いGemini 3 Flashでどこまで出来るのかテストしてみる。

環境

Windows11→後でLinuxに変更
nodejs、git導入済み

準備

GSDでは自動的にGitコミットを行い履歴を管理することが出来る。そのままだと不要なファイルまでコミットしていしまうため、フォルダを作成し、.gitignoreを最初に配置する。.gitignoreファイルはGET SHIT DONEのレポジトリのものをコピーした。

GSDのインストール

アプリ作成予定のフォルダで下記のコマンドを実行する。

npx get-shit-done-cc@latest

オプション選択は下記の通り。

2) Antigravity
2) Local (./.agent) – this project only

実行

上記のフォルダをAntigravityで開き、Antigravity上のチャットウィンドウで下記を実行する。

/gsd-new-project

What do you want to build? Tell me about your idea, the problem it solves, or even just a rough concept. I'll help you flesh it out.

下記の通り日本語で書いてみる。

Spring Boot 4.0.6とReact 19.2を使って、タスク管理システムを作成したい。
That sounds like a powerful stack for a task management system. React 19 and Spring Boot 4 (anticipating the next major release) will provide a very modern foundation.

To help me shape the project correctly, could you tell me more about the vision?

1. **Who is this for?** Is it a personal tool for you, or something designed for teams to collaborate?
2. **What's the "killer feature"?** Besides basic tasks, is there something specific you're looking for (e.g., AI assistance, complex recurring patterns, specific integrations)?
3. **What does "done" look like for the first version?**

I've also prepared some specific directions we could take:

- **Collaboration**: Focus on multi-user support, comments, and real-time updates.
- **Productivity**: Focus on tagging, priorities, deadlines, and dashboard analytics.
- **Simplicity**: A clean, distraction-free "just get it done" interface.
- **Something else**: I'd like to describe my vision in detail.
個人向けのツール。タスクの登録、更新、削除、一覧表示できる状態を最初のゴールとしたい。
シンプルで集中して作業できるインターフェースに重点を置く。
Got it. A minimalist, personal task manager focusing on pure CRUD first.

Regarding the "Simple and Focus-oriented" interface, what does that mean to you in practice?

- **List-Centric**: A single, clean vertical list where you can quickly type and add tasks.
- **Kanban-Lite**: Simple "To Do", "Doing", "Done" columns to see flow at a glance.
- **Focus Mode**: A view that highlights only the "Current Task" to minimize distractions.
- **Minimalist Grid**: A dashboard that shows tasks in a clean grid format.

Also, for the **v1 tasks**, what information should each task hold?
- Just a Title
- Title + Description
- Title + Priority + Due Date
- Title + Status (Done/Not Done)

I'm starting to see the shape of the project. Once we settle these, I can create the `PROJECT.md`.
タスクの流れを一目で把握できる、「ToDo」「進行中」「完了」のシンプルな列。
I think I understand what you're after: a clean, 3-column Kanban-style task manager (ToDo, In Progress, Done) built with the latest Spring Boot and React.

Ready to create `PROJECT.md`?

- **Create PROJECT.md**: Let's move forward
- **Keep exploring**: I want to share more / ask me more
PROJECT.mdを作成してください。
`PROJECT.md` is initialized! Now, let's configure your workflow preferences.

Currently, your `.gitignore` excludes the `.planning/` directory.

I have some recommended settings for our work together:

- **Mode**: **YOLO** (Auto-approve execution) or **Interactive** (I'll ask for approval at each step).
- **Git Tracking**: Should we track planning documents (like `PROJECT.md`, `ROADMAP.md`) in Git? (I'll need to remove `.planning/` from your `.gitignore` if so).
- **Execution**: Run plans in **Parallel** (Faster) or **Sequential** (One by one).
- **AI Agents**: Should I use additional agents for **Research**, **Plan Checking**, and **Verification**? (Improves quality but uses more tokens).

How would you like to proceed?

- **Use Recommended Defaults**: YOLO, track planning docs, parallel execution, all agents enabled.
- **Modify Settings**: Let's go through them one by one.
- **Configure Fresh**: Walk through all options.

PROJECT.md

# Simple Kanban Task Manager

## What This Is

A minimalist, personal task management system that helps users stay focused and organized. It features a simple 3-column Kanban board to visualize the flow of tasks from "To Do" to "In Progress" and finally "Done."

## Core Value

A simple and focus-oriented interface that provides a clear overview of task progress at a glance.

## Requirements

### Validated

(None yet — ship to validate)

### Active

- [ ] Task Dashboard: Display 3 columns ("ToDo", "進行中", "完了") for task visualization.
- [ ] Task Creation: Register new tasks into the "ToDo" column.
- [ ] Task Management: Update task details and move tasks between columns.
- [ ] Task Deletion: Remove tasks that are no longer needed.
- [ ] Single User focus: Designed for individual productivity without complex multi-user overhead.

### Out of Scope

- Multi-user collaboration — Focus is on personal use for v1.
- Complex task details (attachments, subtasks) — Keeping the interface minimalist.
- Third-party integrations (Google Calendar, etc.) — Maintaining a self-contained system.

## Context

- The user wants a modern, cutting-edge foundation using Spring Boot 4.0.6 and React 19.2.
- The primary goal is to have a functional CRUD application with a clean UI that minimizes distractions.

## Constraints

- **Tech Stack**: Spring Boot 4.0.6 — User specified version (anticipating latest features).
- **Tech Stack**: React 19.2 — User specified version for modern UI development.
- **Timeline**: v1 goal is basic CRUD functionality in a Kanban layout.

## Key Decisions

| Decision | Rationale | Outcome |
|----------|-----------|---------|
| Kanban Layout | User requested "ToDo", "進行中", "完了" columns for one-glance tracking. | — Pending |
| Minimalist UI | Focus on reducing distractions for personal productivity. | — Pending |

## Evolution

This document evolves at phase transitions and milestone boundaries.

**After each phase transition** (via `/gsd-transition`):
1. Requirements invalidated? → Move to Out of Scope with reason
2. Requirements validated? → Move to Validated with phase reference
3. New requirements emerged? → Add to Active
4. Decisions to log? → Add to Key Decisions
5. "What This Is" still accurate? → Update if drifted

**After each milestone** (via `/gsd-complete-milestone`):
1. Full review of all sections
2. Core Value check — still the right priority?
3. Audit Out of Scope — reasons still valid?
4. Update Context with current state

---
*Last updated: 2026-05-02 after initialization*

まとめ

ここまでは問題なさそう。次回は「/gsd-discuss-phase 1」に進む。

GET SHIT DONEを既存プロジェクトで実行してみる

GET SHIT DONEの概要

この言葉の本質は、単なる「忙しさ」ではなく「完了(Done)」に全神経を集中させることです。

  • 完璧主義の排除: 80%の出来でも良いから、まずは終わらせて世に出す。
  • スピード重視: 議論や計画に時間をかけすぎるのを嫌い、即座に手を動かす。
  • 結果への執着: 「プロセス」ではなく「何が成し遂げられたか」という結果だけで自分を評価する。

インストール

プロジェクトのフォルダに移動して、下記コマンドを実行し、最新版をインストールする。

npx get-shit-done-cc@latest

2を選択。

2を選択。

実行

下記コマンドを実行し、既存のコードを読み込ませる。

/gsd-map-codebase

下記コマンドを実行し、マイルストーンを作成する。

/gsd-new-milestone

実行結果

It looks like .planning/PROJECT.md doesn't exist yet (we recently mapped the codebase but haven't formally initialized the project yet), but we can configure this as our first milestone.

What do you want to build next in this milestone? (e.g. Any specific new features, enhancements, or bug fixes?)

上記のような質問が帰って来たため、以下の通り指示をする。

今後の機能拡張に備えて修正しておいた方が良い箇所はありますか?

最終的な出力

先ほどのコードベースの調査(gsd-map-codebase)の結果を踏まえると、今後の機能拡張をスムーズかつ安全に進めるために、現在の段階で整備・修正しておきたい箇所(技術的負債や準備不足の領域)がいくつか見受けられます。

次回のマイルストーン(機能追加)に着手する前に、あるいはその最初のフェーズとして、以下の点を解消しておくことを強くお勧めします。

1. データベースのマイグレーション管理(最優先)

  • 現状: バックエンドでは backend/sql/create_tables.sql を利用してDBの初期化を行っています(Dockerの docker-entrypoint-initdb.d にもマウントされています)。
  • 課題: 今後新しい機能を追加する際に「テーブルに新しいカラムを追加する」「既存のデータ構造を変更する」といった変更(ALTER)が発生しますが、現状の仕組みではスキーマのバージョン管理ができません。
  • 対策: FlywayLiquibase といったマイグレーションツールを Spring Boot に導入し、SQLスクリプトをバージョン付き(例: V1__init.sql, V2__add_user_columns.sql)で管理できる体制に変更することをお勧めします。

2. フロントエンドのテスト環境の構築

  • 現状: バックエンド(Spring Boot)には JUnit を用いたテスト機構が含まれていますが、フロントエンドの package.json を見る限り、自動テストフレームワーク(Vitest や Jest、React Testing Library)が設定されていません。
  • 課題: 今後 UI や状態管理(React Queryなど)が複雑化していくと、デグレ(既存機能の予期せぬ破壊)を手動で防ぐのが困難になります。
  • 対策: VitestReact Testing Library のベース環境だけでも構築しておき、今後作成する重要なコンポーネント(フォームなど)に対して単体テストを書ける土台を用意しておくべきです。

3. フロントエンドの API クライアント層の整備

  • 現状: frontend/src/api/ の下には axios.js(設定ファイルのみ)しか存在していません。
  • 課題: エンドポイントが増えるにつれて、各コンポーネント内で直接 axios.get('/api/xxx') のように記述すると、将来的な URL の変更や、型の不整合を管理しきれなくなります。
  • 対策: バックエンドの OpenAPI (Swagger) からフロントエンドの API クライアント(と TypeScript 型定義相当の Zod スキーマなど)を自動生成するツール(例: Orval など)を導入するか、少なくとも src/api/ パッケージ以下にエンドポイントごとの関数の切り出しと、エラーの共通ハンドリング(Axios Interceptorsでの共通トースト通知など)を整備しておくべきです。

4. 認証・セキュリティ情報などのハードコード排除の確認

  • 現状: docker-compose.yml 内で一部の設定(${DB_PASSWORD} など)を変数化していますが、開発中の暫定値などに依存している可能性があります。
  • 対策: Local用・Production用等に向けた Spring の Profile (application-{profile}.properties) や .env ファイルでの環境変数管理が隅々まで行き届いているか点検しておくと、後々デプロイする時に慌てずに済みます。

▶ 次のアクションの提案

もしよろしければ、このまま次期マイルストーンを作成し、
「機能拡張を始めるための技術基盤の整備(インフラ・テスト・API設計基盤)」 を最初のフェーズ (Phase 1) としてロードマップに組み込むことができます。

この流れで /gsd-new-milestone による次期マイルストーンの策定に進みますか? もしくは先述の課題のうち、特にどれか一つを先に修正したい領域はありますか?

さらに下記の通り指示をする。

2. フロントエンドのテスト環境の構築のみを次期マイルストーンとしたいです。

フロントエンドのテスト環境構築をマイルストーン v1.0 として進めるための実装計画を implementation_plan.md に作成しました(エディタまたはコンソール上で確認できるはずです)。

具体的には、プロジェクト管理としてGSDファイル (PROJECT.md, ROADMAP.md など) の初期化を行うと同時に、フロントエンドでの VitestReact Testing Library の設定作業などを行います。

内容をご確認いただき、問題なければこの計画に沿ってインストールやファイル作成の作業を進めさせていただきます。「承認(または進めてOK)」とお知らせください!

結果

やることが明確になって良い。

Google Antigravityすごい!

論より証拠ということで、Antigravityで作成したプログラムをGithub公開した。

プログラムを自分で書いたり、テストデータを作成したりは一切していない。Agentへの指示のみでここまで出来た。

まとめ

  • AIに指示を出すときにある程度のシステム知識は必要。あればあるほど良い。
  • AIの間違いを指摘する程度の知識は必要。
  • ループしてAIの作業が終わらないときがある。
  • AIの知識が古い。数か月前のバージョンを指定される。勝手にバージョンを古いものに戻されたりする。古いバージョンは脆弱性があったりするのでWebアプリ開発では特に注意が必要。
  • 楽だけど早くはない。
  • なぜそのような処理になっているのかAIに回答を求めること出来るため、プログラムを勉強するには良い。

Novel Search System

A full-stack web application for managing and searching novels, built with modern technologies including Spring Boot and React.

Overview

This project is a comprehensive system designed to handle novel data, authors, and user authentication. It features a robust backend API and a responsive, internationalized frontend interface.

Tech Stack

Backend

  • Java: 25
  • Framework: Spring Boot 4
  • Database: MariaDB
  • Security: Spring Security
  • Tools: Maven, Lombok

Frontend

  • Framework: React 19 (Vite)
  • UI Library: Material UI (MUI) v7
  • State Management: TanStack Query (React Query) v5
  • Routing: React Router
  • Forms: React Hook Form + Zod
  • Internationalization: i18next
  • HTTP Client: Axios

Features

  • User Authentication: Secure login and registration system.
  • Novel Management: Create, read, update, and delete novels.
  • Author Management: Manage author profiles and link them to novels.
  • Internationalization (i18n): Full support for English and Japanese languages.
  • Responsive Design: Modern UI adapted for various screen sizes.

Getting Started

Prerequisites

  • Java JDK 25
  • Node.js (Latest LTS recommended)
  • MariaDB Server

Backend Setup

  1. Navigate to the backend directory: cd backend
  2. Configure the database connection in src/main/resources/application.properties if necessary (defaults to localhost:3306).
  3. Build and run the application: ./mvnw spring-boot:run The backend server will start on http://localhost:8080.

Frontend Setup

  1. Navigate to the frontend directory: cd frontend
  2. Install dependencies: npm install
  3. Start the development server: npm run dev The frontend application will be accessible at http://localhost:5173.

Logging

The application is configured to output logs to the logs/ directory:

  • app.log: General application logs.
  • error.log: Error-level logs.
  • debug.log: Debug-level logs for the application package.
  • sql.log: SQL statements and Hibernate logs.

License

MIT

Xのタイムラインから誹謗中傷や攻撃的な投稿を非表示にする

生成AIに「最強のミュートワードリスト」を作らせる(スマホ・PC共通)

「Xの標準機能(ミュート)」と「ChatGPTなどのAI」を組み合わせる、現実的で効果が高い方法です。

手順:

  1. AI(ChatGPTやGemini)に指示する: 誹謗中傷によく使われる言葉、不快な言い回し、暴言のパターンをAIに列挙させます。

    プロンプト例: 「X(Twitter)のタイムラインで、他人を攻撃したり誹謗中傷したりする投稿を見たくありません。そのような投稿によく含まれる単語、フレーズ、隠語を50個リストアップしてください。カンマ区切りで出力してください。」
  2. Xの設定にコピー&ペースト: 出力された単語リストを、Xの「設定とプライバシー」>「プライバシーと安全」>「ミュートとブロック」>「ミュートするキーワード」に登録します。
    • メリット: 無料で、スマホアプリにも適用され、確実に消えます。
    • デメリット: 定期的にリストを更新する必要があります。

    その他の設定項目

    対象を「フォローしていないアカウント」にすると良いでしょう。

    Dockerイメージを軽くする「マルチステージビルド」について

    前回の記事では、複数のコンテナを操る docker-compose.yml の書き方を紹介した。今回は、そのコンテナの中身を定義する Dockerfile について書く。

    今回作成したバックエンド(Java/Spring Boot)とフロントエンド(React/Node.js)のDockerfileには、「マルチステージビルド」と使っている。

    これを使うと、「イメージサイズが小さくなり、セキュリティも向上する」

    バックエンド編:Java (Spring Boot)

    Javaアプリを動かすには、ビルドツールの「Maven」や「Gradle」が必要だが、実行時にこれらは不要となる。そこで、ビルドする段階と、実行する段階を分ける。

    # --- Stage 1: ビルド環境 ---
    FROM maven:3.9-eclipse-temurin-25 AS build
    WORKDIR /app
    COPY pom.xml .
    COPY src ./src
    # テストはCIでやる前提でスキップし、ビルド時間を短縮
    RUN mvn clean package -DskipTests
    
    # --- Stage 2: 実行環境 ---
    FROM eclipse-temurin:25-jre
    WORKDIR /app
    # ビルド環境(Stage 1)で作った成果物(jar)だけをコピー
    COPY --from=build /app/target/*.jar app.jar
    EXPOSE 8080
    ENTRYPOINT ["java", "-jar", "app.jar"]

    • AS build と COPY –from=build: 最初の FROM で「build」という名前をつけ、そこで mvn package して jarファイルを作る。2つ目の FROM が本番用イメージ。ここには Maven もソースコードも含まれない。あるのは 「JRE(Java実行環境)」と「jarファイル」だけ。 これにより、数百MB単位で容量を節約できる。
    • eclipse-temurin:25-jre: JDK(開発キット)ではなく、JRE(実行環境)を選んでいる。コンパイラなどが含まれないため、攻撃者に悪用されるリスクが減る。

    フロントエンド編:Node.js (React/Vueなど)

    フロントエンド開発では npm run dev サーバーを使うが、本番環境でNode.jsサーバーをそのまま動かすのはパフォーマンス的に最適ではない。 ビルドした静的ファイル(HTML/CSS/JS)を、高速なWebサーバーである Nginx に配信させる。

    # --- Stage 1: ビルド環境 ---
    FROM node:24-alpine AS build
    WORKDIR /app
    COPY package.json package-lock.json ./
    # npm install より高速で、lockファイルに忠実な ci を使用
    RUN npm ci
    COPY . .
    RUN npm run build
    
    # --- Stage 2: 配信環境 (Nginx) ---
    FROM nginx:alpine
    # ビルド成果物(dist)をNginxの公開ディレクトリに配置
    COPY --from=build /app/dist /usr/share/nginx/html
    # SPA用の設定ファイルをコピー
    COPY nginx.conf /etc/nginx/conf.d/default.conf
    EXPOSE 80
    CMD ["nginx", "-g", "daemon off;"]

    • Node.js はビルド時のみ使用: Reactなどのコードはブラウザで動くJavaScriptに変換(トランスパイル)する必要がある。これには Node.js が必要だが、変換が終われば Node.js 自体は不要となる。 最終的なイメージは nginx:alpine ベースになるため、サイズはわずか数十MBに収まる。
    • npm ci の採用: npm install ではなく npm ci を使っている。これは package-lock.json を厳密に守ってインストールを行うコマンドで、CI/CDやDockerビルドでの再現性を保証する。
    • カスタムNginx設定: ReactのようなSPA(シングルページアプリケーション)では、どのURLにアクセスしても index.html を返す設定が必要になるため、デフォルト設定を上書きしている。

    まとめ

    今回作成した2つのDockerfileに共通するのは、「作る道具(Maven/Node)」と「動かす場所(JRE/Nginx)」を明確に分けている点となる。

    Docker Composeで、Webアプリ開発環境を作る

    今回は、典型的な「フロントエンド(React等) + バックエンド(Spring/Node等) + データベース(MariaDB)」の構成を例に docker-compose.yml を書いた。

    今回の構成

    3つのコンテナ(Service)が専用のネットワーク内で連携し、データはボリュームによって永続化される構成。

    完成した docker-compose.yml

    以下が今回解説する設定ファイル。例として小説検索システム(Novel Search System)のようなアプリケーションを想定している。

    services:
      backend:
        build: ./backend
        container_name: novel-backend
        ports:
          - "8080:8080"
        environment:
          - DB_URL=jdbc:mariadb://novel-db:3306/${DB_NAME}
          # ... (省略)
        depends_on:
          novel-db:
            condition: service_healthy # ここが重要!
        networks:
          - novel-network
    
      frontend:
        build: ./frontend
        container_name: novel-frontend
        ports:
          - "80:80"
        depends_on:
          - backend
        networks:
          - novel-network
    
      novel-db:
        image: mariadb:12.1
        container_name: novel-db
        environment:
          - MARIADB_DATABASE=${DB_NAME}
          # ... (省略)
        healthcheck:
          test: ["CMD-SHELL", "mariadb-admin ping -h localhost -u root -p$$MARIADB_ROOT_PASSWORD || exit 1"]
          interval: 10s
          timeout: 5s
          retries: 10
        volumes:
          - db_data:/var/lib/mysql
          - ./backend/sql:/docker-entrypoint-initdb.d
        networks:
          - novel-network
    
    networks:
      novel-network:
        driver: bridge
    
    volumes:
      db_data:

    1.データベースの「起動待ち」を完璧にする healthcheck

    Docker Composeでよくあるトラブルが、「DBが完全に立ち上がる前にバックエンドが接続に行き、エラーで落ちる」という現象。

    単なる depends_on: – novel-db だけでは、「コンテナが作成された」ことしか確認できず、「DB接続の準備ができた」ことまでは保証されない。

    そこでHealthcheck(ヘルスチェック) を行う。

    novel-db:
        # ...
        healthcheck:
          test: ["CMD-SHELL", "mariadb-admin ping ..."]
          interval: 10s
          retries: 10

    ここでは mariadb-admin ping コマンドを定期的に実行し、DBが応答するかを監視している。

    backend側では以下のように記述する。

    backend:
        depends_on:
          novel-db:
            condition: service_healthy

    これにより、「DBのヘルスチェックがOKになるまで、バックエンドを起動させない」という制御が可能になる。

    2.データの永続化と初期データの投入

    データベースをコンテナ化する際、コンテナを削除するとデータも消えてしまう。これを防ぐのが volumes 。

    volumes:
          - db_data:/var/lib/mysql
          - ./backend/sql:/docker-entrypoint-initdb.d

    • db_data:/var/lib/mysql: ホスト側(Docker管理領域)にデータを保存する。コンテナを作り直しても、小説データやユーザーデータは消えない。
    • ./backend/sql:/docker-entrypoint-initdb.d: MariaDBやMySQLの公式イメージは、/docker-entrypoint-initdb.d に置かれた .sql ファイルを、初回起動時に自動実行してくれる。 テーブル作成や初期データの投入を自動化できるため、開発メンバー全員が docker compose up 一発で同じDB状態から開発をスタートできる。

    EclipseからVS Codeに開発環境を移した

    フロントエンドの開発をしたかったので、VS Codeを触ってみた。

    殆ど問題なく作業を進めることが出来たが、一部戸惑った点を書き残しておく。

    起動構成はJSON形式で書く

    {
        "configurations": [
            {
                "type": "java",
                "name": "Spring Boot App",
                "request": "launch",
                "mainClass": "com.example.novel.NovelApplication",
                "projectName": "",
                "env": {
                    "DEV_DB_URL": "jdbc:mariadb://url",
                    "DEV_DB_USERNAME": "id",
                    "DEV_DB_PASSWORD": "pass"
                }
            },
            {
                "type": "node",
                "name": "React App",
                "request": "launch",
                "runtimeExecutable": "npm",
                "runtimeArgs": [
                    "run-script",
                    "dev"
                ],
                "cwd": "${workspaceFolder}/frontend",
                "console": "integratedTerminal"
            }
        ]
    }

    1. ひとつ目の設定:Java (Spring Boot) アプリケーション用

    Javaのプログラム(Spring Bootなど)を起動するための設定。

    • "type": "java"
      • Java拡張機能を使ってデバッグ
    • "name": "Spring Boot App"
      • VS Codeの「実行とデバッグ」画面のプルダウンメニューに表示される名前
    • "request": "launch"
      • 新しくプログラムを起動するという意味(対義語は "attach" で、既に動いているプロセスに接続する場合に使う)
    • "mainClass": "com.example.novel.NovelApplication"
      • プログラムのエントリーポイント(public static void main メソッドがあるクラス)を指定
    • "projectName": ""
      • 通常、VS Codeが認識しているJavaプロジェクト名(フォルダ名など)が入る
      • 空欄 "" のままだと、複数のプロジェクトがある場合に起動できない可能性がある。1つだけなら問題なし
    • "env"
      • プログラム実行時にOSの環境変数として渡す値を設定している
      • アプリケーション側(application.properties など)で、DB接続情報をハードコードせずに、ここから読み込むようにしている
        • DEV_DB_URL: 接続先データベースのURL
        • DEV_DB_USERNAME: データベースのユーザーID
        • DEV_DB_PASSWORD: データベースのパスワード

    2. ふたつ目の設定:Node.js (NPM) アプリケーション用

    フロントエンド開発(React, Vueなど)の開発サーバーを起動するための設定。

    • "type": "node"
      • Node.js用のデバッグ
    • "name": "React App"
      • VS Codeの「実行とデバッグ」画面のプルダウンメニューに表示される名前
    • "request": "launch"
      • 新しくプログラムを起動するという意味
    • "runtimeExecutable": "npm"
      • 直接 node コマンドを実行するのではなく、npm コマンドを実行する
    • "runtimeArgs": [ "run-script", "dev" ]
      • npm コマンドに渡す引数
      • つまり、ターミナルで npm run-script dev (または単に npm run dev)と打つのと同じコマンドが実行される
    • "cwd": "${workspaceFolder}/frontend"
      • Current Working Directory(作業ディレクトリ)の指定
      • ${workspaceFolder} はVS Codeで開いているルートフォルダを指す
    • "console": "integratedTerminal"
      • ログの出力先を「デバッグコンソール」ではなく、VS Codeの**「統合ターミナル」**にする

    VS Codeの設定はJSON形式で書く

    {
        "java.configuration.updateBuildConfiguration": "automatic",
        "java.compile.nullAnalysis.mode": "automatic",
        "maven.terminal.customEnv": [
            {
                "environmentVariable": "DEV_DB_URL",
                "value": "jdbc:mariadb://url"
            },
            {
                "environmentVariable": "DEV_DB_USERNAME",
                "value": "id"
            },
            {
                "environmentVariable": "DEV_DB_PASSWORD",
                "value": "pass"
            }
        ]
    }

    1. Javaのビルド・解析設定

    VS CodeがJavaプロジェクトをどう扱うかという、エディタの挙動設定

    • "java.configuration.updateBuildConfiguration": "automatic"
      • 意味: pom.xml(や build.gradle)が書き換えられた際、自動的にプロジェクト構成を更新する
      • Eclipseで言うと: プロジェクトを右クリックして行う 「Maven > プロジェクトの更新 (Alt+F5)」を、保存のたびに全自動でやってくれる機能
      • メリット: 依存ライブラリを追加した際、いちいち手動更新しなくてもすぐにimportできるようになる
    • "java.compile.nullAnalysis.mode": "automatic"
      • 意味: Javaのコードに対するNull(ヌル)分析機能のモード
      • 動作: プロジェクト内にNull注釈(@Nullable@NonNull など)が含まれている場合、自動的にNullポインタ参照の警告を出してくれる
      • メリット: 「ここでNullになる可能性があります」という警告がエディタ上で出るようになり、NullPointerExceptionを未然に防ぎやすくなる

    2. Maven実行時の環境変数設定

    • "maven.terminal.customEnv": [ ... ]
      • 意味: VS CodeのMaven拡張機能を使ってコマンド(例: mvn spring-boot:runmvn test)を実行する際、一時的にセットする環境変数を定義

    Spring Security 7にアップグレードしたらxmlでの設定が使えなくなった

    XMLファイルでSpring Securityを設定するSecurityConfigは非推奨になっていたが、今回完全に削除されたのかクラスファイルが見つからなくなった。

    流石にJavaファイルで設定する方式に変更した。

    元のxmlファイルは下記の通り。
    ※ExtendedAuthenticationSuccessHandler、ExtendedAuthenticationFailureHandlerはログイン成功失敗時に独自処理を行うためのラッパークラス。

    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans:beans xmlns="http://www.springframework.org/schema/security"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:beans="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd">
    
        <!-- Resources not processed by spring security filters -->
        <http pattern="/images/**" security="none" />
        <http pattern="/scripts/**" security="none" />
        <http pattern="/styles/**" security="none" />
    
        <http>
            <intercept-url pattern="/error" access="permitAll" />
            <intercept-url pattern="/login*/**" access="permitAll" />
            <intercept-url pattern="/signup*" access="permitAll" />
            <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
            <intercept-url pattern="/**" access="isAuthenticated()" />
    
            <form-login login-page="/login" authentication-success-handler-ref="authenticationSuccessHandler" authentication-failure-handler-ref="authenticationFailureHandler" />
            <remember-me user-service-ref="userDetails" key="aaa" />
            <logout logout-url="/logout" logout-success-url="/login" invalidate-session="true" delete-cookies="aaa" />
        </http>
    
        <authentication-manager alias="authenticationManager">
            <authentication-provider user-service-ref="userDetails">
                <password-encoder ref="passwordEncoder" />
            </authentication-provider>
        </authentication-manager>
    
        <beans:bean id="authenticationSuccessHandler" class="common.webapp.filter.ExtendedAuthenticationSuccessHandler">
            <beans:constructor-arg value="/top" />
        </beans:bean>
    
        <beans:bean id="authenticationFailureHandler" class="common.webapp.filter.ExtendedAuthenticationFailureHandler">
            <beans:property name="exceptionMappings">
                <beans:props>
                    <beans:prop key="org.springframework.security.authentication.DisabledException">/login/accountDisabled</beans:prop>
                    <beans:prop key="org.springframework.security.authentication.LockedException">/login/accountLocked</beans:prop>
                    <beans:prop key="org.springframework.security.authentication.AccountExpiredException">/login/accountExpired</beans:prop>
                    <beans:prop key="org.springframework.security.authentication.CredentialsExpiredException">/login/credentialsExpired</beans:prop>
                    <beans:prop key="org.springframework.security.authentication.BadCredentialsException">/login/badCredentials</beans:prop>
                </beans:props>
            </beans:property>
        </beans:bean>
    
        <beans:bean id="webexpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler" />
    
    </beans:beans>

    これに対して、Javaファイルは下記の通り。

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig {
    
        private final UserDetailsService userDetailsService;
    
        public SecurityConfig(UserDetailsService userDetailsService) {
            this.userDetailsService = userDetailsService;
        }
    
        @Bean
        public SecurityFilterChain securityFilterChain(HttpSecurity http) {
            http
                .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/images/**", "/scripts/**", "/styles/**",
                                     "/error", "/login*/**", 
                                     "/signup*").permitAll()
                    .requestMatchers("/admin/**").hasAuthority("ROLE_ADMIN")
                    .anyRequest().authenticated()
                )
                .formLogin(form -> form
                    .loginPage("/login")
                    .successHandler(authenticationSuccessHandler())
                    .failureHandler(authenticationFailureHandler())
                    .permitAll()
                )
                .rememberMe(remember -> remember
                    .userDetailsService(userDetailsService)
                    .key("aaa")
                )
                .logout(logout -> logout
                    .logoutUrl("/logout")
                    .logoutSuccessUrl("/login")
                    .invalidateHttpSession(true)
                    .deleteCookies("JSESSIONID", "aaa")
                );
    
            return http.build();
        }
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return PasswordEncoderFactories.createDelegatingPasswordEncoder();
        }
    
        @Bean
        public PasswordEncoder passwordTokenEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        AuthenticationSuccessHandler authenticationSuccessHandler() {
            return new ExtendedAuthenticationSuccessHandler("/top");
        }
    
        AuthenticationFailureHandler authenticationFailureHandler() {
            // 例外マッピングの設定
            Properties mappings = new Properties();
            mappings.put(DisabledException.class.getName(), "/login/accountDisabled");
            mappings.put(LockedException.class.getName(), "/login/accountLocked");
            mappings.put(AccountExpiredException.class.getName(), "/login/accountExpired");
            mappings.put(CredentialsExpiredException.class.getName(), "/login/credentialsExpired");
            mappings.put(BadCredentialsException.class.getName(), "/login/badCredentials");
    
            ExtendedAuthenticationFailureHandler handler = new ExtendedAuthenticationFailureHandler();
            handler.setExceptionMappings(mappings);
            return handler;
        }
    }

    Java 25のパフォーマンス改善点で気になったところ

    特に気になった点だけ調べてみた。

    ウォームアップ短縮(JEP 515)

    Java アプリケーションは JIT コンパイルによって実行速度を最適化するが、その「最適化」に到達するまでのウォームアップ時間は長年の課題だった。特に短命なサービスやバースト型のワークロードでは、起動直後のレスポンス低下が無視出来ない。

    JEP 515 では、過去の実行から収集したプロファイル情報を AOT キャッシュとして保存し、次回の起動時に利用できる仕組みが導入された。これにより、

    • 初期起動から JIT 最適化が効いた状態に近いパフォーマンスを発揮
    • マイクロサービスやサーバレス環境の「コールドスタート問題」を緩和
    • ウォームアップを待たずにスループットやレイテンシが安定

    といったメリットがある。

    トレーニング実行(プロファイル収集)

    まずはアプリを「本番に近い負荷テスト」、「代表的な入力データ」等で実行し、JIT がどのメソッドをよく使うかを記録する。

    java -XX:AOTCacheOutput=app-prof.aot -jar myapp.jar

    本番実行(プロファイル利用)

    保存したプロファイルを読み込み、本番環境で起動する。

    java -XX:AOTCache=app-prof.aot -jar myapp.jar

    Compact Object Headers(JEP 519)

    Java のオブジェクトはヒープ上で管理され、各オブジェクトには「ヘッダ情報」が付与されている。従来は 12 バイトが標準だったが、JEP 519 では ヘッダを 8 バイトに圧縮するオプションが提供された。

    これにより、

    • メモリ削減:多数の小さなオブジェクトを扱うアプリで特に有効。ヒープ効率が上がるため GC 回数も減少する。
    • キャッシュ効率の改善:メモリ占有が減ることで CPU キャッシュに収まるデータ量が増え、アクセスレイテンシが低下する。
    • スループット向上

    利用方法は、JVM 起動時に以下を指定する。

    java -XX:+UseCompactObjectHeaders -jar myapp.jar

    デフォルトでONになっていないということは、全ての環境で改善するわけではないことを表している。

    • COH ではオブジェクトヘッダに格納されていた情報を圧縮/再配置するため、hashCode/同期多用のアプリでは遅くなる可能性
    • ヒープダンプ解析ツールや JVMTI を使った低レベルの計測では、
      オブジェクトレイアウトが従来と変わるため、互換性のリスクあり
    • メモリ帯域が十分に広いサーバー、CPU キャッシュが大きい最新世代のCPUでは効果が限定的になる場合がある(逆に、組込み系やクラウドVMのようにメモリ制約がある環境で効果大)

    適用シナリオ

    これらの最適化はすべてのアプリケーションで均等に効くわけではない。効果が大きいシナリオは以下の通りとなる。

    ウォームアップ短縮(JEP 515)

    • サーバレス、マイクロサービス
    • 短命な CLI ツールやバッチ処理

    Compact Object Headers(JEP 519)

    • ヒープ上に数百万単位のオブジェクトを保持するシステム
    • 高トラフィックの Web サービス
    • データ処理や分析基盤

    まとめ

    Java 25 の JEP 515 と JEP 519 は、単なる言語仕様の追加ではなく、実行基盤のボトルネックを狙い撃ちした改善となっている。
    起動直後のパフォーマンスが課題であればウォームアップ短縮を、ヒープ使用量やGCコストに悩んでいるなら Compact Object Headers を試す価値がある。

    YOLOでYouTubeの映像から物体検出する

    YouTubeのライブ映像を取り込んで物体検出してみる。応用すると風景のライブ映像に野生動物が写ったらアラートを出す。録画するなど出来ると思う。

    前回の下記部分のカメラ映像読み込み部分をYouTubeに置き換える。

    # Load image source
    cap = cv2.VideoCapture(0)

    前回のパッケージに加えて「pip install yt-dlp」を実行しておく。

    実装

    モジュールの読み込み
    from yt_dlp import YoutubeDL
    YouTubeのライブ配信ページURLから、再生用の実URL(多くはHLSのm3u8)を取得する関数
    def get_best_live_url(youtube_url: str) -> str:
        ydl_opts = {
            'quiet': True,
            'no_warnings': True,
            'skip_download': True,
            # Filters in order of ease of live performance
            'format': 'best[protocol^=m3u8]/best[ext=mp4]/best'
        }
        with YoutubeDL(ydl_opts) as ydl:
            info = ydl.extract_info(youtube_url, download=False)
            # `url` contains a URL that can be played directly (assuming m3u8)
            stream_url = info.get('url')
            if not stream_url:
                for f in info.get('formats', []):
                    if 'm3u8' in (f.get('protocol') or ''):
                        stream_url = f.get('url')
                        break
            if not stream_url:
                raise RuntimeError('Unable to get live playback URL.')
            return stream_url
    YouTubeの動画、ライブ配信を開く関数
    def open_live_capture(stream_url: str) -> cv2.VideoCapture:
        cap = cv2.VideoCapture(stream_url)
        # Latency reduction (enabled builds only)
        try:
            cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
        except Exception:
            pass
        return cap
    前回コードの変更部分

    前回のコードの「cap = cv2.VideoCapture(0)」を下記に置き換える。「youtube_url」に取り込みたい動画やライブ映像のURLを入力する。

    youtube_url = 'https://www.youtube.com/watch?v=好きな動画のURL'
    
    stream_url = get_best_live_url(youtube_url)
    print('stream:', stream_url)
    cap = open_live_capture(stream_url)
    
    if not cap.isOpened():
        raise RuntimeError('Failed to open VideoCapture. Please use an FFmpeg-enabled build of OpenCV.')