0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

置換ルールを保存・再利用できるテキスト・ZIP一括変換Webアプリを作った話

0
Posted at

0. この記事で書くこと

個人開発で、テキストやZIP内ファイルを対象に、
置換ルールを適用して一括変換できるWebアプリ「Replace!」を作りました。

Replace!(公開版)
https://same-work.com/

Replace! は、単にその場で文字列を置換するだけのツールから一歩進み、
ひな型となる置換前テキスト(またはZIP)と、
置換ルールをセットで保存し、
あとから繰り返し使えるようにする

ことを重視したWebアプリです。

保存した設定は、自分で再利用するだけでなく、
必要に応じてURL共有したり、設定を公開しアプリ内で検索できるようにしたりできます。
また、サーバー上に設定を保存せず、ローカル完結で利用することもできます。

この記事では、以下について書きます。

  • Replace! で何ができるか
  • なぜ作ったか
  • テンプレートをどう保存・再利用できるようにしたか
  • ローカル処理とサーバー処理をどう分けたか
  • ZIP変換・文字コード・Google Drive連携で考えたこと
  • AIを使ってどのように開発したか

注意:この記事の図にはAIで生成した図表も含まれます。内容は精査しておりますが、ご留意ください。


1. Replace! の全体像

Replace! は、「ひな型」と「置換ルール」をセットで保存し、何度でも使い回せるようにするWebアプリです。

通常の一括置換ツールは「今入力した文字列を置換する」が中心です。
一方でReplace!では、テンプレートとして残したうえで、あとから自分やほかの人が再利用できる仕組みを設けました。

登場する要素は、大きく以下の3つです。

  • テンプレート作成者:ひな型や置換ルールを作る人
  • テンプレート利用者:値を入力して変換する人
  • Replace!:テンプレートの作成・保存・変換を行うアプリ

テンプレート作成者と利用者は同じ人の場合もあります。
例えば、自分用に作った定型文やプログラムのひな型を何度も使い回す場合です。
本アプリでは保存した設定を共有・公開することも可能です。その場合は別の人物になりえます。

Replace! では、作ったテンプレートを主に2つの形で残せます。

1つ目は、Replace! のサーバー上にテンプレートとして保存する方法です。
この方法では、自分用のテンプレートとして再利用できます。
さらに必要に応じて、URL共有したり、テンプレートを公開して他人が参照できるようにしたりできます。

2つ目は、replacecfg.json を同梱してテンプレートの各種設定をユーザーの端末にZIPとして保存しておく方法です。
この方法では、変換設定から実行、さらにその保存までReplace!サーバーを一切介さず、置換前の内容と置換ルールを手元に残せます。
必要であれば、他の人にそのZIPを渡して設定を共有することもできます。

image.png

【トップ画面・モード選択・置換ルール欄のイメージ】
image.png


2. 具体的に作ったものは

Replace! は、テキストやZIP内の複数ファイルに対して、置換ルールを適用できるWebアプリです。

主な機能は以下です。

  • テキスト本文の一括置換
  • ZIP内の複数ファイルの一括置換
  • ファイル名・フォルダ名の置換
  • UTF-8 / Shift_JIS の文字コード自動判定
  • 置換ルールの保存・読み込み
  • ローカル設定ZIPとしての保存
  • マイテンプレート保存
  • 共有URLの発行
  • 公開テンプレート化
  • Googleアカウント連携
  • Google Drive連携によるZIPテンプレート保存・読み込み

このアプリでの「テンプレート」は、単に文章のひな型という意味だけではありません。

Replace! におけるテンプレートは、以下のような情報をまとめたものです。

テンプレート
  ├─ 変換元テキスト
  ├─ 置換対象文字列
  ├─ 置換後の入力値
  ├─ 日付などの定型値
  ├─ 出力ファイル名テンプレート
  ├─ ZIPテンプレート本体への参照
  └─ 利用範囲

