builderscon tokyo 2018 にクックパッド社員が1名登壇いたします!

こんにちは! 広報部のとくなり餃子大好き( id:tokunarigyozadaisuki )です。

朝晩を中心に少しずつ秋の気配を感じられるようになりましたが、夏日かと思うと台風が2つも。雨の被害がこれ以上、拡がりませんように。みなさんも寒暖の差で体調など崩されないようお気をつけください。

さて、2018年9月6日(木)〜2018年9月8日(土)は「知らなかった、を聞く」をテーマとした技術を愛する全てのギーク達のお祭り、「builderscon tokyo 2018」が開催されますね! 

クックパッドからは、技術部の小野 大器(@taiki45)が登壇させていただきます。セッションの内容は公式ページにて英語で紹介しておりますが、本ブログでは本人のコメント付きで、日本語にてご紹介いたします。 講演は日本語で行いますので、ご興味のある方はぜひご参加ください。

セッション情報

2日目 9月7日(金)12:30〜 イベントホール

Building and operating a service mesh at mid-size company

概要説明 クックパッドでのサービスメッシュの導入事例をお話します。大規模な環境ではなくても、サービスメッシュはマイクロサービス周辺の技術的課題の解決に対して効果があります。Envoy proxy を利用してスモールスタートで構築・導入する事例を基に、サービスメッシュに関係する知見や手法、導入後の効果について紹介する予定です。

コメント

マイクロサービス環境において、通信の失敗のハンドリングや障害発生時の対応等、サービス間の通信で悩んだ人は多いのではないでしょうか。このトークでは、以前とは違うアプローチでサービス間の通信周りの課題に対応するサービスメッシュについてお話します。 本セッションの前にはなんと Envoy proxy の作者である Matt のトークもあるので、そちらと一緒に本セッションも楽しんでもらえるとうれしいです。マイクロサービス間の通信に悩んでいる方やこれから悩みそうな方にぜひ。


登壇時の発表内容等に関してご質問がある方は小野に、他にも数名参加いたしますので、会場でクックパッド社員をお見かけの際には、ぜひお声がけくださいね! 

Cloud Firestoreのrulesをテストする

Komerco事業部エンジニアの岸本(id: sgrksmt)です。今日でちょうど入社1年が経ち、現在Komerco -コメルコ-(以下、Komerco)の開発を担当しています。
入社前はお世話になっていたこの技術ブログに自分が投稿する日がくるとは...。

Komercoは、「料理が楽しくなるマルシェアプリ」というコンセプトの元、料理が楽しくなる器やカトラリー、リネン雑貨等を出品/購入できるサービスで、現在はiOS版のアプリケーションを提供しています。

今年2月のCookpad Tech Conf2018や先日催したCookpad Tech Kitchen#16などでもお伝えしてきていますが、現在KomercoではバックエンドでFirebaseを活用しています。
その中で、最近僕が仕組みづくりとして取り組んでいるCloud Firestoreのセキュリティルールのテストの方法についてご紹介します。

Cloud Firestoreのrules

Cloud Firestore(以下、Firestore)では、各プラットフォームで提供しているSDKやREST API経由でデータを安全に読み書きできるよう、セキュリティルールを記述することができます。
主にFirebase Authenticationでの認証を活用しつつ、どのような条件下でドキュメントを読み取ることができるか、書き込むことができるかを設定し、ユーザーのデータを保護します。
また余談にはなりますが、Realtime DBとCloud Storageにもセキュリティルールがあります。

セキュリティルールをしっかり設定しないと、本来読み取られてはいけないデータが悪意のある第三者に読み取られてしまったり、特定のフィールドを書き換えられてしまうといったことが起こります。
一方で、それを防ぐために全てのルールを閉じて、API経由でのみ読み書きできるようにして堅牢にすることも可能ですが、そうするとCloud Firestoreの次のような利点を享受しにくくなります。

  • リアルタイムでの情報の取得
  • オフラインから復帰したときに、ローカルでの変更を書き込む

なので、実際にアプリケーション開発をしていく場合は全ルールを閉じる運用よりは、ルールを適切に設定して運用していくことが多くなるかと思います。

Firestore rulesの確認が大変

