blog Spring Cloud Gatewayを基盤とした統合認証 動的ルーティングインフラ、その先にある課題 前回は、Spring Cloud Gateway(以下、SCG)を活用し、従来の硬直した静的YAML設定構造から脱却して、データベースベースの無停止動的ルーティングアーキテクチャを構築する過程について説明しました。インフラの中核となる設定要素をアプリケーションのビジネスデータ領域へ移行することで、システムの再ビルドやデプロイを行わず、運用中にリアルタイムでルーティングルールを変更できる柔軟性を確保しました。これは、複雑なマルチベンダー環境において、各開発会社間のデプロイ時期のずれやエンドポイントの変更に柔軟に対応できる強力な緩衝地帯となりました。 しかし、実務の観点から見ると、無停止の動的ルーティング環境を完成させたことは、巨大な目的地へ向かう第一歩にすぎませんでした。インフラレベルでトラフィックの通路を柔軟に開いた瞬間、分散マイクロサービスアーキテクチャ(MSA)環境が直面する、より本質的で深刻な実務上の課題が表面化したためです。その中心には、数十の分断されたサービス上でどのように共通のセキュリティ標準を維持するのか、
blog ロールベースルーティングからエピソード分離へ -Banjang Webフロントエンドのデプロイ単位を機能中心に再定義したリファクタリングの振り返り- 1. はじめに — ひとつにまとめられていたアプリ Banjang Webフロントエンドは長い間、episode-banjangという単一のアプリとして運用されていました。ホーム、求人、チーム、マイ、通知、認証、請求など、複数の機能がひとつのビルド成果物に含まれており、ユーザーを区別する方法も単純でした。最上位ルーターの下に、作業者向けと現場管理者向けのルーターをそれぞれ置き、URLのプレフィックスで役割を分ける構成でした。 // AS-IS: pages/worker/router.tsx export const router: RouteObject = { path: 'wkr', // 작업자(worker)는 /wkr/* 로 element: ( <MobileLayout role="worker" ...> <Outlet
blog JVMプロセス内でのPython組み込み戦略 -Jep、JNI、そしてGIL- 1. はじめに:異種ランタイム(Heterogeneous Runtime)統合が直面する課題 現代のバックエンドアーキテクチャでは、サービスロジックが単一の言語だけで構成されるケースはますます減少しています。数値解析(Numerical Analysis)と機械学習推論の領域では、NumpyやScipyを筆頭とするPythonエコシステムが事実上の標準として定着している一方、トランザクション管理と運用安定性の面では、依然としてJVMベースのスタックが優位に立っています。その結果、バックエンドエンジニアは、異なる2つのランタイム(Runtime)の強みをどのように1つのサービスフローに統合するかという問題に直面することになります。 船舶データプラットフォームも同じ課題を抱えていました。航行中の船舶からエンジンや発電機の状態、タンクレベル、船体姿勢データが毎分単位で取り込まれますが、プラットフォームの本質的な役割は、これらを格納することではなく、船体運動を解析し、正常性能に対する低下レベルを算出する演算(Computation
blog Inboxパターンに基づくレガシー連携の非同期化事例 -レガシー大量送信情報の受信区間を同期処理から保存後終了の構造へ移行した経験- 1. はじめに — どのような問題に直面したのか 私が担当したアダプターサービスは、病院のレガシー医療情報システム(以下、レガシー)が送信するアンケート配信情報を受信し、内部マイクロサービス(アカウントサービス、アンケートサービス)に登録する窓口の役割を担っています。一見すると、1件の電文を受け取って登録する単純な連携ですが、運用を開始すると3つの問題が同時に明らかになりました。 * 処理時間がそのままレガシーのトランザクション占有時間になっていました。 1件を処理するために、アダプターはレガシーへの照会を2回、アカウントサービスへの呼び出しを4〜5回、アンケートサービスへの呼び出しを10回余り、合計約20回のREST往復を同期的に実行します。レガシーは大量のデータを自身のトランザクション内で連続して呼び出すため、レガシーがアダプターの処理時間全体を保持したまま待機する構造になっていました。トレースを確認すると、1件あたりのHTTP応答は1.4〜2.
blog AIエージェントの作業を可観測にする 1. 序論:コミットログでは説明できなくなったこと AIコーディングツールをチームに導入すると、コードは速く増えていく一方で、そのコードがどのような過程を経て生まれたのかは、むしろ見えにくくなります。以前はコミットログをざっと確認するだけで、誰が何にどれほど取り組んでいたのか、おおよそ読み取れました。今では同じログを見ても、人が1時間悩んだ結果なのか、エージェントが3分で作ったものなのか区別できません。リポジトリは同じでも、その中で起きていることが見えなくなったのです。 目標は2つに定めました。エージェントが作ったコードであっても、後から加わった人がリポジトリだけを受け取ってすぐに把握し、引き継げるようにすること。そして、どのプロジェクトのリポジトリであっても、分析対象に登録された瞬間から、別途設定なしで指標と推移が出るようにすることです。その後のすべての選択は、この2つによって決まりました。 2. 何を数えるのか 2.1 判別ではなく申告にする 最初はコードの変更内容を分析し、AIが書いた部分を見分けようとしましたが、すぐに断念しました。コミット
blog AIコーディング時代におけるテストの役割 AIコーディングアシスタントを開発に活用することで、コード作成にかかるコストは以前より大幅に下がりました。要件を説明すれば実装コードを素早く生成でき、同じ方法で実装を検証するためのテストコードも併せて作成できます。 私自身もAIコーディングを使う際、検証用テストを積極的に活用してきました。AIが作成したコードをそのまま成果物として受け入れるのではなく、要件を反映したテストも併せて作成し、それを実行して実装結果を改めて確認する方法です。実装コードだけでなくテストコードの作成コストまで下がったことで、テストを追加する負担も以前より軽くなりました。 そのような中、AI開発Workflowを支援するPluginを使いながら、TDD(Test-Driven Development)を改めて見直すようになりました。私が使ったPluginはSuperpowersで、機能実装にTDDを強く適用していました。テストコード自体には慣れていましたが、TDDを実際の開発手法として適用した経験はありませんでした。以前は実装後の検証のためにテストを書いていましたが、TDDではテストが開発プロセ
blog オブザーバビリティとは何か、なぜ注目すべきなのか オブザーバビリティとは何か もともとは制御理論の用語で、システムの外部に出てくる出力だけを見て、内部状態をどの程度把握できるかを意味します。ソフトウェアに置き換えると、こうなります。サーバーに直接入らず、コードを修正してログを増やすこともなく、現在出力されているデータだけで問題を説明できるか。 重要なのは、あらかじめ決めておいた質問に答えるだけではないという点です。応答時間が3秒を超えたら知らせてほしい、というのは事前に決めておく質問です。一方、「昨日の午後、特定の機種でだけアップロードに失敗した理由」は、あらかじめ決めておけなかった質問です。後からこうした質問を投げかけられるなら観測可能なシステムであり、投げかけられないなら、コードを修正してデプロイした後、問題が再び起きるのを待つしかありません。 言葉だけで見ると当然の話のようですが、実際にどのような状況で違いが出るのかは、サービスに当てはめて考えると実感しやすくなります。 班長ノートではなぜ必要なのか 班長ノートは、建設現場の班長とチームリーダーをつなぐアプリです。これまでチャットやSMSでやり
blog 外部API、受信と処理を分離する 1. はじめに 現在運用中の既存サービスでは、データが1分に1件ずつ生成され、DBに保存されています。時間の経過に伴ってデータが順番に入ってくる構造で、1時間を基準にすると、60件のデータがそれぞれの時点に合わせて保存されます。 新しい機能を開発するにあたり、同じテーブルに保存されるものの、既存とは異なる方法で生成されるデータを処理する必要が生じました。 新たに追加されるデータは、外部サービスで過去1時間分のデータを別途分析した結果です。従来は1分ごとに1件ずつデータが入ってきていましたが、新機能では分析完了後、過去1時間分に該当する約60件のデータを1回のAPIリクエストで受け取ります。 サービス内部で開始され、処理のタイミングを制御できる処理と、外部から送られてくるリクエストを同じ方法で処理するのが適切なのか。 今回の作業は、この問いから始まりました。 2. 問題の定義 当初は、既存のデータ処理方式をそのまま活用できると考えていました。既存システムでも、1回の処理で1万件を超えるデータを生成し、それを500件単位に分割してBatch In
blog モダンバックエンド - コンテキスト伝播のリファクタリング(1) [Modern Backend] 講座 Part 1 第7回の第1講義、「コンテキスト伝播のリファクタリング(1)」です。 今回の講義では、掲示板プロジェクトの既存のThreadLocalベースのコンテキスト伝播ロジックを、Java 25のスコープ値(ScopedValue)ベースの構造へ移行するための対象分析と設計リファクタリングを行います。 📌 主な学習内容: * リファクタリング対象となる4つのファイルの確認(RequestContext、RequestContextFilter、PostCreationCoordinatorなど) * 5段階のリファクタリング手順と、コンパイルエラーを基にした誘導方式 * 既存のThreadLocalコード構造の分析と、開発者の慣習(set/clear)に依存していた手動管理箇所(4か所)の確認 * RequestContextホルダー内部をScopedValueベースへ移行し、APIメソ
blog SpreadJS エクセル読み込みパフォーマンス最適化 2 - 実行時間の測定を通じて不要なデータバインディング処理を特定し、改善した経験 - 1. 問題の状況 プロジェクトでは、ReactとTypeScriptを基盤としてExcel業務画面を実装しており、ExcelファイルをWeb上で閲覧・編集できるようにSpreadJS(v17.0.1)を使用しています。ユーザーがアップロードしたExcelファイルをテンプレートとして保存し、詳細画面ではそのテンプレートに実際のデータを連携して画面に表示する構成です。 問題は、Excel詳細画面に入る際の初期ロード時間が極端に長いことでした。画面に入ってもすぐにExcelが表示されず、まず空白画面が表示された後にローディング画面が表示され、さらに一定時間が経過してからExcel画面が表示されました。開発環境で測定した結果、全体のロード時間は約25~30秒でした。使用しているExcelファイルは合計28個のシートで構成されており、シート間の参照や数式なども含まれているため、構造が複雑でした。 このように複雑なロード過程の中で、どの区間が実際に時間を消費しているのかを段階的に測定し
blog Storybookで作る独立したUI開発環境 1. はじめに プロジェクトを進めていると、1つの画面を完成させるまでの過程が、思った以上に複数の作業に分かれていることがあります。 私はプロジェクトで、画面のマークアップ、スタイル、インタラクションなどのUI実装を担当しています。以前までは、パブリッシング用リポジトリと開発用リポジトリが物理的に分かれていたため、私はパブリッシング用リポジトリ内だけで作業していました。画面のマークアップとスタイルを完成させておくと、開発者がそれを開発用リポジトリに移し、ロジックとAPIを組み込むという進め方でした。 しかし、今回のプロジェクトでは構成が変わりました。パブリッシャーが開発用リポジトリに直接入り、ブランチを分けて開発者と並行して作業を進める方式に変わったのです。同じリポジトリ、同じコードベースを共有することで得られるメリットもありましたが、同時に新たな難しさも生じました。同じ画面を同じリポジトリ内で作業しているにもかかわらず、お互いのブランチの作業がまだマージされる前は、完成した画面を一緒に確認することが実際には難しかったのです。 パブリッシング用ブランチで作
blog 20ドルの会話アプリ開発挑戦記 Googleは2026年5月のAndroid Showで、「Create My Widget」という機能を公開しました。この機能は、ユーザーが必要とする機能を自分で説明すると、Geminiが適切なウィジェットを作成してくれるというものです。簡単に言えば、スマートフォンで行うバイブコーディングですね。コーディングはもはや開発者という職種だけのものではなくなり、高性能なノートパソコンも必要とせず、スマートフォンを持っている人なら誰でも自分に必要な機能を直接開発できる時代の幕が開いたのかもしれません。 しかし残念ながら、個人的な業務環境では、セキュリティやレガシーな環境などさまざまな理由から、AIを積極的に活用してみる機会がまだありませんでした。だからといって、レガシー開発において一流の開発者ほど優秀でもない、ただのMediocreな開発者である私は、このような時代に少しずつ取り残されてしまうしかないのでしょうか。 未来のことは分かりませんが、Googleが示した方向性を試してみることにしました。OpenAIのChatGPTで、です(笑)。ビジネスレベルや収益化を目
blog HMAC署名トークンで招待リンクを作成する 1. 一言で先に言うと HMACは、招待リンクをサーバーが発行し、サーバーが検証するときに使う共有秘密鍵です。フロントエンドは署名を計算せず、キーもリンクごとに異なるわけではありません。環境ごとにキーは1つで、リンクごとに異なるのはプールIDと有効期限が入ったペイロードです。 この記事では、人材プールの招待リンクに有効期間を付けるにあたり、HMAC-SHA256署名トークンを選んだ背景、最初に誤解していた点、実際にコードを組み込み、デプロイ設定を分けた過程、そして同じ失敗を繰り返さないために残しておく実務メモをまとめます。理論の整理よりも、「なぜそのときそうしたのか、何が間違っていたのか」を中心に書いています。 2. なぜこの技術を使うことになったのか 2.1 製品要件 班長が人材プールに人を呼ぶときは、共有リンクを使います。既存のリンクは、クエリにプールIDだけを入れる形式でした。リンクを受け取った作業員がアプリに入ると、そのプールへの招待が作成される流れです。問題は、有効期間がなかったことです。一度広まったリンクはいつまでも再び開けてしまい、U
blog NATS JetStreamの障害分析 1. 概要 MSA(Microservice Architecture)ベースのアプリケーションは、1つの巨大なシステムを複数の独立したサービスに分割して構成するアーキテクチャです。各サービスは異なる機能と責任を持ちますが、実際にサービスを提供するためには、サービス間でのデータ連携と状態共有を継続的に行う必要があります。サービス間通信の方式の1つとしてメッセージング方式が使用され、非同期のデータ転送が必要な場合はメッセージブローカー(Message Broker)を利用できます。 Aプロジェクトでは、船舶内の複数のサービス間でセンサーデータやイベントデータを安定的かつ効率的に転送するため、NATSベースの永続メッセージングシステムであるNATS JetStreamを使用しています。NATS JetStreamは、サービス間の非同期メッセージ転送だけでなく、メッセージの保存、Consumerの状態管理、再送などの機能を提供し、継続的に発生する船舶データを安定して処理できるよう支援します。 しかし、実際の開発および運用過程では、船舶サーバーの強制終了やアプリケーシ
blog メインスレッドブロッキングの改善事例 1. 概要 大容量データを扱う場合、データを取得することよりも、取得したデータをどのように加工し、画面に表示するかがパフォーマンスに大きな影響を与えることがあります。 実際のプロジェクトで、数万件のデータを加工してGridに表示する画面を開発した経験があります。単純にサーバーから受け取ったデータをそのまま出力する構成ではなく、互いに分離されたデータを特定の基準で関連付け、複数の値を加工した結果を1つのCellに表示する必要がありました。 開発初期は比較的少ないデータでテストしていたため、パフォーマンスの問題はそれほど顕在化しませんでした。しかし、実際のデータが数万件以上に増加すると、Gridにデータを反映する時点での処理時間が長くなり、データの読み込み中に別の操作を行うと画面がフリーズすることもありました。 最初はGrid自体のレンダリング性能が原因だと考えました。しかし、処理過程を確認すると、Cellデータを作成するために繰り返し実行されていたデータの検索や加工も、かなりのコストを発生させていることが分かりました。 この記事では、当時の経験をもとに、
blog Nginxプロキシでエラー画面を統合する 1. 問題の定義:バラバラなエラー画面とセキュリティリスク エンタープライズおよびマイクロサービスのWebアーキテクチャでエラーが発生すると、エラーが発生した層に応じて表示される画面がバラバラになる問題が発生します。最前線にNginxリバースプロキシがあり、その後段にTomcat内蔵のSpring Bootアプリケーションが接続された環境では、次のような分断された画面が表示されます。 * Spring Boot Whitelabel Error Page: HTTPステータスコード、タイムスタンプ、エラーメッセージが生のままブラウザに表示されます。 * Apache Tomcatのデフォルトエラーページ: サーブレットコンテナレベルで404/500エラーが発生すると、特徴的なグレースケールのTomcat画面が表示されます。 * Nginxのデフォルトエラーページ: バックエンドアプリケーションがダウンしたりタイムアウトしたりすると、無機質な502 Bad Gateway / 504
blog 決裁連携イベントハンドラーの構築 1. はじめに マイクロサービスアーキテクチャ(MSA)環境では、各ドメインサービスが独立したビジネス責任とデータベースを持ちます。本システムの勤怠および休暇ドメイン(Timecard/Leaveサービス)も独自のライフサイクルを実行しますが、ユーザーの休暇申請(LeavePlan)や特殊勤務申請などの主要な業務プロセスには、全社共通ドメインである「外部電子決裁サービス(Approval Service)」との有機的な連携が不可欠です。従来のプロセスは、内部サービス内で承認状態を独自に処理する構造でしたが、全社的な電子決裁の標準化に伴い、外部決裁サービスとの連携パイプラインを構築する必要がありました。しかし、MSA環境で外部決裁システムとの通信を単純な同期(Synchronous)方式で処理すると、決裁サーバーの応答遅延や障害が内部サービスのトランザクションブロッキングとして波及する問題が発生します。また、ユーザーによる申請の提出後、決裁ラインで承認(Approved)、却下(Rejected)、または取り下げ(Withdrawn)が行われる時点は非同期であるため、両
blog LLMが作り、活用するウィキ LLM Wikiは、LLMが特定分野の資料を探して利用する方法をまとめたWikiです。質問に応じて何を先に読むべきかを案内し、資料同士の内容が異なる場合にどの出典を優先するかを知らせます。 まだ確認できていない内容も別途残します。作業中に新たに確認した内容は再びWikiに反映するため、次の作業はすでに整理された基準と残された質問から始められます。 LLM Wikiの必要性 文書が一か所に大量に集まっているからといって、すぐに知識になるわけではありません。必要な資料がどこにあるのか、どの内容が最新なのか、異なる記録が衝突したときに何に従うべきなのかが分からなければ、LLMは大量の文書を読んでも見当違いの答えを出す可能性があります。 図書館が本を積み上げただけの倉庫と異なる理由は、分類と目録があるからです。VUI LLM Wikiも同じ方法で資料を整理します。作業の種類に応じて読む文書を絞り込み、各資料の場所と優先順位を知らせます。 答えを見つけられなかった場合も記録します。暫定的に処理した内容と、まだ確認が必要な質問を残しておけば、LLMは空白を推測で埋
blog 条件付き呼び出しで削減したAPIコスト 1. 概要 前回の記事では、マイデータサービスの運用中に直面したメイン画面の応答遅延を、キャッシュ優先レンダリングとバックグラウンド更新、つまり現在の Stale-While-Revalidate(SWR) パターンと同じ方向性の戦略によって解決した経験を扱いました。今回は、同じサービスの運用で次に直面した課題を扱います。今回は性能ではなく、コストが問題でした。 外部機関照会 API の課金体系が変更され、呼び出し件数そのものが運用コストになったため、「必要なときに毎回すべて照会する」という従来の方式をそのまま維持することが難しくなりました。これを解決するために適用したのが、照会 Timestamp に基づく条件付き呼び出しと、呼び出しタイミングの再配置(バッチ更新 + Lazy Loading)でした。そして今回も、後から振り返ってみると、当時は名前を知らなかったこのアプローチが、HTTP プロトコルが以前から標準で提供してきた条件付きリクエスト(Conditional Request)と同じ発想であることに気づきました。この記事では、問題の状況と解決の過程、そ
blog Scrum AI 【Devlime開発記】Scrum AI導入 1. はじめに スクラムで開発していると、バックログアイテムやユーザーストーリーの作成に思った以上に時間がかかります。会議で「次のスプリントには予約機能を追加しよう」という結論が出ても、開発者の作業は残っています。タイトルを整理し、As-A / I-Want / So-That形式で内容を作成し、受け入れ条件を記載し、ストーリーポイントや価値スコアまで入力しなければなりません。一つだけならすぐに終わりそうですが、スプリントごとに20件前後繰り返すとなると話は変わります。 そこで、DevlimeのScrumモジュールを開発する際に、この作業を減らしてみることにしました。最初に考えた機能は単純でした。要件を入力するとUserStoryの下書きを作成し、過去の類似作業を探してスコアを参考にできるようにする程度です。3週間ほどで終わると予想していました。 実際に開発してみると、予想とは異なりました。AI APIを接続すること自体は難しくなく、試行錯誤の多くは、どの問題をAIに任せるのかを決める過程で発生しました。この記
blog 翻訳自動化パイプライン構築記 -多言語(i18n)リソースを別リポジトリに分離し、自動検証・デプロイまで組み込んだ経験- 1. はじめに 私が担当していた観光地コンテンツサービスでは、韓国語を基準に、英語とウズベク語(ラテン文字・キリル文字の2表記)、カラカルパク語まで、合計4言語・5つの言語パックをサポートしています。 画面のボタン文言から案内メッセージ、バリデーションメッセージまで、すべてのテキストが言語ごとのJSON翻訳ファイルに依存しています。 この記事では、フロントエンドコード内で管理していたこれらの翻訳ファイルを別リポジトリに分離し、翻訳漏れを自動的に検出する検証スクリプトとデプロイスクリプトを作成した経験をまとめます。 2. 従来の方式の限界 分離する前は、フロントエンドプロジェクト内に言語ごとのJSONファイルを配置し、文言の修正依頼が来るたびに基準言語のファイルを修正した後、残り4つの言語ファイルにも同じキーを1つずつ追加していました。 この方式には、大きく3つの問題がありました。 * 文言を1つ修正するだけの軽微な作業でも
blog WebViewのソーシャル認証をRNブリッジへ移行する 1. はじめに 班長ノートのモバイルアプリは、React Nativeがネイティブ機能を担当し、既存のReactウェブアプリケーションをWebViewで提供するハイブリッド構造です。この構造には、すでに実装された画面とビジネスロジックを再利用できるという利点があります。最初は、ソーシャルログインもウェブと同様にWebView内でOAuthページへ遷移すればよいと考えていました。ブラウザで正常に動作していたフローだったため、モバイルでも大きな違いはないだろうと予想していました。 しかし、Googleログインを実際のモバイル環境で実行すると、403 disallowed_useragentエラーが発生しました。原因はOAuthキーやリダイレクトURLではなく、認証画面がアプリの制御する組み込みWebViewで開かれていたことでした。GoogleのポリシーとRFC 8252では、ネイティブアプリのOAuth認証にembedded user-agentを使用しないよう推奨しています。WebViewではホストアプリがコンテンツやCookieにアクセスできる可能性があるため、
blog PostgreSQLのAutovacuumとロック 1. はじめに 運用中のPostgreSQLデータベースでCPU使用率が100%の状態が続く事態が発生し、原因を掘り下げてみると、その中心にはautovacuumがありました。普段はほとんど気にすることのないバックグラウンドプロセスだと思っていましたが、この問題をきっかけに、autovacuumがどのような仕組みで動作し、なぜlock競合まで引き起こす可能性があるのかをある程度理解できました。ここでは、実際に経験した問題をまず整理し、その分析と解決の過程で分かったautovacuumとlockについてまとめます。 2. 何が起きたのか トラフィックが集中する時間帯にバックグラウンドのクリーンアップ処理が遅延し、特定のテーブルのxid年齢が徐々に蓄積していました。最終的にある時点でしきい値を超えたため、PostgreSQLは自動的にautovacuumを1つ強制実行しました。そして、折悪しくその時点でデータの一部に問題があったテーブルをこのプロセスが処理することになり、なかなか終了せず、長時間CPUを占有する状況につながりました。 最初は単純なトラフィック
blog 決済機能の共通コンポーネント実装記 - 決済 SDKを再利用可能なReact共通コンポーネントとして実装した経験 - 1. SDK共通コンポーネントとして実装するに至った経緯 実際の決済画面で処理すべきビジネスロジックは、単にSDKを呼び出すだけにとどまらない幅広いものでした。 * 会員および非会員顧客キーの設定 * 決済金額の変更内容のリアルタイム反映 * バックエンドでの決済データ生成および連携 * インラインおよびポップアップ決済方式の同時サポート * 決済成功時および失敗時のリダイレクト処理 * 決済エラー発生時のバックエンド状態更新 * 重複決済リクエスト防止ロジックの適用 * React Native WebView環境への対応 このようなロジックを各画面で個別に実装すると、重複コードが増えるだけでなく、決済処理ルールの一貫性を維持することが非常に難しくなりま
blog Loopinのvault軽量エントリ適用記 - ダウンロードボタン一つのためにパッケージ全体を取り込んでいた問題を解決するまで - 1. はじめに この第3四半期、社内メッセンジャーサービスLoopinの7.2アップデートを担当しました。その中の一つに、「チャットで受け取ったファイルも自分のファイルボックスに残るようにしたい」という要件がありました。 同じ機能はすでに存在していました。ファイルストレージドメインであるvaultのFileDownloadItemコンポーネントは、ファイル名と容量を表示し、ダウンロードボタンを押すとファイルをダウンロードした後、ユーザーのStashフォルダーに自動登録し、すでに受け取ったファイルであればアイコンを変えて表示するところまで行っていました。ソースは150行ほどの小さなコンポーネントです。チャットで同じものを作り直す理由はなさそうだったので、そのまま利用することにしました。 ところがimportを1行追加しただけでpnpm-lock.yamlに828行が追加され、ビルド成果物にはチャットで使っていないPDFビューアーのワーカーファイル(1MB)が入り込んでいま