作りながら、これは以下のような用途に使えるかなと考えてました。

  • メールや送信文書のひな型作成
  • プログラムひな型作成
  • DDLや初期データ投入SQLの生成
  • 設計書から定型ファイルを生成する補助
  • ZIPで配布するソフトや設定一式のカスタマイズ
  • サーバーに保存しないローカル設定ZIPとしてのテンプレート保管
  • 公開テンプレートを使った、ウィザード不要の簡易カスタマイズ配布

Replace!開発においても、実際に使ったシーンがありましたので3.にて後述します。


3. 具体的なテンプレート活用例

メール文面、DDL生成、ZIPひな型生成の3例で、Replace! の使い道を紹介します。

例1:メール文面の生成

たとえば、以下のような送信用文面テンプレートを用意します。

image.png

置換ルールは以下のように設定します。

image.png

変換結果は以下のようになります。

image.png

【画面イメージ】
image.png
(冒頭にて申し上げた3例ともスクショを貼るとページ全体が見づらくなるため、以下例はスクショを省略します。)

例2:DDL生成の簡易例

次は開発寄りの例です。

たとえば、以下のような置換前本文を用意します。

CREATE TABLE @TableName (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    create_date TEXT NOT NULL
);
INSERT INTO @TableName ( id, name, create_date ) VALUES (
    0,
    '@TableName初期データ',
    '@Today'
);

置換ルールは以下です。

@TableName → M_CUSTOMER
@Today     → yyyy-MM-dd

変換結果は以下のようになります。

CREATE TABLE M_CUSTOMER (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    create_date TEXT NOT NULL
);
INSERT INTO M_CUSTOMER ( id, name, create_date ) VALUES (
    0,
    'M_CUSTOMER初期データ',
    '2026-06-19'
);

この例は簡略化していますが、実際に本 Replace! の開発でも、DDLや初期データ投入SQLの生成に、開発中のReplace!を使いました。

設計書から置換ルールを作り、テーブル名や表示名などを差し替えてSQLを生成できたので、あとからテーブル構造を触るときもかなり楽でした。
なにしろExcelで設計書を変更すれば置換ルールにそれを貼り付けるだけでDDLが出来上がるようにしていたからです。
さらにこの方式による副産物として、よくありがちな設計書とテーブル構造の乖離が起きることもなくなります。

例3:ZIPテンプレートの一括生成

Replace! の強みの一つは、テキスト単体だけでなく、ZIP内の複数ファイル本文やファイル名もまとめて置換できることです。

たとえば、以下のようなプログラムひな型ZIPを用意します。

template.zip
  ├─ @EntityNameController.php
  ├─ @EntityNameService.php
  ├─ @EntityNameRepository.php
  └─ sql/
      └─ create_@TableName.sql

各ファイルの中身にも、以下のような置換対象を含めておきます。

class @EntityNameController
{
    public function index()
    {
        // @EntityName の一覧を取得
    }
}

置換ルールは以下です。

@EntityName → Customer
@TableName  → M_CUSTOMER
@Today      → yyyy-MM-dd

変換後のZIPは、たとえば以下のようになります。

converted.zip
  ├─ CustomerController.php
  ├─ CustomerService.php
  ├─ CustomerRepository.php
  └─ sql/
      └─ create_M_CUSTOMER.sql

ファイル名だけでなく、ファイル本文内の @EntityName@TableName も置換されます。

このように、複数ファイルで構成されるひな型一式を、入力値に応じてまとめて生成できます。

通常の一括置換では「開いているテキスト」だけが対象になりがちですが、Replace! ではZIP一式をテンプレートとして扱えるため、プログラムひな型や設定ファイル一式の生成にも使えます。


4. なぜ作ったか

同じ構造のファイルをコピーして値だけ変える作業を、置換ルールとして再利用したかったのが動機です。

原点は、前職での定型的な開発作業でした。

機能を一つ追加するたびに、ひな型になりそうな複数のプログラムを探し、コピーし、クラス名・画面名・テーブル名・日付などを少しずつ変えていく作業がありました。

