RAKSUL TechBlog

RAKSULグループのエンジニアが技術トピックを発信するブログです

AI活用がプロダクト開発チームに浸透した2ヶ月を振り返る

ラクスルの印刷事業でエンジニアリングマネージャーをしている堀です。

直近では、Canvaとラクスルのパートナーシップに伴い、Canvaのデザインデータをそのまま入稿できる機能をチームで開発し、10月末にリリースしました。

本記事では、このプロジェクトを題材に、8月から10月にかけて実践した「プロダクト開発におけるAI活用の登り方」を振り返ります。

想定読者

  • 開発チームでのAI活用の始め方や進め方の具体像がまだ見えていない方
  • 既にAIを活用しており、プラクティスや改善提案の観点で知見を共有いただける方
  • 前提となるCanva連携の開発概要に興味のある方

前提:どんな開発をしていたか

ラクスルは、名刺やチラシなどをオンラインで簡単に注文できる印刷ECプラットフォームです。今回の取り組みでは、Canva上で作成したデザインをそのままラクスルで入稿できる機能を開発し、10月末に公開しました。

  • canva.comでデザインを開き、アプリから RAKSUL を起動すると、デザインサイズに応じた名刺などの購入動線が自動で提示されます。
  • raksul.comの入稿フローで「Canvaから入稿」を選ぶと、Canvaのデザインデータを直接取得して入稿できます。

詳しくはこちら

ラクスルとしての機能はシステムを分割して実装し、raksul.comに組み込んでいます。将来的な横展開を見据え、拡張しやすいアーキテクチャを採用しました。

開発期間と技術選定

本プロジェクトは、8月下旬に本格始動し、約2ヶ月で10月末リリースまで到達しました。短期間で品質とスピードの両立が求められる中、以下の方針をチームで合意しました。

  • 早期にユーザー価値を届けるため、短いサイクルでアウトプットを出す
  • 横展開しやすく、技術的負債になりにくいアーキテクチャを選ぶ
  • AI活用を前提に開発プロセス全体を設計する

技術選定の考え方

ラクスルのECは取り扱う商材やサービスごとに複数のシステムに分割され、さらに「入稿」「決済」「デザインシステム」などのコンポーネントで構成されています。新しいコンポーネントを増やすと横展開は容易になりますが、メンテナンスコストの増大が課題になります。

「Canvaからデザインデータを取得する」コンポーネントをnpmパッケージとして配布する際、特定のフレームワーク前提となると、導入先のECの技術スタックに依存します。そこで、デザインデータを扱う「UIKit」として切り出し、Web標準であるWeb Componentsで実装する方針を採用しました。これによりフレームワーク非依存での再利用性を確保し、将来の展開時に生じうる制約を最小化しました。

新規実装アプリの構成と横展開の想定イメージ

こうした背景の中で、短期間の開発でも質とスピードを両立させるため、AIでスループットを高めてやり切るという意識をチーム全体で共有しました。

AI活用の目標設定とアクション

本題です。

プロジェクト開始時、全社的なAI活用推進に呼応し、チームの目標を「AI Editorをフル活用し、新しい高生産性の開発スタイルを確立する」としました。

まずはチームでその目標についてブレインストーミングを行い、実行可能なアクションを洗い出し、Cursorの活用方針やAIによるコードレビューなどの具体策を合意しました。

アイデア出しイメージ

開発プロセスへのAI組み込み:実体験

当時Copilotの補完やChatは使っていた一方で、エージェントに主導させるVibe Codingの経験が少ないメンバーもいました。水準を引き上げるため、以下の2点に取り組みました。

  1. コマンド化したワークフローで、ほぼ自律的にAIに実装させる体験から始める
  2. 使い方やTipsを同期的に共有する場(モブプロ、勉強会)を定期的に設ける

1. コマンド化したワークフローで自律実装を促す

当時、Devinは利用可能でしたが、複雑な実装ではやりきれないケースが目立ちました。まずはCursorを軸に、やりたいことを柔軟に実現しアウトプットを増やすことを優先しました。

各工程を自動実行するコマンドを用意し、それらを連続実行するワークフロー(例: /full-workflow)を用意。Issueを作成したらコマンドを実行するだけで、TDDによる実装からPR作成まで進むようにしました。コマンドはリポジトリ内に配置し、チームで共通利用できるようにしました。

2025年8月当時はCursorにPlanモードやSlashCommandがなく、mdファイルとルール定義で代替していました。現在は公式機能として提供されており、汎用コマンドはチーム、ドメイン特化はリポジトリという配置が相性が良いと感じています。

README.md

Agents

2. 同期的な場で使い方とコツを共有

コマンドがあっても、どの場面でどう使うかが掴めないと活用の差が出ます。モブプロや共有会で実演し、使い方の底上げを行いました。

例えば、MCPの活用については、以下のような使い方を紹介しました。

  • Canvaが公開しているMCP dev serverを活用し、設計シーケンス図(Mermaid)をラクスルのコードと併せて自動生成
  • Canva Appのコンポーネントやガイドライン適合性を、MCP経由で情報取得しつつ確認
  • Figma MCPとPlaywright MCPを組み合わせ、デザインデータ取得から実装、補正までを半自律化

ラクスル側とCanva側の両コンテキストを与えた状態で設計させると、設計品質の向上が顕著でした。各メンバーの経験に閉じがちな知見は、積極的に共有しました。

活動の結果

Vibe Coding未経験のメンバーからも「活用のハードルが下がり、工夫できるようになった」という声が多く上がりました。開発だけでなく、以下の工程でもAI活用が活発化しました。

  • 要件確認・設計支援
  • 結合試験項目の自動生成
  • レビュー支援(コードレビューの観点出し、差分要約など)
  • ドキュメント自動生成(外部チーム向け資料、MCPの生成など)

定量面でも、プロジェクト進行時のベロシティは過去比で約1.5倍、マージPR数も倍以上に増加。アウトプット増に合わせてペア・モブレビューを増やし、スループット最大化を意識したことが生産性向上に寄与したと考えています。

さいごに

2025年はAIの進化が特に大きく、チーム開発の在り方にも変化をもたらしました。重要なのは、ツールの有無ではなく、チーム全体で「どう使うか」を具体化し、継続的に学習していくことだと実感しています。今回の事例が、AI活用の初手づくりや次の一歩を考えるきっかけになれば幸いです。

n8n-mcp & n8n-skillsで始めるn8nワークフロー生成

はじめに

ノバセルで PdM をしていた山中です。現在は親会社のラクスルへ出向しており、全社の AI 活用推進を担当しています。ノバセルとラクスルの双方で n8n の社内ホスティング環境が整い、自動化や AI 化は日増しに加速しています。

