<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>to-R</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/" />
    <link rel="self" type="application/atom+xml" href="https://blog.webcreativepark.net/atom.xml" />
    <id>tag:blog.webcreativepark.net,2013-08-10://2</id>
    <updated>2024-05-22T02:17:04Z</updated>
    
    <generator uri="http://www.sixapart.com/movabletype/">Movable Type 5.2.7</generator>

<entry>
    <title>pyenvを利用してvenvで作成した仮想環境内のPythonバージョン</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2024/05/22-105737.html" />
    <id>tag:blog.webcreativepark.net,2024://2.1746</id>

    <published>2024-05-22T01:57:37Z</published>
    <updated>2024-05-22T02:17:04Z</updated>

    <summary>venvで作成したPythonの仮想環境内にインストールされているバージョンを変...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="Python" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p>venvで作成したPythonの仮想環境内にインストールされているバージョンを変更してみました。</p>]]>
        <![CDATA[<h2>pyenvのインストール</h2>

<p>Pythonのバージョン管理にはpyenvを利用します。</p>

<p>brewを使ってインストール</p>

<pre><code>brew install pyenv</code></pre>

<p>.zshrcに以下の設定を追加します。</p>

<pre><code>export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
</code></pre>

<p>変更後にsourceコマンドで変更した内容を反映します。</p>

<pre><code>source ~/.zshrc</code></pre>

<h2>Pythonバージョン変更</h2>

<p>以下のコマンドでインストール可能なバージョンを調べることができます。</p>

<pre><code>pyenv install --list
</code></pre>

<p>今回はPythonの3.9.7をインストールしたかったので以下のコマンドでインストールしてローカル環境に反映します。</p>

<pre><code>pyenv install 3.9.7
pyenv local 3.9.7
python --version # Python 3.9.7
</code></pre>

<h2>venvの仮想環境内に反映</h2>

<p>ローカル環境のPythonのバージョンを変更して仮想環境内を確認してみましょう</p>

<pre><code>source venv/bin/activate
python --version # Python 3.12.2
</code></pre>

<p>まだ、仮想環境内はまだバージョンが変わっていません。</p>

<p>一度--clearオプションをつけて仮想環境を作り直すことで現在のPythonバージョンが反映されるようになります。</p>

<pre><code>deactivate
python3 -m venv venv --clear
source venv/bin/activate
python --version # Python 3.9.7
</code></pre>

<h3>参考サイト</h3>

<p><a href="https://dev.classmethod.jp/articles/change-venv-python-version/">venvで作成した仮想環境内のPythonバージョンを変更したい</a><br />
<a href="https://envader.plus/course/8/scenario/1076">pyenvで複数のバージョンのPythonをインストールしよう！</a><br />
<a href="https://qiita.com/teri_man95/items/b15fd8f1964612331be2">pyenvの使い方</a></p>]]>
    </content>
</entry>

<entry>
    <title>Tailwind CSSで任意のメディアクエリを指定する</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2023/11/02-004008.html" />
    <id>tag:blog.webcreativepark.net,2023://2.1745</id>

    <published>2023-11-01T15:40:08Z</published>
    <updated>2023-11-02T01:29:32Z</updated>

    <summary> Tailwind CSSでは簡単にメディアクエリの指定が可能です。...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="CSS" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p><img src="https://blog.webcreativepark.net/images/tailwind-css-arbitrary-variants-420-66ffd499947b32a33c625e21df8230fc.webp" style="width:100%" alt="Tailwind CSS"/></p>

<p>Tailwind CSSでは簡単にメディアクエリの指定が可能です。</p>]]>
        <![CDATA[<h3>基本的な利用方法</h3>

<p>基本的にはtailwind.config.jsのscreensに指定されているブレークポイントをもとにメディアクリの指定が可能です。</p>

<pre><code>// tailwind.config.js
module.exports = {
  theme: {
    screens: {
      sm: "640px",
      md: "768px",
      lg: "1024px",
      xl: "1280px"
    },
  },
};</code></pre>

<p>このように指定されていれば、以下のように各ブレークポイントに対する指定を行うことができます。</p>

<pre><code>&lt;div class="p-4 sm:p-5 lg:p-6 xl:p-10">
  ....
&lt;/div></code></pre>

<h3>任意のブレークポイントを指定</h3>

<p>細かいスタイルの調整を行う際にもscreensに追加すればscreensの値がどんどんと肥大化してしまいます。</p>

<p>任意のブレークポイントは <strong>min-[500px]:クラス</strong> という指定になります。</p>

<p>以下のサンプルではウィンドウ幅が500pxと900pxと1200px時にパディングがきりかわるように指定されています。</p>

<pre><code>&lt;div class="p-4 min-[500px]:p-5 min-[900px]:p-6 min-[1200px]:p-10">
  ....
&lt;/div></code></pre>

<p>参考: <a href="https://tailwindcss.com/blog/tailwindcss-v3-2#max-width-and-dynamic-breakpoints">Tailwind CSS v3.2: Dynamic breakpoints, multi-config, and container queries, oh my! - Tailwind CSS</a></p>]]>
    </content>
</entry>

<entry>
    <title>Tailwind CSSでdisabled</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2023/10/19-153245.html" />
    <id>tag:blog.webcreativepark.net,2023://2.1744</id>

    <published>2023-10-19T06:32:45Z</published>
    <updated>2023-10-19T07:11:43Z</updated>

    <summary> Tailwind CSSでdisabled属性がついた要素に対してスタイルを当...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="CSS" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p><img src="https://blog.webcreativepark.net/images/tailwind-css-arbitrary-variants-420-66ffd499947b32a33c625e21df8230fc.webp" style="width:100%" alt="Tailwind CSS"/></p>

<p>Tailwind CSSでdisabled属性がついた要素に対してスタイルを当てたい場合には、disabled擬似クラスを利用して指定を行います。</p>]]>
        <![CDATA[<p>以下のように<strong>disabled:</strong>をつけることでdisabled属性がついた場合のスタイルが定義できます。</p>

<pre><code>&lt;input type="text" disabled class="disabled:bg-gray-400" /&gt;</code></pre>

<h3>disabled属性がついた要素の兄弟要素</h3>

<h4>peer</h4>

<p>disabled属性がついた要素の兄弟要素にスタイルを指定したい場合はpeerを利用します。<br />
disabled属性がついた要素にclass属性「peer」を指定して、兄弟要素には「peer-disabled:」としてdisabled属性がついた場合のスタイルが定義できます。</p>

<pre><code>&lt;input type="text" disabled class="peer" /&gt;
&lt;label class="peer-disabled:bg-gray-400"&gt;text&lt;/label&gt;</code></pre>

<h4>arbitrary variants</h4>

<p><a href="https://www.to-r.net/media/tailwind-css-arbitrary-variants/">arbitrary variants</a>を利用するとCSSセレクタを利用してより柔軟に指定ができます。</p>

<p><strong>[:disabled+&]:</strong>と指定をすると隣接セレクタを利用してdisabled属性が指定された要素の次に登場する要素の場合という条件の場合のスタイルが定義できます。</p>

<pre><code>&lt;input type="text" disabled class="peer" /&gt;
&lt;label class="[:disabled+&]:bg-gray-400"&gt;text&lt;/label&gt;</code></pre>
]]>
    </content>
</entry>

<entry>
    <title>Vue.jsでコンポーネントの要素をpropsによって切り替える</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2023/09/15-111836.html" />
    <id>tag:blog.webcreativepark.net,2023://2.1743</id>

    <published>2023-09-15T02:18:36Z</published>
    <updated>2023-09-15T02:29:15Z</updated>

    <summary> Vue.jsでボタンコンポーネントなどを作成する場合、ユースケースに応じてHT...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="JavaScript" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p><img alt="Vue.png" src="https://blog.webcreativepark.net/images/Vue.png" class="mt-image-none" style="width:100%" alt="Vue" /></p>

<p>Vue.jsでボタンコンポーネントなどを作成する場合、ユースケースに応じてHTMLの実体をa要素とbutton要素と切り替えたいケースというのが発生します。</p>]]>
        <![CDATA[<p>そういったケースはVue.jsの<a href="https://ja.vuejs.org/api/built-in-special-elements.html#component">動的コンポーネント</a>を利用することで対応ができます。</p>

<p>以下はasHtml Propsでa要素とbutton要素を切り替えているサンプルです。</p>

<pre><code>&lt;template>
  &lt;component :is="asHtml" :href="href">
    &lt;slot />
  &lt;/component>
&lt;/template>
  
&lt;script setup lang="ts">
withDefaults(
  defineProps&lt;{
    asHtml?: 'a' | 'button';
    href?: sting;
  }>(),
  {
    asHtml: 'a',
    href: undefined,
  },
);
&lt;/script>
</code></pre>

<p>デフォルトはbutton要素が利用できるようにしているのでa要素を利用したい場合のみas-html porpsを指定して利用します。</p>

<pre><code>&lt;template>
  &lt;div>
    &lt;!-- button要素として利用 -->
    &lt;Button>button&lt;/Button>
    &lt;!-- a要素として利用 -->
    &lt;Button as-html="a" href="https://www.to-r.net">a&lt;/Buttons>
  &lt;/div>
&lt;/template></code></pre>]]>
    </content>
</entry>

<entry>
    <title>Nuxt3でESLintの設定をする</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2023/09/13-135105.html" />
    <id>tag:blog.webcreativepark.net,2023://2.1742</id>

    <published>2023-09-13T04:51:05Z</published>
    <updated>2023-09-13T06:09:41Z</updated>

    <summary> 案件でNuxt3のESLint設定をすることがあったので手順をメモしておきます...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="JavaScript" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p><img alt="Nuxt3" src="https://blog.webcreativepark.net/images/Nuxt3-1.png" style="width:100%" class="mt-image-none"  /></p>