難しくありません。
ただし毎回やるには面倒で、他にも作業が溢れる中で実施するには時間が勿体ない、かつ置換漏れや修正漏れも起きやすい作業でした。

そのため私自身はその作業をVBSで簡易的に自動化していました。
特定の業務に寄せた簡単なツールでしたが、手作業で30分ほどかかっていた作業を、数分で済ませられるようになりました。

課題を整理すると、以下のようになります。

同じ構造のファイルを毎回コピーする
  ↓
必要な値だけ置換する
  ↓
手作業だと置換漏れが起きる
  ↓
置換ルールごと保存して再利用したい
  ↓
必要なら他の人にも渡せるようにしたい

単に「この文字をこの文字に置換する」だけなら既存ツールでもできます。

しかし、置換ルールやひな型一式を保存し、再利用し、必要に応じて共有やローカル保管までできるツールはあまり見かけませんでした。

そこが Replace! を作る大きな動機になりました。
そこでVBSを作成した記憶をもとに、WEBツールとして汎用化できないか、との考えを基に生み出したのが本アプリです。


5. 技術スタック

ビルド環境を前提にせず、HTML/CSS/JavaScript/PHP/SQLiteでレンタルサーバーに載せやすい構成としました。

主な技術スタックは以下です。

フロントエンド:
- HTML
- CSS
- JavaScript
- JSZip
- Google Identity Services
- Google Drive API

バックエンド:
- PHP
- SQLite

インフラ:
- ロリポップ!レンタルサーバー
- 独自ドメイン
- .htaccess による内部ファイル保護

外部サービス:
- Googleログイン
- Google Drive

フロントエンドは、基本的にプレーンなHTML/CSS/JavaScriptです。

大きなフレームワークやビルド環境を前提にせず、レンタルサーバー上で動かしやすい構成にしています。


6. 設計方針:ローカル処理と保存方法の分離

ファイルや本文はできるだけブラウザ内で処理し、保存したい場合だけサーバー保存や設定ZIP保存を選べる構成にしました。

Replace! で特に意識したのは、変換処理保存方法を分けることです。

テキストやZIPには、個人情報や業務上の機密情報が含まれる可能性があります。
そのため、ただ変換するだけの処理では、できるだけファイル本文をサーバーへ送らない構成にしたいと考えました。

一方で、置換ルールをあとから再利用したい場合は、どこかにテンプレートを残す必要があります。

そこで2種類の保存方法を用意した理由

テンプレートの保存方法は、大きく2種類用意しました。

1つ目、Replace!サーバー上に変換前テキスト変換設定を保存する方法です。
これは、画面上で手軽にマイテンプレートを再利用したり、あるいはほかの人向けに共有URLを発行したり、公開して検索できるようにしたりする用途に向いています。

2つ目は、変換前テキストファイルに、置換設定を示すreplacecfg.json を同梱したZIPをダウンロードして保管する方法です。
これは、Replace!上のサーバーにテンプレートを保存したくない場合や、元ファイルと置換ルールを手元に残しておきたい用途に向いています。

出力イメージ:

template.zip
  ├─ replacecfg.json
  └─ 変換元テキストやファイル ...

この方式では、置換ルールを含んだZIPを画面上からダウンロードして保管できます。
必要であれば、任意の方法でZIPを丸ごと他の人に共有いただくことで再利用もできます。これによるメリットは、データがアップロードされないことです。

ちなみにこの際に同梱される設定ファイルはJSON形式で保存される方式を選択しました。
理由はあとから設定項目を増やした場合にも、設定名が存在するかどうかで判定できるためです。バージョン情報も持てるので、将来的に設定フォーマットが変わった場合にも対応しやすくなることが強みです。

replacecfg.json
  ├─ 設定名やバージョン情報など
  ├─ 出力ファイル形式設定
  ├─ 置換ルール
  │   ├─ 置換前対象文字列
  │   ├─ 置換後文字列
  │   ├─ 定型値を使用する場合の種類(システム日付yyyy/MM/ddなど)
  │   └─ 正規表現使用有無(将来対応予定)
  └─ 文字コードの指定などのオプション情報