Firestoreのrulesを書いて、期待通りに動作するのかを確かめつつ開発をしていきますが、これが非常に 大変かつ面倒 だったりします。 変更をした後、毎回デプロイして、浸透するまで数分待って、正しく動作するかクライアント側で動作させて確かめるのはかなり非効率ですし、 エラーの内容も「permission-denied」程度のものしかないので何が原因か掴みづらいです。
一応、rulesの構文自体が間違っているかどうかはデプロイ前に検出してくれるので、構文がおかしくなっている場合は気づけますが、typoなんてしてしまった日にはなかなか気づきづらく、デプロイと確認だけで日が暮れてしまうこともありえます。
"もっと効率的にrulesを書いて確認したい..."

そう思いながら今年の前半を過ごしていたのですが、2018年の5月末頃にFirestore rulesの動作を書き込む前に確認できる「シミュレータ」の機能がFirebaseのコンソール上に搭載されました。

rulesのシミュレーター

Firestoreのrulesシミュレーターは、FirestoreのProjectからDatabase→Firestore→ルールとたどり、この部分をクリックすることで利用することができます。

f:id:sgrksmt:20180815180611p:plain

開くと、このような画面になっており、指定したドキュメントのパスに対して、readやwriteのルールを記述されたルールを基にシミュレートして確認することができます。

f:id:sgrksmt:20180815180716p:plain

また、認証情報も指定したテストができるので、認証ありきの条件もシミュレートすることが可能です。
以前私がQiitaに掲載した記事でも操作方法など書いていますのでよければ併せて御覧ください。

シミュレータを用いると以下のメリットがあります。

  • 公開前に条件をシミュレートして確認ができるので、誤った条件のものをデプロイしてしまう心配がない
  • すぐにシミュレートして試せるので、デプロイしてから数分待って、、といった具合に時間をロスすることがない
  • 失敗した場合に、何が原因でどこで失敗しているのか指摘してくれるので、原因がわかりやすい
  • 書き込みに関するシミュレートの場合、実際のDBに書き込みが行われるわけではないので、DBを汚してしまうことがない

ただ、シミュレータでのrulesの動作の確認はコンソール上で簡単に確認ができる反面、以下のデメリットがあります

  • listのオペレーションに関して、queryに関する条件の確認ができない
  • getAfter 関数を用いた条件の確認ができない
  • 2つ以上のドキュメントの書き込みに関するテストができない
  • 数百行になってくるとコンソールがやや重たくなるので編集時にストレスがかかる

今後開発を進めていく上で、継続的にrulesが正しく動作するかを確かめられる環境がないと、新機能の追加や大幅な改修、リファクタリングに耐えられないので、
Firestoreのrulesが正しく動くかどうか のテストを構築していくことにしました。


Firestore rulesのテスト

ここからが本題 になります。大まかな流れとしては

  • 開発環境、本番環境とは別の、 テスト環境 用のFirebase Projectを準備する
  • テスト環境にfirestore.rulesファイルをにデプロイする。
  • テストを書き、実際にテスト環境に対してドキュメントの読み書きを行い、Firestore rulesが期待通りの動作をするかをテストする
  • 手元でテストを実行できる他、CI経由でも継続的にテストが行えるようにする

となります。順に、簡単なテストの例も交えつつ説明していきます。

構成

テストの構成としては、次のようになっています。

f:id:sgrksmt:20180815180802p:plain

普段のアプリケーション開発では開発環境用に必要なものをデプロイしたり、DBの読み書きを行っています。
テストのときは、 テスト用 のFirebase Projectを準備し、そこにrulesをデプロイしています。
そして、テストを実行するときは、向き先をテスト環境のFirebase Projectにし、その環境のFirestoreのDBに対して読み書きを実行し、rulesのテストをします。
また、テストは手元からCLI経由で実行する他に、GitHub上にPullRequestが作成された時や、materブランチにマージされたタイミングで、CI経由でテストを実行するようにしています。

また、この記事では詳しく触れませんが、Komercoでは同様に、CloudFunctionsに関連するテストも同様に、テスト環境のFirebase Project上で行っています。

準備

テストを書くにあたり、jestを利用しているので、npmもしくはyarnにて追加します。
jestは、Facebook社がOSSとして提供している、JavaScriptでユニットテストを行うためのフレームワークです。RSpecのような記述が可能となります。
今回はjestを使った場合でのテストの紹介となりますが、テストのフレームワークは任意のものでも構いません。

また、テストに関連するファイルは、 test/ ディレクトリ以下に配置していきます。 KomercoではCloud Functionsのテストもあるので、 test/rules/ ディレクトリ以下に配置しています。

Firebaseの初期化をする