<p>案件でNuxt3のESLint設定をすることがあったので手順をメモしておきます。</p>]]>
        <![CDATA[<p>Nuxt3は<a href="https://nuxt.com/docs/community/contribution#no-prettier">No Prettierポリシー</a>なのでPrettierは推奨されておらずフォーマッティングも含めてESLintを利用することが推奨されています。</p>

<h3>Nuxt3のインストール</h3>

<p>まずは以下のコマンドでNuxt3のインストール。マネージメントツールはnpmにします。</p>

<pre><code>npx nuxi@latest init my-app</code></pre>

<p>インストールしたら起動しておきます。</p>

<pre><code>cd my-app
npm run dev</code></pre>

<h3>パッケージのインストール</h3>

<p>以下のパッケージをインストールします。</p>

<ul>
<li>eslint</li>
<li>eslint-plugin-nuxt</li>
<li>@typescript-eslint/eslint-plugin</li>
<li>@typescript-eslint/parser</li>
<li>@nuxtjs/eslint-config-typescript</li>
</ul>

<pre><code>npm install -D eslint eslint-plugin-nuxt @nuxtjs/eslint-config-typescript @typescript-eslint/eslint-plugin @typescript-eslint/parser</code></pre>

<h3>設定ファイル</h3>

<p>.eslintrcを以下のような内容でルートに配置します。</p>

<p>基本の設定にmulti-word-component-namesのルールからlayoutsディレクトリやpagesディレクトリを除外するように追加で設定しています。</p>

<pre><code>{
    "root": true,
    "env": {
      "browser": true,
      "es6": true
    },
    "extends": [
      "eslint:recommended",
      "@nuxtjs/eslint-config-typescript",
      "plugin:@typescript-eslint/recommended",
      "plugin:vue/vue3-recommended"
    ],
    "parser": "vue-eslint-parser",
    "parserOptions": {
      "parser": "@typescript-eslint/parser",
      "sourceType": "module"
    },
    "plugins": ["vue", "@typescript-eslint"],
    "overrides": [
      {
        "files": ["layouts/*.vue", "pages/**/*.vue"],
        "rules": { "vue/multi-word-component-names": "off" }
      }
    ]
}
</code></pre>

<h3>実行スクリプトを追加</h3>

<p>package.jsonのscriptsに実行コマンドを追加</p>

<pre><code>{
  ...
  "scripts": {
    ...
    "lint": "eslint --fix './**/*.vue'"
  },
  ....
}</code></pre>

<p>これでESLintが利用できますので `npm run lint` でESLintが実行されるか確認しておきます。</p>

<h3>VS Code用の設定を追加</h3>

<p>以下の内容を　`.vscode/settings.json` に記述しておきます。</p>

<pre><code>{
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
        "source.fixAll": false,
        "source.fixAll.eslint": true
    }
}</code></pre>

<p>これで自動保存時にESLintの内容でフォーマッティングが行われます。<br />
</p>]]>
    </content>
</entry>

<entry>
    <title>Next.jsのAPI RoutesでAuth0の認可を実装する</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2023/03/24-111812.html" />
    <id>tag:blog.webcreativepark.net,2023://2.1741</id>

    <published>2023-03-24T02:18:12Z</published>
    <updated>2023-03-24T03:07:58Z</updated>

    <summary> Auth0は認証・認可の仕組みを提供してくれるIDaaS(Identity a...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="JavaScript" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="nextjs" label="Next.js" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p><img alt="Next.js+Auth0-1.png" src="https://blog.webcreativepark.net/images/Next.js%2BAuth0-1.png" class="mt-image-none" style="width:100%" /></p>

<p><a href="https://auth0.com/jp">Auth0</a>は認証・認可の仕組みを提供してくれるIDaaS(Identity as a Service)です。</p>]]>
        <![CDATA[<p>公式サイトに実装サンプルも豊富でNext.jsを利用した認証のサンプルコードなども公式サイトに用意されており手軽に利用できます。</p>

<p><a href="https://auth0.com/docs/quickstart/webapp/nextjs/01-login">Auth0 Next.js SDK Quickstarts: Login</a></p>

<p>ただ、Next.jsのAPI Routesを利用した認可用のサンプルコードがなかったので作成してみました。</p>

<p>src/features/auth0/util.tsなど適当なファイルに以下のような確認用の関数を作成します。</p>

<p>jsonwebtokenはJWTの確認に利用されるライブラリで、jwk-to-pemはjwkをpemファイルに変換するライブラリです。</p>

<pre><code>import jwt from 'jsonwebtoken';
import jwkToPem from 'jwk-to-pem';
 
/**
 * Auth0の公開鍵をpemに変換して返す
 * @returns 公開鍵のpem
 */
const jwks = async () => {
    // Auth0の公開鍵のURL
    const path = `${process.env.AUTH0_ISSUER_BASE_URL}/.well-known/jwks.json`
    // Auth0の公開鍵を取得
    const response = await fetch(path, {
        method: 'GET'
    });
    const jwk = (await response.json());
    // 公開鍵の１つ目をpemに変換する
    const pem = jwkToPem(jwk.keys[0]);
    return pem;
};
  
/**
 * 受け取ったidトークンが認証済みかどうかを確認して結果を返す
 * @param idToken 
 * @returns 認証済みかどうか
 */
export const authCheck = async (idToken: string) => {
    const pem = await jwks();
    try {
        jwt.verify(String(idToken), pem, { algorithms: ['RS256'] });
        return true
    } catch (e) {
        console.error(e)
        return false
    }
}</code></pre>

<p>src/pags/api/aaaa.tsなどのAPIでは先程作成したauthCheck関数を利用して認可確認をしてから実際の処理を記述していきます。</p>

<pre><code>import { NextApiRequest, NextApiResponse } from "next"
import { getSession } from '@auth0/nextjs-auth0';
import { authCheck } from "@/features/auth0";
  
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  
    const session = await getSession(req, res);
    const idToken = session?.idToken;
  
    if (!idToken || !await authCheck(idToken)) {
        return res.status(401).json({
            message: 'ログインしてください。'
        })
    }
  
   // 認可済みの処理を書く
   res.status(200).json({ aaa: 'bbb' })
};</code></pre>
]]>
    </content>
</entry>

<entry>
    <title>AstroでgetStaticPathsで指定したpropsをTypeScriptの型推論で型情報を取りだす</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2023/02/20-183155.html" />
    <id>tag:blog.webcreativepark.net,2023://2.1740</id>

    <published>2023-02-20T09:31:55Z</published>
    <updated>2023-03-24T02:10:11Z</updated>

    <summary> AstroでgetStaticPathsの返り値としてpropsが指定できます...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="JavaScript" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p><img alt="AstroでgetStaticPathsで指定したpropsを型推論で取りだす" src="https://blog.webcreativepark.net/images/Astro.png"  class="mt-image-none" style="width:100%;height:auto" /></p>

<p>AstroでgetStaticPathsの返り値としてpropsが指定できます。</p>]]>
        <![CDATA[<pre><code>export async function getStaticPaths() {
  return [
    {
      params: { id: "foo" },
      props: { text: "abcd" },
    },
    {
      params: { id: "var" },
      props: { text: "efg" },
    },
  ];
}</code></pre>

<p>TypeScriptで型指定を行いたい場合に、この場合はporpsの方は{ text:string }なのでファイル中に以下のような型定義を行えば型の指定が可能です。</p>

<pre><code>export type Props = {
    text: string;
}</code></pre>

<p>propsはgetStaticPathsの返り値として定義されているので頑張ればのgetStaticPaths返り値の型推論として取り出すこともできます。</p>

<p>以下のコードでは、`ReturnType<typeof getStaticPaths>`で返り値の型を取得してPromise内の配列内のオブジェクトのpropsの型を抽出しています。</p>

<pre><code>export type Props = (ReturnType<typeof getStaticPaths> extends Promise<infer T>
  ? T
  : never)[0]["props"];</code></pre>

<p>可能ではあるものの、ややこしいだけなので別途型情報を明記したほうがよさそうです。</p>

<p>追記:<a href="https://www.to-r.net/media/astro-infergetstaticpropstype/">Astro 2.1でもっと手軽に対応できるInferGetStaticPropsTypeが追加されました。</a></p>]]>
    </content>
</entry>

<entry>
    <title>VSCodeでSveltKitのPrettierのフォーマッティングをエディタ保存時に適用する</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2023/02/14-175342.html" />
    <id>tag:blog.webcreativepark.net,2023://2.1739</id>

    <published>2023-02-14T08:53:42Z</published>
    <updated>2023-02-14T09:03:03Z</updated>

    <summary> SveltKitでVSCodeのPrettierの自動フォーマッティングを有効...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="JavaScript" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p><img alt="SveltKitでVSCodeの自動フォーマッティングを有効にする" src="https://blog.webcreativepark.net/images/SVELTE.png" class="mt-image-none" style="width:100%;height:auto" /></p>

<p>SveltKitでVSCodeのPrettierの自動フォーマッティングを有効にするにはプロジェクトのルートに .vscodeディレクトリを配置して、その中のsettings.jsonに以下の記述を追加する。</p>]]>
        <![CDATA[<pre><code>{
  "[svelte]": {
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "svelte.svelte-vscode"
  },
}</code></pre>

<p>保存したファイルだけではなくプロジェクト全体にPrettierのフォーマッティングを適用したい場合はCLI上から 「npm run format」コマンドを実行すればよい</p>]]>
    </content>
</entry>

<entry>
    <title>Cloudflare PagesにSvelteKitをデプロイする際のNodeバージョン</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2023/01/25-193321.html" />
    <id>tag:blog.webcreativepark.net,2023://2.1738</id>

    <published>2023-01-25T10:33:21Z</published>
    <updated>2023-01-25T11:48:50Z</updated>

    <summary> Cloudflare PagesにSvelteKitで作成したアプリケーション...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="server" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p><img alt="Cloudflare PagesにSvelteKitをデプロイする際のNodeバージョン" src="https://blog.webcreativepark.net/images/Cloudflare.png" style="width:100%" class="mt-image-none" style="" /></p>

<p>Cloudflare PagesにSvelteKitで作成したアプリケーションをデプロイする際のNodeバージョンでハマってしまったのでちょっとメモ</p>]]>
        <![CDATA[<p>執筆時(2022/1/24)でのSvelteKitのバージョンは「<strong>1.1.1</strong>」でCloudflare Pagesにデプロイしようとすると以下のエラーがでてしまいました。</p>

<pre><code>20:20:59.114	npm ERR! code ELIFECYCLE
20:20:59.114	npm ERR! errno 1
20:20:59.114	npm ERR! @sveltejs/kit@1.1.1 postinstall: `node postinstall.js`
20:20:59.114	npm ERR! Exit status 1
20:20:59.114	npm ERR! 
20:20:59.115	npm ERR! Failed at the @sveltejs/kit@1.1.1 postinstall script.
20:20:59.115	npm ERR! This is probably not a problem with npm. There is likely additional logging output above.</code></pre>

<p>Node.jsのLTSの最新バージョンは 「<strong>18.13.0</strong>」だがCloudflare PagesのBuildで利用されるデフォルトのNode.jsのバージョンが「<strong>12.18.0</strong>」とだいぶ古くインストールに失敗するようです。</p>

<p>参考: <a href="https://developers.cloudflare.com/pages/platform/build-configuration/#language-support-and-tools">Build configuration · Cloudflare Pages docs</a></p>

<p>ちなみにSvelteKitの要求するNodeバージョンは16.14以上の16.x.xか18.x.xのようです。</p>

<pre><code>
  "engines": {
    "node": "^16.14 || >=18"
  },
</code></pre>

<p>Cloudflare PagesではNode.jsのバージョンを1.7.xまで指定することができるので16系統の最新を指定すれば、SvelteKitの要求とCloudflare Pageで利用できるバージョンが合致します。</p>

<p>Cloudflare PageではSettingsのEnvironment variablesでNODE_VERSIONの指定が可能ですので、16系統の最新である「v16.19.0」を指定することでビルドとデプロイが正常に動作します。</p>

<p><img alt="Cloudflare PagesでNODE_VERSIONの指定を行っている画面のキャプチャ" src="https://blog.webcreativepark.net/images/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%202023-01-25%2020.39.07.png" style="width:100%" class="mt-image-none" style="" /></p>

<p>17系統でもインストール可能という記事を見かけたのですが、試したところ以下のようなエラーで止まってしまいます。</p>

<pre><code>20:43:14.630	npm ERR! engine Not compatible with your version of node/npm: @sveltejs/kit@1.1.1
20:43:14.630	npm ERR! notsup Not compatible with your version of node/npm: @sveltejs/kit@1.1.1
20:43:14.630	npm ERR! notsup Required: {"node":"^16.14 || >=18"}
20:43:14.630	npm ERR! notsup Actual:   {"npm":"8.11.0","node":"v17.9.1"}</code></pre>

<p>SvelteKitは執筆時点でもかなりの速度でアップデートがおこなわれているので一過性の現状の可能性もありますがハマったひとは参考にしてみてください。</p>]]>
    </content>