上記強みが異なる2つの保存方法(サーバー保存・ローカル保存)を用意することで、要求されるニーズ・用途などに幅広く応じることができると考え、実装しました。

作成者向け画面と利用者向け画面

テンプレートを作る人と利用する人では、求めている画面が異なることも多いです。

テンプレート作成者は、何を置き換えるか、つまり置換対象文字列、置換後値、日付プリセット、出力ファイル名などを編集できる必要があります。

一方で、既存のテンプレート利用時は、基本的に「置換後値」だけを入力できれば十分です。

そのため、Replace! ではルール変更ロックというトグルのON/OFFによって、
作成者向けの編集画面と、利用者向けの入力画面を切り替えられるようにしました。

【ルール変更ロックON/OFFの違い】
image.png

さらに、作成者は設計書やExcelから置換ルールを作ることが多いと考え、
置換ルール欄はExcelやCSVからそのままの区切りで貼り付けられるようにしています。
image.png


7. ZIP変換と文字コード

ZIPをもとにした変換では、ZIP内の複数ファイルを展開し、文字コードやファイル名を考慮しながら置換して再ZIP化しています。

ZIP変換では、単にZIPを展開して文字列置換するだけでは済みませんでした。

主に考慮したのは以下です。

- ZIP内の複数ファイルを対象にする
- テキストファイルだけ本文置換する
- 画像などのバイナリは本文置換せずコピーする
- ファイル名・フォルダ名も置換する
- Windowsで使えないファイル名文字を除去する
- 置換値に / や \ が含まれた際はPath構造を壊さないために無害化("_"に変換)する
- UTF-8 / UTF-8 BOM付き / Shift_JIS を扱う
- サイズが大きめのZIP処理実行中にユーザーの命令により二重実行されないようにする

使用した主なライブラリは以下です。

JSZip:
  ZIPの読み込み、展開、再生成

文字コードまわりは特に面倒でした。

最初はライブラリの自動判定に頼っていましたが、複数のShift_JISファイルを扱ったときに誤判定するケースがありました。

最終的には、UTF-8として妥当なバイト列かどうかを自前で判定し、妥当でなければShift_JISとして扱う方向にしました。
ここは地味ですが、batファイルなども置換対象としている以上、実用面ではかなり重要と判断しテストと修正を行いました。


8. GoogleログインとGoogle Drive連携

会員情報や変換前ZIP本体を自分で抱え込みすぎない方針を選択した理由

Replace! では、テンプレート保存や公開機能などがあり、そのためには会員制度が必要です。そこで会員登録機能を自前では作らずにGoogleログインを活用しています。

自前で会員登録機能を作ることも可能です。
でも今までに知られていない脆弱性が発覚したら?その時仕事も忙しかったら?
そうです、メールアドレス、パスワード、本人確認、パスワードリセットなどを自分で持つと、それだけで管理すべき責任が増えます。
個人開発でそこまで抱えるのは重いと考えました。

そのため、ログインはGoogleアカウント連携に寄せました。
Replace! 側では、Googleから戻される必要最小限のユーザー識別情報を使ってユーザーを識別し、メールアドレスすら保持しない方針にしています。

ZIPテンプレートの保存方法の検討

ユーザーがZIPテンプレートをサーバーに保存した場合、変換前ZIP本体をReplace!サーバー自身に保存する設計では、サーバー容量、削除対応、ファイル内容の管理責任、機密情報混入時のリスクなどが大きくなります。

そこで、ZIP本体はユーザー自身のGoogle Driveに保存し、Replace! 側ではテンプレート設定やDrive上のファイル参照情報を管理する構成にしました。
ここはログイン済でも、改めてDrive連携権限をユーザー本人に求めて意思を確認することで、Google Drive API経由で権限を取得し対応しています。

