GoでWebアプリを作ろう 第一回 : Goで簡単なCRUD
こんにちは、Andyです。
普段はフロントエンドチームでJSばかり書いているのですが、せっかくGoの会社に入ったので良い機会だと思いGoに入門してみました。「Goの作法」を知ればより裏側のシステムについての理解が深まり、フロント側も良いプロダクトが作れるんじゃないかなと期待しています。
せっかく新しい言語を学ぶので、学習の中でやった事や詰まった事を文字で残そうというのが本記事の目的。
とてもじゃないですが1回で全てをカバーできないので数回に分けてチャレンジします。
手探りで自分なりのベストプラクティスを模索している最中なのでマサカリ大歓迎です。
現在のスタック
学習を始めるにあたって、自分のエンジニアとしてのスタックはこんな感じ。
- Ruby on Rails, ES6 (業務レベル)
- PHP, Perl, Python (趣味レベル)
ちなみにgolangの経験値はA Tour of Goを流し読みした程度です
作るもの
簡単なTODOアプリです。次回以降でユーザー認証機能も乗っけていきます。
2018年なので当然*1フロントはSPAで受ける事を考えて、JSONのI/Oを受けるAPIサーバーを想定しています。
またルーティングにGinを利用しています
プロジェクト構成
Railserとして一番始めにつまづくのがGoのプロジェクト構成です。いろいろと宗派があるようなので詳しくは触れません。
$GOPATH
は $HOME/dev/go
に設定しています。プロジェクトは $Home/dev/go/github.com/andoshin11/go-web-app
に配置しました。
アーキテクチャについてはDDDやその他のClean Architectureをベースとしたものも検討したですが、初めてのアプリなのでわかりやすくMVC(or MC?)デザインパターンを採用。
最終的なファイル構成はGitHubを参考のこと
パッケージ管理
依存関係の管理にはdepを利用
$ brew install dep $ dep init
Hello, world!
まずは基本となるControllerを src/controller
以下に作成します
// src/controller/index.go package controller import ( "net/http" "github.com/gin-gonic/gin" ) // IndexGET displays application index page func IndexGET(c *gin.Context) { c.String(http.StatusOK, "Hello, world!") }
上記をHandlerとして登録
// main.go package main import ( "github.com/andoshin11/go-web-app/src/controller" "github.com/gin-gonic/gin" ) func main() { router := gin.Default() router.GET("/", controller.IndexGET) router.Run(":8080") }
$ go run main.go
でサーバーを起動
ページが表示されました👏👏
Databaseの用意
DBはMySQLを利用していきます。
$ mysql -u root mysql> CREATE DATABASE gwa;
なにかと衝突しそうな名前だけどとりあえずDBを作成。
$ go get github.com/pressly/goose $ mkdir db $ touch db/dbconf.yml
// db/dbconf.yml development: driver: mymysql open: tcp:localhost:3306*gwa/root/hogehoge
DBの情報が正しいか疎通確認
$ goose status goose: status for environment 'development' Applied At Migration =======================================
テーブルの作成
gooseで task
テーブルを作成します
$ goose create createTask sql
上記のコマンドを実行するとタイムスタンプを名前に含んだマイグレーションファイルが db/migrations
以下に作成されるので、そちらにSQLを追記
-- db/migrations/2018......createTask.sql -- +goose Up -- SQL in section 'Up' is executed when this migration is applied CREATE TABLE IF NOT EXISTS task ( id INT UNSIGNED NOT NULL, created_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), updated_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), title VARCHAR(255) NOT NULL, PRIMARY KEY(id) ); -- +goose Down -- SQL section 'Down' is executed when this migration is rolled back DROP TABLE task;
$ goose up
を実行してマイグレーションを実行し、テーブルが作成されていることを確認します
ORM(?)の選定
MySQLとの接続をラップしてくれるORMとしてGORMやsqlxなどを検討したものの、
「いちいちstructのメンテやってられんわ」
という強い理由によりxoを採用しました。
現実問題としてファットモデルも避けたかったし、基本的なCRUDを記述することだけ考えると必要十分な気がします。
$ go get github.com/knq/xo $ mkdir src/model $ mkdir -p db/xo/templates $ cp $GOPATH/src/github.com/knq/xo/templates/* db/xo/templates/ $ xo mysql://<user>:<pass>@<host>/<db> -o src/model/ --template-path db/xo/templates/
上記の手順を実行すると src/model/**.xo.go
というファイルが作成されます。 goosedbversion.xo.go
は必要ないので削除して大丈夫です。
Modelの接続
xoコマンドで各テーブルのmodelファイルが作成されたのでアプリケーションに接続していきます。まずは src/model/model.go
を作成してDBインスタンスを返す関数を記述。
// src/model/model.go package model import ( "database/sql" "log" "os" // mysql driver _ "github.com/go-sql-driver/mysql" ) // DBConnect returns *sql.DB func DBConnect() (db *sql.DB) { dbDriver := "mysql" dbUser := "root" dbPass := os.Getenv("MYSQL_ROOT_PASSWORD") // 環境変数から取得 dbName := "gwa" dbOption := "?parseTime=true" db, err := sql.Open(dbDriver, dbUser+":"+dbPass+"@/"+dbName+dbOption) if err != nil { log.Fatal(err) } return db }
続いてTODOの一覧を返すコントローラーアクションを記述。
xo
の設定をしておいてなんですが、今回だけControllerにデータ取得処理を直接書きました。本来なら template
を拡張すべきですが、実際のユースケースとして任意のテーブルのレコードを全取得するケースは中々ないので無視。
// src/controller/task.go package controller import ( "database/sql" "fmt" "net/http" "time" "github.com/andoshin11/go-web-app/src/model" "github.com/gin-gonic/gin" ) // TasksGET returns list of tasks func TasksGET(c *gin.Context) { db := model.DBConnect() result, err := db.Query("SELECT * FROM task ORDER BY id DESC") if err != nil { panic(err.Error()) } tasks := []model.Task{} // iterate result for result.Next() { task := model.Task{} var id uint var createdAt, updatedAt time.Time var title string err = result.Scan(&id, &createdAt, &updatedAt, &title) if err != nil { panic(err.Error()) } task.ID = id task.CreatedAt = createdAt task.UpdatedAt = updatedAt task.Title = title tasks = append(tasks, task) } fmt.Println(tasks) c.JSON(http.StatusOK, gin.H{"tasks": tasks}) }
返り値はリスト形式のJSONです。 main.go
で新たなnamespaceを作成して上記のHandlerを登録
// main.go ... func main() { router := gin.Default() // API namespace v1 := router.Group("/api/v1") { v1.GET("/tasks", controller.TasksGET) } router.GET("/", controller.IndexGET) router.Run(":8080") }
サーバーを再起動してエンドポイントが有効なことを確認。当然結果は空っぽです。
雑にレコードを突っ込んでレスポンスを再確認
良さそう
タスクを追加する
そういえば自動採番の設定を忘れていたので新しいmigrationを追加
$ goose create autoIncrementTask sql
// 2018....autoIncrementTask.sql -- +goose Up -- SQL in section 'Up' is executed when this migration is applied ALTER TABLE task MODIFY id INT UNSIGNED AUTO_INCREMENT NOT NULL; -- +goose Down -- SQL section 'Down' is executed when this migration is rolled back ALTER TABLE task MODIFY id INT UNSIGNED NOT NULL;
$ goose up
xoのアップデートも忘れずに行います。
task
コントローラーにHandlerを追加
// src/controller/task.go ... // TaskPOST creates a new task func TaskPOST(c *gin.Context) { db := model.DBConnect() title := c.PostForm("title") now := time.Now() task := &model.Task{ Title: title, CreatedAt: now, UpdatedAt: now, } err := task.Save(db) if err != nil { panic(err.Error()) } fmt.Printf("post sent. title: %s", title) }
// main.go ... v1.POST("/tasks", controller.TaskPOST) ...
DBにレコードが追加されるようになりました
タスクの更新
新しいHandlerを task
コントローラーに追加
// src/controller/task.go ... // TaskPATCH updates a task func TaskPATCH(c *gin.Context) { db := model.DBConnect() id, _ := strconv.Atoi(c.Param("id")) task, err := model.TaskByID(db, uint(id)) if err != nil { panic(err.Error()) } title := c.PostForm("title") now := time.Now() task.Title = title task.UpdatedAt = now err = task.Update(db) if err != nil { panic(err.Error()) } fmt.Println(task) c.JSON(http.StatusOK, gin.H{"task": task}) }
// main.go ... v1.PATCH("/tasks/:id", controller.TaskPATCH) ...
更新成功!
タスクの削除
同じ要領でコントローラーに追記
// src/controller/task.go // TaskDELETE deletes a task func TaskDELETE(c *gin.Context) { db := model.DBConnect() id, _ := strconv.Atoi(c.Param("id")) // Check if record exists task, err := model.TaskByID(db, uint(id)) if err != nil { panic(err.Error()) } err = task.Delete(db) if err != nil { panic(err.Error()) } c.JSON(http.StatusOK, "deleted") }
// main.go ... v1.DELETE("/tasks/:id", controller.TaskDELETE) ...
削除も問題なくできました。
まとめ
複雑な機能は何もありませんがとりあえずのAPIサーバーができました。Go楽しいですね。
第二回以降はログイン機能とかモデルの関連付けとか作業の自動化とかやりたいです。
それではまた次回
*1:text/templateとか使ってる人類いるんですか?
君は2クリックでVue appを立ち上げたことがあるか!!<CodeSandboxを世の中に広めなければと思った件>
ちょっと時間が経ってしまったのですが、2月にアムステルダムで行われたVue conf AmsterdamとFrontend Developer Love conferenceに参加してきました。
初めての技術カンファレンス&海外でのイベントということで学ぶことばかりの数日間だったのですが、今回はその中でも特に印象に残ったセッションをご紹介します。この記事を読んで「CodeSandboxおもしろそう!」「使ってみたい!!」という方が1人でも増えると嬉しいです。
CodeSandboxとは
CodeSandboxとはオンラインで動くコードエディターで、ReactやVue、Angularなどのフレームワークですぐにアプリケーションを作成できるのが特徴です。
GitHubでオープンに開発が行われているのですが、なにより驚いたのがプロジェクトリーダーのIvesがまだ21歳の学生であること!まだ日本ではあまり知られていないプロダクトですが、将来がとても楽しみなのでこのCodeSandboxの便利な機能をいくつかご紹介したいと思います。
2クリックでアプリを立ち上げる
まずはCodeSandboxのページを開き、右上の"Create Sandbox"をクリックします
するとフレームワークを選択する画面が出てくるので、Vueのアイコンをクリックします
完成です👏👏
Lower the learning curve.
Installing tooling should not stand in the way of getting started
という言葉にもあるように、CodeSandboxでは「コードを書きたい!」と思った人がなるべくスムーズに開発できよう徹底的にしきいを下げる工夫が行われています。
画面に並んでいるのは左端から順にファイルツリー、エディタ、ミニブラウザです。
もちろんここにNPMパッケージを追加したり import/export
を記述することも可能です。
試しに簡単なButtonコンポーネントを作成してみましょう
propsで受け取った text
を表示するだけのbuttonです
ローカルで開発するのと全く同じ感覚で表示までできました
画面下部のコンソールにエラーも出力してくれます
外部ライブラリを読み込もうとすると怒られますが、
ボタンをクリックするだけで自動でライブラリがダウンロードされ、 package.json
にも追記してくれます。
またCodeSandboxの特徴としてライブラリの設定ファイルをGUIからconfigできる機能があります。各種設定の概要を確認しながら調整できるのでなかなか便利です
さらにさらに、ライブラリの追加もGUIの検索ボックスから行えます(バージョンも指定できる)。このお手軽感めっちゃ好き❤️
Encourage sharing & discovery
CodeSandboxで作成したアプリは簡単に共有ができるだけでなく、お目当のプロジェクトを見つけるのも簡単に行えます。
せっかくなので <iframe/>
でエディタを埋め込んでみました。みなさんも実際に僕が書いたコードを確認できるかと思います。ちゃんと動作もするはず・・・
共有方法は簡単。
左上の"share"をクリックすると埋め込みリンクやエディタの共有リンクがすぐに取得できます。
また、CodeSandbox上から直接GitHubのレポジトリを作成してpushすることも可能です
リポジトリができた!!
他のプロジェクトを探したい時はSearchページから検索するだけ。プロジェクト名だけでなく使っているフレームワークやライブラリからも絞りこむことができます
例えばElement UIを使っているプロジェクトならこんな具合に。
プロジェクトのページに飛べば実際にどのように動作して、どのようにコードが書かれているのか学べます
Easy to import
今までに作成したプロジェクトをCodeSandboxに取り込むのも簡単です。方法としては大きく
という3つの方法が用意されていますが、今回はDEMOとしてGitHub importを試してみましょう。
以前作成したVue ToDo List sample(https://github.com/andoshin11/vue-todoList-sample)というレポジトリがあるのでこちらでお試し。
importするにはこのURLを貼り付けるだけ。あとは自動でCodeSandbox上にワークスペースが作成され、GitHubと同期された状態になります。
一瞬でimportが完了しました。こちらも <iframe/>
で埋め込んでみるので是非触ってみてください
https://codesandbox.io/s/github/andoshin11/vue-todoList-sample/tree/master/
Local Editor Experience
CodeSandbox上ではmonaco-editorが動いています。
monaco-editorはブラウザ上で動くVS Codeのようなものです。そこで vetur
をもとにした monaco-vue
が動作しているため、まるでVS Code上で開発しているかのような体験がブラウザで行えるのは良いですね。
その他の機能
ここでは紹介しきれないほどまだまだ機能がたくさんあるのですが、主だったものだけ箇条書き
- Parcelサポート
- preactサポート
- svelteサポート
- Sassサポート
- TypeScriptサポート
- Prettierデフォルトサポート
- ES Lintデフォルトサポート
- Liveコラボレーション機能
- Jest実行サポート
- 静的ファイルホスティング
- HMR
- Emmetサポート
- zipエクスポート
などなど素敵なものがたくさん。OSSなので日々機能が追加されていくのも魅力の一つです。
まとめ
CodeSandboxめっちゃ良い👏👏
簡単なハンズオンやデモをする際にサクッと環境が立ち上がるのが良いですね。そしてシェアの手軽さも嬉しい。
VueやReactの楽しさを知る前にnpm, webpack, babel等の設定でドロップしてしまった人も多いと思います。これからはCodeSandboxをスッと差し出して「とりあえず触ってみて!」と言えるようになると素敵ですね。
普通に開発環境として優秀なので出先でGitHubからプライベートモードでimport → 開発 & デバッグ → 動作確認までワンストップで行えそうで期待大です。
まだリリースされて1年ほどなので今後がとても楽しみ。みなさんもこの機会にぜひ試してみてください!!
おまけ
CodeSandboxへのお布施やIvesへの投げ銭はこちらからどうぞ👇👇
Pull RequestごとにStorybookがビルドされたら最高じゃね?
merpayでは積極的にStorybookを活用してコードと見た目の両方の観点でレビューを行なっていますが、Pull Requestが飛んでくるたびに該当ブランチをpullしてローカルでStorybookを起動するのが大変という運用上の問題を抱えていました。
そこでソリューションチームの協力のもと、PRが作成されるたびにCIからGAE上にユニークなpathをもったStorybookを配信する仕組みが導入されたのが先週のこと。とてもめでたい👏👏
これによってPRのデザインをチェックする時もURLをクリックするだけでよくなりました。
UI開発の概念がひっくり返るほど便利だったので、この仕組みをプライベートの開発でも導入したいと思います。
やりたいこと
- Storybookをホスティングする環境(URLの共有だけで完結するもの)を作りたい
- Pull Requestが作成されるごとに自動で新しいURLを発行したい
- Pull Requestがクローズされたら上記の環境を自動で破壊したい
これらの要件を達成するため始めは社内のスクリプトを丸ごとパクろうかと思いましたが、プロジェクトごとにGCPを設定するのはなかなか大変なので別の方法を模索してみます。
Heroku Review Apps
Heroku Review Appsとは、Herokuにpipelineを設定することでPull Requestが作成されるたびに独自のステージング環境を作成してくれるものです。
さらにPull Requestがクローズされたらそのステージング環境を一定時間後に破棄してくれるなど、今回の要件にピッタリの機能が詰まっています。こいつを今回は使っていきましょう!
プロジェクトの立ち上げとStorybookの設定
いつものようにvue-cli 3.0でプロジェクトを立ち上げました。
RouterやStoreの設定を自動でやってくれるのでいつも重宝しています。以下の記事を参考にしてこちらにStorybookを設定。
シュッとButton componentを作成します。
GitHubレポジトリとHerokuアプリの作成
GitHubにレポジトリを作成して master
をpush。Heroku側でもアプリを作成して接続します
StorybookをビルドしてExpressで配信する
package.json
にコマンドを一行追加。
"build-storybook": "build-storybook -c .storybook -o storybook-static"
このコマンドを実行することでStorybookが静的なページとしてビルドされますが、このままではHerokuで公開できません。
そこで今回はExpressをHeroku上で動かしてそこから配信してみようと思います。まずは Express
を追加。
$ yarn add express
server.js
を作成
// server.js var express = require("express"); var path = require("path"); var serveStatic = require("serve-static"); app = express(); app.use(serveStatic(__dirname + "/storybook-static")); var port = process.env.PORT || 5000; app.listen(port);
そしてHerokuにデプロイする時に実行されるコマンドを package.json
に追記します
"heroku-postbuild": "npm run build-storybook", "start": "node server.js"
heroku-postbuild
はHeroku上で自動で npm install
が走ったあとに実行されるフックで、 start
はデプロイ完了時にトリガーされるものです。
最後にHeroku上でAutomatic DeployをEnableにし、 master
ブランチがpushされたら自動でデプロイが走るように設定します。
この状態で master
をpushもしくは手動デプロイし、ビルドされたStorybookが無事にExpressから配信されていることを確認
これで最新のStorybookがHerokuから配信されるようになりました🎉
Heroku Review Appsの設定
ここからはPRごとに配信を行うためにHeroku Review Appsの設定を行なっていきます。まずは "create new pipeline"で storybook-review-app
という名前のpipelineを作成。(ここの名前は自由です)
そうするとpipelineの管理画面が出てくるので画面左端より "Enable Review Apps..."を選択
Enableにするとプロジェクト内で app.json
ファイルを作成するように言われます。 app.json
の中身はGUIからポチポチ設定できるのですが、今回は特に触らなくて大丈夫です。Buildpackが nodejs
になっていることだけ確認しておいてください。
最下部の "Commit to Repo"を押すとプロジェクトの master
ブランチに自動で app.json
がcommitされます(ちょっと気持ち悪い)
最後にReview Appの設定を行なって完了です。Destroyの期限は最長30日まで設定できますがとりあえず1日で問題ないでしょう。
PRを作成してみる
Heroku Review Appsがちゃんと動作するか実際にテストしてみましょう。ボタンの文言だけ変更する簡単なPRを作成
するとpipeline上で自動でアプリのビルドが始まります
ログも見れちゃう。
ビルド完了。
すごい!新しいバージョンが配信されてる!!
そしてPRのスレッド上にもデプロイが完了した旨の通知が届いています。これをSlackで受け取ったりすると良さそうですね。
URLもおしりにPRナンバーが付与されており、ユニークであることがわかります。
PRをクローズする
PRをマージしてクローズしてみました
一瞬で環境が消えた😲めっちゃ優秀
まとめ
Heroku Review Appsが予想の数段上を行く優秀さでした。これでいつでも開発メンバーやデザインチームの手を煩わせることなくレビューしてもらうことができます。Heroku自体にBasic認証などを設定しておけばプライベートプロジェクトでも大丈夫。
みなさんも(Herokuの無料枠に注意して)どんどんStorybookを育てていきましょう。今回のコードは下に貼っておきます。
P.S.
HerokuでNode.jsが動くならそもそも毎回静的ファイルをビルドしなくて良かった説ある...
文学部歴史学科出身の社会人がエンジニアになるまでやった事/やらなかった事
早いものでエンジニアとして働き始めてから今月でもう1年です。
1年前は時給1,000円ちょっとのバイトと派遣をやっていました。
先日ようやく正社員としてプロエンジニアになれたので、この機会に文学部出身のぼくが勉強し始めてからの3年半でやった事/やらなかった事を備忘録的に残していきたいと思います。
対象読者
- 文系だけどIT業界ではたらきたい学生
- これからプログラミングを勉強してIT業界への転職を考えている社会人
- すでにプログラミングを勉強中の社会人(←エラい)
- 未来の自分
ただの備忘録ではありますが、これからIT業界を目指す誰かの参考になれば嬉しいです。
サマリー
エンジニア以前
タイトルにもある通り大学では文学部歴史学科に所属しており、1行もコードを書いたことがありませんでした。
また最初の仕事も観光・宿泊業だったのでITに全く縁がありませんでした。
「会社のHPとかあればカッコいいなー」「ネットで集客できれば便利だなー」くらいのモチベーションで勉強し始めたのがきっかけです
沼への入口
エンジニアコミュニティの門を叩く
当時僕がいた京都にはCamphor-という学生ITコミュニティがあり(今もある)、そこのエンジニアを紹介してもらったのが沼への入口です。
(余談ですが、初めてエンジニアに会うのに予備知識が無いのは流石に失礼かと思いdotinstallでHTMLとCSSを勉強していきました)
このコミュニティに迎えてもらったおかげで素晴らしいメンターに巡り会えただけでなく、優秀なエンジニアを定点観測することで彼/彼女らが日頃どの程度のInput/Outputを行なっているのかを学ぶことができました。
「現在の地点」でInput/Outputに差が出るのは経験の差なので仕方ないですが、「半年後の地点」でInput/Outputの成長度合いに差が出るのは別の理由があるはずです。優秀なメンバーの「成長度合い」を学習のペースメーカーとして利用できた点で、始めにコミュニティに所属できたのは大きかったかなと思います。
0ヶ月目~: dotinstallでの自己学習
前述のエンジニアコミュニティに顔を出す前の予習としてスタート。やれるとこまでやって質問を1つ持って行こうと決めていたので、HTMLとCSSを修了し、「ローカルサーバーを立てる」章でつまづくまで進めました。
dotinstallには大抵の技術のいろはが揃ってるので、今でも新しい技術を身につけたあとに、タイトルだけ眺めて要点を抑えられてるか確認するため利用しています
この時学んだ事:
- HTML/CSS
- 黒い画面の触り方
2ヶ月目~: Wordpressでサイト制作
「Webサイトを作るにはWordpressを勉強すれば大丈夫」と聞いたので本を買ってブログサイトを作りました。
カスタムテンプレートを自作することで動的にサイトを組むという概念に触れられ、コピペでjQueryも使ってみたりしました。書籍だけでは当然情報不足なのですが、幸いにもWordpressの情報は日本語でも死ぬほどネットに落ちているためそちらも参考にしました。
この延長で自社の会社情報サイトも制作することになり、入り口としては良かったのかなと思います。サイトの公開作業はエンジニアに丸投げしました。
この時学んだ事:
- Wordpressの使い方
- PHPでHello world
10ヶ月目~: Ruby on Rails (Viewだけ)
社内でRailsを使ったWebサービスを作ることになりましたが、人材が不足していたのでMVCのView部分だけ担当しました。自分でもRails Tutorialにチャレンジしたのですが、巨大な壁にはじき返されたのを覚えています。
また、表示に必要なデータを自分でも用意するためController部分も少しだけ触らせてもらいました。
この時学んだ事:
1年2ヶ月目~: Slack Bot
いわゆる"プログラム"的なものが作りたくてSlack Botを作りました。名目としては「業務効率化」のためでしたが実際はネタBotばかり作っていました。その時の記事がこちら。
この頃からJavaScriptで簡単なロジックを書いたりするのがだんだん楽しくなって来ました
この時学んだ事:
- JavaScript (ES5)
- Google Apps Script
- Coffee Script
- Herokuへのデプロイ
1年3ヶ月目~: クローラーの作成(コピペ)
業務でホテル予約サイトやAirbnbのデータを分析する機会があったのですが、MITの学生がクローラースクリプトを公開していたので(下記)そちらを参考にして(コピペして)自社向けのスクリプトを開発しました。
実行は手動で行わなければならずシンプルなCSVを出力するだけのものでしたが、自動で大量のデータを集めることができるのが面白く、いろんなサイトのクローラーを20種類くらい作成しました。DOMを解析したり、HTTP通信の仕組みを勉強できて楽しかったです。
この時学んだ事:
- Pythonの基礎の基礎
- HTTP通信の仕組み
- DOMの仕様
1年5ヶ月目~: Ruby on Rails
3回くらい挫折したTutorialをようやく終えて自分でもサービスを作りたくなってきました。初めてスクラッチからアプリケーションを作り、メンターにボコボコにレビューしてもらいながら一応のリリースまで持って行けて良かったです(すぐクローズしたけど)。
このときはRubyでクローラーも書きましたが、デプロイとインフラは相変わらずリードに丸投げ。
この時学んだ事:
2年2ヶ月目~: Vue.js (SFCだけ)
リードがRailsアプリにVue.jsおよびモダンJSの開発基盤を整えてくれたので、コンポーネントファイルのメンテだけ担当しました。世間的にはちょうどv2.0が出て来てVueが注目され始めたころです。RailsとAPIで通信してUIゴリゴリ動かすのめっちゃ楽しいやんけ、ってなりました。
この時学んだ事:
2年6ヶ月目~: Docker (エンジニアデビュー)
メンタル病んだりいろいろあって会社を離れたものの、手につく職も特になかったのでエンジニアデビュー。 事務作業の片手間でやってたプログラミングがどれだけ世間で通用するか分からなかったので、修行のつもりでWeb制作会社でWordpressのバイトをさせてもらいました。
Wordpress周りのタスクならそれなりにできるようになっていたので、新たにインフラに手を出したくなりDockerの勉強開始。
なんとなくでしか理解していなかったLinuxをもう一度勉強してDockerfile書いたり、docker-composeで複数のミドルウェアを組み合わせたりできるようになってエンジニアとしてレベルアップした気がします
この時学んだ事:
2年7ヶ月目~: モダンフロントエンド
環境の整ったRailsアプリでVueのコンポーネントを書いたことはありましたが、初めてゼロから環境構築するのはこの時が初めて。Webpack、Babel、ES6、Promiseやnpmの使い方などをザックリと理解して最低限のモダンフロントエンド開発環境をスクラッチで組めるようになりました。
この頃は狂ったようにRails + Vueのアプリ作って遊んでいました。だんだんRailsが面倒になってFirebaseのようなBaaSの使い方も覚えました。
この時学んだ事:
- モダンフロントエンド
- Firebase
- 高速なアプリ開発手法
2年7ヶ月目~: Perlでソシャゲ開発
バイトだけでは食べていけないので派遣に登録してソシャゲの下請けでPerlを書くように。
はじめての言語かつオレオレのフレームワークでしたが、MVCを理解してたのでなんとか仕事ができました。それなりの規模のサービスではありながら、実質3~4人で保守と新規開発を全部見てたので死ねました。
それまでORMしか触ったことがなかったので生SQLを書くときに死ぬほど怒られたし、DBやメモリのパフォーマンスなど気にしたこともないままコードを書いていたので本番障害も引き起こしてしまうなど、大規模サービスならではの経験ができて良かったです。(その節はご迷惑をおかけしました)
これがきっかけで「動けば良い」という思考から少し成長し、パフォーマンスやスケーラビリティを考慮した設計をするようになりました。
何十万行もあるクソコ温かみのあるコードベースから気合のgrepでバグを見つけ出すスキルも身につきました。
この時学んだこと:
- パフォーマンスへの意識
- 保守の大変さ
- バッチの運用方法
- CIの使い方
- チーム開発のいろは
- RDBMSの仕様
3年目~: React Nativeでモバイルアプリ開発
バイトと派遣を継続する傍ら、受託案件もいただけるようになりました。フロントエンドが足りていないとのことだったので「よっしゃVueやるぞー!」と意気込んだものの、アプリ開発もやってほしいとのことで泣く泣くReactおよびReact Nativeで開発することに。
社内はおろか周囲にもReduxやReact Nativeの知見を持った人がいなかったので、海外のチャットコミュニティや掲示板で情報収集しながら開発を進めるのはなかなか楽しい経験でした。レールのない世界でスケーラブルなアーキテクチャを模索するのは面白かったです。
まだ開発中でリリースまではいけてないのですが、Xcodeを触る中でモバイルアプリのパラダイムも学ぶことができてとても勉強になりました。
この時学んだこと:
- React
- React Native
- Redux
- Xcode
- Android Studio
3年2ヶ月~: データ分析と機械学習
バイト先まで片道2時間ほどかかっており、ゲームや読書で時間を潰すのも飽きたのでデータサイエンスを勉強し始めました。
そもそも数1Aの記憶すら怪しい文系マンなので、まずは数3Cまで勉強し直すところから。その後UdacityのData AnalystコースとCourseraのMachine Learningコースを受講しました。ライブラリを使えば簡単な画像判定ツールくらいは作れますが、スクラッチで実装しろと言われると厳しいレベルです。
3ヶ月ほど勉強してみましたが他のことに興味が移ったので学習はストップしています
この時学んだこと:
3年6ヶ月目: 現在とこれから
2018年3月1日よりmerpay, Inc.でフロントエンドチームの立ち上げに参画しています。
mercari本社はReactの会社なのですが、merpayではVue.jsを採用して会社全体で新たなチャレンジを行なっている最中です。 BFFサーバーとしてNuxt.jsも採用しているため、「フロントエンドチーム」と名乗りつつバックエンドに踏み込んだ仕事もできる魅力的な環境だと思います。
疑いの余地なく日本で最高のメンバーと最高にBoldな未来を創っていける職場なので、この場所でもっとディープにWeb技術全般を学習・検証していくつもりです。
エンジニアリングを始めてからの数年間、様々な経験を公私ともにさせてもらって本当に恵まれていました。 これからは積極的にイベント等に登壇してコミュニティに還元していきたいと思います。
やらなかった事
これまでで自分がやってきた事を書き終わったので、最後に自分が「やらなかった事」を簡単に紹介します。
ハッカソンに出る
ハッカソンは一度だけ出てもう良いかなって思いました。良くも悪くもいろんなレベルの人がいるので必ずしもプラスにならないのと、ハッカソン常連の仲良しコミュニティ感が肌に合わなかったです。技術力だけじゃなく参加回数や業界年数を加味したコミュニケーションを強いられてバチバチ勝負出来なかったので、やや消化不良気味でした。
魅力的な景品や、同レベル以上のライバルがいる事が保証されてるならアリだと思います。
勉強会を主催する
コミュニティに参加するのは大好きですが、勉強会の企画・運営はあえて避けていました。キャリアの浅いうちから外の人を巻き込んでしまって責任ばかり肥大すると、自分自身のフットワークを殺してしまうかなと思ったからです。
勉強会やイベントの運営ノウハウを学ぶのも大事なのでこの辺りはトレードオフですね。
本を読む
始めこそWordpressの教科書みたいな本を買いましたがそれ以来ほとんど買っていません。他にもRubyのクローラー本やインフラ関係の本を買いましたが、ほぼ流し読み。
情報が体系的にまとまってるのはありがたいですが、勉強したてのころは必要に応じて様々な情報に網羅的に目を通すのが僕には合っていました。普段からブラウザのタブを常に40個くらい開いて横断的に参照しているタイプなので、同程度の本を複数回し読みする運用はキビしかったです。
おかげで(?)海外のフォーラムを覗くのに抵抗がなくなりましたし、複数の著者(情報の提供者)の意見を統合して最終的な情報の良し悪しを判断するクセみたいなものがつきました。
逆にリーダブルコードのようなバイブル的なやつは読んどくべきだったかも、と最近反省しています。
最後に
エンジニアになる以前も営業、経営企画、経理、法務、労務、マーケ、CS等々いろいろやっていましたが、結局今の仕事が一番肌にあっている気がします。
IT業界の面白いところは、会社の枠を超えて業界全体で知識の共有とアップデートが文字通り秒単位で行われている点です。自分がどれだけ勉強しても天井が見えず、「もっともっと勉強してやろう」というモチベーションを他者から得られます。これは他の業界ではなかなか見られない現象です。
ようやくテックカンパニーに就職することはできましたが、チームではブッチギリで経験の浅いペーペーなので、遠慮なく先輩方のスキルを盗ませていただこうと思っています。
繰り返しにはなりますがぼくはコミュニティのお陰で様々な恵まれた経験をしてこられたと思っているので、今後はそこに恩返しもしつつ更に成長速度を加速させていきたいです。
以上!!年度末のエモい記事終了!!!!
おまけ
merpayの求人サイトが新しくなったので是非見てみてください。めちゃんこオシャレです。
弊社に興味ある方がいれば気軽に声かけてください。肉やりましょう。
Falcon Heavyの打ち上げは、紛れもなく僕のアポロだった
1969年7月20日 アポロ11号月面着陸
人類が初めて地球以外の天体に降り立ったこの日、全世界が熱狂した。
この「一人の人間にとっては小さな一歩」が灯した火は半世紀を経た今もなお、多くの人の心の中で輝き続けている。
ある者はその「一歩」を追って月への想いを焦がし、またある者は小惑星探査へと野望を抱き、そしてある者は火星へと夢を見た。
2018年2月7日 午前5時44分
「T-30」のアナウンスが流れた瞬間、僕の身体に電撃が走り全身の毛が逆立つのを感じた。
前の晩から興奮で眠れずとうに疲労の限界を迎えていたけれど、歴史的瞬間を前にして僕の脳からは猛烈な量のアドレナリンが分泌されていた。
25歳の冬、紛れもなく僕は「熱狂」していた。
はじめて宇宙に想いを馳せたのはおそらく9歳か10歳のころ。
小学生の僕が父とお風呂に入るときは必ず父が広い世界の話をしてくれた。ときには歴史の話だったり、物理の話だったり、地理の話だったり、社会のしくみの話だったり、そして宇宙の話だったり。
当時札幌に住んでいた僕は年に数回は青少年科学館に足を運び、プラネタリウムや天体模型に夢中になっていた。いつかの冬休みには4日間の宇宙講座に参加して、ロケットのしくみを勉強したり宇宙食を食べたりした記憶が今でも残っている。
それからしばらくの間、宇宙への想いを忘れていた僕がふたたび「それ」と出会ったのは大学1回生の夏休みのこと。
いわゆる「ブラックバイト」で消耗していた僕は、なけなしのバイト代をはたいて種子島に旅をすることに決めた。そのころから新海誠作品のファンだったこともあり、なんとなく「秒速5センチメートル」に出てきた風景に惹かれて南の島に逃げたくなったのだ。
せっかくの島旅なので北の端から南の端まで巡る計画を立て、宇宙センターの近くにも宿を取った。この時泊まった門倉荘という宿はどうやらJAXAの関係者がよく利用する宿だったようで、食堂の壁にも宇宙飛行士のサインがズラッと並ぶなどしていた。
結局、宇宙一色の町の雰囲気に押されるがまま宇宙センターの見学を申し込む形にはなったが、この時暇そうにしている僕に宇宙センター見学を勧めてくれた宿のおばちゃんには心から感謝している。
生まれて初めてロケットを見た
生まれて初めてロケットのエンジンを見た
生まれて初めてロケットの格納庫を見た
生まれて初めてロケットの発射台を見た
心の底から何かをカッコイイと思うのは、多分初めての経験で、胸の奥から熱いものが込み上げてくるのを感じた。人の手で組み上げられたものが大空の彼方まで飛んでいくのだという「現象」の衝撃が、確かな実感として心に刻み込まれた。
その日、宿に戻って食事をしてから種子島の夜道を2時間ほど散歩した。
思えばこの時が人生で初めてしっかりと自分の将来について考えた瞬間なんだと思う。それまでもぼんやりと「ここの高校に行きたい」「ここの大学に行きたい」「こんな仕事をしたい」などと考えたことはあったけれど、「何を成し遂げて死ぬのか」を明確にしたことはなかった。
宇宙センターでのことを思い出し、夜道を照らす星々の美しさに感謝し感激し、それまでの人生について振り返り、それまで訪れた様々な場所や出会った人々のことを思い出し、そしてなにより若くして命を落とした友人たちの生き様を振り返った時に、19歳の僕は「宇宙を見てから死のう」と決意した。
25歳の僕は、まだ夢を叶えていない。
この6年間でいろいろな回り道をしてきた。
短い間だが大学では歴史のロマンに魅せられて西洋史を専攻した(結局中退した)
経営者として経済的成功を勝ち取ろうとして会社を興し、テレビや新聞に取り上げてもらって浮かれていたりもした。当時は本気で億単位の金を40歳までに稼いで宇宙旅行に行こうと考えていたのだ。(結局メンタルが持たずに多くの人に迷惑をかけた)
そして現在は社会人になってから始めたプログラミングの面白さに魅せられ、技術力を高めつつ社会復帰することに身骨を砕いている。
いろいろやってきたが、多分これが一番性に合っていそうだし夢への近道なのだと最近は思っている。
結局のところ、僕は大富豪になって「宇宙観光」がしたいわけではなく、人類のフロンティアである宇宙開発に貢献し、歴史上誰もまだみたことのない世界を切り拓くことにワクワクするのだと。
去年の夏に、NASAのJPLで無人探査機の研究開発を行なっている小野雅裕さんの講演を拝聴する機会を得られた。
彼の立っている場所は、まさに僕の夢の番地だった。
技術者としてエンケラドスの氷の底を観測しようという小野さんが積み重ねてきた努力や熱意は僕が描く将来の自身のイメージそのもので、これから進む道が間違っていないのだという希望と勇気をもらえるとても素晴らしい瞬間だった。
人類がエウロパやタイタンの海を開拓していく瞬間に携わりたいと今では強く思うし、プロキシマ・ケンタウリbのような系外惑星まで衛星や探査機を届ける技術的な貢献がしたいと、空を見上げるたびに想っている。
2月7日の早朝、200万人が見つめる中でFalcon Heavy打ち上げのカウントダウンが始まった。
それまでもSpaceXのFalcon 9の打ち上げ中継を欠かさず見ていた僕だったが、この時の興奮はいつもの比ではなかった。
2004年にイーロン・マスクがFalcon Heavyの構想をぶち上げてから、多くの資金難、技術的困難、社会的バッシングがあった事を僕たちは知っている。
それら全てを笑い飛ばすかのようにFalcon Heavyは大空へと美しいを弧を描いた。
魔法のように地上へ戻ってくるロケットの姿を見て、全ての人が「これからはロケットも飛行機のように何度も再利用される時代がくるのだ」と考えるより先に肌で感じた。
火星に向けて射出されたロードスターとスターマンを見て、全ての人が「これは他の惑星に荷物や人を届けるためのロケットなのだ」と考えるより先に直感した。
この日、もう一つ個人的に思い出深かった事としては、打ち上げの瞬間を母親と一緒に見られたことだ。
ロケットやSpaceXのことはおろか宇宙のことなどほとんど何も知らない母が、この時ばかりは見たこともないくらい興奮し、「熱狂」していた姿がとても印象的だった。
人が空を見上げる時、そこには年齢も性別も国家も人種も壁なんて存在しない。
なにせあの冷戦時代のアメリカとソ連でさえ宇宙では手を取り合ったのだから(1975年7月17日 アポロ・ソユーズテスト計画)
2018年に全人類にそんな「空を見上げる」機会をプレゼントしてくれたイーロン・マスクとSpaceXのスタッフには賞賛の声を送っても送りつくせない。
打ち上げの成功を喜びあう彼らの笑顔と涙を、僕は一生忘れないだろう。
あのカウントダウンの瞬間からスターマンの中継が途切れる最後の瞬間まで、彼らが与えてくれた感動と希望の灯火を胸にこれからも夢に向かって歩んで行こうと思う。
最短距離ではないだろうしこれからも多くの人に迷惑をかけるだろうけど、もしも僕の「回り道」の途中でその灯火を誰かに分け与えることができたのなら、人類に対して少しは貢献できたと言えるんじゃないかな。
2018年2月7日、200万人と共に「熱狂」したFalcon Heavyの打ち上げは、紛れもなく僕にとってのアポロだった。
2018年2月9日 好日
今度こそVimを攻略したい:neovimをインストールして最高のdev UXを手に入れるまで(その1)
先日の記事に様々な反響をいただいたことをきっかけに「アウトプットやっぱ大事だなー」と思ったので、アドベントカレンダーに関係ないタイミングでも記事を書いてみようと思います。
僕のエディタ遍歴
- ~2014年夏:Pages
大学では歴史学を専攻していました。当時はレポートさえ書ければ良かったのでMacデフォルトのPagesを使用。プログラミングのプの字も知らなかった時代。
- 2014年夏 ~ 2015年:Sublime Text 2
仕事の合間にdotinstallでHTMLとかCSS勉強し始めた時代。プラグインも何も入れずになんとなくかっこいいなーという理由でSublimeを使っていた。この頃はWordpressの本を読んで会社サイトとか作ってました
- 2016年前半:Vim
社内で仕事をお願いしていたエンジニアが黒い画面でよく分からないことやってるの見てすごいなーという憧れでVimを使い始めた。なんとなく .dotfiles
リポジトリとか作ってみたけど .vimrc
の中身は全てネットで見たやつのコピペ。使ってる機能は1割くらいしかなかったと思います。
この頃からRails書き始めましたが、開発するには同時多発的にいろいろなファイルを触らなきゃいけないので当時のVim力では少し厳しかったです。 NERDTree
入れてたけど全然活用できなかった思い出
- 2016年後半 ~ 2017年春:Sublime Text 3
本格的にプロダクト作りたいなーとなって使い慣れたSublimeに戻ってきました。ショートカットやプラグインの使い方もいくつか覚えて体感でこれまでの3倍くらいに開発速度も向上。ブワーっとスクロールして任意のファイルの中身をザッピングできるGUIのありがたさを実感
- 2017年春 ~ 現在:Atom
サイドバーのファイルアイコンが可愛いという理由だけでAtomに乗り換え
あとは痛エディタにしたくて背景画像もこんな感じにしてました
Atomのプラグインやショートカットの手触り感はSublimeと大差ないのですが、いかせん重すぎて開く気にすらならない!!! 最近は案件でXcodeも動かすことが増えてきてだんだん辛くなってきました。。。
Vimに戻るかー
ということで主に以下の理由でVimに戻ることにしました。
いろいろなエディタを触る中で自分が開発に必要とする機能が具体的に見えてきた
昔Vimを使っていたころよりも髪の毛1本ほどは黒い画面の操作に慣れてきたので今度こそいけるやろ感
他のエディター候補としてはMSに行ったときに紹介していただいたVSCodeがとてもとても魅力的だったのですが、「Electronで動くアプリはSlackだけで十分!!」という強い意思を持ってVimを頑張っていきたいと思います。
要件
Vimは何度も挫折を繰り返しているので触り始める前に意識すべき点として以下の項目を挙げました
- 設定ファイルをキチッと管理すること
- カーソル移動のしやすさを最優先にすること
- シームレスにファイルツリーの確認とファイルの移動が行えること
1についてはいうまでもないですが、2と3は過去にVimを捨ててSublimeに走ったクリティカルな理由なので確実に攻略していきたいと思います
neovimのインストール
今回は以前から良いと評判を聞いていたneovimを利用します。(* Mac vimによくわからない破壊的変更を加えすぎて手がつけられない)
Homebrewから neovim
をインストール
$ brew install neovim/neovim/neovim
設定ファイルにpathを通す
デフォルトでneovimは ~/.config/nvim/init.vim
を設定ファイルとして参照しますが(vimでいう .vimrc
)、dotfilesを一限管理したかったので以下のようにシンボリックリンクを貼りました
$ ln -sf ~/dev/dotfiles/nvim/init.vim ~/.config/nvim/init.vim
これでdotfiles以下を変わらずGit管理できます
dein.vimでプラグイン管理
昔は NeoBundle
を用いてプラグインの管理を行なっていた記憶がありますが、今は dein.vim
というのが良いらしいですね。
init.vim
に以下のような設定を追記
let s:dein_path = expand('~/.vim/dein') " dein Scripts----------------------------------------------- if &compatible set nocompatible endif " Required: set runtimepath+=~/.vim/dein/repos/github.com/Shougo/dein.vim " Let dein mange itself if dein#load_state(s:dein_path) " Required: call dein#begin(s:dein_path) " Add or remove your plugins here: call dein#add('Shougo/dein.vim') " Required: call dein#end() call dein#save_state() endif " Install uninstalled plugins on startup if dein#check_install() call dein#install() endif " Required: filetype plugin indent on " End dein Scripts------------------------------------------- syntax enable
call dein#add(<plugin name>)
というように追加したいプラグインを記述するとneovim起動時にそのプラグインが自動でインストールされます。
また、まだ試していませんが TOML
という形式のファイルを別に作成してそれを dein.vim
から読み込むこともできるらしいので、もう少しプラグインの数が増えたら試してみます
カーソルの移動速度を上げる
これは多分Mac独自のHack。「システム環境設定」 -> 「キーボード設定」を見るとキー入力に対するディレイとリピート速度が設定できるので、こちらから適度なスピードに変更。
tmux上でのモード切り替え速度を上げる
インサートモードからノーマルモードへの切り替えがもっさりしていたので調査。どうやらtmux側でescキーに対して入力待ちをしていたのが原因のよう。デフォルトで500msくらいの待ちが発生していたようなので ~/.tmux.conf
に設定を追記
set -g escape-time 0
tmuxをリフレッシュすると動作がサクサク快適になりました
NERDTreeを入れる
neovimの中から別のファイルを開く方法としては基本的に :e <file name>
で十分な気もしますが、視覚的にディレクトリ構造を把握した上で任意のファイルを開きたいという欲求があったので NERDTree
をインストールしました。
call dein#add('scrooloose/nerdtree')
という一行を追記するだけ。
また、ファイルツリーを開く際にいちいち :NERDTreeToggle
などと打つわけにもいかないので以下のkeymapを init.vim
に追加
map <C-t> :NERDTreeToggle<CR>
これで ctrl + t
でファイルツリーを開けるようになりました。
また、 NERDTree
上でのファイルの視認性が悪かったので、以下の設定を追記して拡張子ごとにHighlightするようにしました
" NERDTress File highlighting function! NERDTreeHighlightFile(extension, fg, bg, guifg, guibg) exec 'autocmd filetype nerdtree highlight ' . a:extension .' ctermbg='. a:bg .' ctermfg='. a:fg .' guibg='. a:guibg .' guifg='. a:guifg exec 'autocmd filetype nerdtree syn match ' . a:extension .' #^\s\+.*'. a:extension .'$#' endfunction call NERDTreeHighlightFile('py', 'yellow', 'none', 'yellow', '#151515') call NERDTreeHighlightFile('md', 'blue', 'none', '#3366FF', '#151515') call NERDTreeHighlightFile('yml', 'yellow', 'none', 'yellow', '#151515') call NERDTreeHighlightFile('config', 'yellow', 'none', 'yellow', '#151515') call NERDTreeHighlightFile('conf', 'yellow', 'none', 'yellow', '#151515') call NERDTreeHighlightFile('json', 'yellow', 'none', 'yellow', '#151515') call NERDTreeHighlightFile('html', 'yellow', 'none', 'yellow', '#151515') call NERDTreeHighlightFile('styl', 'cyan', 'none', 'cyan', '#151515') call NERDTreeHighlightFile('css', 'cyan', 'none', 'cyan', '#151515') call NERDTreeHighlightFile('rb', 'Red', 'none', 'red', '#151515') call NERDTreeHighlightFile('js', 'Red', 'none', '#ffa500', '#151515') call NERDTreeHighlightFile('php', 'Magenta', 'none', '#ff00ff', '#151515') call NERDTreeHighlightFile('vue', 'Green', 'none', '#4fc08d', '#4fc08d')
その他
Vueファイルのシンタックスハイライトを追加する
Vue.jsのSingle File Componentである .vue
だけシンタックスハイライトがなくて寂しかったので以下のプラグインを追加しました
まれに動作が不安定な時もありましたが、以下の設定を init.vim
に追記して解決
" autohighlight vue file autocmd FileType vue syntax sync fromstart
emmetを入れる
本業がマークアッパーなのでemmetは必須。ということでdein.vimから emmet-vim
入れました
call dein#add('mattn/emmet-vim')
また、デフォルトだとemmetのkeymapが <C-y>
なのでAtomやSublimeを使ってた人間からするとちょっと不便。
" Configure emmet-vim " set trigger key let g:user_emmet_leader_key='<C-e>'
こんな感じに書くとAtom風にkeymapを変更できます。
実際に呼び出す際は ctrl + e + ,
というように末尾にカンマが必要なので注意
動いた 👏👏
個人的に便利だなーと思ったこと
今回新しくゼロから学んでみて、個人的に便利だなーと思ったneovim(vim)の機能を羅列します。始めてVimを触ったときに知っておきたかった...
o
で次の行を空けてインサートモードに移行:
なんでいままで行末に移動 → インサートモードに移行 → 改行、なんて面倒なことをやっていたんだろうw
0
で前の行を空けてインサートモードに移行:
上に同じ。
r
キーで1文字だけreplace
TypoScriptの使い手なので「1文字だけ修正したい」というケース非常に多いのでとてもありがたい。
I
で行の先頭に移動してインサートモードに移行:A
で行末に移動してインサートモードに移行:ctrl + o
で直前に開いていたファイルに戻るctrl + i
で上記の逆
などなど挙げればキリがないので、続きはまた別の記事にまとめたいと思います。
Vim楽しくなってきたのでもっと使いこなすぞー