Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

Go 언어의 웹 프레임워크 Gin 사용하기

1월부터 하는 Kubernetes에서 실제로 서비스를 구축하기 위해 간단한 마이크로 서비스를 만들어 보고 있다. 주로 Node.js로 하는 게 편하지만 다른 언어도 배워보고 싶어서 Golang을 선택했다. 아주 간단한 API 서버라서 웹 프레임워크가 필요했는데 Go 언어에 대해 아는 게 없어서 좀 찾아봤다.

Top 6 web frameworks for Go as of 2017같은 글을 참고했는데 본격적으로 사용해 보지 않으면 감이 잘 안와서 선택이 쉽지는 않았다. 웹 MVC 프레임워크로는 beegoBuffalo가 있었지만 주로 마이크로 웹 프레임워크를 더 선호하는 편이라서 Gin이나 echo, Iris에 관심이 갔다.

Go My Way #1 - 웹 프레임워크를 보다 보니 일부러 프로젝트가 활발한 것처럼 보이려고 속이던 행동이 발견된 Iris는 일찌감치 제외하고 Gin와 echo 중에서 고민하다가 크게 차이는 없는 것 같아서(특히 지금 내 용도에서는...) 좀 더 스타가 많은 Gin을 선택했다.

Gin

저장소의 README에 나온 내용대로 main.go 파일을 생성한다.

 1// main.go
 2package main
 3
 4import "github.com/gin-gonic/gin"
 5
 6func main() {
 7  r := gin.Default()
 8  r.GET("/ping", func(c *gin.Context) {
 9    c.JSON(200, gin.H{
10      "message": "pong",
11    })
12  })
13  r.Run() // listen and serve on 0.0.0.0:8080
14}

Go 언어에서 dep으로 의존성 관리를 하고 있어서 dep을 초기화한다.

1$ dep init

초기화 후 만들어진 Gopkg.toml 파일에 gin을 사용하기 위해 다음 내용을 추가한다.

1[[constraint]]
2  name = "github.com/gin-gonic/gin"
3  branch = "master"

이제 추가한 의존성을 설치한다.

1$ dep ensure

의존성 설치도 완료되었으면 Hello World 식 예제 애플리케이션을 실행할 수 있다.

 1$ go run main.go
 2[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
 3
 4[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 5 - using env: export GIN_MODE=release
 6 - using code:  gin.SetMode(gin.ReleaseMode)
 7
 8[GIN-debug] GET    /ping                     --> main.main.func1 (3 handlers)
 9[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
10[GIN-debug] Listening and serving HTTP on :8080

Gin 서버가 http://localhost:8080에 떴으므로 이 서버에 요청을 보내면 정상적으로 뜬 것을 확인할 수 있다.

1$ curl -i http://localhost:8080/ping
2HTTP/1.1 200 OK
3Content-Type: application/json; charset=utf-8
4Date: Sun, 29 Apr 2018 16:45:36 GMT
5Content-Length: 18
6
7{"message":"pong"}

실제 웹 애플리케이션을 작성하려면 데이터베이스 라이브러리도 필요하고 프로젝트 폴더 구조도 만들어야 하지만 이 글에서는 Gin에 대한 설정만 얘기하고 있으므로 간단히 Gin의 라우팅만 살펴보자.

 1// main.go
 2package main
 3
 4import (
 5  "net/http"
 6
 7  "github.com/gin-gonic/gin"
 8)
 9
10func main() {
11  r := gin.Default()
12
13  v1 := r.Group("/v1")
14  {
15    v1.GET("/health", health)
16    v1.POST("/signup", signup)
17    v1.POST("/login", login)
18  }
19  r.Run()
20}
21
22func health(c *gin.Context) {
23  c.JSON(http.StatusOK, gin.H{
24    "message": "ok",
25  })
26}
27
28func signup(c *gin.Context) {
29  c.JSON(http.StatusCreated, gin.H{
30    "message": "signed up",
31  })
32}
33
34func login(c *gin.Context) {
35  c.JSON(http.StatusOK, gin.H{
36    "message": "logged in",
37  })
38}

보통 API 서버는 URL에 접두사가 붙으므로 r.Group("/v1")을 사용하면 반복해서 사용하지 않아도 된다. 위 설정은 http://localhost:3000/v1/health로 요청을 받게 된다. 상태 코드를 숫자로 적어도 되지만 net/http에서 상수를 가져와서 HTTP 상태 코드로 사용할 수 있다.

 1$ PORT=3000 go run main.go
 2[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
 3
 4[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 5 - using env: export GIN_MODE=release
 6 - using code:  gin.SetMode(gin.ReleaseMode)
 7
 8[GIN-debug] GET    /v1/health                --> main.health (3 handlers)
 9[GIN-debug] POST   /v1/signup                --> main.signup (3 handlers)
10[GIN-debug] POST   /v1/login                 --> main.login (3 handlers)
11[GIN-debug] Environment variable PORT="3000"
12[GIN-debug] Listening and serving HTTP on :3000

그리고 gin은 기본 포트로 8080을 사용하지만 PORT 환경변수를 지정하면 해당 포트로 서버를 실행하게 된다.

Valid HTML5 Valid CSS WCAG 2.1 AA tested