image.png

Drive連携のスコープは、必要以上に広くしないよう注意しました。

Drive全体を一覧したり、ユーザーのファイルを広く検索したりする用途ではありません。
Replace!で使うZIPテンプレートを保存・読み込みするために必要な範囲に絞っています。

この方針にした理由は、単にGoogleのOAuth審査を軽くしたいからのみでなく、利用者のDrive全体を扱う必要がなかったからです。
自分で全てを抱え込むのではなく、外部サービスに任せるべきところは任せる。
ただし、何を任せて、何を自分のサービスで持つのかは明確にする。

この切り分けが、今回かなり重要でした。


9. テンプレートの利用範囲と所有者チェック

保存したテンプレートは、自分用・URL共有・公開の3つを扱える仕組みについて

Replace! では、サーバー上に保存したテンプレートの利用範囲を大きく3つに分けています。

自分用:
  作成者本人だけが読み込めるテンプレートです。

共有URL:
  URLを知っている人が読み込めるテンプレートです。

公開テンプレート:
  公開テンプレート一覧から検索・読み込みできるテンプレートです。

image.png

他人がテンプレートを読み込める上で気をつけたのは、テンプレートの利用と所有を混同しないことです。

他人が「公開」されたテンプレートを読み込んだ場合、その人はそのテンプレートを利用できます。

しかし、他人が保存したテンプレートの所有者になったわけではありません。
非所有者がテンプレートを読み込んだ場合、元テンプレートを直接上書き・削除・公開設定が変更できないようにする必要があります。
そのため、所有者以外の場合はリクエストに対し更新用のキー情報を返さないようにしています。

一方で、他人のテンプレートを元に、自分用の設定として保存したい場合はありえます。
だからこそ保存が全くできないという挙動にはしていません。

なので、非所有者が保存を実行したときの挙動は次の通りです。

キー情報を非所有者に返していないため、元の所有者のテンプレートを変更することはできません。
その代わり非所有者自身のテンプレートとして新規保存されるようにしています。

他人の公開テンプレートを読み込む
  ↓
自分が必要なルールを変更
  ↓
保存
  ↓
元テンプレートは変更されない
  ↓
自分用テンプレートとして新規保存

公開や共有の仕組みを作ると、「読めること」と「編集できること」が混ざりやすくなります。

ここを曖昧にすると、他人のテンプレートを誤って壊せてしまう可能性があります。

この挙動は地味ですが、かなり重要だと考えています。


10. 公開前に確認したこと

一般的なWeb脆弱性や認可不備は、テスト仕様書を作って一通り確認しました。

個人開発とはいえ、公開する以上、最低限の確認は必要だと考えました。

機能ごとの確認とは別に、公開前のシステムテスト仕様書を作り、以下の観点を確認しました。

  • 主要ユースケース
  • 自分用・URL共有・検索に公開の権限境界
  • 非所有者による更新・削除・公開設定変更ができないこと
  • CSRF / XSS / 認可不備などの代表的なWeb脆弱性
  • エラー時に内部情報を出しすぎないこと
  • DB・ログ・設計資料などがブラウザから直接見えないこと
  • GoogleログインやGoogle Drive連携の導線

細かい機能テストで不具合を潰したうえで、システムテストでは公開前の最終巡回をする、という位置づけです。


11. AIを使った開発について

フロントエンドはAIをかなり活用しつつ、仕様・権限境界・保存方針は自分で判断しました。

今回の開発では、生成AIをかなり活用しました。

特にフロントエンドは、かなりAIに頼りました。

正直、自分はフロントエンドのデザインやUI実装には強い自信がありません。
これまでの個人開発でも、動くものは作れても、見た目や操作性はどうしても素朴になりがちでした。

そこで今回は、フロントエンドについてはAIをかなり活用する方針にしました。

その結果、過去の個人開発成果物と比べても、かなり短時間で見た目や操作性のよい画面にできたと思います。

AIに任せた部分