テストで使うFirebaseの初期化をします。
Cloud Functions等でFirebaseを扱うときは、adminSDKが使えるようadmin権限での初期化をすることが多いのですが、
admin権限でFirebaseを初期化してしまうと、設定したrulesに関係なく管理者権限にて読み書きが可能となってしまうので、Webアプリケーションと同様の初期化を行います。

webhelper.ts を任意のテストディレクトリ以下に配置し、以下のように記述します。

import * as firebase from 'firebase'

const config = {
  apiKey: 'API_KEY',
  authDomain: 'AUTH_DOMAIN',
  databaseURL: 'DATABASE_URL',
  projectId: 'PROJECT_ID',
  storageBucket: 'STORAGE_BUCKET',
  messagingSenderId: 'SERNDER_ID'
}

firebase.initializeApp(config)

const auth = firebase.auth()
const firestore = firebase.firestore()
const settings = { timestampsInSnapshots: true }
firestore.settings(settings)

export { firebase, auth, firestore }

各種configの変数はご自身のテスト環境用のプロジェクトのIDを指定してください。(基本的には.env等から参照することになると思います。)
これら初期化した変数は次のようにテストファイルでimportして使用します。

import * as WebHelper from './helper/webhelper'

const postRef = WebHelper.firestore.collection('post').doc()

モデルを定義する

ドキュメントのモデル定義をします。
今回は例として、Postドキュメントの定義をします。

export enum Path {
  Post = '/posts'
}
export interface Post {
  title: string,
  body: string,
  authorID: string,
  isPublished: boolean
}

次項では、このPostドキュメントのcreateオペレーションに関するテストの例をご紹介します。
(すべてのオペレーションの例を紹介したいのですが、長くなるので割愛します。)

テストを書いていく

前提条件として

  • Firebase Authenticationにて認証されたユーザー
  • 定義したパラメータを全て有している
  • post.authorID が、認証ユーザーのuidと一致する

という条件のもと、書き込みが正常に行われるのを期待するケースと、失敗し、permission-deniedがエラーとして返却されるのを期待するケースを記述します。
(失敗するケースは複数想定されますが、ここでは1つのケースに絞ります。)
posts.test.tsを作成し、次のように記述します。

import * as WebHelper from './helper/webhelper'

enum Path {
  Post = '/posts'
}
interface Post {
  title: string,
  body: string,
  authorID: string,
  isPublished: boolean
}

const makePostDocument = (authorID: string) => {
  return <Post>{
    title: 'test post',
    body: 'test post body',
    authorID: authorID,
    isPublished: true
  }
}

const permissionDeniedError = { code: 'permission-denied' }

describe('post document rules', () => {
  jest.setTimeout(10000)

  let postCollectionRef: WebHelper.firebase.firestore.CollectionReference
  beforeAll(() => {
    postCollectionRef = WebHelper.firestore.collection(Path.Post)
  })

  describe('write', () => {
    describe('create', async () => {
      let authUser: any

      beforeEach(async () => {
        authUser = await WebHelper.auth.signInAnonymously()
      })

      afterEach(async () => {
        await WebHelper.auth.signOut()
      })

      describe('when authorID is equal to auth.uid', () => {
        test('should be succeeded', async () => {
          const post = makePostDocument(authUser.user.uid)
          await expect(postCollectionRef.doc().set(post)).resolves.toBeUndefined()
        })
      })

      describe('when authorID is not equal to auth.uid', () => {
        test('should be failed', async () => {
          expect.assertions(1)
          const post = makePostDocument('xxxxxxxxxxx')
          await expect(postCollectionRef.doc().set(post)).rejects.toMatchObject(permissionDeniedError)
        })
      })
    })
  })
})

(モックデータの作成に関する処理、モデル定義等は別途別のファイルに分割するほうが良いですが、今回は例を示すために同一のファイル内に置いています。) ここまでで実行すると、rulesがまだ書けていない場合はテストが通らないと思います。
次にこのテストを通過するための正しいrulesを記述します。

service cloud.firestore {
  match /databases/{database}/documents {
    function isAuthenticated() {
      return request.auth != null;
    }

    function incomingData() {
      return request.resource.data;
    }

    match /posts/{postID} {
      allow create: if isAuthenticated()
                    && incomingData().keys().hasAll(requiredFields())
                    && incomingData().authorID == request.auth.uid;

      function requiredFields() {
        return ['title', 'body', 'authorID', 'isPublished'];
      }
    }
  }
}

これをテスト環境にデプロイし、再度テストを実行することで、テストが通るようになります。
(注: 紹介のため割愛しているテストケースがありますので、実際にはもう少しテストケースが多く、厚いものになると思います。また、rules自体も、各種フィールドのvalidationを行ったりと複雑になるかと思います)

