動かして覚えるGoのWorkspaceモードの使い方

今までのモジュール管理方法はこちらをご覧ください。

https://qiita.com/Rqixy/items/b906fcb54cf162427775

# 実行環境
- M1 Macbook Air
- macOS Monterey 12.4
- Go 1.18

WindowsでもGoがちゃんとインストールされていれば大丈夫です。

# workspaceモードとは

複数のモジュールを一つのgo.workというファイルで管理し、モジュール間の行き来をスムーズにすると言うものです。

```go.work
go 1.18

use (
	./module1
	./module2
)
```

このような感じでmoduleを複数管理することができます。

## workspaceのコマンド

いくつかコマンドがあるのですが、よく使うinitとuseコマンドを紹介します。
### init

go.workファイルを初期化する時に使います。
```terminal
% go work init
```

initの後ろに既に作成しているモジュールを追加することができます。
```terminal
go work init モジュール名
```

### use
既にgo.workファイルを作成しており新たにモジュールを追加したい時に使います。
```terminal
% go work use モジュール名
```
複数のモジュールを追加したい時は、スペースを空けて書きます。
```terminal
% go work use モジュール1 モジュール2
```

# 実際にやってみよう
まずはディレクトリを作成しましょう。
今回はworkspaceという名前でディレクトリを作成して、workspaceディレクトリに移動します。
```terminal
%　mkdir workspace && cd workspace
```

workspaceディレクトリでhelloディレクトリを作成して、helloディレクトリに移動します。
```terminal
workspace % mkdir hello && cd hello
```

### helloディレクトリ
helloディレクトリでhello.goを作成します。
```terminal
hello % touch hello.go
```

hello.goを記述します。
```go:hello/hello.go
package hello

import "fmt"

func Hello(name string) {
	fmt.Printf("Hello, %s!", name)
}
```

helloディレクトリでgo.modを作成します。
```terminal
hello % go mod init example.com/hello
go: creating new go.mod: module example.com/hello
go: to add module requirements and sums:
        go mod tidy
```

### mainディレクトリ
workspaceに戻ってhelloディレクトリと同じ手順でmainディレクトリを作成して、mainディレクトリに移動します。

```terminal
hello % cd ../
workspace % mkdir main && cd main
```
mainディレクトリでmain.goを作成します。
```terminal
main % touch main.go
```

main.goを記述します。
```go:main/main.go
package main

import "example.com/hello"

func main() {
	hello.Hello("Yuuki")
}
```

mainディレクトリでgo.modを作成します。
```terminal
main % go mod init example.com/main
go: creating new go.mod: module example.com/main
go: to add module requirements and sums:
        go mod tidy
```

このままでは、main.goを実行してもエラーになってしまいます。
```terminal
main % go run main.go
main.go:3:8: no required module provides package example.com/hello; to add it:
        go get example.com/hello
```

### go.workを作成する
では、workspaceディレクトリに戻ってgo.workを作成してみましょう。
```terminal
main % cd ../
workspace % go work init main hello
```
workspaceディレクトリにこのようなgo.workファイルが作成されました。
```go.work
go 1.18

use (
	./hello
	./main
)
```

### 実行してみる
mainディレクトリに移動してmain.goを実行してみましょう。
```terminal
workspace % cd main
main % go run main.go
Hello, Yuuki!
```

ちゃんと動きました！
今までのように、必要なところにいちいちreplace文を書く必要がなく、このようにgo.work作成するだけで簡単に他のモジュールのメソッドを使えますね！

ディレクトリ構造はこのようになっています。
```terminal
workspace % tree
.
├── go.work
├── hello
│   ├── go.mod
│   └── hello.go
└── main
    ├── go.mod
    └── main.go
```

### 新しいモジュールを追加してみる

今回はフレームワークのechoを利用してサーバーの起動のモジュールを作成します。

https://github.com/labstack/echo

workspaceディレクトリに新しくserverファイルを作成して移動します。
```terminal
workspace % mkdir server && cd server
```

serverディレクトリでgo.modファイルを作成します。
```terminal
server % go mod init example.com/server
go: creating new go.mod: module example.com/server
```

serverディレクトリでechoをインストールします。
```terminal
server % go get github.com/labstack/echo/v4
go: added github.com/labstack/echo/v4 v4.7.2
go: added github.com/labstack/gommon v0.3.1
go: added github.com/mattn/go-colorable v0.1.11
go: added github.com/mattn/go-isatty v0.0.14
go: added github.com/valyala/bytebufferpool v1.0.0
go: added github.com/valyala/fasttemplate v1.2.1
go: added golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
go: added golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f
go: added golang.org/x/sys v0.0.0-20211103235746-7861aae1554b
go: added golang.org/x/text v0.3.7
```

serverディレクトリにgo.sumが作成され、go.modとgo.sumがこのようになっていると思います。
```server/go.mod
module example.com/server

go 1.18

require (
	github.com/labstack/echo/v4 v4.7.2 // indirect
	github.com/labstack/gommon v0.3.1 // indirect
	github.com/mattn/go-colorable v0.1.11 // indirect
	github.com/mattn/go-isatty v0.0.14 // indirect
	github.com/valyala/bytebufferpool v1.0.0 // indirect
	github.com/valyala/fasttemplate v1.2.1 // indirect
	golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
	golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
	golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect
	golang.org/x/text v0.3.7 // indirect
)
```
```server/go.sum
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/labstack/echo/v4 v4.7.2 h1:Kv2/p8OaQ+M6Ex4eGimg9b9e6icoxA42JSlOR3msKtI=
github.com/labstack/echo/v4 v4.7.2/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
```

serverディレクトリでserver.goを作成します。
```terminal
touch server.go
```

server.goを記述します。
```go:server/server.go
package server

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

func StartServer() {
	// Echo instance
	e := echo.New()
	
	// Middleware
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())
	
	// Routes
	e.GET("/", helloHandler)
	
	// Start server
	e.Logger.Fatal(e.Start(":1234"))
}

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

###  go.workに追記する
このままだとエラーが出てしまうので、workspaceディレクトリに移動してuseコマンドを使います。
```terminal
server % cd ../
workspace % go work use server
```
「./server」が追加されていればOKです。
```go.work
go 1.18

use (
	./hello
	./main
	./server    // 追加
)
```

### main.goに追記する
main.goにStartServerメソッドを記述します
```go:main/main.go
package main

import (
	"example.com/hello"
	"example.com/server"    // 追加
)

func main() {
	hello.Hello("Yuuki")
	server.StartServer()    // 追加
}
```

### 実行してみる
mainディレクトリに移動してmain.goを実行してみましょう。
```terminal
workspace % cd main
main % go run main.go
Hello, Yuuki!
   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v4.7.2
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\
⇨ http server started on [::]:1234

```

http://localhost:1234/

こちらのURLに移動して「Hello World!」と表示されていればOKです。

最終的なディレクトリ構造はこちらになります。
```terminal
.
├── go.work
├── go.work.sum
├── hello
│   ├── go.mod
│   └── hello.go
├── main
│   ├── go.mod
│   └── main.go
└── server
    ├── go.mod
    ├── go.sum
    └── server.go
```

# まとめ

実際に書きながらworkspaceモードの使い方について、学びました。
みなさんもよかったら自分のパソコンで試してみてください！

こちらが今回作成したソースコードになります。

https://github.com/Rqixy/go-workspace-mode-test

# 参考にしたサイト
https://go.dev/doc/tutorial/workspaces

https://zenn.dev/kimuson13/articles/go-workspace-mode-impressions

https://future-architect.github.io/articles/20220216a/