動かして覚えるGoのモジュールの使い方


色々な記事見ていたんだけど難しく感じたり英語で書いてあって分かりにくかったので、具体的な説明は特になく、こんな感じに動くんだよ。と体感的に理解してもらいたく書きました。

自分としてもまだ知識不足なところあるから、もし間違っていたら教えてくれると嬉しいです。


::: note
今回は1.18から追加されたworkspaceモードではなく、今までのモジュール管理をどうやっていたか動かしながら説明していきます。

workspaceモードはこちらから試せます！↓
[動かして覚えるGoのWorkspaceモードの使い方](https://qiita.com/Rqixy/items/6bdead71dc02eb233376)
:::

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

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

# 使い方
例えば、このようなディレクトリ構造があります。
```terminal
% tree
.
├── main
│   └── main.go
└── module
　　   └── module.go
```
module.goにHelloメソッドを追加します。
```go:module/module.go
package module

func Hello() string {
	h := "hello"
	return h
}
```
mainモジュールでmoduleのHelloメソッドを使いたい場合、
moduleディレクトリ内で「go mod init」を実行します。
```terminal
module % go mod init example.com/module
```
↑「example.com」のところは、aaaa.bbbというようなドットを含む形で書けば好きな文字で良いみたいです。
(ユニーク(誰とも被らない)な文字の方が良いみたい。)
(よく見るのはgithub.com/自分のアカウント名/プロジェクト名/moduleとか)

```module/go.mod
module exmaple.com/module

go 1.18
```
moduleディレクトリにgo.modが生成されます。

mainディレクトリでも同じように作ります。
```terminal
main　% go mod init example.com/main
```
```main/go.mod
module example.com/main

go 1.18
```

こんな感じにｍain.goにmodule/go.modのモジュール名をimportに書きます。
```go:main/main.go
package main

import (
	"fmt"
	"example.com/module"
)

func main() {
	fmt.Println(module.Hello())
}
```
この状態で一度動かしてみます。
mainディレクトリに移動して
```terminal
main % go run main.go
main.go:4:2: module example.com/module provides package example.com/module and is replaced but not required; to add it:
        go get example.com/module
```
動きませんでした。
Google翻訳曰く、「モジュールexample.com/moduleはパッケージexample.com/moduleを提供し、置き換えられますが必須ではありません。 追加するには：go get example.com/module」
と書かれています。

じゃあ言われた通り入力してみましょう。
```terminal
main % go get example.com/module
go get: unrecognized import path "example.com/nodule": reading https://example.com/nodule?go-get=1: 404 Not Found
```
Google翻訳曰く、「認識されないインポートパス"example.com/nodule"：読み取りhttps://example.com/nodule?go-get=1：404 Not Found」
こんな感じにまた怒られてしまいました。
多分これはローカルで作ったものなのでURLがないよ！って怒られているんだと思います。

### 解決方法
main/go.modにreplace文を追加します。
```main/go.mod
module example.com/main

go 1.18

replace example.com/module => ../module
```
このようにreplaceの後に **module/go.modのｍodule名 => 実際のmoduleディレクトリの相対パス** を書きます。
mainのディレクトリの中で
```terminal
main % go mod tidy
```
と入力します。
```main/go.mod
module example.com/main

go 1.18

replace example.com/module => ../module

require example.com/module v0.0.0-00010101000000-000000000000
```
すると、main/go.modにrequire文追加されます。

こうすることで、自分のモジュールを別のモジュールから利用できるようになります。

```terminal
main % go run main.go
hello
```
このような感じでHelloメソッドが使えました。

最終的なディレクトリ構造はこのようになっています。
```
.
├── main
│   ├── go.mod
│   └── main.go
└── module
    ├── go.mod
    └── module.go

```

## 外部パッケージをmoduleにimportして使う方法
次は、外部パッケージとしてechoをserverディレクトリにインポートしてmainディレクトリでサーバーを起動してみます。

まずserverディレクトリを用意します。
ディレクトリ構造はこんな感じ
```.
├── main
│   ├── go.mod
│   └── main.go
├── module
│   ├── go.mod
│   └── module.go
└── server
    └── server.go
```

serverディレクトリに移動して
```terminal
server % go mod init example.com/server
```
go.modファイルを作成します。

https://github.com/labstack/echo

ここのInstallationの「go get github.com/labstack/echo/v4」をserverディレクトリで打ちます。
```terminal
server % go get github.com/labstack/echo/v4
go: downloading github.com/labstack/echo v1.4.4
go get: added github.com/labstack/echo/v4 v4.7.2
```
```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=

```
こんな感じにインストールされたらOK。

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("/", hello)

	// Start server
	e.Logger.Fatal(e.Start(":1234"))
}

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

mainディレクトリに移動して、main.goとgo.modに追記します。
```go:main/main.go
package main

import (
	//"example.com/module"         コメントアウト
	"example.com/server"           // 追加
	//"fmt"                        コメントアウト
)

func main() {
	//fmt.Println(module.Hello())  コメントアウト
	server.StartServer()           // 追加
}

```
```main/go.mod
module example.com/main

go 1.18

replace (
	example.com/module => ../module
	example.com/server => ../server　　　　// 追加   
)

require example.com/module v0.0.0-00010101000000-000000000000
```
複数replaceを書く時は、()で囲んで書くことができる。

書き終わったら、mainディレクトリで「go mod tidy」を入力する
```terminal
% go mod tidy
```
```main/go.mod
module example.com/main

go 1.18

replace (
	example.com/module => ../module
	example.com/server => ../server　　　　// 追加   
)

//　example.com/moduleコメントアウトして使用されていないので削除された
// 新しく使用しているexample.com/serverが追加された！
require example.com/server v0.0.0-00010101000000-000000000000

require (
	github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
	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
	golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
)
```
このように追加されていたらOK。

ではサーバーを起動してみましょう。
```terminal
main % go run main.go

   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v4.7.2
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\
⇨ http server started on [::]:1234

```
この状態で、http://localhost:1234/
こちらに移動すると、Hello World!と表示されているはずです。

最終的にはこのようなディレクトリ構造になっているかと思います。
```
.
├── main
│   ├── go.mod
│   ├── go.sum
│   └── main.go
├── module
│   ├── go.mod
│   └── module.go
└── server
    ├── go.mod
    ├── go.sum
    └── server.go

```
# まとめ
このモジュール管理はGoを勉強していて躓くポイントの一つだと思います。
今回は実際に動かしてみることで、言葉だけじゃ伝わりにくいところでも伝わるやすいのかなという想いで書きました。
なので、よかったらみなさん自身のパソコンで動かしてみてください。

https://github.com/Rqixy/go-module-test

こちら今回作ったソースコードですのでこちらでもよかったら確認してみてください。

# 参考にしたサイト
https://zenn.dev/optimisuke/articles/105feac3f8e726830f8c

https://go.dev/doc/tutorial/create-module

https://qiita.com/hnishi/items/a9217249d7832ed2c035

https://teratail.com/questions/217859

https://code-database.com/knowledges/95

https://zenn.dev/spiegel/articles/20210223-go-module-aware-mode#go.mod-%E3%81%A8-go.sum