[Go] Gin Web Framework CRUD 맛보기.
Intro
Gin Web Framework Gin is a web framework written in Go (Golang). It features a martini-like API with performance that is up to 40 times faster thanks to httprouter. If you need performance and good productivity, you will love Gin.
Hands-On Full-Stack Development with Go 책을 참고하여 Go의 CRUD web api를 만들어보았다. Pagination 및 Swagger를 추가한 코드는 git에서 확인가능하다.
https://github.com/deagwon97/go-gin-crud
src
├── app
│ ├── models
│ │ └── models.go
│ ├── dblayer
│ │ ├── dblayer.go
│ │ └── orm.go
│ └── rest
│ ├── handler.go
│ └── rest.go
│
├── app2
├── app3
├── ...
│
├── database
│ └── database.go
├── routes
│ └── routes.go
├── main.go
│
├── go.mod
└── go.sum
app
위에서 언급한 책에서는 여러 로직이 섞여서 프로젝트가 구성되어있었다. Django의 구조를 참고하여 로직별로 app을 분리하였다. app 폴더에는 models, dblayer rest 이렇게 3개의 package가 존재한다.
1. models : 모델 정의 django의 models.py와 대응된다.
- models.go
package models // Content 구조체 정의 type Content struct { // gorm.Model // gorm.Model은 db구조를 변형한다. ID int ``gorm:"column:content_id" json:"content_id"`` Title string ``gorm:"column:title" json:"title"`` Summary string ``gorm:"column:summary" json:"summary"`` Content string ``gorm:"column:content" json:"content"`` CreatedAt string ``gorm:"column:created_at" json:"created_at"`` UpdatedAt string ``gorm:"column:updated_at" json:"updated_at"`` User int ``gorm:"column:user" json:"user"`` } // Content 함수 정의 // gorm에서 호출하는 테이블 명 커스텀 // 기본값 Content -> contents func (Content) TableName() string { return "content_content" }
- dblayer : 데이터 베이스와 관련된 코드
- dblayer.go
- 데이터베이스 레이어의 모든 동작을 정의하는 인터페이스이다.
- 데이터베이스 레이어 밖의 모든 데이터베이스 관련 코드는 이 인터페이스의 메서드만을 사용한다.
package dblayer import "go-api/content/models" // DBLayer 인터페이스 정의 type DBLayer interface { GetAllContents() ([]models.Content, error) }
- orm.go
package dblayer import ( "database/sql" "go-api/content/models" "gorm.io/driver/mysql" "gorm.io/gorm" ) // ---------------------------------- // DBORM 구조체 정의 // *gorm.DB 타입을 임베드 type DBORM struct { *gorm.DB } // DBORM 생성자 정의 func NewORM(dbengine string, dsn string) (*DBORM, error) { sqlDB, err := sql.Open(dbengine, dsn) // gorm.Open은 *gorm.DB 타입을 초기화한다. gormDB, err := gorm.Open(mysql.New(mysql.Config{ Conn: sqlDB, }), &gorm.Config{}) return &DBORM{ DB: gormDB, }, err } // DBORM 함수 정의 func (db *DBORM) GetAllContents() (contents []models.Content, err error) { return contents, db.Find(&contents).Error } // ----------------------------------
- dblayer.go
2. rest
- handler.go
- 클라이언트의 요청을 처리
- django의 views.py에 대응한다.
- 데이터베이스의 crud와 관련된 함수는 dblayer interface에서 정의된 함수를 사용한다.
- 인증 및 권한, 오류 코드 처리 등과 같은 로직이 추가된다.
package rest
import (
"go-api/content/dblayer"
"go-api/database"
"strconv"
"net/http"
"github.com/gin-gonic/gin"
)
// Handler Class 정의 -----------------
// Handler 구조체 정의
type Handler struct {
db dblayer.DBLayer
}
// Handler 구조체의 생성자는 정의하지 않음
// Handler 함수 정의
func (h *Handler) GetContents(c *gin.Context) {
if h.db == nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "dsn 오류"})
return
}
contents, err := h.db.GetAllContents() // dblayer의 함수 호출
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, contents)
}
// ----------------------------------
// HandlerInterface 정의
type HandlerInterface interface {
// HandlerInterface는 다음과 같은 함수를 갖는다.
GetContents(c *gin.Context)
GetContent(c *gin.Context)
}
// HandlerInterface 생성자 정의
func NewHandler() (HandlerInterface, error) {
dsn := database.DataSource
// DBORM 초기화 - DBORM 생성자 호출
db, err := dblayer.NewORM("mysql", dsn)
if err != nil {
return nil, err
}
return &Handler{
db: db,
}, nil
}
// ----------------------------------
- rest.go
- RESTfull AP의 entrypoint function 선언부이다.
- handler.go에서 정의한 handler 와 entrypoint를 연결한다.
- django의 urls.py와 대응한다.
package rest import "github.com/gin-gonic/gin" func AddContentRoutes(rg *gin.RouterGroup) { content := rg.Group("/content") h, _ := NewHandler() content.GET("/list", h.GetContents) content.GET("/:id", h.GetContent) }
- RESTfull AP의 entrypoint function 선언부이다.
main
1. database
- database.go 데이터 베이스 접근과 관련된 코드를 포함한다. 환경변수의 값을 참조하여 프로젝트 내부에서 db에 접근하는 것을 편리하게 도와준다.
2. routes
- routes.go
여러 app속에 들어있는 rest를 묶어서 하나의 route를 생성한다.
baseUrl과 swagger 추가할 수 있다.
package routes import ( "github.com/gin-gonic/gin" content_rest "go-api/content/rest" ) func Run(address string) error { router := gin.Default() v1 := router.Group("/") content_rest.AddContentRoutes(v1) return router.Run(address) }
3. main
- main.go
func main() { routes.Run(":8000") }
Reference
- Hands-On Full-Stack Development with Go
- https://github.com/gin-gonic/gin