</entry>

<entry>
    <title>Next.js + TypeScriptでgetStaticPropsの型を定義する方法</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2022/01/21-140314.html" />
    <id>tag:blog.webcreativepark.net,2022://2.1736</id>

    <published>2022-01-21T05:03:14Z</published>
    <updated>2022-01-21T05:39:47Z</updated>

    <summary> Next.js + TypeScript においてgetStaticProps...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="React" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="TypeScript" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p><img alt="Next+TypeScript.png" src="https://blog.webcreativepark.net/images/Next%2BTypeScript.png" width="1200" height="630" class="mt-image-none" style="width:100%;height:auto" /></p>

<p>Next.js + TypeScript においてgetStaticPropsの型を定義する方法はいくつか存在します。</p>]]>
        <![CDATA[<p>getStaticPropsの型を定義したいモチベーションとしてはNextPageのPropsとして受け取る値とgetStaticPropsが返すpropsの値を揃えたいという観点が一番でしょう。</p>

<pre><code>import type { NextPage } from 'next'
  
type Props = {
  aaa:string
  bbb:string
}
  
const Home:NextPage&lt;Props> = (props) => {
  const {aaa,bbb} = props
  return (
    &lt;div>page&lt;/div>
  )
}
  
export const getStaticProps = async () => {
  return {
    props:{
      aaa:"aa",
      bbb:"bb",
    }
  }
}
  
export default Home</code></pre>

<p>このままでは以下のようにPropsとしてbbbが欠落している状態を検出できません。</p>

<pre><code>export const getStaticProps = async () => {
  return {
    props:{
      aaa:"aa",
    }
  }
}</code></pre>

<p>必要な値の不足に関してはNext.jsが提供するGetStaticProps型にGenericsとしてPropsを渡して上げると型エラーの検出が可能になります。</p>

<pre><code>import type { NextPage , GetStaticProps } from 'next'
  
type Props = {
  aaa:string
  bbb:string
}
  
const Home:NextPage&lt;Props> = (props) => {
  const {aaa,bbb} = props
  return (
    &lt;div>page&lt;/div>
  )
}
  
export const getStaticProps: <strong>GetStaticProps&lt;Props></strong> = async () => {
  return {
    props:{
      aaa:"aa",
    }
  }
}
  
export default Home</code></pre>

<p>ただ、<strong>GetStaticProps&lt;Props></strong>では以下のように余剰にあるPropsの検出はできません。</p>

<pre><code>export const getStaticProps = async () => {
  return {
    props:{
      aaa:"aa",
      bbb:"bb",
      ccc:"cc",
    }
  }
}</code></pre>

<p>Propsの値を正確に精査したい場合には、返り値として<strong>Promise&lt;GetStaticPropsResult&lt;Props>></strong>を指定します。</p>

<pre><code>import type { NextPage , GetStaticPropsResult } from 'next'
  
type Props = {
  aaa:string
  bbb:string
}
  
const Home:NextPage&lt;Props> = (props) => {
  const {aaa,bbb} = props
  return (
    &lt;div>page&lt;/div>
  )
}  
  
export const getStaticProps = async ():Promise&lt;GetStaticPropsResult&lt;Props>> => {
  return {
    props:{
      aaa:"aa",
      bbb:"bb",
      ccc:"cc",// エラー
    }
  }
}</code></pre>

<p>ミニマムとしてはGetStaticPropsResultの定義のみで当初の目的は達成可能です。</p>

<p>また、Next.jsでは逆のアプローチでgetStaticPropsが返した値の型情報をPageのPropsとして利用する、<a href="https://nextjs.org/docs/api-reference/data-fetching/get-static-props#getstaticprops-with-typescript">InferGetStaticPropsType</a>も用意されているので状況によって使い分けると良いでしょう。</p>]]>
    </content>
</entry>

<entry>
    <title>株式会社トゥーアールの2021年を振り返る</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2021/12/28-134238.html" />
    <id>tag:blog.webcreativepark.net,2021://2.1735</id>

    <published>2021-12-28T04:42:38Z</published>
    <updated>2021-12-28T05:37:33Z</updated>

    <summary>今年の最終営業日なので一年を振り返ってみたいと思います。...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="dialy" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p>今年の最終営業日なので一年を振り返ってみたいと思います。</p>]]>
        <![CDATA[<h3>人員の入れ替え</h3>

<p>2021年は10人のスタッフでスタートしましたが1月に1名増え、2月に1名増え、8月に1名減り、10月に1名減り、12月に1名増えるという、これまでで一番人の入れ替えが多い年でした。</p>

<p>昨年がまったく増減がなかったのでエンジニアの人材市場が活発化しているということで、コロナの影響が仕事面では払拭されており業界的にはよい傾向だと思います。</p>

<p>来年1名増えることも確定しておりますが無理のないように規模は拡大していきたいと考えております。</p>

<h3>事務所減床</h3>

<p>コロナ前からリモートワークを行っていたこともあり、コロナ禍ではほとんど出社するスタッフがいないので2部屋借りていたオフィススペースを1部屋解約して事務所減床をおこないました。</p>

<p>そのときの状況は<a href="https://note.com/tor/n/n61e2b1fe565a">こちら(事務所減床｜Kazuma Nishihata｜note)</a>を参考にしてください。</p>

<h3>お仕事</h3>

<p>これまで、Reactがメインでしたが少しづつNext.jsがメインにシフトしてきています。</p>

<p>フレームワーク別の案件割合は以下のようになります。(規模がまちまちなので12人月を超えるような案件は大規模としています。)</p>

<p>React案件  5件(内大規模3件)<br />
Next案件 3件(内大規模1件)<br />
Vue案件 1件(大規模)<br />
Nuxt案件1件<br />
Angular案件 1件(大規模)</p>

<p>ほかにもコーディングのみの案件やWordPress案件など多数のお仕事をいただきました。</p>

<p>また、これまでのトゥーアールの案件傾向としてはメガベンチャーやベンチャー支援が多かったのですが、官公庁のシステムやサイト開発などのご依頼をチラホラといただくようになり時代の変化を感じられたのも今年の特徴でした。</p>

<h3>まとめ</h3>

<p>いろいろとありましたが今年１年ありがとうございました。<br />
来年はオフラインのイベントなどが開かれるぐらい世の中の状況がよくなっていると良いなと思いつつ、引き続き頑張っていきたいと思います。</p>

<h4>過去の振り返り</h4>

<p><a href="https://www.facebook.com/kazuma.nishihata/posts/10221091163562003">2020年の振り返り(Facebook)</a><br />
<a href="https://blog.webcreativepark.net/2018/12/28-133337.html">2018年の振り返り</a></p>]]>
    </content>
</entry>

<entry>
    <title>Jestでマークダウンを読み込みテストをする</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2021/12/17-160843.html" />
    <id>tag:blog.webcreativepark.net,2021://2.1734</id>

    <published>2021-12-17T07:08:43Z</published>
    <updated>2021-12-17T07:25:21Z</updated>

    <summary> 業務でマークダウンパーサーを作成していてJestでmockデータのマークダウン...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="JavaScript" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p><img alt="Jest" src="https://blog.webcreativepark.net/images/Jest.png" width="1200" height="630" class="mt-image-none" style="width:100%;height:auto" /></p>

<p>業務でマークダウンパーサーを作成していてJestでmockデータのマークダウンを読み込もうとするとエラーになりました。</p>]]>
        <![CDATA[<pre><code>const markdownText = require('./sample.md')</code></pre>

<p>エラー内容としては以下のような感じでJavaScriptの文法おかしいよとのこと。</p>

<blockquote>

<p>Jest encountered an unexpected token</p>

<p>Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.</p>

</blockquote>

<p>マークダウンをstringデータとして取得したい場合は、<a href="https://github.com/keplersj/jest-raw-loader">jest-raw-loader</a>を利用します。</p>

<p>以下のコマンドでjest-raw-loaderをインストールして</p>

<pre><code>npm install jest-raw-loader -D</code></pre>

<p>jest.config.jsのtransformに以下を追加します。</p>

<pre><code>  transform: {
    '\\.md$': 'jest-raw-loader'
  },</code></pre>

<p>これでマークダウンをテキストデータとして読み込みテストができるようになります。</p>]]>
    </content>
</entry>

<entry>
    <title>next/routerを利用しているコンポーネントのテストを行う</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2021/12/02-121932.html" />
    <id>tag:blog.webcreativepark.net,2021://2.1733</id>

    <published>2021-12-02T03:19:32Z</published>
    <updated>2021-12-02T03:32:03Z</updated>

    <summary> next/routerを利用しているコンポーネントを単体でテストする場合にはN...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="React" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p><img alt="Next" src="https://blog.webcreativepark.net/images/Next.png" width="1200" height="630" class="mt-image-none" style="width:100%;height:auto" /></p>

<p>next/routerを利用しているコンポーネントを単体でテストする場合にはNext Routerのモックを事前に作成しておく必要があります。</p>]]>
        <![CDATA[<p>以下のようにnext/routerを利用している場合に</p>

<pre><code>import { useRouter } from 'next/router';
export const Component = () => {
  const router = useRouter();
  return (&lt;h1>{router.pathname}&lt;/h1>)
}</code></pre>

<p>次のようなtestを書くと、router.pathnameがundefindになるとエラーになってしまいます。</p>

<pre><code>import React from 'react';
import { render, screen } from '@testing-library/react';
  
import Component from './Component';
  
describe('Component', () => {
  it('should render', () => {
    const { asFragment } = render(&lt;Component />);
    expect(asFragment()).toMatchInlineSnapshot()
  });
});</code></pre>

<p>その場合は事前にnext/routerにjest.mockでルーター情報を指定してあげることで、その情報をもとにテストが実行されるようになります。</p>

<pre><code>import React from 'react';
import { render, screen } from '@testing-library/react';
  
jest.mock('next/router', () => ({
  useRouter() {
    return {
      route: '/',
      pathname: '/',
      query: '',
      asPath: '',
    };
  },
}));
　　
import Component from './Component';
  
describe('Component', () => {
  it('should render', () => {
    const { asFragment } = render(&lt;Component />);
    expect(asFragment()).toMatchInlineSnapshot()
  });
});</code></pre>
]]>
    </content>