一方で、n8n の柔軟性はエンジニアリング知識を前提にした設計で支えられています。だからこそ非エンジニアには最初の一歩が重く、導入のハードルが高く映ります。私も非エンジニアメンバーに伴走してワークフロー構築を支援していますが、初期構築の壁を越えるには学習コストがかかり、人力だけで支援体制をスケールさせるのは限界があると感じてきました。

では、n8nのワークフローをGeminiやChatGPTに作らせてみると、出来上がるのはノード間が繋がっていないものや、古いバージョンのノードを利用したものなど・・・。精度に問題がありました。 そこで今回は、n8n-mcp と n8n-skills を組み合わせてワークフロー生成をどこまで自動化できるのかを検証してみます。

n8n-mcp / n8n-skills の概要

n8n-mcp は、n8n の 500 以上のノード定義・プロパティ・テンプレートを丸ごと AI から参照できるようにする MCP (Model Context Protocol) サーバーです。Claude などのアシスタントが search_nodesget_nodevalidate_workflow といった MCP ツール経由でノード仕様を照会したり、n8n API を経由してワークフローの作成・更新・実行まで行えます。npx や Docker などでサクッと立ち上げられ、ドキュメント/テンプレート検索、構成バリデーション、ワークフロー差分更新といった “AI による n8n 操作” の下支えをしてくれる存在です。

対になる n8n-skills は、n8n-mcp を正しく使いこなすための Claude Code スキル集です。n8n の式記法、MCP ツールの使い分け、代表的なワークフローパターン、バリデーションエラーの読み解き方、ノードごとの依存関係、さらに JavaScript/Python のコードノードでの定石まで 7 つのスキルとして整理されています。これらを読み込ませることで、AI は n8n-mcp が提供する豊富なデータベースを文脈に応じて呼び出し、現実的なワークフロー設計や自動修正まで一貫して行えるようになります。

二つのツールを組み合わせれば、AI が構築プランニングから実装・実行・デバッグまでを一気通貫で担えるようになります。

今回試すワークフロー

今回は、Slack でボットに声をかけるだけで複数人の Google カレンダーを横断検索し、最適な空き時間を提案・予約まで担ってくれる AI アシスタントを題材にします。面倒なカレンダー確認や候補書き出し、最終的な予定登録をすべて n8n に任せ、ユーザーは Slack 上の自然言語対話だけで調整が完結するイメージです。

利用する主要ツール

  • Slack:ユーザーとのインターフェース(問い合わせと結果通知の両方)
  • Google Gemini:自然言語から参加者や希望日時を抽出する LLM
  • Google Calendar:参加者ごとの予定取得と確定イベントの登録先

2 段階の処理フロー

ワークフロー全体は「空き時間の提案」と「日程の確定」という二つのステップに分けて設計します。

  1. 空き時間の提案
    ユーザーが Slack で「@Bot A さんと B さんの日程調整をお願い」とメンションすると、まず Gemini がメッセージから関係者や制約条件を抽出します。その後、関係者全員の Google カレンダーを取得し、辞退済みの予定や重複を除外しながら空き時間を算出します。計算結果は候補リストとして Slack に返し、ユーザーが選べる状態にします。
  2. 日程の確定
    ユーザーが「12/1 の 10:00 でお願いします」と返信すると、再び Gemini が日時情報を読み取り、本当に全員のカレンダーが空いているかを再確認します。問題がなければ Google Calendar にイベントを自動登録し、完了通知を Slack に返して調整フローを締めます。

この 2 ステップを n8n-mcp と n8n-skills を活用しながら自動生成させ、どこまで実用的なフローに近づけるかを追っていきます。

n8n-mcp 単体で試したケース

まずは n8n-mcp だけを有効化した状態で今回のワークフローを生成してみました。入力したプロンプトは次のとおりです。

このワークフローは、**Slackで日程調整を自動化するAIエージェント**です。ユーザーがSlackでボットにメンションすると、対象者のGoogleカレンダーを確認し、空き時間を提案してくれます。
---
## 主な機能フロー

Slack → AI解析 → Googleカレンダー確認 → 空き時間計算 → Slack返信

## 処理の流れ
**1. トリガー(Webhook)**
- Slackの app_mention イベントを受信
- ボットからのメッセージや削除イベントはフィルタで除外
**2. リクエスト種別の判定**
- **新規リクエスト**(スレッド返信でない場合)→ 候補日程の提示フローへ
- **スレッド返信**(日程確定の返信)→ 日程確定フローへ
---
## 候補日程の提示フロー(新規リクエスト時)
1. **一次回答**: Slackに「リクエストを受け付けました」と即座に返信
2. **AI情報抽出**: Google Geminiで、メッセージから日程調整対象者(例: y.yamanaka)を抽出
3. **Googleカレンダー取得**: 各対象者のカレンダーから予定を取得
4. **予定のフィルタリング**:
   - 「予定なし」設定の予定を除外
   - 欠席(declined)した予定を除外
5. **空き時間計算**: JavaScriptで予定をマージし、空き時間スロットを算出
6. **メッセージ作成**: 日付ごとの空き時間リストを整形
7. **Slack返信**: 候補日程をスレッドに投稿
---
## 日程確定フロー(スレッド返信時)
1. **AI Agent**: ユーザーの返信から確定日時を抽出(例:「12/01 09:00〜10:00で!」)
2. **履歴取得**: スレッドの過去のやり取りを取得
3. **対象者抽出**: 過去メッセージから日程調整対象者を再度抽出
4. **空き確認**: 確定希望日時が本当に空いているか各カレンダーをチェック
5. **結果分岐**:
   - **空いている** → Googleカレンダーに予定を作成し、Slackで確定通知
   - **埋まっている** → 「日程が埋まってしまった」旨をSlackで通知
---
## 使用している主な連携サービス
| サービス | 用途 |
|---------|------|
| **Slack** | トリガー受信、メッセージ送信 |
| **Google Calendar** | 予定取得、予定作成 |
| **Google Gemini** | 自然言語からの情報抽出(対象者、日時) |
---
## 特徴
- **AIによる自然言語理解**: 「y.yamanakaとy.isobeの日程調整をして」のような自然な日本語を解析
- **複数人対応**: 複数の対象者のカレンダーを統合して空き時間を計算
- **日本時間(JST)対応**: タイムゾーンを考慮した日時処理
- **会話形式**: 最初に候補を提示 → ユーザーが選択 → 自動で予定作成という対話型フロー

生成されたワークフローは概ねプロンプトを反映していましたが、次の 2 点で期待と乖離しました。