- UI案の作成
- HTML/CSSのレイアウト調整
- JavaScriptの実装案
- モーダルやカードUIの整備
- 文言のたたき台
- テスト観点の洗い出し
- 規約・プライバシーポリシーのたたき台

自分で判断した部分

バックエンドについては、自分が「こういう設計・実装にしたい」と考えた方針に沿って実装しました。

- DB設計
- テンプレート保存仕様
- 自分用・URL共有・検索に公開の権限境界
- Google Drive連携の責任範囲
- ZIP本体をサーバーに持たない方針
- メールアドレスを保持しない方針
- どの情報をフロントで保持するか
- 非所有者が読み込んだテンプレートをどう扱うか
- テスト実施と最終判断

フロントエンドについても、見た目や実装案はAIを活用しましたが、丸投げではなく画面がどの情報を持っているか、非所有者のテンプレートIDを保持していないか、保存時にどの値を送っているか、といった点は確認するようにしました。

AIにコードを書かせること自体よりも、
出てきた実装が自分の理解できる範囲にあること、不具合が出たときに自分で追えること
を特に重視しました。

そのためAIの実装は逐次コードレビューし、理解できる合理的な実装であること、保守性が低いなど意に沿わない実装は修正を指示するなどを繰り返してからテストに進みました。


12. 苦労したこと

一番重かったのは、実装そのものよりも「何を自分のサービスで持つか」を決めることでした。

すべてを自分で持とうとすると、すぐにパンクします。

ユーザー情報を持つ
メールアドレスを持つ
パスワードを持つ
ZIPファイル本体を持つ
公開テンプレートを持つ

一つ一つ実装できないわけではありません。
しかし、持つものが増えれば増えるほど、管理責任も増えます。

特に、個人情報や機密情報をできるだけ持たない設計にすることは、対外的にも運用面でもかなり重要だと感じていました。

だからこそ、ログインはGoogleに寄せました。
ZIP本体も、必要に応じてGoogle Driveに置く設計としました。
通常の変換では、ファイルや本文をサーバーへ送らないようにしました。
設定ZIPダウンロードという形で、サーバーに保存しなくても保存・再利用できる導線も用意しました。

技術的には、以下もかなり苦労しました。

- Shift_JIS / UTF-8 / UTF-8 BOM付き の文字コード判定
- ZIP内のファイル名・フォルダ名置換
- 置換値に / や \ が入った場合のパス崩れ防止
- 自分用・URL共有・検索に公開の状態遷移
- 未ログインで自分用テンプレートのURLを開いた場合の導線
- ログ設計・実装

実装そのものよりも、公開してよい状態へ持っていくための周辺作業が重かったです。


13. 今後やりたいこと

利用者専用モードとしてiframeに対応し外部サイトへ埋め込めるようにすること、正規表現置換、API実行、通報機能、公開テンプレート強化などを今後の候補として考えています。

今後の候補としては、以下を考えています。

  • iframeなどによる軽量表示モードの活用
  • 正規表現置換
  • API実行
  • メール送信
  • テンプレート通報機能
  • 公開テンプレートの管理強化
  • UI改善
  • 使い方サンプル・利用説明の強化
  • 公開テンプレートの充実

まずはver1.0として公開したものを基に、実際に使いながら改善していく予定です。


14. おわりに

まずはver1.0として公開した。実際に使いながら必要なところから改善していく。

今回の個人開発では、単にWebアプリを作るだけではなく、公開するために必要な周辺作業の重さをかなり実感しました。

機能を作ることよりも、公開してよい状態に持っていくことの方が大変でした。

それでも、自分で仕様を考え、実装し、テストし、規約を用意し、公開直前まで持っていけたことは、ひとつの区切りになったと思います。

Replace! はまだ完璧なサービスではありません。
ただ、まずはver1.0として公開し、必要なところから少しずつ直していこうと思います。

最後に、公開版はこちらです。

Replace!(公開版)
https://same-work.com/

0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?