[go/gin/gorm] Basic REST API 만들기 튜토리얼 (4)
이전 포스트 (1) : https://dev2som.tistory.com/152
이전 포스트 (2) : https://dev2som.tistory.com/153
이전 포스트 (3) : https://dev2som.tistory.com/154
저번 편에 이어서 gorm 패키지를 이용해 DB에 데이터를 바인딩할 수 있는 REST API를 만들어보려 한다.
우선 패키지 구조
go project를 만들어주고 다음 구조로 디렉터리와 go 파일들을 생성한다.
- models 패키지 : DB에 저장될 데이터의 구조체와 DB에 연결하는 코드가 들어있다.
- controllers 패키지 : HTTP 요청에 따라 호출되는 핸들러 함수들이 들어있다.
main.go
package main
import (
"github.com/gin-gonic/gin"
"github.com/___/restapi/controllers"
"github.com/___/restapi/models"
)
func main() {
r := gin.Default()
models.ConnectDatabase()
r.GET("/info", controllers.ReadInfo)
r.POST("/info", controllers.CreateInfo)
r.PUT("/info/:id", controllers.UpdateInfo)
r.DELETE("/info/:id", controllers.DeleteInfo)
r.Run("localhost:8080")
}
서버를 설정하는 메인 파일이다.
main() 함수를 실행할 때 트리거 될 함수들을 선언한다. (이 함수에 대한 내용은 https://dev2som.tistory.com/154 를 참고)
models 패키지의 info.go
package models
type Info struct {
Id int `json:"id" gorm:"primary_key"`
Name string `json:"name"`
Email string `json:"email"`
}
DB에 저장될 테이블의 컬럼 구조가 들어있는 구조체이다.
DB에 테이블로 저장될 때 구조체는 테이블명(소문자 복수형)으로 변경되고, 필드명은 컬럼명(소문자, snake case)로 변경된다.
여기에서 필드 Id는 테이블에서 primary key 가 된다.
models 패키지의 setup.go
package models
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
_ "github.com/jinzhu/gorm/dialects/mysql"
"github.com/jinzhu/gorm"
)
var DB *gorm.DB
func ConnectDatabase() {
var err error
DB, err = gorm.Open("mysql", "유저명:비밀번호@tcp(주소:포트)/test?charset=utf8")
if err != nil {
panic(err)
}
// table 명은 구조체명의 복수형으로 자동 명명된다.
// column 명은 구조체 필드의 소문자 snake case의 이름을 가진다.
DB.AutoMigrate(&Info{})
DB.DB().SetMaxIdleConns(10) // idle connection pool(유휴 연결 풀)의 최대 수 설정
DB.DB().SetMaxOpenConns(100) // 데이터베이스에 대한 열린 연결의 최대 수 설정
}
DB와 연결하는 코드이다. gorm.Open()을 통해 DB의 정보를 불러오고, 이것을 전역 변수인 *gorm.DB DB 변수에 저장했다.
전역변수 DB는 첫글자가 대문자이므로 다른 패키지에서도 참조할 수 있다.
gorm.DB.AutoMigrate(구조체 주소)는 해당 구조체에 관련된 테이블이 생성되어 있지 않다면 자동으로 테이블을 만들어준다.
controller 패키지의 info.go
package controllers
import (
"github.com/gin-gonic/gin"
"github.com/___/restapi/models"
"net/http"
)
type CreateInput struct {
Id int `json:"id" binding:"required"`
Name string `json:"name" binding:"required"`
Email string `json:"email" binding:"required"`
}
type UpdateInput struct {
Name string `json:"name"`
Email string `json:"email"`
}
func ReadInfo(c *gin.Context) {
// 전체 읽기
var infoList []models.Info
models.DB.Find(&infoList)
// 특정 컬럼 읽기
//var info models.Info
//models.DB.First(&info, 3) // primary key 기준으로 info 찾기
//models.DB.First(&info, "id = ?", 5) // id가 5인 info 찾기
if infoList == nil {
c.JSON(http.StatusNoContent, nil)
return
}
c.JSON(http.StatusOK, gin.H{
"status": "ok",
"data": infoList,
})
}
func CreateInfo(c *gin.Context) {
input := CreateInput{}
if err := c.ShouldBind(&input); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
// 생성하기
info := models.Info{Id: input.Id, Name: input.Name, Email: input.Email}
models.DB.Create(&info)
c.JSON(http.StatusOK, gin.H{
"status": "ok",
"data": input,
})
}
func UpdateInfo(c *gin.Context) {
var info models.Info
if err := models.DB.Where("id = ?", c.Param("id")).First(&info).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Record not found!",
})
return
}
input := UpdateInput{}
if err := c.ShouldBind(&input); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": err.Error(),
})
return
}
// 수정하기
models.DB.Model(&info).Updates(input)
// 수정하기 - 특정 필드 수정하기
//models.DB.Model(&info).Update("Name", "Lee")
//models.DB.Model(&info).Update(models.Info{Name: "Lee", Email: "rhdtha01@gmail.com"})
c.JSON(http.StatusOK, gin.H{
"status": "ok",
"data": info,
})
}
func DeleteInfo(c *gin.Context) {
var info models.Info
if err := models.DB.Where("id = ?", c.Param("id")).First(&info).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Record not found!",
})
return
}
// 삭제하기
models.DB.Delete(&info)
c.JSON(http.StatusOK, gin.H{
"status": "ok",
"data": info,
})
}
각 url과 HTTP Method에 따른 핸들러 함수들이 모여있는 파일이다. 각 동작에 대해서는 주석으로 설명을 첨부해두었다.
실행 결과
처음 서버를 동작시키고 테이블을 확인해보면,
아래처럼 구조체 Info에 대한 테이블이 생성되어 있는 것을 볼 수 있다.
이후 차례대로 GET, POST, PUT, DELETE 연산을 진행하면서 확인해본 결과 잘 동작되는 것을 볼 수 있다.