n8n-mcp単体で作成したワークフロー

  1. AI ノードが組み込まれておらず、自然言語の解釈を別途用意しなければならない構成になった
  2. 候補日程の提示フローしか出力されず、確定フローが生成されなかった

結果として “シンプルな雛形” に留まり、このまま運用に載せるには大幅な加筆が必要という印象でした。

n8n-mcp × n8n-skills で試したケース

次に n8n-skills も読み込んだ状態で同じプロンプトを投げ、構成の差を確認しました。

n8n-skillsを組み込んで作成したワークフロー

こちらでは AI Agent ノードが適切に挿入され、候補抽出と日程確定の双方をつなぐフローが生成されました。Slack の会話コンテキストを保持したまま候補を出し分ける構造など、実際の業務で使えるレベルに一気に近づいた感触です。

一方で、Slack の「スレッド履歴を取得するノード」が誤ってメッセージ送信設定になっていたり、タイムゾーンの考慮漏れで候補日時が正しく算出できないなど、細かなバグは残りました。ただし、これらは n8n-mcp のノード検索で適切なノードを引き直したり、実行履歴を確認して原因を突き止めることで解消できました。スキル導入で “ある程度完成形に近いワークフロー” が得られ、残りはバグ修正やパラメータのチューニングに集中できる、という分業イメージです。

n8n-mcpを利用してノードを修正する様子

まとめ

n8n-skills はベースラインとして十分に整ったスキルセットであり、n8n-mcp と組み合わせることで実用的なワークフローを短時間で組み上げられることが確認できました。デバッグまで含めた一連の作業を AI に任せる道筋も見えてきたため、今後の n8n 実装でも積極的に使っていきたいと感じています。

一方で、実運用に耐えるには自分たちでスキルや手順を微調整する必要も見えてきました。

  • スキルを参照せずに n8n-mcp を直接呼び出してしまい、精度が落ちる
  • n8n のバージョン差によるパラメータ違いを吸収できない
  • 一部ノードはドキュメント上に具体的なパラメータ形式がなく、AI が迷いやすい

こうした特性を押さえておくと、AI に任せる領域と人力で補う領域を切り分けつつ、高い再現性で n8n ワークフローを量産できそうです。

FLUX 2.0を32GBユニファイドメモリMacBookで使ってみた

こんにちは。ノバセルCTOの磯部です。

この記事はノバセル テクノ場 出張版2025 Advent Calendar 2025の1日目です。ノバセルでは毎週「テクノ場」と呼ばれるLightning Talkを行い、技術的な共有を行っています。今年は「テクノ場」の出張版として、アドベントカレンダーとして社外にも向けて技術的な共有を行います。

さて、皆さん画像生成AIは使っておられますか。ChatGPTやGeminiで画像を生成した経験があるエンジニアの方は多いと思いますが、これらはクローズドなモデルであり、カスタマイズ性やコスト、セキュリティの面で課題となることもあります。

そこで注目されるのが、ローカル環境で実行可能なOpen Weightモデル(重みが公開されているモデル)です。そして2025年11月、最新のモデル「FLUX 2.0」が登場しました。

FLUX 2.0は、Stable Diffusionの初期開発メンバーも参加するドイツの「Black Forest Labs (BFL)」によって開発されました。

本記事では、FLUX 2.0のOpen Weight版であるFLUX 2.0 [dev]をComfyUIを使ってローカル環境(32GBユニファイドメモリMacBook)で動かせるのかを検証します。

先にネタバラシをしておくとローカル環境で動かすことはできなかったのでタイトルは釣りです。しかし、公式APIノードを使用することでFLUX 2.0 [pro]をComfyUI上で動かすことができたので、その方法をお教えしたいと思います。

FLUX 2.0とは?Stable Diffusionとの違い

FLUX 2.0 [dev]は、320億(32B)パラメータという、Open Weightモデルとしては非常に大規模です。その性能は、現在最高峰とされるクローズドモデル(GoogleのNano Banana Proなど)には一歩及ばないものの、非常に高い性能とコストパフォーマンスを誇ります。

出典:FLUX.2: Frontier Visual Intelligence

アーキテクチャの刷新:DiffusionからFlow Matchingへ

以前の主流であったStable Diffusion(SD1.5, SDXLなど)は、「Latent Diffusion Model (LDM)」を採用しています。これは、ノイズ画像を徐々に綺麗にしていく(拡散プロセス)ことで画像を生成する技術で、主に「U-Net」という構造が使われます。

一方、FLUX 2.0はStable Diffusion 3.0でも採用されている「Latent Flow Matching」という新しいアプローチを採用しています。これは、拡散プロセスよりも効率的に、ノイズから画像への「流れ(Flow)」を直接学習する手法です。

さらに、コア部分にはU-Netではなく、LLMで広く使われる「Transformer」(Rectified Flow Transformer)が採用されています。

graph LR
    subgraph "Stable Diffusion (Diffusion Model / U-Net)"
        A[ノイズ] --> B(デノイズ Step 1);
        B --> C(...);
        C --> D(デノイズ Step N);
    end

    subgraph "FLUX 2.0 (Flow Matching / Transformer)"
        F[ノイズ] ==> G(Flowを直接辿る);
    end

    D --> I[最終画像];
    G --> I;

このアーキテクチャの刷新により、FLUX 2.0はより効率的で、整合性の高い画像生成を実現しています。

高い表現力

FLUX 2.0は、Mistralベースの強力な視覚言語モデル(VLM)をText Encoderとして搭載しています。これにより、複雑なプロンプトを深く理解し、現実世界の知識に基づいた描写が可能です。

性能比較では、Nano Banana Proがロジックを重視し、指示の正確性や構造的な整合性に優れています。一方、FLUX 2.0は美的センスを重視し、映画的でリッチなビジュアル表現に強みがあると評価されることが多いです。

また、FLUX 2.0には以下のような特徴があります。

  • テキストレンダリング能力: Stable Diffusionが苦手としていた文字の描写能力が向上。広告バナーのキャッチコピー生成に有効。
  • マルチリファレンス生成: 最大10枚の参照画像から、スタイルや被写体の一貫性を維持したまま新しい画像を生成できる。

商用利用の注意点

FLUX 2.0にはいくつかのバリエーションがあります。

  • FLUX.2 [Pro] / [Flex]: API経由で提供される商用モデル。価格計算ページ
  • FLUX.2 [dev]: 今回使用するOpen Weightモデル。

FLUX.2 [dev]は誰でもダウンロード可能ですが、「Open Source」とは異なり、「FLUX [dev] Non-Commercial License」の下で公開されています。

