go.mod มีที่มาอย่างไร (2)
Published: 2023-07-27 03:38:27

เล่าต่อจากครั้งที่แล้วที่จะออกแนวเล่าประวัติศาสตร์มากกว่าจะบอกว่า go.mod คืออะไรจริงๆ ครั้งนี้ก็เลยจะขอเล่าต่ออีกสักหน่อยหนึ่ง ซึ่งก็ขอเริ่มต้นจากค่า env ที่น่าสนใจ 2 ค่าอันได้แก่

- GOPATH
- GOROOT

ซึ่งในยุคแรกเริ่มที่ยังไม่มี Go Module ใช้กัน ผมเห็นกับตาว่ามีคนสับสน 2 ค่านี้จริงจังมาก และเห็นบ่อยๆที่กำหนด 2 ค่านี้ให้ชี้ไปที่ path เดียวกัน ซึ่งมันพอรับในได้วันแรกๆที่เขียน code แต่ถ้าปล่อยไว้นานวันเข้า มันอาจจะมีบางอย่างผิดปกติไป ซึ่งก็เห็นอีกว่า หลายคนไม่เข้าใจสิ่งนี้และก็ลงเอยด้วยการต้องลง Go กันใหม่ หรือยอมแพ้เลิกเขียนไปเลยก็มี

**GOROOT** ค่านี้คือ path ของ Go หรือจะเรียกว่า SDK ของ Go ก็ได้เช่นกัน เป็นที่ที่เมื่อเราติดตั้งภาษา Go ไว้ที่ไหนก็ตาม เราก็จะต้องกำหนดค่า GOROOT ไปไว้ที่นั่นเช่น ถ้าติดตั้ง Go ไว้ที่ `/usr/local/go` ค่า GOROOT ก็จะหน้าตาแบบนี้

```sh
GOROOT=/usr/local/go
```

ซึ่งในนั้นก็จะประกอบไปด้วย Go compiler และพวก standard libraries ต่างๆที่มาพร้อมกับตัว compiler

**GOPATH** ค่านี้ เดิมทีเคยถูกกำหนดให้เป็น workspace เพื่อใช้เขียนโปรแกรม และเก็บพวก open source lib ต่างๆ และ tools ทั้งหลายที่เขียนด้วย Go โดยจะมีโครงสร้างหลัก 3 directories

```
- bin
- pkg
- src
```

และโดย deafult แล้ว เจ้าค่า GOPATH มักจะถูกกำหนดให้อยู่ที่ HOME directory เช่น `/Users/pallat/go` (โดยชื่อ `pallat` คือชื่อ account ของเรานะครับ)

ิ- bin จะมีไว้สำหรับ program ต่างๆที่ถูกเขียนด้วย Go และเมื่อใช้คำสั่ง `go install` มันก็จะถูก bulid และนำมาวางไว้ที่นี่โดยอัตโนมัติ ซึ่งเราก็ควร append  เจ้า path นี้เข้าไปใน PATH env ด้วย เพื่อความสะดวกในการเรียกใช้งาน ตัวอย่าง program ที่อยู่ในนั้นก็เช่น golangci-lint, gosec, gopls, etc.
- pkg เดิมจะถูกเก็บ lib ลงไปตรงๆตาม path ที่เราไป `go get` มาเลย แต่เมื่อเราเริ่มใช้ Go Module แล้ว การเก็บ lib จะถูกนำไปไว้ใน `pkg/mod` อีกที 
- src ในยุคก่อนที่จะใช้ Go Module เราจะต้องไปเขียนโค้ดกันในนี้เท่านั้น โดยจะสร้าง directory ให้แต่ละ repo แยกกันให้รู้ว่าเราเขียนอะไรเช่น `src/myapp` หรือ `src/github.com/pallat/myapp`

เล่าจนเหนื่อย มาเข้าเรื่อง go.mod กันสักหน่อย
ทุกวันนี้เราไปเขียน Go ที่ path ไหนก็ได้ทั้งนั้น และถ้าไม่ได้ใช้อะไรเกินเลยจาก standard lib ที่มีให้ และทำ flat structure เราก็ไม่จำเป็นต้องมี go.mod เลยก็ยังได้ แต่ที่ถูกที่ควรแล้ว การสร้างให้ถูกต้องตั้งแต่แรกก็น่าจะดีกว่า โดยคำสั่งในการสร้าง go.mod หรือก็คือ Go Module ของเราก็คือ

1. สร้าง project directory ก่อน
2. cd เข้าไปใน project นั้น
3. ใช้คำสั่ง `go mod init [projectname]` ตั้งชื่อ project name ที่ต้องการได้เลย

การตั้งชื่อ projectname มี 2 รูปแบบหลักๆคือ
1. ตั้งตามใจ
2. ตั้งตาม URI ของ repo จริงๆเช่น `go mod init github.com/pallat/demo`

_การตั้งชื่อตาม URI มีประโยชน์เพื่อให้ repo เราจะสามารถถูก import ไปใช้จาก repo อื่นๆได้_

หลักจากนั้นเราจะเห็นไฟล์ go.mod เกิดขั้น ซึ่งเมื่อสร้างใหม่ๆมันจะมีแค่ 2 บรรทัดหน้าตาแบบนี้

```go
module github.com/pallat/demo

go 1.20
```

และเมื่อตอนรันคำสั่งเสร็จเราก็จะเห็นข้อความแบบนี้

```sh
go: creating new go.mod: module github.com/pallat/demo
go: to add module requirements and sums:
	go mod tidy
```

ในข้อความจะชี้จุดต่อไปให้เราว่า เมื่อเราเริ่มมีการใช้ lib หรือนำ module อื่นๆจากภายนอกเข้ามาใช้ ให้รันคำสั่ง `go mod tidy` ซึ่งความจริง เราจะใช้คำสั่ง `go get [URI]` แบบเดิมก็ได้เช่นกัน เพียงแต่ต้องทำกับทุก mod ที่เราต้องการใช้ แต่การใช้คำสั่ง `go mod tidy` จะช่วยให้สะดวกกว่า กรณีที่เราเขียนโค้ดเรียกใช้ mod/lib ไปก่อน แล้วพอใช้คำสั่ง `tidy` มันก็จะไปวิ่งหาว่าเราเรียกใช้อะไรที่ยังไม่ได้ `go get` มาบ้าง แล้วมันก็จะไป get ลงมาให้ แถมตัวไหนไม่ใช้แล้วมันก็จะเอาออกให้ด้วย

ยังมีคำสั่งอื่นๆในหมวด `go mod` อีก โดยเราสามารถขอดู help ได้ด้วยคำสั่ง `go mod help`

อื่นๆก็เช่น `go mod download` ในกรณีที่เรามี go.mod แล้ว เราต้องการ download dependencies ลงมาใหม่หมด หรือ `go mod vendor` เพื่อเอา dependencies หรือ mod ที่เราต้องการใช้ ลงมาไว้ใน directory ชื่อ `vendor` ใน repo ของเราเลยเป็นต้นครับ