事前にテストデータを作成する

テストを書いていると、セキュリティルールの制約を受けずにテストデータを作成したり、
CloudFunctionsで生成したり、事前にDBに格納しているのが前提で、読み出すだけのデータを準備したいケースがでてきます。
その場合はadmin権限でFirebaseを初期化したものを別途用意し、rulesの息のかからないところでテストデータを作成します。
adminhelper.ts というファイルを作成し、そこでAdmin SDKの初期化を行います。

import * as admin from 'firebase-admin'

admin.initializeApp({ credential: admin.credential.cert(require('admin sdk jsonのpath')) })
const auth = admin.auth()
const firestore = admin.firestore()
const settings = { timestampsInSnapshots: true }
firestore.settings(settings)

export { admin, auth, firestore }

(注: admin用のservice accountの取扱には注意です。 外部に漏れぬように 対策する必要があります)
参考: サーバーに Firebase Admin SDK を追加する

これにより、事前にPostドキュメントを作成し、そのPostドキュメントが取得可能かどうかのテストが次のように記述できます。

import * as WebHelper from './helper/webhelper'
import * as AdminHelper from './helper/adminhelper'

enum Path {
  Post = '/posts'
}
interface Post {
  title: string,
  body: string,
  authorID: string,
  isPublished: boolean
}

const permissionDeniedError = { code: 'permission-denied' }

const savePostDocument = async (isPublished: boolean) => {
  const postRef = AdminHelper.firestore.collection(Path.Post).doc()
  await postRef.set({
    title: 'test post',
    body: 'test post body',
    authorID: 'xxxxxxxx',
    isPublished: isPublished
  })
  return postRef
}

describe('post document rules', () => {
  jest.setTimeout(10000)

  let postCollectionRef: WebHelper.firebase.firestore.CollectionReference
  beforeAll(() => {
    postCollectionRef = WebHelper.firestore.collection(Path.Post)
  })

  describe('read', () => {
    describe('get', () => {
      afterEach(async () => {
        await WebHelper.auth.signOut()
      })

      describe('when user is authenticated', () => {
        describe('try to get valid document', () => {
          test('should be succeeded', async () => {
            await WebHelper.auth.signInAnonymously()
            const mockPostRef = await savePostDocument(true)
            await expect(postCollectionRef.doc(mockPostRef.id).get()).resolves.toBeDefined()
          })
        })

        describe('try to get invalid document', () => {
          test('should be failed', async () => {
            expect.assertions(1)
            await WebHelper.auth.signInAnonymously()
            const mockPostRef = await savePostDocument(false)
            await expect(postCollectionRef.doc(mockPostRef.id).get()).rejects.toMatchObject(permissionDeniedError)
          })
        })
      })

      describe('when user is not authenticated', () => {
        test('should be failed', async () => {
          expect.assertions(1)
          const mockPostRef = await savePostDocument(true)
          await expect(postCollectionRef.doc(mockPostRef.id).get()).rejects.toMatchObject(permissionDeniedError)
        })
      })
    })
  })

  // ...writeのrule
})

今回追加した、postドキュメントのgetに関するルールを次のように追加することで、上記テストが通るようになります。

service cloud.firestore {
  match /databases/{database}/documents {
    function isAuthenticated() {
      return request.auth != null;
    }

    function incomingData() {
      return request.resource.data;
    }

    function existingData() {
      return resource.data;
    }

    match /posts/{postID} {
      allow get: if isAuthenticated() && existingData().isPublished;
      allow create: if isAuthenticated()
                    && incomingData().keys().hasAll(requiredFields())
                    && incomingData().authorID == request.auth.uid;

      function requiredFields() {
        return ['title', 'body', 'authorID', 'isPublished'];
      }
    }
  }
}

料金面はどうか

実際にテストデータを作って、読み書きを行うので、コスト(料金)がかかるのでは、、と思われるかもしれませんが、
Komercoではrulesのテストに加え、Cloud Functionsのテストも実施しています。Pull Requestが作成される、あるいはmasterブランチが作成されたときにテストを実施していますが、 料金はほとんどそこまでかかっていません。

また、先にも触れましたが、テストを実行する環境と、普段開発している環境を分けているので、開発環境のDBが汚染されたりすることもありません。

Firestoreのルールのテストを書くことでのメリット

