開拓馬の厩

いろいろやる

FuseBoxというイケてるmodule bundlerがあるらしい

FuseBoxというmodule bundlerがイケてるらしいので使ってみた github.com

どこらへんがイケてるのか

公式サイトGitHubのREADMEによると以下のような利点があるらしい。

  • 早い
  • デフォルトでTypeScriptやJSX記法に対応している
  • devサーバー内蔵
  • HMR(hot module replacement)がサポートされている
  • configが楽

実際に使った感想としては、configファイルが簡素な点、で専用のCLIを使わない点、後述のWebIndexPluginなどが主な魅力だと思われる。

実際に使ってみた

実際に使ったソースコードこちら

以降は、実際に書いたコードを提示しながら*1、FuseBoxの使い方を説明する。 「ただbuildするだけ→devサーバーを使う→pluginを入れる」のように後半になるにつれて高度な機能の説明になっていく。

とりあえずbuild

まず適当なtsファイルをbuildしてみる。src/ディレクトレリを作って、その中にindex.tsという名前で以下のソースコードを保存する。

// index.ts

const msg: string = "Hello World";
console.log(msg);

次にyarnを初期化して、fuse-boxtypescriptをインストールする。

$ yarn init
$ yarn add -D fuse-box typescript

READMEに習って以下のようなconfigファイルを書き、fuse.jsの名前で保存する。

// fuse.js
const { FuseBox } = require("fuse-box");

const fuse = FuseBox.init({
  homeDir: "src",
  output: "dist/$name.js",
  sourceMaps: true,
});

fuse.bundle("app").instructions("> index.ts");

fuse.run();

fuse.bundle()ソースコードを一つのjsファイルにまとめるコマンドで あり、.instructions()でビルドの起点になるファイルを指定する(webpack.config.jsでいうところのentry)。

設定ファイルを記述したら、以下のコマンドを実行する。FuseBoxはwebpack-cliの専用のCLIコマンドは用意されておらず、configファイルをそのまま実行する形式を取っている(npm i -gしたくない派の人喜びそう)。

node fuse

すると、dist/ディレクトリが作られ、中にビルド結果がapp.jsapp.js.mapの名前で出力される。

ちなみに今回のように、tsconfig.jsonを作らずに実行すると、src/以下に自動生成してくれる。 targetbaseUrlなどの設定項目はfuse.jsに書いた通りにしてくれるのでちょっと便利。

devサーバーを動かす

fuse.jsにdevサーバーを立てる設定を追記する。 ついでに出力されるjsとsource mapをブラウザ向けのものにしておく。

// fuse.js
const { FuseBox } = require("fuse-box");

const fuse = FuseBox.init({
  homeDir: "src",
  output: "dist/$name.js",
  target: "browser@es5", // ブラウザ向けのコードを吐かせる。ついでにバージョンもes5に指定
  sourceMaps: {vender: true}, // ブラウザ向けのsourceMapを出すようにしておく
});

// devサーバーの設定
fuse.dev({
    port: 8888 // port番号。デフォルトは4444。
    root: 'dist' // devサーバーのルートディレクトリ。デフォルトはFuseBox.init()の`output`で指定したディレクトリ。
});

fuse.bundle("app")
    .instructions(`> index.ts`)
    .watch() // tsを書き換えるたびに差分コンパイルを行う
    .hmr(); // hot module replacementを有効にする

fuse.run();

ES5で出力するようにしたので、index.tsもES6以降の機能を使ったやつにしてみる。

enum LogLevel {
    Info,
    Error,
}
type Message = { type: LogLevel; message: string };

const showMessage = (m: Message): string => {
    switch (m.type) {
        case LogLevel.Info:
            return `📢 ${m.message}`;
        case LogLevel.Error:
            return `⚠️ ${m.message}`;
        default:
            throw new Error("invalid message");
    }
};

const hello: Message = { type: LogLevel.Info, message: "Hello World" };
document.querySelector("body").innerHTML = showMessage(hello);

最後にdist/index.htmlを作成すれば準備完了。

<!-- index.html -->

<body>
  <div id="app"></div>
  <script src="/app.js"></script>
</body>

node fuseを実行し、localhost:8888にアクセスすると「📢 Hello World」が表示される。 ついでにapp.jsを確認すると、ES6やTypeScript特有の記法がES5に変換されていることが確認できる。