</entry>

<entry>
    <title>Github Actionsのキャッシュ機能を利用してNext.jsのデプロイを高速化</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2021/11/23-215330.html" />
    <id>tag:blog.webcreativepark.net,2021://2.1732</id>

    <published>2021-11-23T12:53:30Z</published>
    <updated>2021-11-23T13:16:18Z</updated>

    <summary> Next.jsのアプリケーションのデプロイにGithub Actionsを利用...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="React" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p><img alt="Github Next" src="https://blog.webcreativepark.net/images/Github%2BNext.png" width="1200" height="630" class="mt-image-none" style="width:100%;height:auto" /></p>

<p>Next.jsのアプリケーションのデプロイにGithub Actionsを利用していたところ以下のような警告が出ているのに気づきました。</p>]]>
        <![CDATA[<blockquote>
<p>warn  - No build cache found. Please configure build caching for faster rebuilds. Read more: <a href="https://err.sh/next.js/no-cache">https://err.sh/next.js/no-cache</a></p>
<p>info  - Creating an optimized production build...</p>
<p>Attention: Next.js now collects completely anonymous telemetry regarding usage.</p>
<p>This information is used to shape Next.js' roadmap and prioritize features.</p>
<p>You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
<p><a href="https://nextjs.org/telemetry">https://nextjs.org/telemetry</a></p>
</blockquote>

<p>これによると .next/cache ディレクトリをキャッシュしておくことによりbuildタスクの高速化が図れるようです。</p>

<p>例示されているGitHub Actionsのcacheコードをデプロイタスクに追加</p>

<pre><code>jobs:
  build:
    runs-on: ubuntu-latest
  
    steps:
      - uses: actions/checkout@v1
      - name: Use Node.js 14.15.4
        uses: actions/setup-node@v1
        with:
          node-version: '14.15.4'
     <span> - name: Use Github Action cache
        uses: actions/cache@v2
        with:
          path: ${{ github.workspace }}/.next/cache
          # Generate a new cache whenever packages or source files change.
          key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
          # If source files changed but packages didn't, rebuild from a prior cache.
          restore-keys: |
            ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-</span>
      - name: npm install
        run: npm ci
      - name: npm build
        run: npm run build --if-present
        env:
          CI: true</code></pre>

<p>こうすることでbuildタスクの所要時間が30秒ちょっとから20秒ちょっとに短縮されました。キャッシュの取得と保存に3秒づつ追加されましたので実感はありませんがビルドするページの総量が増えてきた場合に威力を発揮しそうです。</p>

<p>これまで雰囲気でGithub Actionsを使っていましたがまだ知らない便利機能がありそうなので改めて確認したいと思いました。</p>

<h4>関連エントリー</h4>

<p><a href="https://blog.webcreativepark.net/2021/11/02-160621.html">Nuxt.js / Next.jsで作成したサイトをGitHub ActionsでAWS S3にデプロイする</a></p>]]>
    </content>
</entry>

<entry>
    <title>Next.js のStorybookでSassを利用する(webpack5対応)</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2021/11/19-112033.html" />
    <id>tag:blog.webcreativepark.net,2021://2.1731</id>

    <published>2021-11-19T02:20:33Z</published>
    <updated>2021-11-19T03:37:34Z</updated>

    <summary> Next.js で Storybookを利用しようとした際にsass-load...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="React" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p><img alt="Storybook + Next.js" src="https://blog.webcreativepark.net/images/Storybook%2BNext.png" width="1200" height="630" class="mt-image-none" style="max-width:100%;height:auto" /></p>

<p>Next.js で Storybookを利用しようとした際にsass-loaderがエラーを出したのでまとめてみました。</p>]]>
        <![CDATA[<p>まずは以下のコマンドでNext.jsをインストールして</p>

<pre><code>npx create-next-app</code></pre>

<p>次に以下のコマンドでStorybookをインストールします。</p>

<pre><code>npx sb init</code></pre>

<p>これで以下のコマンドでStorybookが起動できるようになります。</p>

<pre><code>yarn storybook</code></pre>

<p>ひとまずNext.jsのReactコンポーネントが表示できるか確認するため、<strong>stories/Home.stories.jsx</strong>に以下のファイルを作成してStorybookに表示できるか確認します。</p>

<pre><code>import React from 'react';
  
import  Home  from '../pages/index';
  
export default {
  title: 'Example/Home',
  component: Home,
};
  
export const Template = () => &lt;Home />;</code></pre>

<p>これで表示は確認できるはずです。</p>

<h3>Next.jsにSassにインストール</h3>

<p>以下のコマンドでSassをインストール</p>

<pre><code>yarn add sass -D</code></pre>

<p><strong>styles/globals.css</strong>を<strong>styles/globals.scss</strong> というファイル名に変更して、Sassの動作確認ができるように以下の内容を変更しておきます。</p>

<pre><code>$color: red;
 
body {
  background: $color;
}</code></pre>

<p><strong>pages/_app.js</strong>のCSS読み込みも以下のように変更しておきます。</p>

<pre><code>import '../styles/globals.css'
↓
import '../styles/globals.scss'</code></pre>

<p>これで以下のコマンドでNextアプリケーションを起動すると背景が赤に変わってSassがちゃんと動作しているのが確認できます。</p>

<pre><code>yarn dev</code></pre>

<h3>StorybookでSassを利用</h3>

<p><strong>.storybook/preview.js</strong>の冒頭に以下を追加してSassを利用するとエラーになってしまいます。</p>

<pre><code>import '../styles/globals.scss'</code></pre>

<p>以下がエラー内容。</p>

<pre><code>ERROR in ./styles/globals.scss 3:5
Module parse failed: Unexpected token (3:5)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders</code></pre>

<p>webpackのloaderを追加しろとのことなので以下のコマンドで追加します。</p>

<pre><code>yarn add sass-loader -D</code></pre>

<p><strong>.storybook/main.js</strong>にwebpackFinalを追加します。</p>

<pre><code>module.exports = {
  "stories": [
    "../stories/**/*.stories.mdx",
    "../stories/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials"
  ],
  webpackFinal: async (config) => {
    config.module.rules.push({
      test: /\.scss$/,
      use: ['style-loader', 'css-loader', 'sass-loader'],
    });
    return config;
  }
}</code></pre>

<p>これでStorybookを起動しようとすると別のエラーが表示されるようになります。</p>

<pre><code>ERROR in ./styles/globals.scss (./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./styles/globals.scss)
Module build failed (from ./node_modules/sass-loader/dist/cjs.js):
TypeError: this.getOptions is not a function</code></pre>

<p>実はsass-loaderの11以上がwebpack5にしか対応しておらずStorybookで利用されるのがwebpack4なのでこのようなエラーになってしまいます。</p>

<p>そのため、以下のコマンドでversion 10.1.1のsass-loaderがインストールするとエラーが出ずにStorybookでSassを利用できるようになります。</p>

<pre><code>yarn add sass-loader@10.1.1 -D</code></pre>

<h3>Storybookでwebpack5を利用</h3>

<p>上記の10.1.1のsass-loaderを利用するでもよいのですが今回は<a href="https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#webpack-5">Migration</a>を参考にStorybookでwebpack5を利用できるように変更して最新のsass-loaderが利用できるように変更します。</p>

<p>まずは以下のコマンドで必要なパッケージをインストールします。</p>

<pre><code>yarn add webpack @storybook/builder-webpack5 @storybook/manager-webpack5 -D</code></pre>

<p><strong>.storybook/main.js</strong>にcoreを追加します。</p>

<pre><code>module.exports = {
  core: {
    builder: "webpack5",
  },
  "stories": [
    "../stories/**/*.stories.mdx",
    "../stories/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials"
  ],
  webpackFinal: async (config) => {
    config.module.rules.push({
      test: /\.scss$/,
      use: ['style-loader', 'css-loader', 'sass-loader'],
    });
    return config;
  }
}</code></pre>

<p>これでStorybookで最新のsass-loaderが利用できるようになります。<br />
</p>]]>
    </content>
</entry>