テストを書くことによるメリットとしては

  • アプリケーションを手動で動かしたり、シミュレータに頼ることなく(継続的に)rulesが正しく動作するか確かめることができる
  • rulesの変更が容易になる(書き換えた結果、正しくない場合にテストがあることで検知することができる。)

が挙げられます。普段のアプリケーション開発と同様で、テストがあることで、後の変更にも強くなり、rulesを変更することに臆することもなく、また高速に動作検証が行えるようになります。
また、事前にセキュリティを意識したドキュメントの設計がしやすくなり、見通しも良くなるので最近の開発では新機能の追加や改善に伴ってドキュメントの設計をするときは、同時にrulesのテストも記述し開発をしています。

さいごに

Firestoreのrulesに関して、より継続的に確認が行えるためのテストについてご紹介しました。
今回ご紹介したサンプルコードをGitHubにて公開していますので、よければ併せて御覧ください。

今後公式からテストする手段が提供される可能性もありますし、オフライン(ローカル)でのテストが提供される可能性もありますが、
現状Komercoではオンラインテストにて実施して、Firestore rulesの継続的なテストを行えるようにしています。
また、こうした事例に関わらず、Komerco開発チームではFirebaseと向き合って、その上でサービス提供ができるよう努めていきます。


クックパッドでは、Firebaseなど最新技術に興味がある!技術的に挑戦してサービスをより良くしたい!というエンジニアを募集しています。

自作キーボード沼 自由研究ノート

こんにちは!広報部のとくなり餃子大好き( id:tokunarigyozadaisuki )です。

クックパッドのSlackには無数のオープンチャンネルが存在していますが、最近盛り上がりを見せているのが「#keyboards」というチャンネル。先週末コミックマーケットが開催されていたためここ最近はその話でもちきりの様子でしたが、普段から大事な仕事道具であるキーボードにこだわりを持った社員が日々情報交換をしています。興味本位で社員のキーボードをのぞいでみると、ピカピカ光るものから、カチカチッと音がなるもの、アルファベットも数字も書いていないもの……その多彩さにびっくりします。 そこで、クックパッドエンジニアの最近のキーボード事情を調査してみました! 

なお、HHKBやRealforceはクックパッドでは当たり前だったので、紹介は割愛いたします。

クックパッドエンジニアのキーボード

@takai

f:id:tokunarigyozadaisuki:20180814150420j:plain

キーボード概要

名称:Keebio Fourier
スイッチ:Cherry MX 茶軸(45g)
レイアウト :40%スプリットキーボード 

なぜ今のキーボードにしたのか

自作するならスプリットキーボードで、かつコンパクトな感じに仕上げたいと思っていたところ、Keebio の Fourier を見つけて、「ミニマムでストイック、まさに自分のためのキーボードだ」と思いました。

気に入っているポイント

40%キーボードって、ほどよく不便で楽しいじゃないですか。数字キーが無いわけですから、使うにあたって工夫する必要があります。そこを自分好みにカスタマイズして使いこなしてると「俺すごい」という気持ちになれるんです。キーキャップのカラーリングもこだわりのポイントで、Signature Plastics社のGRANIT KEYSETにインスパイアされました。好みのレイアウトだと、キーキャップのセットが売っていなかったので、キーキャップ単位で購入したりと、そこは妥協せずに頑張りました。

@slightair

f:id:tokunarigyozadaisuki:20180814150437j:plain

キーボード概要

名称:Ergo42
スイッチ:Cherry MX 赤軸(45g)
レイアウト:7x4格子配列スプリットキーボード

なぜ今のキーボードにしたのか

少し前にスプリットキーボードに挑戦してみよう、でもキーが減るのはちょっと怖いなと思い、比較的キーが多いViterbi Keyboard(7x5格子配列)を作って使い始めたのですが、想像と違ってキーを余らせてしまっていました。 また同じキー配置で文字を打ちたいので、自宅と会社の間で持ち歩いていたのですが、面倒くさくなってきてもう一台作りたいなと考えていました。 そんなところに一行少ないだけでちょうどよく使えそうなErgo42の開発キットの販売がはじまったので飛びついてしまいました。国産なので注文してすぐ届きました。

気に入っているポイント

はんだ付けは大変ですが、キーキャップやキーマップなど、自分好みにいじることができるのが楽しいですね。特に、基板の底にLEDを配置して光らせるUnderglow(アンダーグロウ)が気に入っています。キーキャップの色は黒を基本に、特殊キーなどを青系にしていて、光の色もそれに合わせています。みんなから声かけられるようになったし、目を引く、やったぜ! という気持ちです。

