<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Katsuya Kubo on Medium]]></title>
        <description><![CDATA[Stories by Katsuya Kubo on Medium]]></description>
        <link>https://medium.com/@katsuya.kubo?source=rss-430db322893f------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*jZkyKIAzN8qOQIx9FGbjTQ.jpeg</url>
            <title>Stories by Katsuya Kubo on Medium</title>
            <link>https://medium.com/@katsuya.kubo?source=rss-430db322893f------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 11:57:52 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@katsuya.kubo/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[ScalaTestでTestcontainersのコンテナを使い回してみた]]></title>
            <link>https://medium.com/nextbeat-engineering/scalatest%E3%81%A7testcontainers%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%82%92%E4%BD%BF%E3%81%84%E5%9B%9E%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F-0d43a5fec9a6?source=rss-430db322893f------2</link>
            <guid isPermaLink="false">https://medium.com/p/0d43a5fec9a6</guid>
            <category><![CDATA[engineer]]></category>
            <category><![CDATA[scalatest]]></category>
            <category><![CDATA[testcontainer]]></category>
            <category><![CDATA[test]]></category>
            <category><![CDATA[scala]]></category>
            <dc:creator><![CDATA[Katsuya Kubo]]></dc:creator>
            <pubDate>Tue, 19 Mar 2024 05:58:50 GMT</pubDate>
            <atom:updated>2024-03-19T05:58:50.877Z</atom:updated>
            <content:encoded><![CDATA[<h4>ScalaTestにおけるTestcontainersコンテナの効率的な利用方法</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dabsAbwPrAeQkAW3Ru76PA.png" /><figcaption>画像引用元：<a href="https://github.com/testcontainers/testcontainers-scala/blob/master/logo.png">https://github.com/testcontainers/testcontainers-scala/blob/master/logo.png</a></figcaption></figure><h4>はじめに</h4><p>こんにちは、<a href="https://www.nextbeat.co.jp/"><strong>ネクストビート</strong></a>でエンジニアをしている<a href="https://twitter.com/katzkb"><strong>久保</strong></a>です。</p><p><a href="https://testcontainers.com/"><strong>Testcontainers</strong></a> は、テストコード中でDockerコンテナを使うためのライブラリです。<br>DBやファイルシステムの部分をクリーンな状態でテストすることができ、様々な言語に対応しています。<br>今回はそのTestcontainersを使って、ScalaTestでコンテナを使い回す方法を紹介します。<br><a href="https://github.com/testcontainers/testcontainers-java">testcontainers-java</a> のScalaラッパーであり公式でサポートされている <a href="https://github.com/testcontainers/testcontainers-scala"><strong>testcontainers-scala</strong></a> を使います。</p><p>testcontainers-scalaはScalaTestをラップしたトレイトも提供しており使い勝手が良いです。基本的な使用方法は割愛します。</p><h4>なぜコンテナを使い回すのか</h4><p>Testcontainersは通常、テストケースやテストスイートごとにコンテナを起動します。<br>そうすることで、テストケース・スイート間で影響を及ぼさずに済むのが利点です。<br>しかしコンテナの起動には時間がかかるため、テストが増えるたびに実行時間が長くなってしまいます。 そのため、ライブラリのページでも「sbtのIntegrationTestでの実行を推奨する」と言及されています。</p><p><a href="https://github.com/testcontainers/testcontainers-scala/blob/master/docs/src/main/tut/setup.md">testcontainers-scala/docs/src/main/tut/setup.md at master · testcontainers/testcontainers-scala</a></p><blockquote><em>Although Testcontainer tests are mere unit tests, it’s best (although not mandatory) to use them as sbt integration tests since they spawn a separate environment, can be slower and require specific configuration.</em></blockquote><p>機械翻訳：<br>「Testcontainersのテストは単なるユニットテストですが、別の環境を起動するため、遅くなり、特定の設定が必要になるため、sbtの統合テストとして使用することが最適です。」</p><p>つまり頻繁に実行するユニットテストとしてではなく、例えばリリース前の検証の段階でCIで実行する等が推奨されています。<br>ただ、もし開発段階で気軽に実行してバグの混入を早期に検知できるのであればそれに越したことはないなと思いました。<br>同じニーズはやはりあるようで、実験的な機能としてですがコンテナを使い回す <strong>withReuse</strong> というオプションが提供されています。</p><p><a href="https://java.testcontainers.org/features/reuse/">Reusable Containers (Experimental)</a></p><p>しかしこの <strong>withReuse</strong> オプションは testcontainers-scala の ScalaTest ラッパーでは<strong>未対応</strong>です（ 2024/03/14 時点）</p><p><a href="https://github.com/testcontainers/testcontainers-scala/issues/160#issuecomment-765082768">Is there a way to share the same single container instance between suites? · Issue #160 · testcontainers/testcontainers-scala</a></p><blockquote>At the current moment, there is no supported way for doing this. It’s because scalatest doesn’t provide any functionality for sharing data like this between tests.</blockquote><p>機械翻訳：<br>「現在のところ、これを行う方法はサポートされていません。ScalaTest はテスト間でこのようなデータを共有する機能を提供していないからです。」</p><p>そこで、今回は <strong>withReuse</strong> を使わずに、コンテナを使い回す方法を紹介します。</p><h4>Singleton Container Pattern</h4><p>本家のtestcontainersでも<a href="https://java.testcontainers.org/test_framework_integration/manual_lifecycle_control/#singleton-containers"> <strong>Javaでの実装例</strong></a> が紹介されている方法です。<br>しかしScalaにstaticは無いのでそれに相当する <a href="https://docs.scala-lang.org/ja/tour/singleton-objects.html"><strong>シングルトンオブジェクト</strong></a> で実装する必要があります。</p><p>注：コード例はScala3で書かれていますが2.13でも適用できます</p><p><strong>① シングルトンオブジェクトの定義</strong></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/734cb4313eab7d4373b7e8128a502648/href">https://medium.com/media/734cb4313eab7d4373b7e8128a502648/href</a></iframe><p>トップレベルにあるオブジェクト（＝シングルトンオブジェクト）として定義することでプロセス内で一つのコンテナを使い回すことができます。さらにこれを各テストスイートに提供するためのトレイトを定義します。</p><p><strong>② 汎用テストトレイトの定義</strong></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8c9c89a7f8193e89a1626b7081c65694/href">https://medium.com/media/8c9c89a7f8193e89a1626b7081c65694/href</a></iframe><p>このトレイトをテストスイートでextends、もしくはmixinすることで、テストケース間でコンテナを使い回すことができます。</p><p><strong>③ テストスイートで利用する</strong></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/30a56d5b670d938373fc1c431fc931a9/href">https://medium.com/media/30a56d5b670d938373fc1c431fc931a9/href</a></iframe><p>これと同じようなテストスイートをもう一つ書いてみましたが、実際にDocker Desktopで起動しているコンテナを見ると一つになっていることがわかります。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5_E7d3eE6rf_QUwFe5GBGw.png" /><figcaption>※ ryukコンテナはTestcontainersのライフサイクル管理用のコンテナです。</figcaption></figure><h4>注意点</h4><p>この <strong>Singleton Container Pattern</strong> にはいくつか注意点があります。</p><p><strong>① テストデータの初期化が必要<br></strong>コンテナを使い回すことで他のテスト実行時のデータが残ってしまい、そのままだとクリーンな状態でテストが行えません。<br>そのため明示的にDBの初期化を行う必要があります。</p><p>今回の例では <a href="https://gist.github.com/katzkb/e097ca302d0f15de09991f17782c9a4a">SingletonMySQLContainerSpec.scala</a> の beforeAll で ScriptUtils.runInitScriptを使って下記のようなクエリを読み込んで実行しています。</p><pre>DROP DATABASE IF EXISTS `test`;<br>CREATE DATABASE `test`;</pre><p><strong>② 複数のテストを並列実行できない<br></strong>同じコンテナを使い回す都合上、各テストケースで行われたDBの変更がお互いに影響しあい、実質的に並列実行できなくなります。<br>限られた条件下では可能かもしれませんが、この方法を採用する場合は下記のオプションを指定してテストの並列実行を無効化する必要があります。</p><pre>// build.sbt<br>Test / fork := true,</pre><p>当初、並列の無効化なら Test / parallelExecution := falseなのでは？と思い設定したのですがこれだと同一プロジェクト内のテストは直列実行されるのですが、<strong>サブプロジェクト間のテストは並列実行されてしまいます</strong>。<br>今回のサンプルコードは単一プロジェクトですが現場ではマルチプロジェクトになることも多いかと思います。<br>そこで代わりに指定するのが Test / fork := true です。<br>このオプションは指定するとテスト時に新たなJVMプロセスを立ち上げて実行してくれるのですが、実はその中で実行されるテストは <strong>サブプロジェクトも含めた全てのテストにおいて直列で実行されます。</strong><br>そのためこちらを指定することで全てのテスト間で意図しない干渉を防ぐことができました。<br>ここは私も完全に理解できていないのですがこちらの記事を参考にさせていただきました。</p><p><a href="https://xuwei-k.hatenablog.com/entry/2020/03/14/234758">sbtにおけるテストの並列実行の設定詳細解説 - xuwei-k&#39;s blog</a></p><p>ただこの並列テストの問題は通常のTestcontainersの利用方法でも少なからず抱えている問題で、例えばテストスイートごとにコンテナを立ち上げる場合はテストスイート内のテストケース同士が干渉してしまうのでテストケースの並列実行には「レコードのIDを被らせない」等の工夫が必要になります。<br>テストケースごとにコンテナを立ち上げることもできますが、それこそコンテナの起動時間がさらに伸びてしまいます。</p><p>またこの Test / fork := trueの設定ではコンテナテスト的に嬉しい点もあり、 <strong>「testコマンド終了時に自動的にコンテナが終了」 </strong>してくれます。<br>これはforkを有効にすることでtest実行時に新たなJVMプロセスが作られ、テスト終了と共にそのJVMプロセスも終了するためです。<br><em>参考: </em><a href="https://www.scala-sbt.org/1.x/docs/Forking.html"><em>https://www.scala-sbt.org/1.x/docs/Forking.html</em></a></p><p>これは <strong>「開発時に繰り返しテストを実行したい」という要件ととても相性が良い </strong>です。<br>「全テストが終了したらコンテナを終了する」という処理を独自で書くのは難易度が高い気がするのでこれはありがたい副次的効果でした。</p><h4>おわりに</h4><p>本記事では Singleton Container Pattern を用いて Testcontainers のコンテナを使い回す方法を紹介しました。<br>本来の利用方法とは異なりますが、同じニーズを抱えていらっしゃる方の参考になれば幸いです。</p><p>今回用いたソースは実際に動くものをこちらに用意しています。<a href="https://github.com/katzkb/tc-scala-sample">https://github.com/katzkb/tc-scala-sample</a></p><h3>We are hiring!</h3><p>本記事をご覧いただき、ネクストビートの技術や組織についてもっと話を聞いてみたいと思われた方、カジュアルにお話しませんか？</p><p>・今後のキャリアについて悩んでいる<br>・記事だけでなく、より詳しい内容について知りたい<br>・実際に働いている人の声を聴いてみたい</p><p>など、まだ転職を決められていない方でも、ネクストビートに少しでもご興味をお持ちいただけましたら、ぜひカジュアルにお話しましょう！</p><p>🔽申し込みはこちら<br><a href="https://hrmos.co/pages/nextbeat/jobs/1000008">https://hrmos.co/pages/nextbeat/jobs/1000008</a></p><p>また、ネクストビートについてはこちらもご覧ください。</p><p>🔽エントランスブック<br><a href="https://note.nextbeat.co.jp/n/nd6f64ba9b8dc">https://note.nextbeat.co.jp/n/nd6f64ba9b8dc</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0d43a5fec9a6" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nextbeat-engineering/scalatest%E3%81%A7testcontainers%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%82%92%E4%BD%BF%E3%81%84%E5%9B%9E%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F-0d43a5fec9a6">ScalaTestでTestcontainersのコンテナを使い回してみた</a> was originally published in <a href="https://medium.com/nextbeat-engineering">nextbeat-engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Auth0 Actionsで Progressive Profiling を実装してみた]]></title>
            <link>https://medium.com/nextbeat-engineering/auth0-actions%E3%81%A7-progressive-profiling-%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F-8be443a9c789?source=rss-430db322893f------2</link>
            <guid isPermaLink="false">https://medium.com/p/8be443a9c789</guid>
            <category><![CDATA[engineer]]></category>
            <category><![CDATA[auth0]]></category>
            <category><![CDATA[sveltekit]]></category>
            <category><![CDATA[progressive-profiling]]></category>
            <category><![CDATA[authentication]]></category>
            <dc:creator><![CDATA[Katsuya Kubo]]></dc:creator>
            <pubDate>Thu, 13 Apr 2023 08:06:46 GMT</pubDate>
            <atom:updated>2023-04-13T08:07:25.228Z</atom:updated>
            <content:encoded><![CDATA[<h4>Auth0 Actions を用いてSvelteKit で Progressive Profilingを実装してみました。</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/668/1*z6YnkFKaPX68FsyQJDzW4w.jpeg" /><figcaption>auth0 by okta</figcaption></figure><h4><strong>はじめに</strong></h4><p>こんにちは。エンジニアの久保です。 <br>最近は<a href="https://www.hoikushibank.com/">保育士バンク！</a>というサービスを中心にプロダクト横断の基盤部分を開発しています。<br>今回は認証基盤である<a href="http://auth0.com/">Auth0</a>を使ったProgressive Profilingについて記事を書かせていただきます。</p><h4>本記事を読むことで分かること</h4><ul><li>Auth0を用いた Progressive Profiling の概要</li><li>公式ドキュメントには無い具体的な実装例</li><li>IDaaSの便利さ</li></ul><h4>免責事項</h4><ul><li>本記事では一部セキュリティに言及しますが、そこが本題ではないため割愛している部分があります。他文献も参考にしてください。</li><li>サンプルコードで一部 Local Storage や cookie を用いていますが、あくまでサンプルコードですので利用するストレージは別途ご検討ください</li><li>Auth0に関する仕様やUIは2023年4月10日時点のものです。最新の公式ドキュメントも併せてご参考ください。</li></ul><h4>概要</h4><p>皆さんは <a href="https://auth0.com/blog/jp-progressive-profiling/">Progressive Profiling</a> についてご存知でしょうか。<br>簡単に言いますと「段階的なフォーム入力体験」のことを指します。<br>どんなサービスでもそうですが、コンバージョン率を上げるために登録フォームの入力項目はなるべく減らしたいのが常かと思います。<br>ですがIDとパスワード以外にも欲しいユーザーデータは多々あります。<br>そこで一度にすべての情報入力を求めるのではなく段階的に入力を求めれば、まずは登録だけはしてくれるかもしれません。<br>ということで今回は Auth0 の Actions という機能を用いてそれを実装してみます。<br>Auth0 はアカウントの認証・管理機能を提供しているいわゆる IDaaS です。<br>実装するフォームのステップとしては下記の通りです。</p><p>1. ID/Passwordを入力して登録（New Universal Login）<br>2. 氏名や住所などの追加情報を入力（独自実装）<br>3. 登録完了</p><p>基本的な認証処理の説明は省かせていただきます。<br>また実装は弊社サービスでも導入している <a href="https://kit.svelte.jp/">SvelteKit</a> で行います。</p><h4>シーケンスの確認と解説</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/964/1*JtpH6vQHvpGy6Y5Po2-27Q.png" /><figcaption>シーケンス図</figcaption></figure><p>「追加情報入力フェーズ」以外の部分はありふれた認証フローです。<br>では9番から15番までを作っていきます。</p><h4>Auth0 Actions を使用する</h4><p>Auth0には様々なアクションをトリガーにして予め設定していた処理を実行させることができます。<br>ではさっそく作っていきましょう。<br>サイドメニューの 「Actions &gt; Library」 から遷移し、画面上部のBuild Customを押します。<br>名前は任意で、トリガーには「Login / Post Login」を選びます。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*N2RI9ldUvTZvmSzv_aYknA.png" /><figcaption>自分だけのActionを作ります</figcaption></figure><p>今回は登録時に追加情報を入力してもらうのが目的です。<br>なので「<strong>Post User Registration</strong>」かと思われるかもしれないのですが、このトリガーは非同期で実行されるので今回の目的であるProgressive Profilingには使えません。<br>各Actionの概要は<a href="https://auth0.com/docs/customize/actions/actions-overview">こちら</a>からご確認いただけます。<br>また途中離脱などされて再開した場合もやはり入力してほしいはずです。<br>そうなると「<strong>Login</strong>」が最適です。 <strong>Login</strong> は登録したあとの初回ログイン、離脱・ログアウト後のログイン時にも処理が走るためです。<br>作成するとこのようなテンプレートが既に用意されています。</p><pre>exports.onExecutePostLogin = async (event, api) =&gt; {<br>};<br>// exports.onContinuePostLogin = async (event, api) =&gt; {<br>// };</pre><p>ログインをトリガーにしてこの 「<strong>onExecutePostLogin</strong>」が呼び出されます。シーケンス図でいうところの9番の処理です。<br>「<strong>onContinuePostLogin</strong>」の方は後ほど解説します。<br>まずは最初の関数を利用して追加情報画面にリダイレクトしてあげましょう。</p><h4>onExecutePostLoginの実装 （シーケンス：⑨, ⑩）</h4><pre>const redirectToValidationPage = async (event, api) =&gt; {<br>  const YOUR_DOMAIN = event.secrets.YOUR_DOMAIN <br>  const token = api.redirect.encodeToken({ // ①-1<br>    secret:           event.secrets.AUTH0_ACTIONS_SECRET, // ①-2<br>    payload: {<br>      continue_uri:   `https://${YOUR_DOMAIN}/continue`, // ②<br>      name:           event.user.name ?? &#39;&#39;, // ③-1<br>      address:        event.user.user_metadata.address ?? &#39;&#39;, // ③-2<br>    }<br>  })<br>}<br>exports.onExecutePostLogin = async (event, api) =&gt; {<br>  // 追加情報が未入力であれば追加情報入力画面へリダイレクトする<br>  if (!event.user.name || !event.user.user_metadata.address) { // ④<br>    await redirectToValidationPage(event, api)<br>  }<br>};</pre><ul><li>まず引数の <strong>event</strong> と <strong>api</strong> ですが、こちらにはActions内で使える便利なメソッドやデータが用意されています（<a href="https://auth0.com/docs/customize/actions/flows-and-triggers/login-flow/event-object">event</a>, <a href="https://auth0.com/docs/customize/actions/flows-and-triggers/login-flow/api-object">api</a>）</li><li>①-1：リダイレクト時はGETアクセスですので、<strong>api.redirect.encodeToken</strong> を使うことでやり取りするデータを署名し正当性を担保します</li><li>①-2：この <strong>AUTH0_ACTIONS_SECRET</strong> が共通秘密鍵で、これで署名検証します。ハードコーディングは避けられるように、Actionsには環境変数を登録ことができます。Actionsのエディタ左側にある鍵マークから登録しましょう。もちろん名称は何でも良いです。ちなみに値も特段決まりはありませんが私はUUIDを生成して使いました</li><li>②：これが追加情報入力後の遷移先で、先程出てきた <strong>onContinuePostLogin</strong> を呼び出すエンドポイントです</li><li>③：この２つが今回の追加情報です。以前に片方だけ入力した場合を想定してこのような実装にしています</li><li>④：この関数はログインのたびに呼び出されますので、既に入力済みな場合を想定してガードを書きましょう</li></ul><h4>/auth/login/step/2 の実装 （シーケンス：⑪〜⑭）</h4><p>このActionsからリダイレクトされた先の実装はサービスの仕様によって様々ですが、今回は追加情報入力フォームをあと１段階だけ挟みます。</p><h4>+page.server.ts （シーケンス：⑪）</h4><pre>import { env } from &#39;$env/dynamic/private&#39;<br>import * as jwt from &#39;jsonwebtoken&#39;<br>import { error } from &#39;@sveltejs/kit&#39;<br><br>/** @type {import(&#39;./$types&#39;).PageServerLoad} */<br>export async function load({ url }) {<br>  const query        = url.searchParams<br>  const sessionToken = query.get(&#39;sessionToken&#39;)<br>  const state        = query.get(&#39;state&#39;)<br><br>  if (!sessionToken || !state) {<br>    throw error(400, { message: &#39;Bad Request&#39; })<br>  }<br><br>  let decodedToken: DecodedSessionToken<br>  try {<br>    decodedToken = jwt.verify(sessionToken, env.AUTH0_ACTIONS_SECRET, { // トークンの検証<br>      issuer:     env.AUTH0_DOMAIN,<br>      algorithms: [&#39;HS256&#39;],<br>    }) as DecodedSessionToken<br>  } catch {<br>    throw error(400, { message: &#39;Bad Request&#39; })<br>  }<br>  // クライアントに値を返却<br>  return {<br>    userId:      decodedToken.sub,<br>    continueUri: decodedToken.continue_uri,<br>    name:        decodedToken.name,<br>    address:     decodedToken.address,<br>    state<br>  }<br>}</pre><p>Actionsから返ってきたトークンはJWTなのでAuth0公式が公認している<a href="https://jwt.io/">JWTライブラリ</a>で検証してあげましょう。<br>シークレットなどは環境変数に保存して呼び出しています。<br>無事検証できればActionsから送られたpayloadが手に入ります。<br>continue_uri や state は後続処理を呼び出す際に必要なので手間ですが引き回しましょう。</p><h4>+page.svelte（シーケンス：⑫）</h4><pre>&lt;form method=&quot;POST&quot; action=&quot;?/continue&quot;&gt;<br>  &lt;FormGroup floating label=&quot;氏名&quot;&gt;<br>    &lt;Input type=&quot;text&quot; name=&quot;name&quot; value=&quot;{$page.data.name}&quot; required /&gt;<br>  &lt;/FormGroup&gt;<br>  &lt;FormGroup floating label=&quot;住所&quot;&gt;<br>    &lt;Input type=&quot;text&quot; name=&quot;address&quot; value=&quot;{$page.data.address}&quot; required /&gt;<br>  &lt;/FormGroup&gt;<br>  &lt;input type=&quot;hidden&quot; name=&quot;userId&quot; value=&quot;{$page.data.userId}&quot; /&gt;<br>  &lt;input type=&quot;hidden&quot; name=&quot;continueUri&quot; value=&quot;{$page.data.continueUri}&quot; /&gt;<br>  &lt;input type=&quot;hidden&quot; name=&quot;state&quot; value=&quot;{$page.data.state}&quot; /&gt;<br>  &lt;Button&gt;登録&lt;/Button&gt;<br>&lt;/form&gt;</pre><p>レイアウトのためにBootstrapを使っていますが普通のタグに読み替えてもらって問題ありません。<br>Svelteの<a href="https://www.google.com/url?sa=t&amp;rct=j&amp;q=&amp;esrc=s&amp;source=web&amp;cd=&amp;cad=rja&amp;uact=8&amp;ved=2ahUKEwjuotKUp5_-AhVnsFYBHcmWBJYQFnoECBEQAQ&amp;url=https%3A%2F%2Fkit.svelte.jp%2Fdocs%2Fform-actions&amp;usg=AOvVaw1IkxqLL5R61JiHZGEhZjRu">Form actions</a>を使って、先程の <strong>+page.server.ts</strong> に入力値を返却します。</p><h4>+page.server.ts（シーケンス：⑬, ⑭）</h4><p>先程の +page.server.ts に追加でフォームの受け口を作ります</p><pre>/** @type {import(&#39;./$types&#39;).Actions} */<br>export const actions = {<br>  continue: async ({request}) =&gt; {<br>    const data        = await request.formData();<br>    const name        = data.get(&#39;name&#39;)<br>    const address     = data.get(&#39;address&#39;)<br>    const userId      = data.get(&#39;userId&#39;)<br>    const continueUri = data.get(&#39;continueUri&#39;)<br>    const state       = data.get(&#39;state&#39;)<br>    if (!name || !address || !userId || !continueUri || !state) {<br>      throw error(400, { message: &#39;Bad Request&#39; })<br>    }<br>    const sessionToken =<br>      jwt.sign(<br>        { continue_uri: continueUri, name, address, state },<br>        env.AUTH0_ACTIONS_SECRET,<br>        {<br>          algorithm: &#39;HS256&#39;,<br>          subject:   userId,<br>          issuer:    env.APP_URL,<br>          expiresIn: 60,<br>        })<br><br>    throw redirect(302, `${continueUri}?state=${state}&amp;sessionToken=${sessionToken}`)<br>  }<br>};</pre><p>ポイントとしては <strong>jwt.sign</strong> の部分です。<br>ここで再度Actionsに返すのですが、やはり改ざんがあれば気付けるように署名する必要があります。<br><strong>issuer（発行者）</strong>には自身のアプリケーションのURLを設定しましょう。</p><h4>onContinuePostLoginの実装（シーケンス：⑮）</h4><pre>// ユーザーデータを更新するためのメソッドを用意<br>// https://community.auth0.com/t/updating-user-profile-from-actions/85206<br>const updateUser = async (event, data) =&gt; {<br>  const ManagementClient = require(&#39;auth0&#39;).ManagementClient<br>  <br>  const management = new ManagementClient({<br>    domain:       event.secrets.AUTH0_DOMAIN,<br>    clientId:     event.secrets.CLIENT_ID,<br>    clientSecret: event.secrets.CLIENT_SECRET<br>  })<br><br>  const params = { id: event.user.user_id }<br>  await management.updateUser(params, data, function (err) {<br>    if (err) {<br>      console.log(err)<br>    }<br>  })<br>}<br><br>exports.onContinuePostLogin = async (event, api) =&gt; {<br>  let payload<br>  // トークンを検証して取り出し<br>  try {<br>    payload = api.redirect.validateToken({<br>      secret:             event.secrets.AUTH0_ACTIONS_SECRET,<br>      tokenParameterName: &#39;sessionToken&#39;<br>    })<br>  } catch (e) {<br>    console.log(e)<br>    api.access.deny(&#39;トークンの検証に失敗しました&#39;)<br>    return<br>  }<br>  // 追加情報が未入力であれば追加情報入力画面へリダイレクトする<br>  if (!payload.name || !payload.address) {<br>    await redirectToValidationPage(event, api)<br>    return<br>  }<br>  // 問題がなければデータを更新して完了<br>  api.user.setUserMetadata(&#39;address&#39;, payload.address)<br>  await updateUser(event, { name: payload.name })<br>};</pre><p>以上のプロセスが完了するとシーケンス図の16番、 <strong>/api/auth/callback</strong> に返ってきます。<br>そこからの処理は他の方記事や公式ドキュメントが充実していますのでそちらをご確認ください。<br>ここまで完了したらActionsエディタの右上ので「Deploy」ボタンでデプロイしましょう。</p><h4>フローに乗せる</h4><p>作ったActionsはFlowに組み込むことで実際にトリガーされます。<br>Actions &gt; Flows から 「Login」 を選択しましょう。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oBV6QDaIOtmhFXuvWJ7RkQ.png" /><figcaption>フローの一覧</figcaption></figure><p>Flowの全体像が表示されます。右側の「Custom」を選ぶと先程デプロイした自作のActionsが出てきますので、ドラッグアンドドロップで組み込みましょう。<br>右上のApplyをして反映完了です。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qnot6nehoAU-rYBlir0XNQ.png" /></figure><h4>最後に</h4><p>Auth0はドキュメントが豊富ではあるのですがすべて英語で、またActions自体の記事もあるにはありますが具体的な事例もあると良いなと思い本記事を書かせていただきました。<br>データ保持部分等はかなり省略していますが、やはりユーザーの管理をここまで任せられ、かつ柔軟なビジネス要件にも対応しているAuth0は調査してみてとても勉強になりました。<br>今回利用したソースは<a href="https://github.com/katzkb/auth0-post-login-action-sample">こちら</a>に上げてあります。<br>本記事が何かのお役に立てますと幸いです。<br>ありがとうございました。</p><h3>We are hiring!</h3><p><a href="https://www.nextbeat.co.jp/">株式会社ネクストビート</a>では</p><p><strong>「人口減少社会において必要とされるインターネット事業を創造し、ニッポンを元気にする。」<br></strong>を理念に掲げ一緒に働く仲間を募集しております。</p><p><a href="https://www.nextbeat.co.jp/recruit">https://www.nextbeat.co.jp/recruit</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8be443a9c789" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nextbeat-engineering/auth0-actions%E3%81%A7-progressive-profiling-%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F-8be443a9c789">Auth0 Actionsで Progressive Profiling を実装してみた</a> was originally published in <a href="https://medium.com/nextbeat-engineering">nextbeat-engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Scala: マッピングモデルを使う構造で表示用データを高速に組み立てる方法]]></title>
            <link>https://medium.com/nextbeat-engineering/scala-%E3%83%9E%E3%83%83%E3%83%94%E3%83%B3%E3%82%B0%E3%83%A2%E3%83%87%E3%83%AB%E3%82%92%E4%BD%BF%E3%81%86%E6%A7%8B%E9%80%A0%E3%81%A7%E8%A1%A8%E7%A4%BA%E7%94%A8%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E9%AB%98%E9%80%9F%E3%81%AB%E7%B5%84%E3%81%BF%E7%AB%8B%E3%81%A6%E3%82%8B%E6%96%B9%E6%B3%95-168ac1e81094?source=rss-430db322893f------2</link>
            <guid isPermaLink="false">https://medium.com/p/168ac1e81094</guid>
            <category><![CDATA[engineer]]></category>
            <category><![CDATA[algorithms]]></category>
            <category><![CDATA[scala]]></category>
            <dc:creator><![CDATA[Katsuya Kubo]]></dc:creator>
            <pubDate>Thu, 07 Apr 2022 09:11:02 GMT</pubDate>
            <atom:updated>2022-04-07T09:11:02.660Z</atom:updated>
            <content:encoded><![CDATA[<h4>はじめに</h4><p>こんにちは。エンジニアの久保です。<br>普段は<a href="https://kidsna-connect.com/site/">保育士バンク！コネクト</a>というサービスを開発しています。<br>今回はScalaでコレクション操作する際のパフォーマンスに関する小ネタについてお話ししたいと思います。</p><h4>概要</h4><p>「ユーザーが複数のグループに所属する」という設計の場合。<br>こんなかんじのマッピングテーブルを使う実装ってよくあると思います。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/902/1*A_9R8i4i-K0qD-2nXEuf5w.png" /></figure><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e1a1b5039889166cb0fd7dd00c8fb651/href">https://medium.com/media/e1a1b5039889166cb0fd7dd00c8fb651/href</a></iframe><p>で、このデータ構造から下記のような「ユーザとその所属グループ一覧」を複数取得したい場合のScalaでの良い書き方と悪い書き方を紹介していこうと思います。</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c5fdd0dc0f5ede873f8c9fda11f5a3aa/href">https://medium.com/media/c5fdd0dc0f5ede873f8c9fda11f5a3aa/href</a></iframe><p>言語やフレームワークによっては LEFT JOIN など使ってクエリの方で解決する手段もあると思いますが、今回はScalaの世界で実装する前提で書きます。<br>ちなみにバージョンは 2.13.1です。</p><h4>■ 悪いパターン</h4><h4>実装確認</h4><p>まず悪いパターンから。</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/70c8ab700f725989335802e279962b5d/href">https://medium.com/media/70c8ab700f725989335802e279962b5d/href</a></iframe><p>1. userSeq を map で回して<br>2. userId で GroupMember を特定し<br>3. それに紐づく Group を取得する</p><p>といった流れですね。<br>仕様をそのまま素直に書くとこういう書き方になるかと思います。<br>どこがいけないのでしょうか？<br>では実行速度を見てみます。<br>まずは10件。</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bb051c2f68d29c009b37e340ed7725ed/href">https://medium.com/media/bb051c2f68d29c009b37e340ed7725ed/href</a></iframe><p>3回ほど実行。</p><pre>$ scala testA.scala<br>3ミリ秒<br>$ scala testA.scala<br>2ミリ秒<br>$ scala testA.scala<br>3ミリ秒</pre><p>問題なさそう。<br>では10,000件。</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6719a50ee253a1da1afa0cf6d9b7efb5/href">https://medium.com/media/6719a50ee253a1da1afa0cf6d9b7efb5/href</a></iframe><p>同じく3回実行。</p><pre>$ scala testA.scala<br>5231ミリ秒<br>$ scala testA.scala<br>4681ミリ秒<br>$ scala testA.scala<br>5225ミリ秒</pre><p>悲惨ですね。なぜこうなってしまうのでしょうか。</p><h4>問題箇所</h4><p>計算量を確認します。</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0385e5c46b2b73ce1230bc5e90ad7885/href">https://medium.com/media/0385e5c46b2b73ce1230bc5e90ad7885/href</a></iframe><p>filter はコレクションの先頭から末尾まで確認するので、計算時間は線形です。<br>なので計算量は</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/235/1*6bTGK1omajh9S9ADfYrv4w.png" /></figure><p>となり、ユーザー数、グループ数、マッピング数それぞれが10,000件(n=10,000)の場合<br>200,000,000 = 2*10,000*10,000</p><p>2億回。絶望的ですね。<br>O(n²) の部分をなんとかすれば速くなりそうです。</p><h4>■ 良いパターン</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b34928ca5722374d4177bbe14164d7fa/href">https://medium.com/media/b34928ca5722374d4177bbe14164d7fa/href</a></iframe><p>要らないと思いますが10件で実行した結果。</p><pre>$ scala testB.scala<br>4ミリ秒<br>$ scala testB.scala<br>4ミリ秒<br>$ scala testB.scala<br>2ミリ秒</pre><p>大差無いですね。<br>では10,000件</p><pre>$ scala testB.scala<br>472ミリ秒<br>$ scala testB.scala<br>420ミリ秒<br>$ scala testB.scala<br>470ミリ秒</pre><p>うーん、爆速ってわけじゃないですが割とマシになりました。</p><h4>工夫した点</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d77719951202ac26333a44f99c1eaaf9/href">https://medium.com/media/d77719951202ac26333a44f99c1eaaf9/href</a></iframe><ul><li>①で最初に GroupMember を使って、ユーザーIdをキーにしたグループリストのマップを作ってしまいます。</li><li>②でマッピング数分回るので10,000回</li><li>③は今回の例では1回。ユーザーが10グループに所属してたら10回となりますがさほど影響しません</li><li>④はfindなので線形、Vectorを使えば実質定数です（<a href="https://docs.scala-lang.org/ja/overviews/collections/performance-characteristics.html">性能特性</a>)</li><li>⑤はユーザー数分</li></ul><p>なので</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/257/1*7cOCMTiy7c_W6v7jjprdcQ.png" /></figure><p>となりn=10,000なら20,000。<br>2万回で済む、というわけですね。<br>全ユーザーが10グループに属しているとしても11万回で、悪いパターンの2億回よりはマシです。</p><h3>まとめ</h3><p>Scala勉強し始めたころは filter が便利すぎて多用してしまいがちですが、計算量はしっかり意識して実装したいですね。<br>find は定数なので、 groupBy でマップを作ってしまってキーで find する、というこの方法は割と色んな場面で使えるのではないかと思います。<br>また、データ量が少ないと問題が表面化しないのも罠ですね。</p><p>お読み頂きありがとうございました。<br>改善の余地はある気がするので、<br>間違っている点、改善できる点あればご指摘頂けますと幸いです🙇‍♂️</p><h3>We are Hiring!</h3><p><a href="https://www.nextbeat.co.jp/">株式会社ネクストビート</a>では</p><p><strong>「人口減少社会において必要とされるインターネット事業を創造し、ニッポンを元気にする。」</strong></p><p>を理念に掲げ<a href="https://hrmos.co/pages/nextbeat/jobs?category=1435504483358539776">一緒に働く仲間を募集</a>しております。</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=168ac1e81094" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nextbeat-engineering/scala-%E3%83%9E%E3%83%83%E3%83%94%E3%83%B3%E3%82%B0%E3%83%A2%E3%83%87%E3%83%AB%E3%82%92%E4%BD%BF%E3%81%86%E6%A7%8B%E9%80%A0%E3%81%A7%E8%A1%A8%E7%A4%BA%E7%94%A8%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E9%AB%98%E9%80%9F%E3%81%AB%E7%B5%84%E3%81%BF%E7%AB%8B%E3%81%A6%E3%82%8B%E6%96%B9%E6%B3%95-168ac1e81094">Scala: マッピングモデルを使う構造で表示用データを高速に組み立てる方法</a> was originally published in <a href="https://medium.com/nextbeat-engineering">nextbeat-engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Webサービス開発会社でIoT製品を開発した話（プロトタイピング編）]]></title>
            <link>https://medium.com/nextbeat-engineering/web%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E9%96%8B%E7%99%BA%E4%BC%9A%E7%A4%BE%E3%81%A7iot%E8%A3%BD%E5%93%81%E3%82%92%E9%96%8B%E7%99%BA%E3%81%97%E3%81%9F%E8%A9%B1-%E3%83%97%E3%83%AD%E3%83%88%E3%82%BF%E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0%E7%B7%A8-7ebba8c57ee7?source=rss-430db322893f------2</link>
            <guid isPermaLink="false">https://medium.com/p/7ebba8c57ee7</guid>
            <category><![CDATA[project-management]]></category>
            <category><![CDATA[bluetooth]]></category>
            <category><![CDATA[engineer]]></category>
            <category><![CDATA[nfc]]></category>
            <category><![CDATA[iot]]></category>
            <dc:creator><![CDATA[Katsuya Kubo]]></dc:creator>
            <pubDate>Mon, 12 Apr 2021 08:54:54 GMT</pubDate>
            <atom:updated>2021-04-12T08:54:54.527Z</atom:updated>
            <content:encoded><![CDATA[<h4>Bluetooth対応のFeliCaカードリーダーを開発しました</h4><p>こんにちは。エンジニアの久保です。<br>普段は<a href="https://kidsna-connect.com/site/">KIDSNAコネクト</a>というサービスを開発しています。<br><a href="https://medium.com/nextbeat-engineering/%E7%AA%81%E6%92%83-%E9%9A%A3%E3%81%AE%E3%82%AD%E3%83%BC%E3%83%9C%E3%83%BC%E3%83%89-nextbeat-2019-953708e5095b">自作キーボード制作</a>が趣味なのですが、それが高じて今回IoT製品の開発をマネジメントすることになりました。<br>現在量産体制に移行しているのですが、今回はその手前であるプロトタイピングをどのようにして進めたのかについて書かせていただきます。<br>また、私自信プロジェクトマネジメント自体も初めての経験でしたのでその観点での気づきも併記させていただきます。</p><h3>何を作ったのか</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vhvF_lWubf8cstr1q5sSzA.jpeg" /><figcaption>割とコンパクト</figcaption></figure><p>写真はプロトタイプのものです。<br>BluetoothでiPadとペアリングして利用できる<strong>FeliCaカードリーダー</strong>を開発しました。<br>といっても汎用的に利用できるわけではなく、弊社が提供している <a href="https://kidsna-connect.com/site/">KIDSNAコネクト</a>の打刻用のiPadアプリで使うためにカスタマイズして開発しました。<br>FeliCaのIDmの読み取りを行うだけなので、入退室管理などにもよく使われるMifareカードのUIDも読み取れるようにしました。</p><p><a href="https://cweb.canon.jp/idprinter/iccard/select/iccardcategory.html">FeliCa、Mifareの違いを解説。ICカードの種類と特長を比較する：キヤノン</a></p><h3>なぜ作ったのか</h3><p>前述した KIDSNAコネクト では主に保育士さん向けに勤怠・労務管理機能を提供しており、これまでは市販のBluetooth対応FeliCaカードリーダーを利用して出勤・退勤の打刻を行えるようにしていました。<br>しかしこれが生産終了となり代替品も見つからないという事態が発生しました。</p><p><a href="https://www.sony.co.jp/Products/felica/business/products/comparison.html">Sony Japan | FeliCa | 法人のお客様 | 製品情報 | PaSoRi（パソリ）製品比較表</a></p><p>もちろん打刻方法自体を変えることも検討しました。<br>web画面上で打刻する機能やQRコードで打刻、顔認証など様々検討しましたが、現状のFeliCaカードリーダーで使い慣れているお客様も多くいる中で代替手段を強いるのも忍びないという思いがありました。</p><p>そんな折に <strong>「カードリーダー自体を作ってしまうのはどうか」 </strong>という案が上がり、自作キーボード好き・電子工作好きを公言していた私に声がかかりました。<br><strong>無ければ作ればいい</strong>、という至極シンプルな発想です。<br>自社で作るメリットとして、他社製品に依存しない形で打刻機能を提供できるようになります。</p><h3>協力会社の選定</h3><p>まず弊社にはIoT製品開発に関するナレッジがありません。<br>そのためPoC・プロトタイピングはその知見のある会社の協力を仰ぐこととにしました。<br>今回は弊社CIO/VPoEである三井の紹介で <a href="https://mocketech.com/">もっけ技研</a> 様にご協力いただきました。</p><p><a href="https://mocketech.com/">Top | Mocke Tech. LLC. - 合同会社もっけ技研</a></p><p>またこの先、基板製造サービスや部品メーカー等様々な外部組織と連携して進めていきますが、選定基準としては下記の項目が上げられます。<br>どんなプロジェクトでも重視される項目ですが、IoTのプロジェクトにおいてはより強く影響する部分が多いかと思います。</p><p><strong>①費用</strong><br>やはり費用を如何に抑えるかというところはプロジェクトとして大事です。<br>仕事を依頼できそうな数社に目星をつけ、相見積もりを取りましょう。<br>「ここしかない！」というサービスを見つけても最低もう一社からは見積もりは取りましょう。<br>2社以上を比較することで、どのようなことをサービスとして提供してくれるのか？<br>どの辺りにコストがかかるのかなど正当性がわかります。</p><p><strong>②納品速度<br></strong>費用と同じくらい大切です。<br>同じ部品でもメーカーさんによって数週間〜数ヶ月単位で差があったりします。<br>「どこも同じだろう」と思い込んでいると、実はもっと短縮できたなんていうケースもあります。<br>見積もりを頂く段階で納期についてもざっくり聞いておくと良いです。</p><p><strong>③レスポンスの早さ</strong><br>プロジェクトにおいては返答一つの遅れがスケジュールに地味に響いてきます。遅いところは本当に遅く、見積もりを依頼した数社の中では一週間返事が返ってこないところもありました。<br>問い合わせへの返信の早さ・見積もり提出の早さ等で見てみると良いかもしれません。</p><h3>スケジュールの策定</h3><p>今回のプロジェクトのデッドラインは、<strong>既製品であるカードリーダーの保有在庫が尽きるまで </strong>です。<br>このプロジェクトは2020年4月に開始しましたが、その時点の試算では1年後である2021年3月までは保有在庫分で間に合うと判断しました。<br>ですので具体的なゴールとしては<br><strong>「2021年3月末時点で自社製カードリーダーを500台納品すること」</strong><br>と設定しました。<br>そこから逆算して<strong>「2020年9月末までにプロトタイピングを完了させる」</strong>というのを当面のゴールとして設定しました。<br>あとはそれに必要な部品の調達や実装など、作業をブレークダウンしてWBSツール（今回はInstagantt）を利用して可視化しました。<br>もちろんこの間に代替の打刻機能も平行して検討を進め、万が一に備えました。</p><p><a href="https://instagantt.com/">Online Gantt Chart Software for Project Management | Instagantt</a></p><h3>スケジュールを左右する要素</h3><p>大まかなスケジュールを引くことはできましたが、実際に進めてみるとソフトウェア開発ではあまり無い要素でスケジュールを左右するものがありました。</p><p><strong>①部品調達にかかる時間<br></strong>プロトタイプを作るには各電子部品や外装の素材等の調達が必要です。<br>一般的なものであればECサイトでも購入できますが、モノによっては直接秋葉原に行って電子部品屋さんで調達したほうが早いモノもあります。<br>特にモジュール系は定常的に量産していないものだとストックが無いことも多く、余裕を持った調達スケジュールが必要です。</p><p><strong>②基板製造にかかる時間</strong><br>ソフトウェアの場合、失敗しても修正はすぐに行なえますがハードウェアはそうはいきません。<br>基板も最初こそブレッドボードなどで柔軟な形で検証しますが、いざ基板を作ってみると意外と部品配置を変えたいことも多く、2~3回は基板の作り直しが発生しました。<br>また、基板製造・部品実装には1回につき少なくとも約2週間ほどかかると見ておいたほうが良いです。</p><p><strong>③プロトタイプ製品のやりとりにかかる時間</strong><br>コロナ禍で製品開発をするにあたって障壁だったのがモノのやりとりです。IoT製品では不具合を確かめるにはやはり現物が必要になります。<br>今回は協力会社の他に社内のアプリエンジニアにも協力いただいていたので、そこに私を含めた3者間でプロトタイプ製品をやりとりしていました。<br>通常であれば弊社オフィスと協力会社オフィス間でやりとりできれば良かったのですが、緊急事態宣言で、フルリモート化したため、それぞれの自宅宛にモノを配送する、といった対応が必要になりました。<br>また、そのやりとりにも数日かかるため、わずかではありましたがスケジュールにも影響しました。</p><p>このようにIoT製品・ハードウェアはその性質から、<strong>物理的に時間が必要になることが多い</strong>です。ソフトウェアとは違い、工夫でどうにかできない部分もあるのでやはりプロジェクト全体として3ヶ月ほどのバッファは持っておくと良いと感じました。</p><h3>見積原価計算書の作成</h3><p>開発・製造にかかるコストも見積もらなければなりません。<br>書式や書き方については下記の書籍が参考になりました。<br>それ以外もIoTの製品開発・量産体制に関する有意義な情報が載っています。</p><p><a href="https://www.oreilly.co.jp/books/9784873118840/">メイカーとスタートアップのための量産入門</a></p><p>予想のつかない部分の方が多いですが、分かっているものからリストアップし管理するようにしました。<br>具体的にかかるコストとしては</p><ul><li>協力会社への業務委託費</li><li>基板製造/実装費用</li><li>部品購入費</li><li>外装製造費</li><li>組立費</li></ul><p>これらから、量産した際に1台あたりにかかる費用（原価）を試算します。<br>そのためにはまず各費用を<strong>固定費</strong>か<strong>変動費</strong>に分類する必要があります。<br>固定費とは生産台数によって左右されないものです。<br>今回でいえば 「協力会社への業務委託費」 が当てはまります。<br>他の費用は生産台数によって左右されるので変動費にあたります。</p><p>生産数５００台、卸率100%、各費用を100万円と仮定して表にしてみると下記のようになります。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/564/1*sJCe-2MCK9OdZqSlEr02sQ.png" /><figcaption>項目や費用はあくまでサンプルです</figcaption></figure><p>当たり前ですが、<strong>価格は最低でも変動費単価より上</strong>に設定する必要があります。でないと作るたびに赤字になってしまいます。<br>また、卸率100%として考えると「固定費/生産台数」の分も足すことで1台目の販売から製造原単価を回収することができます。<br>しかし固定費は生産台数によって増えることが無いため、価格が変動費より高ければいつかは固定費分の原価も回収できます。</p><p>では、いくらに設定すれば、何台販売することで原価を回収できるのでしょうか。いわゆる損益分岐点ですが、それは下記のようにすると可視化できます。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/595/1*ED240pbFap7I-whPbkeukA.png" /><figcaption>本体価格を製造原単価より低く設定した場合、2ロット目に入ってようやく元が取れます</figcaption></figure><h3>各費用の見積もり</h3><p><strong>①基板にかかる費用</strong><br>今回、基板製造/実装費用と部品費用は協力会社よりプロトタイプが上がってきた段階で見積れました。<br>今回は P版.com という基板製造・実装を行なっているサービスを利用し、web上で見積もりを取りました。</p><p><a href="https://www.p-ban.com/">プリント基板の製造・設計・実装ならプリント基板ネット通販P板.com</a></p><p><strong>②外装にかかる費用</strong><br>外装に関しては大きく分けて3つの手段があります</p><ul><li>3Dプリンター</li><li>プラスチック射出成形金型</li><li>市販品のケース</li></ul><p>プロトタイプでは3Dプリンターで作成しましたが、量産時はそれだとコスパが悪いので金型を作るか、既製品を使う必要があります。<br>500台程度では金型はコスパが悪いので既製品を使うことを想定しました。</p><p><a href="https://www.takachi-el.co.jp/">タカチ電機工業 - 産業用・工業用のケース・ボックス・ラック・筐体の製造メーカー</a></p><h3>法令の確認</h3><p>利用する部品や技術が決まったら、それが国内で販売可能かどうか確認する必要があります（確認しながら部品・技術選定するというのが正しい手順ですが）。<br>具体的には</p><ul><li>技術基準適合証明、技術基準適合認定（いわゆる技適）</li><li>電波法</li><li>特許/著作権</li></ul><p>などです。<br>これらに抵触してしまうと量産しても販売できません。<br>量産フェーズに入った後で気づいた場合大きく手戻りするので、徹底的に確認しましょう。<br>弊社では法務担当を通し弁護士様に確認していただきました。<br>今回の製品では技適や電波法に関しては認証済みのモジュールをそのまま使っていたので問題ありませんでした。<br>しかし Bluetooth 技術を使っているので <strong>BluetoothSIG</strong> という団体に製品登録をする必要がありました。<br>登録料が必要なのですがなんと<strong>その額 $8,000 😱</strong><br>Typoではありません。<strong>約80万円</strong>です。</p><p>このように法規制や特許利用額など、プロジェクト自体の続行可否を左右する要素が詰め込まれています。<br>なのでプロトタイピングの時点からこれらを考慮する必要があります。<br>実はこれに気づいたのは<strong>量産直前</strong>で、問題の解決に中々苦労しました……。</p><p>関連: <a href="https://medium.com/nextbeat-engineering/c4042ffa001">ESP32を用いたBLE製品をBluetoothSIGに登録した話</a></p><h3>量産に入る前に……</h3><p>ここまで来ると現実的に量産して販売可能かどうか、費用面・スケジュール面・技術面で判断が付くようになるかと思います。<br>そうなるといよいよ量産です。<br>が、何事もぶっつけ本番では失敗します。やり直しの難しいハードウェアなら尚更ですので、やはりまだまだやることがあります。</p><p>これらも書いていきたいのですが少し長くなりそうなので、今回はプロトタイピング編として一旦ここまでとさせていただきます。<br>最後までお読みいただきありがとうございました。</p><p><a href="https://www.nextbeat.co.jp/">株式会社ネクストビート</a>では<br><strong>「人口減少社会において必要とされるインターネット事業を創造し、ニッポンを元気にする。」<br></strong>を理念に掲げ一緒に働く仲間を募集しております。<br>ソフトウェアに限らず、必要であればIoT製品も作ってしまう。<br>無ければ作ればいい。<br>そんな永遠ベンチャーな会社です！</p><p><a href="https://hrmos.co/pages/nextbeat/jobs/0301163">SaaS／エンジニアオープンポジション | 株式会社ネクストビート</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7ebba8c57ee7" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nextbeat-engineering/web%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E9%96%8B%E7%99%BA%E4%BC%9A%E7%A4%BE%E3%81%A7iot%E8%A3%BD%E5%93%81%E3%82%92%E9%96%8B%E7%99%BA%E3%81%97%E3%81%9F%E8%A9%B1-%E3%83%97%E3%83%AD%E3%83%88%E3%82%BF%E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0%E7%B7%A8-7ebba8c57ee7">Webサービス開発会社でIoT製品を開発した話（プロトタイピング編）</a> was originally published in <a href="https://medium.com/nextbeat-engineering">nextbeat-engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ESP32を用いたBLE製品をBluetoothSIGに登録した話]]></title>
            <link>https://medium.com/nextbeat-engineering/esp32%E3%82%92%E7%94%A8%E3%81%84%E3%81%9Fble%E8%A3%BD%E5%93%81%E3%82%92bluetoothsig%E3%81%AB%E7%99%BB%E9%8C%B2%E3%81%97%E3%81%9F%E8%A9%B1-c4042ffa001?source=rss-430db322893f------2</link>
            <guid isPermaLink="false">https://medium.com/p/c4042ffa001</guid>
            <category><![CDATA[bluetooth]]></category>
            <category><![CDATA[engineer]]></category>
            <category><![CDATA[iot]]></category>
            <dc:creator><![CDATA[Katsuya Kubo]]></dc:creator>
            <pubDate>Wed, 07 Apr 2021 02:06:50 GMT</pubDate>
            <atom:updated>2021-04-07T02:28:37.593Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7700dWQLQIreW5jSXbekTg.png" /></figure><p>こんにちは。エンジニアの久保です。<br>普段は<a href="https://kidsna-connect.com/site/">KIDSNAコネクト</a>というサービスを開発しています。<br><a href="https://medium.com/nextbeat-engineering/%E7%AA%81%E6%92%83-%E9%9A%A3%E3%81%AE%E3%82%AD%E3%83%BC%E3%83%9C%E3%83%BC%E3%83%89-nextbeat-2019-953708e5095b">自作キーボード制作</a>が趣味なのですが、それが高じて今回IoT製品の開発をマネジメントすることになり、色々と躓いた部分がありました。<br>特に今回はBluetoothSIGへの製品登録について書きたいと思います。</p><h3>ESP-WROOM-32</h3><p><a href="https://www.espressif.com/en/products/modules/esp32">ESP32 Wi-Fi &amp; Bluetooth Modules I Espressif</a></p><p>今回用いたのは、皆さんご存知の通称ESP32。<br>Espressif 社から提供されている、Wi-FiやBLEといったモジュールが搭載された便利マイコンです。<br>プロトタイピングにはもってこいです。<br>今回はそれを製品版にも使うことになりました。</p><h3>BluetoothSIG</h3><p><a href="https://www.bluetooth.com/">Bluetooth Technology Website | The official website of Bluetooth technology.</a></p><p>Bluetooth技術を用いた製品を製造・販売するには<a href="https://www.bluetooth.com/">BluetoothSIG</a>に製品を登録する必要があります。<br>登録と言ってもその手段はいくつかあり、<br>①認証済み製品を指定して利用登録をするパターン<br>②認証済み製品を組み合わせて指定し、利用登録をするパターン<br>③適合試験を受けるパターン<br>があります。</p><p>最も簡単なのは①です。<br>認証済み製品を用いていなかったり、改変を加えた場合は③で対応する必要があり、費用も増えます。<br>①か②であれば必要な費用は登録料<strong>$8,000</strong>のみです（といっても高額ですが）。</p><p>今回は当初①で済むと思っていたのですが、最終的には②の方法で登録しました。<br>その経緯を後述します。</p><h3>結論</h3><p>先に結論だけ述べますと、今回は</p><ul><li>Controller Subsystem: <a href="https://launchstudio.bluetooth.com/ListingDetails/27858">ESP-WROOM-32</a></li><li>Host Subsystem: <a href="https://launchstudio.bluetooth.com/ListingDetails/97856">ESP NimBLE Host</a></li></ul><p>で登録しました。<br>実際に登録されたページはこちら: <a href="https://launchstudio.bluetooth.com/ListingDetails/124255"><strong>KIDSNA Connect IC Card Reader</strong></a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*i4NNrYkpFX-JW0oYKEnMGg.png" /></figure><p>当初、Host Subsystemには <a href="https://launchstudio.bluetooth.com/ListingDetails/66288">BR/EDR/LE Dual-mode Host Stack</a> を用いようとしていたのですが、これが最新のBLE仕様では非推奨とのことで、<strong>「NimBLE」</strong>の方に量産直前に切り替えることになりました。<br>これからはESP32のBLE周りはNimBLEが標準になっていくそうなので、NimBLEを選んだ方が無難です。ちなみにHost Subsystem等、構成を後から変えようとすると別途登録料が$8,000必要です。</p><p>今回は回避方法があり、事なきを得ましたが、Bluetooth製品を開発するとなった段階でどのモジュールを使うのか、それがどのような手段で登録可能なのか、は調査必須であると学びました。</p><h3>Product Typeについて</h3><p>今回SIGへの登録に躓いた要因として<strong>「Product Type」</strong>の認識漏れがありました。<br>BluetoothSIG認証済みのモジュールには、それぞれProduct Typeという種別が付与されて登録されます。<br>この種別によって利用登録の仕方が変わるので利用するモジュールのProdcut Typeは確認必須です。<br>認証済みモジュールは下記から型番などで検索できます。<br><a href="https://launchstudio.bluetooth.com/listings/search">https://launchstudio.bluetooth.com/listings/search</a></p><p>詳細は省きますが、Product Typeが <strong>「End Product」</strong> であれば、前述した①の方法で対応可能です。<br>ですが ESP-WROOM-32 は<strong> 「Controller Subsystem」</strong> で登録されていました。<br>Controller Subsystem は <strong>「Host Subsystem」</strong> と組み合わせなければ登録できません。<br>Controller Subsystem はざっくり言ってしまえばハードウェア部分を指し、Host Subsystem はアプリケーションを動かすソフトウェア部分を指します。<br>つまりソフトウェア部分でどのモジュールを使うのか決めてあげる必要があった、ということです。</p><h3>BluetoothSIGへのアカウント登録（準備）</h3><p>今回はBluetoothSIGへの登録も初めてでしたのでその手順も載せます。<br>本筋からは逸れますので、既にご存知の方は読み飛ばしてください。<br>注意点は下記の通りです。</p><ol><li>BluetoothSIGのアカウントは基本的に１法人につき１アカウント</li><li>登録者は会社役員、もしくはその役員から委任された人物である必要がある</li><li>登記簿謄本（履歴事項全部証明書）が必要</li></ol><p>1に関しては登録に用いるドメイン部で判断しているらしく、同じドメインのアドレスは基本的に１つしか登録できません。<br>なのでgmail.com等は利用不可です。会社ドメインのアドレスが必要です。<br>さらに1社1アカウントの制限があるので、社員の個人アドレスで登録すると何かと不便です。<br>今回私は社内で共有できるメーリングリストを新たに作成し、そのアドレスで登録するようにしました。<br>これで関係者はSIGからの連絡を受け取れますし、仮に私が担当を外れても何ら問題ありません。</p><p>2に関しては、社長に承認を得て社長名義で登録を行いました。<br>役員から委任された人物としての登録でも可能なのですが、その証明にも別途書類が必要です。<br>ですのでやはり役員に承認を得てその名義で登録するというのが一番シンプルかと思います。</p><h3>BluetoothSIGへのアカウント登録（作業）</h3><p>メンバーのタイプは「アダプター」と「アソシエイト」があります。<br>継続的に製品を開発していくならアソシエイトの方が割安らしいですが、弊社ではその予定はなかったので今回はアダプターメンバーとして登録しました。<br><a href="https://www.bluetooth.com/ja-jp/develop-with-bluetooth/join/">https://www.bluetooth.com/ja-jp/develop-with-bluetooth/join/</a></p><p>1. 国を選んでメールアドレスを入力</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IQqyF6kpTxceTHYyWIsiHw.png" /></figure><p>2. 企業情報を登録（登記簿謄本をアップロード）</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qBLrXpqKjeBkifdOvX-HeQ.png" /></figure><p>3. 登録者情報を入力</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MiOlnZWaJkTWJ0i4Lwe-Rg.png" /></figure><p>4. 住所を入力</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7uoelQ-e-mCDwchQ017ynw.png" /></figure><p>5. 最終確認して確定</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UszZ2A_34DCKZaVBM1hMEg.png" /></figure><p>これでしばらくすると登録が完了します。<br>すべて英語ですがフォーム自体は至ってシンプルです。<br>臆せず登録しましょう。</p><h3>製品の登録（準備）</h3><p>製品の登録に必要な情報は下記の通りです。</p><ul><li>Product Full Name: 製品名</li><li>Model Number： 型番</li><li>Description: 製品説明</li><li>Listing Date: 公開日（登録作業の翌日以降〜3ヵ月の範囲）</li><li>Publish Date: 製品情報の公開日（Listing Date以降の日付、かつ登録作業お翌日以降で3ヵ月以内)</li><li>Category: 製品カテゴリ（選択式）</li></ul><p>弊社が登録した内容は先程も記載した登録ページと照らし合わせれば確認いただけます。<br><a href="https://launchstudio.bluetooth.com/ListingDetails/124255">KIDSNA Connect IC Card Reader</a></p><h3>製品の登録（作業）</h3><p>1. <a href="https://www.bluetooth.com/">https://www.bluetooth.com/</a> にアクセス<br>2. 左上のMyBlueを押し、サイドメニューのMyListingsを選択</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*khErY5bZFtrBuOZiWuRb2g.png" /></figure><p>3. Getting Started タブを選択し、No Required Testingの方のボタンを押します</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JeAvHmzNJ6DHA6qqoPNzNA.png" /></figure><p>4. プロジェクト名を入れ、Controller Subsystem(97618) と Host Subsystem(131934)のQDIDを入力し選択します</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Rce10g8NUqIIqA3vQO85rw.png" /></figure><p>5. Add Productボタンを押下して用意した情報を入力します</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*U96XGezOsoYmWT2p0TwkkQ.png" /></figure><p>6. Pay Declaration Feeボタンから登録料を支払いましょう</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5Gc_TNKnQGTkBkTBOg2OtQ.png" /></figure><p>7. 内容を最終確認して完了です</p><h3>最後に</h3><p>以上の作業を行えば数日で登録完了のメールが送られてきます。<br>これでこの製品にBluetoothマークを利用したり、そもそもBluetooth製品として製造・販売することが許諾されます。<br>サイトは全部英語ですし、手順も煩雑で費用も高額と、初心者には優しくない設計でしたが、様々な方の助けをお借りし、なんとか登録することができました。<br>近々SIG登録に至った製品自体の開発経緯も記事にしたいと考えています。<br>最後までお読みいただきありがとうございました。</p><p><a href="https://www.nextbeat.co.jp/">株式会社ネクストビート</a>では<br><strong>「人口減少社会において必要とされるインターネット事業を創造し、ニッポンを元気にする。」<br></strong>を理念に掲げ一緒に働く仲間を募集しております。<br>ソフトウェアに限らず、必要であればIoT製品も作ってしまう。<br>そんな永遠ベンチャーな会社です！</p><p><a href="https://hrmos.co/pages/nextbeat/jobs/0301163">SaaS／エンジニアオープンポジション | 株式会社ネクストビート</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c4042ffa001" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nextbeat-engineering/esp32%E3%82%92%E7%94%A8%E3%81%84%E3%81%9Fble%E8%A3%BD%E5%93%81%E3%82%92bluetoothsig%E3%81%AB%E7%99%BB%E9%8C%B2%E3%81%97%E3%81%9F%E8%A9%B1-c4042ffa001">ESP32を用いたBLE製品をBluetoothSIGに登録した話</a> was originally published in <a href="https://medium.com/nextbeat-engineering">nextbeat-engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[突撃！隣のキーボード nextbeat 2019]]></title>
            <link>https://medium.com/nextbeat-engineering/%E7%AA%81%E6%92%83-%E9%9A%A3%E3%81%AE%E3%82%AD%E3%83%BC%E3%83%9C%E3%83%BC%E3%83%89-nextbeat-2019-953708e5095b?source=rss-430db322893f------2</link>
            <guid isPermaLink="false">https://medium.com/p/953708e5095b</guid>
            <dc:creator><![CDATA[Katsuya Kubo]]></dc:creator>
            <pubDate>Fri, 27 Dec 2019 06:59:50 GMT</pubDate>
            <atom:updated>2019-12-27T07:55:23.620Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*D_TntJheH0IGk7li6GVKZg.jpeg" /></figure><p>こんにちは。<strong>nextbeat</strong>のDevelopment Division に所属している <a href="https://twitter.com/katzkb"><strong>久保</strong></a> です。</p><p>本日は弊社のキーボード事情について書かせて頂きます。</p><p><strong>※「突撃！隣のキーボード」について</strong><br>自社のキーボード事情についてまとめた記事によく使われるタイトルで、様々な企業様が毎年投稿されています。</p><p><a href="https://www.google.com/search?sxsrf=ACYBGNR43jTq45sxz8k04e_VJyN_UqQahg%3A1575949409655&amp;ei=YRTvXfzQJ4WHr7wPh8SaoAs&amp;q=%E7%AA%81%E6%92%83%EF%BC%81%E9%9A%A3%E3%81%AE%E3%82%AD%E3%83%BC%E3%83%9C%E3%83%BC%E3%83%89&amp;oq=%E7%AA%81%E6%92%83%EF%BC%81%E9%9A%A3%E3%81%AE%E3%82%AD%E3%83%BC%E3%83%9C%E3%83%BC%E3%83%89&amp;gs_l=psy-ab.3..0i4i37.180825877.180827478..180828390...0.0..0.197.623.2j3......0....1..gws-wiz.......0j35i39.nXoApaOsNvM&amp;ved=0ahUKEwi8hfyFlarmAhWFw4sBHQeiBrQQ4dUDCAs&amp;uact=5"><strong>突撃！隣のキーボード — Google 検索</strong></a></p><p>ここ数年で自作キーボードなるものが流行りだし、割れたり光ったり多様化が進んでいるキーボード界隈ですが、弊社のキーボード事情はどうなっているのか。<br>それでは紹介してまいります。</p><h3>キーボード種別の割合</h3><p>はじめに弊社の開発グループではどのようなキーボードが多く使われているのかグラフにしてみました。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*h3wpXb_ADqwg1hn5Vy_uXQ.png" /></figure><p>なんと同率１位で<strong>ビルトイン(MacBookProのキーボード)</strong>と<strong>自作</strong>が並びました！<br><em>※自作は『現在制作中。完成したら業務で使う』という方も含んでいます。<br></em>全体的にみると7割以上の方が何かしら外付けキーボードを利用している、という結果になりました。<br>外付けキーボードを使用しているメンバーからは</p><ul><li>MacBookの位置が自由になるのでディスプレイとかぶらなくなる(40inchディスプレイユーザー)</li><li>MacBookをスタンドに乗せて、ディスプレイとして見やすい位置に置ける</li><li>打鍵感にはこだわりたいので、お店で選びに選んで購入した</li></ul><p>という声が上がっていました。</p><h3>ビルトインキーボード</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0Fx0d8WuP1qEEr9nLcoNnw.jpeg" /></figure><p>ビルトインユーザーの方からは</p><ul><li>Macひとつで済むし、コンパクト</li><li>家と職場で環境を揃えたい</li><li>モノを増やしたくない</li></ul><p>などの意見を頂きました。<br>確かに<strong>いつでもどこでも同じ環境で開発でき、しかもコンパクト</strong>というのはビルトインにしかない利点ですね！</p><h3>自作キーボード</h3><p>次に多かったのは自作キーボード。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*t__AxNctJOP5WKaww3HDBQ.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7VwHsHXnE_h7wxaDePO5Qg.jpeg" /><figcaption>左：私が所属しているチームはいつのまにか全員自作キーボードユーザーに / 右：プロダクトのマスコットキャラをあしらったキーキャップ</figcaption></figure><p>少々割合の多さに驚いているのですが、種別でいうと</p><ul><li>Mint60</li><li>Lily58 pro</li><li>Corne Cherry</li><li>Silver Bullet</li></ul><p>の4種類でした。</p><p>自作キーボードユーザーからは</p><ul><li>肩こり対策として分割キーボードを使ってみたかった</li><li>自作キーボードをやってみたかった</li><li>光らせたい</li><li>程よいキーの数、サイズ感</li></ul><p>などのご意見を頂きました。また女性エンジニアからは『<strong>手の大きさ的に馴染むものが選べる</strong>ので、個人的には女性ほど自作キーボード推奨したい』という意見も頂きました。<br>私はCorne Cherryを愛用しているのですが、男性でもやはり自分の手のサイズ感に合ったキーボードというのは使いやすいなと感じております。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KrJP86Fw2nuKJne0K335nQ.jpeg" /><figcaption>クリエイティブオーナーのデスク。”分割”という特徴を最大限に活かした配置。</figcaption></figure><p>ちなみに最近はオフィスでも自作キーボードを作っています。</p><h3>Katz on Twitter</h3><p>ついに社内でもキーボード作りを始めてしまった</p><p>次に割合が多かったのは<strong>メカニカルキーボード(HHKB以外)</strong>です。</p><h3>メカニカルキーボード</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LzsFjna7mCf4TJjr3tdjHg.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9coWIawXhEo-NMt5vl2heg.jpeg" /><figcaption>左：打鍵感が心地よいと評判の <strong>ProgresTouch</strong> <strong>RETRO</strong>/ 右：キーの反応位置まで調節可能な <strong>RealForce for mac</strong></figcaption></figure><p>こちらも種類は様々で</p><ul><li>Majestouch 2</li><li>ProgresTouch RETRO</li><li>Realforce for mac</li><li>Das Keyboard</li><li>HAVIT HV-KB390L (勝利キーボード)</li></ul><p>などがありました。</p><p>中でも弊社CTOの衣笠は<strong>15年前</strong>からFilcoの <strong>Majestouch </strong>を愛用しているらしく、毎年同じものを購入しているとのこと(キートップの印字が無くなってしまうので)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-fW_-vasyfIbsVxlhBGTuQ.jpeg" /><figcaption>Majestouch2 / 隣のkensingtonからも開発環境へのこだわりが感じられます</figcaption></figure><p>また弊社内において<strong>勝利キーボード</strong>と呼ばれている少々変わった名前のキーボードを使っている方もおりました。<br>いわゆる日本語訳の誤訳結果なのですが名前以外もいろいろ誤訳がありSlackでも盛り上がっていました。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/643/1*Hbn9ZC0nItIZQXIDo8Jxgg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/643/1*xwGFSHA-kOp3WSvUme9kSQ.png" /><figcaption>勝利キーボード誕生の瞬間</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/644/1*2RqksCBJnGPDi9fbBngeZQ.png" /><figcaption>購入画面に至ってはもはや理解不能</figcaption></figure><p>怖気づくことなく購入を決意した彼には賛辞を送りたいです。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VS7i6uGIrUSOy6nWDqxM7w.jpeg" /><figcaption>こちらが<strong>勝利キーボード</strong>こと<strong>HAVIT HV-KB390L</strong>。パームレストの高さにもこだわり、フラットなキーピッチになるようにしているとのこと</figcaption></figure><h3>HHKB</h3><p>そして次に多かったのはHHKB！<br>あえてメカニカルキーボードと分けたのはその割合の多さゆえです。<br>なんと<strong>HHKBだけで18.5％!!</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*G4AJvvbXVHTUUx6QZsadTg.jpeg" /><figcaption>皆さん有線無線・印字有無など様々なタイプをお持ちでしたが色は黒一色でした</figcaption></figure><p>HHKBユーザの方々からは</p><ul><li>打鍵感が心地よい</li><li>音がイイ！！</li><li>Macに乗せられる(尊師スタイル)ほどよいサイズ感</li></ul><p>とのお声が上がりました。私もその打鍵感に魅せられ愛用していた時期がありました。最新作が発表されたこともあり、HHKBの輪はまだまだ広がりそうです。</p><h4>メンブレン</h4><p>最後にご紹介するのは外付けのメンブレンキーボードです。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cRpODoDHbssHI2d3VogHAw.jpeg" /><figcaption>京都オフィスのエンジニアが愛用している<strong>シザースイッチ</strong>キーボード</figcaption></figure><p>メンブレンユーザーの方々からは</p><ul><li>Macとの親和性</li><li>かっこいいデザインのものが多い</li></ul><p>とのご意見を頂きました。<br>たしかにMacBookProのメンブレンキーボードとの同じ使用感であった方が違和感なく使い分けられそうです！</p><h3>その他のデータ</h3><p>さて、ここからは他の統計データからも弊社のキーボード事情を読み解いてみます。</p><h4>キー配列</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*fYhfXTThh-44MuM6Ub5ysw.png" /><figcaption>自作等、特殊なものはその他に分類されています</figcaption></figure><p>まずは<strong>キー配列</strong>です。<br>こちらはUSが過半数を占めていますね。<br>ですがあえてUSを選んだ、というよりはJIS/USどちらでも対応できる、といった方も多く、その場その場の環境に合わせているようです。<br>ですがやはり記号の位置などが違うのでそれぞれこだわりが強い方もいらっしゃいました。</p><h4>軸の種類</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*8_9VwSfwrhp6FI-QfJY0cA.png" /></figure><p>次に<strong>軸の種類</strong>です。<br>これはやはりビルトインであるMacBookのバタフライキーボードが最大勢力でした。次いで自作勢のタクタイル、HHKBの静電容量方式が並んでいます。ちなみに先述の勝利キーボードは『ロープロファイルのクリッキー』という珍しいタイプでした。</p><h4>キーコンフィグ</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*td5rP5lqjFJS-LHcahwC-A.png" /></figure><p>また、キーボードにこだわる人はハードウェアやソフトウェアでの設定(<strong>キーコンフィグ</strong>)もこだわっているのでは？<br>と思い、何かしら制御している人とそうでない人で分類してみました。</p><p>約半数が何かしらの設定をしているということで詳しく聞いてみると、</p><ul><li>特定のアプリを呼び出すホットキーを設定している</li><li>CapsLockをEcsやCtrlにしている</li><li>Ctrlを押しながらhjklを入力することでカーソルを上下左右に移動するよう設定している</li></ul><p>などなど皆さんの独自のこだわりが垣間見えました。<br>私もQMKのマウスキー機能などを愛用しています。</p><h4>以前使用していたキーボード</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*vYEI_3MsaSp0It3-_z2whw.png" /></figure><p>さらに<strong>以前使用していたキーボード</strong>を聞いてみたところ、ほとんどの方がビルトインのキーボードを使用していたことがわかりました。<br>ここ1年で外付けキーボードを購入したり、自作キーボードを作ってみた、という方が多いという結果になりました。</p><p>こうなってくると<strong>1年後の弊社のキーボード事情</strong>はどうなっているのだろう🤔と気になってしまい、『次に使ってみたいキーボードはありますか？』と聞いてみました。</p><h4>次に使ってみたいキーボード</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*uE2pPOks-cO5EEE2RNVMJg.png" /></figure><p><strong>自作に興味がある方多め！</strong><br>皆さん何かしら現状のキーボードに小さな不満はあるらしく、それを解決する策のひとつとして自作を候補に入れている方がいらっしゃいました。</p><p>そしてやはり<strong>HHKBが人気！！</strong><br>周りで使っている方が多く、興味がそそられる方が多いようです。<br>私も新卒時代先輩に憧れて初めて購入した外付けキーボードがHHKBでした。不動の人気は衰えるどころか新作のおかげで勢いを増しているようです。</p><p>興味深かったのが『自作はハードルが高いけど、何かしら<strong>セパレートキーボード</strong>は使ってみたい』という声が少数ながらありました。<br>既製品でももちろん分離型キーボードはあるので試して頂きたいですね！</p><p>以上、弊社のキーボード事情に関する今年の調査結果でした！<br>思った以上に自作率が高かったり、HHKBの厚い層があったり、個人的にはとても興味深い内容でした。<br>1年後にこの割合がどうなっているか、またご報告できればと存じます。</p><p>この記事はCorne Cherryで書きました。</p><h3><strong>お知らせ</strong></h3><p>ネクストビートでは毎週木曜日に社内外の交流の場「Passenger’s Bar」を開放しています。もしこの記事を観て「ネクストビート気になる」「キーボード談義したい」「話聞いてみたい」という方がいらっしゃったら、気軽に遊びに来てください。</p><p>▼過去開催時の様子</p><p><a href="https://medium.com/nextbeat-engineering/%E3%83%90%E3%83%BC%E3%83%86%E3%83%B3%E3%83%80%E3%83%BC%E3%81%8C%E4%BD%9C%E3%82%8B%E3%81%8A%E9%85%92%E9%A3%B2%E3%81%BF%E3%81%AA%E3%81%8C%E3%82%89%E6%B0%97%E8%BB%BD%E3%81%AB%E7%A4%BE%E5%86%85%E5%A4%96%E4%BA%A4%E6%B5%81%E3%81%8C%E3%81%A7%E3%81%8D%E3%82%8B-%E5%A4%9C%E6%B4%BB-%E3%81%AE%E9%96%8B%E5%82%AC%E3%83%AC%E3%83%9D%E3%83%BC%E3%83%88-%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2-%E3%83%87%E3%82%B6%E3%82%A4%E3%83%8A%E3%83%BC%E7%B7%A8-c512bb907aa5">バーテンダーが作るお酒飲みながら気軽に社内外交流ができる「夜活」の開催レポート（エンジニア・デザイナー編）</a></p><p>▼申込フォーム</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fdocs.google.com%2Fforms%2Fd%2Fe%2F1FAIpQLSeFk04pU9F0gsrGtJvzu8_q83WhgRWq4NWv3vXYH4FKGGkK9g%2Fviewform%3Fembedded%3Dtrue&amp;url=https%3A%2F%2Fdocs.google.com%2Fforms%2Fd%2Fe%2F1FAIpQLSeFk04pU9F0gsrGtJvzu8_q83WhgRWq4NWv3vXYH4FKGGkK9g%2Fviewform%3Fusp%3Dsend_form&amp;image=https%3A%2F%2Flh3.googleusercontent.com%2Fs1lEfrBZ7y5uDoib1idp_uK4I1WwrNUj-6pO1isQOINTfEkXTMpJvUPMTXcTEzXRNew%3Dw1200-h630-p&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=google" width="760" height="675" frameborder="0" scrolling="no"><a href="https://medium.com/media/d74be71f9336fcbe4560062018910c99/href">https://medium.com/media/d74be71f9336fcbe4560062018910c99/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=953708e5095b" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nextbeat-engineering/%E7%AA%81%E6%92%83-%E9%9A%A3%E3%81%AE%E3%82%AD%E3%83%BC%E3%83%9C%E3%83%BC%E3%83%89-nextbeat-2019-953708e5095b">突撃！隣のキーボード nextbeat 2019</a> was originally published in <a href="https://medium.com/nextbeat-engineering">nextbeat-engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Scalaでコレクションを前後処理しつつ走査する]]></title>
            <link>https://medium.com/nextbeat-engineering/scala%E3%81%A7%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E5%89%8D%E5%BE%8C%E5%87%A6%E7%90%86%E3%81%97%E3%81%A4%E3%81%A4%E8%B5%B0%E6%9F%BB%E3%81%99%E3%82%8B-401a4edb6bab?source=rss-430db322893f------2</link>
            <guid isPermaLink="false">https://medium.com/p/401a4edb6bab</guid>
            <category><![CDATA[scala]]></category>
            <category><![CDATA[engineer]]></category>
            <category><![CDATA[functional-programming]]></category>
            <dc:creator><![CDATA[Katsuya Kubo]]></dc:creator>
            <pubDate>Fri, 22 Feb 2019 08:06:31 GMT</pubDate>
            <atom:updated>2019-02-22T08:46:36.643Z</atom:updated>
            <content:encoded><![CDATA[<h4>関数型言語らしく書くには</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/436/1*qLFnX0GGTSdaihfMyyNZHw.png" /></figure><h4>はじめに</h4><p>こんにちは。<strong>nextbeat</strong> 開発グループに所属している <a href="https://twitter.com/katzkb"><strong>久保</strong></a> です。<br><strong>nextbeat</strong> では保育園向け業務支援サービス <a href="https://asp.kidsna.com/lp"><strong>KIDSNAコネクト</strong></a> の開発を担当しています。</p><p>現在 <strong>KIDSNAコネクト</strong> では主に保育園の <strong>経営業務</strong> が楽になるような機能を開発しています。<br>具体的には <strong>労務管理</strong>、<strong>保育料金管理 </strong>等 <strong>バックオフィス系 </strong>の機能強化です。</p><h4><strong>連続するデータを１つずつ前後処理する</strong></h4><p>バックオフィス系の機能はビジネスロジックが複雑になりがちです。<br>手続き型言語ばかりやってきた自分としては、複雑な処理を関数型ライクに書くことに苦労していました。<br>つい先日も <strong>連続するデータの差分を１つずつ取得する </strong>処理で「なんとか綺麗に書けないか」と悩んでいました。<br>その際、諸先輩方にアドバイスを頂きましたので共有しようと思います。</p><p>今回はもっと簡素化した、<br><strong>１年間の月次平均気温データから各月の気温差を取得する処理</strong><br>を例に見ていきたいと思います。</p><h4>その１：再帰とmatch式を使う</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6e6bdb64867a2288ad3c0535ca5f3567/href">https://medium.com/media/6e6bdb64867a2288ad3c0535ca5f3567/href</a></iframe><p><strong>再帰</strong> と <strong>パターンマッチ</strong> を使った関数型言語らしい書き方かと思います。<br>１つ目のポイントとしては <strong>match式</strong> の部分です。</p><pre>case head +: second +: tail =&gt;</pre><p><strong>SeqLike</strong> な値に対してこのようなパターンマッチを行うと、以下のように評価されます。</p><pre>case １つ目の値 +: ２つ目の値 +: それ以降の残りの値 =&gt;</pre><p>今回の場合、一番最初のループでは以下のように評価されます。</p><pre>Seq(4, 5, 11, 17, 19, 22, 28, 28, 22, 19, 14, 7) match {<br>  case 4 +: 5 +: (11, 17, 19, 22, 28, 28, 22, 19, 14, 7) =&gt;<br>    f(Seq.empty :+ abs(4 - 5), 5 +: (11, 17, 19, 22, 28, 28, 22, 19, 14, 7))<br>  ……</pre><p><strong>head</strong> , <strong>second</strong> として取り出し、前後処理しています。<br>そして２つ目のポイントですが再帰関数(f)です。<br><strong>第１引数は気温差のSeq</strong>、<strong>第２引数は残りの気温Seq</strong>を渡しています。<br>これを繰り返すと、<strong>tSeq</strong> の中身が１つになった時点で、match式１つ目のパターンにマッチしなくなります。<br>そして、最終的に今までの結果を集めた <strong>diffSeq</strong> を返すことで気温差Seqを取得しています。</p><p>ちなみに関数fの上にある</p><pre><a href="http://twitter.com/scala">@scala</a>.annotation.tailrec</pre><p>はその下の再帰関数が<strong>末尾再帰に最適化されているかどうか</strong>を、コンパイル時にチェックするためのアノテーションです。<br>末尾再帰として最適化されていないとコンパイラが<strong>コンパイルエラー</strong>として扱ってくれます。</p><p>再帰関数は階層が深くなるとスタックオーバーフローする可能性がありますが、末尾再帰最適になっているとコンパル時にループへ変換されるのでこれを回避する事ができます。なので特殊な理由がない限りアノテーションは付けて末尾再帰にすべきです。</p><h4>その２：前後の値を１セットにしたコレクションを使う</h4><p>個人的には再帰処理の方が書き始め易いのですが、やはり最終的に読みやすいのは <strong>foldLeft</strong> でしょうか。最初は再帰で書いてその後 fold に清書する、というのもアリかもしれません。</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/772a4eacdad7a59398ee367c3912de1e/href">https://medium.com/media/772a4eacdad7a59398ee367c3912de1e/href</a></iframe><p>まず最初に <strong>sliding</strong> を使い、先頭から2つずつ要素を取得したコレクションを生成します。sliding の返り値は <strong>Iterator[Seq[Int]]</strong> です。実際にどんな値が返ってきているか確認するために toListしてみると下記のようになります。</p><pre>tSeq.sliding(2).toList<br>// -&gt; res0: List[Seq[Int]] = List(List(4, 5), List(5, 11), List(11, 17), List(17, 19), List(19, 22), List(22, 28), List(28, 28), List(28, 22), List(22, 19), List(19, 14), List(14, 7))</pre><p>コード上ではSeqのままですが、これを <strong>foldLeft </strong>で回して、気温差を取得していきます。</p><h4>まとめ</h4><p>関数型言語に慣れた方々からすれば大した内容ではなかったかもしれませんが、PHPerだった自分としては目から鱗でした。<br>今回はその１とその２の違いについてまでは踏み込めませんでしたので、今後はそういったパフォーマンス等の違いも調べたいと思っています。</p><h4>告知</h4><p>隔週木曜日に恵比寿のオフィスで夜活（交流会）を実施しています。<br>転職希望ではなくても当社にご興味をお持ちの方は是非遊びに来てください！<br> 綺麗なオフィスで軽食とお酒を楽しみましょう！</p><p><a href="https://www.wantedly.com/companies/nextbeat2/post_articles/137956">ネクストビートの「夜活」を紹介します！ | イベント</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=401a4edb6bab" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nextbeat-engineering/scala%E3%81%A7%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E5%89%8D%E5%BE%8C%E5%87%A6%E7%90%86%E3%81%97%E3%81%A4%E3%81%A4%E8%B5%B0%E6%9F%BB%E3%81%99%E3%82%8B-401a4edb6bab">Scalaでコレクションを前後処理しつつ走査する</a> was originally published in <a href="https://medium.com/nextbeat-engineering">nextbeat-engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>