このライセンスの解釈は非常に重要です。

  1. 研究・個人利用: 無料で利用可能。
  2. 生成物(Output)の利用: 生成された画像自体は、禁止用途を除き商用利用可能。
  3. モデルの利用制限: モデル自体を「Non-Commercial Purpose(非商用目的)」以外で利用することは制限されている。

ライセンス条項では、「収益を生む活動」での利用は非商用目的に含まれないと明記されています。したがって、企業が業務の一環として(例:従業員が広告制作のためにローカルPCで画像を生成する)このモデルを利用する場合、それは「商用目的でのモデルの利用」と見なされます。

企業が業務フローに組み込む場合は、BFLから商用ライセンスを取得する必要があります。セルフホスト用の商用ライセンスは、月額$1999(20万枚まで)で提供されています。ライセンスページ

本記事では、あくまで技術検証という位置づけでFLUX.2 [dev]を使用します。

まずは公式デモでFLUX 2.0の性能を体験する

ローカル環境構築の前に、まずはBlack Forest Labsの公式サイトで性能を体験してみましょう。

https://bfl.ai/play

汎用的な冬のキャンペーンバナーを想定し、テキスト描写を試すプロンプトを入力してみます。

プロンプト例:

A promotional banner for a winter campaign. A cheerful Japanese woman in a warm coat is smiling in a snowy city street. Bokeh background with illumination. Text on the image: "冬の集客応援キャンペーン". High quality, commercial photography.

実際の広告として効果的かはさておき、非常にリアルな画像と「冬の集客応援キャンペーン」というテキストが破綻なく描写されています。フォントにはまだ違和感が残りますが、従来のStable Diffusionではこのレベルのテキスト生成は困難でした。

最大10までの素材をインプットとして使用することもできます。どうでしょう?及第点ぐらいはあるんじゃないでしょうか。

ローカル環境でFLUX 2.0を動かす

Open Weightモデルの真価は、ローカル環境で自由にカスタマイズできることにあります。FLUX 2.0を動かすためのインターフェースとして、今回はComfyUIを使用します。

ComfyUIとは?

ComfyUIは、画像生成プロセスをノード(機能ブロック)を繋ぎ合わせることで構築するツールです。

Stable Diffusion用と思われがちですが、ComfyUIは汎用的なプラットフォームであり、FLUXのような異なるアーキテクチャのモデルもサポートしています。処理の流れが可視化されるため、複雑なワークフローも管理しやすいのが特徴です。

勝算

FLUX.2 [dev](32Bパラメータ)は非常に巨大で、本来はデータセンター級のGPU(64GB以上のVRAM)が必要です。しかし、以下の技術によりコンシューマー環境でも動作可能になっています。

  1. 量子化 (Quantization): モデルの精度を維持しつつデータサイズを削減する技術。通常、AIモデルの重みはFP16(16ビット浮動小数点数)やBF16で保持されますが、これをより小さいビット数で表現することでメモリ使用量を削減します。BFLとNVIDIA、ComfyUIチームの協力により、FP8(8ビット浮動小数点数)に量子化されたモデルが提供されており、VRAM要件が約40%削減されます。
  2. Weight Streaming / Offloading: VRAMが不足する場合に、モデルの一部をシステムRAMに退避させる技術。ComfyUIではこの機能が強化されています。

検証環境

今回は以下の環境で検証しました。Apple Silicon MacはメモリをGPUと共有するユニファイドメモリ構造のため、Offloadingとの相性が良く、32GBのメモリがあれば動作が期待できるかも知れません。

  • PC: MacBook Air (15-inch, 2025)
  • Chip: Apple M4
  • Memory: 32GB

ワークフローの解説

ComfyUI公式ドキュメントで提供されているFLUX 2.0 [dev] 用サンプルワークフローをインポートします。

※ 本記事では、ComfyUIのセットアップ、モデルのダウンロード、ワークフローのインポートは完了している前提で進めます。

主要な部分を解説します。

1. モデルのロード (Step 1エリア)

画像生成には、役割の異なる3つのモデルが必要です。

  • UNETLoader: FLUX 2.0の本体(拡散モデル)をロードします。ここではFP8量子化されたflux2_dev_fp8mixed.safetensorsを使用します。
  • CLIPLoader: Text Encoder(VLM)をロードします。CLIP(またはVLM)は、プロンプト(テキスト)をモデルが理解できる数値表現(ベクトル)に変換する役割を担います。FLUX 2.0の強力な言語理解はここに由来します。
  • VAELoader: VAE (Variational Autoencoder)をロードします。画像を効率的な「潜在空間」に圧縮(エンコード)したり、画像に復元(デコード)したりする役割を持ちます。画像生成の計算は潜在空間で行われます。

2. プロンプト入力 (Step 2エリア)

  • CLIPTextEncode: ここにプロンプトを入力します。

3. 参照画像 (Step 3エリア)

  • LoadImage, ReferenceLatent: 参照画像を使用する機能(Multi-Reference)です。今回はまずText-to-Imageを試すため、ReferenceLatentノードをバイパス(無効化)します。

4. サンプリングと保存 (Custom samplerエリア)

  • SamplerCustomAdvanced: ここが画像生成の心臓部です。設定されたステップ数に基づき、Flow Matchingアルゴリズムを用いて画像を生成します。
  • VAEDecode: 潜在空間のデータを画像データに復元し、保存します。

ローカル環境での実践

この環境を使って、先程公式デモで試した冬期キャンペーンバナーを生成してみましょう。

CLIPTextEncodeノードに、同じプロンプトを入力します。解像度は1024x1024(widthheightノードで設定)とします。

A promotional banner for a winter campaign. A cheerful Japanese woman in a warm coat is smiling in a snowy city street. Bokeh background with illumination. Text on the image: "冬の集客応援キャンペーン". High quality, commercial photography.

実行結果

NVIDIA製の高性能GPU(RTX 4090など)であれば1分未満で完了します。

M4 MacBook (32GBメモリ) の環境で、ステップ数20(デフォルト設定)で実行してみましたが、66GBのメモリを必要とし、Swapが大量に発生した結果PCがシャットダウンしてしまいました。

開発元やNVIDIAの公式情報によると、Flux 2.0のメモリ要件は以下のようになっています。

  • デフォルト(FP16/BF16): 約90GBのVRAMが必要。
  • 省メモリモード(lowVRAM): 約64GBのVRAMが必要。

FP8量子化は、メモリ使用量を約40%削減します。これを省メモリモード(64GB)に適用して計算すると、64GB × (1 - 0.40) = **約38.4GB**となります。

