Redux + TypeScript 周りの調査
最近 Redux に手を伸ばし始めました。JavaScript で経験を積まずにいきなり TypeScript から始めたせいか無駄に試行錯誤したりハマっている気がしますが、めげずに界隈の状況把握から進めていきたいと思います。
今回は主に typesafe-actions と typescript-fsa を 1日 触った感じを軽く書いていきます。特に網羅的には書いていません。
side-effect 系ライブラリ
今回は深くは触れませんが、前提として Redux の action の dispatch に際しての副作用を提供するライブラリの代表的なところを挙げていきます。
この中では redux-thunk と redux-observable を少し使ってみました。redux-observable を利用するには RxJS をある程度触って理解することが必須となります。そして一応書けたコードを見て「これって保守性どうなのだろう」と思わされたので、もう少しこの辺は調査を進めたいところです。
しかし React の勉強をして Redux はそれとはまた別の勉強が必要になり、さらに RxJS の勉強もしないといけないとなると、新しくアサインされた人には辛いプロジェクトになるかもしれないですね(というより保守人員を求めるのであれば Redux ってどうなんでしょうね)。
action 系ライブラリ
action の定義を楽にしたり定型化したりするライブラリですね。FSA(Flax Standard Action) に基づいた設計の action を生成するものをよく見かけます。
- typescript-fsa (star: 290, npm: 9k/week, last update: 4 month)
- README に redux-observable のサンプルがある
- サポートライブラリに redux-thunk と redux-saga と redux-observable を扱うものがある
- typesafe-actions (star: 361, npm: 9k/week, last update: 3 month)
- こちらの方が微妙に勢いがある
- redux-observable のサンプルしか見つからない
という 2 つのライブラリが目につきます。勢いにも知名度にもそれほど違いがないようなので両者で迷いますね。簡単に触った感じの違いをそれぞれ見ていきます。
typescript-fsa の action 定義
const login = actionCreator.async< LoginParams, LoginInfo, ErrorResponse >('LOGIN')
のように定義します。これで login.started/done/failed
という非同期処理用の 3 種類の action が定義されます。ペイロードの type narrowing には isType が用いられ User-Defined Type Guards で実現されているみたいです。
typesafe-actions の action 定義
const login = createAsyncAction( 'LOGIN_REQUEST', 'LOGIN_SUCCESS', 'LOGIN_FAILURE' )<LoginParams, LoginInfo, ErrorResponse>();
のように定義します。これで login.request/success/failure
という非同期処理用の 3 種類の action が定義されます。typescript-fsa との違いとして 3 種類のアクション名を定義しなければならないのですが action の数が増えてくると結構面倒です。あるデータモデルのCRUD操作をしようとすると複数GETも含めて 15 個の名前を定義せねばならず、さらにそれがデータモデル分必要になってくるという。
関数なんかで生成しようとしても、現状の TypeScript には String Literal をマッピングするような操作(プレフィックスをつけた新たな String Literal を定義する等)ができず、関数を通すとただの string 型に落ちてしまうという弊害があります。typesafe-actions の type narrowing は String Literal により実現されているようなので、その恩恵にあずかるには全てのアクション名をきちんと定義する必要がありそうです。
action 定義ライブラリについての所感
パッと見の定義の楽さで言えば typescript-fsa を取りたいところですが、そこからさらに redux-thunk を使うか redux-observable を使うかの選択次第で、実は typesafe-actions が使いやすいかもしれないですし、そうでないかもしれません。
さらにこれがより実際的になってくると fetch 対象のデータモデル数が 7-8 種類程度の分量になってきたり、combineReducers
などの階層化が必要になってきたりして、その場合もちゃんと分かりやすく構造化して実装を整理できるのか、そして型定義はどうなるか、そういったこと次第でもまた選択肢が変わってくるかもしれません。
実際に typescript-fsa と typesafe-actions を行ったり来たりした感想ですが、そこまで大規模な書き換えは必要にならなかったので、現在の実装が詰み気味になり対向が気になってきたら書き換える、という方針も取れなくはないかもしれません(不安)。主に書き換えたポイントとしては以下の通りです。
- import するライブラリ
- 非同期用の action の 3 種類のフィールド名
- type narrowing の方法
ただ typescript-fsa-reducers というライブラリを使って reducer を書いていると行き来がしづらくなるので方針が固まるまでは if
で地道に reducer を書くようにとどめておくのがいいかもしれません。
...という、Redux + TypeScript + Side Effect という交差点では、やはりというか組み合わせが爆発しているのだなという、ため息に似た現状振り返りなのでした。この辺はもう、検証しながら正解スタックを探す覚悟を決めねばやっていけません。
といってもそもそも Redux をそこまでまだ理解していないので、まずはそこから、プラクティスをかき集めていきたいです。
...Apollo が懐かしいです。そう、初めは Apollo が提供している機能を「別に Redux でハンドメイドできるんじゃないか」と思って書き始めたのでした。分厚いレイヤーに嫌気がさして Redux に手を伸ばしたわけですが、自分で書くと想像以上に込み入ったコードになってしまい...フレームワークって偉大ですね、という身も蓋もない着地点が視界の端に移り始めてきたのでした。
React のファイル名から受ける想定利用法
React を触り始めて 1 年くらい経過しましたが、始めの頃はモジュールのファイル名の命名規則がよくわからなくて迷走していたのをふと思い出しました。
例えば「load data」の 2 語からなるファイル名を考えると、以下 の 3 パターンが考えられます。
load-data.js
(kebab-case)loadData.js
(lowerCamelCase)LoadData.js
(UpperCamelCase)
ある程度慣れてくると、ファイル名から作者が想定しているそのモジュールの利用法が読み取れるようになります。 今回はその個人的な感覚のまとめになります。
load-data.js から感じ取る利用法
旧来(いつだろう)の JavaScript 的には一番自然に見えるモジュールファイル名ですね。 「各メンバを個々に読み込んで利用するタイプのモジュール」と受け取れます。
import { func1, func2, const1 } from './load-data' const result1 = func1() + const1 const result2 = func2()
loadData.js から感じ取る利用法
初見で「おや、命名規則が読み取れない...」と思わされたモジュール名です。
これは「default export されている function
のメンバが存在するモジュール」と受け取れます。
import loadData from './loadData' const myData = loadData()
default export しているメンバがあるので、可能ならファイル名と同じ名前で利用してね、というメッセージを感じますね。
LoadData.js から感じ取る利用法
これも上のパターンに似ていて「default export されている class
のメンバが存在するモジュール」と受け取れます。
import LoadData from './LoadData' const loadData = new LoadData()
もしくは React などの JSX であればコンポーネントとして利用できる、という意味も追加されます。 この場合の実装はクラス・関数問わずになりますね。
import LoadData from './LoadData' function MyComponent() { return <LoadData /> }
JSX の場合は new
するクラスか、それともコンポーネントか、どちらを指すモジュールなのかは仕様や実装を見なければ判断できませんね。
Material UI + TypeScript で withStyles (JSS) を型付きで使う
Material-UI を使い始めて数日が経ち、ようやくJSSの思想が飲み込めてきました。 当初の違和感もだんだん薄れ、今では(今のところは)確かに楽だよねという感覚になってきております。
今回は型定義についてです。
import * as React from 'react' import { createStyles, Theme, WithStyles, withStyles } from '@material-ui/core/styles' interface Props extends WithStyles<typeof styles> { // my props... } function MyComponent({ classes }: Props) { return ( <div className={classes.root}> {/* my contents... */} </div> ) } const styles = createStyles({ root: { // my styles... }, }) export default withStyles(styles)(MyComponent)
と定義すると classes.xyz
のように styles
変数の定義に存在しない ClassKey
にアクセスしようとするとエディタが怒ってくれるようになります。
typeof styles
で styles
オブジェクトを型の世界に持ち込んでいるのがみそですね。
関数形式の styles
を利用する場合は以下のようにします。
const styles = ({ spacing }: Theme) => createStyles({ root: { // my styles... }, })
始めは createStyles((theme: Theme) => ({ ... }))
と定義しようと勘違いしておりました。
そして後から気づいたのですが、今回の内容は全部 https://material-ui.com/guides/typescript/ に書いてありましたね。
Material UI + TypeScript で Mixin を定義する
TypeScript + Materia UI でアプリを書いていて、テーマ関係のメソッドの中に createMixins
というものを見つけたので使い方を調べてみました。
デフォルトの mixin といえば gutter
くらいしかなくて、自分でも登録できたら色々使えそうだな...と思い、型定義を追いかけながらコードを試行錯誤したところ、多少不恰好ですが動くものができたのでメモしておきます。
プロジェクトコードは以下のリポジトリにあります。
プロジェクト作成
プロジェクトは create-react-app
の react-scripts-ts
で生成したものをベースにしています。この辺は特に詳細は説明しません。
create-react-app practice-mui-with-jsonschema-form --scripts-version=react-scripts-ts --use-npm
Theme の定義
Theme
を定義しているファイルの全体のコードは以下のようになります。
import { createMuiTheme } from '@material-ui/core/styles' import createMixins from '@material-ui/core/styles/createMixins' import { CSSProperties } from '@material-ui/core/styles/withStyles' // `Mixins` 定義を拡張する declare module '@material-ui/core/styles/createMixins' { interface Mixins { myBackgroundMixin: CSSProperties, // <- custom mixin! } } // `createMixins` で利用するインスタンスを取得する const { breakpoints, spacing } = createMuiTheme() const mixins = createMixins(breakpoints, spacing, { myBackgroundMixin: { backgroundColor: '#ff0000' } }) export default createMuiTheme({ mixins, palette: { divider: '#ff0000' }, })
以下、個々のポイントを説明していきます。
Mixin の定義を拡張する
createMixins
で参照されている interface Mixins
の定義を見ていると何やら意味深なコメントが書いてあります。
export interface Mixins { gutters: (styles?: CSSProperties) => CSSProperties; toolbar: CSSProperties; // ... use interface declaration merging to add custom mixins }
TypeScript の柔軟であり分かりづらい部分ですが、interface
などの定義は後追い拡張(マージ)が行えます。
ここでは以下のように myBackgroundMixin
を追加定義してみました。
declare module '@material-ui/core/styles/createMixins' { interface Mixins { // (=˘ ꒳ ˘=) 展開先を赤く染め上げる血みどろの Mixin... myBackgroundMixin: CSSProperties, } }
Mixin の定義を行う
これにより createMixins
の引数である MixinsOptions
にも拡張が反映され、createMixins
内で myBackgroundMixin
が定義できるようになります
const mixins = createMixins(breakpoints, spacing, { // (=˘ ꒳ ˘=) 血みどろの実装...容赦のないビビッドな赤 myBackgroundMixin: { backgroundColor: '#ff0000' } })
上の定義の拡張を行わないと Object literal may only specify known properties, and 'myBackgroundMixin' does not exist in type 'MixinsOptions'.
というエラーが出ます。
ちょっと曲者な createMixins の引数
createMixins
の引数には Breakpoint
と Spacing
のインスタンスが必要なのですがいちいち用意したくはないですよね。今回は小手先のアイディアとして一度 createMuiTheme()
を実行してデフォルトのインスタンスを取得しています。
const { breakpoints, spacing } = createMuiTheme()
もっといい方法がある気がしますね。
実際に利用してみる
上記の定義を含めた Theme
インスタンスを MuiThemeProvider
経由で提供してやれば、配下のコンポーネントで今回定義した myBackgroundMixin
を利用することができます。
import * as React from 'react' import Button from '@material-ui/core/Button' import { createStyles, Theme, withStyles, WithStyles } from '@material-ui/core/styles' interface Props extends WithStyles<typeof styles> { name: string } function MyButton({ classes, name }: Props) { return ( <Button className={classes.root}> {name} </Button> ) } const styles = ({ mixins, palette }: Theme) => createStyles({ root: { ...mixins.myBackgroundMixin, color: palette.common.white, } }) export default withStyles(styles)(MyButton)
無事、手元では赤いボタンが表示されました。
ここまで、細切れの説明になりましたので、冒頭で紹介したリポジトリで全体を見ていただくのが分かりやすいかもしれません。
VSCode の Atom One Dark テーマをもう少しシックにカスタムする
ひと昔前まで Atom を使っていたのですが builtin terminal を始めとした機能性には抗えず VSCode に移行したのは記憶に新しいところです。
Atom の良かった点は何と言ってもあの、コーディングに最低限必要な情報以外を切り捨てたインタフェースです。ボタンの種類が少ないのもありますが、色使いのノイズの少なさは初めて見た時に目がハートマークになるくらいに気に入ったところです。
まさに私がエディタに求めていたものでした。
VSCode に移行するにあたって最後の後ろ髪になったのがこのカラーテーマで、何しろ VSCode のデフォルトのスタイルではこのように...
青色のステータスバーがチカチカするわ、ボタンが多いわ、バッジが目立つわで Atom のあのデザイン大好き人間からすると直視に耐えない(言い方が悪い)ユーザインタフェースです。機能が多いのはいいのですが、別に常に見えている必要はなく、そもそも使わないコントロールが大半だったりで、もっとこう何というか Zen な感じが欲しいんです。
VSCode の機能性は捨てがたい、しかしこのノイジーなインタフェースは...という葛藤。
で、VSCode には Atom One Dark という Atom のデフォルトのダークテーマをベースにしたテーマパッケージが存在します。それを適用すると以下のようになります。
とてもシックになりましたね。十分と言えば十分なのですが、まだ Atom には存在しない左端のバー(Activity Bar)が存在するのが気になります。どうでもいいと言えばいいのですがやっぱり気になります。ボタンなど少ないに越したことはないのです。しかし消したら消したで不便なので目立たせない方向性で微調整をしていきたいと思います。ついでにバッジの色も目立たないようにしていきます。コーディング領域以外は基本出しゃばらせたくはありませんね。
試行錯誤 & カラーピッキング & コピペの成果で以下のスタイルに行き着きました。
設定はこんな感じで行いました。差分だけ貼っておきます。
"explorer.openEditors.visible": 0, "workbench.iconTheme": "vs-minimal", "workbench.colorCustomizations": { "activityBar.background": "#20252C", "activityBar.foreground": "#626C83", "activityBarBadge.background": "#567", "badge.background": "#20252C", "statusBar.background": "#20252C", "statusBar.noFolderBackground": "#222225", "statusBar.debuggingBackground": "#511f1f", "gitDecoration.ignoredResourceForeground": "#5B6270", "list.inactiveFocusForeground": "#444", "sideBar.background": "#20252C", "sideBarSectionHeader.background": "#20252C", "foreground": "#999", "terminal.foreground": "#999", "terminal.ansiRed": "#E26B73", "terminal.ansiGreen": "#96C475", "terminal.ansiBlue": "#5DADF1", "terminal.ansiCyan": "#51B6C3", "terminal.ansiMagenta": "#C774DF", "terminal.ansiYellow": "#E5C076", }, "workbench.colorTheme": "Atom One Dark",
設定項目は VS Code Theme Color Reference を見ながら行いました。 色以外の設定項目は以下の通りです。
"explorer.openEditors.visible": 0
はファイルツリーと同じペインに表示される「Open Editors」を消しています- ペイン中のウィジェットが単一になるとメニューバーが上端にマージされるようなのでかなり見た目のノイズが減ります
"workbench.iconTheme": "vs-minimal"
はデフォルトのアイコンがカラフルすぎるのでシックなものを採用しました
Atom だとウィンドウの上の白いバーが目立っていましたが、VSCode の場合はその領域もデザインに統合されているので、結果的に Atom 以上にシックなデザインにカスタマイズできたと思います。
ここまでやったらもう Atom に未練は無くなりました。ありがとう、Atom はとても素晴らしいエディタの形を私に見せてくれました。Atom のもたらしてくれたものを思い残すことなく VSCode に引き継ぎ、これからも思う存分 VSCode の機能性を満喫したいと思います。
最後にもう一度 Atom を見ておきます。エディタの表示面だけで言えばひとつの完成形です。
そして今回の VSCode を。
まだちょっと肩のボタンが多いですね。こういうのでも消せたら嬉しい人間はまちがいなくこの世に存在するのです。また時間があったらリファレンスを漁ってみます。
設定ファイルは myhome/settings.json at master · asa-taka/myhome · GitHub で管理していますので、最新版が気になる方は見ていただければと思います。
Wireshark でTLSの通信を読む
TLSの勉強中です。TLSの通信を眺めるにはやっぱりHTTPSだろうということで簡単なHTTPSサーバを立ててトラフィックを眺めてみたいと思います。『プロフェッショナルSSL/TLS』にも書かれていました。「一番良い方法は実際のパケットを見ることです」と。
下準備
Caddy を利用します。HTTPSとHTTP用の設定ファイルを用意します。
$ tree . ├── Caddyfile.http ├── Caddyfile.https ├── cert.pem ├── key.pem └── public └── index.html $ cat Caddyfile.http :8080 root public $ cat Caddyfile.https :8443 tls cert.pem key.pem root public
自己署名証明書は以下のコマンドで生成しました。
$ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes
HTTPS通信を覗いてみる
Wireshark 上であらかじめ lo0
をキャプチャする設定で待ち構えておきます。
フィルタは tcp.port == 8443
に設定しておきます。
$ caddy -conf Caddyfile.https
でサーバを立て
$ curl -v --insecure https://127.0.0.1:8443
で叩きます。
総パケット数は 39 でした。
HTTP通信を覗いてみる
$ caddy -conf Caddyfile.http
でサーバを立て
$ curl -v http://127.0.0.1:8080
で叩きます。
総パケット数は 14 でした。
サンプルプロジェクト
以上のような設定をまとめて GitHub に上げておきました。ご参考になれば幸いです。
『PKIハンドブック』覚え書き
2000年初版の『PKIハンドブック』が中古でお安かったので覚え書きを残していきます。
2015年出版の『プロフェッショナルSSL/TLS』と並行して読んでいるので気になる部分があれば抜き出してみようと思ったのですが...初心者的な視点から見ると特に目立った差異はありませんでした。 今後気をつけて読んでいきたいです。
「SSL Ver3.0 からバージョンアップしたTLSを基に解説する」と本文にありますが、 普段から数年単位で要素技術や仕様がガラリと変わり得るフロントエンドの技術調査をすることが多い身からすると笑ってしまうくらい変わっていませんね。 それだけ学習量対効果に秀でた技術領域なのかな、という感触を受けています。 一度学んでおけば数十年単位で土台となる技術が身につくという。
この辺はHTTPも似たようなものですね。 そしてPKIと言えばSSLで、SSLと言えばHTTPSです。 フロントエンド側の人間でも Docker やデプロイ系の領域に手を伸ばし始めるとこの辺りの知識の有無が利いてきますね。 もしくはマッシュアップ方面の方がHTTPSには馴染みが深かったりするのでしょうか。
とにかく、日常で触れる機会も実は気にしてみると結構多かったりする領域なので、このあたりの仕組みや語彙はフロントエンドのトラブルシュートにも結構な係数で利いてくると思います。 という期待を込めて『PKIハンドブック』を読んでいきたいと思います。
本書を読むにあたり
18年前の本書を読むことについて
2018年現在、2000年初版の本書を読むことに対し、個人的に意義であると感じていることです。
- 『プロフェッショナルSSL/TSL』とは別視点の本を読むことで概要把握の正確性の一助とする
- (フロントエンド技術と比較して)息の長い技術がこの期間でどの程度変化したかに素朴に興味がある
- 素朴に歴史、隔世の感を感じたい
勉強半分、趣味半分といったところです。
どんな技術にも起こり、流行り、廃りの流れがあり、そしてその流れを追いきれなかった現実のシステムは恐らく大量に残されていて、この先自分もどこかでそれに出くわすだろうと。 スナップショットでない、時間幅を持った知識や経験って、もしかしてそういうところで活きてくるんじゃないかなと。 「昔の本を読む」という単なる追体験、真似事ですが、それでも年表を追う以上の何かが得られたらいいなと思っております。
現在の興味領域に照らし合わせ、PKIの仕組みから運用のかじりについて触れられている第8章までを読んでいくつもりです。
SSL/TLS簡易年表
年 | SSL | TLS | 備考 |
---|---|---|---|
1994 | 2.0 | - | Netscape |
1995 | 3.0 | - | Netscape |
1995 | (3.1) | 1.0 | |
2006 | (3.2) | 1.1 | |
2008 | (3.3) | 1.2 | |
2018 | (3.4) | 1.3 |
1. PKI の基礎知識
- 脅威の種類
- 盗聴、なりすまし、改ざん、事後否認
- 事後否認は初耳でした、情報の発信者側から内容が改ざんされていると主張することですね
- 共通鍵暗号方式
- 公開鍵暗号方式(非対称鍵暗号方式)
- RSA(1978)
- その他の暗号方式・署名方式
- 一方向性ハッシュアルゴリズム
- メッセージ認証(完全性保証)
- 以下のMICとHMACは秘密情報と一方向ハッシュを利用したもの*3
- MIC(Message Integrity Checksum)
- HMAC(Keyed-Hashing for Message Authentication)
- デジタル署名(完全性保証)
- 公開鍵暗号方式と一方向性ハッシュを組み合わせた完全性保証の方式
- 相手認証
- 鍵配送
2. 公開鍵基盤
- PKIの要素と役割
3. 公開鍵証明書と失効リスト(CRL)
読み進め次第追記するつもりです... ˘ω˘
*1:つまり共通鍵暗号と公開鍵暗号には「計算量」と「鍵の管理の手間」の間にトレードオフがあると考えられるのでしょうか
*2:「離散対数問題」が利用されている手法が多いので掘り進めてみたいですね。他に ElGamal の方式もその応用例として挙げられていました。
*3:完全性の検証はどう行うのか、秘密情報を共有していること前提か。つまり「メッセージ認証は共通鍵的なものでデジタル署名は公開鍵暗号方式を利用したもの」という認識でいいのでしょうか。
*4:つまり秘密鍵の漏洩はこの前提を覆し、その「本人」に対する相手認証が全て機能しなくなるということになりそう。
*5:この信頼できる第三者の存在を前提とした「信頼モデル」と呼べるものは初見ではどこか情報工学っぽくない感じもしますが、実際の社会の「信頼」という概念も結局はこんなところなのでしょうね。現実世界の反映と考えると次第に慣れていきます。