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 파일을 생성한다.

// main.go
package main

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

func main() {
  r := gin.Default()
  r.GET("/ping", func(c *gin.Context) {
    c.JSON(200, gin.H{
      "message": "pong",
    })
  })
  r.Run() // listen and serve on 0.0.0.0:8080
}

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

$ dep init

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

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

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

$ dep ensure

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

$ go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env: export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> main.main.func1 (3 handlers)
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080

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

$ curl -i http://localhost:8080/ping
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Sun, 29 Apr 2018 16:45:36 GMT
Content-Length: 18

{"message":"pong"}

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

// main.go
package main

import (
  "net/http"

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

func main() {
  r := gin.Default()

  v1 := r.Group("/v1")
  {
    v1.GET("/health", health)
    v1.POST("/signup", signup)
    v1.POST("/login", login)
  }
  r.Run()
}

func health(c *gin.Context) {
  c.JSON(http.StatusOK, gin.H{
    "message": "ok",
  })
}

func signup(c *gin.Context) {
  c.JSON(http.StatusCreated, gin.H{
    "message": "signed up",
  })
}

func login(c *gin.Context) {
  c.JSON(http.StatusOK, gin.H{
    "message": "logged in",
  })
}

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

$ PORT=3000 go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env: export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /v1/health                --> main.health (3 handlers)
[GIN-debug] POST   /v1/signup                --> main.signup (3 handlers)
[GIN-debug] POST   /v1/login                 --> main.login (3 handlers)
[GIN-debug] Environment variable PORT="3000"
[GIN-debug] Listening and serving HTTP on :3000

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

2018/04/30 02:53 2018/04/30 02:53