カテゴリー: JavaScript

  • ESLintのルール無効化とファイル全体への適用方法【初心者向け解説+シングルトンパターンの実例付き】

    TypeScript や JavaScript で開発をしていると、ESLint から警告が出ることがあります。
    例えば、未使用の変数推奨されていない構文に対してエラーや警告を表示してくれます。これは便利ですが、開発中に一時的に無効にしたいケースも出てきます。

    この記事では、ESLint の警告の中でもよく見る @typescript-eslint/no-unused-vars を「一行だけ」または「ファイル全体」で無効にする方法を紹介します。
    加えて、ESLint が非推奨とする var を使った シングルトンパターンの実装例と、それに対するルール無効化の方法も併せて紹介します。


    1. ESLintとは?

    ESLint は JavaScript や TypeScript のコード品質をチェックする静的解析ツールです。
    未使用の変数、インデント、var の使用など、さまざまなスタイルや安全性のルールを設定できます。

    例えば、以下のようなコードを書いたとき:

    const unusedValue = 123;

    SLint は「unusedValue は使われていない」という警告を出します。
    これは @typescript-eslint/no-unused-vars というルールに基づいています。


    2. @typescript-eslint/no-unused-vars を無効にするには?

    このルールが有効になっていると、次のようなコードに警告が出ます:

    function greet(name: string) {
      // name を使わなければ警告される
    }

    一行だけ無効にする方法

    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const unused = "This won't trigger an ESLint warning";

    ファイル全体を無効にする方法

    ファイルの先頭に以下を記述することで、そのファイル内のすべての unused-vars 警告が無効になります。

    /* eslint-disable @typescript-eslint/no-unused-vars */

    一時的に無効にして、再び有効にする方法

    /* eslint-disable @typescript-eslint/no-unused-vars */
    
    const temp = "This won't be warned";
    
    /* eslint-enable @typescript-eslint/no-unused-vars */
    
    const again = "This will trigger a warning";
    

    3. var を使ったときに出る警告と no-var のルール

    現代の JavaScript では、letconst の使用が推奨されています。
    そのため ESLint の no-var ルールが有効だと、次のような警告が出ます。

    var name = "Shota";
    // 警告:'var' declarations are forbidden. Use 'let' or 'const' instead.
    

    しかし、ES5 環境との互換性を保ちたい、あるいは教育・学習目的で var を使用したい場合もあるでしょう。


    4. var を許可するには?

    特定のファイルで var の使用を許可したい場合、ファイル先頭に以下を追加します:

    /* eslint-disable no-var */
    

    特定の行だけを無効にしたい場合は:

    // eslint-disable-next-line no-var
    var value = "Allowed just for this line";
    

    5. var を使ったシングルトンパターンの実装例

    ここからは実用的なコード例として、var を使ってシングルトンを実装する方法を紹介します。
    ES5 環境や古いブラウザをターゲットにしたい場合などに便利です。

    コード全体(ESLintのルール無効化も含む)

    /* eslint-disable @typescript-eslint/no-unused-vars */
    /* eslint-disable no-var */
    
    var Singleton = (function () {
      var instance;
    
      function createInstance() {
        var obj = new Object("I am the instance");
        return obj;
      }
    
      return {
        getInstance: function () {
          if (!instance) {
            instance = createInstance();
          }
          return instance;
        }
      };
    })();
    
    // 使用例
    var a = Singleton.getInstance();
    var b = Singleton.getInstance();
    
    console.log(a === b); // true(同じインスタンス)
    console.log(a);       // "I am the instance"
    

    6. 解説:なぜこの実装でシングルトンになるのか?

    • Singleton は即時関数(IIFE)で囲まれています。
    • instance は関数スコープ内のローカル変数なので、外部から直接アクセスできません。
    • getInstance メソッドを通じて、初回はインスタンスを生成し、2回目以降は同じものを返すようになっています。

    この仕組みにより、どこで呼び出しても 常に同じオブジェクトが返ってくるのです。


    7. ESLintのルールは柔軟に活用しよう

    ESLint はあくまで「ガイドライン」です。プロジェクトや状況に応じてルールを柔軟に無効化・変更して使いましょう。

    例えば:

    • ライブラリ開発中に一時的に未使用変数が出る
    • 古いブラウザに対応するため var を使う必要がある
    • 外部APIの仕様上、未使用に見える変数がどうしても必要

    こうした状況では、**「ルールを完全に無視する」のではなく、「必要な範囲で一時的に制御する」**ことが大切です。


    まとめ

    項目方法
    特定の行で未使用変数の警告を無効化// eslint-disable-next-line @typescript-eslint/no-unused-vars
    ファイル全体で無効化/* eslint-disable @typescript-eslint/no-unused-vars */
    var の警告を抑制/* eslint-disable no-var */
    シングルトン実装即時関数+クロージャで instance を保持

    おわりに

    この記事では、ESLint のルールを制御する方法と、JavaScript での実用的な設計パターンであるシングルトンの例を紹介しました。

    ESLint のルールをすべて守るのではなく、「なぜそのルールがあるのか」を理解した上で、適切に制御することが、柔軟で保守性の高いコードにつながります。

    今後もあなたの開発が快適でスムーズに進むことを願っています!

  • ReactのuseEffect・useCallbackと非同期処理のメモ化戦略:SVG生成を例に徹底解説!

    Reactで開発をしていると、副作用(side effects)をどう管理するかは非常に重要なテーマの1つです。API呼び出し、イベントリスナーの登録、ローカルストレージの読み書きなど、コンポーネントの描画とは直接関係しない処理をうまく扱わないと、バグやパフォーマンス低下につながります。

    この記事では、ReactのuseEffectやuseCallbackの正しい使い方を、OGP画像(Open Graph Protocol image)の生成を例に丁寧に解説します。また、非同期処理のメモ化(memoization)戦略と、開発中のプロダクトfile-binへの応用についても紹介します。


    なぜ useEffect が必要なのか?

    Reactでは、描画(render)とは別に**副作用(side effects)**を扱う必要があります。たとえば、次のようなケース:

    • サーバーからデータを取得
    • ユーザーが入力した値に応じて処理を実行
    • ページロード時に一度だけ何かをしたい

    これらは、すべて useEffect の出番です。

    基本形

    useEffect(() => {
      // 副作用処理
    }, []);

    第二引数に [](空の配列)を渡すことで、初回マウント時にのみ実行されるという仕様になります。依存配列に変数を入れると、それが変化するたびに再実行されます。


    useCallback を使う理由
    次に useCallback。これは、関数の「再生成を防ぐ」ために使います。Reactでは、関数を毎回新しく作るのが普通ですが、依存配列に入れたときに予期しない再実行を防ぐために関数の「参照」を保つ必要があります。

    以下のように書くと、title, filename, template が変わらない限り、同じ関数インスタンスが使われます:

    const handleGenerate = useCallback(async () => {
      const svgMarkup = await createOgpImage(title, filename, template);
      setSvg(svgMarkup);
    }, [title, filename, template]);

    これにより、useEffecthandleGenerate を依存配列に入れても、意図しない副作用の再実行を防げます。


    useEffecthandleGenerate を使う理由

    useEffect(() => {
      handleGenerate();
    }, [handleGenerate]);

    このように、関数を依存配列に入れることはReactのルール上正しい書き方です。handleGenerate の中で title, filename, template を使っている以上、それが変わったら再実行すべきだからです。

    では、逆にこれを空の依存配列 [] にするとどうなるでしょうか?

    useEffect(() => {
      handleGenerate(); 
    }, []); // ← NG!

    この場合、titlefilename が変わっても handleGenerate の中の値は更新されないので、古い状態のSVGを生成し続けるバグになります。


    非同期処理のメモ化はできるのか?

    ここで疑問が出てきます。

    SVGを毎回生成するのはコストが高い。
    同じパラメータなら、前に生成した結果を使えないのか?

    これはいわゆるメモ化(memoization)の話です。

    しかし、useMemoは使えない

    ReactのuseMemoは値のメモ化には使えますが、非同期処理(Promise)には非対応です。以下のようなコードは動きません

    // ❌ useMemo は非同期の値を返せない
    const svgMarkup = useMemo(async () => {
      return await createOgpImage(title, filename, template);
    }, [title, filename, template]);

    解決策:自前でキャッシュを作る

    非同期処理をメモ化するには、MapuseRef を使った手動キャッシュ戦略が有効です。

    シンプルな例

    const cacheRef = useRef<Map<string, string>>(new Map());
    
    useEffect(() => {
      const key = `${title}-${filename}-${template}`;
      const cached = cacheRef.current.get(key);
      if (cached) {
        setSvg(cached);
        return;
      }
    
      const generate = async () => {
        const svgMarkup = await createOgpImage(title, filename, template);
        cacheRef.current.set(key, svgMarkup);
        setSvg(svgMarkup);
      };
      generate();
    }, [title, filename, template]);

    このようにすれば、すでに生成されたSVGはキャッシュされ、再生成をスキップできます。


    実用例:OGP画像のプレビューコンポーネント

    ここまでの技術を使って、以下のような OgpPreview コンポーネントが実装できます。

    export function OgpPreview({ title, filename, template = 0 }) {
      const [svg, setSvg] = useState<string | null>(null);
    
      const handleGenerate = useCallback(async () => {
        const svgMarkup = await createOgpImage(title, filename, template);
        setSvg(svgMarkup);
      }, [title, filename, template]);
    
      useEffect(() => {
        handleGenerate();
      }, [handleGenerate]);
    
      return (
        <div className="ogp-preview">
          {svg && <div dangerouslySetInnerHTML={{ __html: svg }} />}
        </div>
      );
    }

    この設計により、title, filename, template が変わったときだけ新しくSVGが生成され、他のケースでは再利用されるようになります。


    file-binにおける応用

    この記事で紹介した内容は、筆者が開発中のエンドツーエンド暗号化ファイル共有サービス「file-bin」のOGP生成にも活かされています。

    file-binとは?

    file-bin は、以下のような特徴を持つモダンなファイル共有サービスです:

    • エンドツーエンド暗号化でセキュリティ重視
    • ゲストアップロード対応(無料で10MBまで)
    • Pro会員は容量制限の拡張、MyPage機能あり
    • OGP画像の自動生成で共有リンクがリッチに見える
    • AWS Amplify + Next.js + Stripe をフル活用

    この中でも、共有リンクにOGP画像を生成する機能では、まさにここで解説した非同期処理のメモ化と副作用の最適管理が使われています。

    画像生成APIの呼び出し回数が増えると、コストにも影響が出るため、再生成を抑えるキャッシュ戦略は必須でした。


    まとめ

    • useEffect は副作用の管理に必須
    • useCallback で関数の再生成を防ぐ
    • useMemo は非同期には使えないので、手動キャッシュを使う
    • SVGや画像の生成などコストが高い処理にはメモ化戦略が効果的
    • 実際のプロダクト(file-bin)でもこの設計が活きている

    file-bin を使ってみよう!

    あなたも、セキュアかつ手軽にファイルをシェアしたいなら、file-bin をぜひ試してみてください。

    • https://file-bin.com にアクセスして即アップロード
    • 登録すれば Pro 会員として高機能を解放
    • SVGベースのOGP表示で、リンクを送るだけで魅力的なプレビューが表示

  • 【2025年版】nanoidとは?UUIDより短く安全な一意IDの生成方法とfile-binでの活用事例

    一意なIDの生成は、ウェブ開発・API設計・データベース設計などにおいて欠かせない技術です。この記事では、軽量・高速・安全なID生成ライブラリである「nanoid」の使い方を詳しく解説します。また、UUIDとの比較や、実際にnanoidを活用しているファイル共有サービス「file-bin」の事例も紹介します。

    この記事でわかること

    • nanoidとは?基本機能とインストール方法
    • UUIDとの違いと使い分け
    • nanoidのセキュリティと衝突率
    • ファイル共有サービスfile-binでの実用例
    • SEOにも強い短いURLの設計ノウハウ

    nanoidとは何か?

    nanoid(ナノアイディー)は、JavaScriptで使用できる短くて衝突しづらい一意なID(Unique ID)を生成するライブラリです。

    従来、ID生成にはUUID(ユニバーサルユニークID)が広く使われてきましたが、UUIDは「長すぎる」「読みづらい」「URLに適さない」といった課題があります。

    nanoidはこれらの課題を解決するために設計されました。

    主な特徴

    特徴内容
    高速約2倍以上の生成速度(UUID v4比)
    小さいgzip圧縮時で約108バイト
    高セキュリティ暗号学的安全性(CSPRNGを使用)
    衝突しにくい小さなIDでも極めて低い衝突率
    カスタマイズ性長さ・文字セットを自由に変更可能

    nanoidのインストール方法

    Node.jsプロジェクトまたはフロントエンドのWebアプリケーションに簡単に導入できます。

    npm install nanoid

    フロントエンドで使いたい場合はCDNも利用可能:

    <script src="https://cdn.jsdelivr.net/npm/nanoid"></script>

    nanoidの使い方

    デフォルト(21文字)

    import { nanoid } from 'nanoid';
    
    const id = nanoid(); 
    console.log(id); // 例: "V1StGXR8_Z5jdHi6B-myT"

    長さを指定(例: 12文字)

    const shortId = nanoid(12);
    console.log(shortId); // 例: "aK8s2Pz9QbTx"

    カスタム文字セット(数字だけ)

    import { customAlphabet } from 'nanoid';
    
    const numbersOnly = customAlphabet('0123456789', 8);
    console.log(numbersOnly()); // 例: "34829570"

    UUIDとnanoidの比較

    多くの開発者が使用するUUID(バージョン4)とnanoidの違いを以下にまとめました。

    項目UUID v4nanoid
    長さ約36文字(例: 550e8400-e29b...約21文字(カスタム可)
    セキュリティ基本的にない(Math.random等)暗号学的安全性あり
    衝突率極めて低い同等以上に低い
    可読性低い(記号や長さのため)高い(URLやUIに適する)
    カスタマイズ性なし高い
    使用ライブラリuuidパッケージnanoidパッケージ

    結論: 短く安全なIDが必要な場合は、UUIDよりもnanoidを使う方が効率的で実用的です。


    SEO的に強い短いURL設計とは?

    検索エンジンやSNSで共有されるURLにおいて、短くて意味のあるIDやパスを使うことは大きなSEOメリットになります。

    • URLが短いとCTR(クリック率)が上がる
    • SNSで切れずに表示されやすい
    • 共有・コピーが簡単になる
    • ブランディングやユーザー信頼感にも貢献

    nanoidのように、英数字のみで構成された短く予測困難なIDをURLに使用することで、セキュリティSEOの両方を高められます。


    実例:file-binでのnanoid活用

    file-binは、エンドツーエンド暗号化付きのファイルアップロード・共有サービスです。ファイルにアクセスするためのダウンロードリンクに、nanoidを使った短い一意IDを活用しています。

    file-binの主な特徴

    • 最大10MBの匿名ファイルアップロード(ゲスト)
    • 登録ユーザーは大容量ファイル共有が可能
    • 全ファイルを自動暗号化&復号
    • 高速ダウンロードリンク生成
    • 管理者向けのアクセスログ機能付き

    ダウンロードURLの一例

    arduinoコピーする編集するhttps://file-bin.com/d/xYz8T9WqLsA

    この部分(xYz8T9WqLsA)が、nanoidによって生成された短くて安全な一意IDです。

    なぜnanoidを採用?

    file-bin開発者の視点では以下の理由でnanoidが選ばれました:

    1. 短くてユーザーフレンドリー:URLが見やすく、共有しやすい
    2. セキュア:推測されにくく、意図しないアクセスを防止
    3. 高速生成:APIレスポンスを最小化
    4. 簡単に導入できる:追加の構成が不要でメンテナンスが楽

    ユーザー体験(UX)とセキュリティを両立したいなら、UUIDよりもnanoidが正解!


    まとめ:nanoidで次世代のID生成を

    nanoidは、現代のWebアプリケーションに求められる「短くて、安全で、衝突しない」IDを簡単に実現できる素晴らしいライブラリです。

    • UUIDに代わる次世代の一意ID生成手段
    • URL設計・SEO対策にも強い
    • セキュアなWebサービスとの相性抜群
    • 実例:file-binのようなファイル共有サービスで大活躍
  • [React]スプレッド構文って結局なんなの?[JavaScript]

    Reactアプリケーションを開発していると、状態管理は避けて通れない重要なテーマです。特に、配列状のデータを条件に応じて更新する処理は、どのプロジェクトでも頻繁に登場します。この記事では、ReactのsetState関数内で複雑な条件分岐を用いた状態更新ロジックの実例とその解説を行いながら、より理解を深めていきます。また、記事の最後では便利なファイル共有サービス「file-bin」についてもご紹介します。


    状態更新ロジックの実例

    まずはこちらのコードをご覧ください。

    (prev) =>
      prev.map((n) =>
        n.virtual
          ? n
          : n.id === node.id
          ? { ...n, status: "selected" }
          : affected.has(n.id)
          ? { ...n, status: "descendant" }
          : { ...n, status: "up" }
      )

    このコードは、ReactのuseStateuseReducerなどのフックで状態更新を行う際に使われる関数の一例です。ここで行っているのは、状態(おそらくノードの一覧)を条件によって部分的に更新する処理です。

    変数の役割

    • prev: 現在の状態(配列)
    • n: 配列内の1つの要素(ノード)
    • node: 選択されたノード
    • affected: 影響を受けるノードIDの集合(Set型と想定)

    各条件の解説

    1. n.virtual ? n
      • ノードが仮想ノード(virtualがtrue)であれば変更しないでそのまま返します。
    2. n.id === node.id ? { ...n, status: "selected" }
      • 今選択されているノードであれば、状態を"selected"にします。
    3. affected.has(n.id) ? { ...n, status: "descendant" }
      • 影響を受けるノードであれば、状態を"descendant"に更新します。
    4. 上記以外: { ...n, status: "up" }
      • 特に該当しないノードは"up"状態に。

    このように、配列を.map()で処理しながら、条件に応じて個々のオブジェクトをコピー(...n)してステータスだけを変更しています。


    なぜスプレッド構文が必要なのか

    JavaScriptのオブジェクトは参照型なので、元のオブジェクトをそのまま編集してしまうと、副作用が生まれ、Reactがうまく変更を検知できなくなる可能性があります。そのため、状態更新時は必ず新しいオブジェクトを作るのが基本です。

    { ...n, status: "selected" }

    このように書くことで、オブジェクトnの全プロパティをコピーしつつ、statusだけを上書きできます。これは”イミュータブルな更新”と呼ばれ、Reactでは推奨されているパターンです。

    リファクタリングのアイデア

    可読性の高いコードにするためには、条件を整理して関数化するのも有効です。

    const getNextStatus = (n) => {
      if (n.virtual) return n;
      if (n.id === node.id) return { ...n, status: "selected" };
      if (affected.has(n.id)) return { ...n, status: "descendant" };
      return { ...n, status: "up" };
    };
    
    const next = prev.map(getNextStatus);

    このように関数に切り出すことで、.map()の中のロジックがスッキリしますし、再利用も可能になります。


    状態管理に役立つTips

    1. オブジェクトのコピーを忘れずに
    2. .map()の中で直接DOM操作や副作用はNG
    3. 条件が複雑になったら関数に切り出す
    4. 型(TypeScript)を使うとミスが減る

    便利なファイル共有サービス「file-bin」のご紹介

    状態管理とは少し話が逸れますが、開発やチーム作業をしていると「ちょっとこのファイル共有したいな」という場面、ありませんか?

    そんなときに便利なのが file-bin です。

    file-bin の特徴

    • 無料で使える
    • 登録なしでファイルアップロード可能
    • URLをシェアするだけでOK
    • エンドツーエンド暗号化で安心

    ReactやJavaScriptのコードを一時的に共有したいときにも、file-binを使えばURLひとつで送れちゃいます。

    自分の作ったアプリの動作ログ、スクリーンショット、設定ファイルなど、どんなファイルでもすぐにアップして安全に共有可能です。

    開発者なら1度は使ってみて損はないサービスです。

    👉 詳細・ご利用はこちら: https://file-bin.com


    まとめ

    今回の記事では、Reactでの状態更新ロジックとして、map()とスプレッド構文を組み合わせた実例を紹介しました。複雑な分岐条件でも、きちんと整理すれば可読性も保てますし、アプリの品質にもつながります。

    また、開発のちょっとした場面で便利なfile-binもぜひ活用してみてください。React × 開発効率化の組み合わせで、もっと快適なコーディングライフを送りましょう!

  • 【解説】JavaScriptライブラリ「sharp」でPNG画像を生成してみた【Node.js × 画像処理】

    こんにちは。今回は、Node.js環境で使える高速な画像処理ライブラリ「sharp」を使って、PNG画像を生成する方法を詳しく紹介していきます。

    WebアプリやAPI開発において、サーバーサイドで動的に画像を生成するシーンは意外と多くあります。たとえば次のようなケースです:

    • テキスト入りサムネイル画像の自動生成
    • OGP(Open Graph Protocol)画像のダイナミック生成
    • QRコードやバーコードを画像として出力
    • PDFからページごとに画像化する処理の一環

    こうした場面で「sharp」は非常に頼れる存在です。この記事では、sharpでPNG画像を生成・加工し、ファイルとして保存するまでの具体的な流れを解説します。

    sharpとは?

    sharpは、Node.js上で使える高性能な画像処理ライブラリです。特徴としては:

    • C++ベースのlibvipsを利用しているため、非常に高速かつ省メモリ
    • JPEG、PNG、WebP、AVIF、TIFFなど多くの画像フォーマットをサポート
    • リサイズ、トリミング、回転、フォーマット変換、合成など多彩な機能
    • Promiseベースで非同期処理に対応

    npmで簡単に導入でき、バックエンド処理だけでなく、静的サイトジェネレータやバッチ処理でも重宝します。

    npm install sharp

    sharpでPNG画像を生成する基本コード

    まず、シンプルに「空のPNG画像を作成する」コードから見てみましょう。

    const sharp = require('sharp');
    
    // 透明な400x300のPNG画像を生成
    sharp({
      create: {
        width: 400,
        height: 300,
        channels: 4,
        background: { r: 255, g: 255, b: 255, alpha: 0 } // 完全に透明
      }
    })
      .png()
      .toFile('output.png')
      .then(() => {
        console.log('PNG画像が生成されました');
      })
      .catch(err => {
        console.error('エラー:', err);
      });
    

    このように、createオプションで空の画像を定義し、それを.png()で出力しています。背景色をalpha: 1にすれば白背景になります。

    テキストや画像を合成してサムネイル画像を生成する

    もっと実用的な例として、画像上にテキストや他の画像を合成してOGP画像のようなものを生成してみます。

    1. ベース画像の作成

    const sharp = require('sharp');
    
    const width = 800;
    const height = 418;
    
    const background = {
      create: {
        width,
        height,
        channels: 4,
        background: '#282c34'
      }
    };
    
    const base = sharp(background).png();

    2.テキストの画像を作成(SVG形式で)

    const titleSvg = `
    <svg width="${width}" height="${height}">
      <style>
        .title { fill: white; font-size: 48px; font-family: sans-serif; }
      </style>
      <text x="50" y="100" class="title">SharpでOGP画像を作成してみた!</text>
    </svg>
    `;
    
    const titleBuffer = Buffer.from(titleSvg);

    3.合成してPNG画像として出力

    base
      .composite([
        { input: titleBuffer, top: 0, left: 0 }
      ])
      .png()
      .toFile('ogp.png')
      .then(() => {
        console.log('OGP風画像が生成されました');
      });

    このように、SVGでテキストを記述してからsharpで画像上に合成することで、簡単にダイナミックなサムネイルが作成できます。

    PNG圧縮率を変えるオプション:.png({ compressionLevel })

    PNGは可逆圧縮形式であり、品質を犠牲にせずにファイルサイズを最適化できます。sharpでは、.png()に次のようなオプションを渡すことで圧縮率を制御可能です。

    .png({ compressionLevel: 9 }) // 最も高圧縮

    compressionLevel09の範囲で指定できます:

    • 0: 圧縮なし(最速、ファイルサイズ大)
    • 9: 最大圧縮(最も遅いがファイルサイズ小)

    たとえばバッチ処理でサイズ最小化を優先する場合はcompressionLevel: 9を、リアルタイムな生成が必要な場合は56程度がおすすめです。

    バッファで返す / Base64エンコードでWebに埋め込む

    PNG画像をファイルとして保存せず、バッファ形式で得てWebに埋め込みたい場合もあります。

    const buffer = await sharp({
      create: {
        width: 200,
        height: 200,
        channels: 4,
        background: '#00f'
      }
    }).png({ compressionLevel: 6 }).toBuffer();
    
    const base64 = buffer.toString('base64');
    const dataUrl = `data:image/png;base64,${base64}`;
    
    console.log(dataUrl); // HTMLのimgタグに埋め込める

    生成したPNG画像をdata:image/png;base64,...形式でHTMLやOGP用メタタグに直接埋め込めるため、CDNを使わず画像を渡したいときに便利です。

    使用上の注意点

    • 圧縮率を高くすると処理時間が伸びるため、用途に応じたバランスが必要です。
    • PNGは可逆形式のため、画質は劣化しませんが、写真用途にはWebPやJPEGの方がサイズが小さく済むケースがあります。
    • 圧縮オプション以外にも、alphaチャンネルを含むかどうかでもファイルサイズは大きく変わります。

    応用:QRコードやバーコードと連携

    「sharp」は画像の合成にも強いため、別のライブラリ(たとえばqrcodebwip-js)と組み合わせて、QRコードを含んだPNG画像を自動生成することもできます。

    const QRCode = require('qrcode');
    
    QRCode.toBuffer('https://example.com').then(qrBuffer => {
      sharp({
        create: {
          width: 500,
          height: 500,
          channels: 4,
          background: '#ffffff'
        }
      })
        .composite([{ input: qrBuffer, top: 100, left: 100 }])
        .png({ compressionLevel: 6 })
        .toFile('qr_image.png');
    });

    こういった自動化処理は、Eコマースやチケットサービスでも活用できますね。


    生成した画像を共有するなら:file-binがおすすめ

    今回のようにsharpで生成したPNG画像を手軽に共有したいときにおすすめなのが、**Shota Ninomiya氏が開発中のファイル共有サービス「file-bin」**です。

    file-binの特徴:

    • ゲストユーザーでも最大10MBまでアップロード可能
    • ログインユーザーは大容量ファイルも利用可能
    • アップロード時にエンドツーエンド暗号化(E2EE)&圧縮を自動実行
    • ダウンロードリンクにOGP画像や説明文(メタデータ)を設定可能

    「生成したPNGをアップロード → SNSで即共有」という流れがスムーズに行えるため、開発中のツールやプロトタイプ共有にも最適です。

    今後のアップデートでは、ダウンロード通知やアクセス制限機能なども追加予定とのことで、セキュリティに配慮しつつ画像を共有したいユーザーには非常に心強いサービスとなりそうです。

    ベータ版を公開中です!フィードバックをしてくださる方を募集中です!

    https://file-bin.com


    まとめ

    この記事では、JavaScriptライブラリ「sharp」を使ってPNG画像を生成・加工・出力する一連の流れを解説しました。

    sharpのポイント:

    • 高速かつ高機能な画像処理ライブラリ
    • PNGの背景・テキスト合成・他画像との合成が簡単
    • .png({ compressionLevel })で圧縮率を制御できる
    • ファイル保存、Base64埋め込み、API出力など応用自在
    • QRコード・OGP生成にも相性抜群
    • 共有はfile-binのような暗号化サービスと連携すると便利

    sharpは、サーバーサイドでの画像処理における最強の相棒といっても過言ではありません。特にPNG生成では、透明背景や高圧縮による活用の幅が広がるため、アイデア次第でいろいろな場面に応用できるでしょう。


    何か気になる点やsharpの応用例があれば、コメントで教えてください!file-binに関しても、別記事で詳しく取り上げていく予定です。

  • ブラウザ上でのファイル圧縮処理:ユーザー体験とパフォーマンスの最適化を両立する技術

    はじめに

    近年、Webアプリケーションはますます高機能化しており、デスクトップアプリケーションと遜色ないユーザー体験を提供することが求められるようになりました。その中で注目されているのが「ブラウザ上でのファイル圧縮処理」です。従来であればサーバー側で行っていた圧縮処理を、クライアントサイドで実行することで、通信負荷を軽減し、ユーザーの待ち時間を短縮することが可能になります。

    この記事では、ブラウザでのファイル圧縮処理の実現方法、使用される技術、メリット・デメリット、実装時の注意点などを詳しく解説します。

    なぜブラウザでファイルを圧縮するのか?

    通信量の削減

    ユーザーが大きなファイルをアップロードする場合、ネットワーク帯域に大きな負担がかかります。特にモバイル回線や低速ネットワーク環境では、アップロードにかかる時間がユーザー体験を大きく損ねます。事前にクライアント側でファイルを圧縮してから送信することで、通信コストと時間を大幅に削減できます。

    サーバー負荷の軽減

    サーバーで圧縮処理を行うにはCPUリソースが必要です。アクセス数が多くなると、サーバー側の負荷が急激に増し、アプリケーション全体のパフォーマンスに悪影響を及ぼす可能性があります。クライアントサイドで圧縮を済ませることで、サーバーはより多くのリクエストに対応可能になります。

    プライバシー保護

    E2E(エンド・ツー・エンド)暗号化と組み合わせることで、ファイルの内容をサーバー管理者にも見せずに保管・転送することができます。圧縮と暗号化を事前にブラウザで済ませることで、セキュアかつ軽量なファイル転送が実現します。

    ブラウザで使える圧縮アルゴリズム

    1. Gzip(pako)

    pako は、JavaScriptで書かれた高速なgzipライブラリです。ほぼネイティブのzlibと互換性があり、Node.jsとの連携や、サーバーとのフォーマット整合性を確保したいときに便利です。

    import pako from 'pako';
    
    const input = new TextEncoder().encode("Hello, world!");
    const compressed = pako.deflate(input);
    const decompressed = pako.inflate(compressed, { to: 'string' });

    2. zip(fflate, JSZip)

    Webアプリで複数ファイルの一括アップロードを実現する場合、Zip形式が便利です。fflateは非常に軽量で高速なライブラリで、Web Workerとの親和性も高く、最近の主流になっています。

    import { zipSync, strToU8 } from 'fflate';
    
    const zipped = zipSync({ 
      'hello.txt': strToU8('Hello, Zip!') 
    });

    3. Brotli(wasm実装)

    Brotliはgzipよりも高い圧縮率を誇る新しいアルゴリズムですが、JavaScriptでの実装は限られており、通常はWebAssembly(Wasm)経由で実現されます。読み込みコストがあるため、用途に応じて使い分けが必要です。

    ファイル圧縮の実装例(画像圧縮)

    画像のように容量が大きく、かつユーザーが頻繁にアップロードするファイル形式では、圧縮による効果が顕著です。以下は、JSZip を使った画像の圧縮例です。

    // ファイルをZIP圧縮する
    export async function compressFile(file: File): Promise<ArrayBuffer> {
      const zip = new JSZip();
      
      // ファイルをZIPに追加
      zip.file(file.name, file);
      
      // ZIPを生成してArrayBufferで返す
      return await zip.generateAsync({
        type: 'arraybuffer',
        compression: 'DEFLATE',
        compressionOptions: {
          level: 9
        }
      });
    }

    このように、圧縮処理は数行のコードで実装できるうえ、ユーザーの体感パフォーマンスが大きく改善されます。

    実装時の注意点

    1. ブラウザの互換性

    圧縮処理にはTypedArrayやBlob、ArrayBufferなどのAPIが必要です。古いブラウザではサポートされていないことがあるため、ポリフィルの導入や代替手段の検討が必要です。

    2. 大容量ファイルへの対応

    ファイルサイズが非常に大きい場合、JavaScriptのメモリ制限やGC(ガベージコレクション)によるパフォーマンス低下が発生する可能性があります。Web Workerを使ってメインスレッドをブロックしないよう設計することが重要です。

    3. セキュリティ

    圧縮処理の際に、ユーザーが意図しない内容が含まれていたり、処理結果が可逆的である場合は注意が必要です。圧縮処理の前後でMIMEタイプの確認やウイルスチェックを挟むなど、セキュリティ対策も併せて実装しましょう。

    圧縮処理とE2E暗号化の連携

    前述のように、圧縮と暗号化を組み合わせることで、高速かつ安全なファイル共有が可能になります。たとえば次のようなフローです:

    1. ブラウザでファイルをZip圧縮
    2. 圧縮済みバイナリに対してAESで暗号化
    3. 暗号化ファイルをアップロード
    4. ダウンロード時に復号&展開

    これにより、通信経路だけでなく保存時も含めた完全な秘匿性が担保されます。

    今後の展望

    WebAssemblyの進化により、ブラウザ上でのファイル処理能力はますます強化されています。今後はさらに高性能な圧縮ライブラリが登場し、ネイティブアプリに近い操作性をWebアプリ上で再現できるようになるでしょう。また、Zero-Knowledge Proofや符号理論を組み合わせた次世代のファイル圧縮・認証スキームの実装にも期待が集まっています。

    file-bin.comの紹介

    file-binは、セキュアなファイル共有を目的としたモダンなWebアプリケーションです。E2E暗号化をベースにした設計で、アップロードされたファイルはサーバーにも中身が見えない状態で保存されます。

    主な特徴は以下の通りです:

    • クライアントサイドでの暗号化・圧縮を実施(ファイル内容はブラウザ内でAES暗号化されてからアップロード)
    • 容量制限とユーザー制御:ゲスト、一般会員、Pro会員といったロールによってファイルサイズ制限を制御
    • OGP対応のダウンロードリンク生成で、共有時も美しい表示が可能
    • IPアドレスやUser-Agentの記録による安全性担保
    • MyPage機能でファイル管理・統計表示

    file-bin.comは、ブラウザだけで完結するスマートなファイル共有体験を実現したい全ての人におすすめのサービスです。

    現在ベータ版を公開中です!フィードバックをいただける方募集中です!

    https://file-bin.com

    まとめ

    ブラウザ上でのファイル圧縮処理は、パフォーマンスの最適化セキュリティの向上を両立させるための重要な手段です。軽量なライブラリと正しい設計を組み合わせれば、驚くほど簡単に導入することができます。そして、file-binのような最新サービスは、まさにこの技術の恩恵を最大限に活用しています。

    これからのWeb開発では、ユーザーの手元でできる処理を積極的に活用し、サーバーの役割を最小限に抑える設計思想がますます重要になっていくでしょう。

  • PrismaをJavaScriptプロジェクトで使ってMySQLを簡単に操作する方法

    この記事では、JavaScriptプロジェクトにPrismaを導入して、MySQLデータベースに接続し、簡単に操作する方法をわかりやすく解説します。

    Prismaとは?

    Prismaは、Node.jsやJavaScriptプロジェクトで使えるORM(Object-Relational Mapping)ツールです。これを利用すると、SQLを書かずに直感的で安全にデータベースを操作できます。

    Step1:Prismaのインストール

    まず、npmを使ってPrismaをプロジェクトに追加します。

    npm install @prisma/client
    npm install prisma --save-dev

    次に、Prismaの初期設定を行います。

    npx prisma init

    このコマンドを実行すると、プロジェクト内にprisma/schema.prismaというファイルが生成されます。

    Step2:データベース接続設定

    schema.prismaを開き、MySQLデータベースへの接続設定を記述します。

    datasource db {
      provider = "mysql"
      url      = env("DATABASE_URL")
    }
    
    generator client {
      provider = "prisma-client-js"
    }

    プロジェクトのルートにある.envファイルに接続情報を設定します。

    DATABASE_URL="mysql://ユーザー名:パスワード@ホスト名:ポート番号/データベース名"

    Step3:データモデルの定義

    次に、データモデルを定義します。

    model User {
      id        Int      @id @default(autoincrement())
      name      String
      email     String   @unique
      createdAt DateTime @default(now())
    }

    Step4:データベースに反映

    モデルの設定が終わったら、次のコマンドでデータベースに反映させます。

    npx prisma migrate dev --name init

    これでPrismaがMySQLにテーブルを作成します。

    Step5:JavaScriptでPrismaを使う

    以下のようにPrismaを使ってデータを操作できます。

    // PrismaClientのインスタンスを生成
    const { PrismaClient } = require('@prisma/client');
    const prisma = new PrismaClient();
    
    async function main() {
      // ユーザーの作成
      const newUser = await prisma.user.create({
        data: {
          name: 'John Doe',
          email: 'john@example.com'
        }
      });
    
      console.log('作成したユーザー:', newUser);
    
      // ユーザー一覧を取得
      const users = await prisma.user.findMany();
      console.log('ユーザー一覧:', users);
    }
    
    main()
      .then(async () => {
        await prisma.$disconnect();
      })
      .catch(async (e) => {
        console.error(e);
        await prisma.$disconnect();
        process.exit(1);
      });

    まとめ

    Prismaを使えば、JavaScriptプロジェクトから簡単かつ安全にMySQLを操作でき、開発速度を大幅に向上させることができます。ぜひプロジェクトで試してみてください。

  • ESLintで特定の箇所だけvarを許可する方法

    背景

    JavaScriptでシングルトンを実装していた際に、どうしてもvarを使用する必要がありました。

    プロジェクトの設定でESLintを利用しているため、varを使うと以下のようなエラーが発生しました。

    7:3  Error: Unexpected var, use let or const instead.  no-var

    普段はletconstを使用するのが推奨されていますが、特別なケースとしてシングルトン実装などでスコープを明示的に調整するため、あえてvarが必要になることがあります。

    特定箇所だけルールを無効化する方法

    ESLintには、一時的に特定のルールを無効化するコメントがあります。

    単一行で許可する場合

    varを許可したい行の直前に次のコメントを追加します。

    // eslint-disable-next-line no-var
    var singletonInstance = null;

    複数行で許可する場合

    複数行にわたり許可したい場合は、以下のように囲みます。

    /* eslint-disable no-var */
    var singletonInstance = null;
    var anotherVar = 123;
    /* eslint-enable no-var */

    注意点

    • 原則として、varを利用するのは避け、可能な限りletまたはconstを利用しましょう。
    • 特定のケースでのみ、意図を明確にコメントに残したうえで許可するのがベストプラクティスです。

    以上の方法で、ESLintルールを柔軟に適用しつつ、特殊な実装にも対応できます。

  • JavaScriptの==と===の違い

    JavaScriptを学ぶときに避けて通れないのが、=====の違いです。これらの演算子はどちらも「等しいかどうか」をチェックするために使いますが、実際にはその動作が大きく異なります。この記事では、=====の違いを明らかにし、それぞれがどのような場合に使用されるべきかを解説します。

    1. ==(抽象等価演算子)

    ==は「抽象等価演算子」とも呼ばれ、2つの値が「等しいかどうか」を比較します。しかし、この演算子は自動的に型変換(Type Coercion)を行います。つまり、比較する値が異なるデータ型であった場合でも、JavaScriptは自動的に型を変換して比較を試みます。

    例えば:

    console.log(5 == "5"); // true

    上記のコードでは、5(数値)と"5"(文字列)が比較されています。==演算子はまず文字列を数値に変換し、その後に比較を行うため、結果はtrueになります。

    2. ===(厳格等価演算子)

    一方で、===は「厳格等価演算子」と呼ばれ、値だけでなくデータ型も同時に比較します。つまり、2つの値が異なるデータ型であった場合、型変換を行わずにfalseを返します。

    例えば:

    console.log(5 === "5"); // false

    こちらでは、数値の5と文字列の"5"が比較されていますが、型が異なるため結果はfalseになります。

    3. =====、どちらを使うべき?

    多くのJavaScript開発者は、型変換による予期しないバグを避けるために===(およびその反対の!==)を使用することを推奨しています。型変換が必要ない限り、===を使用することでより予測可能で安定したコードを書くことができます。

    もちろん、特定のシナリオで型変換が意図された場合、==を使うこともありますが、その場合は注意深く意図を確認し、必要に応じてコメントを残しておくと良いでしょう。

    4. まとめ

    • == は抽象等価演算子で、異なるデータ型間で型変換を行い比較を行う。
    • === は厳格等価演算子で、型変換を行わずに値とデータ型の両方を比較する。

    JavaScriptで予測可能な挙動を実現するためには、===を使用することが推奨されます。特に大型プロジェクトやチーム開発では、型の違いによるバグを防ぐためにも厳格等価演算子をデフォルトとする方が良いでしょう。

    JavaScriptの理解が深まるにつれ、これらの演算子の使い分けができるようになると、より安全で信頼性の高いコードが書けるようになるはずです。

  • JavaScriptとMySQLでトランザクションを実現する方法

    導入

    データベーストランザクションは、一連の操作を一つの単位として扱い、全ての操作が完了するか、一つでも失敗した場合には全てを元に戻す(ロールバックする)ための仕組みです。これにより、データの整合性と安全性が保たれます。本記事では、Node.js環境でMySQLデータベースを扱う際のトランザクションの基本的な使い方を解説します。

    環境設定

    まず、mysqlモジュールをプロジェクトにインストールする必要があります。Node.jsのプロジェクトルートで以下のコマンドを実行してください。

    npm install mysql

    データベース接続

    次に、MySQLデータベースへの接続を設定します。この例では、データベースの設定を含むcreateConnectionメソッドを使用しています。

    const mysql = require('mysql');
    
    const connection = mysql.createConnection({
      host: 'localhost',
      user: 'username',
      password: 'password',
      database: 'databaseName'
    });
    
    connection.connect(err => {
      if (err) throw err;
      console.log('データベースへの接続に成功しました!');
    });

    トランザクションの開始

    トランザクションを開始するには、beginTransactionメソッドを使用します。これにより、以降のクエリが一つのトランザクションとして扱われます。

    connection.beginTransaction(err => {
      if (err) throw err;
      // トランザクション開始後の処理
    });

    トランザクション内のクエリ実行

    トランザクション内で複数のクエリを実行します。各クエリが成功したことを確認しながら進めます。

    クエリが失敗した場合、ロールバックが行われます。

    クエリにある「?」には、左から順にデータ配列の中身が挿入されます。これによって、SQLインジェクション攻撃を防いでいます。

    const query1 = 'INSERT INTO table_name (column1) VALUES (?)';
    const data1 = ['value1'];
    
    connection.query(query1, data1, (err, results) => {
      if (err) {
        return connection.rollback(() => {
          throw err;
        });
      }
      
     const query2 = 'UPDATE table_name SET column2 = ? WHERE column1 = ?';
     const data2 = ['value2', 'value1'];
    
     connection.query(query2, data2, (err, results) => {
       if (err) {
         return connection.rollback(() => {
           throw err;
         });
       }
       // コミット処理へ
     });
    });

    トランザクションのコミットと終了

    全てのクエリが成功した場合、commitメソッドを呼び出してトランザクションをコミットします。

    connection.commit(err => {
      if (err) {
        return connection.rollback(() => {
          throw err;
        });
      }
      console.log('トランザクションのコミットに成功!');
    });

    まとめ

    このように、Node.jsとMySQLを使用してトランザクションを管理することで、データの整合性を確保しつつ効率的にデータベース操作を行うことができます。トランザクションの正しい扱い方を学ぶことは、任意のバックエンド開発者にとって重要なスキルの一つです。