正直に白状すると、私はFP16からFP8量子化することで、メモリ使用率が50%近く削減できるものだと思い込んでいました。しかし、実際にはEmbedding層(入力)とLM Head(出力)等の量子化されないレイヤーの存在が大きく、他にも計算中間値用メモリの消費もあり、削減率は50%に大きく届かないようです。

しかし、モデルの実行に約38.4GBが必要だとしても、なぜ66GBもの使用量になってしまったのでしょうか。どうやら、ComfyUIがモデルをロードする際の挙動に起因しそうです。

  • ロード時のメモリ倍増現象: ComfyUIが巨大なモデルファイルをロードする際、一時的にファイルサイズの約2倍のメモリ領域を必要とすることが報告されている
  • ピーク時の計算: Flux 2.0関連ファイル(モデル本体とText Encoder)の合計が33GB〜38GB程度である場合、ロードの瞬間には理論上66GB〜76GBのメモリが一時的に必要となる。観測された66GBという数値は、この現象によるもの。

公式APIノードの使用

ローカル環境で動作しなかったというオチだと少し寂しいのでもう少し調べてみると、Black Forest Labsから公式のAPIノードの提供がありました。

使い方は簡単で、ComfyUIにログインしてクレジットを追加した後に、Node Libraryメニューから [api node] > [image] > [BFL] > [Flux.2 [pro] Image]を選択するだけです。

公式APIノードはText EncoderやVAEを内包しており、実行させるためのワークフローは非常にシンプルです。以下のように「Load Image」と「Save Image」の間を繋ぐだけで動作させることができます。

ただ、この方法だとLoraやControlNetでのカスタマイズは当然できず、ComfyUIを使うメリットはあまりないので、これならばAPIを直接実行するのとあまり変わらないと思います。

今後の展望と代替案

今回の検証では、32GBユニファイドメモリ環境でのFLUX 2.0 [dev]のローカル動作は困難であることがわかりました。APIノードは手軽ですが、ComfyUIの最大の利点であるカスタマイズ性(LoRAやControlNetの利用など)を活かしてFLUX 2.0を使いたい場合、現状では主に2つのアプローチが考えられます。

1. さらなる量子化モデル(FP4)の登場を待つ

現在提供されているのはFP8(8ビット)量子化モデルですが、今後さらにメモリ効率の良いFP4(4ビット浮動小数点数)モデルが登場する可能性があります。実際、前バージョンのFLUX 1系でも、リリースから数ヶ月遅れてFP4モデルが公式から提供されました。FLUX 2.0でも同様にFP4版が登場すれば、32GB環境でも動作が期待できます。

2. Cloud GPUを利用する

今すぐにFLUX 2.0をカスタマイズ環境で使いたい場合、最も現実的で推奨される解決策はCloud GPUサービスの利用です。RunPodのようなサービスでは、高性能なGPU(例: A100やRTX 4090)を時間単位でレンタルできます。

Cloud GPU上にComfyUIを構築すれば、VRAM不足を心配することなく、API経由では不可能な複雑なワークフロー構築や自由なカスタマイズが可能になります。コストはかかりますが、高性能GPUを使っても個人利用の範囲であれば比較的安価に収まるケースも多いです。

ただし、ComfyUIとは別にCloud GPUサービスのアカウント作成や環境設定が必要になるため、本記事では詳細な説明は省略します。

まとめ

本記事では、次世代画像生成モデルFLUX 2.0 [dev] の特徴と、ComfyUIを使ったローカル環境での利用方法について解説しました。

  • FLUX 2.0はFlow MatchingとTransformerを採用し、特にテキスト描写とフォトリアリズムに優れている。
  • FLUX 2.0 [dev]はOpen Weightモデルだが、企業による業務利用には商用ライセンスが必要となる点には十分注意が必要。
  • FLUX 2.0 [dev]は32Bの巨大モデルであり、量子化とComfyUIの最適化を用いても、32GBユニファイドメモリMacBook環境では動作しなかった。
  • FLUX 2.0 [dev]を制限されたリソース下でカスタマイズするには、今後のFP4モデル登場を待つか、Cloud GPUサービスを利用するのが現実的な選択肢となる。
  • FLUX 2.0 [pro]はBlack Forest Labsが提供している公式APIノードを使用することができるが、カスタマイズ性はない。

ラクスル入社して1年を振り返る

はじめに

ラクスル Advent Calendar 2025 1日目を担当する、MBS開発部でDirectorを務めているkasuya(@n.ile)です。

MBS開発部は、ラクスルの中核事業である印刷ECの開発・運用を担当しています。業務内容の詳細については、先日公開されたインタビュー記事をご覧いただければと思います。本稿では、12月で入社1年を迎えるにあたり、この1年間の取り組みを振り返ります。

年間230億円の印刷ECを支える心臓部 ― MBS開発部が挑む「10年後の再設計」|ラクスル株式会社

入社1年目のテーマ:「整備」

入社1年目は、組織とプロセスの基盤整備に注力しました。チームが迷わず課題に取り組める環境を構築すること、具体的には働き方の標準化と体制の最適化を最優先事項として進めてきました。

この1年を漢字1文字で表すなら「整」です。

主な取り組み

1. 明確な生産性指標の設定

MBS開発部は、2025年を通じて開発生産性の向上に取り組んできました。

開発生産性には多様な定義がありますが、当部では仕事量の生産性を主要指標として採用しています。本来は期待付加価値の生産性を追求すべきところですが、まず「開発リズムの確立」という観点から、測定可能性の高いこの指標を選択しました。

開発生産性の考え方については、広木大地氏の整理を参考にしています。

参照:開発生産性について議論する前に知っておきたいこと - Qiita

2025年前半では、問い合わせ対応やデータ抽出などの保守業務と機能開発の比率をスプリント単位で測定し、保守比率の低減を目標としました。業務実態の可視化とボトルネックの特定において一定の成果を得られましたが、改善策の多くが管理画面の提供などの機能開発を伴うため、プロジェクトとして推進する必要があり、まとまったリソース確保が課題となりました。

2025年後半では、全社的なAI活用推進を受け、AI Agentを活用した開発生産性向上を目標に設定しました。Tech本部全体で議論を重ね、mainブランチへのマージPR数を要員数で除した値(Merged PR / Head Count)を指標として採用しました。本指標については本部内でも議論がありましたが、測定の容易性と直感的な理解のしやすさを重視しました。

プロジェクト状況による変動を考慮し、月次および四半期単位でトレンドを追跡しています。AI活用の進展により、前年比約20%の生産性向上を実現しています。

2. 開発環境・ツールの整備

開発環境の整備は重要な課題でした。システムの複雑化に伴い、ローカル開発環境の構築手順も複雑化し、環境構築に多大な時間を要する状況が続いていました。新規メンバーの受け入れ時には、環境構築だけで初週の大半を費やすケースもあり、体制強化の観点からも改善が必要でした。