@eisuke

f:id:tokunarigyozadaisuki:20180814150442j:plain

キーボード概要

名称:TMK Alps64
スイッチ:Alps SKCM SALMON
レイアウト:60%キーボード

なぜ今のキーボードにしたのか

ビンテージキーボードが好きで、以前はIBMマシンで使用されていたバックスプリング式キーボードを使っていました。60%くらいのコンパクトさで自分好みのキータッチのキーボードを作ろうと思い、CHERRY軸を触ってみたのですが自分にはしっくりこなかったので別のものを探していました。その中で、80〜90年代に生産された多くのキーボードに使用されていたAlps軸が気になりました。特にサーモン(ピンク)軸が気になったので、サーモン軸を使っている Apple Extended Keyboard をオークションで購入し、解体してキースイッチを取り出し自作してみました。

気に入っているポイント

タクティカルキーボードの少しだけカチッとくる、この独特で軽めな押し心地がいいですね。Alps軸の古代パーツ感も気に入っています。 

@ragi256

f:id:tokunarigyozadaisuki:20180814150453j:plain

キーボード概要

名称:Helix
スイッチ:Kailhロープロファイル 赤軸(45g)とKailhロープロファイル 茶軸(45g)の併用
レイアウト:6x5格子配列スプリットキーボード

なぜ今のキーボードにしたのか

昔から特殊な形状のキーボードが好きで、Dactylキーボードを作っているブログ記事を見たときに自分でもDactylを作ってみたいと思いました。しかし、はんだ付けもやったことがない自分には難しそうだったので、一旦難易度を下げて簡単なキーボードを作ることにしました。Helixに決めた理由は、以前から一度試してみたかったKailhのロープロファイルスイッチが使えるからです。

気に入っているポイント

親指を使うキーだけ押し心地を変えたかったので、親指周りだけを茶軸にして他は赤軸にしました。キーキャップは、デフォルトの刻印セットに加えて無地の白と黒を見た目で覚えやすいように配置しています。スプリットキーボードなので、椅子の肘掛けに肘先を置いて使えるのもいいですね。

@uzzu

f:id:tokunarigyozadaisuki:20180814171034p:plain

キーボード概要

名称:NIZ keyboard Plum 75 EC Keyboard
スイッチ:静電容量無接点方式(35g)
レイアウト:75%キーボード

なぜ今のキーボードにしたのか

社会人になってから、主にRealforce 86Uを8年くらい使っていました。特に困ってもいなかったのですが気分転換をしたくなり、Realforceの打鍵感が好きだったのでそこはあまり変わらず、それでいて、いじりやすくて不要なキーが無いコンパクトなものを探していて、NIZ keyboardにしました。他の皆さんのキーボードとは違い、組まれた状態で売られているので自作とは言えないですね……。

気に入っているポイント

75%である事(ファンクションキーは欲しいけど十字キーとハードウェアキーと操作キーはいらない)、さらに右側の特殊キーを十字キーに変えられるというのが、まさに自分の需要に合っていて気に入っています。加えて、静電容量無接点スイッチなのにCherry MX互換のキーキャップが採用されているので、他の自作キーボードと同様にキーキャップも変えられますし、バネを付けることでキーの重さが変えられるんです。Realforceの偏荷重モデルの重さを参考にしつつ、気になる所の重さを調整して自己最適化しています。今後、キーキャップは変更していきたいと思っています。

@ayemos

f:id:tokunarigyozadaisuki:20180814150747j:plain

キーボード概要

名称:WASD Keyboard
スイッチ:Cherry MX 青軸(50g)
レイアウト:60%キーボード

なぜ今のキーボードにしたのか

大学生の時HHKBを使っていたのですが、ErgoDoxで組まれた自作キーボードを見たとき、自分好みのキーキャップに変えられるものがほしいと思いました。

気に入っているポイント

深夜に酔った勢いで作ったので気に入ってるとかこだわりとかはないですね。漢字を使ったキーキャップデザインを自作しました。

最後に

いかがでしたでしょうか。以前はHHKBシリーズを使っていて、そこから自作キーボードの門を叩いたという社員が多いように感じました。自分にとって最適なキーボードを求めて細かいところから自作する人、見た目の可愛さを求めて工夫する人と、こだわりは様々。インタビューしていてとても楽しかったです。徐々に詳しくなってきましたよ! Gherkinってキーボードをつくってみたいと思っています。

今回紹介しきれなかった社員の自作キーボードについてはまた次回。お楽しみに!