きょこみのーと

元六本木でGo書いてました。今はVRでGo書いてます。

AnnictのAndroidアプリをKotlinでつくった

Kotlinの入門がてら作ったのですが、リリースに至るまでの流れとか利用してるライブラリとかの話しをしようかなと思います。

play.google.com

ざっくり以下の話しをしようと思います。

  • 作ったものについて
  • 開発期間について
  • Kotlinについて
  • 利用したライブラリなどについて
  • CIについて
  • タスク管理について

作ったものについて

f:id:kyokomi:20180110223121p:plain

画面はこんな感じで普通にRecylerViewでのリスト表示です。 今回は、Android Architecture ComponentsPagingLibraryを使ってかなり楽できました。(またあとで説明します)

このアプリの自分の用途について

毎クール30作品とか全部見てると時間がいくらあっても足りないので、 視聴中アニメでモチベーションが低くなってる作品のステータスを(見てる -> 一時中断 -> 視聴中止)にちゃんと更新したかった。 というモチベーションで作りました。

どのアニメ観ようかな〜とかの用途ではなく、観てる前提で自分は何を観るきなくしているのか?や自分が次に観るべきアニメはどれなんだというのを把握して確実に消化していく用途です。

ちなみに視聴時の自分ルールはこんな感じ

  • 今期で3話以上未視聴が溜まったら一時中断にする
  • 一時中断にしたものはクールの終わったときに振り返って見るかどうか?を他の人のレビューなどを参考に決める
  • ここで見ないを選択した場合は、視聴中止にする

アプリ利用の前提条件

  • Annictを結構使ってるユーザー
  • 大体今期やってるアニメを把握してる
  • Annictのチャンネル登録をちゃんと設定している
  • まとめて視聴したりせず、毎日今期アニメを消化しつづけているアニメ好き

「今期はこれだ!」みたいな1点読みとかしないで、ほぼ毎クール7割近くの作品の数話を観て徐々に観る作品を削っていく感じの人だと尚良い

開発期間について

最初に12/2くらいに開発をスタートして、最低限の機能を1日〜2日くらいで作ってちょこっとバグ直したりしてたのが12月前半。
年末までちょこちょこ触って、1/4〜1/8で一気に仕上げました。

12中旬くらいの時点でそこそこ動いてたので、わりと毎日個人的に使ってドッグフーディングできてたのは結構モチベーションにつながってよかった。 後は「やるぞ!」って気持ちと一気にやる時間があるのがやっぱり大事ですね。

f:id:kyokomi:20180110223819p:plain

f:id:kyokomi:20180110223058p:plain

f:id:kyokomi:20180110223050p:plain

Kotlinについて

Kotlinについてですが、とにかく書きやすくてコード書いてて気持ちよかったなぁ〜という感じでした。事前に以下の書籍を読んでいたので、ひとまずほとんど困らず開発できました。 ただ、もっとうまく書けたのかな〜とか思いながら書いたコードが結構あるので、今後もリファクタリングしていければと思います。

Kotlinイン・アクション

Kotlinイン・アクション

Kotlinスタートブック -新しいAndroidプログラミング

Kotlinスタートブック -新しいAndroidプログラミング

利用したライブラリなどについて

PagingLibraryについて

今回特に役立ったこちらについてもうちょっと説明。 とは言え、以下の図を見るのが一番早いw

https://developer.android.com/images/topic/libraries/architecture/paging-threading.gif 画像引用元: https://developer.android.com/topic/libraries/architecture/paging.html

今回で言うとRoom経由でSQLiteのデータがそのままRecyclerViewのリストに対応している感じになってます。 例えば、新しいデータをInsertすると参照しているRecyclerViewはハンドリングするコード等書かずにPagingLibrary内で notifyItemInserted(position) を呼び出してくれます。Deleteした場合は同様に notifyItemRemoved(position) ですね。

つまり、SQLite側のデータをどう更新するかを意識するだけでViewにはいい感じに反映してくれるという感じで最高でした。 もちろんLiveDataなので、画面回転時などにActivityが再生成されてもちゃんとobserveするコードが再度呼び出されてクラッシュするようなことも無くかなり楽でした。