この課題に対し、Dockerコンテナ化の推進と構築手順の見直しを実施しました。その結果、環境構築時間を数日から数時間へ短縮し、個別環境の差異も最小化することができました。複雑な環境を粘り強く整理してくれた担当メンバーには深く感謝しています。

AI活用については、一部で先行利用されていたCursorを標準ツールとして採用し、全エンジニアにアカウントを付与しました。これにより、AI Agentを活用した開発を本格的に推進する体制を整えました。

導入当初はコードリーディングでの活用が中心でしたが、チーム内外での事例やTipsの共有が活発化し、現在ではVibe Codingの実践も広がっています。コードの9割以上をAIが生成するプロジェクトも出現しており、開発スタイルの変革が進んでいます。

3. スクラムの再定着

5月に新卒研修を修了した3名が、MBS開発部に配属されました。

新卒研修ではスクラムスタイルでの開発実習を行っていましたが、改めて基礎理解を深めるため、「スクラムガイドを読む会」を実施しました。他部門の新卒および若手社員も参加し、全4回に分けてスクラムガイドの内容を解説しました。スクラムの理論、価値基準、チームの役割、イベント、成果物などを扱いましたが、私自身にとっても基礎を再確認する貴重な機会となりました。

また各チームには、スプリントゴールの明確化、ベロシティの継続的確認、レトロスペクティブでの振り返りなど、スクラムの基本原則を徹底するよう働きかけています。

まとめ

入社1年目を振り返ると、「整備」に注力した1年でした。生産性指標の設定、開発環境の改善、スクラムプロセスの再定着という3つの軸で基盤を構築し、チームが安心して業務に集中できる状態を作ることができたと考えています。

2年目は、この基盤の上で、より高い付加価値を生み出すフェーズへ移行していきます。開発生産性の定義も「仕事量」から「期待付加価値」へシフトし、事業成長により直接的に貢献できる開発組織を目指していきます。

マルチフロントエンド環境での統一認証基盤を実現する内部パッケージの設計と実装

はじめまして。エンジニアの小川です。弊社のAIマーケティングチームにてフルスタックで開発を行っています。
本記事では、マルチフロントエンド環境における認証基盤の課題とその解決策として開発した@novasell/auth-kitパッケージの設計思想と実装について詳しく解説します。

はじめに

弊社のAIマーケティングプラットフォームでは顧客ごとの要件や影響範囲を考慮して、機能別に独立したフロントエンドアプリケーションを構築しています。

このようなマルチフロントエンド環境では認証認可の導入において将来的に以下のような課題が見えていました:

  • 認証ロジックの重複実装によるメンテナンスコストの増大
  • 認可設定の一元管理の困難さ
  • Auth0設定の分散による設定ミスのリスク

これらの潜在的な課題を先回りして解決するため、私たちは@novasell/auth-kitという内部パッケージを開発しました。

システム構成と背景

マルチフロントエンド構成を採用した理由

私たちのプラットフォームが複数のフロントエンドアプリケーションで構成されているのには、いくつかの重要な理由があります:

  1. 顧客ごとのカスタマイズ性: 各顧客が必要とする機能だけを有効化し、不要な機能による複雑性を避ける
  2. 独立したリリースサイクル: 機能ごとに異なる開発速度やリリース頻度に対応
  3. 影響範囲の最小化: 一つの機能の変更が他の機能に影響を与えないアーキテクチャ

現在の構成は以下のようになっています:

frontend/
├── .env                  # 環境変数(全アプリで共有)
├── app_a/                # メイン機能アプリケーション
├── app_b/                # サブ機能アプリケーション
├── portal/               # ポータルサイト
├── dashboard/            # 分析ダッシュボード
└── packages/
    ├── auth-kit/         # 認証基盤パッケージ
    └── api-client/       # API通信パッケージ

このような構成では、各アプリケーションが独立している一方で、認証のような横断的な機能をどう実装するかが重要な課題となります。すべてのアプリケーションで個別に認証を実装すると、コードの重複や設定の不整合といったリスクがあります。

Auth0導入の経緯

すでに社内の他プロダクトではAuth0を使用した認証基盤が稼働していました。プロダクト間でのSSOを実現し、ユーザー体験を統一するため、このプラットフォームでも同じAuth0テナントを使用することにしました。

実現したかったこと

1. 柔軟な認可設定

アプリケーションが複数存在するため、以下のような認可制御が必要でした:

  • アプリレベル認可: ユーザーごとにアクセス可能なアプリケーションを制御
  • 認証のみモード: 一部のページでは認証のみで認可チェックをスキップ
  • ページ単位の制御: ページごとに異なる認証・認可レベルを設定

2. 開発者体験の向上

フロントエンド開発者が簡単に認証機能を導入できるよう、以下を実現しました:

  • 2つの導入パターン: シンプルな一括設定と細かい制御が可能な個別設定
  • TypeScript完全対応: 型安全な実装
  • 環境変数の集約: Auth0関連の設定を一元管理

3. セキュリティとユーザー体験

認証後のユーザー体験とセキュリティを両立させるため:

  • セキュアなリダイレクト: ログイン後、元々アクセスしようとしていたページへ安全に自動遷移
  • URL検証: ホワイトリスト方式でリダイレクト先を検証し、オープンリダイレクト脆弱性を防止

実装の詳細

コア実装

1. AuthProvider - 基盤となる認証プロバイダー

AuthProviderは、Auth0のSDKをラップし、アプリケーション全体に認証機能を提供する最も基礎的なコンポーネントです。このコンポーネントの主な責務は以下の通りです:

  • Auth0 SDKの初期化と設定
  • ログイン後のリダイレクト処理
  • セッション管理機能の統合
// src/AuthProvider.tsx
export const AuthProvider = ({ children }: AuthProviderProps) => {
  // ログイン後のリダイレクト処理
  const onRedirectCallback = (appState?: { returnTo?: string }) => {
    const fallback = getAllowedOrigin();
    const returnTo = appState?.returnTo || fallback;
    // セキュアなURL検証を行い、不正なリダイレクトを防止
    const safeReturnTo = getSafeReturnToUrl(returnTo);
    window.location.href = safeReturnTo;
  };

  return (
    <Auth0Provider
      domain={auth0Config.domain}
      clientId={auth0Config.clientId}
      authorizationParams={auth0Config.authorizationParams}
      useRefreshTokens={auth0Config.useRefreshTokens}
      cacheLocation={auth0Config.cacheLocation}
      onRedirectCallback={onRedirectCallback}
    >
      {children}
    </Auth0Provider>
  );
};

