20200630のGoに関する記事は10件です。

Goにおけるnewと宣言のみの違い

newで初期化することとvarで宣言する違い

newで初期化

newで初期化すると以下のようなコードになります。

var i *int = new(int)

これを出力すると

package main

import "fmt"

func main() {
    var i *int = new(int)
    //もしくはi := new(int)
    fmt.Printf("%T %v\n", i, *i) → *int 0
}

となってnewではポインタ型を返します。

構造体だったりはポインタで使用されることが多いのでnewを使うことが多い。

type Vertex struct{
  X,Y int
}
....
v := new(Vertex)
//v := &Vertex{}と同じ

varで宣言のみ

varで宣言すると以下のようなコードになります。

var i2 *int

これを出力すると

package main

import "fmt"

func main() {
    var i2 *int
    fmt.Printf("%T\n", i2) → *int
    fmt.Println(i2) → <nil>
}

となり、nil値となる。

javaなどと同じで宣言だけでは、入らないですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

gin gormでテーブル操作(少し改良版)

以前gin gormでテーブル操作で一連のテーブル操作をやってみた

しかし、どうもBDのコネクションは保持したままの方が良いようなので、修正してみる
ついでにこちらと同じGo Modules管理に変更している

以下雑にコードをのせているだけなので、何が変わったかは↑の記事と見比べてください(すまん

docker-compose.yml

docker-compose.yml
services:
  app:
    container_name: gin_app
    image: golang:1.12.0-alpine
    volumes:
      - .:/go/src/app
    command: >
      sh -c "cd /go/src/app &&
      apk update &&
      apk add --no-cache git &&
      GO111MODULE=off go get -u github.com/codegangsta/gin &&
      go mod init || : &&
      gin -i run"
    ports:
      - 3001:3001
    environment:
      GO111MODULE: "on"

DB

db/db.go
package db

import (
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

var database *gorm.DB

func Connection() *gorm.DB {
    var err error
    database, err = gorm.Open("mysql", "root:@tcp(db:3306)/gin_app?charset=utf8&parseTime=True&loc=Local")
    if err != nil {
        panic("failed to connect database")
    }
    database.LogMode(true)
    return database
}

func DB() *gorm.DB {
    return database
}

毎回接続せずにコネクションを変数に保持する形式に変更。他packageでは db.DB() で取得できるようにしている

マイグレーション

こちらもメソッドに。任意で実行できるように変更

migrate/migrate.go
package main

import (
    "app/db"
    "app/model"

    _ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
    db.Connection()
    defer db.DB().Close()

    db.DB().AutoMigrate(&model.User{})
    db.DB().AutoMigrate(&model.UserName{})
}

マイグレーション実行
Go Modules管理に変更したら以前の方法だとエラーになるので少し修正

$ docker exec -it gin_app sh -c "cd /go/src/app; go run migrate/migrate.go"

モデル

ここは変更なし

model/user.go
package model

import (
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

type User struct {
    gorm.Model
    UserName UserName
}
model/user_name.go
package model

import (
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

type UserName struct {
    gorm.Model
    UserID uint
    Name   string
}

コントローラ

DBの取得方法を変更

controller/user.go
package controller

import (
    "app/db"
    "app/model"

    "github.com/gin-gonic/gin"
)

type User struct{}

func NewUser() *User {
    return &User{}
}

func (t *User) Get(c *gin.Context) {
    db := db.DB()

    var user model.User
    result := db.First(&user, c.Param("id")).Related(&user.UserName)
    c.JSON(200, result.Value)
}

func (t *User) List(c *gin.Context) {
    db := db.DB()

    var users []model.User
    result := db.Preload("UserName").Find(&users)
    c.JSON(200, result.Value)
}

func (t *User) Create(c *gin.Context) {
    db := db.DB()

    var user model.User
    db.Create(&user)

    var userName model.UserName
    c.BindJSON(&userName)
    userName.UserID = user.ID
    db.Create(&userName)
}

func (t *User) Update(c *gin.Context) {
    db := db.DB()

    var user model.User
    db.First(&user, c.Param("id")).Related(&user.UserName)
    if user.UserName.ID > 0 {
        c.BindJSON(&user.UserName)
        db.Save(&user.UserName)
    }
}

func (t *User) Delete(c *gin.Context) {
    db := db.DB()

    var user model.User
    db.First(&user, c.Param("id")).Related(&user.UserName)
    if user.ID > 0 {
        db.Delete(&user)
    }
    if user.UserName.ID > 0 {
        db.Delete(&user.UserName)
    }

    // こっちの消し方でも良い
    // db.Where("id = ?", c.Param("id")).Delete(&model.User{})
    // db.Where("user_id = ?", c.Param("id")).Delete(&model.UserName{})
}

main

はじめにDB接続をするように変更

main.go
package main

import (
    "app/controller"

    "github.com/gin-gonic/gin"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
    db.Connection()
    defer db.DB().Close()

    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.GET("/users", controller.NewUser().List)
    r.GET("/users/:id", controller.NewUser().Get)
    r.POST("/users", controller.NewUser().Create)
    r.PUT("/users/:id", controller.NewUser().Update)
    r.DELETE("/users/:id", controller.NewUser().Delete)

    r.Run()
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CircleCIからCloudRunへ自動的にデプロイする

この記事は、CircleCIを使って、Google Cloud Platform(GCP)のサービスであるCloudRunへシステムをデプロイをする方法をまとめたものです。

CircleCIを使えばgithubにpushしたソースコードを自動的にテスト、ビルド、デプロイすることができます。
なおこの記事内では、githubとCircleCIの連携方法やCircleCIの利用方法については省略しています。

ポイント

・ CircleCIのCloudRun用Orbsを利用する
・ GCPに接続するための接続情報を環境変数に設定する

構成

全体のイメージはこんな感じ。

名称未設定ファイル.png

GitHubの構成はこんな感じ。
言語はGo1.13です。

github
/.circleci
  |-config.yml
/sample-app
  |-Dockerfile
  |-main.go
  |-... (他アプリソース群)

手順

  1. CircleCIのジョブ設定 (設定ファイルを作成)
  2. CircleCIからContainerRegistry、ClourRunに接続するための環境変数を設定
  3. githubに対してソースコードをpushする

CircleCIのジョブ設定

CircleCIのジョブ設定は、上記のようにgithubのリポジトリにconfig.ymlを追加するだけです。
CircleCIからCloudRunにデプロイするためのOrbsが提供されているのでそれを利用します。
今回はマネージドのCloudRunの方にデプロイするようにします。

ちなみにOrbsというのは、いわゆるライブラリのようなもので、他のymlファイルをインポートしています。
自分で書くのが難しい部分を使いやすくして提供してくれています。

下記の設定は、githubにpushされたソースコードをテスト、ビルド、デプロイする設定になっています。

config.yml
version: 2.1 # use CircleCI 2.1

orbs:
    gcp-cloud-run: circleci/gcp-cloud-run@1.0.2

executors:
    build:
        docker:
            - image: circleci/golang:1.13 #

jobs:
    # テスト時はgo1.13のdockerイメージを使ってgo testを実行
    test-job:
        executor:
            name: build
        steps:
            - checkout
            - run:
                name: Run unit tests
                command: | 
                    cd sample-app
                    go test -v ./...
    # デプロイ時はContainerRegistoryにビルドしたイメージを保管し、CloudRunへのデプロイを実行
    # リポジトリのDockerfileに従ってビルドされる
    # プロジェクトID test-project-hogehogeは変更する
    deploy-job:
        docker:
            - image: 'cimg/base:stable'
        steps:
            - checkout
            - run:
                command: cd sample-app
            - gcp-cloud-run/init
            - gcp-cloud-run/build:
                source: ./sample-app
                tag: 'asia.gcr.io/test-project-hogehoge/sample-app:${CIRCLE_SHA1}'
            - gcp-cloud-run/deploy:
                image: 'asia.gcr.io/test-project-hogehoge/sample-app:${CIRCLE_SHA1}'
                platform: managed
                region: asia-northeast1
                service-name: sample-app
                unauthenticated: true

#テスト用ジョブ(テストのみ)とデプロイ用ジョブ(ビルド、デプロイ)に分けて実行
workflows:
    test-and-deploy:
        jobs:
            - test-job
            - deploy-job

CircleCIの環境設定

CircleCIからGCPへ接続するためにはサービスアカウント情報が必要になります。
なのでアクセス権限を持ったサービスアカウント(のJSONキー)をGCP上で作成しておいてください。
※よくわからなければCloudBuildとCloudRunの管理者権限を付与すれば権限上は大丈夫

CircleCIでは下記を環境変数として設定することでGCPへの接続が可能になります。
・GCLOUD_SERVICE_KEY
・GOOGLE_PROJECT_ID
・GOOGLE_COMPUTE_ZONE

これらを下記のようにCircleCIプロジェクト設定画面で設定すればOK
スクリーンショット 2020-06-30 16.46.45.png

※GCLOUD_SERVICE_KEYにはJSONキーをそのまま入れています

CloudRunの設定

CloudRun側では特に設定は必要ありません。
CircleCIが完了すれば自動的にデプロイされます。

CircleCIの実行

githubにソースコードをpushしてCircleCIを実行してみてください。
CircleCIでテストが実行され、ビルドしたコンテナイメージがCloudRunにデプロイするまでを自動的に実現することができます。

参考

https://circleci.com/orbs/registry/orb/circleci/gcp-cloud-run
https://circleci.com/docs/ja/2.0/google-auth/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CloudRunでRedis(Memorystore)を利用する

この記事は、Google Cloud Platform(GCP)のサービスであるCloudRunでRedisを利用するための方法をまとめたものです。

CloudRundもサーバレスVPCアクセスが利用可能になったので、CloudRunからMemorystore(Redis)を利用することが可能になっています。
CluodRunでできることがどんどん増えていくのは嬉しいですね。

ポイント

  • フルマネージドなインメモリサービスであるMemorystore(Redis,Memcached)を利用する
  • Memorystoreを利用するにはプライベートアドレスでの接続が必要
  • GAEやCloudRunから接続する場合はサーバーレスVPCアクセスを利用する
    ※GAEやCloudRunはマネージドサービスなのでプライベートアドレスが付与されない

手順

  1. サーバーレスVPCを作成
  2. MemoryStoreを作成
  3. CloudRunを作成

1. サーバーレスVPCを作成

CloudRunからアクセスするためのネットワークを作成します。

GCPコンソールから
VPCネットワーク > サーバーレスVPCアクセス
に移動し、コネクタを作成
(初回はAPIを有効化する必要あり)

例えば下記のように設定します。

スクリーンショット 2020-06-30 12.49.43.png

2. Memorystore(Redis)を作成

次に、Memorystore(Redis)を下記の点に注意して作成します。

  • ロケーションを 1.で作成したコネクタと同じリージョンにする
    (今回はasia-northeast1)

  • ネットワークを1.で作成したコネクタと同じネットワークにする
    (今回はdefault)

これらを設定することで 1.のコネクタを通したVPCアクセスが可能になります。

3. CloudRunを作成

最後に、CloudRunにソースコードをデプロイします。

変数設定にRedisのIPアドレス、ポート番号を指定する

2.で作成したMemorystoreのIPアドレス、ポート番号を環境変数に下記のように設定します。

スクリーンショット 2020-06-30 12.45.50.png

接続設定に作成したサーバーレスVPCコネクタを指定する

CloudRunが接続するコネクタを下記の画像のように設定します。

スクリーンショット 2020-06-30 12.39.01.png

サンプルコード

サイト訪問によるカウントアップ

main.go
// Command redis is a basic app that connects to a managed Redis instance.
package main

import (
    "fmt"
    "log"
    "net/http"
    "os"

    "github.com/gomodule/redigo/redis"
)

var redisPool *redis.Pool

func incrementHandler(w http.ResponseWriter, r *http.Request) {
    conn := redisPool.Get()
    defer conn.Close()

    counter, err := redis.Int(conn.Do("INCR", "visits"))
    if err != nil {
        http.Error(w, "Error incrementing visitor counter", http.StatusInternalServerError)
        return
    }
    fmt.Fprintf(w, "Visitor number: %d", counter)
}

func main() {
    redisHost := os.Getenv("REDISHOST")
    redisPort := os.Getenv("REDISPORT")
    redisAddr := fmt.Sprintf("%s:%s", redisHost, redisPort)

    const maxConnections = 10
    redisPool = redis.NewPool(func() (redis.Conn, error) {
        return redis.Dial("tcp", redisAddr)
    }, maxConnections)

    http.HandleFunc("/", incrementHandler)

    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }
    log.Printf("Listening on port %s", port)
    if err := http.ListenAndServe(":"+port, nil); err != nil {
        log.Fatal(err)
    }
}

Dockerfileはかなり適当...

FROM golang:1.13
RUN mkdir -p /app
WORKDIR /app
COPY . /app
RUN go build /app/main.go
ENTRYPOINT ["/app/main"]

ここまでの設定ができていれば、リロードするごとに数値がカウントアップされるサイトがデプロイされていると思います。

参考サイト

GAEからサーバレスVPCへの接続
https://cloud.google.com/appengine/docs/standard/python/connecting-vpc?hl=ja#creating_a_connector

GAE/GoでRedisへ接続する
https://cloud.google.com/appengine/docs/standard/go/using-memorystore?hl=ja

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rust は流行りそうもないので、Go を使う。

最近、暇で以下のサービスを作った。
https://deau-project.herokuapp.com/

バックエンドには仕事で使ったことがない、Go にした。
勉強してたのは、Rust で、仕事のCSVを分析するシステムを Rust で作ったけど、
この先、使うことがないのでは? と思っている。

以前、Haskell が流行りそうで、だいぶ勉強したけど、今では話題にすらならない。
関数型も、いまいち浸透していない。

流行る言語は、何かしらの手間を無くす。
Ruby が流行ったのは、Rails がフレームワーク使用時の共通作業を無くしたから。
PHP が未だに廃れないのは、WordPress が共通作業を無くしてるから。

Go は流行る。もう流行っている?
理由は学習コストを減らしたから。

今回、Go Gin を使用した、そして、簡単に実装できた。
情報も充分にあった。

Rust でも、情報に関しては充分にある。
ただ、実装時に面倒だなと感じることが多い。
私のように趣味で実装するなら、尚更、面倒に感じる。

Rust が流行るには、組込み系にターゲットを絞るか、
当時の Rails のような手間を省く何かが必要だと思う。
組込み系で考えれば、C++でよくあるミスを防げる。確認の手間を省ける。

今回、サーバーサイドに Go を使用したが、この先はどうかわからない。
趣味でならいいが、仕事で使うとなると、関数型である必要がある。
もし、今から新しいシステムを作るとして、サーバーサイドに何を使用すればいいのか。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Goの実行環境をDockerで構築しようと際に発生した「no Go files in /go/src/github.com/go-playground/localesエラー」を解決

はじめに

root@xxxxxxxxxxxx:/go# go get github.com/go-playground/locales

いつものようにパッケージをインストールしていたら次のエラーが発生。

can't load package: package github.com/go-playground/locales: no Go files in /go/src/github.com/go-playground/locales

スクリーンショット 2020-06-30 14.49.04.png

ここのlocalesフォルダの中に何もファイルなんてねーよ!って言われてるんですね。そんなこと言われても。。。

解決方法

go getをしても同じエラーが吐かれるだけだったので、試しにlocalesフォルダを削除して、再度以下コマンドを叩いてみました。

root@xxxxxxxxxxxx:/go# go get github.com/go-playground/locales

すると2分程度でパッケージのインストールが終わりました。

スクリーンショット 2020-06-30 14.51.55.png

入った!

無事コンパイルが終了し、GoをDocker環境で構築することができました。Docker便利です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MacにGo言語開発環境をインストールする

最終更新日

2020年6月30日

検証を行ったmacOSのバージョンは下記です。

  • macOS Catalina 10.15.5

インストールするもののバージョンは下記です。

この記事が古くなった場合、下記の手順は最新のインストール手順とは異なっている可能性があります。

Go SDK

インストール

(1) https://golang.org/dl/ にアクセスしてください。

(2) go1.14.4.darwin-amd64.pkg をダウンロードしてください。
スクリーンショット 2020-06-30 14.22.35.png

(3) ダウンロードしたPKGファイルをダブルクリックしてください。

(4) [続ける]をクリックしてください。

スクリーンショット 2020-06-30 14.24.59.png

(5) [このコンピュータのすべてのユーザ用にインストール]を選択して、[続ける]をクリックしてください。

スクリーンショット 2020-06-30 14.26.04.png

(6) [インストール]をクリックしてください。パスワード入力を求められたら、macOSアカウントのパスワードを入力してください。

スクリーンショット 2020-06-30 14.26.16.png

(7) [閉じる]をクリックしてください。

スクリーンショット 2020-06-30 14.27.19.png

(8) [ゴミ箱に入れる]をクリックしてください。

スクリーンショット 2020-06-30 14.28.00.png

確認

(1) ターミナルで go version コマンドを実行してください。インストールしたGoのバージョンが表示されれば成功です。

スクリーンショット 2020-06-30 14.28.54.png

Visual Studio CodeのGo Extension

Visual Studio Code自体のインストールは別記事にて → MacにVisual Studio Code(VSCode)をインストールする

インストール

(1) Visual Studio Codeを起動してください。

(2) Extensionの検索画面を開いて「go」で検索→[Go Team at Google]のGoエクステンションをInstallしてください。

スクリーンショット 2020-06-30 14.15.32.png

動作確認

(1) 適当なフォルダにhello.goというファイルを作成して、Visual Studio Codeで下記のように編集してください。

package main

import "fmt"

func main() {
    fmt.Println("Hello!");
}

(2) ターミナルで先ほどのフォルダに移動して、 go run hello.go コマンドを実行してください。「Hello!」と表示されれば成功です。

スクリーンショット 2020-06-30 14.29.59.png

Git

Gitのインストール

別記事にて → MacにGitをインストールする

動作確認

(1) 適当なフォルダ(先ほどのhello.goのフォルダとは別にしてください)に、go.modというファイルを作成して、Visual Studio Codeで下記のように編集してください。

module sample

go 1.14

(2) ターミナルで先ほどのフォルダに移動して、 go get github.com/labstack/echo/v4@v4.1.16 コマンドを実行してください。下記のように表示されれば成功です。

スクリーンショット 2020-06-30 14.32.45.png

(3) go.modを確認してください。 require github.com/labstack/echo/v4 v4.1.16 // indirect という記述が追加されています。

module sample

go 1.14

require github.com/labstack/echo/v4 v4.1.16 // indirect

(4) go.modと同じフォルダにserver.goというファイルを作成して、Visual Studio Codeで下記のように編集してください。

package main

import (
  "net/http"
  "github.com/labstack/echo/v4"
)

func main() {
  e := echo.New()

  e.GET("/", hello)

  e.Logger.Fatal(e.Start(":1323"))
}

func hello(c echo.Context) error {
  return c.String(http.StatusOK, "Hello!")
}

(5) ターミナルで先ほどのフォルダに移動して、 go run server.go コマンドを実行してください。次のように表示されれば成功です。

スクリーンショット 2020-06-30 14.33.34.png

(6) ブラウザで http://localhost:1323 にアクセスしてください。「Hello!」と表示されれば成功です。

スクリーンショット 2020-06-30 14.34.38.png

(7) 先ほどのターミナルでCtrl + Cを押下してください。Echoが停止します。

スクリーンショット 2020-06-30 14.34.57.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WindowsにGo言語開発環境をインストールする

最終更新日

2020年6月30日

検証を行ったWindows 10のバージョンは下記です。
- Windows 10 Home 2004 19041.329

インストールするもののバージョンは下記です。
- Go SDK 1.14.4
- Visual Studio CodeのGo Extension(Visual Studio Code自体のインストールは別記事にて → WindowsにVisual Studio Codeをインストールする
- Git(別記事にて → WindowsにGit Bashをインストールする

この記事が古くなった場合、下記の手順は最新のインストール手順とは異なっている可能性があります。

Go SDK

インストール

(1) https://golang.org/dl/ にアクセスしてください。

(2) go1.14.4.windows-amd64.msi をダウンロードしてください。

スクリーンショット 2020-06-30 11.21.48.png

(3) ダウンロードしたMSIファイルをダブルクリックしてください。

(4) [Next]をクリックしてください。

スクリーンショット 2020-06-30 11.23.38.png

(5) [I accept the terms in the License Agreement]にチェックを入れて、[Next]をクリックしてください。

スクリーンショット 2020-06-30 11.24.10.png

(6) [Next]をクリックしてください。

スクリーンショット 2020-06-30 11.25.02.png

(7) [Install]をクリックしてください。

スクリーンショット 2020-06-30 11.25.25.png

(8) [はい]をクリックしてください。

スクリーンショット 2020-06-30 11.25.55.png

(9) [Finish]をクリックしてください。
スクリーンショット 2020-06-30 11.28.04.png

確認

(1) コマンドプロンプトで go version コマンドを実行してください。インストールしたGoのバージョンが表示されれば成功です。

スクリーンショット 2020-06-30 11.31.16.png

Visual Studio CodeのGo Extension

Visual Studio Code自体のインストールは別記事にて → WindowsにVisual Studio Codeをインストールする

インストール

(1) Visual Studio Codeを起動してください。

(2) Extensionの検索画面を開いて「go」で検索→[Go Team at Google]のGoエクステンションをInstallしてください。

スクリーンショット 2020-06-30 11.56.17.png

動作確認

(1) 適当なフォルダにhello.goというファイルを作成して、Visual Studio Codeで下記のように編集してください。

package main

import "fmt"

func main() {
    fmt.Println("Hello!");
}

(2) コマンドプロンプトで先ほどのフォルダに移動して、 go run hello.go コマンドを実行してください。「Hello!」と表示されれば成功です。

スクリーンショット 2020-06-30 13.27.22.png

Git

Gitのインストール

別記事にて → WindowsにGit Bashをインストールする

動作確認

(1) 適当なフォルダ(先ほどのhello.goのフォルダとは別にしてください)に、go.modというファイルを作成して、Visual Studio Codeで下記のように編集してください。

module sample

go 1.14

(2) コマンドプロンプトで先ほどのフォルダに移動して、 go get github.com/labstack/echo/v4@v4.1.16 コマンドを実行してください。下記のように表示されれば成功です。

スクリーンショット 2020-06-30 13.31.41.png

(3) go.modを確認してください。 require github.com/labstack/echo/v4 v4.1.16 // indirect という記述が追加されています。

module sample

go 1.14

require github.com/labstack/echo/v4 v4.1.16 // indirect

(4) go.modと同じフォルダにserver.goというファイルを作成して、Visual Studio Codeで下記のように編集してください。

package main

import (
  "net/http"
  "github.com/labstack/echo/v4"
)

func main() {
  e := echo.New()

  e.GET("/", hello)

  e.Logger.Fatal(e.Start(":1323"))
}

func hello(c echo.Context) error {
  return c.String(http.StatusOK, "Hello!")
}

(5) コマンドプロンプトで先ほどのフォルダに移動して、 go run server.go コマンドを実行してください。

次のようなダイアログが表示された場合は[アクセスを許可する]をクリックしてください。

スクリーンショット 2020-06-30 13.36.30.png

次のように表示されれば成功です。

スクリーンショット 2020-06-30 13.37.00.png

(6) ブラウザで http://localhost:1323 にアクセスしてください。「Hello!」と表示されれば成功です。

スクリーンショット 2020-06-30 13.38.15.png

(7) 先ほどのコマンドプロンプトでCtrl + Cを押下してください。Echoが停止します。

スクリーンショット 2020-06-30 13.39.16.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ionic & golangでリアルタイム大喜利アプリを作りました

アプリのリリースまでなんとか漕ぎ着けたので、宣伝も兼ねて使ったフレームワークについて紹介します。

どんなアプリか

写真で一言アプリです。
特徴的なのはリアルタイムで集まった人たちが、お互いに投稿し合ったボケを評価し合う部分です。

マッチングした最大10名が、制限時間内に一つのお題写真に対してボケを投稿します。
投稿はお互いに評価することができ、一定数イイネを集めると一本獲得になります。

こちらからダウンロード可能です。
よかったら遊んでみてください。

android
https://play.google.com/store/apps/details?id=jp.co.popbits.funnyapp

ios
https://apps.apple.com/jp/app/funny-one/id1515018792

アプリ側

ionic

アプリのベースはIonic Frameworkで作成しました。

https://ionicframework.com/

ざっくりいうと、
webview + Angular
にネイティブっぽいリッチな見た目のコンポーネントと、共通化されたプラグインが利用できます。
プラグインの例としてはSocial Sharingやdeeplink等があります。

Angularではなく、vueやreactで記載することも可能なようです。
※reactは最近入ったらしい。


android, ios同一のコードで記載できるので工数削減になりました。
環境構築やビルドなどもかなりスムーズにできました。
(多少のつまづきはありますが、ネイティブアプリの環境構築にはつきものです)

懸念点として、webviewベースであるためパフォーマンスがあまり良くないのではないか気になりましたが、
端末の性能も上がってきているからか、思ったよりサクサク動いてくれています。

createjs

大喜利バトルの部分はcreatejsで作成しました。
https://createjs.com/

Adobe Animate
https://www.adobe.com/jp/products/animate.html
でcreatejs用にアニメーションを書き出しています。

アプリへのつなぎ込みには色々と試行錯誤が必要でしたが、細かい話になるので割愛します。

大喜利バトルでは後述するgolangサーバとwebsocketで接続し、イベント毎にアニメーションを変化させます。


サーバ側

golang

リアルタイムや並列処理に強そうなので採用しました。
pythonもよく書くのですが、golangは型があるのでバグが起きづらい、
パフォーマンスが良いなどのメリットがあります。

コードは誰が書いても同じ書き方になるよう矯正され、必然的に可読性が高くなります。

一方、リアルタイム部分(websoket接続)はgoroutine と channelを駆使して作るのですが、
正直自分以外理解できないコードになってしまったと思います。

並列処理そのものが難しいのもあると思います。
通常の処理とは違いコードの上から追っていくだけでは読み解くことができず、
どこからchannnelにアクセスが有るなど把握していないといけません。

並列処理は色々な書き方ができるので正解がない感じです。

gin

https://github.com/gin-gonic/gin

デファクトスタンダードっぽいので採用しました。
良さげです。

melody

https://github.com/olahol/melody

websocketを扱う際にいい感じの機能を提供してくれます。

sqlx + squirrel

https://github.com/jmoiron/sqlx
クエリ結果をstructsにマッピングしてくれます。

https://github.com/Masterminds/squirrel
SQLクエリビルダーです。
where句の検索条件をqueryオブジェクトに対して注入していく、
みたいなことがやりたい場合はこれでSQLを作ります。

最初はgormを使っていたのですが、最終的に上の構成に書き換えることにしました。
gormは人気があるようなので使っていたのですが、挙動が直感的でなかったのと、
思いも寄らない部分で大量のSQLを発行されていたことがあったりで、嫌になってやめました。

まとめ

色々紹介しましたが、ionicはかなり気軽にアプリが作成できるので本当におすすめです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

go修行14日目 ユニットテストとか

testing

└── mylib
    ├── human.go
    ├── math.go
    ├── math_test.go

testされるファイル

package mylib

func Average(s []int) int {
    total := 0
    for _, i := range s {
        total += i
    }
    return int(total / len(s))
}

testするファイル

  • 3が期待される
package mylib

import "testing"

func TestAverage(t *testing.T) {
    v := Average([]int{1, 2, 3, 4, 5})
    if v != 3 {
        t.Error("Expected 3, got", v)
    }
}

run test

  • vscodeから

image.png
image.png

  • コマンドラインから
PS C:\Users\yuta\go\src\awesomeProject> go test ./...
?       awesomeProject  [no test files]
ok      awesomeProject/mylib    (cached)
?       awesomeProject/mylib/under      [no test files]
  • 数字変えるとFAILになる
package mylib

import "testing"

func TestAverage(t *testing.T) {
    v := Average([]int{1, 2, 3, 4, 5, 6,7})
    if v != 3 {
        t.Error("Expected 3, got", v)
    }
}
--- FAIL: TestAverage (0.00s)
    C:\Users\yuta\go\src\awesomeProject\mylib\math_test.go:8: Expected 3, got 4
FAIL
FAIL    awesomeProject/mylib    0.374s
FAIL
  • Skip処理
package mylib

import "testing"

var Debug bool = true

func TestAverage(t *testing.T) {
    // Debugがtrueならスキップ
    if Debug {
        t.Skip("Skip reason")
    }
    v := Average([]int{1, 2, 3, 4, 5, 6, 7})
    if v != 3 {
        t.Error("Expected 3, got", v)
    }
}

PS C:\Users\yuta\go\src\awesomeProject> go test ./... -v
?       awesomeProject  [no test files]
=== RUN   TestAverage
    TestAverage: math_test.go:10: Skip reason
--- SKIP: TestAverage (0.00s)
PASS
ok      awesomeProject/mylib    0.368s
?       awesomeProject/mylib/under      [no test files]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む