ただ、かなりレールに乗ってる感はあるのでちょっとレールを外れるようなことをしようとすると、SQLiteに変なデータを突っ込んで無理やりViewHolderの処理でハンドリングして〜みたいな感じしかできなさそうな点などある程度諦めが必要かもしれません。

が、めっちゃ開発は楽になったので今後も使っていきたいと思います!

CIについて

www.bitrise.io

今回、Bitriseを初めて使いました。とにかく最高でした。 過去にWerckerやCircleCIでAndroidのbuildをしていたときに比べてやることが全然無くて、殆どがWebコンソール上で完結してとにかく最高でした。 keytoolの管理等もいつも悩まされていたのを管理してくれたりGooglePlayStoreへUploadするstepも用意されていたりなど助かりました。

CircleCIやwerckerだとたまに謎のメモリ不足?とかのbuild失敗があったのですが、それも発生しなくなった気がします。

タスク管理について

Zenhubを使ってやりました。ポイントの見積もりは結構雑だったんですが、 サクッと終わらせられる粒度にタスクを分割してたのでまあまあいい感じだった気がします。

f:id:kyokomi:20180110231734p:plain

タスクの一部を抜粋したものがこちらです。最初にばーっと10タスクつくら作って優先度決めて開発を進めていくうちにドンドンタスクを追加して、その都度優先度を変えてみたいな感じでガンガンやってました。

f:id:kyokomi:20180110231925p:plain

今後

ひとまず細かいバグ修正を優先していこうと思います。 現状AnnictのWeb側で設定する前提になっているチャンネル設定とか視聴状況の変更とかもアプリ内で出来るように〜とかも気が向いたらやるかもしれません。

バグ報告とかお気軽にお待ちしておりますので〜何卒お手柔らかに〜

2017年振り返り(技術編)

2018年を振り返るとき用にメモ書きを残すことにした。

Go

インフラまわり

  • 仕事はAWS使ってたので、この一年大体普通のWebアプリケーション作る分には、1から運用までやれる気がする
    • ただ、ecsとか自分が使い慣れてるものを使うに限る...
  • GCPはほとんど触ってない
  • FirebaseAuthは仕事で使ってたので結構ハマりどころとかわかる
    • Twitterの認証がなんか結構地雷なので辛かった
    • メールのtemplateとかも痒いところに手がとどかない感じだった
    • 昔に作ったProjectのメールtemplateを日本語に変更できないとかあったな
    • export機能がないので別のサービスに移行するのが困難
  • FirebaseAuthで痛い目にあったので、Auth0を使うようにした
    • 色々なProviderに対応してて結構いい感じだった
    • そこまで悪い点はないけど、tokyoリージョンないのでオーストラリアにしてたというくらい?
    • 将来の規模感とかちゃんと考えて、auth0使うのはありかも
    • 認証時のCallbackをjavascriptでいじったりできる機能があったのは感動した
  • terraformは、前半は結構触ってた
    • terraform最高かよって気持ち
    • 大体AWSコンソール上で手で作っていい感じだったらterraformで組んでみるという流れ
    • 大体terraform公式のドキュメントみたら分かるのでなんとかはなる
    • count周りを乱用してapplyしないとこのcountが解決できないんだけどapplyが失敗するみたいなデッドロック状態でハマったことがある
    • 最近は、自分よりガッツリterraform見れる人が現れて任せてしまってサボり気味