2. ProtectedRoute - 細粒度のアクセス制御

ProtectedRouteコンポーネントは、ページやコンポーネント単位で認証・認可を制御する重要な役割を担っています。このコンポーネントの特徴は、認証のみのチェックと、アプリケーションレベルの認可チェックを柔軟に切り替えられる点です。

// src/ProtectedRoute.tsx
export const ProtectedRoute = ({
  appName,
  children,
  authenticationOnly,  // 認証のみか、認可も必要か
  accessDeniedRedirectUrl,
  loadingComponent,
  errorComponent
}: ProtectedRouteProps) => {
  const { isLoading, isAuthenticated, error, login } = useAuth();
  const appAccessStatus = useAccessibleApp(appName);
  const loginInitiated = useRef(false);

  // 未認証の場合、自動的にログインページへ
  useEffect(() => {
    if (!isLoading && !isAuthenticated && !loginInitiated.current) {
      const returnTo = window.location.href;
      loginInitiated.current = true;
      login(returnTo); // 現在のURLを保持してログイン
    }
  }, [isLoading, isAuthenticated, login]);

  // ... 状態に応じた画面表示ロジック
};

3. AppAuthProvider - シンプルな一括設定

開発者の利便性を考慮して、AuthProviderとProtectedRouteを組み合わせたAppAuthProviderコンポーネントを提供しています。これにより、1つのコンポーネントでアプリケーション全体を保護できます:

// src/AppAuthProvider.tsx
export const AppAuthProvider = ({
  children,
  appName,
  authenticationOnly = false,
  accessDeniedRedirectUrl = `${window.location.origin}/access-denied`,
  loadingComponent,
  errorComponent
}: AppAuthProviderProps) => (
  <AuthProvider>
    <ProtectedRoute
      appName={appName}
      authenticationOnly={authenticationOnly}
      accessDeniedRedirectUrl={accessDeniedRedirectUrl}
      loadingComponent={loadingComponent}
      errorComponent={errorComponent}
    >
      {children}
    </ProtectedRoute>
  </AuthProvider>
);

認可システムの実装

アプリケーションレベルの認可は、バックエンドAPIと連携して実現しています:

// src/useAccessibleApp.ts
import { useUser } from './useUser';
import { type UserResponse } from '@novasell/api-client';

export type AppAccessStatus =
  | { status: 'checking' }
  | { status: 'granted', user: UserResponse }
  | { status: 'denied', user: UserResponse }
  | { status: 'error', error: Error };

export const useAccessibleApp = (appName: string): AppAccessStatus => {
  const { user, error } = useUser();

  if (!user) return { status: 'checking' };
  if (error) return { status: 'error', error };

  // ユーザーのアクセス可能アプリリストをチェック
  const hasAccess = user.accessible_apps.some(app => app.name === appName);

  return hasAccess
    ? { status: 'granted', user }
    : { status: 'denied', user };
};

環境変数の統一管理

auth-kitの大きな特徴の一つが、環境変数の統一管理です。Viteのビルド設定を工夫することで、パッケージ内からfrontendディレクトリのルートにある環境変数ファイルを読み込めるようにしました:

// vite.config.ts
export default defineConfig(({ mode }) => {
  // 現在のディレクトリから読み込み
  const currentEnv = loadEnv(mode, process.cwd(), '')
  // 一個上の階層から読み込み
  const parentEnv = loadEnv(mode, path.resolve(__dirname, '../'), '')

  // 環境変数をマージ
  const env = { ...parentEnv, ...currentEnv }

  return {
    // ... その他の設定
    define: {
      // VITE_で始まる環境変数をすべて定義
      ...Object.keys(env).reduce((prev: Record<string, string>, key) => {
        if (key.startsWith('VITE_')) {
          prev[`import.meta.env.${key}`] = JSON.stringify(env[key]);
        }
        return prev;
      }, {})
    }
  };
});

これにより、各アプリケーションは個別にAuth0の設定を持つ必要がなくなり、frontendディレクトリルートの.envファイルで一元管理できます:

# frontend/.env
VITE_AUTH0_DOMAIN=your-tenant.auth0.com
VITE_AUTH0_CLIENT_ID=your-client-id
VITE_AUTH0_AUDIENCE=https://api.example.com
VITE_AUTH0_REDIRECT_URI=http://localhost:3000

パッケージ側では、これらの環境変数を読み取って設定オブジェクトを生成します:

// src/config.ts
const getAuth0Config = (): Auth0Config => {
  const domain = import.meta.env.VITE_AUTH0_DOMAIN;
  const clientId = import.meta.env.VITE_AUTH0_CLIENT_ID;
  const audience = import.meta.env.VITE_AUTH0_AUDIENCE;

  // 必須設定値の検証
  if (!domain) throw new Error('VITE_AUTH0_DOMAIN is required');
  if (!clientId) throw new Error('VITE_AUTH0_CLIENT_ID is required');
  if (!audience) throw new Error('VITE_AUTH0_AUDIENCE is required');

  return {
    domain,
    clientId,
    authorizationParams: {
      redirect_uri: import.meta.env.VITE_AUTH0_REDIRECT_URI || '',
      audience,
      scope: 'openid profile email offline_access',
    },
    ...
  };
};

実際の利用方法

パターン1: AppAuthProvider(シンプルな一括設定)

アプリケーション全体に認証・認可を適用する場合に最適です。このパターンは、すべてのページで認証が必要なアプリケーションや、公開ページが存在しないアプリケーションに適しています:

// frontend/app_a/src/App.tsx
import { AppAuthProvider } from "@novasell/auth-kit";

const App = () => (
  <QueryClientProvider client={queryClient}>
    <AppAuthProvider appName="app_a">
      <TooltipProvider>
        <HashRouter>
          <Routes>
            <Route path="/" element={<Index />} />
            <Route path="*" element={<NotFound />} />
          </Routes>
        </HashRouter>
      </TooltipProvider>
    </AppAuthProvider>
  </QueryClientProvider>
);

このパターンの利点は以下の通りです:

メリット: - 実装の簡潔さ: わずか1つのコンポーネントの追加で認証・認可機能を実装 - 包括的な保護: アプリケーション全体が自動的に保護される - メンテナンスの容易さ: 認証ロジックの変更が一箇所で完結 - 設定の柔軟性: 必要に応じてカスタムローディング画面やエラー画面を設定可能

パターン2: AuthProvider + ProtectedRoute(細かい制御)

公開ページと保護ページが混在するアプリケーションや、ページごとに異なる認証レベルを設定したい場合に適しています。このパターンは、ポータルサイトやコーポレートサイトのように、一部のコンテンツは公開し、一部は認証を必要とする構成に最適です:

