- 投稿日:2020-06-29T22:29:28+09:00
Goのはじめかた
はじめに
この記事ではローカルにGoの環境を構築しHello Worldを表示するまでのメモを記載してます。
数年サーバサイドはPHPを触っていて、静的言語には無縁でしたがそろそろPHP以外の言語にチャレンジしてみたいというところと、あまり趣味がないの始めてみることにしました。環境
MacOS 10.15
Go1.14.4
Homebrew 2.4.2Go Install
インストールはHomebrewを使えば非常にシンプルです。
$ brew update $ brew install goインストールされているかversionを表示させ確認しましょう。
$ go versionコード実行
コード実行も非常に簡単です。
まずgoファイルを作成します。$ touch main.goその後以下を
main.go
に記載します。package main import "fmt" func main() { fmt.Println("HelloWorld") }こちらで最低限必要なコードは記述できました。
続いて実行ですが2パターン選択肢があります。// run go run main.go // build go build main.go ./mainGoはコンパイラ言語のため実行都度コンパイルし実行する必要があります。それがbuildのパターンです。
一度go build
でビルド後、直下のファイルを指定し実行します。
もう一つがgo run
のパターンです。
毎回ビルドするのはとても手間がかかるのでビルド+実行を同時に行ってくれる優れものです。それぞれ試してみましょう,
HelloWorld
と値が出力されれば成功です。最後に
いかがだったでしょうか?非常に簡単に実行できたと思います。
案件数の兼ね合い等もあり、0からエンジニアを目指す方にはあまりオススメできないかもしれませんが、既にバックエンドエンジニアとして就業され、PHP,Rudyの次に学ぶ言語としてはとても良いのかなと考えております。あと
Go 〇〇
で検索しても全然ヒットしないですね笑
- 投稿日:2020-06-29T21:13:16+09:00
cobra-cmder で Go の CLI を簡単に作る
概要
Go 言語における CLI アプリ作成で一番使われる定番のライブラリといえば spf13/cobra でしょう。 Hugo や Kubernetes といった著名なプロジェクトでも cobra を使っているそうです。
しかしながら自分の場合、フラグの値を格納するのにグローバル変数を使ったり、初期化で
init()
を使ったりという cobra の流儀が気に入らず、これまで利用を敬遠してきました。一番問題だと考えているのはユニットテストの記述が困難であることです。もちろん cobra でもグローバル変数や
init()
を一切使わず構造体のフィールドやメソッドだけで構成することは可能ですが、その場合 cobra コマンド (コードジェネレータ) の支援はなく、すべて手で書く必要があります。先日より、上記の問題点を解消しようと取り組んだところ、簡単かつエレガントな cobra の構成手法を確立することができました。そのためのライブラリを yaegashi/cobra-cmder というモジュールとして公開したので紹介します。
基本的な使用法
cobra-cmder で CLI を作る例題として、次のような呼び出し方ができる app というアプリを考えてみます。
$ app alpha one -h Usage: app alpha one [flags] Flags: -h, --help help for one -i, --int int Int flag Global Flags: -b, --bool Bool flag -s, --string string String flag $ app alpha one -b -s abc -i 123 true abc 123フラグ (オプション) は次のように各コマンドで定義されています。また、上位コマンドのフラグは下位コマンドでも使うことができるものとします。
フラグ 型 コマンド -b
--bool
bool
app
-s
--string
string
app alpha
-i
--int
int
app alpha one
コマンド型の定義
まず、各コマンドの専用型となる構造体を定義します。各コマンド型には、フラグの値を格納するフィールドと、上位コマンド型を参照する埋め込みフィールド (最上位のコマンド型を除く) を用意しておきます。
// App - app command type App struct { Bool bool // storage for flag -b } // AppAlpha - app alpha command type AppAlpha struct { *App // storage for parent Cmder (embedded) String string // storage for flag -s } // AppAlphaOne - app alpha one command type AppAlphaOne struct { *AppAlpha // storage for parent Cmder (embedded) Int int // storage for flag -i }コマンド型の名前はなんでもかまいませんが、この例のようにコマンド階層を反映したものにしておくことをおすすめします。
cmder.Cmder インターフェースの実装
cobra-cmder で重要な役割を演じるのは、次に示す
cmder.Cmder
インターフェースです。type Cmder interface { Cmd() *cobra.Command }次の手順では、各コマンド型について
cobra.Command
を返すCmd()
メソッドを実装することで、 このcmder.Cmder
インターフェースに適合するようにしていきます。func (app *App) Cmd() *cobra.Command { cmd := &cobra.Command{ Use: "app", } cmd.PersistentFlags().BoolVarP(&app.Bool, "bool", "b", false, "Bool flag") return cmd } func (app *AppAlpha) Cmd() *cobra.Command { cmd := &cobra.Command{ Use: "alpha", } cmd.PersistentFlags().StringVarP(&app.String, "string", "s", "", "String flag") return cmd } func (app *AppAlphaOne) Cmd() *cobra.Command { cmd := &cobra.Command{ Use: "one", Run: app.Run, } cmd.Flags().IntVarP(&app.Int, "int", "i", 0, "Int flag") return cmd } func (app *AppAlphaOne) Run(cmd *cobra.Command, args []string) { fmt.Println(app.Bool, app.String, app.Int) }最後の
AppAlphaOne
型のメソッドについて、次の点に注目してください。
AppAlphaOne.Cmd()
で特定インスタンスに対するメソッド呼び出しapp.Run
を関数として設定しています。このような呼び出しは Go 言語では method values としてサポートされています。AppAlphaOne.Run()
ではapp.Bool
app.String
のように上位のコマンド型で定義されたフィールドに直接アクセスしています。これができるのは各コマンド型の定義で上位コマンド型の埋め込みをしているからです。上位コマンドを含むフラグの値を格納した変数へのアクセスが、グローバル変数を使うことなく自然な形で実現できています。
コマンド階層の関連付け
次のようなメソッド定義により、各コマンドの階層の関連付けを行います。
func (app *App) AppAlphaCmder() cmder.Cmder { return &AppAlpha{App: app} } func (app *AppAlpha) AppAlphaOneCmder() cmder.Cmder { return &AppAlphaOne{AppAlpha: app} }上位コマンド型に下位コマンド型のインスタンスを生成し
cmder.Cmder
として返すメソッドを追加します。また下位コマンド型インスタンスには上位コマンド型インスタンスのポインタを設定します。これらのメソッドの名前はなんでも構いません。ただし
cmder.Cmder
の全階層にわたりユニークな必要があるので、この例のようにコマンド型の名前を一部に使用することをおすすめします。cobra.Command の生成と実行
最後に
main()
を実装します。次のようにcmder.Cmd()
関数に最上位コマンド型のインスタンスを渡して呼び出すことにより、コマンド型の階層をトラバースしてすべての設定が反映されたcobra.Command
を生成することができます。これはそのままExecute()
メソッドで実行できます。func main() { app := &App{} cmd := cmder.Cmd(app) err := cmd.Execute() if err != nil { os.Exit(1) } }以上で CLI の実装は完了です。完全なソースコードが Go Playground にあるので参照してください。
main()
のcmd.SetArgs()
により、様々なコマンドライン引数を与えて動作確認ができます。ユニットテスト
cobra-cmder を使えばグローバル変数や
init()
を使わずに cobra を構成できますので、CLI のユニットテストが容易にできます。 Go Playgound に例があるので参照してください。CLI からの出力をモッキングするため、最上位コマンド型
App
に対して変数Out
とメソッドPrintln
Printf
を追加しています。type App struct { Out io.Writer // mocking output Bool bool // storage for flag -b } func (app *App) Print(args ...interface{}) (int, error) { return fmt.Fprint(app.Out, args...) } func (app *App) Println(args ...interface{}) (int, error) { return fmt.Fprintln(app.Out, args...) } func (app *App) Printf(format string, args ...interface{}) (int, error) { return fmt.Fprintf(app.Out, format, args...) }これらは
AppAlphaOne
のような下位コマンド型からも呼び出して使うことができます。func (app *AppAlphaOne) Run(cmd *cobra.Command, args []string) { app.Println(app.Bool, app.String, app.Int) }テスト本体では次のように Go 言語で標準的なテーブルベースのテストが実現できています。
func TestApp(t *testing.T) { tests := []struct { args []string want string err bool }{ {args: []string{"alpha", "one", "-b", "-s", "abc", "-i", "123"}, want: "true abc 123\n", err: false}, {args: []string{}, want: "", err: false}, {args: []string{"alpha"}, want: "", err: false}, {args: []string{"alpha", "one"}, want: "false 0\n", err: false}, {args: []string{"beta"}, want: "", err: false}, {args: []string{"alpha", "-i", "123"}, want: "false 123", err: false}, } for _, tt := range tests { args := strings.Join(tt.args, " ") buf := &bytes.Buffer{} cmd := cmder.Cmd(&App{Out: buf}) cmd.SetArgs(tt.args) cmd.SetOut(ioutil.Discard) cmd.SetErr(ioutil.Discard) err := cmd.Execute() if tt.err && err == nil { t.Errorf("%q returns no error", args) } if !tt.err && err != nil { t.Errorf("%q returns error: %s", args, err) } got := string(buf.Bytes()) if got != tt.want { t.Errorf("%q returns %q, want %q", args, got, tt.want) } } }このテストの実行結果は次のとおりです。
=== RUN TestApp TestApp: prog.go:103: "beta" returns error: unknown command "beta" for "app" TestApp: prog.go:103: "alpha -i 123" returns error: unknown shorthand flag: 'i' in -i TestApp: prog.go:107: "alpha -i 123" returns "", want "false 123" --- FAIL: TestApp (0.00s) FAILその他の使用例
サンプルコード cmd/sample は、 cobra-cmder を使ったより複雑なコマンド階層の構築例です。
このサンプルでは、ひとつのコマンドごとにひとつのファイルを使用しています。メソッド定義の追加という非侵襲的な手法でコマンド階層の関連付けしていることから、このように各コマンドの実装をファイル単位で独立させることができ、コマンドの追加・削除などのメンテナンスが容易になっています。
cobra-cmder は次のような CLI アプリケーションでも使われています。
cobra-cmder の実装
cobra-cmder は
cmder.Cmder
インターフェースとcmder.Cmd()
という小さな関数で構成されます。これらは cmder.go ファイルに実装されています。// Cmd traverses a Cmder hierarchy and returns a configured cobra.Command. // It recursively calls all methods that return a Cmder // to collect and associate cobra.Command instances. func Cmd(c Cmder) *cobra.Command { return recCmd(c, map[string]bool{}) } // outTypes is a constant for the array of function output types var outTypes = []reflect.Type{reflect.TypeOf((*Cmder)(nil)).Elem()} // recCmd is the actual worker function to visit and collect Cmder instances. // mmap is for bookkeeping already visted method names. func recCmd(c Cmder, mmap map[string]bool) *cobra.Command { cmd := c.Cmd() inV := reflect.ValueOf(c) inT := reflect.TypeOf(c) funcT := reflect.FuncOf([]reflect.Type{inT}, outTypes, false) methods := []reflect.Method{} for i := 0; i < inT.NumMethod(); i++ { m := inT.Method(i) if m.Func.Type() != funcT || mmap[m.Name] { continue } methods = append(methods, m) } for _, m := range methods { mmap[m.Name] = true } for _, m := range methods { subC := m.Func.Call([]reflect.Value{inV})[0].Interface().(Cmder) subCmd := recCmd(subC, mmap) cmd.AddCommand(subCmd) } for _, m := range methods { mmap[m.Name] = false } return cmd }関数の本体である
cmder.recCmd()
はリフレクションを利用し、引数のコマンド型インスタンスに定義されたメソッドでcmder.Cmder
を返すものを呼び出し、得られたcmder.Cmder
を下位のコマンド型として、自分自身を再帰的に呼び出します。そうして集めた下位コマンドの
cobra.Command
は、引数のコマンド型インスタンスのCmd()
メソッドで得たcobra.Command
にAddCommand()
メソッドで関連付けられます。これを戻り値として関数を終了します。上位コマンド型の埋め込みをしている場合、上位コマンド型で定義されたメソッドもリフレクションで取得できてしまいますが、それらは呼び出しのループを避けるために呼ばないようにする必要があります。
しかしながら Go 言語のランタイムでは、埋め込みのフィールドに由来するメソッドとそうでないメソッドを区別する方法がありません。そこでやむなく
mmap
マップにより一度呼んだメソッド名を記録する手段を採用しています。コマンド型の階層間でメソッド名がユニークな必要があるという制約はこの実装により生じています。まとめ
cobra-cmder を使えば cobra でグローバル変数や
init()
を使うことなく CLI のコマンド階層の構築ができることを示しました。この手法には次のような特長があります。
- 各コマンドの実装の記述が簡単
- グローバル変数および
init()
の排除によりユニットテストの記述が容易- method values や埋め込みフィールドの活用による、コマンド階層を反映したわかりやすいストレージアクセス
- メソッド定義を用いた非侵襲的なコマンド階層の関連付け手法による、各コマンドの追加・削除などのメンテナンス性の向上
その一方で次の改善点が考えられます。
cmder.Cmd()
ではリフレクションを利用しており実行の都度コマンド型の階層をトラバースしているため、実行開始までに時間がかかる。ビルド前に静的なコマンド階層構築のコードを自動生成できることが望ましい。以上です。気に入ったらぜひ使ってみてください。プルリクエストもお待ちしております!
- 投稿日:2020-06-29T19:02:32+09:00
無料で個人開発をはじめてみようと思っている人へおすすめしたいアレコレ2020
初めましてみきおです、今までずっとROM専でした。
業務では主にバックエンドを担当しています。フロントエンドは生来のデザインセンスの悪さもあって避けてきましたが、去年あたりからフルスタックエンジニアという言葉に憧れを抱き、ずっとやりたいやりたいと思っているだけだった個人開発を今年ついにはじめました。
ようやくWEBアプリをほぼほぼ無料で2つほど作成したので2020年版(2019年から)のおすすめのあれやこれやを紹介したいと思います。
基本的にドメイン以外は全部無料でできます。対象
- これから個人開発をはじめようとしている人
- 個人開発について読み漁ったけれどどの構成がベストなのか悩んでいる人
- 無料でどこまでできるのか気になってる人
- 他の人がどんな構成にしているか興味がある人
はじめに
個人開発をはじめるにあたって、先人たちのブログやQiitaの投稿を読み漁りましたが、大きく2つのポイントがあると感じました。
- 継続
- 集客1
継続については、イニシャルコストはもちろんのこと、ランニングコストも込みでいかに安くで稼働させ続けられるかが肝になります。大手を含め無料で利用できるサービスが増えてきており、先人たちが公開してくれた4,5年前の無料構成と少しずつ変化してきているため、今回実際に使用したものをメインに紹介していきます。
また、WEBアプリの構成を記載しますが、バックエンド的には変わりはないはずなのでスマホアプリの場合でも参考になるかと思います。ホスティング
現状は文句なしでOracle Always Free のコンピュートだと思います。
ホスティングサービスではないためもちろん各種設定を自前でやらないといけないですが、下記が無料のホスティングサービスや他の無料枠のコンピュートとは違います。
- 東京リージョンが選択可能
- 1GBのメモリ
レスポンスを気にするのであれば東京(日本内)リージョンが必須条件になってくるかと思います。
また、メモリについて多くが500MB以下でした。1つのWEBアプリをホストするならそれだけで済むかもしれませんが、複数のWEBアプリのホストやアクセス数の増加によって一時的だとしてもメモリの使用量の増えてしまう場合も考慮すると物理1GBは安心感があります。
私は2台の無料枠のうち1台を開発2とテスト環境にし、もう1台を本番環境にしています。言語
使い慣れた得意な言語が良いですが、せっかくだから別の言語を学びたいという方にはGO言語をおすすめします。
- 単体でWEBサーバーとして稼働可能
- 軽量、起動時、驚愕の10MB3
- 言語自体がhttpやtemplate機能をサポートしていて楽。
- フルスタックを含むWEBアプリケーションフレームワークが群雄割拠で競い合っていてレスポンスの改善や機能の拡充が目まぐるしい
その他Goの特徴としてマルチスレッドが挙げられますが、今回はそこまで重要ではないため割愛します。
DB
NoSQL
NoSQLならdynamodbです。永年無料で25GBの容量があります。ただし25GBの容量欲しさにSQLのようなことをさせようとすると痛い目をみます。(軽傷で済みましたが痛い目みました。)
SQL
SQLなら暫定でoracle always free ATPです。20GBあります。ただし注意点がいくつかあります。
別でおすすめがあればぜひ教えてください。
ドメイン
古くからの正統派ドメインか新興の格安ドメインか
語呂遊びなどが理由でない限りは.comなどの正統派ドメインの中で比較的安いドメインをおすすめします。.workや.xyzなどの格安ドメインは1円~とセールがされていますが、アダルトな業者や悪徳業者がよく利用しているようでtwitterでの表示や検索で除外されるとの情報を見かけます。
イニシャルコストも最低限にしたい場合は格安ドメインで良いかと思いますが、見栄えも考慮してケチらずに正統派ドメインをおすすめします。複数ドメインかサブドメインか
よほどの自信がない限りはサービスごとにドメインを取得する必要はなくサブドメインで問題ないでしょう。
ちなみに私は1つ目のWEBサービスの公開時に2つ目のことを全く考えていなかったため、現在は1つ目が独自ドメイン、2つ目が新たに取得したドメインのサブドメインという構成になっています。定期処理系
awsのlambdaもしくはgcpのCloud Functionsで決まりだと思います。
それぞれ条件によって実行環境が変わるため、グローバルIPが勝手に変わります。スクレイピングとの相性が良すぎます。
私はdynamodbにデータを入れたかったためlambdaを使用しました。
スクレイピングは用法用量を正しくお守り下さい。おわりに
ちなみに今回作成したWEBアプリは「書籍ランキングをnote記事から集計」するBlog Book Rankというもの7であり、構成は以下になっています。
- aws lambda(定時集計用)
- 言語:golang
- aws dynamodb(集計結果格納用)
- oracle always freeマシン(WEBサーバー用)
- letsencrypt-証明書、cronで自動更新(webroot方式じゃないと証明書更新時にWEBサービスにstopが発生)
- nginx-ドメイン単位のルーティング(以前のWEBアプリも同居のため振り分けしたりhttpsに飛ばしたり)
- golang, gin(実働WEBアプリ)8
- bootstrap, jquery, awesomefont(画面整え用)
それぞれの手順や詳細、1つ目に作成したWEBアプリについても今後投稿していければと思います。
ちなみに本記事も集客の一環です。 ↩
code-serverを入れてどのPCからでも開発ができるようにしていますが、詳細は別途投稿予定です。 ↩
2つ目に作成したほぼ静的なWEBアプリだと常時12MBほどです。ログイン機能などをつけた1つ目のアプリは徐々に上がっていっていますが、100MBほどで安定しています。 ↩
クエリ実行の最適化まわりのデータらしく、勝手に減ったりしますが基本増えていきます。それが原因でDBは止まりませんが、容量が増えていく様はストレスを感じてしまうため胸をはっておすすめできない一番の理由になってます。現象についてご存知の方がいましたら教えてください。 ↩
無料枠の仕様ですが、アクセスしていても停止される時があって困っています。 ↩
私の場合はサポートされたいないことを知らずに利用を始めてしまったため、結果としてフレームワーク側を修正しました。めちゃめちゃ時間かかりました。 ↩
わかる人にはわかるかと思いますがテック・ブック・ランクに触発されて作っています。 ↩
1つ目のWEBアプリではginではなくbeegoを使いました。 ↩
- 投稿日:2020-06-29T19:02:32+09:00
これから個人開発をはじめてみようと思っている人へおすすめしたいアレコレ2020
初めましてみきおです、今までずっとROM専でした。
業務では主にバックエンドを担当しています。フロントエンドは生来のデザインセンスの悪さもあって避けてきましたが、去年あたりからフルスタックエンジニアという言葉に憧れを抱き、ずっとやりたいやりたいと思っているだけだった個人開発を今年ついにはじめました。
ようやくWEBアプリをほぼほぼ無料で2つほど作成したので2020年版(2019年から)のおすすめのあれやこれやを紹介したいと思います。
基本的にドメイン以外は全部無料でできます。対象
- これから個人開発をはじめようとしている人
- 個人開発について読み漁ったけれどどの構成がベストなのか悩んでいる人
- 無料でどこまでできるのか気になってる人
- 他の人がどんな構成にしているか興味がある人
はじめに
個人開発をはじめるにあたって、先人たちのブログやQiitaの投稿を読み漁りましたが、大きく2つのポイントがあると感じました。
- 継続
- 集客1
継続については、イニシャルコストはもちろんのこと、ランニングコストも込みでいかに安くで稼働させ続けられるかが肝になります。大手を含め無料で利用できるサービスが増えてきており、先人たちが公開してくれた4,5年前の無料構成と少しずつ変化してきているため、今回実際に使用したものをメインに紹介していきます。
また、WEBアプリの構成を記載しますが、バックエンド的には変わりはないはずなのでスマホアプリの場合でも参考になるかと思います。ホスティング
現状は文句なしでOracle Always Free のコンピュートだと思います。
ホスティングサービスではないためもちろん各種設定を自前でやらないといけないですが、下記が無料のホスティングサービスや他の無料枠のコンピュートとは違います。
- 東京リージョンが選択可能
- 1GBのメモリ
レスポンスを気にするのであれば東京(日本内)リージョンが必須条件になってくるかと思います。
また、メモリについて多くが500MB以下でした。1つのWEBアプリをホストするならそれだけで済むかもしれませんが、複数のWEBアプリのホストやアクセス数の増加によって一時的だとしてもメモリの使用量の増えてしまう場合も考慮すると物理1GBは安心感があります。
私は2台の無料枠のうち1台を開発2とテスト環境にし、もう1台を本番環境にしています。言語
使い慣れた得意な言語が良いですが、せっかくだから別の言語を学びたいという方にはGO言語をおすすめします。
- 単体でWEBサーバーとして稼働可能
- 軽量、起動時、驚愕の10MB3
- 言語自体がhttpやtemplate機能をサポートしていて楽。
- フルスタックを含むWEBアプリケーションフレームワークが群雄割拠で競い合っていてレスポンスの改善や機能の拡充が目まぐるしい
その他Goの特徴としてマルチスレッドが挙げられますが、今回はそこまで重要ではないため割愛します。
DB
NoSQL
NoSQLならdynamodbです。永年無料で25GBの容量があります。ただし25GBの容量欲しさにSQLのようなことをさせようとすると痛い目をみます。(軽傷で済みましたが痛い目みました。)
SQL
SQLなら暫定でoracle always free ATPです。20GBあります。ただし注意点がいくつかあります。
別でおすすめがあればぜひ教えてください。
ドメイン
古くからの正統派ドメインか新興の格安ドメインか
語呂遊びなどが理由でない限りは.comなどの正統派ドメインの中で比較的安いドメインをおすすめします。.workや.xyzなどの格安ドメインは1円~とセールがされていますが、アダルトな業者や悪徳業者がよく利用しているようでtwitterでの表示や検索で除外されるとの情報を見かけます。
イニシャルコストも最低限にしたい場合は格安ドメインで良いかと思いますが、見栄えも考慮してケチらずに正統派ドメインをおすすめします。複数ドメインかサブドメインか
よほどの自信がない限りはサービスごとにドメインを取得する必要はなくサブドメインで問題ないでしょう。
ちなみに私は1つ目のWEBサービスの公開時に2つ目のことを全く考えていなかったため、現在は1つ目が独自ドメイン、2つ目が新たに取得したドメインのサブドメインという構成になっています。定期処理系
awsのlambdaもしくはgcpのCloud Functionsで決まりだと思います。
それぞれ条件によって実行環境が変わるため、グローバルIPが勝手に変わります。スクレイピングとの相性が良すぎます。
私はdynamodbにデータを入れたかったためlambdaを使用しました。
スクレイピングは用法用量を正しくお守り下さい。おわりに
ちなみに今回作成したWEBアプリは「書籍ランキングをnote記事から集計」するBlog Book Rankというもの7であり、構成は以下になっています。
- aws lambda(定時集計用)
- 言語:golang
- aws dynamodb(集計結果格納用)
- oracle always freeマシン(WEBサーバー用)
- letsencrypt-証明書、cronで自動更新(webroot方式じゃないと証明書更新時にWEBサービスにstopが発生)
- nginx-ドメイン単位のルーティング(以前のWEBアプリも同居のため振り分けしたりhttpsに飛ばしたり)
- golang, gin(実働WEBアプリ)8
- bootstrap, jquery, awesomefont(画面整え用)
それぞれの手順や詳細、1つ目に作成したWEBアプリについても今後投稿していければと思います。
ちなみに本記事も集客の一環です。 ↩
code-serverを入れてどのPCからでも開発ができるようにしていますが、詳細は別途投稿予定です。 ↩
2つ目に作成したほぼ静的なWEBアプリだと常時12MBほどです。ログイン機能などをつけた1つ目のアプリは徐々に上がっていっていますが、100MBほどで安定しています。 ↩
クエリ実行の最適化まわりのデータらしく、勝手に減ったりしますが基本増えていきます。それが原因でDBは止まりませんが、容量が増えていく様はストレスを感じてしまうため胸をはっておすすめできない一番の理由になってます。現象についてご存知の方がいましたら教えてください。 ↩
無料枠の仕様ですが、アクセスしていても停止される時があって困っています。 ↩
私の場合はサポートされたいないことを知らずに利用を始めてしまったため、結果としてフレームワーク側を修正しました。めちゃめちゃ時間かかりました。 ↩
わかる人にはわかるかと思いますがテック・ブック・ランクに触発されて作っています。 ↩
1つ目のWEBアプリではginではなくbeegoを使いました。 ↩
- 投稿日:2020-06-29T11:40:11+09:00
GoとNuxtで飲食店検索ができるLINE BOTとLINEミニアプリ(LIFF)を作ってみた
はじめに
Go / Nuxt.js / LINE Messaging API / Google Maps API / クリーンアーキテクチャ で、飲食店検索やお気に入り登録できるLINE BOTとLIFFアプリを作ってみました!
LIFFアプリとはLINEやWeb上で動作するアプリのことです。
LINEミニアプリと呼ばれることもあります。
詳しくは後述します。LINE BOTを含めバックエンドは元から作っていたのですが、ビジネスロジック部分をAPI化してLIFFと連携できるようにしてみました。
バックエンドに関する部分は以下の記事にまとめています。
こちらを読んでいなくても、この記事は読めるように書いています!
バックエンドやクリーンアーキテクチャに興味のある方はぜひ読んでみてください。Goとクリーンアーキテクチャで飲食店検索ができるLINE BOT作ってみた
⇒Qiitaデイリーで最高5位にランクインしました!この記事では、全体構成やLIFFアプリに関する部分などをまとめようと思います。
読んだら幸せになれそうな人
- 個人開発に興味がある人
- モダンな技術に興味がある人
- Go, Nuxtに興味がある人
- LINE Messaging API (LINE BOT) やLIFFに興味がある人
こんなのつくりました
LINE BOT
デモ動画のロングバージョンはこちら(Twitter)にあります。
LIFF
デモ動画のロングバージョンはこちら(Twitter)にあります。
(くそ画質ですいません。。)お試しはこちら
LINE BOT
以下のURLにアクセスするか、QRコードをLINEで読み取るとお試しできるので、良ければ試してみてください!
LIFF
上記からアクセスするとLINE上で起動することができます。
Webブラウザ上ではこちらからお試し頂けます。
後述しますが、LINE上で起動する方が安定しています。。
ちなみにデモ動画はLINE上で起動したものです。つくった目的
以下を目的として作ってみました。ほぼ前の記事と同じです。
- Goとクリーンアーキテクチャ、Nuxtの習熟
- LINE関係のノウハウの蓄積
- 好きな飲食店を気軽にメモしたかった(でも食べログなどのアプリは入れたくなかった)
Goは人気急上昇中の言語なので、今のうちに勉強してGo案件にジョインしたいという下心強めです笑
クリーンアーキテクチャを採用した理由は、自分がMVCパターンにしか触れたことしかなく、そろそろ新しいフレームワークを習得したいと思ったからです。
また、LINE関係のノウハウを貯めておきたかったのでLINE BOTを題材にしました。
LINE上でミニアプリが動作できるようになる LINEミニアプリ が近いうちにリリースされるみたいなので波乗りしたいところです。ソースコード
LIFFってなに
説明
簡単に言うと、LINEと簡単に連携できて、ブラウザやLINE上で動作するアプリを作れるやつです。
以下公式HPからの抜粋です。
LINE Front-end Framework(LIFF)は、LINEが提供するウェブアプリのプラットフォームです。このプラットフォームで動作するウェブアプリを、LIFFアプリと呼びます。
LIFFアプリを使うと、LINEのユーザーIDなどをLINEプラットフォームから取得できます。LIFFアプリではこれらを利用して、ユーザー情報を活用した機能を提供したり、ユーザーの代わりにメッセージを送信したりできます。
Front-end Frameworkということで、LIFFはフロントエンドで動作します。
入門はこちら
以下にLIFF入門者向けの記事を書いていますので良かったら参考にしてください。
【LINE/LIFF入門】LIFFでLINE公式アカウント(旧LINE@)上に予約フォームを作成する(GitHub Pages使用)ちなみに上記記事をもとにLTもしました!
以下にその時のスライドがあるのでこちらも参考にしてみてください。
LIFFでLINE公式アカウント上に予約フォームを作成する
技術面全般
使用技術
- Go 1.14
- Nuxt.js 2.13
- LINE Messaging API
- LINE BOTが作れるAPI
- Google Maps API
- Google Mapの情報を取得できるAPI
- LIFF
- クリーンアーキテクチャ
- Heroku
Goの使用ライブラリはこちらに記載しています。
全体構成
- GoサーバとNuxtサーバの2つを用意
- サーバはHerokuを使用
- GoでLINE BOTとビジネスロジックを実装
- ビジネスロジックにはLINE BOT用の口とAPI用の口の2つを持たせる
- ビジネスロジックでLINEやGoogleMapのAPIをごちゃごちゃやっています
- NuxtでLIFFアプリを実装
- LIFFアプリからGoで実装したAPIをたたく
今回の肝
LINEユーザは一意のユーザIDを持っています。
これを使うことで、LINE BOTからお気に入り登録しても、LIFFからお気に入り登録しても、どちらにも同じ結果が反映されるようにしました。Nuxt
Nuxtそのものは特筆すべき使い方はしませんでした。
Nuxtのディレクトリ構成や思想に従って、淡々とView部分やAPI通信部分を実装しました。CORS
今回はNuxtサーバから、別ドメインであるGoサーバのAPIを叩きに行っています。
これを実現するにはCORSの設定が必要であり、何も設定しないと別のドメインからはアクセスできない(はず)です。自分はGoのechoというフレームワークを使っており、CORSはこんな感じ(github)設定できます。
server.goe.Use(middleware.CORSWithConfig(middleware.CORSConfig{ AllowOrigins: []string{"Nuxtサーバのドメイン"}, }))他ドメインからのアクセスを全て許容する場合は以下の通りです。
e.Use(middleware.CORS())NuxtとLIFF
実装する上で2つのポイントがあります。
このポイントを抑えておけば淡々と実装していくだけです。LIFFの初期化&LINEログイン
LIFFを使うにはまずLIFFの初期化が必要です。
また、LINEログインはLINE上でアプリを起動する場合には不要ですが、Webブラウザからアクセスする場合には必要です。今回はstoreのactionにこんな感じ(github)で実装しました。
storeやactionはVuexの知識です。store/index.jsexport const actions = { login({ commit, dispatch }) { liff .init({ liffId: process.env.LIFF_ID }) .then(() => { // Webブラウザからアクセスされた場合は、LINEにログインする if (!liff.isInClient() && !liff.isLoggedIn()) { window.alert("LINEアカウントにログインしてください。") liff.login({ redirectUri: location.href }) } var token = liff.getIDToken() this.$cookies.set('jwt_token', token) commit('mutateLineIDToken', token) }) .catch(err => { console.log("LIFF Initialization failed ", err) }) this.app.router.push('/') } }ログイン画面遷移時にこのlogin関数を実行して、LIFF初期化&LINEログインを行います。
LINEログインといっているのは、コード中のliff.login({ redirectUri: location.href })
の部分です。
ログイン後にはliff.getIDToken()
でIDTokenを取得してstoreとcookieに保存します。この実装だけだとIDTokenが失効した際にエラーが出てしまうのですが解消していません。
なんかめんどくさくなって諦めちゃいました (^q^)IDTokenからユーザIDを抽出する
LIFFでIDTokenを取得して、それをバックエンドに投げてユーザIDを抽出します。
LIFF単体でユーザIDを取得することもできますが、ユーザIDをそのままバックエンドに投げてしまうとセキュリティ的によろしくないためひと手間加えます。ここの実装に結構時間がかかりましたが仕方ないですね。。
抽出方法については、LINE Developersにまとまっていました。以下シーケンス図の抜粋です。
IDTokenをバックエンドに投げた後、さらにLINE PlatformのAPIを叩くことでユーザIDを抽出できます。
IDTokenが失効していると怒られます。バックエンドはこんな感じ(github)で実装しました。
GoでPOSTのHTTPリクエストを送る際は、http.PostForm
を使います。api_controller.gotype verifyResp struct { Sub string `json:"sub"` } // getLineUserIDByToken tokenからLINEのuserIDを取得する func getLineUserIDByToken(idToken string) string { values := url.Values{} values.Add("id_token", idToken) values.Add("client_id", os.Getenv("LIFF_CHANNEL_ID")) resp, err := http.PostForm( "https://api.line.me/oauth2/v2.1/verify", values, ) if err != nil { logrus.Errorf("Error Parsing LINEIDToken: %v", err) return "" } body, _ := ioutil.ReadAll(resp.Body) defer resp.Body.Close() jsonBytes := ([]byte)(string(body)) data := new(verifyResp) if err := json.Unmarshal(jsonBytes, data); err != nil { logrus.Errorf("Error JSON Unmarshal: %v", err) return "" } return data.Sub }LINEBOTとLIFFを連携させる意義
実際のビジネスやサービス的なところにはあまり繋がらないかもしれませんね。
今回はあくまで技術習得をメインとして作っていたのですが、途中から「あれ、これ意味あるんか。。」となってモチベーションが下がった時もありました 笑LINE BOTにもLIFFにもそれぞれ長所/短所があるので、互いに補い合える可能性はあるかもしれません。(浅い意見)
機能説明
クリーンアーキテクチャの理解に時間かけすぎて(言い訳)、大したもの作っていないのでさらっとだけ。
キーワード検索
場所の名前や調べたい情報を入力すると、検索結果を返す。
位置情報検索(LINE BOTのみ)
トーク画面の左下の「+」ボタンをタップして「位置情報」を選択すると、位置情報に応じた検索結果を返す。
現在地などを送信することで、現在いるお店や付近のお店を調べることができる。
※送信された位置情報は保存していない。お気に入り登録
検索結果の「Add to my favorites」をタップすると、場所をお気に入り登録できる。
お気に入り一覧表示
LINEBOT: 「My FAVORITE」バナーをタップすると、登録したお気に入りが確認できる。
LIFF: トップページにデフォルトで表示。お気に入り解除
お気に入り一覧で「Remove」をタップすると、場所をお気に入り解除できる。
感想
Goの習熟を第一目的としていた割には、少し時間を割き過ぎました。。
自分でサービス開発をしたいと考えているので、入門的にもっとライトなものを作って、それから本格的な個人サービスを作れば良かったなという感じです。ただ、Goは現場で即通用するレベルに引き上げられたと感じていますし、クリーンアーキテクチャやNuxt、LINEへの理解を深められたので良かったです。
しばらくは以下のことに注力していきたいと思います。
- モダンな案件への参画、できればGo使いたい
- 個人サービス開発
- (せっかくだから、知人経由でLINE BOTやLIFFアプリ開発の仕事とれないかな。。)
Twitterの方でもLINEに限らず、モダンな技術習得やサービス開発の様子を発信したりしているので良かったらチェックしてみてください!
@yagi_eng
- 投稿日:2020-06-29T09:33:06+09:00
go修行13日目 Package
Package
- 1階層
awesomeProject/ ├── main.go └── mylib └── math.gomain.go
package main import ( "awesomeProject/mylib" "fmt" ) func main() { s := []int{1, 2, 3, 4, 5} fmt.Println(mylib.Average(s)) }math.go
package mylib func Average(s []int) int { total := 0 for _, i := range s { total += i } return int(total / len(s)) }結果
3
- 2階層
階層
awesomeProject/ ├── main.go └── mylib ├── human.go ├── math.go └── under └── sub.gomain.go
package main import ( "awesomeProject/mylib" "awesomeProject/mylib/under" "fmt" ) func main() { s := []int{1, 2, 3, 4, 5} fmt.Println(mylib.Average(s)) mylib.Say() under.Hello() }math.go
package mylib func Average(s []int) int { total := 0 for _, i := range s { total += i } return int(total / len(s)) }human.go
package mylib import "fmt" func Say() { fmt.Println("Human!") }sub.go
package under import "fmt" func Hello() { fmt.Println("Hello!") }出力
3 Human! Hello!Public,Private
- 外部から利用する場合には
Public
のように頭は大文字main.go
package main import ( "awesomeProject/mylib" "awesomeProject/mylib/under" "fmt" ) func main() { s := []int{1, 2, 3, 4, 5} fmt.Println(mylib.Average(s)) mylib.Say() under.Hello() person := mylib.Person{Name: "Mike", Age: 20} fmt.Println(person) fmt.Println(mylib.Public) fmt.Println(mylib.private) }human.go
package mylib import "fmt" var Public string = "Public" var private string = "private" type Person struct { Name string Age int } func Say() { fmt.Println("Human!") }出力
private
は頭が小文字でunexpotedと出ている.\main.go:19:14: cannot refer to unexported name mylib.private .\main.go:19:14: undefined: mylib.private
- 投稿日:2020-06-29T08:50:55+09:00
GoでAPIを使ってみたら心が3回くらい折れた話。
こんにちは。そうはぶです。
APIの利用は今までで一番楽しく、学びも一番大きかった反面、プログラミングで初めて絶望しそうになりました。初めての絶望
ついにAPIに初挑戦しました!が、プログラミングで初めて絶望しました。
というのも、「API利用 golang」などでググると自分はその記事を何一つ理解できないのに、それを書いた人はなにも苦戦せず当たり前のように実装していて、「自分にはプログラミングには才能がないのか」と病みました。
このときの僕のように初めてAPI利用に挑戦してみて絶望してプログラミングをやめようとしている人にとって、同じように苦しんでやり続けたらなんとかできるようになったよと勇気づけられたり、基礎を終えてそろそろAPIを利用してみようかなと思っている方々の参考に一つになれば幸いです。API利用において特に苦戦した箇所
これは間違いなく「認証認可や、HTTPの基礎的な事項」です。コンピュータサイエンスの基礎を学ばずにこの2ヶ月走り続けていた報いがきたという感じでした。
ヘッダーとボディがわからないレベルではなく、レスポンスとリクエストすらほぼ理解していませんでした。
この辺りの勉強は正直とても楽しいとは言えないですが、やはり基礎は何においても本当に大切なことなんだと改めて実感させられました。使用API
選んだのはTwitterAPIでした。言語はGoです。
心が折れた
とりあえず最初にTwitterAPIの公式ドキュメントを読みました。しかしこの時点では前述の通りリクエストとレスポンスの違いすら怪しく、OAthなど初めて聞いたし、ドキュメントのAuthenticationを読んでも一ミリも理解できず正直絶望しました。
なにがなんなのか全く理解できず、ドキュメントで理解できたところは一つもありませんでした。返されるJSONも「この時点ではこれどうやって使うの??」とただひたすら混乱していました。
前述の通り、「Twitter API golang」などで検索すると、みんな簡単そうに実装していて自分にはプログラミングの才能がなかったのかと諦めかけました。とりあえずAPIは諦めた
たくさん検索しているうちに、自分がAPIを全く理解できないのは才能というよりコンピュータサイエンスの基礎がないせいなのではないかという仮説が立ちました。その日の内に評判の良かったWebを支える技術、Real World HTTP 第2版を買いました。
翌日、Webを支える技術が届いたので早速読んでみたところ衝撃的なわかりやすさでサクサク読んでいけました。これは僕のようにHTTPって何?レベルの人に本当におすすめです。急激に自分の世界が広がるような感覚がありました。
結局この日のうちに8割ほど読み終えました。残りは付録みたいな感じだったので1日で全部読めるくらいでした。何度でも言いますが本当におすすめです。その翌日にReal World HTTP 第2版が届きました。これはHTTPの基本をGoで実装しながら解説していくという本で、Webを支える技術に比べるとめちゃくちゃボリューミーかつ難しいですが、Goを使うHTTPを学習したい僕にとってはまさにぴったりな本でした。いまだに読み切れてませんが、何かにぶつかるたびに開くバイブルみたいな存在です。特にGoでクエリーやクッキーの送信を実装したことが理解の大きな助けになりました。
初めて書いた記事もこちらを参考にして書きました。公式ドキュメントリベンジ
コンピュータサイエンスの基礎を学んだ僕は再びTwitterAPIの公式ドキュメントに挑戦しました。
結果からいうと完全理解とまではいきませんでした。。。
しかし、もちろん前回の理解度とは天と地の差でした。curlで何をリクエストしているかや、なぜレスポンスがJSONで返ってくるのかがわかったし、これはとても大きな成長を感じました。便利なサードパーティーパッケージに出会う
大きな成長は感じれたものの理解し切れなかった悔しさもありなんとも言えない気持ちになっていたところで、OAuth1とgo-twitterに出会います。
これは衝撃的でした。公式ドキュメントに比べて理解がかなりしやすかったです。これをつかって公式ドキュメントと照らし合わせながら自分が今何をやっているのか随時確認しながら実装していきました。なんとかTwitterAPIを利用できた
OAuth1とgo-twitterを使ってなんとかできたのがこちらです。
設定用ファイル
config.gopackage config import ( "github.com/dghubble/go-twitter/twitter" "github.com/dghubble/oauth1" ) //OAuth1.0で認可し、APIクライアントを作成 func NewClient() *twitter.Client { config := oauth1.NewConfig("YOUR CONSUMER KEY", "YOUR CONSUMER SECRETKEY") token := oauth1.NewToken("YOUR ACCESS TOKEN", "YOUR ACCESS TOKEN SECRET") httpClient := config.Client(oauth1.NoContext, token) client := twitter.NewClient(httpClient) return client }関数用ファイル
function.gopackage function import ( "fmt" "github.com/dghubble/go-twitter/twitter" ) //タイムラインの投稿者とその投稿内容をセットで出力 func ShowTimeline(client *twitter.Client) { var tweets []twitter.Tweet tweets, _, _ = client.Timelines.HomeTimeline(nil) for _, tweet := range tweets { fmt.Printf("投稿者: %d\n", tweet.User.Name) fmt.Printf("内容: %s\n", tweet.Text) fmt.Println("============================================================================") } }実行ファイル
main.gopackage main import ( "twitter_api/config" "twitter_api/function" ) func main() { //クライアントを定義 client := config.NewClient() //タイムライン表示 function.ShowTimeline(client) }こうすると、
投稿者:〇〇
内容:〜〜〜〜〜〜〜〜〜〜〜〜〜〜
という形で自分のタイムラインが表示されます。
表示されたときの感動は本当にすごかったです。まとめ
コンピュータサイエンスの基礎から学び直したのでたったこれだけのものを作るのに50時間くらいかかったと思います。
成果物はとても小さいですが得られたものはものすごく大きかったです。
APIの利用は最初の一歩がとても大変なのかなと思いました。一歩目はをなんとか乗り越えることができたのでこれからガンガンAPIを利用してもっと大きな成果物を作成していきたいと思います。参考
- 投稿日:2020-06-29T08:50:55+09:00
GoでAPIを使ってみようとしたら心が3回くらい折れた話。
こんにちは。そうはぶです。
APIの利用は今までで一番楽しく、学びも一番大きかった反面、プログラミングで初めて絶望しそうになりました。初めての絶望
ついにAPIに初挑戦しました!が、プログラミングで初めて絶望しました。
というのも、「API利用 golang」などでググると自分はその記事を何一つ理解できないのに、それを書いた人はなにも苦戦せず当たり前のように実装していて、「自分にはプログラミングには才能がないのか」と病みました。
このときの僕のように初めてAPI利用に挑戦してみて絶望してプログラミングをやめようとしている人にとって、同じように苦しんでやり続けたらなんとかできるようになったよと勇気づけられたり、基礎を終えてそろそろAPIを利用してみようかなと思っている方々の参考に一つになれば幸いです。API利用において特に苦戦した箇所
これは間違いなく「認証認可や、HTTPの基礎的な事項」です。コンピュータサイエンスの基礎を学ばずにこの2ヶ月走り続けていた報いがきたという感じでした。
ヘッダーとボディがわからないレベルではなく、レスポンスとリクエストすらほぼ理解していませんでした。
この辺りの勉強は正直とても楽しいとは言えないですが、やはり基礎は何においても本当に大切なことなんだと改めて実感させられました。使用API
選んだのはTwitterAPIでした。言語はGoです。
心が折れた
とりあえず最初にTwitterAPIの公式ドキュメントを読みました。しかしこの時点では前述の通りリクエストとレスポンスの違いすら怪しく、OAthなど初めて聞いたし、ドキュメントのAuthenticationを読んでも一ミリも理解できず正直絶望しました。
なにがなんなのか全く理解できず、ドキュメントで理解できたところは一つもありませんでした。返されるJSONも「この時点ではこれどうやって使うの??」とただひたすら混乱していました。
前述の通り、「Twitter API golang」などで検索すると、みんな簡単そうに実装していて自分にはプログラミングの才能がなかったのかと諦めかけました。とりあえずAPIは諦めた
たくさん検索しているうちに、自分がAPIを全く理解できないのは才能というよりコンピュータサイエンスの基礎がないせいなのではないかという仮説が立ちました。その日の内に評判の良かったWebを支える技術、Real World HTTP 第2版を買いました。
翌日、Webを支える技術が届いたので早速読んでみたところ衝撃的なわかりやすさでサクサク読んでいけました。これは僕のようにHTTPって何?レベルの人に本当におすすめです。急激に自分の世界が広がるような感覚がありました。
結局この日のうちに8割ほど読み終えました。残りは付録みたいな感じだったので1日で全部読めるくらいでした。何度でも言いますが本当におすすめです。その翌日にReal World HTTP 第2版が届きました。これはHTTPの基本をGoで実装しながら解説していくという本で、Webを支える技術に比べるとめちゃくちゃボリューミーかつ難しいですが、Goを使うHTTPを学習したい僕にとってはまさにぴったりな本でした。いまだに読み切れてませんが、何かにぶつかるたびに開くバイブルみたいな存在です。特にGoでクエリーやクッキーの送信を実装したことが理解の大きな助けになりました。
初めて書いた記事もこちらを参考にして書きました。公式ドキュメントリベンジ
コンピュータサイエンスの基礎を学んだ僕は再びTwitterAPIの公式ドキュメントに挑戦しました。
結果からいうと完全理解とまではいきませんでした。。。
しかし、もちろん前回の理解度とは天と地の差でした。curlで何をリクエストしているかや、なぜレスポンスがJSONで返ってくるのかがわかったし、これはとても大きな成長を感じました。便利なサードパーティーパッケージに出会う
大きな成長は感じれたものの理解し切れなかった悔しさもありなんとも言えない気持ちになっていたところで、OAuth1とgo-twitterに出会います。
これは衝撃的でした。公式ドキュメントに比べて理解がかなりしやすかったです。これをつかって公式ドキュメントと照らし合わせながら自分が今何をやっているのか随時確認しながら実装していきました。なんとかTwitterAPIを利用できた
OAuth1とgo-twitterを使ってなんとかできたのがこちらです。
設定用ファイル
config.gopackage config import ( "github.com/dghubble/go-twitter/twitter" "github.com/dghubble/oauth1" ) //OAuth1.0で認可し、APIクライアントを作成 func NewClient() *twitter.Client { config := oauth1.NewConfig("YOUR CONSUMER KEY", "YOUR CONSUMER SECRETKEY") token := oauth1.NewToken("YOUR ACCESS TOKEN", "YOUR ACCESS TOKEN SECRET") httpClient := config.Client(oauth1.NoContext, token) client := twitter.NewClient(httpClient) return client }関数用ファイル
function.gopackage function import ( "fmt" "github.com/dghubble/go-twitter/twitter" ) //タイムラインの投稿者とその投稿内容をセットで出力 func ShowTimeline(client *twitter.Client) { var tweets []twitter.Tweet tweets, _, _ = client.Timelines.HomeTimeline(nil) for _, tweet := range tweets { fmt.Printf("投稿者: %d\n", tweet.User.Name) fmt.Printf("内容: %s\n", tweet.Text) fmt.Println("============================================================================") } }実行ファイル
main.gopackage main import ( "twitter_api/config" "twitter_api/function" ) func main() { //クライアントを定義 client := config.NewClient() //タイムライン表示 function.ShowTimeline(client) }こうすると、
投稿者:〇〇
内容:〜〜〜〜〜〜〜〜〜〜〜〜〜〜
という形で自分のタイムラインが表示されます。
表示されたときの感動は本当にすごかったです。まとめ
コンピュータサイエンスの基礎から学び直したのでたったこれだけのものを作るのに50時間くらいかかったと思います。
成果物はとても小さいですが得られたものはものすごく大きかったです。
APIの利用は最初の一歩がとても大変なのかなと思いました。一歩目はをなんとか乗り越えることができたのでこれからガンガンAPIを利用してもっと大きな成果物を作成していきたいと思います。参考
- 投稿日:2020-06-29T00:45:49+09:00
GOCVを使ってみた
こんにちはwattak777です。
色々と機会があり、お勉強のためGo言語によるOpenCV「gocv」を入れるところまでをまとめました。
環境
- ホストOS:Windows 10
- VirtualBox:6.0.22
- ゲストOS:Ubuntu 18.04 LTSGoのセットアップ
sudo apt install -y golangOpenCVのセットアップ
セットアップには以下の記事を参考にしました。
Ubuntu 18.04 ServerにOpenCV 4.1.0をソースからインストール(install-opencv.sh使用)事前に登録していた場合はOpenCVを削除
sudo apt autoremove libopencv-dev python-opencvシェルの取得
wget --no-check-certificate https://raw.githubusercontent.com/milq/milq/master/scripts/bash/install-opencv.shファイルを編集
vim install-opencv.shinstall-opencv.sh###################################### # INSTALL OPENCV ON UBUNTU OR DEBIAN # ###################################### # -------------------------------------------------------------------- | # SCRIPT OPTIONS | # ---------------------------------------------------------------------| OPENCV_VERSION='4.2.0' # Version to be installed OPENCV_CONTRIB='NO' # Install OpenCV's extra modules (YES/NO) # -------------------------------------------------------------------- |上記のバージョンとCONTRIBを適宜修正。
インストール実行
chmod +x install-opencv.sh ./install-opencv.sh$\tiny{メモリ8GBで2時間ほど走らせても終わらなかったので「?」と思ってたらVirtualBoxのCPU数が「1」のままでした、という失敗談も添えておきます。}$
GoCVの導入
以下のページを参照しました。
GoCVを試すまでのお話インストール
go get -u -d gocv.io/x/gocv※要gitのインストール
サンプルプログラムの取得と実行
git clone https://github.com/hybridgroup/gocv cd gocv go run ./cmd/version/main.go以下のようにgocvのバージョンとopencvのバージョンが出力されればOKです。
gocv version: 0.23.0 opencv lib version: 4.1.0helloが走らなかった件
cmd配下にはいろいろとサンプルがありまして「hello」もあるので走らせようと思ったら、どうやらカメラを映して表示させるプログラムだったようで、VirtualBox上でUbuntu 18.04を動かしたのですがカメラが反応せずでした。
そこで、以下の方法で実現し、helloも実行できました。
Extension Packの導入
下記がダウンロードページとなります。
Download VirtualBox
※自分の場合はさらにそこから古いバージョンのダウンロードページに行く必要がありましたが…。デバイスの追加
設定はゲストOSを起動した状態でコマンドプロンプトを開け、Webカメラの追加設定をホストOS側の
VBoxManager
を用いて行います。C:\Users\UserName>cd "\Program Files\Oracle\VirtualBox" C:\Program Files\Oracle\VirtualBox>使用している環境を取得
C:\Program Files\Oracle\VirtualBox> VBoxManage.exe list runningvms "Guest Name" {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}接続しているカメラを表示
C:\Program Files\Oracle\VirtualBox> VBoxManage.exe list webcams Video Input Devices: 1 .1 "Certain WebCam" \\Camera Path上記の「.1」をアタッチ
C:\Program Files\Oracle\VirtualBox> VboxManage controlvm "Guest Name" webcam attach .1※参考は以下
VirtualBox上の仮想OSでWebカメラを使えるようにするこれでゲストOS上で
go run
をかけるとカメラに映った画像が表示されました。