Android

  • 仕事でiOSアプリをちょっとつくっていた時期があって、Androidもやるかも?と思って復習してた
  • 個人アプリをkotlinで書き直したり
  • クリーンアーキテクチャ推しだったんだけど、最近はAndroid Architecture Componentsで書いてる
    • Paging Libraryとかがすごすぎるんだけど、ちょっと変わったことやろうとするとSQLiteのデータをいじらないといけなくて... う〜んってことがあった
    • RecyclerViewの最後まで行ったときに続きを取得するクルクル表示のViewとかを差し込みたいときなど
    • LiveDataで取得している箇所が、SQLiteのデータを更新したら反映されるというはマジ最高だなと思った(KONAMI
  • やっぱAndroid開発は楽しいな〜というのを思い出したり
  • 最初の会社辞めたときにAndroid開発がしたいので!って言ってたなというのを思い出した
  • 次の会社はGoが書きたいので!って言って辞めたきもする( ^ω^)
  • Kotlinが結構いい感じなので2018年もやっていきたい

Unity

  • 前半は結構触ってた
  • 2Dで放置ゲーをクライアントを作ってた(またエターなった)
  • VR周りの開発は正直数学の知識が足りなくてしんどかった
  • 普通にuGUIとかでUI組んでC#API叩くコード書いて〜みたいなのは普通に出来るレベルにはなった
  • とはいえ2Dの開発ばっかりしててもと思って、3DのSRPGを作ってたいた時期もあった(6〜7月)
  • またエターナってしまったようだな(やる気はあるんです...!)
  • 今年は触る機会あるかな〜?わからん...

iOS

  • やるぞやるぞ!!って思ってたらやることなく終わった
  • 縁が無い

Kotlin

  • 12月くらいからServerSideKotlin書き始めた
  • とりあえずSpringBootで入門中(まあなんでもいい)
  • herokuでもいいんだけどGAEが好きなのでGAE/Kotlinで動かしながら色々触ってるレベル
    • なんかよくわかってないけど、GAE/Kotlinやってる人がググってもあまり情報でてこないので適当にblogに書いていくつもり
    • HelloWorldまでして終わってる人は沢山みた

個人タスク管理

  • Trello -> Asana -> Zenhubという激しい入れわかりだった
  • 一旦zenhubで落ち着いている
  • 欠点をあげるとprivateのtaskを管理しているので、Githubの草が無駄に増えてしまう点(githubとシームレスにという利点でもあるのだが)
  • 個人の生産性をバーンダウンチャートで可視化できるのは結構良い
  • ちなみに先月はこんな感じ f:id:kyokomi:20180104232219p:plain
  • iceboxに1ヶ月以上先にやりそうなタスクをいれる、backlogに今月やる予定のものを雰囲気でポイント見積もりしていれる
    • 読書とか個人開発とか
    • 読書はぶ厚めの本の場合(1日で読めないもの)は章単位でタスクに分割したりしてる(こんな感じ) f:id:kyokomi:20180104232516p:plain
    • 〜を買うみたいな買い物系とか一瞬終わる系はtodoistで管理している(個人生産性という観点でいれるようにしている)
  • boradの機能とepicという概念が今のところ重要
  • githubの上に乗っかってるのデータの持ち方やもっさりしてるところはデメリットではある
  • 最強のタスク管理ツールは未だに探してる...

最強のメモ帳

  • 最強のMarkdownツールが沢山合った記憶...
  • 結局Evernoteを廃止してinkdropに落ち着いている
  • 月額払うのを決めた(11月くらい?)。そろそろ年間契約にしてもいいかもしれない
  • まあEvernoteでも良かったんだけどやっぱMarkfownで書きたいという欲が強がね...
  • あとEvernoteで長文書くとめっちゃもっさりしてた時期があってストレスだった
  • Inkdropは作者がすごいスピードでレスくれたりバリバリ開発してくれてるので枯れるまでは使い続けそう(枯れたらどうなん...というのはわからない)

2017年を振り返る

今年もTwitterを遡って振り返ってる。そのため、いろいろなイベントを取りこぼしてるはず。

1月〜5月

  • ひたすらcluster.のロンチに向けて仕事してた気がする
  • 5月にロンチしたが、初回のイベント人多すぎて爆死した panora.tokyo

6〜7月

8月

9月

10月〜11月

12月

アニメ

  • 劇場版なのはさんを観た:
    • 観てる時にあれ、これ収集つくのか?とか思ってたら2部構成だったときの顔してた
  • 劇場版プリヤを観た:
    • 思ってた以上にプリズマシロウだったけど良かった
  • 劇場版Fate/stay night[Heaven's Feel]を観た:
    • ひたすら最高だった。原作またプレイしたくなったけど我慢する

ゲーム(クリアしたもののみ)

まとめ

  • 毎年、国士無双和了してる気がするので来年も実績解除するぞ
  • 個人Webサービスは結局作れなかった...個人開発のゲームもロンチできなかった...
  • Goは結構相変わらず書いてた
  • ドメイン知識以外のところで迷わなくなって開発速度Upを感じた
  • Kotlinを一応覚えた。書いてて結構気持ち良いので来年もやっていく
  • 仕事とのバランスをうまく保ててるようでバランス悪かったような気がした(メリハリがイマイチだったような?)
  • 仕事で必要なスキルや知識が技術面よりも組織とか開発体制みたいなところに寄ってる気がしてなんかモヤモヤ
  • 技術のステ振りとか何やっていくかとか今後何やりたいのかとかちゃんと考えていかないとな〜と思った

毎週テキサス・ホールデムをやってエンジニア力を鍛える弊社の紹介

これはCluster,Inc. Advent Calendar 2017の第10日目の記事です。

弊社の会社紹介しようかなと思います。 弊社VRの会社なんですが、社内では毎週テキサス・ホールデムのポーカーをやっています。(全然VR関係ないです 😇)

IVSのピッチコンテストLaunch Padというイベントで優勝して 本当に叶うAmazonウィッシュリスト というのがAmazonさんより贈呈されて、僕がポーカーやりたいなーと思ってポーカーテーブルをいれたらめっちゃ社内で流行って大満足という感じです 😄

他にもボドゲがいっぱい

これがIVSのイベントの記事です

jp.techcrunch.com

テキサス・ホールデムとは?

テキサス・ホールデム(Texas hold 'em)はポーカーの一種。各プレイヤーごとに配られる2枚の手札と、コミュニティ・カードと呼ばれる全プレイヤー共通のカード(最大5枚)を組み合わせてプレーする。アメリカ合衆国のカジノにおいては最もポピュラーなゲームのひとつである。通常は2人から10人で行われる。

Wikipediaより引用

社内の様子など

f:id:kyokomi:20171130030307j:plainf:id:kyokomi:20171208180933j:plain

  • 大体週1で開催されてる(毎週木曜の20:00くらいから23:00くらいまでやってる)
  • 前職でお世話になった@axrossさんが外部講師(?)として時々きてくれる(結構頻度高)
  • 社外の人も口コミというか、社員の友人的な感じで参加して結構ワイワイやってる
  • ポーカーは論理や確率に基づいたプレイが大事で、ポーカーが上手い人はエンジニアとしての仕事の進め方とか判断力を鍛えられている気がする(そんな気がする)
  • 主に上達目的でリングゲームをやっているがたまに気分転換にトーナメントをやったりしてる
  • ポーカー道あたりで用語を抑えたあとにフィル・ゴードン本(緑)を買って読むメンバーが結構多い(そろそろ会社の本棚に置いてもいいのでは感)あとはひたすら遊んでる
  • 社員13人中9人くらいポーカーやっててVRの会社のはずなんだけど、ポーカーの会社みたいになってる現状

フィル・ゴードンのポーカー攻略法 入門編 カジノブックシリーズ

フィル・ゴードンのポーカー攻略法 入門編 カジノブックシリーズ

弊社でポーカーで遊びたーいという方がいたら気軽にお声がけください!

現場で役立つシステム設計の原則を読んだ

ざっくり説明すると、DDDの文脈にあるドメインモデルやドメインオブジェクトとかを意識してプログラミングとかやっていきましょうという感じ。

エリック・エヴァンスのDDD本は概念的な話やユビキタス言語とかをちゃんと定義してドメインモデルを作っていってチーム全員でやっていき!って感じで、 なんか手っ取り早くいい感じのアーキテクチャとかプログラミングでパワーアップして、いい感じのプロダクトを作りたい!!!! って人には難しい内容となっていたと思います。

(DDD本、自分はとてもいい本だと思ってますので、あとで読むのはおすすめします)

本書はDDD本と違って、プログラミングからのアプローチでDDDの一部を徐々に学んでいく感じがしました。

他にもオブジェクト指向やWebAPI設計やDB設計やリファクタリングなど、システム開発で必要となる基本的な知識を様々な良書から引用して、実際に開発するときに役立つ形でまとまっていました。

正直、 数年前に読みたかった... という一冊です。(引用元の本はほとんど読んでいたので)

システム開発する前にたくさん読む本がある〜という人向けに一冊である程度必要な知識を得ることができてとても良いと感じました。 理解できなかったところやもうちょっと深掘りしたいと思ったものは、書籍に引用元の本がリンクされているのでそれを辿っていくと良さそうです。

いきなりDDDとか言われても... みたいな人にも優しい内容になっていて、とりあえずこの本の内容を理解して開発していればいずれ「DDDやるぞ!」ってなったときに割りとスムーズにやっていけそうだなと思いました。

goa勉強会 in 六本木一丁目でLTしてきましたので内容の補足など

istyle.connpass.com

LTしてきました。色々学びあって楽しかったです。

会場提供のistyleさんありがとうございました。

少しだけ補足説明とexampleコードを書いたので紹介しようと思います。

LTした資料

speakerdeck.com

注意点

  • goとgoaについてすでにある程度理解がある人向けです
  • goaのversionは、v1.2.0の前提で話しています

go generate使ってコード生成して開発する流れについて

–forceをつけることで毎回mainのgenerateは作り直してます。

//go:generate goagen -o gen main --force -d github.com/kyokomi/example_goa_api/_design
//go:generate goagen -o gen app -d github.com/kyokomi/example_goa_api/_design
//go:generate goagen -o gen swagger -d github.com/kyokomi/example_goa_api/_design

_design下を変更したときの作業手順は以下のような感じになります。

  1. _design下を編集する
  2. ルートディレクトリでgo generateする
  3. 生成された gen/main.go から追加したリソースのMountのコードをルートディレクトリの main.go にコピペする
  4. gen/ 下に生成されたリソースのControllerを application/controllers下にコピーしてimport等を直す(リソース追加ではなく編集の場合は、必要なMethodだけコピペする)

swaggerの配布について

werckerのworkflowの設定はこんな感じです。

f:id:kyokomi:20170617235511p:plain

buildのworkflowはexampleのリポジトリをご覧ください(https://github.com/kyokomi/example_goa_api/blob/master/wercker.yml)。 build のworkflowが終わったあとに design/* のブランチ名だったら put-swagger のworkflowが実行される感じになってます。

put-swagger:
  box: golang:1.8
  push-to-ecr:
  steps:
    - script:
        name: set timezone and deploy tag
        code: |
          export VERSION=`cat $WERCKER_ROOT/VERSION`
          echo $VERSION
    - edgecaseadmin/install-aws-cli:
        key:    $AWS_ACCESS_KEY_ID
        secret: $AWS_SECRET_ACCESS_KEY
        region: $AWS_REGION
    // s3にswagger.jsonをコピーする
    - script:
        name: s3 put swagger
        code: |
          aws s3 cp $WERCKER_ROOT/gen/swagger/ s3://swager.example.dev/$WERCKER_GIT_REPOSITORY/$VERSION/ --recursive
    // s3に配置したswaggerのjsonをurlに指定したswagger-uiのリンクをpullRequestコメントする
    - script:
        name: swagger github comment
        code: |
          go get github.com/kyokomi/gh-rice
          gh-rice \
            -t "$GITHUB_TOKEN" \
            -o "$WERCKER_GIT_OWNER" \
            -r "$WERCKER_GIT_REPOSITORY" \
            -b "$WERCKER_GIT_BRANCH" \
            -c "http://swagger.example.dev/?url=http://swagger.example.dev/$WERCKER_GIT_REPOSITORY/$VERSION/swagger.json"

※s3のパスとかは適当です

exampleコード

ヘルスチェックのAPIくらいしか書いてないのであれですが、参考になればと。

github.com

gormのCallbackをつかってexplain結果をログ出力する

はじめに

explainは基本的に実装時に自分で叩いてると思いますが、dev環境とかそこそこデータとか利用頻度が高い環境で雑に垂れ流したいなーと思ってgormのDebug実装を眺めていたらCallbackという仕組みがあったので、それを使ってみました。

作ったもの

github.com

使い方

gormのexampleをにちょこっと追加してみました。

package main

import (
    "fmt"

    _ "github.com/go-sql-driver/mysql" // mysqlを使う
    "github.com/jinzhu/gorm"
    explain "github.com/kyokomi/gorm-explain"
)

type options struct {
    user     string
    password string
    host     string
    port     int
    dbName   string
    location string
}

func buildDataSourceName(opts options) string {
    const format = "%s:%s@tcp(%s:%d)/%s?parseTime=True&loc=%s"
    return fmt.Sprintf(format, opts.user, opts.password, opts.host, opts.port, opts.dbName, opts.location)
}

// Product product table struct
type Product struct {
    gorm.Model
    Code  string
    Price uint
}

func main() {
    opts := options{
        user:     "test-user",
        password: "test-user",
        host:     "127.0.0.1",
        port:     3306,
        dbName:   "test_db",
        location: "UTC",
    }
    db, err := gorm.Open("mysql", buildDataSourceName(opts))
    if err != nil {
        panic("failed to connect database")
    }
    defer db.Close()
    db = db.Debug() // query trace
    db.Callback().Query().Register("explain", explain.Callback) // add explain callback

    // Migrate the schema
    db.AutoMigrate(&Product{})

    // Create
    db.Create(&Product{Code: "L1212", Price: 1000})

    // Read
    var product Product
    db.First(&product, 1)                   // find product with id 1
    db.First(&product, "code = ?", "L1212") // find product with code l1212

    // Update - update product's price to 2000
    db.Model(&product).Update("Price", 2000)

    // Delete - delete product
    db.Delete(&product)
}

実行した結果

(あと、gormのDebug機能をつかうとqueryがログに吐かれるようになるのでこれを合わせて使うと便利です)

(/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:53)
[2017-05-05 18:16:38]  [1.08ms]  INSERT INTO `products` (`created_at`,`updated_at`,`deleted_at`,`code`,`price`) VALUES ('2017-05-05 18:16:38','2017-05-05 18:16:38',NULL,'L1212','1000')

(/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:57)
[2017-05-05 18:16:38]  [0.95ms]  SELECT * FROM `products`  WHERE `products`.`deleted_at` IS NULL AND ((`products`.`id` = '1')) ORDER BY `products`.`id` ASC LIMIT 1
+-------+----------------+----------+---------------+---------+------------------+--------+------------+--------+---------+-------------+--------------------------------------------------------+
|    id |    select_type |    table |    partitions |    type |    possible_keys |    key |    key_len |    ref |    rows |    filtered |                                                  Extra |
+=======+================+==========+===============+=========+==================+========+============+========+=========+=============+========================================================+
|     1 |         SIMPLE |          |               |         |                  |        |            |        |         |             |    Impossible WHERE noticed after reading const tables |
+-------+----------------+----------+---------------+---------+------------------+--------+------------+--------+---------+-------------+--------------------------------------------------------+



  1 Fix example to add query trace

(/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:58)
[2017-05-05 18:16:38]  [1.11ms]  SELECT * FROM `products`  WHERE `products`.`deleted_at` IS NULL AND ((code = 'L1212')) ORDER BY `products`.`id` ASC LIMIT 1
+-------+----------------+-------------+---------------+---------+----------------------------+----------------------------+------------+----------+---------+-------------+---------------------------------------+
|    id |    select_type |       table |    partitions |    type |              possible_keys |                        key |    key_len |      ref |    rows |    filtered |                                 Extra |
+=======+================+=============+===============+=========+============================+============================+============+==========+=========+=============+=======================================+
|     1 |         SIMPLE |    products |               |     ref |    idx_products_deleted_at |    idx_products_deleted_at |          5 |    const |       2 |         100 |    Using index condition; Using where |
+-------+----------------+-------------+---------------+---------+----------------------------+----------------------------+------------+----------+---------+-------------+---------------------------------------+


(/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:61)
[2017-05-05 18:16:38]  [0.86ms]  UPDATE `products` SET `price` = '2000', `updated_at` = '2017-05-05 18:16:38'  WHERE `products`.`deleted_at` IS NULL AND `products`.`id` = '7'

(/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:64)
[2017-05-05 18:16:38]  [0.88ms]  UPDATE `products` SET `deleted_at`='2017-05-05 18:16:38'  WHERE `products`.`deleted_at` IS NULL AND `products`.`id` = '7'

注意点

さっと書いたので、fmtで出力してます。ちゃんと自前のロガーとかで出力したい場合は、このコード参考に自分で書いてください 🙏🏻 (もしくはPullRequestまってます 😇 )

おわり

explainの結果に filesortが含まれていたら〜みたいなHandlerを設定できるような感じに改修とかすると便利そうだな〜とか思ってます。