// frontend/portal/src/App.tsx
import { AuthProvider, ProtectedRoute } from '@novasell/auth-kit';

function App() {
  return (
    <AuthProvider>
      <Router>
        <Routes>
          {/* 公開ページ - 認証不要 */}
          <Route path="/" element={<HomePage />} />

          {/* 認証のみ必要なページ - ログインユーザーなら誰でもアクセス可 */}
          <Route path="/apps" element={
            <ProtectedRoute
              appName="portal"
              authenticationOnly={true}
              accessDeniedRedirectUrl="/access-denied"
            >
              <AppsPage />
            </ProtectedRoute>
          } />

          {/* 認可も必要なページ - 特定のアプリアクセス権限が必要 */}
          <Route path="/admin" element={
            <ProtectedRoute
              appName="portal"
              authenticationOnly={false}
              accessDeniedRedirectUrl="/access-denied"
            >
              <AdminPage />
            </ProtectedRoute>
          } />

          <Route path="/access-denied" element={<AccessDeniedPage />} />
        </Routes>
      </Router>
    </AuthProvider>
  );
}

このパターンが提供する柔軟性は以下の通りです:

メリット: - ページレベルの制御: 各ページで異なる認証・認可ポリシーを適用 - 段階的な認証: 公開ページ → 認証のみ → 認可必須という段階的なアクセス制御 - UXの最適化: 必要な時だけ認証を求めることで、ユーザー体験を向上 - カスタマイズ性: ページごとに異なるエラー画面やリダイレクト先を設定

API通信との統合

auth-kitは単独で機能するだけでなく、同じく内部パッケージとして開発したapi-clientと密接に連携します。これにより、認証トークンの取得からAPIリクエストへの付与まで、シームレスに処理されます:

// api-clientとの統合例
import { createAuthenticatedApiClient } from '@novasell/api-client';
import { useAuth } from '@novasell/auth-kit';

const useApiClient = () => {
  const { getAccessTokenSilently } = useAuth();

  return createAuthenticatedApiClient({
    baseURL: import.meta.env.VITE_API_URL,
    tokenProvider: getAccessTokenSilently,
  });
};

この統合により、開発者は認証トークンの管理を意識することなく、保護されたAPIエンドポイントにアクセスできます。トークンの更新も自動的に処理されるため、セッションタイムアウトの心配も不要です。

工夫点と技術的な配慮

1. 段階的な導入を可能にする2つのパターン

auth-kitの最大の工夫点は、開発者のニーズに応じて選べる2つの導入パターンを提供していることです。これにより、プロジェクトの規模や要件に関わらず、最適な方法で認証機能を実装できます。

AppAuthProviderパターン(シンプル): - わずか1つのコンポーネントでアプリ全体を保護 - 設定項目を最小限に抑え、デフォルト値で動作 - 新規プロジェクトや小規模アプリケーションに最適

AuthProvider + ProtectedRouteパターン(柔軟): - ページごとに認証・認可レベルを細かく制御 - 公開ページと保護ページの混在が可能 - 既存アプリケーションへの段階的な導入に最適

この2つのパターンにより、以下のメリットを実現しています:

  • 学習コストの最小化: シンプルなケースはシンプルに、複雑なケースは必要に応じて複雑に
  • 段階的な移行: 既存アプリケーションに少しずつ認証を追加可能
  • 柔軟な要件対応: プロジェクトの成長に合わせてパターンを変更可能

2. セキュアなURL検証

認証後のリダイレクト処理は、オープンリダイレクト脆弱性の温床となりやすい部分です。悪意のあるユーザーが、リダイレクト先URLを操作してフィッシングサイトに誘導する可能性があります。

auth-kitでは、ホワイトリスト方式でURLを検証し、安全性を確保しています:

// src/urlValidator.ts
export const getSafeReturnToUrl = (url: string): string => {
  try {
    const urlObj = new URL(url);

    // 許可されたオリジンのみにリダイレクト
    if (allowedOrigins.includes(urlObj.origin)) {
      return url;
    }

    // 不正なURLの場合はデフォルトに
    return getAllowedOrigin();
  } catch {
    return getAllowedOrigin();
  }
};

この実装により、事前に定義された信頼できるドメインへのリダイレクトのみが許可され、外部サイトへの意図しない遷移を防ぎます。

auth-kitが実現した価値

auth-kitパッケージの開発により、マルチフロントエンド環境での認証基盤を効率的に構築できました。

開発効率の向上

  • 迅速な認証導入: 新規アプリケーションでも数分で認証機能を実装可能
  • 学習コストの最小化: Auth0の詳細な知識がなくても適切な認証機能を導入
  • 統一された実装: チーム全体で一貫した認証パターンを使用

運用・保守性の向上

  • 一元管理: 認証ロジックの変更を全アプリケーションへ即座に反映
  • 設定の統一: 環境変数の一元管理により設定ミスを防止
  • 型安全性: TypeScriptによる型定義で実装時のエラーを早期発見

ユーザー体験の向上

  • シームレスなSSO: 複数のアプリケーション間でのスムーズな認証体験
  • 適切なリダイレクト: ログイン後、元々アクセスしようとしていたページへの自動遷移

今後の展望

自動セットアップツールの提供

現在は手動でコンポーネントをラップする必要がありますが、将来的にはCLIツールやコードジェネレーターを提供し、より簡単に認証機能を導入できるようにしたいと思っています:

# 将来のCLI
npx @novasell/auth-kit init --app-name "new-app"

このようなツールにより、ボイラープレートコードの生成や設定ファイルの自動生成が可能になり、さらに開発効率が向上すると考えています。

まとめ

マルチフロントエンド環境における認証・認可の複雑さは、適切な抽象化とパッケージ化により大幅に軽減できます。auth-kitパッケージの開発を通じて、以下の重要な教訓を得ました:

学びと教訓

  1. 初期からの共通化: それぞれで認証機能を実装してしまう前に共通パッケージ化することで、技術的負債の蓄積を防げる
  2. 開発者体験を重視: 2つの利用パターンを提供することで、段階的な導入や様々な要件に柔軟に対応できる
  3. 設定の統一管理: 設定を一箇所で管理することで、アプリケーション間での設定のズレを防止し、Auth0の設定変更時も一度の修正で全体に反映できる

マルチフロントエンド環境での認証基盤構築は決して簡単ではありませんが、適切なアプローチとツールを選択することで、開発効率とユーザー体験の両方を向上させることが可能です。
この記事が、同様の課題に直面している開発チームの参考になれば幸いです。