// app.js の抜粋
var LogLevel; // TypeScriptのenumで書いた箇所
(function (LogLevel) {
    LogLevel[LogLevel["Info"] = 0] = "Info";
    LogLevel[LogLevel["Error"] = 1] = "Error";
})(LogLevel || (LogLevel = {}));
var showMessage = function (m) { // ES6のarrow関数で書いた箇所
    switch (m.type) {
        case LogLevel.Info:
            return "\uD83D\uDCE2 " + m.message;

WebIndexPluginでindex.htmlを自動生成

前章ではindex.htmlを手動でdist/に置いた。しかし、dist/は自動出力専用にして、手書きのコードは他のディレクトリで管理したいという要求もあるだろうと思われる(私はそうしたい)。 そこで、WebIndexPluginというものを使ってそれを実現する。

まず、src/public/template.htmlを作る。

<!-- hemplate.html(Emmetのテンプレをちょっと書き換えただけ) -->

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>My Page</title>
</head>
$css<!-- ここにstyleタグが挿入される -->
<body>
  <div id="app"></div>
  $bundles <!-- ここにscriptタグが挿入される。設定次第で、src=""で別ファイルから読み込むかinlineか選べる -->
</body>
</html>

fuse.jsプラグインの設定を追加する。

// fuse.js

const { FuseBox, WebIndexPlugin } = require("fuse-box"); // WebIndexPluginもimportする

const fuse = FuseBox.init({
  homeDir: "src",
  output: "dist/$name.js",
  target: "browser@es5",
  sourceMaps: {vender: true},
  plugins: [WebIndexPlugin({ template: "src/public/template.html" })], // これを書き足す
});

実行するとdist/index.htmlが自動生成される。ちなみに、WebIndexPluinの引数でtemplateを指定しない場合、適当なhtmlファイルを自動生成が作られる。便利。

まだまだあるぞ

まだ色々紹介したい機能があるが、長くなったので記事を分けたい。 残っているネタは以下の通り。

  • AltCSSを使ってみた
  • JSXを使ってみた
  • fuse-box/sparkyで静的ファイルのコピーをしてみた

ついでにwebpackあたりとの比較もやってみたい(未着手)。

ただ、7月17日時点で、依存ライブラリに脆弱性が見つかっているようなので本格的に使うなら次のバージョンを待った方がよさそう*2f:id:pf_siedler:20190717180724p:plain

*1:清書の段階で修正した箇所もあるためgitのlogと不整合な箇所もある

*2:version 4の準備が進んでいるらしく、hot fixするのかv 4公開のタイミングで直すのかよくわからない。7月17日時点でdependencyのバージョンを上げるissue/PRは出てないのでcontributionチャンスかも?

弊社でCycle.jsに寄付した話

Open Collectiveを使って業務で使っているOSSライブラリに寄付をした話をする。

Cycle.js

会社でCycle.jsというフロントエンドフレームワークを使っている。

github.com

Cycle.jsはステート管理やをひとつの関数として実装して、 ブラウザ上の操作やHTTP通信などの"外界"の動作を関数外の処理に任せるというアーキテクチャを採用している。

f:id:pf_siedler:20190712162458p:plain
Webアプリはブラウザ操作やHTTPレスポンスを受け取り、ビューやHTTPリクエストを吐く関数である

弊社プロダクトのフロントエンド部分はほとんどCycle.jsで実装しており、大変お世話になっている。 そこで、Cycle.jsのOSSコミュニティに資金援助をしよういう話が社内であがった。

Open Collective

Cycle.jsはOpen Collectiveというサイトで寄付金を受け付けている。

opencollective.com

Open Collectiveは投げ銭サービスの一種で、主にGitHubOSSコミュニティ向けに作られている。

寄付する側での会員登録、寄付の方法を説明する。

会員登録

個人で会員登録をする場合、メールアドレスと名前を入力するだけで登録できる。 パスワード入力は不要で、登録メアド宛に送られてくるmagic linkを開くことでログインする。*1

法人として登録する場合、上記に追加して

  • 団体名
  • WEBサイトURL
  • GitHub Organizationの URL(任意)
  • Twitter ID(任意)

の入力を求められる。 GitHub OrganizationやTwitter IDを入力しておくと、団体のアイコンを自動で取得してくれるみたいなのでちょっと便利。

お支払い

次に寄付の方法を説明する。

まず寄付先のコミュニティのサイトを開き、「コントリビュート」のボタンをクリックする。

すると、コミュニティが指定した寄付のプランが表示される。 ここでプラン通りに寄付をしても良いが、ページ下部の「Or make a custom financial contribution」を選んで自分で寄付額を決めることもできる。

f:id:pf_siedler:20190712163138p:plain

自分で寄付額を決める場合、金額と寄付の頻度(1回、毎月、毎年の3パターン)を入力する。

f:id:pf_siedler:20190712163314p:plain
ちなみに通貨単位がユーロ建てになっているコミュニティもある。寄付金を決める際は為替を要チェック

寄付金の支払い方法は以下から選べる。

  • クレジットカード決済
  • PayPal*2
  • 銀行振込

銀行振込で海外送金するのは色々大変そうなので、実質クレカとPayPalの二択になると思われる。

実際に寄付してみた

社長にお願いしたら1000ドルぽんと出してくれた :sasuga_shacho:

そして公式サイトに載った。

f:id:pf_siedler:20190712163708p:plain

さいごに

Cycle.jsはかなりイケてるフレームワークなので、ガンガン開発してほしいですね。

herp.careers

herp.careers

*1:つまり、メールを受け取れる人が本人という形で本人確認を行っている。

*2:寄付先の団体が米ドル建てで寄付を受け付けている場合のみ?

開拓馬 バージョン 24.0.0を公開しました

24歳の誕生日なので去年からの差分をリリースノートっぽく書いた

  • 自力で生活費を稼ぐ機能を追加しました
  • TypeScriptを扱う機能を追加しました
    • Cycle.jsでフロントエンドを実装する機能を追加しました
    • stylusやpugを使ってある程度の静的サイトを記述する機能を追加しました
    • TypeORMを使ってDBにアクセスする機能を追加しました*1
    • fp-tsio-tsで素のTSより型安全なコードを書く機能を追加しました
    • circleCIのconfigを書く機能を追加しました
    • Jest, Mocha, TypeMoqを使ってテストコードを書く機能を追加しました
    • 稀にDefinitelyTypedにPRを出す機能を追加しました
  • 余剰資産を投資信託で運用するように変更しました from Folio
  • 研究意欲がないのに大学院に行ってしまう不具合を修正しました
  • レーニングジムに週1~2回通いヘルスケアに勤しむ機能を追加しました
  • コーヒーの好みを苦味が強いものから酸味の強いものに変更しました from LIGHT UP COFFEE
  • 認知行動療法を導入することで自己肯定感が急激に低下する不具合を解消しました from デビット・D・バーンズ
    • 何もしていないときに焦りを覚える現象が発生しづらくなりました
    • 根拠の無い自己肯定が有効であるという認識を導入しました
    • 「人間は何を成し遂げたとしても死んで無に帰す」という世界観を持つようになりました from 中島義道*2, ショーペンハウアー, シオラン
    • ↑を「優れた洞察力を持つ」と言い換ることでペシミズムをある種の長所と捉えるようになりました
  • 引っ越しによりロード時間*3を短縮しました
  • 食洗機を利用して時短家事を行うようになりました(めっちゃ便利)
  • 図書館を利用することで読書量を増やしました
    • web予約機能を利用するようになりました(イマドキの図書館は便利ね)
    • 蔵書リクエストを出すようになりました
  • Nintendo Switchで遊ぶようになりました from Nintendo
    • 昨年11月に購入してから400時間ぐらい遊んでる
  • スマホゲームに手を出すようになりました
  • SEKIROクリアしました*5
  • 欲しいものリストで誕生日プレゼントをねだる機能が追加されました

*1:機能を追加してません。昨日TypeORMでmysqlのdockerイメージぶっ壊したので

*2:中島氏の本は14歳の頃に必死で読んでいたが、大人になってまた違う形で解釈できるようになったような気がする

*3:通勤時間の意味

*4:DOTA2 Mod版は操作性がいまいちなのであんまりやってない

*5:一週目不死断ちエンドのみ。50時間ぐらいかかった

研究と鬱と休学の話

これはEEICアドベントカレンダー2日目の記事です。

研究する気のない人が大学院に入って精神状態がボロボロなった話とそこから這い上がった話をします。

TL;DR

  • 人は心が荒むとスピリチュアルに頼りがち。でも認知行動療法に頼ったほうがいい
  • つらくなったら学生相談室にGO
  • 「勉強をしたい」と「研究をしたい」は別。大学院は後者のための場所

研究室と研究生活について

学部4年(去年)の4月から、電子情報工学科のIIS-Lab(矢谷研究室)に配属されました。 その後、大学院に進学した後も在籍し続け、1年半ほどこの研で過ごしました。

IIS-Labの研究内容は公式サイトを見ればわかるので、研究室の雰囲気や学生の過ごし方を軽く説明すると、

  • 毎週水曜日に全体ミーティング、また週一で先生と1対1でミーティング(コアタイムはこれだけ)。
  • 一人1プロジェクト。解決すべき問題を探すところから実験計画まで、全部自分で決める。
  • 研究の進捗管理GitHubのissue、業務連絡はSlack。
  • 論文はOverleafで書く。共有リンクを渡せば、先生も上級生もわりとガッツリ添削してくれる。*1
  • 発表練習が手厚い。卒論発表や輪講の直前はミーティングで発表練習をやる。
  • 学生居室がキレイ。(ベンチャー企業のオフィス感)
  • 修士1年の夏に企業のインターンに行くことが推奨されている。
  • 1/3ぐらいが留学生。*2
  • 計算資源はMicrosoft Azure。研究室でパートナー契約的なものをしてるらしく、高いインスタンスを借りても大丈夫っぽい。(書き忘れていたので2019/01/15追記)

自分で考えたテーマで研究したい人、研究のモチベーションが高い人にはとてもいい環境だと思います。 逆にそうでない人をきっぱりと休学・退学させてくれるのもいいところなのかな……(ごまかしで卒業できてしまうのよりは間違いなく良い)

気の乗らない研究生活。鬱の傾向が現れる

そんな素晴らしい環境に身を置きながらも、研究生活を送るにつれて私の精神はすり減っていきました。

その原因は研究の進捗が芳しくなかったことです。 まず、研究テーマを見つけるという段階から躓きました。 どのような研究をやりたいかというビジョンを明確に持たないまま4年生になってしまったため、やりたい研究が思いつかないという状態に陥りました。 IIS-Labでは、研究テーマは各人が自分で決めるという方針だったため、テーマが降ってくる*3こときも期待できず、ただ途方に暮れる状態でした。 結局、なんとかひねり出した微妙なアイデアを元に研究っぽい何かをでっち上げるというような酷い卒業研究でした。 こんな具合で卒業研究は散々な結果だったのですが、大学院入試には受かっていたためそのまま大学院に進学し、修士の研究でもテーマが見つからないという問題に直面し……

毎週のミーティングで先生に報告できるような進捗を作らなければという重圧や、修論発表の期日までに成果を出せないかもしれないという漠然とした不安と戦う毎日でした。 こうした不安は大学にいる時間だけでなく、買い物している間やご飯を食べている間、寝る前の布団に潜っているときも、趣味の登山の最中ですら、常にこうした不安からは逃れられない状態でした。 自由な時間はたくさんありましたが、心から休める時間は1秒たりともありませんでした。

そんな生活を送っていたら不眠症になり、保健センター精神科*4で診てもらったところ「鬱の傾向*5がある」と言われました。

認知行動療法、マインドフルネス、スピリチュアル

こうして鬱の傾向にあることが発覚したのですが、そこから抜け出すために色々な「あがき」をしました。

まず認知行動療法に取り組みました。 認知行動療法というのは、出来事に対するネガティブな思い込み(自動思考)を言語化し、合理的な思考に修正することで精神を安定させる手法で、鬱やパニック障害の治療方法のひとつです。 精神科医デイビット・D・バーンズの名著「いやな気分よさようなら」や「フィーリングGoodハンドブック*6を読み込みました。 そこに記載されているノートテイキング手法を試したり、日々認知の歪みを意識して生活したりすることで、ネガティブな自動思考を徐々に和らげていきました。 他にも認知行動療法の亜種であるAcceptance Commitment Therapy(ACT)についても調べ、できる範囲で実践しました。

他にもマインドフルネス(瞑想)にも取り組みました。 マインドフルネスとは「何も考えない」状態を作ることでストレスを和らげる手法です。 基本的に道具を使わなくてもマインドフルネスは可能なのですが、ガイドがある方がやりやすいと考えたので、HeadSpacePauseといったアプリを使いました。 ちなみにHeadSpaceのようなMindfulness-based Applicationは学術研究の対象として注目されてきているようです。 コンピュータサイエンスを用いてWell-beingを追求するという目標の元、HCIと精神医学を組み合わせた学際的な研究は最近流行ってきているみたいです(マインドフルネスは決してスピリチュアルやオカルトのたぐいではないぞ)。 「ウェルビーイングの設計論*7」等の書籍に詳しく書いてあるので興味を持った方はご一読してみることをおすすめします。

他にも、南米のスピリチュアルな儀式*8を試したり(今思い返すと相当キテた)、 ちょっとした一人旅に行ったり*9、 半ばヤケになって色々試しました。

認知の歪みを意識すると気分が落ち込みにくくなるという発見は今でも役立ってます。

東大の学生相談ネットワーク

学内の学生相談ネットワークもフル活用しました。 大学生活を送るうちに心を病む人というのはわりと多いらしく、ほとんどの大学には学生相談室という施設が設置されています。 東大の場合、学生相談室の他に、精神保健支援室(保健センター精神科)、コミュニケーション・サポートルーム、なんでも相談室、ピアサポートルームという施設が存在し、まとめて学生相談ネットワークと呼ばれています。 私はピア・サポートルーム以外は全部利用して精神の安定させていきました。 せっかくなので使い方とかどんな雰囲気かというのを書いておきます。

  • 学生相談室
    • スクールカウンセラーの方と相談できる場所
    • 基本的には毎週 or 隔週ぐらいのペースで通院(通室?)する
    • 予約制
      • ネット予約だと1~2週間後になる場合が多い
      • 実は電話予約だと融通が効く場合がある
  • 精神保健支援室(保健センター精神科)
    • 精神科医臨床心理士に診察してもらえる
    • 初回はスクールカウンセラーの方と面談 and アンケート
    • 基本的に薬を処方してくれるだけ。カウンセリングはあまりやらない
    • 診察料は無料、薬代は自費。健康保険が使えず全額負担
    • 初回は窓口に直接行く。二回目以降は予約制
  • コミュニケーション・サポートルーム(コミュサポ)
    • 発達障害の支援 and 自己理解を深めるという位置付けの相談室
    • 基本は学生相談室とほぼ同じ
    • 会話の練習をしたり、知能試験を受けたりすることができる
      • WAIS 3という1回2万円の試験が無料*10
    • 発達障害の医学的な診断を受けることも可能
      • 診断を受ける場合、臨床心理士が両親に電話して子供の頃の様子を聞くというフェイズが挟まる
      • ちなみに私も受けてみたが、発達障害というよりは社交不安症の傾向らしい
    • 隔週ぐらいのペースで通院(通室?)、予約制なのは学生相談室と同じ
  • なんでも相談室
    • なんでも相談できるらしい
    • 一度行ったが、学生相談室を紹介されただけだった
    • 精神状態の悩みは「学生相談室 or コミュサポ行きましょうね」になるような気がする
  • 工学部学生相談室
    • 学生相談ネットワークの管轄ではなく、工学部が独自に設置している学生相談室
    • カウンセラーじゃなくて事務員的な人が常駐してる?
    • 1回行ったけど正直微妙だった

まとめると、保健センター精神科でおくすりをもらいつつ、学生相談室とコミュサポのどちらか or 両方でカウンセリングを受けるのが良いんじゃないかな。 悩みを言語化したり、自分の状態をより深く理解したりすることはとても大事です。

どうやら自分は研究をしたいわけではないらしい

そんなもろもろの「あがき」や学生相談ネットワークの支援によって現在の私の精神状態はかなり安定しています。 そして今は何をしているかというと、大学院を休学してスタートアップ企業でエンジニアをしています。 今やってることについては他の記事*11で語るとして休学に至った心境についてお話します。

鬱の傾向が晴れて今の自分の状況が冷静に捉えられるようになった私は極めて単純な心理に気付きました。 つまるところ、自分は研究をやりたいわけではなかったのです。 大学に入ったときは講義を受けて色々なことを学びたいと考えていましたし、大学院入試をした動機の中には他の研究者が書いた論文を読むのが好きという要素も多少ありました。 しかし、自分が論文を書いて他の研究者に読ませることには全く興味がないということに気付きました。

本当にやりたいことは研究ではなかった、他にやりたいことがあるはず、仮にやりたいことがまったくなく自分の人生に意味がないとしても研究するよりはマシな時間の潰し方はある。。。 と、あれこれ考えるうちに、嫌々ながら研究を続けるより休学してエンジニアとして働いたほうが良いのではないかという結論に至りました。

  • 自分はお金とスキルを得られてうれしい
  • 先生は私の指導に当てていた時間を自由に使えてうれしい
  • 企業は私という労働力を得られてうれしい

などなど、私が休学して働くことであらゆるリソースが正しく使われるようになるわけです。*12 もちろん修士卒の肩書が遠のくというデメリットもありますが、研究に対するモチベーションが低い自分が、なんちゃってMaster of Engineeringで不当に高く評価されるのは、今後の人生において良い結果を産むはずがありません。

そんなことを考えた私は、次の日には休学届を完成させ、次の日には現在のインターン先に面接に行き(あっさり採用してくれた弊社に感謝)、あっという間にエンジニアに転生していました。 こうして研究する気がない学生が一人消え、人手不足のIT業界に働き手が一人増え、世界は少し良くなったとさ、めでたし。めでたし。

*1:研究室見学では論文執筆環境はあんまり語られることがないが、それなりに重要な要素だと思う

*2:今年秋入学で3人入った。彼らが秋入学したのと入れ違いで休学したので、どんな人なのかあまり知らない

*3:教授や院生が研究テーマを考え下級生にやらせる現象

*4:学内の病院的な施設

*5:精神科医による診察結果であるが、診断書が発行されるレベルの本格的な診断ではないため「傾向」というファジーな表現がされている

*6:この2冊は鬱の状態で読むには内容的にも物理的にもやや重いのでそれっぽい入門書を図書館で借りたほうがいいかも

*7:この本は私が発注した(ドヤ顔)ので工学部2号館図書館に蔵書されている

*8:瞑想をして精霊を呼ぶ的なやつ

*9:レンタカーで四万温泉にドライブして事故る(駐車場で隣の車を擦った)。レンタカー屋の保険2100円で全部解決できた上に警察に通報して事故の後処理をするという貴重な体験ができたのは良い買い物だった

*10:バラマキではなく医学研究の一貫である。念の為

*11:退学アドベントカレンダー13日のエントリ

*12:このあたりやたら大げさに書いているが、休学決めた日は鬱の傾向が裏返って軽い躁状態になっていたのか「俺は休学して世界を正しくするぞおおおおおぉ」と本気で考えていた

クックパッドインターンでちょっぴり成長した

10 day 技術インターンシップに参加してちょっぴり*1成長してきました。かなり楽しかったので体験記を書きます。

internship.cookpad.com

前半5日間

前半の5日は講義形式でwebアプリケーションの開発について学びました。

初日はオフィス見学と環境構築で、2~5日目で以下のようなことを学びました。

  • 事業開発
  • サーバサイド(Ruby on Rails)
  • フロントエンド(React Native、Typescript)
  • クラウドインフラ(AWS、Docker 内製デプロイツールの itamaehako のハンズオン*2 )

個人的にはクラウドインフラの回が一番楽しかったです。 EC2やRDSのインスタンスを立てたり、ロードバランサを置いて負荷分散をしたりといったことを体験しました。 これらは学生が個人でやるには限界があり、ここで体験できたのはとても良かったと思います。 サーバの設定内容をコードの形式で保存しChefツールで環境構築を自動化することで、サーバ環境構築の冪等性を確保するという概念を知ることもできました。

また、実習として、ISUCON形式でアプリケーションの処理速度をインターン参加者同士で競うということも行われました。 私は2位に入賞することができ、賞品としてレシピ本をもらいました🥈

後半5日間

後半はOJT*3コースとPBL*4コースに分かれて参加者ごとに異なる活動をしました。 OJTは実際にクックパッドの開発現場に参加するコースで、PBLは与えられた課題を解決するようなアプリケーションをチームで開発するコースです(やや期間が長いハッカソンのようなもの)。 私はPBLコースに配属され、「食材の購入を支援するアプリを作る」という課題に3人のグループで挑戦しました。

我々のチームは「休日に1週間分の食材を買い込んで、平日はそれらを使って料理する」というシナリオを想定し、1週間分の献立を決めると必要な食材の総量を計算してくれるアプリを作成しました。 自分は主にクライアントサイドの開発担当としてひたすらReact Nativeを書きました。 React Navigationで画面遷移を実装したり、Native BaseでUIを設計したり、コンポーネントはできる限りSFC*5になるように書いて使いまわしやすくしたり実践的なReact Nativeの開発ができました。

ただ、ライブラリの選定や設計方針を予め相談した方が良かったのかもしれません。今回のアプリではヘッダ部分をReact Navigationが生成するものを使いましたが、個人的には使いづらいように感じる部分もありました。 React Native Navigation等の他のライブラリを使用する、ヘッダ部分もNative Baseで実装するという選択肢をあまり考慮せず、なんとなくで決めてしまったのは良くなかったと思います。

仮に今回作ったものを実際に事業として動かすのであれば、デザイン面*6と技術面*7での課題を解決するためにユーザインタビューや機械学習モデルの構築などが必要で、事業開発のほんの一部分しか体験できていないようにも思いました(インターンの期間中にユーザインタビューまでやるのは無理そうだけど)。

参加前はOJTコースで現場の業務環境を体験したいと考えており、PBLコースに配属されたのを残念に思っていました*8。 しかし、アプリの設計から実装まで技術レベルが同じぐらいのメンバーでグループ開発するという体験はそんなにできることではないため、それはそれで良い体験になったのではと思います。

感想

いろいろなことが体験できた10日間でした。 Ruby on RailsやReact Nativeといったフレームワークを初体験したり、インフラエンジニアリングという分野のことを知ったりすることができました。

正直、今回のインターンそれ自身による成長はそこまで大きくは無いと思います。本当に成長するのは今回触れた技術をアルバイトなどで活用したときになるのではと思います。 しかしながら、今回のインターンを通じてweb系エンジニアの方々の働き方を垣間見たり、同期の学生達が様々なことに挑戦していることを知ったりして、勉強していくモチベーションが高まりました。

このインターンで成長したというよりは、今後成長する足がかりができた感じがしました。(植物に例えるなら、幹が伸びるというよりは新しい枝が生えたのかな)

学んだこと

ほぼ初見でしたが、2週間で多少使えるレベルにはなったかなと思います。 特にReact NativeはPBLコースの5日で沢山書いたので、その気になれば自力でアプリひとつ作れる程度にはなったかな? Typescriptやweb版のReact.jsの理解も深まりました。

  • 新規事業開発

企業がどのようにして新規事業を立案したり、ユーザのフィードバックを得たりしているのかを体験できました。大学では得られないものだったと思います。 研究のテーマ探しやユーザスタディなどでも役に立ちそうだと思いました。

新しく興味を持ったこと

  • インフラの最適化 ISUCON

AWSへのデプロイや、クラウドインフラの最適化は独学では体験しにくい分野なので体験できてよかったと思います。 インフラエンジニアという職種を知ることもできました。 また、社内ISUCONを体験して本家ISUCONにも興味が湧きました。

  • Rust

インターンの内容とは無関係ですが、参加者にRustaceanがいて布教されました。 Web Assemblyと組み合わせて使ってみたいです。

その他

クックパッドはオフィスにキッチンがある変わった会社です。 そのキッチンを使って、インターン参加者や社員の皆さんとごはんを作りました。 大勢で料理をしてみんなで食べるというのはとても楽しかったです。それに料理も美味しかったです。餃子とか唐揚げとか中華スープとか🥟 f:id:pf_siedler:20180913154158j:plain 3日目の餃子パーティー f:id:pf_siedler:20180913154204j:plain 最終日の懇親会

*1:圧倒的成長というが界隈で使われていますが、「圧倒的」という部分に嘘っぽさとブラック労働的な苦労自慢のニュアンスが感じられて好きではないので「ちょっぴり成長」したことにします

*2:どちらもオープンソースとして公開されている

*3:On-the-Job Training

*4:Problem Based Learning

*5:Stateless Functional Component

*6:献立を立てる→必要な食材を計算というコンセプトなので、献立を立てるという体験を楽しくさせるデザイン上の工夫が必要だと考えられる。また、世間一般の消費者は休日にまとめ買いや献立を予め決めるといった行動をどの程度行っているのかを分析し、このアプリが本当に必要とされているか見極めなければならない

*7:クックパッドのレシピに記載されている材料欄をもとに買うべき食料を算出しているため、食材を1人分に正規化したり、「適量」とか「お好み」とかいった表現をいい感じの数量に変換したり等で、提示する食材量の妥当性を確保する必要がある

*8:OJTコースのみ時給が支払われるという待遇の違いもあったが、その点について不満はなかった

Python & Serverless FrameworkでLambdaファンクションをサクッと作ってデプロイする

Serverless FrameworkAmazon AWSの開発&管理を補助するフレームワークです。 最近アルバイト先でserverlessを用いたアプリケーションに関わり、その際に使いかたを軽く勉強したので、ブログに書き残しておきます。

環境

  • Node.js 10.6.0
  • Python 2.7.15
  • serverless 1.29.2

Installation

ServerlessはNode.jsのアプリケーションとして配布されており、npmコマンドからインストールできます。

npm install -g serverless

# 確認
serverless --help
sls --help # slsという短縮コマンドが自動でエイリアスされるらしい

サービスを作ってデプロイ

インストールが完了したので、簡易的なLambdaのファンクションを作成してAWSにデプロイするところまでやってみましょう。 今回は毎朝8時にLINE Notificationを送ってくれる目覚ましファンクションを作ってみます。

サービスの新規作成

sls createコマンドでサービスを新規作成できます。 このとき-tオプションでアプリのテンプレートを、-pオプションでサービスを保存するパスを指定できます。 テンプレートはaws-python*1、保存先は適当な作業ディレクトリ上のlambdaMezamashiというディレクトリにします。

cd /path/to/working/dir #適当な作業ディレクトリに移動
sls create -t aws-python -p lambdaMezamashi
cd lambdaMezamashi
ls
# => handler.py   serverless.yml

コマンドを打つと、lambdaMezamashiというディレクトリが作成され、handler.pyserverless.ymlという2つのファイルが自動生成されます。 handler.pyはLambdaのファンクションを書くファイルで、serverless.ymlは設定ファイルになります。

とりあえず動かす

アプリが完成したのでとりあえず動かしてみます。 テンプレートにはhelloという関数名で簡単なメッセージを返す関数が定義されています。 sls invoke local -f {関数名}で指定した関数名の関数をローカル環境で実行できます。

sls invoke local -f hello
# => {
# =>     "body": "{\"input\": {}, \"message\": \"Go Serverless v1.0! Your function executed successfully!\"}", "statusCode": 200
# => }

LINE Notificationを送る関数を記述

LINE Notificationの使いかたはこちらの記事でも解説しています。

まず、LINE Notifyのページからアクセストークンを取得し、環境変数LINE_NOTIFY_TOKENに書き入れます。

export LINE_NOTIFY_TOKEN=XXXXXXXXX # bash zsh等の場合
set -x LINE_NOTIFY_TOKEN XXXXXXXXX # fishの場合

次に以下のようにhandler.pyを書き換え、LINE NotifyのAPIを叩くコードを追加します。

# -*- coding: utf-8 -*-

import os
import json
import requests

def mezamashi(event, context):
    output_url = "https://notify-api.line.me/api/notify"
    token = os.getenv("LINE_NOTIFY_TOKEN", "")

    headers = {"Authorization" : "Bearer "+ token}
    payload = {"message" :  "こんにちは"}

    response = requests.post(output_url ,headers = headers ,params=payload)

    return response.json()

def hello(event, context):
    body = {
        "message": "Go Serverless v1.0! Your function executed successfully!",
        "input": event
    }

    response = {
        "statusCode": 200,
        "body": json.dumps(body)
    }

    return response

次にpipコマンドでrequestsをインストールします。

pip install requests

次にserverless.ymlを書き換え、mezamashi関数に関数名を割り当てます。

service: lambdaMezamashi

provider:
  name: aws
  runtime: python2.7

  environment:
    # LINE Notifyのアクセストークンを記入
    # invoke localで実行する場合はこれを記述しなくても動作するが、デプロイ時に必要となる
    LINE_NOTIFY_TOKEN: XXXXXXXXXXXXXXXX

functions:
  hello:
    handler: handler.hello
  mezamashi:
    handler: handler.mezamashi

そうしたら、serverless invoke localを用いて動作確認を行います。

sls invoke local -f mezamashi
# => {
# =>     "status": 200,
# =>     "message": "ok"
# => }
# (LINEに通知が来る)

スケジュール実行の設定

Lambdaファンクションの実装が完成したので、これをスケジュール実行するように設定を書きます。 serverless.ymlfunctionsの部分にイベント設定を追記するだけです。

functions:

    #<中略>

mezamashi:
    handler: handler.mezamashi
    events: # イベントの設定を記述
      - schedule: cron(0 23 * * ? *) # cron式で日本時間の朝8時を指定

ついでにAPI Gatewayも使う

LambdaはAPI Gatewayと組み合わせることでwebアプリのバックエンドとして使用するケースもよくあります。 少し脱線しますが、ymlを用いてLambdaファンクションをAPI Gatewayと結びつける方法も説明しておきます。

eventsの項目に- http: {メソッド} {APIのURI}の形式で記述します。 こうすることでデプロイした際に自動でAPI Gatewayが作成されます。

functions:
  hello:
    handler: handler.hello
    events:
      - http: GET hello

オフラインでテストする

ファンクションが書けたので早速デプロイと行きたいところですが、その前にローカルホストでLambdaファンクションをホストして、APIのテストを行う方法を解説します。 これは、業務でデバッグAPIのテストコードを走らせる場合にも必要なことなので覚えておいて損はありません。

まず、必要なプラグインをインストールします。インストールはsls plugin install -n {プラグイン名}で行います。どうやらこのコマンドはnpm i --save-devをラップしてnode_moduleをインストールしつつ、serverless.ymlプラグイン情報を追記してくれるものらしいです。

sls plugin install -n serverless-offline
npm install serverless-offline-python --save-dev # slsコマンドからインストールできないためnpmを直接使う

次にserverless.ymlの末尾に以下のように書き込んでserverlessとプラグインを紐付けます*2

plugins:
  - serverless-offline
  - serverless-offline-python

serverless-offlineserverless-offline-pythonをインストールしたことで、serverless offlineというコマンドが使えるようになります。これによりローカル環境でcurlコマンド等を用いてAPIデバッグが可能になります。

sls offline
#=> Serverless: Starting Offline: dev/ap-northeast-1.
#=>  (中略)
#=> Serverless: Offline listening on http://localhost:3000

# =====他のterminal窓から以下を実行=====

curl localhost:3000/hello
#=> helloファンクションが動く

ちなみにserverless-offline-schedulerというオフライン環境でスケジュール実行のデバッグができるプラグインがありますが、Node.jsで書いたものしか動かないみたいです*3

デプロイする

最後にデプロイの方法を説明します*4

AWSアカウントの設定

最初にAWSアカウントの設定をします。

AWSのマネジメントコンソールにログインし、IAMから新規ユーザーを追加します。 f:id:pf_siedler:20180822104021p:plain f:id:pf_siedler:20180822104717p:plain

ユーザー名を適当に設定し、「プログラムによるアクセス」にチェックを入れます。 f:id:pf_siedler:20180822104040p:plain

「既存のポリシーを直接アタッチ」から「AdministratorAccess」を選択します*5 *6f:id:pf_siedler:20180822104044p:plain

ユーザーの作成が完了したら、「アクセスキーID」と「シークレットアクセスキー」を控えておきます。シークレットアクセスキーはこのときにしか確認できないため、忘れた場合には再度アカウントを作成する必要があります。 f:id:pf_siedler:20180822104034p:plain

これを、sls config credentialsコマンドを用いて設定します。XXXXXの箇所は適宜アクセスキー、アクセスキーシークレットに書き換えてください。

sls config credentials --provider aws --key XXXXX --secret XXXXX

デプロイ、実行、削除

アカウントの設定が済んだら遂にデプロイです。

Serverless Frameworkはsls deployコマンドを実行するだけで簡単にデプロイできるようになっています。 しかし、今回のようにPythonで実装している場合は、自分で書いたコードだけでなくサードパーティ製のライブラリも一緒にデプロイする必要があります。

serverless-python-requirementsというプラグインをインストールするとデプロイ時に自動でこの作業をやってくれるため、今回はこのプラグインを利用することにします。

sls plugin install -n serverless-python-requirements # プラグインのインストール
pip freeze > requirements.txt # 依存関係をrequirements.txtというファイルに書き出す

上記のコマンドを実行したら、デプロイします。

sls deploy
#=> Serverless: Installing requirements of requirements.txt in .serverless...
#=> Serverless: Packaging service...
#=> (中略)
#=> service: lambdaMezamashi
#=> stage: dev
#=> region: ap-northeast-1
#=> stack: lambdaMezamashi-dev
#=> api keys:
#=>   None
#=> endpoints:
#=>   GET - https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello
#=> functions:
#=>   hello: lambdaMezamashi-dev-hello
#=>   mezamashi: lambdaMezamashi-dev-mezamashi

これで完成です。毎朝8時にLINEに通知が来るようになりますし、https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/helloURIcurlコマンドやAjax通信でアクセスするとhelloファンクションをWeb APIとして叩くことができます。

デプロイしたアプリの削除も簡単に行うことができ、sls removeコマンドを実行するだけです。

sls remove
#=> Serverless: Getting all objects in S3 bucket...
#=> Serverless: Removing objects in S3 bucket...
#=> Serverless: Removing Stack...
#=> Serverless: Checking Stack removal progress...
#=> .............................
#=> Serverless: Stack removal finished...

さいごに

今回はServerless Frameworkの使い方を軽く説明しました。 Serverless Frameworkは他にもDynamoDBやS3といったサービスを操作することができ、その名の通りサーバレスなwebアプリケーションを構築することができます。

S3でwebページをホストし、Lambda & API Gatewayでバックエンドの処理を行い、DynamoDBやCognitoでユーザ情報を管理するという構築にすればどんなWebアプリでも作れそうですね。

参考資料

*1:was-python3だとローカルで動かす際にpyenv等を用いてver 3.6のPython環境を用意する必要があります。今回は面倒なので2.x系で我慢

*2:# sls plugin ~~ コマンドを走らせた際に自動で追記してくれるため、serverless-offline-python以外は不要かも

*3:実際にソースコードを確認したところ、Lambdaファンクションが記述されたjsファイルをrequireで読み込んで実行するという実装がされているようだった https://github.com/ajmath/serverless-offline-scheduler

*4:これ以降の部分で解説するコマンドを実行するとLambdaファンクションがAPI Gatewayを通して全世界から実行可能な状態になります。Lambdaファンクションに脆弱性が存在した場合には思わぬ被害を被る可能性があることを心に止めておいてください。

*5:公式ドキュメントでもAdmin権限を設定する(https://serverless.com/framework/docs/providers/aws/guide/credentials/)という解説されています。

*6:Serverless Frameworkは開発中でやれることがどんどん増えています。そのためAdmin権限でないと機能に制限がかかり、開発が面倒になる可能性があります。Amdin権限で開発しつつ、製品を公開する際に権限を必要最低限に絞るような運用が望ましいと思われます。

ほとんどのエンジニアには解けるらしいパズルを解いてみた

ジャバ・ザ・ハットリ氏作成の「ほとんどのエンジニアには解けるパズル」*1シリーズを全問解いたので感想とかwrite upとかを書きます。

出題者のブログはこちら

問題一覧はこちら(全8問)

目次

感想

問題はマイルドなCTFみたいでした。CTFはakictfksnctfの問題をちまちま解いたり、SECCONの過去問を漁ったりしていたので、こうした形式のパズルには多少慣れていました。ここに上げたサイトの問題と比較すれば「ほとんどのエンジニアには解けるパズル」は簡単な部類なのかなと思います。

解くにあたってcurlコマンドの基礎を学ぶことができたのは収穫でした。もともとcurlコマンドに対する理解は「-Oコマンド付けてファイルを落とすやつでしょ」ぐらいだったので、一般的なサーバにリクエストを投げるという使い方を確認することができました。

その意味では、最初の3問は有意義でしたが、第4問以降は単なる数学パズルの色合いが濃く、学習という観点ではそこまで得るものはなかったのかもしれません。ただ、数学パズルやCTFの練習問題としては結構楽しかったと感じています。

Write up(ネタバレ注意)

復習、忘備録を兼ねてここに解法を残して置こうと思います。ここから先はネタバレが含まれているため、自力で解きたい方は読まないようにお願いします。

実のところ問題作成者はネタバレ非推奨と発言しているため、ここに解法を書くのはあまり褒められた行為ではないのかもしれません。しかし、curlコマンドの使い方等の技術的な知識は、

  1. 「未知の知識が必要な場面が生じる」
  2. 「使い方を調査する≒答えを見る」
  3. 「実際に使う」
  4. 「次回以降マニュアルを見ずに使えるようになる」

というプロセスを踏んで覚えるものであると私は思います。3の後に後戻りせず4に到達できるのであれば、2で閲覧する答えが解説書であっても、info curlの出力であっても、本記事のネタバレであっても本日的には変わらないのでは?むしろ、答えが参照しやすくなっている方が2の段階でつまずくことがなくなっていいのでは?という考えからここに解法を載せることにしました(一応ボタンを押さないと表示されないようにはしておきます)。

解説はHTTP通信などをほとんど知らない人が読んでもふんわりと理解できる程度には詳しく書くように心がけました。解いているうちに詰まってしまったら読んでみるのもいいのかもしれません。curlの使用例を知りたいという人も解説を見ながら第1問を解いてみるのもいいかもしれません。

第1問

第2問

第2問以降は暇があったら書きます(第1問程度の文量で書くと結構長くなりそうなので)。

*1:○○なら誰でもできるっていう釣りタイトル個人的にはきらい