2021年買って良かったモノ13選
こういうポストをちらほら見かけるので自分もまとめてみます。毎年恒例にできればいいな。
なんやかんやでガジェットみたいなモノと酒しかねぇなという感じではあるんですが・・・
もの
YubiKey 5 NFC
2段階認証しておくのが当たり前になってきた時代ですが、いちいち二次元バーコード読んだりするのもめんどくさいし復元が謎のコードというのもしんどいなぁと思っていたので導入。
NFCも着いてるのでiPhoneの二段階認証もこれで管理できる。セキュリティ意識を高めたい人にはお奨めしたいデバイス。
SSHのキーも内部に格納できるので、サーバー周りとAWSのアカウントをまとめて権限を委譲するとかも物理的にできるのでかなり便利です。
Easthills Outdoor(イーストヒルズ) 保冷リュック
これ次の酒カテゴリに入れるか迷いましたが一応汎用性もあるのでこちらで。
なんとこいつは一升瓶が入るのでちょっとした外飲み(夏のBBQなど)に一升瓶を保冷して持って行ける優れもの。
とはいえ今在庫が無いので同じ様なの買うならこちら?
Easthills Outdoors バックパック クーラー 32缶
ヘラマンタイトン ヘララップ
結束バンド「インシュロック」を作っているヘラマンタイトンが出してる電線などをまとめる保護チューブ。スパイラルチューブ自体はよく見ると思いますが、このヘララップはまとめる作業が圧倒的に楽になります。
φ5 ~ 30mmまでいろんな種類があるので適宜選んでもらえたら。自分は用途的に8mmを選択しましたが、PC周りの整理とかに使いたい場合は16mmのものがいいかも。
より詳細が知りたい場合はヘラマンタイトンのウェブサイトを確認して下さい。
Elgato Stream Deck
自分が買ったのは一世代前ですが、妙に値上がりしてるので現行モデルを載せてます。
ストリーマー用のガジェットなんですが、普通に左手デバイスとして便利です。
青軸キーボードの左手デバイスを使っていましたが、アプリケーションによって配置を覚えるのが大変なんです。このデバイスはボタンがディスプレイになっているので、ボタンの役割が一目見ただけで分かるのがとても便利です。
更に、機能によっては動的な表示(例えばタイマーだとカウントダウンの数値がでる)もできるのでそういうところも便利。
Toggl Trackなどタイムトラッキングアプリを利用している人には超お薦めデバイスです。
ソニー 完全ワイヤレスイヤホン WF-SP900
ここ数年ぐらいは風呂でシャワーを浴びている時ですら音を聞いているのでIP68ぐらいの防水機能は欲しいところ。(スマホのスピーカーではシャワー時に音が聞けません)
最初は昔使っていたAftershokzのTitaniumを使っていた(とはいえコレはIP55なのでお風呂での使用は推奨しません)のですが、折れて故障するトラブルになってしまい別のモノを探すことに・・・
実は3000円~5000円ぐらいのデバイスを5つぐらい試したのですが、全て一ヶ月前後で故障しました。まぁシャワーまで耐えられるデバイスの方が本来不思議なんですが。
このデバイスを購入して2ヶ月ほどなので、ここまで故障しなかったデバイスはコレが初めてです。
とはいえ説明書にも「シャワーなどの利用は想定していません」と書かれているので一応非推奨です。ご注意を。
ソニー ワイヤレスノイズキャンセリングイヤホン WF-1000XM4
今まではWF-1000XM3を利用していてそこまでバージョンアップする必要性もないと思っていたんですが、コンプライ Ts-200を愛用しているとランニングコストがまぁまぁかかることに気づき、それなら新しいやつ買うかぁと思って買いました。
ノイズキャンセリングのレベルも上がっていますが、個人的に嬉しかったのが風切り音の低減(WF-1000XM3は少し風が吹くと鼓膜がなくなる)と外音取り込みがよりクリアに聞こえる様になったと感じてます。
あとはイヤーピースの劣化度合いがどんなもんか検証するのみです。
サンワダイレクト キーボードスライダー
基本はキーボードが目の前にあれば嬉しいのですが、何か書きながらキーボードを利用したいときなどはいくら机が広くてもどちらかを奥に追いやるワケにも行きません。
そういうときに便利なのがこのキーボードスライダー。机の下にキーボードをおいておけば、キーボードで作業しながらノートを書くということも出来ます。
机の広さに応じて大きさを選ぶと良いと思いますが、マウスもおきたいならMかLをオススメします。
酒
個人的に買って良かった酒関連です。飲んで良かったというよりは1本買って良かったという意味合いが深いかも。
色々な要素を鑑みて絞りました。
グレンケアン ブレンダーズ モルトグラス 6個セット
いきなり酒ではないんですがやはり飲み比べするときに並べて飲み比べたいもの。
モルトグラスを必要最低限のクオリティで個数を揃えるならグレンケアン一択です。蒸留所などに行く機会がある人はおそらく蒸留所で買うのが安いしロゴが入っててオシャレなのでそこで買うのがオススメです。
(蒸留所では大体一脚500~800円ぐらいで売ってると思います)
有り難いことにモルト飲み比べをしてくれる友人がいるので今年買い足しました。
Douglas Laing XOP Miltonduff 25 yo [1994]
ダグラスレインのXOPシリーズのミルトンダフ。最高にうまい。蜂蜜系のあま~いウイスキーが好きなら絶対好き。
長熟にしかない複雑みも感じられるのに比較的低価格なのはとても良いリリース。
Bruichladdich The Ternary Project
国内で輸入してるお店はあまり見たことないですが直販から買えます。
いわゆるクラシックラディとポートシャーロットとオクトモアのバッティングモルト。混ぜれば良いってもんじゃねぇぞ!と怒りたくもなる内容ですがおそらく飲めばその真価を感じられるかと。
ポートシャーロットとオクトモアの占める割合が高いので、それはもう煙くさいウイスキーなんですが麦芽の甘みや乳酸感とのバランスもしっかり取れているし余韻もかなり続くドチャクソ満足度の高い一本です。
ノブクリーク 9年 シングルバレル・リザーブ
今年買ったコスパ部門1位だと思うバーボン。60度を超しててシングルバレルで5000円以下なら文句の言い様はないでしょう。
やはりバーボンはハイプルーフ。バチバチのパンチ感にバニラと樽感が乗ってきたらそれはもう美味しい!
あんまりコスパコスパ言いたくはないですが、地域毎にコスパ良いものを考えて行くのも楽しそうです。
【九尾】なすひかり48% 生酒
個人的に勝手に推してる天鷹酒造さんの【九尾】シリーズで通年販売になったリリース。
香りや旨みなどのバランスが好きで、挑戦的なシリーズなのに良心的な価格設定なので是非いろんな人に飲んでいただきたいと思えるし、自分でもリピートしたいと思える日本酒です。
WAKAZE THE BARREL LIMITED -CRONOS-
限定ボトルで今は買えないんですが、もしかしたらそのうち買えるようになるかもしれないので。
貴醸酒の様な造りの原酒をコニャック樽で熟成させた日本酒で、香り味わいのほとんどが洋酒!って感じのフルーティーで甘いのですが、飲むと奥に日本酒を感じるとても面白いリリースでした。
同じ様なリンク8888も去年飲んで美味しかったのですが、今年もこんな完成度が高く面白味もあるSAKEに出会えて幸せでした。
ほんとはもっといっぱい紹介したいお酒はありますが、まぁ厳選ということでこの辺で。
Raspberry Pi Zero Wのセットアップ
防備録も兼ねてRaspberry Pi Zero Wのセットアップ方法をかいておきます。
後で気づいたんですが多分IPの固定もheadless setupが可能だと思います。(時間があればどっかでチャレンジします)
必要なモノ
- Raspberry Pi Zero W本体
- SDカード(8GBぐらいでいい気がしますが、正直そこまで値段が変わらないので16GBぐらいあって良いと思います。)
- SDカード書き込み機(USB SD変換のようなもの、PCにSDカードを直接させる場合は不要)
- 電源(USB microBで電源供給します。500mAぐらいは流せる電源が望ましいと思います。)
- コンセントに挿して直接USBケーブルが生えてるタイプもありますが、普通のUSB ACアダプタにUSB microBケーブルを挿す方が汎用性もあるし値段もそこまで変わらないと思いますのでお薦めです。
- 無線LAN
細々したのがめんどくさいという人はスターターキットを買ってしまうのも手でしょう。
セットアップ
OSのインストール
Raspberry Pi Imagerのセットアップ
今はRaspberry Pi Imagerという便利ツールがあるのでソレを利用すると簡単にSDカードのセットアップが出来ます。
Raspberry Piのサイトから該当のインストーラーをダウンロードしてインストールして下さい。
SDカードのセットアップ
Raspberry Pi Zeroは基本GUIは使わないと思うのでLite版をインストールします。Operating Systemの項目はPi OS LITEを選択します。
Storageは各々のSDカードを指定してください。
上記が完了すればWRITEというボタンが押せると思うので書き込み開始してください。
暫く待つと完了すると思います。
無線LANのセットアップ
画面とキーボードを接続してセットアップしても良いんですが、基本的にminiHDMIやmicroUSB A→USB Aみたいな気持ち悪い変換を普通は持っていないと思います。
なので画面など無しでセットアップ(headlessみたいなワードで検索すると色々出てくると思います)します。
SSHの有効化
公式ドキュメントに従い、SDカードのルートディレクトリ(一番上)に'ssh'という空ファイルを作ります。
どんな方法でも良いですが、Windowsならファイルパスを利用するバーを利用するのが手軽でいいです。
SDカードのディレクトリ(自分の場合はH: でした)にエクスプローラーで移動して、パスのバーに
cmd /C copy nul ssh
と入力してEnterを押すと、開いているディレクトリにssh
という名前の空ファイルが作成されます。
ファイルが出来てればOKです。
WiFiの設定
公式ドキュメントに従いますが、boot
フォルダを作成してくれと書かれていますがたぶんSD名がbootなのでこれは無視してカード直下にwpa_supplicant.conf
というファイルを作成します。このファイルに設定を記載します。
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev update_config=1 country=JP network={ ssid="<WiFiのSSID>" psk="<WiFiのパスワード>" }
上記で設定は完了です。
電源を入れてしばらく待ちます(たぶん5分以内ぐらいだったと思います。)
初期設定
SSHでログイン
初期設定を行う為、SSHでログインします。
上記までのセットアップでssh pi@raspberrypi.local
でログインできる筈です。
デフォルトのパスワードはraspberry
です。
raspi-config
基本的な初期設定はsudo raspi-config
で行えるようになっています。
raspi-config
では
- パスワード変更
- 地域情報の設定(タイムゾーン)
を調整します。
パスワード変更
raspi-config
の画面は矢印キーとEnterで操作できますが、一番上の1 System Options
でEnterを押します。
その次にS3 Password
をEnterで選択します。
ウィザードに従ってパスワードを設定します。
これで次回以降ログイン時にはパスワードが今決めたものになります。
あとでSSHの公開鍵認証に変更しようと思うので、ログインで使う事は無いかもしれませんが念のため変更しておくことをお薦めします。
地域情報の設定
sudo raspi-config
で対話式の設定項目を出した後に5 Localisation Options
に入ります。
次にL1 Locale
を選択します。
そうするとConfigureing locales
という画面に切り替わります。
キー操作で上下に移動出来ると思います。デフォルトではen_GB.UTF-8 UTF-8
が選択されていますが、一般的なen_US.UTF-8 UTF-8
も追加しておきます。
日本語が使いたい人はja_JP.UTF-8 UTF-8
も選択しても良いかもしれません。
default localeをen_US.UTF-8 UTF-8
にして完了
次にタイムゾーンを変更します。
先ほどの5 Localisation Options
に入ったあとで次はL2 Timezone
を選択します。
自分は日本に合わせたいのでAsia
を選択します。
町の一覧が出てくるのでTokyo
を選択します。
これで完了です。一応時間がちゃんと設定されているか確認するにはdate
コマンドなどを使ってみるのも良いでしょう。
IPの固定
今はDHCPでIPが降ってきてるだけなので固定しておきます。
公式サイトに従い、/etc/dhcpcd.conf
の最後に追記します。
interface wlan0 static ip_address=192.168.0.4/24 static routers=192.168.0.254 static domain_name_servers=192.168.0.254
(この辺の設定は適宜変えて下さい。)
IPは適当なモノか、思いつかなければ降ってきてるIPを固定するのでも良いでしょう。
自分はip addr show
コマンドで確認してそれを利用しました。
WiFiの追加設定
一応WiFiのパスワードを平文で入力しているのでハッシュ化しておきます。
wpa_passphrase
コマンドで必要情報が出力できるので、コレを利用します。
sudo sh -c 'wpa_passphrase "SSHD名" "パスワード" >> /etc/wpa_supplicant/wpa_supplicant.conf'
以前のWiFiの設定やパスワードなどが記載された状態なので、適宜/etc/wpa_supplicant/wpa_supplicant.conf
を編集して下さい。
たぶん
network={ ssid="<WiFiのSSID>" psk="<WiFiのパスワード>" } network={ ssid="<WiFiのSSID>" #psk="<WiFiのパスワード>" psk=ハッシュ値 }
みたいになってるので、
network={ ssid="<WiFiのSSID>" psk=ハッシュ値 }
にしてあげて下さい。
公開鍵認証の設定
ここは通常のLinuxと変わらないと思いますので割愛します。
パッケージなどの更新
この辺まできたら大体完了なのでパッケージの更新などをしておきます。
sudo apt update && sudo apt upgrade -y sudo apt dist-upgrade
速度を気にされる方はリポジトリを日本にしてから更新した方が早いと思います。
pipのインストールあたりでたぶん悪さするので推奨しません。
sudo sh -c 'mv /etc/apt/sources.list /etc/apt/sources_old.list; echo "deb http://ftp.jaist.ac.jp/raspbian stretch main contrib non-free rpi" > /etc/apt/sources.list'
create-react-appで作成したプロジェクトのテスト
React周辺は周りの誰にも聞けないし本当に手探りで色々やってます。
いいかげんフロントでもテストを導入しないと意味不明になってきたので、まずはReact自体のテストを学習します。
基本的にはJest公式などを参考にしながら進めています。
環境構築
プロジェクトの作成
この辺はすっ飛ばしても良さそうですが、今回は学習ということで新規にプロジェクトを作成していきます。
いつもはNext.jsを利用していますが、極力シンプルに進めたいので通常のReactプロジェクトを作成していきます。
npx create-react-app react-test-learning --template typescript
とりあえず実行しておきます
cd react-test-learning yarn start
いつものくるくるReactマークが出ればOKです。
ロジックのテスト
簡単なテスト
まずはReactのテストというよりJavaScriptのテストを行ってみます。
srcディレクトリにsum.ts
とsum.test.ts
を追加します。
sum.ts
export const sum = (a: number, b: number): number => { return a + b }
sum.test.ts
import {sum} from "./sum" test("add 1 + 2 to equal 3", () => { expect(sum(1,2)).toBe(3) })
これでとりあえずテストは回る筈です。
yarn test
でJestを実行します。
PASS src/sum.test.ts √ add 1 + 2 to equal 3 (2 ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 1.888 s, estimated 4 s Ran all test suites related to changed files.
とりあえず良さそうです。
クリーンなプロジェクトだとTypeScriptを利用しているのでBabelの設定などいるんでしょうが、create-react-appを利用してプロジェクトを作成しているのでその辺はよしなにしてくれているっぽいです。
ちゃんとしたテスト入門であればexpect
のMatcher
を色々見ていくところですが、その辺はやりながら覚えることにします。
基本に立ち返る際にはJestの公式を参照したいところです。
非同期のテスト
非同期のコードが含まれているテストにはテストに渡す関数の引数に与えられるdone
を使うかPromiseで処理するかのどちらかだそうです。
おそらくナウいのはPromiseだと思いますし、今は async/await を多用すると思うのでその辺で試してみます。
めんどくさいので先ほどのsum.ts
に追記します。
sum.ts
~ export const promiseFunction = (value: number) => { return new Promise((resolve)=>{ setTimeout(() => { resolve(value) }, 1000) }) }
sum.test.ts
import {sum, promiseFunction} from "./sum" ~ test("async test", async () => { const value = await promiseFunction(100) expect(value).toBe(100) })
Jestを実行します。
PASS src/sum.test.ts √ add 1 + 2 to equal 3 (1 ms) √ async test (1000 ms) Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 0 total Time: 3.8 s Ran all test suites related to changed files.
ちゃんとasyncの方はsetTimeoutで設定した1000msかかってます。
describeブロックとかスコープの話
簡単なテストであれば上記までのtest
で処理出来そうですが、もう少し大がかりなテストになってくるとブロック化してテストを回したくなるはずです。(例えば、どこかに通信して取得したデータに対して、それぞれ処理を行うモジュールなど)
テストファイルのグローバルにbeforeEach
やbeforeAll
などを記載することも出来ますが、describe
ブロックに分けて実行する方がシンプルだと思います。
セットアップとティアダウンに関しては公式の情報が参考になると思います。
testとit
ウェブ上ではit
を利用しているテストもありましたが、testはitのエイリアスなので全く同じモノのようです。
ただ公式ドキュメントでもitを利用してテストを書いているケースはないので可読性?からかtestを利用する方がよさそうです。
Reactのテスト
スナップショットテスト
JavaScript(TypeScript)のテストは上記までのもので大丈夫そうではある(モックとかあるけどその辺は後回し)ので、Reactのテストを行っていきます。
まずはレンダリングがうまくいってるか分かりやすいスナップショットテストを行ってみます。
create-react-appでプロジェクトを作成していると、App.tsx
とApp.test.tsx
が自動で作られているのでコレを使い回します。
テストするコードはJestの公式を参考に、react-test-renderer
を使用しない形でスナップショットを取っています。
App.tsx
import React, {useState} from 'react'; import logo from './logo.svg'; import './App.css'; type Status = "normal" | "hovered" const App = () => { const [status, setStatus] = useState<Status>("normal") const onMouseEnter = () => { setStatus("hovered") } const onMouseLeave = () => { setStatus("normal") } return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.tsx</code> and save to reload. </p> <a className={status} href="https://reactjs.org" onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} > Learn React </a> </header> </div> ) } export default App
App.test.tsx
import React from 'react' import { render, screen, fireEvent } from '@testing-library/react' import App from './App' test('changes the class when hovered', () => { const testComponent = render(<App />) expect(testComponent).toMatchSnapshot() fireEvent.mouseEnter(screen.getByRole("link")) expect(testComponent).toMatchSnapshot() });
Jestを実行すると、src
ディレクトリ下に__snapshots__
ディレクトリが作成され、スナップショットが保存されていると思います。
とはいえ、この辺は個人的にはStorybookのVisual Testingで良い気がするのでたぶん使わないと思います。
レンダリングテスト
データなどを受信したり非同期処理を行った後に正しくレンダリングされているかテストします。
とはいえこの辺は実際に作って見て考える要素が大きいので、ここではTesting Libraryの公式例を挙げておくにとどめておきます。
基本的にはロジックのテストと同様に行いますが、下記に注意しながらテストを組み立てていくようです。
- 検証したい要素をTesting Libraryの
screen
のメソッドのgetByText
やgetByRole
などで取得して評価する - イベントの発火はTesting Libraryの
fireEvent
を利用して発火させる - イベントの発火の影響を待つ場合はTesting Libraryの
waitFor
を利用してawaitで待つ
という感じのようです。
もしテストに慣れて記事に出来る程度に知識がついたら別の記事にするかも知れません。
Next.js+TypeScript+CSS Modulesの環境でStorybookを使う
去年の10月に同じような記事を書いた気がするんですが、もううまく動かなくなってました。
元々フロントは専門外なのでちょくちょくしか触らないのですが、なかなかに辛い・・・
と思ってたのも1日。やはり世界には優秀な方々がたくさんいるようで、既に解決策を提示して下さっております。
Configure Storybook to work with Next.js, TypeScript, and CSS Modules · GitHub
main.js
のwebpackFinal
で/\.module\.css$/
(CSS Modulesのファイル)をstyle-loader+css-loaderで読み込ませようという処理を試みている記事はウェブ上にいくらか見つけていたんですが、何故かうまくいってませんでした。
この記事では一旦普通のCSSファイルをエスケープするために
~ newConfig.module.rules.find( rule => rule.test.toString() === '/\\.css$/' ).exclude = /\.module\.css$/; ~
という処理を挟んでいるので、コレが効いてくるのでしょう。
おそらくStorybookのCSSをWebpackのどこかの処理で読み込んでいるのがCSS Modulesと競合してうにゃうにゃなっているんでしょうか。フロントエンド何も分からないマンとしては意味不明です。
上記のgistとその参照元を参考に、CSS Modulesだけ適応したいので、.storybook
内にあるファイルは下記のように変更しました。
一応style-loaderとcss-loaderが入ってない場合は入れといて下さい。
yarn add -D style-loader css-loader
まずはmain.js
const path = require('path') module.exports = { stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], addons: ['@storybook/addon-links', '@storybook/addon-essentials'], presets: [path.resolve(__dirname, './next-preset.js')], }
次にnext-preset.js
を作成して、下記の通りにしました。
module.exports = { webpackFinal: async (config) => { const { module = {} } = config const newConfig = { ...config, module: { ...module, rules: [...(module.rules || [])], }, } newConfig.module.rules.find( (rule) => rule.test.toString() === '/\\.css$/' ).exclude = /\.module\.css$/ newConfig.module.rules.push({ test: /\.module\.css$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 1, modules: true, }, }, ], }) return newConfig }, }
preview.js
はとりあえずそのままです。グローバルでCSSを読み込ませたい場合はここで読み込ませるみたいです。
一応例を挙げるなら
import '../src/styles.css';
とか。
YouTubeのSuperChatをGoogle Spredsheetに読み込む(SuperChatEvents: list使用編)
YouTube Live Streaming API(YouTube Data API v3)を利用してスーパーチャット情報をGoogle Spredsheetに出力してみました。
が、結論から言えば実用性が無かったので供養エントリです。なんでダメだったか知りたい人など対象です。
(あとOAuthの勉強の題材探してる人とか)
内容を要約すれば
SuperChatEvents: list
を利用してGoogle Spredsheetにデータを抽出する方針、実際にココまではできた- 上記APIはOAuth認証で承認したアカウントのスーパーチャットを抽出できる(第三者のスーパーチャットは取得できない)
- 最新50件のみ取得可 ← コレが問題
という感じです。
実用的なのは検証中(クォータ割り当て上限の引き上げは現在やりとり中・・・)
前提
HTMLのパースをしないワケ
結構スーパーチャットを抽出するツールは有志の方々によって開発されています。
ただし、大体YouTubeのチャット欄のHTMLをパースして抽出する方針のものが散見されます。
(これはおそらくなのですが、どっかのバージョンアップまでチャット情報を取得するAPIが無かったのにも起因している可能性はあります)
僕個人としてはYouTube側がしっかりしたツールを用意していない方が問題だと思っているのでいちいち指摘したくないんですが、これはYouTubeの規約に抵触する可能性があります。
利用規約に以下の行為は禁止するという下記のような記載があります。
本サービスの利用には制限があり、以下の行為が禁止されています。
[省略]
3. 自動化された手段(ロボット、ボットネット、スクレーパなど)を使用して本サービスにアクセスすること。ただし、(a)公開されている検索エンジンを YouTube の robots.txt ファイルに従って使用する場合、または(b)YouTube が事前に書面で許可している場合を除きます。
じゃあ実行タイミングが手動ならええんか?という規約の穴を突くような発想もしたくなりますが、意図としては「負荷増えるからAPI以外でのスクレイピングすんな」だと思うので
の3択が考えられます。
2番目は過去に盥回しにされた人を観測していた経験があり、3番目はアレなので1番目の方針で考えましょう。
なんでスプレッドシート+JS?
非エンジニアの方でも比較的使いやすいスプレッドーシートの拡張機能として実装した方が役に立つかなぁと思った次第です。
TypeScriptに脳を侵された人間なので純JavaScriptみたいな言語は非常に辛いのですが、この規模ならどんな言語でもヨシとします。
実装
流れ
OAuth認証の前準備をしておく
基本的にこの辺を参照して下さい。もしかしたら古くなってるかも。
ただし、上記の準備では不十分です。不十分な部分は後ほど解説します。
スプレッドシートを作成する
とりあえずスプレッドシートを作成しないとなにも始まらないので作成します。
スプレッドシートのコンソールから新規作成します。
https://docs.google.com/spreadsheets/?usp=mkt_sheets
とりあえず「無題のスプレッドシート」という箇所に何かファイル名を入れておいて保存しておきます。
名前を適当に入れたら勝手に保存されます。
スクリプトを追加する
「ツール」→「スクリプトエディタ」を選択するとスプレッドシートに紐付いたスクリプトが作成されます。
先ほどと同様に適当に名前は付けておきましょう。
スクリプトを書く
main.gs
とtoken.gs
の二種類に分けてますが、1ファイルでも別に動くと思います。
ファイルを追加するには、左上の「ファイル」→「New」→「スクリプトファイル」で名前を適当に入れます。
main.gs
を追加したい場合は「Enter new file name」と書かれた箇所にmain
と入れると作成されます。
main.gs
token.gs
OAuth認証を行う
OAuth認証の準備をする(続編)
OAuth認証の前準備をしておくの手順では不十分な箇所をここに来てようやくすすめて行きます。
token.gs
にはdoGet
という関数が含まれているのですが、これはOAuth認証のリダイレクトで飛ばされた先のウェブアプリケーションになっています。
('ω')。o(????????????)という人は、とりあえず無視して読み進めて下さい。たぶん分かります
OAuth認証を承認するには
- Googleの承認画面にアクセスする
- 自分の作ったスクリプトがGoogleアカウントの機能を利用する(今回はYouTube Data API v3の読み取りのみ)
- 戻ってきてアクセストークンを取得する為のいろんなidとかを取得する
という流れがあります。
トークン情報をメモっておくシートを作成する
スプレッドシートにトークン情報を保存しておくシートを作成します。
まだ何も書いてない「シート1」の名前を変更するか、新しくシートを作るかして、「token」というシートを作成してください。
そこに、OAuth認証の前準備をしておくで取得した「クライアントID」と「クライアントシークレット」を保存しておきます。
分かる方は適当な位置で大丈夫ですが、よく分からない人は画像の通りA1~A8に名称(無くても良いんですが分かりやすいので)、B1~B8に該当する情報を入力していきます。
今回はB1に「クライアントID」を、B2に「クライアントシークレット」を入力して下さい。
Googleの承認画面にアクセスする
本来であればこの辺はWebアプリケーションに組み込んで運用するものだと思いますが、いかんせんめんどくさ過ぎるのでURLを生成する関数を実行してURLを取得します。
そしてコレをブラウザにコピペして承認してしまおうという魂胆です。
token.gs
を開き、メニューから「公開」→「ウェブアプリケーションとして導入」を選択します。
すると「Deploy as web app」という表示がでてくるので、特に何も考えずに下にある「Deploy」というボタンを押します。
初めて実行する際には「このスクリプトがスプレッドシートの編集や外部サービスへのアクセス(YouTubeからデータを取ってくる操作)」を許可する必要があります。
一度許可していれば後からは何も言われませんが、上記の「Deploy」を押すと下記の警告が表示されるかと思います。
「許可を確認」を押します。
自分のアカウントを選択して次に進みます。
(ここでアプリ名が「test01」になってますが、これはスクリプトの名前(エディタが表示されてるところの左上にあるやつ)です。)
「このアプリは確認されていません」とヤバそうな表示が出ますがヤバくないです。自分の作ったばっかりのアプリケーションなのでGoogle様による確認が済んでないのは当然です。
進む為に左下の詳細をクリックして進みます。
順調にいけば最後にURLが表示されます。このURLをtoken.gs
の13行目にあるdeployURL
として保存します。
例えばURLがhttps://script.google.com/macros/s/hogefuga/exec
と表示されていれば、token.gs
の13行目を下記のように修正します。
const deployURL = "https://script.google.com/macros/s/hogefuga/exec"
ここまで来たら、ツールバーの「関数を選択」という箇所で「makeAccessTokenURL」を選択し、三角アイコンのボタンを押します。
問題無く実行できれば、準備の段階に作成した「token」というシートの一番下にURLが追記されています。
早々にアクセスしたくなるのですが今アクセスするとはじかれます。
OAuth認証の前準備をしておくでOAuth画面の作成など行ったと思いますが、同じ画面で先ほどのdeployURLへのリダイレクトを許可します。
このクライアント名のところをクリックすると変種画面に移動します。
「承認済みの JavaScript 生成元」という箇所にhttps://script.google.com
を、「承認済みのリダイレクト URI」にdeployURL
に設定したURLを入力します。
画像では2個URLが入ってますが、1個です。
「保存」を押して完了です。
OAuth認証を行う(トークンを取得する為のcodeの取得)
ここまで来たら、先ほど「makeAccessTokenURL」で作成したURLにアクセスします。
アクセスすると、先ほど「ウェブアプリケーションとして導入」の時に見たような画面が表示されます。
OAuthクライアント作成時の名前が表示されており、その下にYouTubeのチャンネル(アカウント)が表示されていると思います。
該当するアカウント(スーパーチャット情報が取得したいアカウント)をクリックして次に進みます。
また見覚えのある画面だと思いますので、同様に処理していきます。
ここの承認の際に「YouTubeアカウントの表示」となっている事を確認して下さい。
僕が悪意のある人間で、なにがしか余計なことをするのであれば情報の表示のみならずコンテンツの編集やアカウントの情報変更など全てできる権限を承認させます。
ここでは「表示」とあるので、最悪情報が取られるだけで済みます。それはそれで困りますが、ソースも全部出してるのでどういう処理してるのかは確認して下さい。
しつこいぐらい承認のステップがありますが、OAuthは簡単にできる反面結構権限が強いです。
このぐらい「ほんまに大丈夫なんか?」と聞かれる行為であるということを肝に据えて開発をしましょう。(自戒を込めて)
「許可」を押して処理完了です。
{"status":"ok"}
と表示されれば処理完了です。
上記の処理の最後でエラーページが表示される
幾つかアカウントを所持してログインしていると、上記の処理の最後でエラーになる事があります。
その場合はそのブラウザを閉じずに、エラーページが表示されているブラウザのURLを確認して下さい。
おそらくパラメータにcode
というものが含まれていると思います。
https://script.google.com/macros/s/hogefuga/exec?code=XXXXXXXXXXXXXXXX&hoge=YYYYYYYYYYYYYYYYYY...
上記のcode=XXXXXXXXXXXXXXXX
にあたるXXXXXXXXXXXXXXXX
の情報さえあれば問題無いのでもしエラーが表示されてしまった場合はそちらをコピーして下さい。
そして、スプレッドシートで作成した「token」というシートのB3にあたるcode
という箇所に貼り付けて置いて下さい。
OAuth認証を行う(新規トークンの発行)
上記までうまくいっていればあとはそこまで難しい処理では無い筈です。
token.gs
のgetNewAccessToken
を実行します。
問題が起こらなければ何事も無かったかの用に処理が終わります。
(codeを生成して時間がたっているとエラーが発生するかも知れません)
スプレッドシートのtoken
のシートにrefresh_token
などが生成されているのを確認しておきましょう。
トークンが生成されていれば、後はこのtoken.gs
を直接触ることは無いかと思います。
実行する
それではとりあえず実行してみます。
取得する関数はgetSuperChatEvents
として宣言しています。getSuperChatEvents
を実行して下さい。
問題無ければtoken
を作成していたスプレッドシートに実行時間の名前がついたシートが追加されていると思います。
スーパーチャットが有効化されていて何件か取得できる場合はこのようになります。
問題点
上記まで実行できれば「おっ、これでスプレッドシートにデータが集約できて便利やな!」という感じなんですが、様々な問題点がありました。
もしかしたら今後APIが改善されるかもしれないので、さしあたり今回用意したスクリプトの意図などを列挙しておきます。
- SuperChatはアカウント(チャンネル)に対して発生するイベントであり、どのライブで発生したものかは現段階では判別できない
- SuperChatEvents: listは最新のSuperChatを取得するAPIなので特定期限~現在まで、という縛りでシートに書き出す仕様にした
- 全部書き出したいって人は
main.gs
の1行目にあるafterDate
に格納されている日付をかなり古い日付にすればたぶん大丈夫
- 全部書き出したいって人は
- 一番上でも書いたが最新50件のみ取得可、50件以上古いデータは取得できない。これは実際に観測しましたし、海外のフォーラムなどでも言及がありました
- 上記の制約込みで考えると、GASの最短実行間隔である1分の中でスーパーチャットが50件未満であれば、ライブ時に1分間隔で実行するトリガーを作成して毎分
getSuperChatEvents
を実行するというのはできそう
今後の課題
たぶんYouTube Live Streaming APIのチャットを取得するAPIを利用してスーパーチャットのみを抽出するのが確実と思われます。
ただし、このAPIをライブ配信時に取得し続ける方針は同時接続人数5000~6000ぐらいの状況下で1分あたりのクオータコストが60ぐらいでした。
1アカウントに割り当てられた日にちの上限は10000なので、この規模のライバーさんの放送だと2.7時間が限度という事になります。
実用的で無いこともないんですが、コメント数が増えればクオータコストが上がる可能性がある(こちらは機会があれば別記事で紹介します)ので心許ない気がします。
ちなみに、SuperChatEvents: list
のコストは直接分からないんですがたぶん1か2です。チャットを全部取得するよりはコストが低いので可能性としてはこちらを3秒に1回実行する(16件/秒程度のスパチャ速度まで捌ける、一日MAX8時間程度)のが現実的かもしれません。
Storybookでwebpackの設定に手を加えずにCSS Moduleを適応する
※2021-02-10 追記、下記の方法や他の方法でStorybook上でCSS Moduleがうまくあたらないので、今後はstyled-componentsの採用を検討しています。 そのうちまたこの方法でうまくいくようになるかも知れないので残しておきますが、現段階ではうまくいきませんでした
Next.jsでもデフォルトで使える様になったのでCSS Moduleを積極的に採用していこうかと思っていたのですが、webpackを通したあとでないとスタイルが適応されないので、デフォルトの設定だとStorybookで確認できません。
css-loaderに読み込ませる設定をwebpackの設定ファイルに追記する方法もあるみたいなのですが、色々探していたところstorybook-css-modules-preset
というパッケージを見つけました。
パッケージをインストールした後にStorybookのアドオン設定をするだけでcssがロードされる様になります。
yarn add -D storybook-css-modules-preset
した後に.storybook/main.js
を下記の通りにするだけで適応されました。
module.exports = { stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], addons: ['@storybook/addon-links', '@storybook/addon-essentials', 'storybook-css-modules-preset'], }
めっちゃ楽。
Next.js (TypeScript) + Storybook + Amplify の初期構築(@2020-10-03)
前回からの続きです。
Amplifyを導入して嬉しいのは主に
- AppSyncによるGlaphQL APIの利用
- Cognitoによる認証
だと思います。
今回はAmplify DocsにあるようにGraphQL APIを利用することを持って初期構築としてみます。
まず最初に
必要最低限の環境構築
- AWSアカウント(当たり前ですが)
Amplify CLIのセットアップ
インストール
AmplifyはCLI経由で色々することが多いです(というか、CLIは必須?)。
公式ドキュメント通りnpmでインストールします。
npm install -g @aws-amplify/cli
インストールが完了するとamplify
コマンドが利用できます。
初期設定
Amplify CLI経由でAWSサービスを触るユーザー設定を行います。
amplify configure
コマンドを実行するとAWSコンソールのログイン画面に飛ばされるので、ログインします。
その後、幾つか質問されるので回答していきます。
Specify the AWS Region ? region: # 任意のリージョン、日本ならたぶんap-northeast-1 Specify the username of the new IAM user: ? user name: # 分かりやすい名前付けといた方が後々困らない Complete the user creation using the AWS console
アカウント作成の為にブラウザに飛ばされます。
うまくアカウント作成画面に移動できなかった場合はコンソールに表示されているURLに直接アクセスしましょう。
基本的に「次へ」を連打で良いと思います。好みがある場合は都度修正して下さい。
アカウントが作成できたら、コンソールへ戻ります。
accessKeyIdとsecretAccessKeyが問われますので、それぞれアクセスキーIDとシークレットアクセスキーを入力します。
両方を入力すると、プロファイル名を問われます。基本的にはdefault
で良いと思いますが、アカウントを使い分けたりしている人は名前を変えておくと良いかもしれません。
(一応この設定はC:\Users\ユーザー名\.aws
内にあるcredentials
の中に記載されています。)
以上でAmplify CLIの初期設定は完了です。
Storybookのセットアップ
必須では無いんですが、Storybookを利用したいのでこのタイミングでセットアップします。
基本的には下記の記事を参考にさせていただきました。
Storybookのインストール
超絶簡単です。Storybookの公式にあるとおり
npx sb init
で完了です。
サンプルのコンポーネントもsrc/stories
に生成されるようで、これらがtsxファイルで用意されているところを鑑みるとtypescriptのプロジェクトかどうかもインストール時にチェックしているようです。
試しに起動するには
yarn storybook
でStorybookが起動します。
Web上には、どうもトラブルがあったりする影響なのかnpm/yarnでパッケージをインストールしている方が多いように見受けられます。
もし今後触っていく内にトラブルが多いようでしたら手動でパッケージをインストールする方法を試してみたいと思います。
一応react-docgen-typescript
も便利そうだったのですが、基本的にデフォルトでTypeScript使っていれば型を表示してくれるのと、プロパティに?
を付与した場合の挙動がめんどくさそうだったので導入しませんでした。
参考までに比較画像を掲載しておきます。
プロジェクトでのAmplifyのセットアップ
初期化
まずはプロジェクトのルートディレクトリでAmplify CLIを用いて初期化する必要があります。
ampllify init
いくつか質問されるので答えていきます。
? Enter a name for the project nextamplified ? Enter a name for the environment dev ? Choose your default editor: IntelliJ IDEA #(ここはお好きなIDEなどをお選び下さい) ? Choose the type of app that you're building javascript Please tell us about your project ? What javascript framework are you using react ? Source Directory Path: src ? Distribution Directory Path: build ? Build Command: npm.cmd run-script build ? Start Command: npm.cmd run-script start ? Do you want to use an AWS profile? Yes ? Please choose the profile you want to use default
大体デフォルトです。
問題無くAmplifyの初期化が終われば、Amplifyをアプリケーションで利用する為にライブラリを入れておきます。
yarn add aws-amplify @aws-amplify/ui-react
GraphQL APIとデータベースの作成
準備
GraphQLのAPIを有効にするとAWS側でAppSyncの準備がなされます。
本来であればDynamoDBなどの設定も必要ですが、その辺を全てCloudFormationで処理してくれるので、基本的にウィザードに沿って進めるだけで大丈夫です。
amplify add api
でAPIを追加していきます。
? Please select from one of the below mentioned services: GraphQL ? Provide API name: nextamplified ? Choose the default authorization type for the API API key ? Enter a description for the API key: ? After how many days from now the API key should expire (1-365): 7 ? Do you want to configure advanced settings for the GraphQL API Yes, I want to make some additional changes. ? Configure additional auth types? Yes ? Choose the additional authorization types you want to configure for the API Amazon Cognito User Pool Cognito UserPool configuration Use a Cognito user pool configured as a part of this project. ? Configure conflict detection? No ? Do you have an annotated GraphQL schema? No ? Choose a schema template: Single object with fields (e.g., “Todo” with ID, name, description) The following types do not have '@auth' enabled. Consider using @auth with @model - Todo Learn more about @auth here: https://docs.amplify.aws/cli/graphql-transformer/directives#auth GraphQL schema compiled successfully. Edit your schema at [プロジェクトパス]\amplify\backend\api\nextamplified\schema.graphql or place .graphql files in a directory at [プロジェクトパス]\amplify\backend\api\nextamplified\schema ? Do you want to edit the schema now? Yes Please edit the file in your editor: [プロジェクトパス]\amplify\backend\api\nextamplified\schema.graphql Successfully added resource nextamplified locally
基本的にはドキュメント通りやってますが、試しにやってみる分にはCognitoの認証などは不要だと思います。
とりあえずスキーマを見てみます。Choose a schema template
でSingle object with fields
を選択している場合はamplify/backend/api/nextamplified/schema.graphql
に下記の通りのファイルが生成されています。
type Todo @model { id: ID! name: String! description: String }
このまま試しても良さそうですが、一応ドキュメントの通りに修正します。
type Post @model @auth(rules: [{ allow: owner }, { allow: public, operations: [read] }]) { id: ID! title: String! content: String! }
@auth
ディレクティブで認証処理を入れています。この辺の挙動はドキュメントを参照して下さい。
(個人的にこの辺の柔軟さはAppSyncを採用する理由になっても良いと思っています)
編集が完了したらAPIをAWS上に生成します。
AWS上にバックエンドを生成(ローカルでモックを作る場合はとりあえず不要)
以降の操作は課金対象になる(DynamoDBやCongnito)と思いますので、ご留意下さい。ほぼ無料枠に収まると思いますが。
最初に現在の状況を確認しておきます。
amplify status
おそらく2個のリソース(AuthとApi)が表示されていると思います。
これらのリソースを下記の操作でAWSに生成します。
amplify push
AppSyncのGraphQLスキーマのコンパイルがまず走ると思います。
? Do you want to generate code for your newly created GraphQL API Yes ? Choose the code generation language target typescript ? Enter the file name pattern of graphql queries, mutations and subscriptions src\graphql\**\*.ts ? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes ? Enter maximum statement depth [increase from default if your schema is deeply nested] 2 ? Enter the file name for the generated code src\API.ts
とりあえず基本デフォルトです。
データ構造が複雑になった際にはmaximum statement depth
あたりをもう少し深くしてもいいかもしれません。
このウィザードが完了するとリソースを生成します。少々時間がかかると思います。
完了すると、ターミナルにGraphQLのエンドポイントとAPIキーが表示されると思います。
GraphQL endpoint: https:// GraphQL API KEY:
この状態まで行けばAWS上にはリソースが生成されています。
GraphQLのウェブコンソールを確認する場合は
amplify console api
で該当URLに飛ばされるようです。(AWSアカウントのログインが必要です)
ローカルでモックを生成する
AppSyncはローカルでモックを走らせて開発することができます。
下記のコマンドを打てばローカルでAppSyncのサーバーが起動します。
amplify mock api
料金が発生しないという安心感もありますが、何よりAWSにリソースを適応するのに結構時間がかかるので、基本こちらでの開発がメインになると思います。
GraphQLツールバーでデータを挿入する
前述のAWSリソースでもローカルモックでもいいのですが、とりあえずデータを入れて見ます。
(基本的にローカル環境での想定で話を進めます)
GraphQLのツールバー(ウェブコンソール)から下記のミューテーションを走らせます。
mutation CreatePost { createPost(input: {title: "Test Post", content: "post content"}) { id owner title updatedAt createdAt content } }
たぶんなにがしかの文句を言われる筈です。
これは@auth
ディレクティブがAPIキー認証はreadのみ許容するというスキーマな為です。
Use: API Key
という箇所をクリックしてUse: User Pool
(Cognito認証)にし、Update Auth
をクリックして適当な認証トークンを設定します(基本は何も変更せずにGenerate Token
で問題無いです)。
その後にもう一度上記のMutationを実行すると右側に適当なレスポンスが表示されると思います。
右側にレスポンスが表示されていれば大丈夫ですが、一応Queryも実行してみます。
query ListPosts { listPosts { items { content createdAt id owner title } } }
問題無くPostのリスト(とはいえ1個ですが)が取得できていると思います。
SSRでのAPI利用
ようやっとコンポーネントを触ります。
とりあえずチュートリアルということで、src/pages/index.tsx
に全て記載していきます。
index.tsx
でcssを参照しているので、cssもsrc
ディレクトリに移動しています。
これでトップページは表示されます。
AmplifyAuthenticator
といれるだけで、簡易的ではありますが認証保護+アカウント生成の機能までやってくれるのは嬉しいところです。
とりあえず起動してみます。
yarn dev
ローカルで開発するときはあわせて
amplify mock api
も実行しておきます。
問題無く表示されていればAmplifyAuthenticator
のところからユーザーを作成して適当に記事を投稿してみます。
たぶん404に飛ばされますが、これは作成後に記事のページに飛ぶ処理を書いておきながら飛んだ先のコンポーネントが無いためです。
SSGでのAPI利用
Next.jsの特徴としてSSRとSSGの両方を使い分けられます。
ここではSSGを利用して記事ページを作成します。src/pages/posts/[id].tsx
に作成していきます。
上記のyarn dev
とamplify mock api
で確認できると思います。
デプロイ
AWS上へのデプロイはServerless Frameworkを利用することができるようです。
ビルドしたデータをamplify publish
する方法もありそうですが、Next.jsを利用するならServerless Frameworkを利用しておくのが無難だと思います。
ルートディレクトリにserverless.yml
を追加します。
# serverless.yml nextamplified: component: "@sls-next/serverless-component@1.17.0"
ドキュメントではバージョンは1.16.0ですが、1.16.0だとThe parameter MinTTL is required.
と怒られてしまいます。
あとこの辺で躓いたのですが、tsconfigでincremental
をtrueにしていると文句いわれるかもしれません。
とりあえずtsBuildInfoFile
を書けば良さそうだったのでtsconfig.json
にtsBuildInfoFile
を"./.tsbuildinfo"
に設定する記述を追記しました。
たぶん以上の設定で準備は完了です。下記のコマンドでデプロイします。
npx serverless
ターミナル上にcloudfrontのURLなどの情報が出てきたら完了です。
ただし、上記の投稿した先のURL(ルートURL/posts/uuid
)はSSGを利用しているので、ビルド時にデータが無ければ表示されないと思います。
Serverless Frameworkを利用してデプロイするのが非常に簡単ですが、CI/CDを考えるとAmplify Consoleを利用してGitリポジトリからデプロイされる方式を採る方がいいかもしれません。