<entry>
    <title>AWS CloudFrontで特定ディレクトリを別のS3ストレージを割り当てる</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2021/11/03-105301.html" />
    <id>tag:blog.webcreativepark.net,2021://2.1730</id>

    <published>2021-11-03T01:53:01Z</published>
    <updated>2021-11-03T06:43:35Z</updated>

    <summary>AWS CloudFrontを利用すれば特定ディレクトリを別のS3ストレージを割...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="AWS" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p>AWS CloudFrontを利用すれば特定ディレクトリを別のS3ストレージを割り当てることができます。</p>]]>
        <![CDATA[<p>以下のような感じでexample.comにアクセスした場合はメインS3にアクセスしますがsub-dirディレクトリ以下にアクセスした場合はサブS3のS3ストレージのファイルを表示します。</p>

<p>https://example.com/* [メインS3]<br />
https://example.com/sub-dir/* [サブS3]</p>

<p><br />
<h3>S3にファイルを配置</h3></p>

<p>まずはサブS3にファイルを配置しておきます。配置する際にはルートパスから指定する必要があります。<br />
つまり、sub-dir/を作成してその内側にhttps://example.com/sub-dir/*以下のファイルの指定を行います。</p>

<h3>CloudFrontの設定変更</h3>

<p>CloudFrontではメインS3にアクセスを設定しているディストリビューションを選択します。</p>

<h4>オリジン</h4>

<p>オリジンタブに遷移してから「オリジンを作成」を押下します。</p>

<p>オリジンドメインにサブS3のを選択<br />
S3 バケットアクセスにはサブS3にアクセスできるOAIを指定</p>

<h4>ビヘイビア</h4>

<p>ビヘイビアタブに遷移してから「ビヘイビアを作成」を押下します。</p>

<p>パスパターン sub-dir/*<br />
オリジンとオリジングループ S3のオリジン</p>

<p>こちらを設定して「ビヘイビアを作成」を押下します。</p>

<p>そうするとhttps://example.com/sub-dir/* にアクセスした際にサブS3で設定したファイルの内容が表示されるようになります。</p>]]>
    </content>
</entry>

<entry>
    <title>Nuxt.js / Next.jsで作成したサイトをGitHub ActionsでAWS S3にデプロイする</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2021/11/02-160621.html" />
    <id>tag:blog.webcreativepark.net,2021://2.1729</id>

    <published>2021-11-02T07:06:21Z</published>
    <updated>2021-11-23T13:17:01Z</updated>

    <summary>GitHub Actionsを利用するとNuxt.jsやNext.jsなどで作成...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="AWS" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p>GitHub Actionsを利用するとNuxt.jsやNext.jsなどで作成したWebアプリケーションを簡単にAWS S3にデプロイすることができます。</p>]]>
        <![CDATA[<h3>AWS S3にアップできるIAMユーザーを作成する</h3>

<p>まずはAWSの管理画面からAWS S3にデプロイ可能なIAMポリシーとIAMユーザーを作成しましょう。</p>

<p>IAMポリシーは作成画面でJSONを指定することで、当該S3バケットを操作可能なポリシーを作成します。</p>

<pre><code>{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:ListBucket",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::[バケット名]",
                "arn:aws:s3:::[バケット名]/*"
            ]
        }
    ]
}</code></pre>

<p>IAMユーザーはアクセスの種類を「プログラムによるアクセス」を選択して、アクセス権の設定では既存のポリシーを直接アタッチを選択して先程作成したIAMポリシーにチェックを入れます。</p>

<p>これでAWS S3にデプロイ可能なIAMユーザーが作成できますのでアクセスキーIDとシークレットアクセスキーをメモしておきましょう。</p>

<h3>GitHub ActionsにAWS S3にデプロイ設定をする</h3>

<p>まずはGitHubの管理画面からSecretsとしてAWSの情報を登録します。<br />
今回は以下のように設定しましょう。</p>

<p>S3_ACCESS_KEY_ID　: アクセスキーID<br />
S3_SECRET_ACCESS_KEY : シークレットアクセスキー<br />
S3_BUCKET : S3バケット名</p>

<p>コードでは、.github/workflows/deploy.ymlというファイルを以下の内容で作成します</p>

<p>これは <strong>aws-serverブランチ</strong>にpushが行われた際にNuxt.jsアプリケーションのビルドを行い、先程のS3にアップを行います。<br />
<strong>workflow_dispatch</strong>の指定を行っているのでGitHubの管理画面から手動でワークフローを実行することも可能です。</p>

<pre><code>name: deploy to aws
  
on:
  workflow_dispatch:
  push:
    branches:
      - aws-server
  
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Build
        uses: actions/setup-node@v1
      - run: |
          npm install
          npm run generate
      - uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1
      - name: Copy Files to s3
        run: |
          aws s3 sync ./dist s3://${{ secrets.S3_BUCKET }}</code></pre>

<h3>Next.jsの場合</h3>

<p>Next.jsの場合も基本的には同じですがいくつかコマンドが変わります。</p>

<p>package.jsonに以下のbuildコマンドを追加して</p>

<pre><code>  "scripts": {
    "dev": "next dev",
    "start": "next start",
    <span>"build": "next build && next export",</span>
  },</code></pre>

<p><br />
.github/workflows/deploy.ymlは以下のように指定を行います。</p>

<pre><code>name: deploy to aws
  
on:
  workflow_dispatch:
  push:
    branches:
      - aws-server
  
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Build
        uses: actions/setup-node@v1
      - run: |
          npm install
         <span> npm run build</span>
      - uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1
      - name: Copy Files to s3
        run: |
          aws s3 sync <span>./out</span> s3://${{ secrets.S3_BUCKET }}</code></pre>

<h4>関連エントリー</h4>

<p><a href="https://blog.webcreativepark.net/2021/11/23-215330.html">Github Actionsのキャッシュ機能を利用してNext.jsのデプロイを高速化</a></p>]]>
    </content>
</entry>

<entry>
    <title>AWS CloudFront FunctionsでBasic認証を設定する</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2021/11/01-140811.html" />
    <id>tag:blog.webcreativepark.net,2021://2.1728</id>

    <published>2021-11-01T05:08:11Z</published>
    <updated>2021-11-01T05:58:56Z</updated>

    <summary>これまでAWSのS3にアップした静的ファイルにBasic認証を設定するには Cl...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="AWS" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p>これまでAWSのS3にアップした静的ファイルにBasic認証を設定するには CloudFront経由でLambda@EdgeでBasic認証を指定する必要がありましたがCloudFront Functionsの登場によって、CloudFront Functionsを利用して少しだけ簡単に設定できるようになりました。</p>]]>
        <![CDATA[<p>CloudFront FunctionsはCloudFrontのサイドバーの「関数」から作成できます。</p>

<p>関数ページでは右上の「関数を作成」から適当な名前をつけてBasic認証用の関数を作成します。</p>

<p>CloudFront Functions はLambda@Edgeと違いECMAScript 5.1相当のJavaScriptしか記述できないので注意が必要です。</p>

<pre><code>function handler(event) {
  var authUser = 'user';
  var authPass = 'pass';
  
  var authString = 'Basic ' + (authUser + ':' + authPass).toString('base64');
  
  var request = event.request;
  var headers = request.headers;
  
  if (
    typeof headers.authorization === "undefined" ||
    headers.authorization.value !== authString
  ) {
    return {
      statusCode: 401,
      statusDescription: "Unauthorized",
      headers: { "www-authenticate": { value: 'Basic realm="Please Enter Your Password"' } }
    };
  }
  
  return request;
}</code></pre>

<p>作成後は変更を保存して発行するとCloudFrontで利用可能になります。</p>

<h3>CloudFrontでFunctionsを設定</h3>

<p>CloudFrontのディストリビューションからBasic認証を設定したいディストリビューションを選択して、ビヘイビアからオリジンを選択し編集します。</p>

<p>関数の関連付けのビューワーリクエストに</p>

<p>関数タイプ ： CloudFront Functions<br />
関数 ARN/名前 :作成していた Basic認証用の関数</p>

<p>を指定することでCloudFront FunctionsでBasic認証が可能になります。</p>]]>
    </content>
</entry>

<entry>
    <title>AWS SAM のtemplate.yamlをdeploy環境ごとに切り替える</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2021/10/28-162051.html" />
    <id>tag:blog.webcreativepark.net,2021://2.1727</id>

    <published>2021-10-28T07:20:51Z</published>
    <updated>2021-10-28T07:38:00Z</updated>

    <summary>develop や staging 、 productionなどの複数の環境で ...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="AWS" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p>develop や staging 、 productionなどの複数の環境で template.yaml を変更したいことがあります。</p>]]>
        <![CDATA[<p>まずtemplate.yaml に Parametersを追加します。ここで追加した値が sam deploy時の --parameter-overridesに利用できる値が定義できますので今回はデプロイ環境を定義します。</p>

<pre><code>Parameters:
  Environment:
    Type: String
    AllowedValues:
      - development # 開発環境
      - staging # ステージング環境
      - production # 本番環境
    Default: development</code></pre>

<p>次にMappingsで各環境ごとの設定したい値を指定します。今回はデプロイ環境ごとにlambdaのRoleを切り替えたかったので以下のように定義しています。</p>

<pre><code>Mappings:
  EnvironmentMap:
    development:
      lambdaRole: 'arn:aws:iam::xxxxxx'
    staging:
      lambdaRole: 'arn:aws:iam::yyyyy'
    production:
      lambdaRole: 'arn:aws:iam::yyyyy'</code></pre>

<p>最後に可変させたい箇所を　<strong>!FindInMap [EnvironmentMap, !Ref Environment, lambdaRole]</strong>とします。</p>

<pre><code>Resources:
  SetFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: xxx/
      Handler: app.lambdaHandler
      Runtime: nodejs14.x
      Role: !FindInMap [EnvironmentMap, !Ref Environment, lambdaRole]
      Events:
        HelloWorld:
          Type: Api 
          Properties:
            Path: /xxx/
            Method: post</code></pre>

<p>これでdeploy時に--parameter-overridesを指定することでdeploy環境ごとtemplate.yamlの値を切り替えることが可能になります。</p>

<pre><code>sam deploy ....  --parameter-overrides Environment=development</code></pre>]]>
    </content>
</entry>

<entry>
    <title>AWS Lambda(Node.js)でJimpを使って画像のバリデーションを行う</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2021/10/27-215707.html" />
    <id>tag:blog.webcreativepark.net,2021://2.1726</id>

    <published>2021-10-27T12:57:07Z</published>
    <updated>2021-10-27T13:19:29Z</updated>

    <summary>Node.jsでバリデーションを行うには sharpがよく利用されますが、lib...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="JavaScript" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p>Node.jsでバリデーションを行うには <a href="https://www.npmjs.com/package/sharp">sharp</a>がよく利用されますが、libvipsで書かれたsharpは環境によってはうまくインストールできないことがあります。</p>]]>
        <![CDATA[<p>今回、AWS Lambdaで画像のバリデーションを行うにあたりインストール時にハマってしまったこともあり、sharpではなく Pure JavaScriptで書かれた <a href="https://www.npmjs.com/package/jimp">Jimp</a>を利用しました。</p>

<p>利用方法はnpmでパッケージインストールを行い。</p>

<pre><code>npm install jimp</code></pre>

<p>ファイルの冒頭などで読み込んでおきます。</p>

<pre><code>const jimp = require('jimp')</code></pre>

<p>今回のバリデート内容はファイルの実体が画像種であるか、画像のサイズが1000x1000以下かです。<br />
フロントでも同様のバリデートを行っているためこれらのルールから外れる場合は強制的に処理を中断します。</p>

<pre><code>// 画像データのバリデート
const image = await jimp.read(FileData).catch(function (err) {
      console.log('画像データが不正です')
      throw new Error('画像データが不正です')
})
if (image.bitmap.width > 1000 || image.bitmap.height > 1000) {
      console.log('画像サイズが不正です')
      throw new Error('画像サイズが不正です')
}</code></pre>

<p>jimp.readで画像パスは画像バッファを指定することで画像以外の場合はエラーを返します。<br />
また、jimp.read()は画像データを返してくれて、image.bitmap.widthやimage.bitmap.heightで画像のサイズを取得することができますのでサイズの判定に利用します。</p>]]>
    </content>
</entry>

<entry>
    <title>IE11が終わる日</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2021/05/24-120642.html" />
    <id>tag:blog.webcreativepark.net,2021://2.1725</id>

    <published>2021-05-24T03:06:42Z</published>
    <updated>2021-05-25T06:58:47Z</updated>

    <summary>Microsoftが「Internet Explorer は Microsoft...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="tools" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p>Microsoftが「<a href="https://blogs.windows.com/japan/2021/05/19/the-future-of-internet-explorer-on-windows-10-is-in-microsoft-edge/">Internet Explorer は Microsoft Edge へ – Windows 10 の Internet Explorer 11 デスクトップアプリは 2022 年 6 月 15 日にサポート終了 - Windows Blog for Japan</a>」というリリースを行いました。<br />
</p>]]>
        <![CDATA[<p>よくよく読み進めていくと、これまでの「セキュリティアップデートを行なわない」といったサポート中止とは異なるすごく強制力のある発表で事実上来年以降にIE11を使った開発がほぼできなくなってしまうのではないかと感じました。</p>

<p>サポート終了後 にどうなるかというと <a href="https://blogs.windows.com/japan/2021/05/19/internet-explorer-11-desktop-app-retirement-faq/">FAQ</a>によるとMicrosoft Edge にリダイレクトされてしまうとのことでIE11は起動すらできなくなってしまうようです。</p>

<blockquote>

<p>Windows 10における Internet Explorer デスクトップ アプリケーションを 2022 年 6 月 15 日に廃止し、サポートを終了します。この日以降、IE 11 デスクトップ アプリケーションを利用しようとすると Microsoft Edge にリダイレクトされます。<br />
<cite><a href="https://blogs.windows.com/japan/2021/05/19/internet-explorer-11-desktop-app-retirement-faq/">「Internet Explorer 11 デスクトップ アプリケーションのサポート終了」の発表に関連する FAQ</a></cite><br />
</blockquote></p>

<p>ただ、これによりIE11のレンダリングでサイトが全く見れなくなるわけではなくMicrosoft Edgeの Internet Explorer モードという機能を利用するとIE11のレンダリングで確認することができます。 </p>

<p>以下、トゥーアールのサイトをMicrosoft Edge の Internet Explorer モードで表示した状態です。</p>

<p><a href="https://blog.webcreativepark.net/images/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%202021-05-24%2012.19.07.png"><img  style="width:100%" alt="画像:トゥーアールのサイトをIE11モードで表示" src="https://blog.webcreativepark.net/images/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%202021-05-24%2012.19.07.png" ></a></p>

<p>ただ、Internet Explorer モードはFlagをonにするというだいぶ上級者向けの設定を行わないと使うことができません。</p>

<p>参考：<a href="https://support.microsoft.com/ja-jp/office/microsoft-edge-%e3%81%ae-internet-explorer-%e3%83%a2%e3%83%bc%e3%83%89-6604162f-e38a-48b2-acd2-682dbac6f0de?ui=ja-JP&rs=ja-JP&ad=JP">Microsoft Edge の Internet Explorer モード - Office サポート</a></p>

<p>上級者向けとはいえ閲覧が可能なのでお客さんがどうしても表示したいと主張される場合には対応することもあるのかなと考えていたのですが、なんとInternet Explorer モードでは開発者ツールが使えないようです。</p>

<p>以下、Microsoft Edge の Internet Explorer モードで開発者ツールを表示した状態です。</p>

<p><a href="https://blog.webcreativepark.net/images/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%202021-05-24%2012.24.03.png"><img style="width:100%" alt="Internet Explorer モードで開発者ツールを表示" src="https://blog.webcreativepark.net/images/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%202021-05-24%2012.24.03.png"  /></a></p>

<p>「DevToosがページから切断されました。ページを再度読み込むと、DevTools は自動的に再接続されます」と表示されますがページを再読み込みしても繋がらずに開発者ツールを使うのは無理そうでした。</p>

<p>もしかしたら2022 年 6 月 15 日までに利用できるようになるかもしれませんが、このまま利用できなさそうな空気感は否めません。</p>

<p>そのため、IE11でこれまで通り開発するにはアップデートが適応されるまえのWindows 10 OSをアップデートせずに持ち続ける必要があります。</p>

<p>実際に昔はこのような方法で旧バージョンのブラウザ確認をするというのはよくあったのですが、昨今では中々に敷居が高いものだと思います。</p>

<p>そのためお客さんがどうしても表示したいと主張されてもなかなか難しいですので、我々製作者がIE11に対応するというのはほぼなくなってしまうのでないかと思いました。</p>

<p><strong>2021/05/25 追記</strong><br />
<ins>IEChooserを利用すればIE modeのデバッグが可能らしいです。</ins><br />
参考: <a href="https://jser.info/2021/05/25/ie-webcontainers-user-agent-client-hints/">2021-05-25のJS: IEの単体アプリとしてのサポート終了、WebContainers、User-Agent Client Hints - JSer.info</a></p>]]>
    </content>
</entry>

<entry>
    <title>自宅ネットワーク高速化の道のり</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2020/05/07-235636.html" />
    <id>tag:blog.webcreativepark.net,2020://2.1724</id>

    <published>2020-05-07T14:56:36Z</published>
    <updated>2020-05-08T01:56:37Z</updated>

    <summary>昨年12月に引っ越してから自宅のネットワークがすごく貧弱だなと不満を持っていたと...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="works" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p>昨年12月に引っ越してから自宅のネットワークがすごく貧弱だなと不満を持っていたところに、緊急事態宣言を受けて強制的にテレワークの実施を余儀なくされてしまいました。</p>]]>
        <![CDATA[<p>研修等のお仕事もフルオンラインに切り替わり、私の自宅回線が貧弱だと色々と迷惑をかけてしまうので自宅ネットワーク高速化を色々とトライしました。</p>

<p>結論はIPv6化したというお話ですがそこに至るまでの足取りを記録しておきます。</p>

<h3>当初のネットワーク環境</h3>

<p>光回線を引いているのですがマンションがVDSL方式のためベストエフォートで100Mbpsとそれほどの速度が出ません。ここを変えるには大掛かりな工事が必要でもっと小手先の対応でどうにかしたいというのが本エントリーの趣旨です。</p>

<p>プロバイダーは申込みの後の確認電話で勧められたぷららを契約してました。本記事の最後ではぷららを契約したことがプラスに繋がります。</p>

<p>プロバイダーから貸与されたモデムにルーターとして<a href="https://support.apple.com/ja-jp/airmac">AirMac</a>を繋げてWifiを飛ばしているが当初の環境でした。</p>

<p>モデム -> AirMac -> PC</p>

<p>通信速度にムラがありますが19時頃に下りで</p>

<p>リビング 8Mbps<br />
仕事場 0.8Mbps</p>

<p>となりWifiルーターを設置しているリビングから離れている仕事場では通常のブラウジングも苦しい状態になることもありました。</p>

<h3>改善案1:Google Nest Wifi導入</h3>

<p>流石に仕事にならないので改善案として<a href="https://store.google.com/jp/product/nest_wifi">Google Nest Wif</a>iを導入しました。Google Nest Wifiは拡張ポイントと合わせて利用することでWifiの接続快適距離を伸ばしてくれるメッシュWifiに対応しており、リビングから仕事場までの減速を軽減してくれることを期待していました。</p>

<p>通信速度にムラがありますが19時頃に下りで</p>

<p>リビング 8Mbps<br />
仕事場 2Mbps</p>

<p>ぐらいの誤差で収まるようになりました。下り2MbpsあればYouTube見ながらでもお仕事できますね。ちょいちょ詰まりますが。</p>

<h3>改善案2:IPv6導入</h3>

<p>Google Nest Wifiの導入で満足していたのですがTwitterで以下の様な投稿が流れてきました。</p>

<div><iframe class="note-embed" src="https://note.com/embed/notes/n79c77cc6dd05" style="border: 0; display: block; max-width: 99%; width: 494px; padding: 0px; margin: 10px 0px; position: static; visibility: visible;" height="400"></iframe><script async src="https://note.com/scripts/embed.js" charset="utf-8"></script></div>

<p>ネットワーク情弱な私でもIPv6が早いは知っており、意識してプロバイダ(ぷらら)もルーター(Google Nest Wifi)もIPv6に対応しているのを確認して契約、購入しているので勝手にIPv6になっているとおもっていました。</p>

<p>ただちゃんど調べてみると現状はIPv4での通信を行っておりIPv6では通信してないです。</p>

<p>もっと調べてみると、ぷららはIPoE方式接続という方式でIPv6を実現しておりGoogle Nest WifiはIPoE方式接続に対応していないらしいです。</p>

<p>IPv6形式で接続するためにはIPoE形式に対応したルーターが必要とのこと。3万円近くかけてNestWifiと拡張ポイントを揃えた私としては受け入れがたい現実です。</p>

<p>しかし、ぷららのサイトをみるとIPv6形式に対応したルーター(Aterm WG1200Hs4)を無料で貸与してくれるとのことで無料なら試すしかないとレンタルしてみました。</p>

<p>そして、本日届いて設置してみたところ速度のムラはかなり大きいですが、</p>

<p>リビング 30〜40Mbps<br />
仕事場 30〜40Mbps</p>

<p>とよくわからない速度が出るようになりました。当初の0.8Mbpsと比較するとかなりの高速化に対応することができました。</p>

<h3>結論</h3>

<p>IPv6導入しましょう<br />
</p>]]>
    </content>
</entry>

<entry>
    <title>もう、SP用のWebデザインを倍の解像度で作るのをやめよう</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2020/01/16-121629.html" />
    <id>tag:blog.webcreativepark.net,2020://2.1722</id>

    <published>2020-01-16T03:16:29Z</published>
    <updated>2020-01-18T00:11:10Z</updated>

    <summary>デザイナーがコーディングできるべきかという議論がSNS上で白熱していますが個人的...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="design" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p>デザイナーがコーディングできるべきかという議論がSNS上で白熱していますが個人的にはあまり興味がなく、コーディング経験の有無に関係なくWebに適したデザインデータを作ってもらえたらあとはフロントエンドの領分であると思っています。<br />
</p>]]>
        <![CDATA[<p>とはいえ、Webに適したデザインデータをつくれないデザイナーが多いのでああいった議論が白熱するのではないかとも思っています。</p>

<p>Webに適していないデザインデータとはどういったものかというと、古くから言われているものではPhotoshopの『乗算』を使うとCSSで表現できないためダメというものがあったりします。</p>

<p>CSSでもmix-blend-modeという機能で対応できるけどブラウザの対応状況を鑑みるに、よほどの理由がないかぎりPhotoshopの『乗算』を使っていはいけないのが今の現実です。</p>

<p>参考：<a href="https://developer.mozilla.org/ja/docs/Web/CSS/mix-blend-mode">mix-blend-mode - CSS: カスケーディングスタイルシート | MDN</a></p>

<p>最近、落ち着いたかなと思うことにはアイコンなどのグラフィックパーツは極力ベクターデータで作成するというお作法。</p>

<p>ラスター画像でしか書き出しできないパーツはpngで出力することになり高精細度ディスプレイや高精細度モニタでぼやけて見えてしまうので結局最終的にsvgに差し替えることになり実装や確認をするプレーヤーのコストも考えると２度手間では済まないコストが掛かってしまうので、最初からsvgで出力できるベクターデータで作成するのが好ましいのが現在の多くの現場の現実です。(これもPhotshopのデザインデータにありがちな現象です。)</p>

<h3>本題</h3>

<p>そして最近思っていることであまり議論されていないものとして、SP用のデザインを倍の解像度で作成する必要がないというもの、FigmaやAdobe XDで渡されるデザインデータにはないのですが、やはりPhotshopのデザインデータにありがちな傾向です。</p>

<p>9年ほど前に「<a href="https://blog.webcreativepark.net/2011/06/16-103025.html">スマートフォンサイトをデザインする上で知っておくべき10のTIPS</a>」という記事を書いてそこそこバズったのですがそこで書いた倍の解像度でデザインするという慣習が残っているようですが、レスポンシブが普及してSVGが使えるようになった今、倍の解像度で作る必要が全く無いです。</p>

<p>そもそも、なぜ倍の解像度でデザインする必要があったかというと、iPhone4が発売された当初は先程述べたSVGが利用できない端末も多く高精細度ディスプレイ対応としてSVGを利用することができなかったのでpngで表現するしかなくデザイン作成のタイミングから高精細度ディスプレイ用に倍の解像度で作っておこうというものでした。</p>

<p>また当時のサイトは割とグラフィカルなサイトが多かったのですが、フラットデザインから始まる昨今のシンプルなデザイントレンド上においては写真以外でラスター画像が必要なケースというのが少なくなってきています。(当然例外もあります）</p>

<p>そして、レスポンシブが一般になった今、最終的にPCとSPで同一の画像データを扱うのが一般的なのでSPのデザインデータの解像度を倍にする理由がほとんどありません。高解像度の画像が必要なケースもありますがこれは高解像度の画像をデザインデータに埋め込めばよいのでわざわざベースの解像度を倍で作成する必要はないでしょう。</p>

<p>というわけでSP用のWebデザインを倍の解像度で作成するメリットはほとんど無くなってきています。</p>

<p>PC用とSP用のデザインデータを作成する際に各パーツのサイズを変えて配置するのは本当に無駄な作業なのでやめたほうがよいでしょう。</p>

<p>(もう、WebデザインはPhotshopで作らないほうが良いのではとも思っておりますが、これは私の領分ではないので深くは言及しません。</p>]]>
    </content>
</entry>

<entry>
    <title>ReactのuseRefを再描画がかからないState管理として利用する</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2020/01/09-201128.html" />
    <id>tag:blog.webcreativepark.net,2020://2.1721</id>

    <published>2020-01-09T11:11:28Z</published>
    <updated>2020-01-09T12:01:44Z</updated>

    <summary>React HooksではuseStateなどで定義された値が更新された場合にそ...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="React" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p>React HooksではuseStateなどで定義された値が更新された場合にそのコンポーネントの再描画を行う。</p>]]>
        <![CDATA[<p>以下のコンポーネントではaddボタンを押されるたびにuseStateで管理しているcountが更新されAppコンポーネントの再描画が行われconsole上に「render」の文字列が出力されます。</p>

<pre><code>export default function App() {
  const [count, changeCount] = useState(0);
  console.log("render");
  return (
    &lt;div className="App">
      &lt;input
        type="button"
        value="add"
        onClick={() => {
          console.log(count);
          changeCount(count + 1);
        }}
      />
    &lt;/div>
  );
}</code></pre>

<p>これはReact上のエコシステムとして仕方ないですが表示に関係ない内部的なフラグの変化など再描画が望ましくないケースもあり、そういったケースではuseStateの代わりにuseRefを利用することで再描画されない(LifeCycleに影響を与えない)Stateを作成することができます。</p>

<p>useRefは以下のように利用して、参照する場合はcount.currentを、変更する場合はcount.currentを直接変更します。</p>

<pre><code>export default function App() {
  const count = useRef(0);
  console.log("render");
  return (
    &lt;div className="App">
      &lt;input
        type="button"
        value="add"
        onClick={() => {
          console.log(count.current);
          count.current += 1;
        }}
      />
    &lt;/div>
  );
}</code></pre>]]>
    </content>
</entry>

<entry>
    <title>TypeScriptのインデックス シグネチャでオブジェクトのキーの型を指定する</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2020/01/06-115728.html" />
    <id>tag:blog.webcreativepark.net,2020://2.1720</id>

    <published>2020-01-06T02:57:28Z</published>
    <updated>2020-01-09T03:15:54Z</updated>

    <summary>TypeScriptを指定してオブジェクトのキーに自由な値を指定するためのインデ...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="TypeScript" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p>TypeScriptを指定してオブジェクトのキーに自由な値を指定するためのインデックス シグネチャという機能が用意されています。<br />
</p>]]>
        <![CDATA[<pre><code>type Bar = {
  [key: string]: number
}
// OK
const bar01: Bar = {
  aaa: 1
}
// OK
const bar02: Bar = {
  bbb: 1,
  ccc: 2
}</code></pre>

<p>このままではキー使える文字列に制約がありませんのでユニオンタイプなどを利用して制約を入れようとしてもうまくいきません</p>

<pre><code>type Foo = 'aaa' | 'bbb'
type Bar = {
  [key: Foo]: number
}</code></pre>

<p>パラメーターの型にはユニオンタイプではなくマップされたオブジェクト型に変更して以下のようにすれば指定することができます。</p>

<pre><code>type Foo = 'aaa' | 'bbb'
type Bar = {
  [key in Foo]: number
}
// OK
const bar01: Bar = {
  aaa: 1,
  bbb: 2
}
// NG
const bar02: Bar = {
  ccc: 1,
  eee: 2
}
// NG
const bar03: Bar = {
  aaa: 1,
  bbb: 2,
  ccc: 3
}</code></pre>

<p>この場合はユニオンタイプで指定されたすべての値をキーに持つオブジェクトしか作れませんがオプショナルプロパティ(?のこと)を利用することで任意のキーを持つオブジェクトを指定することができます。</p>

<pre><code>type Foo = 'aaa' | 'bbb'
type Bar = {
  [key in Foo]?: number
}
// OK
const bar01: Bar = {
  aaa: 1,
  bbb: 2
}
// OK
const bar02: Bar = {
  aaa: 1,
}
// NG
const bar03: Bar = {
  aaa: 1,
  bbb: 2,
  ccc: 3
}</code></pre>
]]>
    </content>
</entry>

<entry>
    <title>GatsbyJSで &quot;WebpackError: ReferenceError: IDBIndex is not defined&quot;とエラーが出た場合の対処方法</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2019/05/27-002036.html" />
    <id>tag:blog.webcreativepark.net,2019://2.1719</id>

    <published>2019-05-26T15:20:36Z</published>
    <updated>2019-05-26T15:33:10Z</updated>

    <summary>GatsbyJSで gatsby buildコマンド実行時に「WebpackEr...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="React" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p>GatsbyJSで <strong>gatsby build</strong>コマンド実行時に「WebpackError: ReferenceError: IDBIndex is not defined」とエラーが出たのでその対応方法のメモ。</p>]]>
        <![CDATA[<p>具体的にはFirebaseを読み込んでいる以下のようなコードでエラーが発生しました。</p>

<pre><code>import firebase from "firebase"
 
const config = {
  apiKey: "xxx",
  authDomain: "xxx",
  databaseURL: "xxx",
  projectId: "xxx",
  storageBucket: "xxx",
  messagingSenderId: "xxx",
  appId: "xxx",
}
 
firebase.initializeApp(config)
 
export const functions = firebase.functions()</code></pre>

<p>importしているサードパティーのライブラリがwindowオブジェクトなどにアクセスしている場合に発生するらしいです。</p>

<p>これは以下のようにrequireを利用してwindowオブジェクトがある場合にのみ読み込むようにすることで対応することができます。</p>

<pre><code>let _functions
if (typeof window !== "undefined") {
  const firebase = require("firebase")
  
  const config = {
    apiKey: "xxx",
    authDomain: "xxx",
    databaseURL: "xxx",
    projectId: "xxx",
    storageBucket: "xxx",
    messagingSenderId: "xxx",
    appId: "xxx",
  }
 
  firebase.initializeApp(config)
  _functions = firebase.functions()
}
 
export const functions = _functions</code></pre>

<p>参考: <a href="https://www.gatsbyjs.org/docs/debugging-html-builds/">Debugging HTML Builds | GatsbyJS</a><br />
</p>]]>
    </content>
</entry>

<entry>
    <title>React.lazyを利用してJavaScriptファイルを分割ロード</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2019/05/21-200627.html" />
    <id>tag:blog.webcreativepark.net,2019://2.1717</id>

    <published>2019-05-21T11:06:27Z</published>
    <updated>2019-05-22T06:50:50Z</updated>

    <summary>Reactでは作成したアプリケーションはwebpackにより全てのコードが1つの...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="React" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p>Reactでは作成したアプリケーションはwebpackにより全てのコードが1つのJavaScriptファイルにバンドルされます。</p>]]>
        <![CDATA[<p>小さなアプリケーションならよいですが大きなアプリケーションですと初期表示に読み込むJSファイルが増加していく問題を抱えています。</p>

<p><a href="https://reactjs.org/docs/react-api.html#reactlazy">React.lazy</a> は JavaScriptファイルを分割して利用しているコンポーネントが呼び出されたタイミングでロードするための機能です。</p>

<h3>React.lazyの利用</h3>

<p>次のように記述すると遅延読み込み用のコンポーネントが作成されます。</p>

<pre><code>const SomeComponent = React.lazy(() => import('./SomeComponent'));</code></pre>

<p>React.lazyはReact.Suspense と一緒に利用して、このコンポーネントが呼び出されたタイミングで通信を行いコンポーネントファイルを取得します。</p>

<pre><code>const MyComponent = () => {
  return (
    &lt;React.Suspense fallback={null}>
      &lt;div>
        &lt;SomeComponent />
      &lt;/div>
    &lt;/React.Suspense>
  );
}</code></pre>

<p>React.Suspenseのfallback属性にはファイルの読み込み時に表示したいコンポーネントを指定することができ、表示が不要ならnullを指定します。</p>

<p>多くのケースではReact Routerなどと合わせて、ページごとに読み込みを行うと不要なコンポーネントの情報を読み込まずにページの描画が可能になんるでしょう。</p>

<h3>React Routerとの利用方法</h3>

<p>React Routerと利用する場合にはRouterコンポーネントとSwitchコンポーネントの間にSuspenseを記述して利用を行います。</p>

<pre><code>import React, {lazy,Suspense} from 'react';
import { BrowserRouter as Router, Route, Switch  } from 'react-router-dom';
  
const HomeComponent  = () => &lt;div>home&lt;/div>
const Page1Component = lazy(() => import('./Page1Component'));
const Page2Component = lazy(() => import('./Page2Component'));
  
const LoadingComponent  = () => &lt;div>Loading...&lt;/div>
  
function App() {
  return (
    &lt;Router>
      &lt;Suspense fallback={LoadingComponent}>
        &lt;Switch>
          &lt;Route path="/" exact component={HomeComponent} />
          &lt;Route path="/page1" component={Page1Component}/>
          &lt;Route path="/page1" component={Page2Component}/>
        &lt;/Switch>
      &lt;/Suspense>
    &lt;/Router>
  );
}
export default App;</code></pre>

<h3>関連エンントリー</h3>

<p><a href="https://blog.webcreativepark.net/2019/04/03-201809.html">ReactのSyntheticEventとDebounce</a><br />
<a href="https://blog.webcreativepark.net/2019/01/23-023216.html">ReactのContext APIを利用してコンポーネント間の情報を共有</a></p>]]>
    </content>
</entry>

<entry>
    <title>WordPressのRest APIのユーザー情報に項目を追加する</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2019/05/06-212554.html" />
    <id>tag:blog.webcreativepark.net,2019://2.1715</id>

    <published>2019-05-06T12:25:54Z</published>
    <updated>2019-05-06T13:05:50Z</updated>

    <summary>WordPressのRest APIのユーザー情報に項目を追加する必要があったの...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="WordPress" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p>WordPressのRest APIのユーザー情報に項目を追加する必要があったのでその際のメモ書き。</p>]]>
        <![CDATA[<p>具体的には以下のようなスタッフ一覧のページを作成したかったですがデフォルトでは役職の表記や各種SNSアカウントなどの項目に対応できなたかったのでカスタマイズをしました。</p>

<p><img alt="ユーザー一覧のサンプル" src="https://blog.webcreativepark.net/images/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%202019-05-06%2021.26.43.png" class="mt-image-none" style="width:100%" /></p>

<h3>ユーザー情報に項目を追加</h3>

<p>Rest APIに限らずWordPressでユーザー情報に項目を追加する場合は以下の内容をfunctions.phpに追加します。</p>

<pre><code>function my_user_meta($wb) {
	$wb['position'] = '役職';
	$wb['twitter'] = 'twitter';
	$wb['facebook'] = 'facebook';
	$wb['github'] = 'github';
	$wb['instagram'] = 'instagram';
	return $wb;
}
add_filter('user_contactmethods', 'my_user_meta', 10, 1);</code></pre>

<p>代入している値は管理画面上のラベルに利用されます。</p>

<p>通常のテンプレートで上記の内容を取得する場合は<strong>get_the_author_meta関数</strong>を利用して以下のように記述します。</p>

<pre><code>foreach($users as $user):
	$uid = $user->ID;
	print get_the_author_meta('twitter',$uid);
endforeach;</code></pre>

<p>ただしRest APIではこれらの情報が付与されませんのでカスタマイズする必要があります。</p>

<h3>ユーザー情報の項目をRest API に追加</h3>

<p>Rest APIではrest_api_initアクション時に必要んあ項目を付与することが可能です。<br />
先ほど追加した項目を</p>

<pre><code>function adding_user_meta_rest() {
	register_rest_field(
		'user',
		'user_meta',
		array(
			'get_callback'      => 'facebook_get_user_field',
			'update_callback'   => null,
			'schema'            => null,
		)
	);
}
function facebook_get_user_field( $user, $field_name, $request ) {
	return array(
		'position' => get_the_author_meta('position',$user['id']),
		'twitter' => get_the_author_meta('twitter',$user['id']),
		'facebook' => get_the_author_meta('facebook',$user['id']),
		'github' => get_the_author_meta('github',$user['id']),
		'instagram' => get_the_author_meta('instagram',$user['id']),
	);
}
add_action( 'rest_api_init', 'adding_user_meta_rest' );</code></pre>

<p>こうすることで各ユーザーのレスポンスに<strong>user_meta</strong>が追加され、ユーザー管理画面で入力した内容が反映され以下のようなJSONが出力されるようになります。</p>

<pre><code>{
  "name": "西畑 一馬",
  ...
  "user_meta": {
    "position": "代表取締役 / フロントエンドエンジニア",
    "twitter": "KazumaNishihata",
    "facebook": "kazuma.nishihata",
    "github": "KazumaNishihata",
    "instagram": "kazumanishihata"
  }
}</code></pre>

<h3>姓名などのデフォルト項目を追加</h3>

<p>デフォルト項目である姓名などもRest APIに出力されません。</p>

<p>正確にはContextをeditにすれば出力されるのですが今回は認証なしに取得したかったのでContextをeditがviewのまま出力したいという意味です。</p>

<p>この場合は先程記載した<strong>facebook_get_user_field</strong>内の項目をカスタマイズすることで対応が可能です。</p>

<p>具体的には<strong>get_user_meta</strong>で姓(first_name)を取得して出力しています。</p>

<pre><code>function facebook_get_user_field( $user, $field_name, $request ) {
	return array(
		'position' => get_the_author_meta('position',$user['id']),
		'twitter' => get_the_author_meta('twitter',$user['id']),
		'facebook' => get_the_author_meta('facebook',$user['id']),
		'github' => get_the_author_meta('github',$user['id']),
		'instagram' => get_the_author_meta('instagram',$user['id']),
		'retirement' => get_the_author_meta('retirement',$user['id']),
		'first_name' => get_user_meta( $user['id'], 'first_name', true )
	);
}</code></pre>

<p>Rest APIは柔軟にカスタマイズできるのですが慣れていないとドキュメント等を読み込まないとわからないことが多いので<br />
<a href="https://developer.wordpress.org/reference/functions/register_rest_route/">register_rest_route</a>などで独自のエンドポイントを作るのが早いかもと思ってしまいました。</p>

<h3>関連エントリー</h3>

<p><a href="https://blog.webcreativepark.net/2019/05/06-130633.html">WordPressのRest API のUser一覧で非投稿者も表示する</a><br />
</p>]]>
    </content>
</entry>

<entry>
    <title>WordPressのRest API のUser一覧で非投稿者も表示する</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2019/05/06-130633.html" />
    <id>tag:blog.webcreativepark.net,2019://2.1714</id>

    <published>2019-05-06T04:06:33Z</published>
    <updated>2019-05-06T04:15:03Z</updated>

    <summary>WordPressのRest API でユーザー一覧を取得した場合はデフォルトの...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="WordPress" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p>WordPressのRest API でユーザー一覧を取得した場合はデフォルトの設定では記事の投稿があるユーザーしか表示されません。</p>]]>
        <![CDATA[<p>非投稿者も Rest APIに表示したい場合はfunctions.phpに以下のfilterを追加します。</p>

<pre><code>function prefix_remove_has_published_posts_from_wp_api_user_query( $prepared_args, $request ) {
	unset( $prepared_args['has_published_posts'] );
	return $prepared_args;
}
add_filter( 'rest_user_query', 'prefix_remove_has_published_posts_from_wp_api_user_query', 10, 2 );</code></pre>

<p>参考: <a href="https://github.com/WP-API/WP-API/issues/2300">WP Users Endpoint doesn&#39;t return all users · Issue #2300 · WP-API/WP-API</a></p>]]>
    </content>
</entry>

<entry>
    <title>ReactのSyntheticEventとDebounce</title>
    <link rel="alternate" type="text/html" href="https://blog.webcreativepark.net/2019/04/03-201809.html" />
    <id>tag:blog.webcreativepark.net,2019://2.1713</id>

    <published>2019-04-03T11:18:09Z</published>
    <updated>2021-04-20T06:29:02Z</updated>

    <summary>Reactではevent設定時にSyntheticEvent（合成イベント）が生...</summary>
    <author>
        <name>西畑一馬</name>
        
    </author>
    
        <category term="React" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="https://blog.webcreativepark.net/">
        <![CDATA[<p>Reactではevent設定時にSyntheticEvent（合成イベント）が生成されネイティブのイベントと設定した関数の中継ぎをします。</p>]]>
        <![CDATA[<h3>ReactのSyntheticEvent</h3>

<p>例えば下記のようにinput要素のonChangeイベントにhandleChange関数を設定した場合直接ネイティブのChangeイベントがhandleChange関数を実行するのではなく間に生成されているSyntheticEventを仲介してhandleChange関数を実行がされます。</p>

<p><iframe src="https://codesandbox.io/embed/00xm7npy9n?fontsize=14&view=editor" title="ReactのSyntheticEventとDebounce1" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe></p>

<p>通常は気にすることはないのですが、SyntheticEventはパフォーマンスの観点からイベントオブジェクトを保持しておらず非同期処理の場合には注意が必要です。</p>

<p>下記のようにhandleChange関数内で非同期処理をおこないイベントオブジェクトにアクセスした場合にエラーになってしまいます。</p>

<p><iframe src="https://codesandbox.io/embed/888p530q69?fontsize=14&module=%2Fsrc%2Findex.tsx&view=editor" title="ReactのSyntheticEventとDebounce2" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe></p>

<p>これを防ぐにはハンドル関数内で <strong>e.persist();</strong> を設定することでイベントオブジェクトが破棄されずに利用できるようになります。(e.currentTargetなどの一部のプロパティはe.persist();を記述しても破棄されてしまいます。)</p>

<p><iframe src="https://codesandbox.io/embed/130683ynv3?fontsize=14&view=editor" title="ReactのSyntheticEventとDebounce3" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe></p>

<h3>SyntheticEventとDebounce</h3>

<p>この性質で厄介なのがDebounceによるハンドリングが上手に行かないこと。Debounce自体が内部的に非同期処理で制御をしているため制御後に<strong>e.persist();</strong> を指定しても意味がなく制御前に指定をしなくてはいけない。</p>

<p><iframe src="https://codesandbox.io/embed/1vn63r982l?fontsize=14&module=%2Fsrc%2Findex.tsx&view=editor" title="ReactのSyntheticEventとDebounce4" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe></p>

<p>そのためhandleChange関数内で<strong>e.persist();</strong> を実行してdebouncedHandleChange関数内で実際の処理を行うという順番を変えることで対応が可能です。</p>

<p><iframe src="https://codesandbox.io/embed/4pn4wk0zx?fontsize=14&view=editor" title="ReactのSyntheticEventとDebounce5" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe></p>

<p>ただこのままでは<strong>e.persist();</strong>が何度も実行され、せっかくDebounceしている意味が薄れる。その場合はイベントオブジェクではなくイベントオブジェクトのターゲットオブジェクトをdebouncedHandleChange関数に引き渡すことでも対応ができる。</p>

<p><iframe src="https://codesandbox.io/embed/5ww3qk358l?fontsize=14&module=%2Fsrc%2Findex.tsx&view=editor" title="ReactのSyntheticEventとDebounce5" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe></p>

<h3>参考</h3>

<p><a href="https://ja.reactjs.org/docs/events.html#event-pooling">合成イベント (SyntheticEvent) – React</a><br />
<a href="https://medium.com/trabe/react-syntheticevent-reuse-889cd52981b6">React SyntheticEvent reuse – Trabe – Medium</a><br />
<a href="https://hyunseob.github.io/2018/06/24/debounce-react-synthetic-event/">[React] Debounce SyntheticEvent | DailyEngineering</a><br />
</p>]]>
    </content>
</entry>

</feed>
